-#
+#
# Copyright 2016 RIFT.IO Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
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():
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
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. #
'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',
+ path = os.path.join(self._rift_var_root_dir,
+ 'launchpad/packages/vnfd',
+ self._project.name,
agent_vnfr.vnfr_msg.vnfd.id,
- 'charms/trusty',
+ '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
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
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 {}: {}".
"""
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:
+ vnfr = self._juju_vnfs[vnfr_id].vnfr
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_id, 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):
# 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):
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):
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))
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'