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