Bug 412 - Fixed an unitialized vnfr access in vnf config primitive
[osm/SO.git] / rwcm / plugins / rwconman / rift / tasklets / rwconmantasklet / jujuconf.py
1 #
2 # Copyright 2016 RIFT.IO Inc
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 #
16
17 import asyncio
18 import os
19 import re
20 import tempfile
21 import time
22 import yaml
23
24 import rift.mano.utils.juju_api as juju
25 from . import riftcm_config_plugin
26
27
28 def get_vnf_unique_name(nsr_name, vnfr_name, member_vnf_index):
29 """Get the unique VNF name.
30 Charm names accepts only a to z and non-consecutive - characters."""
31 name = "{}-{}-{}".format(nsr_name, vnfr_name, member_vnf_index)
32 new_name = ''
33 for c in name:
34 if c.isdigit():
35 c = chr(97 + int(c))
36 elif not c.isalpha():
37 c = "-"
38 new_name += c
39 return re.sub('\-+', '-', new_name.lower())
40
41
42 class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
43 """
44 Juju implementation of the riftcm_config_plugin.RiftCMConfigPluginBase
45 """
46 def __init__(self, dts, log, loop, project, account):
47 riftcm_config_plugin.RiftCMConfigPluginBase.__init__(self, dts, log, loop,
48 project, account)
49 self._name = account.name
50 self._type = 'juju'
51 self._ip_address = account.juju.ip_address
52 self._port = account.juju.port
53 self._user = account.juju.user
54 self._secret = account.juju.secret
55 self._rift_install_dir = os.environ['RIFT_INSTALL']
56 self._rift_var_root_dir = os.environ['RIFT_VAR_ROOT']
57
58 ############################################################
59 # This is wrongfully overloaded with 'juju' private data. #
60 # Really need to separate agent_vnfr from juju vnfr data. #
61 # Currently, this holds agent_vnfr, which has actual vnfr, #
62 # then this juju overloads actual vnfr with its own #
63 # dictionary elemetns (WRONG!!!) #
64 self._juju_vnfs = {}
65 ############################################################
66
67 self._tasks = {}
68 self._api = juju.JujuApi(log, loop,
69 self._ip_address, self._port,
70 self._user, self._secret)
71
72 @property
73 def name(self):
74 return self._name
75
76 @property
77 def agent_type(self):
78 return self._type
79
80 @property
81 def api(self):
82 return self._api
83
84 @property
85 def agent_data(self):
86 return dict(
87 type=self.agent_type,
88 name=self.name,
89 host=self._ip_address,
90 port=self._port,
91 user=self._user,
92 secret=self._secret
93 )
94
95 def vnfr(self, vnfr_id):
96 try:
97 vnfr = self._juju_vnfs[vnfr_id].vnfr
98 except KeyError:
99 self._log.error("jujuCA: Did not find VNFR %s in juju plugin", vnfr_id)
100 return None
101
102 return vnfr
103
104 def get_service_name(self, vnfr_id):
105 vnfr = self.vnfr(vnfr_id)
106 if vnfr and 'vnf_juju_name' in vnfr:
107 return vnfr['vnf_juju_name']
108 return None
109
110 def juju_log(self, level, name, log_str, *args):
111 if name is not None:
112 g_log_str = 'jujuCA:({}) {}'.format(name, log_str)
113 else:
114 g_log_str = 'jujuCA: {}'.format(log_str)
115 getattr(self._log, level)(g_log_str, *args)
116
117 # TBD: Do a better, similar to config manager
118 def xlate(self, tag, tags):
119 # TBD
120 if tag is None:
121 return tag
122 val = tag
123 if re.search('<.*>', tag):
124 self._log.debug("jujuCA: Xlate value %s", tag)
125 try:
126 if tag == '<rw_mgmt_ip>':
127 val = tags['rw_mgmt_ip']
128 except KeyError as e:
129 self._log.info("jujuCA: Did not get a value for tag %s, e=%s",
130 tag, e)
131 return val
132
133 @asyncio.coroutine
134 def notify_create_vlr(self, agent_nsr, agent_vnfr, vld, vlr):
135 """
136 Notification of create VL record
137 """
138 return True
139
140 @asyncio.coroutine
141 def notify_create_vnfr(self, agent_nsr, agent_vnfr):
142 """
143 Notification of create Network VNF record
144 Returns True if configured using config_agent
145 """
146 # Deploy the charm if specified for the vnf
147 self._log.debug("jujuCA: create vnfr nsr=%s vnfr=%s",
148 agent_nsr.name, agent_vnfr.name)
149 self._log.debug("jujuCA: Config = %s",
150 agent_vnfr.vnf_configuration)
151 try:
152 vnf_config = agent_vnfr.vnfr_msg.vnf_configuration
153 self._log.debug("jujuCA: vnf_configuration = %s", vnf_config)
154 if not vnf_config.has_field('juju'):
155 return False
156 charm = vnf_config.juju.charm
157 self._log.debug("jujuCA: charm = %s", charm)
158 except Exception as e:
159 self._log.Error("jujuCA: vnf_configuration error for vnfr {}: {}".
160 format(agent_vnfr.name, e))
161 return False
162
163 # Prepare unique name for this VNF
164 vnf_unique_name = get_vnf_unique_name(agent_nsr.name,
165 agent_vnfr.name,
166 agent_vnfr.member_vnf_index)
167 if vnf_unique_name in self._tasks:
168 self._log.warn("jujuCA: Service %s already deployed",
169 vnf_unique_name)
170
171 vnfr_dict = agent_vnfr.vnfr
172 vnfr_dict.update({'vnf_juju_name': vnf_unique_name,
173 'charm': charm,
174 'nsr_id': agent_nsr.id,
175 'member_vnf_index': agent_vnfr.member_vnf_index,
176 'tags': {},
177 'active': False,
178 'config': vnf_config,
179 'vnfr_name': agent_vnfr.name})
180 self._log.debug("jujuCA: Charm %s for vnf %s to be deployed as %s",
181 charm, agent_vnfr.name, vnf_unique_name)
182
183 # Find the charm directory
184 try:
185 path = os.path.join(self._rift_var_root_dir,
186 'launchpad/packages/vnfd',
187 self._project.name,
188 agent_vnfr.vnfr_msg.vnfd.id,
189 'charms',
190 charm)
191 self._log.debug("jujuCA: Charm dir is {}".format(path))
192 if not os.path.isdir(path):
193 msg = "jujuCA: Did not find the charm directory at {}".format(path)
194 self._log.error(msg)
195 path = None
196 # Return from here instead of forwarding the config request to juju_api
197 raise Exception(msg)
198 except Exception as e:
199 self.log.exception(e)
200 return False
201
202 if vnf_unique_name not in self._tasks:
203 self._tasks[vnf_unique_name] = {}
204
205 self._log.debug("jujuCA: Deploying service %s",
206 vnf_unique_name)
207 yield from self.api.deploy_application(
208 charm,
209 vnf_unique_name,
210 path=path,
211 )
212 return True
213
214 @asyncio.coroutine
215 def notify_instantiate_vnfr(self, agent_nsr, agent_vnfr):
216 """
217 Notification of Instantiate NSR with the passed nsr id
218 """
219 return True
220
221 @asyncio.coroutine
222 def notify_instantiate_vlr(self, agent_nsr, agent_vnfr, vlr):
223 """
224 Notification of Instantiate NSR with the passed nsr id
225 """
226 return True
227
228 @asyncio.coroutine
229 def notify_terminate_nsr(self, agent_nsr, agent_vnfr):
230 """
231 Notification of Terminate the network service
232 """
233 return True
234
235 @asyncio.coroutine
236 def notify_terminate_vnfr(self, agent_nsr, agent_vnfr):
237 """
238 Notification of Terminate the network service
239 """
240 self._log.debug("jujuCA: Terminate VNFr {}, current vnfrs={}".
241 format(agent_vnfr.name, self._juju_vnfs))
242 try:
243 vnfr = agent_vnfr.vnfr
244 service = vnfr['vnf_juju_name']
245
246 self._log.debug("jujuCA: Terminating VNFr {}, {}".format(
247 agent_vnfr.name,
248 service,
249 ))
250 yield from self.api.remove_application(service)
251
252 del self._juju_vnfs[agent_vnfr.id]
253 self._log.debug("jujuCA: current vnfrs={}".
254 format(self._juju_vnfs))
255 if service in self._tasks:
256 tasks = []
257 for action in self._tasks[service].keys():
258 tasks.append(action)
259 del tasks
260 except KeyError as e:
261 self._log.debug ("jujuCA: Terminating charm service for VNFr {}, e={}".
262 format(agent_vnfr.name, e))
263 except Exception as e:
264 self._log.error("jujuCA: Exception terminating charm service for VNFR {}: {}".
265 format(agent_vnfr.name, e))
266
267 return True
268
269 @asyncio.coroutine
270 def notify_terminate_vlr(self, agent_nsr, agent_vnfr, vlr):
271 """
272 Notification of Terminate the virtual link
273 """
274 return True
275
276 @asyncio.coroutine
277 def _vnf_config_primitive(self, nsr_id, vnfr_id, primitive,
278 vnf_config=None, wait=False):
279 self._log.debug("jujuCA: VNF config primitive {} for nsr {}, "
280 "vnfr_id {}".
281 format(primitive, nsr_id, vnfr_id))
282
283 if vnf_config is None:
284 vnfr_msg = yield from self.get_vnfr(vnfr_id)
285 if vnfr_msg is None:
286 msg = "Unable to get VNFR {} through DTS".format(vnfr_id)
287 self._log.error(msg)
288 return 3, msg
289
290 vnf_config = vnfr_msg.vnf_configuration
291 self._log.debug("VNF config= %s", vnf_config.as_dict())
292
293 try:
294 vnfr = self._juju_vnfs[vnfr_id].vnfr
295 service = vnfr['vnf_juju_name']
296 self._log.debug("VNF config %s", vnf_config)
297 configs = vnf_config.config_primitive
298 for config in configs:
299 if config.name == primitive.name:
300 self._log.debug("jujuCA: Found the config primitive %s",
301 config.name)
302 params = {}
303 for parameter in config.parameter:
304 val = None
305 for p in primitive.parameter:
306 if p.name == parameter.name:
307 if p.value:
308 val = self.xlate(p.value, vnfr['tags'])
309 break
310
311 if val is None:
312 val = parameter.default_value
313
314 if val is None:
315 # Check if mandatory parameter
316 if parameter.mandatory:
317 msg = "VNFR {}: Primitive {} called " \
318 "without mandatory parameter {}". \
319 format(vnfr_msg.name, config.name,
320 parameter.name)
321 self._log.error(msg)
322 return 'failed', '', msg
323
324 if val:
325 val = self.convert_value(val, parameter.data_type)
326 params.update({parameter.name: val})
327
328 rc = ''
329 exec_id = ''
330 details = ''
331 if config.name == 'config':
332 exec_id = 'config'
333 if len(params):
334 self._log.debug("jujuCA: applying config with "
335 "params {} for service {}".
336 format(params, service))
337
338 rc = yield from self.api.apply_config(params, application=service)
339
340 if rc:
341 rc = "completed"
342 self._log.debug("jujuCA: applied config {} "
343 "on {}".format(params, service))
344 else:
345 rc = 'failed'
346 details = \
347 'Failed to apply config: {}'.format(params)
348 self._log.error("jujuCA: Error applying "
349 "config {} on service {}".
350 format(params, service))
351 else:
352 self._log.warn("jujuCA: Did not find valid "
353 "parameters for config : {}".
354 format(primitive.parameter))
355 rc = "completed"
356 else:
357 self._log.debug("jujuCA: Execute action {} on "
358 "service {} with params {}".
359 format(config.name, service, params))
360
361 resp = yield from self.api.execute_action(
362 service,
363 config.name,
364 **params,
365 )
366
367 if resp:
368 if 'error' in resp:
369 details = resp['error']['message']
370 else:
371 exec_id = resp['action']['tag']
372 rc = resp['status']
373 if rc == 'failed':
374 details = resp['message']
375
376 self._log.debug("jujuCA: execute action {} on "
377 "service {} returned {}".
378 format(config.name, service, rc))
379 else:
380 self._log.error("jujuCA: error executing action "
381 "{} for {} with {}".
382 format(config.name, service,
383 params))
384 exec_id = ''
385 rc = 'failed'
386 details = "Failed to queue the action"
387 break
388
389 except KeyError as e:
390 msg = "VNF %s does not have config primitives, e=%s", \
391 vnfr_id, e
392 self._log.exception(msg)
393 raise ValueError(msg)
394
395 while wait and (rc in ['pending', 'running']):
396 self._log.debug("JujuCA: action {}, rc {}".
397 format(exec_id, rc))
398 yield from asyncio.sleep(0.2, loop=self._loop)
399 status = yield from self.api.get_action_status(exec_id)
400 rc = status['status']
401
402 return rc, exec_id, details
403
404 @asyncio.coroutine
405 def vnf_config_primitive(self, nsr_id, vnfr_id, primitive, output):
406 try:
407 vnfr = self._juju_vnfs[vnfr_id].vnfr
408 except KeyError:
409 msg = "Did not find VNFR {} in Juju plugin".format(vnfr_id)
410 self._log.debug(msg)
411 return
412
413 output.execution_status = "failed"
414 output.execution_id = ''
415 output.execution_error_details = ''
416
417 rc, exec_id, err = yield from self._vnf_config_primitive(
418 nsr_id,
419 vnfr_id,
420 primitive)
421
422 self._log.debug("VNFR {} primitive {} exec status: {}".
423 format(vnfr_id, primitive.name, rc))
424 output.execution_status = rc
425 output.execution_id = exec_id
426 output.execution_error_details = err
427
428 @asyncio.coroutine
429 def apply_config(self, agent_nsr, agent_vnfr, config, rpc_ip):
430 """ Notification on configuration of an NSR """
431 pass
432
433 @asyncio.coroutine
434 def apply_ns_config(self, agent_nsr, agent_vnfrs, rpc_ip):
435 """
436
437 ###### TBD - This really does not belong here. Looks more like NS level script ####
438 ###### apply_config should be called for a particular VNF only here ###############
439
440 Hook: Runs the user defined script. Feeds all the necessary data
441 for the script thro' yaml file.
442
443 Args:
444 rpc_ip (YangInput_Nsr_ExecNsConfigPrimitive): The input data.
445 nsr (NetworkServiceRecord): Description
446 vnfrs (dict): VNFR ID => VirtualNetworkFunctionRecord
447
448 """
449 def get_meta(agent_nsr):
450 unit_names, initial_params, vnfr_index_map = {}, {}, {}
451
452 for vnfr_id in agent_nsr.vnfr_ids:
453 juju_vnf = self._juju_vnfs[vnfr_id].vnfr
454
455 # Vnfr -> index ref
456 vnfr_index_map[vnfr_id] = juju_vnf['member_vnf_index']
457
458 # Unit name
459 unit_names[vnfr_id] = juju_vnf['vnf_juju_name']
460
461 # Flatten the data for simplicity
462 param_data = {}
463 self._log.debug("Juju Config:%s", juju_vnf['config'])
464 for primitive in juju_vnf['config'].initial_config_primitive:
465 for parameter in primitive.parameter:
466 value = self.xlate(parameter.value, juju_vnf['tags'])
467 param_data[parameter.name] = value
468
469 initial_params[vnfr_id] = param_data
470
471
472 return unit_names, initial_params, vnfr_index_map
473
474 unit_names, init_data, vnfr_index_map = get_meta(agent_nsr)
475
476 # The data consists of 4 sections
477 # 1. Account data
478 # 2. The input passed.
479 # 3. Juju unit names (keyed by vnfr ID).
480 # 4. Initial config data (keyed by vnfr ID).
481 data = dict()
482 data['config_agent'] = dict(
483 name=self._name,
484 host=self._ip_address,
485 port=self._port,
486 user=self._user,
487 secret=self._secret
488 )
489 data["rpc_ip"] = rpc_ip.as_dict()
490 data["unit_names"] = unit_names
491 data["init_config"] = init_data
492 data["vnfr_index_map"] = vnfr_index_map
493
494 tmp_file = None
495 with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
496 tmp_file.write(yaml.dump(data, default_flow_style=True)
497 .encode("UTF-8"))
498
499 self._log.debug("jujuCA: Creating a temp file: {} with input data".format(
500 tmp_file.name))
501
502 # Get the full path to the script
503 script = ''
504 if rpc_ip.user_defined_script[0] == '/':
505 # The script has full path, use as is
506 script = rpc_ip.user_defined_script
507 else:
508 script = os.path.join(self._rift_var_root_dir, 'launchpad/nsd',
509 self._project.name,
510 agent_nsr.id, 'scripts',
511 rpc_ip.user_defined_script)
512 self.log.debug("jujuCA: Checking for script in %s", script)
513 if not os.path.exists(script):
514 script = os.path.join(self._rift_install_dir, 'usr/bin', rpc_ip.user_defined_script)
515
516 cmd = "{} {}".format(rpc_ip.user_defined_script, tmp_file.name)
517 self._log.debug("jujuCA: Running the CMD: {}".format(cmd))
518
519 coro = asyncio.create_subprocess_shell(cmd, loop=self._loop,
520 stderr=asyncio.subprocess.PIPE)
521 process = yield from coro
522 err = yield from process.stderr.read()
523 task = self._loop.create_task(process.wait())
524
525 return task, err
526
527 @asyncio.coroutine
528 def apply_initial_config(self, agent_nsr, agent_vnfr):
529 """
530 Apply the initial configuration
531 Expect config directives mostly, not actions
532 Actions in initial config may not work based on charm design
533 """
534
535 try:
536 vnfr = self._juju_vnfs[agent_vnfr.id].vnfr
537 service = vnfr['vnf_juju_name']
538 except KeyError:
539 self._log.debug("Did not find VNFR %s in Juju plugin",
540 agent_vnfr.name)
541 return False
542
543 vnfr_msg = yield from self.get_vnfr(agent_vnfr.id)
544 if vnfr_msg is None:
545 msg = "Unable to get VNFR {} ({}) through DTS". \
546 format(agent_vnfr.id, agent_vnfr.name)
547 self._log.error(msg)
548 raise RuntimeError(msg)
549
550 vnf_config = vnfr_msg.vnf_configuration
551 self._log.debug("VNFR %s config: %s", vnfr_msg.name,
552 vnf_config.as_dict())
553
554 # Sort the primitive based on the sequence number
555 primitives = sorted(vnf_config.initial_config_primitive,
556 key=lambda k: k.seq)
557 if not primitives:
558 self._log.debug("VNFR {}: No initial-config-primitive specified".
559 format(vnfr_msg.name))
560 return True
561
562 rc = yield from self.api.is_application_up(application=service)
563 if not rc:
564 return False
565
566 try:
567 if vnfr_msg.mgmt_interface.ip_address:
568 vnfr['tags'].update({'rw_mgmt_ip': vnfr_msg.mgmt_interface.ip_address})
569 self._log.debug("jujuCA:(%s) tags: %s", vnfr['vnf_juju_name'], vnfr['tags'])
570
571 for primitive in primitives:
572 self._log.debug("(%s) Initial config primitive %s",
573 vnfr['vnf_juju_name'], primitive.as_dict())
574 if primitive.config_primitive_ref:
575 # Reference to a primitive in config primitive
576 class Primitive:
577 def __init__(self, name):
578 self.name = name
579 self.value = None
580 self.parameter = []
581
582 prim = Primitive(primitive.config_primitive_ref)
583 rc, eid, err = yield from self._vnf_config_primitive(
584 agent_nsr.id,
585 agent_vnfr.id,
586 prim,
587 vnf_config,
588 wait=True)
589
590 if rc == "failed":
591 msg = "Error executing initial config primitive" \
592 " {} in VNFR {}: rc={}, stderr={}". \
593 format(prim.name, vnfr_msg.name, rc, err)
594 self._log.error(msg)
595 return False
596
597 elif primitive.name:
598 config = {}
599 if primitive.name == 'config':
600 for param in primitive.parameter:
601 if vnfr['tags']:
602 val = self.xlate(param.value,
603 vnfr['tags'])
604 config.update({param.name: val})
605
606 if config:
607 self.juju_log('info', vnfr['vnf_juju_name'],
608 "Applying Initial config:%s",
609 config)
610
611 rc = yield from self.api.apply_config(
612 config,
613 application=service,
614 )
615 if rc is False:
616 self.log.error("Service {} is in error state".format(service))
617 return False
618 else:
619 # Apply any actions specified as part of initial config
620 for primitive in vnfr['config'].initial_config_primitive:
621 if primitive.name != 'config':
622 self._log.debug("jujuCA:(%s) Initial config action primitive %s",
623 vnfr['vnf_juju_name'], primitive)
624 action = primitive.name
625 params = {}
626 for param in primitive.parameter:
627 val = self.xlate(param.value, vnfr['tags'])
628 params.update({param.name: val})
629
630 self._log.info("jujuCA:(%s) Action %s with params %s",
631 vnfr['vnf_juju_name'], action, params)
632 self._log.debug("executing action")
633 resp = yield from self.api.execute_action(
634 service,
635 action,
636 **params,
637 )
638 self._log.debug("executed action")
639 if 'error' in resp:
640 self._log.error("Applying initial config on {} failed for {} with {}: {}".
641 format(vnfr['vnf_juju_name'], action, params, resp))
642 return False
643 except KeyError as e:
644 self._log.info("Juju config agent(%s): VNFR %s not managed by Juju",
645 vnfr['vnf_juju_name'], agent_vnfr.id)
646 return False
647 except Exception as e:
648 self._log.exception("jujuCA:(%s) Exception juju "
649 "apply_initial_config for VNFR {}: {}".
650 format(vnfr['vnf_juju_name'],
651 agent_vnfr.id, e))
652 return False
653
654 return True
655
656 def add_vnfr_managed(self, agent_vnfr):
657 if agent_vnfr.id not in self._juju_vnfs.keys():
658 self._log.info("juju config agent: add vnfr={}/{}".
659 format(agent_vnfr.name, agent_vnfr.id))
660 self._juju_vnfs[agent_vnfr.id] = agent_vnfr
661
662 def is_vnfr_managed(self, vnfr_id):
663 try:
664 if vnfr_id in self._juju_vnfs:
665 return True
666 except Exception as e:
667 self._log.debug("jujuCA: Is VNFR {} managed: {}".
668 format(vnfr_id, e))
669 return False
670
671 @asyncio.coroutine
672 def is_configured(self, vnfr_id):
673 try:
674 agent_vnfr = self._juju_vnfs[vnfr_id]
675 vnfr = agent_vnfr.vnfr
676 if vnfr['active']:
677 return True
678
679 vnfr = self._juju_vnfs[vnfr_id].vnfr
680 service = vnfr['vnf_juju_name']
681 resp = self.api.is_application_active(application=service)
682 self._juju_vnfs[vnfr_id]['active'] = resp
683 self._log.debug("jujuCA: Service state for {} is {}".
684 format(service, resp))
685 return resp
686
687 except KeyError:
688 self._log.debug("jujuCA: VNFR id {} not found in config agent".
689 format(vnfr_id))
690 return False
691 except Exception as e:
692 self._log.error("jujuCA: VNFR id {} is_configured: {}".
693 format(vnfr_id, e))
694 return False
695
696 @asyncio.coroutine
697 def get_config_status(self, agent_nsr, agent_vnfr):
698 """Get the configuration status for the VNF"""
699 rc = 'unknown'
700
701 try:
702 vnfr = agent_vnfr.vnfr
703 service = vnfr['vnf_juju_name']
704 except KeyError:
705 # This VNF is not managed by Juju
706 return rc
707
708 rc = 'configuring'
709
710 try:
711 # Get the status of the application
712 resp = yield from self.api.get_application_status(service)
713
714 # No status means the application is still pending deployment
715 if resp is None:
716 return rc
717
718 if resp == 'error':
719 return 'error'
720 if resp == 'active':
721 return 'configured'
722 except KeyError:
723 self._log.error("jujuCA: Check unknown service %s status", service)
724 except Exception as e:
725 self._log.error("jujuCA: Caught exception when checking for service is active: %s", e)
726 self._log.exception(e)
727
728 return rc
729
730 def get_action_status(self, execution_id):
731 ''' Get the action status for an execution ID
732 *** Make sure this is NOT a asyncio coroutine function ***
733 '''
734
735 try:
736 self._log.debug("jujuCA: Get action status for {}".format(execution_id))
737 resp = self.api._get_action_status(execution_id)
738 self._log.debug("jujuCA: Action status: {}".format(resp))
739 return resp
740 except Exception as e:
741 self._log.error("jujuCA: Error fetching execution status for %s",
742 execution_id)
743 self._log.exception(e)
744 raise e
745
746 def get_service_status(self, vnfr_id):
747 '''Get the service status, used by job status handle
748 Make sure this is NOT a coroutine
749 '''
750 service = self.get_service_name(vnfr_id)
751 if service is None:
752 self._log.error("jujuCA: VNFR {} not managed by this Juju agent".
753 format(vnfr_id))
754 return None
755
756 # Delay for 3 seconds before checking as config apply takes a
757 # few seconds to transfer to the service
758 time.sleep(3)
759 return self.api._get_service_status(service=service)