except DbException as e:
self.logger.error("Updating nsr_id={}: {}".format(nsr_id, e))
- def n2vc_callback(self, nsd, vnfd, vnf_member_index, workload_status, *args):
+ def n2vc_callback(self, model_name, application_name, workload_status, db_nsr, vnf_member_index, task=None):
"""Update the lcm database with the status of the charm.
Updates the VNF's operational status with the state of the charm:
Updates the network service's config-status to reflect the state of all
charms.
"""
- if workload_status and len(args) == 3:
- # self.logger.debug("[n2vc_callback] Workload status \"{}\"".format(workload_status))
- try:
- (db_nsr, vnf_index, task) = args
-
- nsr_id = db_nsr["_id"]
- nsr_lcm = db_nsr["_admin"]["deploy"]
- nsr_lcm["VCA"][vnf_index]['operational-status'] = workload_status
-
- if task:
- if task.cancelled():
- return
-
- if task.done():
- exc = task.exception()
- if exc:
- nsr_lcm = db_nsr["_admin"]["deploy"]
- nsr_lcm["VCA"][vnf_index]['operational-status'] = "failed"
- db_nsr["detailed-status"] = "fail configuring vnf_index={} {}".format(vnf_index, exc)
- db_nsr["config-status"] = "failed"
- self.update_nsr_db(nsr_id, db_nsr)
- else:
- units = len(nsr_lcm["VCA"])
- active = 0
- statusmap = {}
- for vnf_index in nsr_lcm["VCA"]:
- if 'operational-status' in nsr_lcm["VCA"][vnf_index]:
-
- if nsr_lcm["VCA"][vnf_index]['operational-status'] not in statusmap:
- # Initialize it
- statusmap[nsr_lcm["VCA"][vnf_index]['operational-status']] = 0
-
- statusmap[nsr_lcm["VCA"][vnf_index]['operational-status']] += 1
-
- if nsr_lcm["VCA"][vnf_index]['operational-status'] == "active":
- active += 1
- else:
- self.logger.debug("No operational-status")
-
- cs = ""
- for status in statusmap:
- cs += "{} ({}) ".format(status, statusmap[status])
- db_nsr["config-status"] = cs
- self.update_nsr_db(nsr_id, db_nsr)
- except Exception as e:
- # self.logger.critical("Task create_ns={} n2vc_callback Exception {}".format(nsr_id, e), exc_info=True)
- self.logger.critical("Task create_ns n2vc_callback Exception {}".format(e), exc_info=True)
- pass
+ if not workload_status and not task:
+ self.logger.error("Task create_ns={} n2vc_callback Enter with bad parameters")
+ return
+
+ if not workload_status:
+ self.logger.error("Task create_ns={} n2vc_callback Enter with bad parameters, no workload_status")
+ return
+
+ try:
+ self.logger.debug("[n2vc_callback] Workload status \"{}\"".format(workload_status))
+ nsr_id = db_nsr["_id"]
+ nsr_lcm = db_nsr["_admin"]["deploy"]
+ nsr_lcm["VCA"][vnf_member_index]['operational-status'] = workload_status
+
+ if task:
+ if task.cancelled():
+ return
+
+ if task.done():
+ exc = task.exception()
+ if exc:
+ nsr_lcm = db_nsr["_admin"]["deploy"]
+ nsr_lcm["VCA"][vnf_member_index]['operational-status'] = "failed"
+ db_nsr["detailed-status"] = "fail configuring vnf_index={} {}".format(vnf_member_index, exc)
+ db_nsr["config-status"] = "failed"
+ self.update_nsr_db(nsr_id, db_nsr)
+ else:
+ vca_status = nsr_lcm["VCA"][vnf_member_index]['operational-status']
+
+ units = len(nsr_lcm["VCA"])
+ active = 0
+ statusmap = {}
+ for vnf_index in nsr_lcm["VCA"]:
+ if vca_status not in statusmap:
+ # Initialize it
+ statusmap[vca_status] = 0
+
+ statusmap[vca_status] += 1
+
+ if vca_status == "active":
+ active += 1
+
+ cs = ""
+ for status in statusmap:
+ cs += "{} ({}) ".format(status, statusmap[status])
+ db_nsr["config-status"] = cs
+ self.update_nsr_db(nsr_id, db_nsr)
+
+ except Exception as e:
+ # self.logger.critical("Task create_ns={} n2vc_callback Exception {}".format(nsr_id, e), exc_info=True)
+ self.logger.critical("Task create_ns n2vc_callback Exception {}".format(e), exc_info=True)
+ pass
def vca_deploy_callback(self, db_nsr, vnf_index, status, task):
# TODO study using this callback when VCA.DeployApplication success from VCAMonitor
vnf_index = str(c_vnf["member-vnf-index"])
vnfd = needed_vnfd[vnfd_id]
if vnfd.get("vnf-configuration") and vnfd["vnf-configuration"].get("juju"):
- nsr_lcm["VCA"][vnf_index] = {}
vnfd_to_config += 1
proxy_charm = vnfd["vnf-configuration"]["juju"]["charm"]
proxy_charm
)
+ # Setup the runtime parameters for this VNF
+ params = {
+ 'rw_mgmt_ip': nsr_lcm['nsr_ip'][vnf_index],
+ }
+
+ # ns_name will be ignored in the current version of N2VC
+ # but will be implemented for the next point release.
+ ns_name = 'default'
+ application_name = self.n2vc.FormatApplicationName(
+ 'default',
+ vnfd['name'],
+ vnf_index,
+ )
+
+ nsr_lcm["VCA"][vnf_index] = {
+ "model": ns_name,
+ "application": application_name,
+ "operational-status": "init",
+ "vnfd_id": vnfd_id,
+ }
+
self.logger.debug("Passing artifacts path '{}' for {}".format(charm_path, proxy_charm))
task = asyncio.ensure_future(
- self.n2vc.DeployCharms(nsd, vnfd, vnf_index, charm_path, self.n2vc_callback, db_nsr, vnf_index, None)
+ self.n2vc.DeployCharms(
+ ns_name, # The network service name
+ application_name, # The application name
+ vnfd, # The vnf descriptor
+ charm_path, # Path to charm
+ params, # Runtime params, like mgmt ip
+ {}, # for native charms only
+ self.n2vc_callback, # Callback for status changes
+ db_nsr, # Callback parameter
+ vnf_index, # Callback parameter
+ None, # Callback parameter (task)
+ )
)
- task.add_done_callback(functools.partial(self.n2vc_callback, None, None, None, None, None, db_nsr, vnf_index))
+ task.add_done_callback(functools.partial(self.n2vc_callback, None, None, None, None, db_nsr))
- # task.add_done_callback(functools.partial(self.vca_deploy_callback, db_nsr, vnf_index, None))
self.lcm_tasks[nsr_id][order_id]["create_charm:" + vnf_index] = task
db_nsr["config-status"] = "configuring" if vnfd_to_config else "configured"
db_nsr["detailed-status"] = "Configuring 1/{}".format(vnfd_to_config) if vnfd_to_config else "done"
step = db_nsr["detailed-status"] = "Deleting charms"
self.logger.debug(logging_text + step)
for vnf_index, deploy_info in nsr_lcm["VCA"].items():
- if deploy_info and deploy_info.get("appliation"):
+ if deploy_info and deploy_info.get("application"):
+ # n2vc_callback(self, model_name, application_name, workload_status, db_nsr, vnf_member_index, task=None):
+ # self.n2vc.RemoveCharms(model_name, application_name, self.n2vc_callback, model_name, application_name)
task = asyncio.ensure_future(
- self.n2vc.RemoveCharms(nsd, vnfd, vnf_index, self.n2vc_callback, db_nsr, vnf_index, None)
+ self.n2vc.RemoveCharms(
+ deploy_info['model'],
+ deploy_info['application'],
+ self.n2vc_callback,
+ db_nsr,
+ vnf_index,
+ )
)
+
self.lcm_tasks[nsr_id][order_id]["delete_charm:" + vnf_index] = task
except Exception as e:
self.logger.debug(logging_text + "Failed while deleting charms: {}".format(e))
+++ /dev/null
-#!/usr/bin/python3
-# -*- coding: utf-8 -*-
-
-from juju_api import JujuApi
-from juju.model import ModelObserver
-import logging
-import os
-import os.path
-import re
-
-
-class VCAMonitor(ModelObserver):
- """Monitor state changes within the Juju Model."""
- context = None
-
- async def on_change(self, delta, old, new, model):
- """React to changes in the Juju model."""
- status = None
- db_nsr = self.context['db_nsr']
- vnf_index = self.context['vnf_index']
-
- nsr_lcm = db_nsr["_admin"]["deploy"]
- nsr_id = nsr_lcm["id"]
- application = self.context['application']
-
- if delta.entity == "unit":
- # We only care about changes to a unit
- if delta.type == "add" and old is None:
- if new and new.application == application:
- status = "BUILD"
- elif delta.type == "change":
- if new and new.application == application:
- if new.agent_status == "idle":
- if new.workload_status in ("active", "blocked"):
- status = "ACTIVE"
-
- elif delta.type == "remove" and new is None:
- if new and new.application == application:
- status = "DELETING"
-
- if status:
- nsr_lcm["VCA"][vnf_index]['operational-status'] = status
-
- # TODO: Clean this up, and make it work with deletes (if we need
- # TODO: to update the database post-delete)
- # Figure out if we're finished configuring
- count = len(nsr_lcm["VCA"])
- active = 0
- for vnf_index in nsr_lcm["VCA"]:
- if nsr_lcm["VCA"][vnf_index]['operational-status'] == "ACTIVE":
- active += 1
- if active == count:
- db_nsr["config-status"] = "done"
- else:
- db_nsr["config-status"] = "configuring {}/{}".format(active, count)
-
- try:
- self.context['db'].replace(
- "nsrs",
- nsr_id,
- db_nsr
- )
-
- # self.context['db'].replace(
- # "nsr_lcm",
- # {"id": self.context['nsr_lcm']['id']},
- # self.context['nsr_lcm']
- # )
- except Exception as e:
- # I've seen this happen when we handle a delete, because the
- # db record is gone by the time we've finished deleting
- # the charms.
- print("Error updating database: ", e)
-
- pass
-
-
-def GetJujuApi(config):
- # Quiet logging from the websocket library. If you want to see
- # everything sent over the wire, set this to DEBUG.
-
- ws_logger = logging.getLogger('websockets.protocol')
- ws_logger.setLevel(logging.INFO)
-
- api = JujuApi(server=config['host'],
- port=config['port'],
- user=config['user'],
- secret=config['secret'],
- log=ws_logger,
- model_name='default'
- )
- return api
-
-
-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():
- c = chr(97 + int(c))
- elif not c.isalpha():
- c = "-"
- new_name += c
- return re.sub('\-+', '-', new_name.lower())
-
-
-def get_initial_config(initial_config_primitive, mgmt_ip):
- config = {}
- for primitive in initial_config_primitive:
- if primitive['name'] == 'config':
- for parameter in primitive['parameter']:
- param = parameter['name']
- if parameter['value'] == "<rw_mgmt_ip>":
- config[param] = mgmt_ip
- else:
- config[param] = parameter['value']
- return config
-
-
-async def DeployApplication(vcaconfig, db, db_nsr, vnfd,
- vnf_index, charm_path):
- """
- Deploy a charm.
-
- Deploy a VNF configuration charm from a local directory.
- :param dict vcaconfig: The VCA portion of the LCM Configuration
- :param object vnfd: The VNF descriptor
- ...
- :param int vnfd_index: The index of the vnf.
-
- :Example:
-
- DeployApplication(...)
- """
- nsr_lcm = db_nsr["_admin"]["deploy"]
- nsr_id = db_nsr["_id"]
- vnf_id = vnfd['id']
-
- if "proxy" in vnfd["vnf-configuration"]["juju"]:
- use_proxy = vnfd["vnf-configuration"]["juju"]["proxy"]
- else:
- # TBD: We need this to handle a full charm
- use_proxy = True
-
- application = get_vnf_unique_name(
- db_nsr["name"].lower().strip(),
- vnf_id,
- vnf_index,
- )
-
- api = GetJujuApi(vcaconfig)
-
- await api.login()
- if api.authenticated:
- charm = os.path.basename(charm_path)
-
- # Set the INIT state; further operational status updates
- # will be made by the VCAMonitor
- nsr_lcm["VCA"][vnf_index] = {}
- nsr_lcm["VCA"][vnf_index]['operational-status'] = 'INIT'
- nsr_lcm["VCA"][vnf_index]['application'] = application
-
- db.replace("nsrs", nsr_id, db_nsr)
-
- model = await api.get_model()
- context = {
- 'application': application,
- 'vnf_index': vnf_index,
- 'db_nsr': db_nsr,
- 'db': db,
- }
- mon = VCAMonitor()
- mon.context = context
- model.add_observer(mon)
-
- await api.deploy_application(charm,
- name=application,
- path=charm_path,
- )
-
- # Get and apply the initial config primitive
- cfg = get_initial_config(
- vnfd["vnf-configuration"].get(
- "initial-config-primitive"
- ),
- nsr_lcm['nsr_ip'][vnf_index]
- )
-
- await api.apply_config(cfg, application)
-
- await api.logout()
-
-
-async def RemoveApplication(vcaconfig, db, db_nsr, vnf_index):
- """
- Remove an application from the Juju Controller
-
- Removed the named application and it's charm from the Juju controller.
-
- :param object loop: The event loop.
- :param str application_name: The unique name of the application.
-
- :Example:
-
- RemoveApplication(loop, "ping_vnf")
- RemoveApplication(loop, "pong_vnf")
- """
- nsr_id = db_nsr["_id"]
- nsr_lcm = db_nsr["_admin"]["deploy"]
- application = nsr_lcm["VCA"][vnf_index]['application']
-
- api = GetJujuApi(vcaconfig)
-
- await api.login()
- if api.authenticated:
- model = await api.get_model()
- context = {
- 'application': application,
- 'vnf_index': vnf_index,
- 'db_nsr': db_nsr,
- 'db': db,
- }
-
- mon = VCAMonitor()
- mon.context = context
- model.add_observer(mon)
-
- print("VCA: Removing application {}".format(application))
- await api.remove_application(application)
- nsr_lcm["VCA"][vnf_index]['application'] = None
- db.replace("nsrs", nsr_id, db_nsr)
- await api.logout()