Fix potential race condition
[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 self._log.error("jujuCA: Did not find the charm directory at {}".format(path))
194 path = None
195 except Exception as e:
196 self.log.exception(e)
197 return False
198
199 if vnf_unique_name not in self._tasks:
200 self._tasks[vnf_unique_name] = {}
201
202 self._tasks[vnf_unique_name]['deploy'] = self.loop.create_task(
203 self.api.deploy_application(charm, vnf_unique_name, path=path))
204
205 self._log.debug("jujuCA: Deploying service %s",
206 vnf_unique_name)
207
208 return True
209
210 @asyncio.coroutine
211 def notify_instantiate_vnfr(self, agent_nsr, agent_vnfr):
212 """
213 Notification of Instantiate NSR with the passed nsr id
214 """
215 return True
216
217 @asyncio.coroutine
218 def notify_instantiate_vlr(self, agent_nsr, agent_vnfr, vlr):
219 """
220 Notification of Instantiate NSR with the passed nsr id
221 """
222 return True
223
224 @asyncio.coroutine
225 def notify_terminate_nsr(self, agent_nsr, agent_vnfr):
226 """
227 Notification of Terminate the network service
228 """
229 return True
230
231 @asyncio.coroutine
232 def notify_terminate_vnfr(self, agent_nsr, agent_vnfr):
233 """
234 Notification of Terminate the network service
235 """
236 self._log.debug("jujuCA: Terminate VNFr {}, current vnfrs={}".
237 format(agent_vnfr.name, self._juju_vnfs))
238 try:
239 vnfr = agent_vnfr.vnfr
240 service = vnfr['vnf_juju_name']
241
242 self._log.debug ("jujuCA: Terminating VNFr %s, %s",
243 agent_vnfr.name, service)
244 self._tasks[service]['destroy'] = self.loop.create_task(
245 self.api.remove_application(service)
246 )
247
248 del self._juju_vnfs[agent_vnfr.id]
249 self._log.debug ("jujuCA: current vnfrs={}".
250 format(self._juju_vnfs))
251 if service in self._tasks:
252 tasks = []
253 for action in self._tasks[service].keys():
254 tasks.append(action)
255 del tasks
256 except KeyError as e:
257 self._log.debug ("jujuCA: Terminating charm service for VNFr {}, e={}".
258 format(agent_vnfr.name, e))
259 except Exception as e:
260 self._log.error("jujuCA: Exception terminating charm service for VNFR {}: {}".
261 format(agent_vnfr.name, e))
262
263 return True
264
265 @asyncio.coroutine
266 def notify_terminate_vlr(self, agent_nsr, agent_vnfr, vlr):
267 """
268 Notification of Terminate the virtual link
269 """
270 return True
271
272 @asyncio.coroutine
273 def _vnf_config_primitive(self, nsr_id, vnfr_id, primitive,
274 vnf_config=None, wait=False):
275 self._log.debug("jujuCA: VNF config primitive {} for nsr {}, "
276 "vnfr_id {}".
277 format(primitive, nsr_id, vnfr_id))
278
279 if vnf_config is None:
280 vnfr_msg = yield from self.get_vnfr(vnfr_id)
281 if vnfr_msg is None:
282 msg = "Unable to get VNFR {} through DTS".format(vnfr_id)
283 self._log.error(msg)
284 return 3, msg
285
286 vnf_config = vnfr_msg.vnf_configuration
287 self._log.debug("VNF config= %s", vnf_config.as_dict())
288
289 try:
290 service = vnfr['vnf_juju_name']
291 self._log.debug("VNF config %s", vnf_config)
292 configs = vnf_config.config_primitive
293 for config in configs:
294 if config.name == primitive.name:
295 self._log.debug("jujuCA: Found the config primitive %s",
296 config.name)
297 params = {}
298 for parameter in config.parameter:
299 val = None
300 for p in primitive.parameter:
301 if p.name == parameter.name:
302 if p.value:
303 val = self.xlate(p.value, vnfr['tags'])
304 break
305
306 if val is None:
307 val = parameter.default_value
308
309 if val is None:
310 # Check if mandatory parameter
311 if parameter.mandatory:
312 msg = "VNFR {}: Primitive {} called " \
313 "without mandatory parameter {}". \
314 format(vnfr_msg.name, config.name,
315 parameter.name)
316 self._log.error(msg)
317 return 'failed', '', msg
318
319 if val:
320 val = self.convert_value(val, parameter.data_type)
321 params.update({parameter.name: val})
322
323 rc = ''
324 exec_id = ''
325 details = ''
326 if config.name == 'config':
327 exec_id = 'config'
328 if len(params):
329 self._log.debug("jujuCA: applying config with "
330 "params {} for service {}".
331 format(params, service))
332
333 rc = yield from self.api.apply_config(params, application=service)
334
335 if rc:
336 rc = "completed"
337 self._log.debug("jujuCA: applied config {} "
338 "on {}".format(params, service))
339 else:
340 rc = 'failed'
341 details = \
342 'Failed to apply config: {}'.format(params)
343 self._log.error("jujuCA: Error applying "
344 "config {} on service {}".
345 format(params, service))
346 else:
347 self._log.warn("jujuCA: Did not find valid "
348 "parameters for config : {}".
349 format(primitive.parameter))
350 rc = "completed"
351 else:
352 self._log.debug("jujuCA: Execute action {} on "
353 "service {} with params {}".
354 format(config.name, service, params))
355
356 resp = yield from self.api.execute_action(
357 service,
358 config.name,
359 **params,
360 )
361
362 if resp:
363 if 'error' in resp:
364 details = resp['error']['message']
365 else:
366 exec_id = resp['action']['tag']
367 rc = resp['status']
368 if rc == 'failed':
369 details = resp['message']
370
371 self._log.debug("jujuCA: execute action {} on "
372 "service {} returned {}".
373 format(config.name, service, rc))
374 else:
375 self._log.error("jujuCA: error executing action "
376 "{} for {} with {}".
377 format(config.name, service,
378 params))
379 exec_id = ''
380 rc = 'failed'
381 details = "Failed to queue the action"
382 break
383
384 except KeyError as e:
385 msg = "VNF %s does not have config primitives, e=%s", \
386 vnfr_id, e
387 self._log.exception(msg)
388 raise ValueError(msg)
389
390 while wait and (rc in ['pending', 'running']):
391 self._log.debug("JujuCA: action {}, rc {}".
392 format(exec_id, rc))
393 yield from asyncio.sleep(0.2, loop=self._loop)
394 status = yield from self.api.get_action_status(exec_id)
395 rc = status['status']
396
397 return rc, exec_id, details
398
399 @asyncio.coroutine
400 def vnf_config_primitive(self, nsr_id, vnfr_id, primitive, output):
401 try:
402 vnfr = self._juju_vnfs[vnfr_id].vnfr
403 except KeyError:
404 msg = "Did not find VNFR {} in Juju plugin".format(vnfr_id)
405 self._log.debug(msg)
406 return
407
408 output.execution_status = "failed"
409 output.execution_id = ''
410 output.execution_error_details = ''
411
412 rc, exec_id, err = yield from self._vnf_config_primitive(
413 nsr_id,
414 vnfr_id,
415 primitive)
416
417 self._log.debug("VNFR {} primitive {} exec status: {}".
418 format(vnfr.name, primitive.name, rc))
419 output.execution_status = rc
420 output.execution_id = exec_id
421 output.execution_error_details = err
422
423 @asyncio.coroutine
424 def apply_config(self, agent_nsr, agent_vnfr, config, rpc_ip):
425 """ Notification on configuration of an NSR """
426 pass
427
428 @asyncio.coroutine
429 def apply_ns_config(self, agent_nsr, agent_vnfrs, rpc_ip):
430 """
431
432 ###### TBD - This really does not belong here. Looks more like NS level script ####
433 ###### apply_config should be called for a particular VNF only here ###############
434
435 Hook: Runs the user defined script. Feeds all the necessary data
436 for the script thro' yaml file.
437
438 Args:
439 rpc_ip (YangInput_Nsr_ExecNsConfigPrimitive): The input data.
440 nsr (NetworkServiceRecord): Description
441 vnfrs (dict): VNFR ID => VirtualNetworkFunctionRecord
442
443 """
444 def get_meta(agent_nsr):
445 unit_names, initial_params, vnfr_index_map = {}, {}, {}
446
447 for vnfr_id in agent_nsr.vnfr_ids:
448 juju_vnf = self._juju_vnfs[vnfr_id].vnfr
449
450 # Vnfr -> index ref
451 vnfr_index_map[vnfr_id] = juju_vnf['member_vnf_index']
452
453 # Unit name
454 unit_names[vnfr_id] = juju_vnf['vnf_juju_name']
455
456 # Flatten the data for simplicity
457 param_data = {}
458 self._log.debug("Juju Config:%s", juju_vnf['config'])
459 for primitive in juju_vnf['config'].initial_config_primitive:
460 for parameter in primitive.parameter:
461 value = self.xlate(parameter.value, juju_vnf['tags'])
462 param_data[parameter.name] = value
463
464 initial_params[vnfr_id] = param_data
465
466
467 return unit_names, initial_params, vnfr_index_map
468
469 unit_names, init_data, vnfr_index_map = get_meta(agent_nsr)
470
471 # The data consists of 4 sections
472 # 1. Account data
473 # 2. The input passed.
474 # 3. Juju unit names (keyed by vnfr ID).
475 # 4. Initial config data (keyed by vnfr ID).
476 data = dict()
477 data['config_agent'] = dict(
478 name=self._name,
479 host=self._ip_address,
480 port=self._port,
481 user=self._user,
482 secret=self._secret
483 )
484 data["rpc_ip"] = rpc_ip.as_dict()
485 data["unit_names"] = unit_names
486 data["init_config"] = init_data
487 data["vnfr_index_map"] = vnfr_index_map
488
489 tmp_file = None
490 with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
491 tmp_file.write(yaml.dump(data, default_flow_style=True)
492 .encode("UTF-8"))
493
494 self._log.debug("jujuCA: Creating a temp file: {} with input data".format(
495 tmp_file.name))
496
497 # Get the full path to the script
498 script = ''
499 if rpc_ip.user_defined_script[0] == '/':
500 # The script has full path, use as is
501 script = rpc_ip.user_defined_script
502 else:
503 script = os.path.join(self._rift_var_root_dir, 'launchpad/nsd',
504 self._project.name,
505 agent_nsr.id, 'scripts',
506 rpc_ip.user_defined_script)
507 self.log.debug("jujuCA: Checking for script in %s", script)
508 if not os.path.exists(script):
509 script = os.path.join(self._rift_install_dir, 'usr/bin', rpc_ip.user_defined_script)
510
511 cmd = "{} {}".format(rpc_ip.user_defined_script, tmp_file.name)
512 self._log.debug("jujuCA: Running the CMD: {}".format(cmd))
513
514 coro = asyncio.create_subprocess_shell(cmd, loop=self._loop,
515 stderr=asyncio.subprocess.PIPE)
516 process = yield from coro
517 err = yield from process.stderr.read()
518 task = self._loop.create_task(process.wait())
519
520 return task, err
521
522 @asyncio.coroutine
523 def apply_initial_config(self, agent_nsr, agent_vnfr):
524 """
525 Apply the initial configuration
526 Expect config directives mostly, not actions
527 Actions in initial config may not work based on charm design
528 """
529
530 try:
531 vnfr = self._juju_vnfs[agent_vnfr.id].vnfr
532 service = vnfr['vnf_juju_name']
533 except KeyError:
534 self._log.debug("Did not find VNFR %s in Juju plugin",
535 agent_vnfr.name)
536 return False
537
538 vnfr_msg = yield from self.get_vnfr(agent_vnfr.id)
539 if vnfr_msg is None:
540 msg = "Unable to get VNFR {} ({}) through DTS". \
541 format(agent_vnfr.id, agent_vnfr.name)
542 self._log.error(msg)
543 raise RuntimeError(msg)
544
545 vnf_config = vnfr_msg.vnf_configuration
546 self._log.debug("VNFR %s config: %s", vnfr_msg.name,
547 vnf_config.as_dict())
548
549 # Sort the primitive based on the sequence number
550 primitives = sorted(vnf_config.initial_config_primitive,
551 key=lambda k: k.seq)
552 if not primitives:
553 self._log.debug("VNFR {}: No initial-config-primitive specified".
554 format(vnfr_msg.name))
555 return True
556
557 rc = yield from self.api.is_application_up(application=service)
558 if not rc:
559 return False
560
561 try:
562 if vnfr_msg.mgmt_interface.ip_address:
563 vnfr['tags'].update({'rw_mgmt_ip': vnfr_msg.mgmt_interface.ip_address})
564 self._log.debug("jujuCA:(%s) tags: %s", vnfr['vnf_juju_name'], vnfr['tags'])
565
566 for primitive in primitives:
567 self._log.debug("(%s) Initial config primitive %s",
568 vnfr['vnf_juju_name'], primitive.as_dict())
569 if primitive.config_primitive_ref:
570 # Reference to a primitive in config primitive
571 class Primitive:
572 def __init__(self, name):
573 self.name = name
574 self.value = None
575 self.parameter = []
576
577 prim = Primitive(primitive.config_primitive_ref)
578 rc, eid, err = yield from self._vnf_config_primitive(
579 agent_nsr.id,
580 agent_vnfr.id,
581 prim,
582 vnf_config,
583 wait=True)
584
585 if rc == "failed":
586 msg = "Error executing initial config primitive" \
587 " {} in VNFR {}: rc={}, stderr={}". \
588 format(prim.name, vnfr_msg.name, rc, err)
589 self._log.error(msg)
590 return False
591
592 elif primitive.name:
593 config = {}
594 if primitive.name == 'config':
595 for param in primitive.parameter:
596 if vnfr['tags']:
597 val = self.xlate(param.value,
598 vnfr['tags'])
599 config.update({param.name: val})
600
601 if config:
602 self.juju_log('info', vnfr['vnf_juju_name'],
603 "Applying Initial config:%s",
604 config)
605
606 rc = yield from self.api.apply_config(
607 config,
608 application=service,
609 )
610 if rc is False:
611 self.log.error("Service {} is in error state".format(service))
612 return False
613 else:
614 # Apply any actions specified as part of initial config
615 for primitive in vnfr['config'].initial_config_primitive:
616 if primitive.name != 'config':
617 self._log.debug("jujuCA:(%s) Initial config action primitive %s",
618 vnfr['vnf_juju_name'], primitive)
619 action = primitive.name
620 params = {}
621 for param in primitive.parameter:
622 val = self.xlate(param.value, vnfr['tags'])
623 params.update({param.name: val})
624
625 self._log.info("jujuCA:(%s) Action %s with params %s",
626 vnfr['vnf_juju_name'], action, params)
627 self._log.debug("executing action")
628 resp = yield from self.api.execute_action(
629 service,
630 action,
631 **params,
632 )
633 self._log.debug("executed action")
634 if 'error' in resp:
635 self._log.error("Applying initial config on {} failed for {} with {}: {}".
636 format(vnfr['vnf_juju_name'], action, params, resp))
637 return False
638 except KeyError as e:
639 self._log.info("Juju config agent(%s): VNFR %s not managed by Juju",
640 vnfr['vnf_juju_name'], agent_vnfr.id)
641 return False
642 except Exception as e:
643 self._log.exception("jujuCA:(%s) Exception juju "
644 "apply_initial_config for VNFR {}: {}".
645 format(vnfr['vnf_juju_name'],
646 agent_vnfr.id, e))
647 return False
648
649 return True
650
651 def add_vnfr_managed(self, agent_vnfr):
652 if agent_vnfr.id not in self._juju_vnfs.keys():
653 self._log.info("juju config agent: add vnfr={}/{}".
654 format(agent_vnfr.name, agent_vnfr.id))
655 self._juju_vnfs[agent_vnfr.id] = agent_vnfr
656
657 def is_vnfr_managed(self, vnfr_id):
658 try:
659 if vnfr_id in self._juju_vnfs:
660 return True
661 except Exception as e:
662 self._log.debug("jujuCA: Is VNFR {} managed: {}".
663 format(vnfr_id, e))
664 return False
665
666 @asyncio.coroutine
667 def is_configured(self, vnfr_id):
668 try:
669 agent_vnfr = self._juju_vnfs[vnfr_id]
670 vnfr = agent_vnfr.vnfr
671 if vnfr['active']:
672 return True
673
674 vnfr = self._juju_vnfs[vnfr_id].vnfr
675 service = vnfr['vnf_juju_name']
676 resp = self.api.is_application_active(application=service)
677 self._juju_vnfs[vnfr_id]['active'] = resp
678 self._log.debug("jujuCA: Service state for {} is {}".
679 format(service, resp))
680 return resp
681
682 except KeyError:
683 self._log.debug("jujuCA: VNFR id {} not found in config agent".
684 format(vnfr_id))
685 return False
686 except Exception as e:
687 self._log.error("jujuCA: VNFR id {} is_configured: {}".
688 format(vnfr_id, e))
689 return False
690
691 @asyncio.coroutine
692 def get_config_status(self, agent_nsr, agent_vnfr):
693 """Get the configuration status for the VNF"""
694 rc = 'unknown'
695
696 try:
697 vnfr = agent_vnfr.vnfr
698 service = vnfr['vnf_juju_name']
699 except KeyError:
700 # This VNF is not managed by Juju
701 return rc
702
703 rc = 'configuring'
704
705 try:
706 # Get the status of the application
707 resp = yield from self.api.get_application_status(service)
708
709 # No status means the application is still pending deployment
710 if resp is None:
711 return rc
712
713 if resp == 'error':
714 return 'error'
715 if resp == 'active':
716 return 'configured'
717 except KeyError:
718 self._log.error("jujuCA: Check unknown service %s status", service)
719 except Exception as e:
720 self._log.error("jujuCA: Caught exception when checking for service is active: %s", e)
721 self._log.exception(e)
722
723 return rc
724
725 def get_action_status(self, execution_id):
726 ''' Get the action status for an execution ID
727 *** Make sure this is NOT a asyncio coroutine function ***
728 '''
729
730 try:
731 self._log.debug("jujuCA: Get action status for {}".format(execution_id))
732 resp = self.api._get_action_status(execution_id)
733 self._log.debug("jujuCA: Action status: {}".format(resp))
734 return resp
735 except Exception as e:
736 self._log.error("jujuCA: Error fetching execution status for %s",
737 execution_id)
738 self._log.exception(e)
739 raise e
740
741 def get_service_status(self, vnfr_id):
742 '''Get the service status, used by job status handle
743 Make sure this is NOT a coroutine
744 '''
745 service = self.get_service_name(vnfr_id)
746 if service is None:
747 self._log.error("jujuCA: VNFR {} not managed by this Juju agent".
748 format(vnfr_id))
749 return None
750
751 # Delay for 3 seconds before checking as config apply takes a
752 # few seconds to transfer to the service
753 time.sleep(3)
754 return self.api._get_service_status(service=service)