X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=n2vc%2Fn2vc_juju_conn.py;h=b2ffa0a22baf6cf1a44bc7f789f19d1ccb1a8622;hb=513cb2d19abfbe5b3aea879bf1a0561ea211e7d4;hp=55220d64ab389f178fd4ac13dea63a5809f88b49;hpb=582b923b8f3f7104411c39ebdba63949d606ecd1;p=osm%2FN2VC.git diff --git a/n2vc/n2vc_juju_conn.py b/n2vc/n2vc_juju_conn.py index 55220d6..b2ffa0a 100644 --- a/n2vc/n2vc_juju_conn.py +++ b/n2vc/n2vc_juju_conn.py @@ -39,7 +39,7 @@ from n2vc.n2vc_conn import N2VCConnector from n2vc.n2vc_conn import obj_to_dict, obj_to_yaml from n2vc.libjuju import Libjuju from n2vc.store import MotorStore -from n2vc.utils import get_ee_id_components +from n2vc.utils import get_ee_id_components, generate_random_alfanum_string from n2vc.vca.connection import get_connection from retrying_async import retry @@ -93,7 +93,7 @@ class N2VCJujuConnector(N2VCConnector): db_uri = EnvironConfig(prefixes=["OSMLCM_", "OSMMON_"]).get("database_uri") self._store = MotorStore(db_uri) self.loading_libjuju = asyncio.Lock(loop=self.loop) - + self.delete_namespace_locks = {} self.log.info("N2VC juju connector initialized") async def get_status( @@ -791,33 +791,60 @@ class N2VCJujuConnector(N2VCConnector): :param: vca_id: VCA ID """ self.log.info("Deleting namespace={}".format(namespace)) - libjuju = await self._get_libjuju(vca_id) + will_not_delete = False + if namespace not in self.delete_namespace_locks: + self.delete_namespace_locks[namespace] = asyncio.Lock(loop=self.loop) + delete_lock = self.delete_namespace_locks[namespace] - # check arguments - if namespace is None: - raise N2VCBadArgumentsException( - message="namespace is mandatory", bad_args=["namespace"] - ) + while delete_lock.locked(): + will_not_delete = True + await asyncio.sleep(0.1) - _nsi_id, ns_id, _vnf_id, _vdu_id, _vdu_count = self._get_namespace_components( - namespace=namespace - ) - if ns_id is not None: - try: - models = await libjuju.list_models(contains=ns_id) - for model in models: - await libjuju.destroy_model( - model_name=model, total_timeout=total_timeout + if will_not_delete: + self.log.info("Namespace {} deleted by another worker.".format(namespace)) + return + + try: + async with delete_lock: + libjuju = await self._get_libjuju(vca_id) + + # check arguments + if namespace is None: + raise N2VCBadArgumentsException( + message="namespace is mandatory", bad_args=["namespace"] ) - except Exception as e: - raise N2VCException( - message="Error deleting namespace {} : {}".format(namespace, e) - ) - else: - raise N2VCBadArgumentsException( - message="only ns_id is permitted to delete yet", bad_args=["namespace"] - ) + ( + _nsi_id, + ns_id, + _vnf_id, + _vdu_id, + _vdu_count, + ) = self._get_namespace_components(namespace=namespace) + if ns_id is not None: + try: + models = await libjuju.list_models(contains=ns_id) + for model in models: + await libjuju.destroy_model( + model_name=model, total_timeout=total_timeout + ) + except Exception as e: + self.log.error(f"Error deleting namespace {namespace} : {e}") + raise N2VCException( + message="Error deleting namespace {} : {}".format( + namespace, e + ) + ) + else: + raise N2VCBadArgumentsException( + message="only ns_id is permitted to delete yet", + bad_args=["namespace"], + ) + except Exception as e: + self.log.error(f"Error deleting namespace {namespace} : {e}") + raise e + finally: + self.delete_namespace_locks.pop(namespace) self.log.info("Namespace {} deleted".format(namespace)) async def delete_execution_environment( @@ -1045,6 +1072,68 @@ class N2VCJujuConnector(N2VCConnector): primitive_name=primitive_name, ) + async def upgrade_charm( + self, + ee_id: str = None, + path: str = None, + charm_id: str = None, + charm_type: str = None, + timeout: float = None, + ) -> str: + """This method upgrade charms in VNFs + + Args: + ee_id: Execution environment id + path: Local path to the charm + charm_id: charm-id + charm_type: Charm type can be lxc-proxy-charm, native-charm or k8s-proxy-charm + timeout: (Float) Timeout for the ns update operation + + Returns: + The output of the update operation if status equals to "completed" + + """ + self.log.info("Upgrading charm: {} on ee: {}".format(path, ee_id)) + libjuju = await self._get_libjuju(charm_id) + + # check arguments + if ee_id is None or len(ee_id) == 0: + raise N2VCBadArgumentsException( + message="ee_id is mandatory", bad_args=["ee_id"] + ) + try: + ( + model_name, + application_name, + machine_id, + ) = N2VCJujuConnector._get_ee_id_components(ee_id=ee_id) + + except Exception: + raise N2VCBadArgumentsException( + message="ee_id={} is not a valid execution environment id".format( + ee_id + ), + bad_args=["ee_id"], + ) + + try: + + await libjuju.upgrade_charm( + application_name=application_name, + path=path, + model_name=model_name, + total_timeout=timeout, + ) + + return f"Charm upgraded with application name {application_name}" + + except Exception as e: + self.log.error("Error upgrading charm {}: {}".format(path, e)) + + raise N2VCException( + message="Error upgrading charm {} in ee={} : {}".format(path, ee_id, e) + ) + async def disconnect(self, vca_id: str = None): """ Disconnect from VCA @@ -1140,7 +1229,7 @@ class N2VCJujuConnector(N2VCConnector): """ Build application name from namespace :param namespace: - :return: app-vnf--vdu--cnt- + :return: app-vnf--vdu--cnt-- """ # TODO: Enforce the Juju 50-character application limit @@ -1167,7 +1256,12 @@ class N2VCJujuConnector(N2VCConnector): else: vdu_count = "-cnt-" + vdu_count - application_name = "app-{}{}{}".format(vnf_id, vdu_id, vdu_count) + # Generate a random suffix with 5 characters (the default size used by K8s) + random_suffix = generate_random_alfanum_string(size=5) + + application_name = "app-{}{}{}-{}".format( + vnf_id, vdu_id, vdu_count, random_suffix + ) return N2VCJujuConnector._format_app_name(application_name)