X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=rwcm%2Fplugins%2Frwconman%2Frift%2Ftasklets%2Frwconmantasklet%2Fjujuconf.py;h=b003df8da86707b8cf163e237413ecd702e40696;hb=07c745714bdf2a2c1e69001cf4faf6a83cecc293;hp=dce31c3eef2242d9bb720ecf08d8c1fead015f29;hpb=255ff03a528a3090ce7f46f0a63b65da3e6f9bcf;p=osm%2FSO.git diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/jujuconf.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/jujuconf.py index dce31c3e..b003df8d 100644 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/jujuconf.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/jujuconf.py @@ -1,4 +1,4 @@ -# +# # Copyright 2016 RIFT.IO Inc # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,9 +25,10 @@ import rift.mano.utils.juju_api as juju from . import riftcm_config_plugin -# Charm service name accepts only a to z and -. -def get_vnf_unique_name(nsr_name, vnfr_short_name, member_vnf_index): - name = "{}-{}-{}".format(nsr_name, vnfr_short_name, member_vnf_index) +def get_vnf_unique_name(nsr_name, vnfr_name, member_vnf_index): + """Get the unique VNF name. + Charm names accepts only a to z and non-consecutive - characters.""" + name = "{}-{}-{}".format(nsr_name, vnfr_name, member_vnf_index) new_name = '' for c in name: if c.isdigit(): @@ -35,15 +36,16 @@ def get_vnf_unique_name(nsr_name, vnfr_short_name, member_vnf_index): elif not c.isalpha(): c = "-" new_name += c - return new_name.lower() + return re.sub('\-+', '-', new_name.lower()) class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): """ Juju implementation of the riftcm_config_plugin.RiftCMConfigPluginBase """ - def __init__(self, dts, log, loop, account): - riftcm_config_plugin.RiftCMConfigPluginBase.__init__(self, dts, log, loop, account) + def __init__(self, dts, log, loop, project, account): + riftcm_config_plugin.RiftCMConfigPluginBase.__init__(self, dts, log, loop, + project, account) self._name = account.name self._type = 'juju' self._ip_address = account.juju.ip_address @@ -51,7 +53,7 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): self._user = account.juju.user self._secret = account.juju.secret self._rift_install_dir = os.environ['RIFT_INSTALL'] - self._rift_artif_dir = os.environ['RIFT_ARTIFACTS'] + self._rift_var_root_dir = os.environ['RIFT_VAR_ROOT'] ############################################################ # This is wrongfully overloaded with 'juju' private data. # @@ -174,22 +176,25 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): 'tags': {}, 'active': False, 'config': vnf_config, - 'vnfr_name' : agent_vnfr.name}) + 'vnfr_name': agent_vnfr.name}) self._log.debug("jujuCA: Charm %s for vnf %s to be deployed as %s", charm, agent_vnfr.name, vnf_unique_name) # Find the charm directory try: - path = os.path.join(self._rift_artif_dir, - 'launchpad/libs', - agent_vnfr.vnfr_msg.vnfd_ref, - 'charms/trusty', + path = os.path.join(self._rift_var_root_dir, + 'launchpad/packages/vnfd', + self._project.name, + agent_vnfr.vnfr_msg.vnfd.id, + 'charms', charm) self._log.debug("jujuCA: Charm dir is {}".format(path)) if not os.path.isdir(path): - self._log.error("jujuCA: Did not find the charm directory at {}". - format(path)) + msg = "jujuCA: Did not find the charm directory at {}".format(path) + self._log.error(msg) path = None + # Return from here instead of forwarding the config request to juju_api + raise Exception(msg) except Exception as e: self.log.exception(e) return False @@ -197,12 +202,13 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): if vnf_unique_name not in self._tasks: self._tasks[vnf_unique_name] = {} - self._tasks[vnf_unique_name]['deploy'] = self.loop.create_task( - self.api.deploy_service(charm, vnf_unique_name, path=path)) - self._log.debug("jujuCA: Deploying service %s", vnf_unique_name) - + yield from self.api.deploy_application( + charm, + vnf_unique_name, + path=path, + ) return True @asyncio.coroutine @@ -237,23 +243,22 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): vnfr = agent_vnfr.vnfr service = vnfr['vnf_juju_name'] - self._log.debug ("jujuCA: Terminating VNFr %s, %s", - agent_vnfr.name, service) - self._tasks[service]['destroy'] = self.loop.create_task( - self.api.destroy_service(service) - ) + self._log.debug("jujuCA: Terminating VNFr {}, {}".format( + agent_vnfr.name, + service, + )) + yield from self.api.remove_application(service) del self._juju_vnfs[agent_vnfr.id] - self._log.debug ("jujuCA: current vnfrs={}". - format(self._juju_vnfs)) + self._log.debug("jujuCA: current vnfrs={}". + format(self._juju_vnfs)) if service in self._tasks: tasks = [] for action in self._tasks[service].keys(): - #if self.check_task_status(service, action): tasks.append(action) del tasks except KeyError as e: - self._log.debug ("jujuCA: Termiating charm service for VNFr {}, e={}". + self._log.debug ("jujuCA: Terminating charm service for VNFr {}, e={}". format(agent_vnfr.name, e)) except Exception as e: self._log.error("jujuCA: Exception terminating charm service for VNFR {}: {}". @@ -268,129 +273,156 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): """ return True - def check_task_status(self, service, action): - #self.log.debug("jujuCA: check task status for %s, %s" % (service, action)) - try: - task = self._tasks[service][action] - if task.done(): - self.log.debug("jujuCA: Task for %s, %s done" % (service, action)) - e = task.exception() - if e: - self.log.error("jujuCA: Error in task for {} and {} : {}". - format(service, action, e)) - raise Exception(e) - r= task.result() - if r: - self.log.debug("jujuCA: Task for {} and {}, returned {}". - format(service, action,r)) - return True - else: - self.log.debug("jujuCA: task {}, {} not done". - format(service, action)) - return False - except KeyError as e: - self.log.error("jujuCA: KeyError for task for {} and {}: {}". - format(service, action, e)) - except Exception as e: - self.log.error("jujuCA: Error for task for {} and {}: {}". - format(service, action, e)) - raise - return True - @asyncio.coroutine - def vnf_config_primitive(self, nsr_id, vnfr_id, primitive, output): - self._log.debug("jujuCA: VNF config primititve {} for nsr {}, vnfr_id {}". + def _vnf_config_primitive(self, nsr_id, vnfr_id, primitive, + vnf_config=None, wait=False): + self._log.debug("jujuCA: VNF config primitive {} for nsr {}, " + "vnfr_id {}". format(primitive, nsr_id, vnfr_id)) - try: - vnfr = self._juju_vnfs[vnfr_id].vnfr - except KeyError: - self._log.error("jujuCA: Did not find VNFR %s in juju plugin", - vnfr_id) - return - output.execution_status = "failed" - output.execution_id = '' - output.execution_error_details = '' + if vnf_config is None: + vnfr_msg = yield from self.get_vnfr(vnfr_id) + if vnfr_msg is None: + msg = "Unable to get VNFR {} through DTS".format(vnfr_id) + self._log.error(msg) + return 3, msg + + vnf_config = vnfr_msg.vnf_configuration + self._log.debug("VNF config= %s", vnf_config.as_dict()) try: service = vnfr['vnf_juju_name'] - vnf_config = vnfr['config'] self._log.debug("VNF config %s", vnf_config) - configs = vnf_config.service_primitive + configs = vnf_config.config_primitive for config in configs: if config.name == primitive.name: self._log.debug("jujuCA: Found the config primitive %s", config.name) params = {} - for parameter in primitive.parameter: - if parameter.value: - val = self.xlate(parameter.value, vnfr['tags']) - # TBD do validation of the parameters - data_type = 'string' - found = False - for ca_param in config.parameter: - if ca_param.name == parameter.name: - data_type = ca_param.data_type - found = True - break - if data_type == 'integer': - val = int(parameter.value) - if not found: - self._log.warn("jujuCA: Did not find parameter {} for {}". - format(parameter, config.name)) + for parameter in config.parameter: + val = None + for p in primitive.parameter: + if p.name == parameter.name: + if p.value: + val = self.xlate(p.value, vnfr['tags']) + break + + if val is None: + val = parameter.default_value + + if val is None: + # Check if mandatory parameter + if parameter.mandatory: + msg = "VNFR {}: Primitive {} called " \ + "without mandatory parameter {}". \ + format(vnfr_msg.name, config.name, + parameter.name) + self._log.error(msg) + return 'failed', '', msg + + if val: + val = self.convert_value(val, parameter.data_type) params.update({parameter.name: val}) + rc = '' + exec_id = '' + details = '' if config.name == 'config': - output.execution_id = 'config' + exec_id = 'config' if len(params): - self._log.debug("jujuCA: applying config with params {} for service {}". + self._log.debug("jujuCA: applying config with " + "params {} for service {}". format(params, service)) - rc = yield from self.api.apply_config(params, service=service, wait=False) + rc = yield from self.api.apply_config(params, application=service) if rc: - # Mark as pending and check later for the status - output.execution_status = "pending" - self._log.debug("jujuCA: applied config {} on {}". - format(params, service)) + rc = "completed" + self._log.debug("jujuCA: applied config {} " + "on {}".format(params, service)) else: - output.execution_status = 'failed' - output.execution_error_details = \ + rc = 'failed' + details = \ 'Failed to apply config: {}'.format(params) - self._log.error("jujuCA: Error applying config {} on service {}". + self._log.error("jujuCA: Error applying " + "config {} on service {}". format(params, service)) else: - self._log.warn("jujuCA: Did not find valid parameters for config : {}". + self._log.warn("jujuCA: Did not find valid " + "parameters for config : {}". format(primitive.parameter)) - output.execution_status = "completed" + rc = "completed" else: - self._log.debug("jujuCA: Execute action {} on service {} with params {}". + self._log.debug("jujuCA: Execute action {} on " + "service {} with params {}". format(config.name, service, params)) - resp = yield from self.api.execute_action(config.name, - params, - service=service) + resp = yield from self.api.execute_action( + service, + config.name, + **params, + ) if resp: if 'error' in resp: - output.execution_error_details = resp['error']['Message'] + details = resp['error']['message'] else: - output.execution_id = resp['action']['tag'] - output.execution_status = resp['status'] - if output.execution_status == 'failed': - output.execution_error_details = resp['message'] - self._log.debug("jujuCA: execute action {} on service {} returned {}". - format(config.name, service, output.execution_status)) + exec_id = resp['action']['tag'] + rc = resp['status'] + if rc == 'failed': + details = resp['message'] + + self._log.debug("jujuCA: execute action {} on " + "service {} returned {}". + format(config.name, service, rc)) else: - self._log.error("jujuCA: error executing action {} for {} with {}". - format(config.name, service, params)) - output.execution_id = '' - output.execution_status = 'failed' - output.execution_error_details = "Failed to queue the action" + self._log.error("jujuCA: error executing action " + "{} for {} with {}". + format(config.name, service, + params)) + exec_id = '' + rc = 'failed' + details = "Failed to queue the action" break except KeyError as e: - self._log.info("VNF %s does not have config primititves, e=%s", vnfr_id, e) + msg = "VNF %s does not have config primitives, e=%s", \ + vnfr_id, e + self._log.exception(msg) + raise ValueError(msg) + + while wait and (rc in ['pending', 'running']): + self._log.debug("JujuCA: action {}, rc {}". + format(exec_id, rc)) + yield from asyncio.sleep(0.2, loop=self._loop) + status = yield from self.api.get_action_status(exec_id) + rc = status['status'] + + return rc, exec_id, details + + @asyncio.coroutine + def vnf_config_primitive(self, nsr_id, vnfr_id, primitive, output): + try: + vnfr = self._juju_vnfs[vnfr_id].vnfr + except KeyError: + msg = "Did not find VNFR {} in Juju plugin".format(vnfr_id) + self._log.debug(msg) + return + + output.execution_status = "failed" + output.execution_id = '' + output.execution_error_details = '' + + rc, exec_id, err = yield from self._vnf_config_primitive( + nsr_id, + vnfr_id, + primitive) + + self._log.debug("VNFR {} primitive {} exec status: {}". + format(vnfr.name, primitive.name, rc)) + output.execution_status = rc + output.execution_id = exec_id + output.execution_error_details = err @asyncio.coroutine def apply_config(self, agent_nsr, agent_vnfr, config, rpc_ip): @@ -472,7 +504,9 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): # The script has full path, use as is script = rpc_ip.user_defined_script else: - script = os.path.join(self._rift_artif_dir, 'launchpad/libs', agent_nsr.id, 'scripts', + script = os.path.join(self._rift_var_root_dir, 'launchpad/nsd', + self._project.name, + agent_nsr.id, 'scripts', rpc_ip.user_defined_script) self.log.debug("jujuCA: Checking for script in %s", script) if not os.path.exists(script): @@ -497,95 +531,125 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): Actions in initial config may not work based on charm design """ - vnfr = agent_vnfr.vnfr - service = vnfr['vnf_juju_name'] + try: + vnfr = self._juju_vnfs[agent_vnfr.id].vnfr + service = vnfr['vnf_juju_name'] + except KeyError: + self._log.debug("Did not find VNFR %s in Juju plugin", + agent_vnfr.name) + return False - rc = yield from self.api.is_service_up(service=service) + vnfr_msg = yield from self.get_vnfr(agent_vnfr.id) + if vnfr_msg is None: + msg = "Unable to get VNFR {} ({}) through DTS". \ + format(agent_vnfr.id, agent_vnfr.name) + self._log.error(msg) + raise RuntimeError(msg) + + vnf_config = vnfr_msg.vnf_configuration + self._log.debug("VNFR %s config: %s", vnfr_msg.name, + vnf_config.as_dict()) + + # Sort the primitive based on the sequence number + primitives = sorted(vnf_config.initial_config_primitive, + key=lambda k: k.seq) + if not primitives: + self._log.debug("VNFR {}: No initial-config-primitive specified". + format(vnfr_msg.name)) + return True + + rc = yield from self.api.is_application_up(application=service) if not rc: return False - action_ids = [] try: - vnf_cat = agent_vnfr.vnfr_msg - if vnf_cat and vnf_cat.mgmt_interface.ip_address: - vnfr['tags'].update({'rw_mgmt_ip': vnf_cat.mgmt_interface.ip_address}) + if vnfr_msg.mgmt_interface.ip_address: + vnfr['tags'].update({'rw_mgmt_ip': vnfr_msg.mgmt_interface.ip_address}) self._log.debug("jujuCA:(%s) tags: %s", vnfr['vnf_juju_name'], vnfr['tags']) - config = {} - try: - for primitive in vnfr['config'].initial_config_primitive: - self._log.debug("jujuCA:(%s) Initial config primitive %s", vnfr['vnf_juju_name'], primitive) + for primitive in primitives: + self._log.debug("(%s) Initial config primitive %s", + vnfr['vnf_juju_name'], primitive.as_dict()) + if primitive.config_primitive_ref: + # Reference to a primitive in config primitive + class Primitive: + def __init__(self, name): + self.name = name + self.value = None + self.parameter = [] + + prim = Primitive(primitive.config_primitive_ref) + rc, eid, err = yield from self._vnf_config_primitive( + agent_nsr.id, + agent_vnfr.id, + prim, + vnf_config, + wait=True) + + if rc == "failed": + msg = "Error executing initial config primitive" \ + " {} in VNFR {}: rc={}, stderr={}". \ + format(prim.name, vnfr_msg.name, rc, err) + self._log.error(msg) + return False + + elif primitive.name: + config = {} if primitive.name == 'config': for param in primitive.parameter: if vnfr['tags']: - val = self.xlate(param.value, vnfr['tags']) + val = self.xlate(param.value, + vnfr['tags']) config.update({param.name: val}) - except KeyError as e: - self._log.exception("jujuCA:(%s) Initial config error(%s): config=%s", - vnfr['vnf_juju_name'], str(e), config) - config = None - return False - - if config: - self.juju_log('info', vnfr['vnf_juju_name'], - "Applying Initial config:%s", - config) - - rc = yield from self.api.apply_config(config, service=service) - if rc is False: - self.log.error("Service {} is in error state".format(service)) - return False - - - # Apply any actions specified as part of initial config - for primitive in vnfr['config'].initial_config_primitive: - if primitive.name != 'config': - self._log.debug("jujuCA:(%s) Initial config action primitive %s", - vnfr['vnf_juju_name'], primitive) - action = primitive.name - params = {} - for param in primitive.parameter: - val = self.xlate(param.value, vnfr['tags']) - params.update({param.name: val}) - - self._log.info("jujuCA:(%s) Action %s with params %s", - vnfr['vnf_juju_name'], action, params) - - resp = yield from self.api.execute_action(action, params, - service=service) - if 'error' in resp: - self._log.error("Applying initial config on {} failed for {} with {}: {}". - format(vnfr['vnf_juju_name'], action, params, resp)) - return False - - action_ids.append(resp['action']['tag']) + if config: + self.juju_log('info', vnfr['vnf_juju_name'], + "Applying Initial config:%s", + config) + + rc = yield from self.api.apply_config( + config, + application=service, + ) + if rc is False: + self.log.error("Service {} is in error state".format(service)) + return False + else: + # Apply any actions specified as part of initial config + for primitive in vnfr['config'].initial_config_primitive: + if primitive.name != 'config': + self._log.debug("jujuCA:(%s) Initial config action primitive %s", + vnfr['vnf_juju_name'], primitive) + action = primitive.name + params = {} + for param in primitive.parameter: + val = self.xlate(param.value, vnfr['tags']) + params.update({param.name: val}) + + self._log.info("jujuCA:(%s) Action %s with params %s", + vnfr['vnf_juju_name'], action, params) + self._log.debug("executing action") + resp = yield from self.api.execute_action( + service, + action, + **params, + ) + self._log.debug("executed action") + if 'error' in resp: + self._log.error("Applying initial config on {} failed for {} with {}: {}". + format(vnfr['vnf_juju_name'], action, params, resp)) + return False except KeyError as e: self._log.info("Juju config agent(%s): VNFR %s not managed by Juju", vnfr['vnf_juju_name'], agent_vnfr.id) return False except Exception as e: - self._log.exception("jujuCA:(%s) Exception juju apply_initial_config for VNFR {}: {}". - format(vnfr['vnf_juju_name'], agent_vnfr.id, e)) + self._log.exception("jujuCA:(%s) Exception juju " + "apply_initial_config for VNFR {}: {}". + format(vnfr['vnf_juju_name'], + agent_vnfr.id, e)) return False - # Check if all actions completed - pending = True - while pending: - pending = False - for act in action_ids: - resp = yield from self.api.get_action_status(act) - if 'error' in resp: - self._log.error("Initial config failed: {}".format(resp)) - return False - - if resp['status'] == 'failed': - self._log.error("Initial config action failed: {}".format(resp)) - return False - - if resp['status'] == 'pending': - pending = True - return True def add_vnfr_managed(self, agent_vnfr): @@ -613,7 +677,7 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): vnfr = self._juju_vnfs[vnfr_id].vnfr service = vnfr['vnf_juju_name'] - resp = self.api.is_service_active(service=service) + resp = self.api.is_application_active(application=service) self._juju_vnfs[vnfr_id]['active'] = resp self._log.debug("jujuCA: Service state for {} is {}". format(service, resp)) @@ -642,12 +706,13 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): rc = 'configuring' - if not self.check_task_status(service, 'deploy'): - return rc - try: - resp = yield from self.api.get_service_status(service=service) - self._log.debug("jujuCA: Get service %s status? %s", service, resp) + # Get the status of the application + resp = yield from self.api.get_application_status(service) + + # No status means the application is still pending deployment + if resp is None: + return rc if resp == 'error': return 'error'