Modifications for test of feature 7953
[osm/LCM.git] / osm_lcm / ns.py
index 2e0ca39..e6dec82 100644 (file)
@@ -25,7 +25,7 @@ import json
 from jinja2 import Environment, Template, meta, TemplateError, TemplateNotFound, TemplateSyntaxError
 
 from osm_lcm import ROclient
-from osm_lcm.lcm_utils import LcmException, LcmExceptionNoMgmtIP, LcmBase, deep_get
+from osm_lcm.lcm_utils import LcmException, LcmExceptionNoMgmtIP, LcmBase, deep_get, get_iterable, populate_dict
 from n2vc.k8s_helm_conn import K8sHelmConnector
 from n2vc.k8s_juju_conn import K8sJujuConnector
 
@@ -43,37 +43,9 @@ from uuid import uuid4
 __author__ = "Alfonso Tierno"
 
 
-def get_iterable(in_dict, in_key):
-    """
-    Similar to <dict>.get(), but if value is None, False, ..., An empty tuple is returned instead
-    :param in_dict: a dictionary
-    :param in_key: the key to look for at in_dict
-    :return: in_dict[in_var] or () if it is None or not present
-    """
-    if not in_dict.get(in_key):
-        return ()
-    return in_dict[in_key]
-
-
-def populate_dict(target_dict, key_list, value):
-    """
-    Update target_dict creating nested dictionaries with the key_list. Last key_list item is asigned the value.
-    Example target_dict={K: J}; key_list=[a,b,c];  target_dict will be {K: J, a: {b: {c: value}}}
-    :param target_dict: dictionary to be changed
-    :param key_list: list of keys to insert at target_dict
-    :param value:
-    :return: None
-    """
-    for key in key_list[0:-1]:
-        if key not in target_dict:
-            target_dict[key] = {}
-        target_dict = target_dict[key]
-    target_dict[key_list[-1]] = value
-
-
 class NsLcm(LcmBase):
     timeout_vca_on_error = 5 * 60   # Time for charm from first time at blocked,error status to mark as failed
-    total_deploy_timeout = 2 * 3600   # global timeout for deployment
+    timeout_ns_deploy = 2 * 3600   # default global timeout for deployment a ns
     timeout_charm_delete = 10 * 60
     timeout_primitive = 10 * 60  # timeout for primitive execution
 
@@ -81,7 +53,7 @@ class NsLcm(LcmBase):
     SUBOPERATION_STATUS_NEW = -2
     SUBOPERATION_STATUS_SKIP = -3
 
-    def __init__(self, db, msg, fs, lcm_tasks, ro_config, vca_config, loop):
+    def __init__(self, db, msg, fs, lcm_tasks, config, loop):
         """
         Init, Connect to database, filesystem storage, and messaging
         :param config: two level dictionary with configuration. Top level should contain 'database', 'storage',
@@ -96,21 +68,9 @@ class NsLcm(LcmBase):
 
         self.loop = loop
         self.lcm_tasks = lcm_tasks
-        self.ro_config = ro_config
-        self.vca_config = vca_config
-        if 'pubkey' in self.vca_config:
-            self.vca_config['public_key'] = self.vca_config['pubkey']
-        if 'cacert' in self.vca_config:
-            self.vca_config['ca_cert'] = self.vca_config['cacert']
-        if 'apiproxy' in self.vca_config:
-            self.vca_config['api_proxy'] = self.vca_config['apiproxy']
-        if 'enableosupgrade' in self.vca_config:
-            if self.vca_config['enableosupgrade'].lower() == 'false':
-                self.vca_config['enable_os_upgrade'] = False
-            elif self.vca_config['enableosupgrade'].lower() == 'true':
-                self.vca_config['enable_os_upgrade'] = True
-        if 'aptmirror' in self.vca_config:
-            self.vca_config['apt_mirror'] = self.vca_config['aptmirror']
+        self.timeout = config["timeout"]
+        self.ro_config = config["ro_config"]
+        self.vca_config = config["VCA"].copy()
 
         # create N2VC connector
         self.n2vc = N2VCJujuConnector(
@@ -773,6 +733,13 @@ class NsLcm(LcmBase):
         start_deploy = time()
         vdu_flag = False  # If any of the VNFDs has VDUs
         ns_params = db_nslcmop.get("operationParams")
+        if ns_params and ns_params.get("timeout_ns_deploy"):
+            timeout_ns_deploy = ns_params["timeout_ns_deploy"]
+        else:
+            timeout_ns_deploy = self.timeout.get("ns_deploy", self.timeout_ns_deploy)
+
+        # Check for and optionally request placement optimization. Database will be updated if placement activated
+        await self.do_placement(logging_text, db_nslcmop, db_vnfrs)
 
         # deploy RO
 
@@ -909,7 +876,7 @@ class NsLcm(LcmBase):
         self.logger.debug(logging_text + step)
 
         old_desc = None
-        while time() <= start_deploy + self.total_deploy_timeout:
+        while time() <= start_deploy + timeout_ns_deploy:
             desc = await self.RO.show("ns", RO_nsr_id)
 
             # deploymentStatus
@@ -938,7 +905,7 @@ class NsLcm(LcmBase):
                     detailed_status_old = db_nsr_update["_admin.deployed.RO.detailed-status"] = detailed_status
                     self.update_db_2("nsrs", nsr_id, db_nsr_update)
                 await asyncio.sleep(5, loop=self.loop)
-        else:  # total_deploy_timeout
+        else:  # timeout_ns_deploy
             raise ROclient.ROClientException("Timeout waiting ns to be ready")
 
         step = "Updating NSR"
@@ -1119,6 +1086,7 @@ class NsLcm(LcmBase):
                     element_under_configuration = "{}-{}".format(vdu_id, vdu_index or 0)
 
             # Get artifact path
+            self.fs.sync()  # Sync from FSMongo
             artifact_path = "{}/{}/charms/{}".format(
                 base_folder["folder"],
                 base_folder["pkg-dir"],
@@ -1137,7 +1105,7 @@ class NsLcm(LcmBase):
             # create or register execution environment in VCA
             if is_proxy_charm:
 
-                await self._write_configuration_status(
+                self._write_configuration_status(
                     nsr_id=nsr_id,
                     vca_index=vca_index,
                     status='CREATING',
@@ -1173,7 +1141,7 @@ class NsLcm(LcmBase):
                 credentials["username"] = username
                 # n2vc_redesign STEP 3.2
 
-                await self._write_configuration_status(
+                self._write_configuration_status(
                     nsr_id=nsr_id,
                     vca_index=vca_index,
                     status='REGISTERING',
@@ -1199,7 +1167,7 @@ class NsLcm(LcmBase):
 
             step = "Install configuration Software"
 
-            await self._write_configuration_status(
+            self._write_configuration_status(
                 nsr_id=nsr_id,
                 vca_index=vca_index,
                 status='INSTALLING SW',
@@ -1211,6 +1179,12 @@ class NsLcm(LcmBase):
             self.logger.debug(logging_text + step)
             await self.n2vc.install_configuration_sw(ee_id=ee_id, artifact_path=artifact_path, db_dict=db_dict)
 
+            # write in db flag of configuration_sw already installed
+            self.update_db_2("nsrs", nsr_id, {db_update_entry + "config_sw_installed": True})
+
+            # add relations for this VCA (wait for other peers related with this VCA)
+            await self._add_vca_relations(logging_text=logging_text, nsr_id=nsr_id, vca_index=vca_index)
+
             # if SSH access is required, then get execution environment SSH public
             if is_proxy_charm:  # if native charm we have waited already to VM be UP
                 pub_key = None
@@ -1244,10 +1218,13 @@ class NsLcm(LcmBase):
             initial_config_primitive_list = config_descriptor.get('initial-config-primitive')
 
             # sort initial config primitives by 'seq'
-            try:
-                initial_config_primitive_list.sort(key=lambda val: int(val['seq']))
-            except Exception as e:
-                self.logger.error(logging_text + step + ": " + str(e))
+            if initial_config_primitive_list:
+                try:
+                    initial_config_primitive_list.sort(key=lambda val: int(val['seq']))
+                except Exception as e:
+                    self.logger.error(logging_text + step + ": " + str(e))
+            else:
+                self.logger.debug(logging_text + step + ": No initial-config-primitive")
 
             # add config if not present for NS charm
             initial_config_primitive_list = self._get_initial_config_primitive_list(initial_config_primitive_list,
@@ -1269,7 +1246,7 @@ class NsLcm(LcmBase):
                 # NS
                 stage = 'Stage 5/5: running Day-1 primitives for NS'
 
-            await self._write_configuration_status(
+            self._write_configuration_status(
                 nsr_id=nsr_id,
                 vca_index=vca_index,
                 status='EXECUTING PRIMITIVE'
@@ -1301,7 +1278,7 @@ class NsLcm(LcmBase):
             step = "instantiated at VCA"
             self.logger.debug(logging_text + step)
 
-            await self._write_configuration_status(
+            self._write_configuration_status(
                 nsr_id=nsr_id,
                 vca_index=vca_index,
                 status='READY'
@@ -1309,7 +1286,7 @@ class NsLcm(LcmBase):
 
         except Exception as e:  # TODO not use Exception but N2VC exception
             # self.update_db_2("nsrs", nsr_id, {db_update_entry + "instantiation": "FAILED"})
-            await self._write_configuration_status(
+            self._write_configuration_status(
                 nsr_id=nsr_id,
                 vca_index=vca_index,
                 status='BROKEN'
@@ -1359,8 +1336,8 @@ class NsLcm(LcmBase):
         except Exception as e:
             self.logger.warn('Error writing all configuration status, ns={}: {}'.format(nsr_id, e))
 
-    async def _write_configuration_status(self, nsr_id: str, vca_index: int, status: str,
-                                          element_under_configuration: str = None, element_type: str = None):
+    def _write_configuration_status(self, nsr_id: str, vca_index: int, status: str = None,
+                                    element_under_configuration: str = None, element_type: str = None):
 
         # self.logger.debug('_write_configuration_status(): vca_index={}, status={}'
         #                   .format(vca_index, status))
@@ -1368,7 +1345,8 @@ class NsLcm(LcmBase):
         try:
             db_path = 'configurationStatus.{}.'.format(vca_index)
             db_dict = dict()
-            db_dict[db_path + 'status'] = status
+            if status:
+                db_dict[db_path + 'status'] = status
             if element_under_configuration:
                 db_dict[db_path + 'elementUnderConfiguration'] = element_under_configuration
             if element_type:
@@ -1378,6 +1356,37 @@ class NsLcm(LcmBase):
             self.logger.warn('Error writing configuration status={}, ns={}, vca_index={}: {}'
                              .format(status, nsr_id, vca_index, e))
 
+    async def do_placement(self, logging_text, db_nslcmop, db_vnfrs):
+        placement_engine = deep_get(db_nslcmop, ('operationParams', 'placement-engine'))
+        if placement_engine == "PLA":
+            self.logger.debug(logging_text + "Invoke placement optimization for nslcmopId={}".format(db_nslcmop['id']))
+            await self.msg.aiowrite("pla", "get_placement", {'nslcmopId': db_nslcmop['_id']}, loop=self.loop)
+            db_poll_interval = 5
+            wait = db_poll_interval * 4
+            pla_result = None
+            while not pla_result and wait >= 0:
+                await asyncio.sleep(db_poll_interval)
+                wait -= db_poll_interval
+                db_nslcmop = self.db.get_one("nslcmops", {"_id": db_nslcmop["_id"]})
+                pla_result = deep_get(db_nslcmop, ('_admin', 'pla'))
+
+            if not pla_result:
+                raise LcmException("Placement timeout for nslcmopId={}".format(db_nslcmop['id']))
+
+            for pla_vnf in pla_result['vnf']:
+                vnfr = db_vnfrs.get(pla_vnf['member-vnf-index'])
+                if not pla_vnf.get('vimAccountId') or not vnfr:
+                    continue
+                self.db.set_one("vnfrs", {"_id": vnfr["_id"]}, {"vim-account-id": pla_vnf['vimAccountId']})
+        return
+
+    def update_nsrs_with_pla_result(self, params):
+        try:
+            nslcmop_id = deep_get(params, ('placement', 'nslcmopId'))
+            self.update_db_2("nslcmops", nslcmop_id, {"_admin.pla": params.get('placement')})
+        except Exception as e:
+            self.logger.warn('Update failed for nslcmop_id={}:{}'.format(nslcmop_id, e))
+
     async def instantiate(self, nsr_id, nslcmop_id):
         """
 
@@ -1436,6 +1445,11 @@ class NsLcm(LcmBase):
             # read from db: operation
             step = "Getting nslcmop={} from db".format(nslcmop_id)
             db_nslcmop = self.db.get_one("nslcmops", {"_id": nslcmop_id})
+            ns_params = db_nslcmop.get("operationParams")
+            if ns_params and ns_params.get("timeout_ns_deploy"):
+                timeout_ns_deploy = ns_params["timeout_ns_deploy"]
+            else:
+                timeout_ns_deploy = self.timeout.get("ns_deploy", self.timeout_ns_deploy)
 
             # read from db: ns
             step = "Getting nsr={} from db".format(nsr_id)
@@ -1467,7 +1481,7 @@ class NsLcm(LcmBase):
                 vnfd_ref = vnfr["vnfd-ref"]                     # vnfd name for this vnf
                 # if we haven't this vnfd, read it from db
                 if vnfd_id not in db_vnfds:
-                    # read from cb
+                    # read from db
                     step = "Getting vnfd={} id='{}' from db".format(vnfd_id, vnfd_ref)
                     self.logger.debug(logging_text + step)
                     vnfd = self.db.get_one("vnfds", {"_id": vnfd_id})
@@ -1520,6 +1534,7 @@ class NsLcm(LcmBase):
                     nsr_id=nsr_id,
                     db_nsr=db_nsr,
                     db_vnfrs=db_vnfrs,
+                    db_vnfds=db_vnfds
                 )
             )
             self.lcm_tasks.register("ns", nsr_id, nslcmop_id, "instantiate_KDUs", task_kdu)
@@ -1549,7 +1564,7 @@ class NsLcm(LcmBase):
             task_instantiation_list.append(task_ro)
 
             # n2vc_redesign STEP 3 to 6 Deploy N2VC
-            step = "Looking for needed vnfd to configure with proxy charm"
+            step = "Deploying proxy and native charms"
             self.logger.debug(logging_text + step)
 
             nsi_id = None  # TODO put nsi_id when this nsr belongs to a NSI
@@ -1709,9 +1724,7 @@ class NsLcm(LcmBase):
 
             # Wait until all tasks of "task_instantiation_list" have been finished
 
-            # while time() <= start_deploy + self.total_deploy_timeout:
             error_text_list = []
-            timeout = 3600
 
             # let's begin with all OK
             instantiated_ok = True
@@ -1720,17 +1733,15 @@ class NsLcm(LcmBase):
             # let's begin with VCA 'configured' status (later we can change it)
             db_nsr_update["config-status"] = "configured"
 
+            step = "Waiting for tasks to be finished"
             if task_instantiation_list:
                 # wait for all tasks completion
-                done, pending = await asyncio.wait(task_instantiation_list, timeout=timeout)
+                done, pending = await asyncio.wait(task_instantiation_list, timeout=timeout_ns_deploy)
 
                 for task in pending:
                     instantiated_ok = False
-                    if task == task_ro:
-                        # RO task is pending
-                        db_nsr_update["operational-status"] = "failed"
-                    elif task == task_kdu:
-                        # KDU task is pending
+                    if task in (task_ro, task_kdu):
+                        # RO or KDU task is pending
                         db_nsr_update["operational-status"] = "failed"
                     else:
                         # A N2VC task is pending
@@ -1740,11 +1751,8 @@ class NsLcm(LcmBase):
                 for task in done:
                     if task.cancelled():
                         instantiated_ok = False
-                        if task == task_ro:
-                            # RO task was cancelled
-                            db_nsr_update["operational-status"] = "failed"
-                        elif task == task_kdu:
-                            # KDU task was cancelled
+                        if task in (task_ro, task_kdu):
+                            # RO or KDU task was cancelled
                             db_nsr_update["operational-status"] = "failed"
                         else:
                             # A N2VC was cancelled
@@ -1755,11 +1763,8 @@ class NsLcm(LcmBase):
                         exc = task.exception()
                         if exc:
                             instantiated_ok = False
-                            if task == task_ro:
-                                # RO task raised an exception
-                                db_nsr_update["operational-status"] = "failed"
-                            elif task == task_kdu:
-                                # KDU task raised an exception
+                            if task in (task_ro, task_kdu):
+                                # RO or KDU task raised an exception
                                 db_nsr_update["operational-status"] = "failed"
                             else:
                                 # A N2VC task raised an exception
@@ -1778,15 +1783,15 @@ class NsLcm(LcmBase):
             if error_text_list:
                 error_text = "\n".join(error_text_list)
                 db_nsr_update["detailed-status"] = error_text
-                db_nslcmop_update["operationState"] = nslcmop_operation_state = "FAILED_TEMP"
                 db_nslcmop_update["detailed-status"] = error_text
+                db_nslcmop_update["operationState"] = nslcmop_operation_state = "FAILED"
                 db_nslcmop_update["statusEnteredTime"] = time()
             else:
                 # all is done
+                db_nsr_update["detailed-status"] = "done"
+                db_nslcmop_update["detailed-status"] = "done"
                 db_nslcmop_update["operationState"] = nslcmop_operation_state = "COMPLETED"
                 db_nslcmop_update["statusEnteredTime"] = time()
-                db_nslcmop_update["detailed-status"] = "done"
-                db_nsr_update["detailed-status"] = "done"
 
         except (ROclient.ROClientException, DbException, LcmException) as e:
             self.logger.error(logging_text + "Exit Exception while '{}': {}".format(step, e))
@@ -1856,7 +1861,167 @@ class NsLcm(LcmBase):
             self.logger.debug(logging_text + "Exit")
             self.lcm_tasks.remove("ns", nsr_id, nslcmop_id, "ns_instantiate")
 
-    async def deploy_kdus(self, logging_text, nsr_id, db_nsr, db_vnfrs):
+    async def _add_vca_relations(self, logging_text, nsr_id, vca_index: int, timeout: int = 3600) -> bool:
+
+        # steps:
+        # 1. find all relations for this VCA
+        # 2. wait for other peers related
+        # 3. add relations
+
+        try:
+
+            # STEP 1: find all relations for this VCA
+
+            # read nsr record
+            db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
+
+            # this VCA data
+            my_vca = deep_get(db_nsr, ('_admin', 'deployed', 'VCA'))[vca_index]
+
+            # read all ns-configuration relations
+            ns_relations = list()
+            db_ns_relations = deep_get(db_nsr, ('nsd', 'ns-configuration', 'relation'))
+            if db_ns_relations:
+                for r in db_ns_relations:
+                    # check if this VCA is in the relation
+                    if my_vca.get('member-vnf-index') in\
+                            (r.get('entities')[0].get('id'), r.get('entities')[1].get('id')):
+                        ns_relations.append(r)
+
+            # read all vnf-configuration relations
+            vnf_relations = list()
+            db_vnfd_list = db_nsr.get('vnfd-id')
+            if db_vnfd_list:
+                for vnfd in db_vnfd_list:
+                    db_vnfd = self.db.get_one("vnfds", {"_id": vnfd})
+                    db_vnf_relations = deep_get(db_vnfd, ('vnf-configuration', 'relation'))
+                    if db_vnf_relations:
+                        for r in db_vnf_relations:
+                            # check if this VCA is in the relation
+                            if my_vca.get('vdu_id') in (r.get('entities')[0].get('id'), r.get('entities')[1].get('id')):
+                                vnf_relations.append(r)
+
+            # if no relations, terminate
+            if not ns_relations and not vnf_relations:
+                self.logger.debug(logging_text + ' No relations')
+                return True
+
+            self.logger.debug(logging_text + ' adding relations\n    {}\n    {}'.format(ns_relations, vnf_relations))
+
+            # add all relations
+            start = time()
+            while True:
+                # check timeout
+                now = time()
+                if now - start >= timeout:
+                    self.logger.error(logging_text + ' : timeout adding relations')
+                    return False
+
+                # reload nsr from database (we need to update record: _admin.deloyed.VCA)
+                db_nsr = self.db.get_one("nsrs", {"_id": nsr_id})
+
+                # for each defined NS relation, find the VCA's related
+                for r in ns_relations:
+                    from_vca_ee_id = None
+                    to_vca_ee_id = None
+                    from_vca_endpoint = None
+                    to_vca_endpoint = None
+                    vca_list = deep_get(db_nsr, ('_admin', 'deployed', 'VCA'))
+                    for vca in vca_list:
+                        if vca.get('member-vnf-index') == r.get('entities')[0].get('id') \
+                                and vca.get('config_sw_installed'):
+                            from_vca_ee_id = vca.get('ee_id')
+                            from_vca_endpoint = r.get('entities')[0].get('endpoint')
+                        if vca.get('member-vnf-index') == r.get('entities')[1].get('id') \
+                                and vca.get('config_sw_installed'):
+                            to_vca_ee_id = vca.get('ee_id')
+                            to_vca_endpoint = r.get('entities')[1].get('endpoint')
+                    if from_vca_ee_id and to_vca_ee_id:
+                        # add relation
+                        await self.n2vc.add_relation(
+                            ee_id_1=from_vca_ee_id,
+                            ee_id_2=to_vca_ee_id,
+                            endpoint_1=from_vca_endpoint,
+                            endpoint_2=to_vca_endpoint)
+                        # remove entry from relations list
+                        ns_relations.remove(r)
+                    else:
+                        # check failed peers
+                        try:
+                            vca_status_list = db_nsr.get('configurationStatus')
+                            if vca_status_list:
+                                for i in range(len(vca_list)):
+                                    vca = vca_list[i]
+                                    vca_status = vca_status_list[i]
+                                    if vca.get('member-vnf-index') == r.get('entities')[0].get('id'):
+                                        if vca_status.get('status') == 'BROKEN':
+                                            # peer broken: remove relation from list
+                                            ns_relations.remove(r)
+                                    if vca.get('member-vnf-index') == r.get('entities')[1].get('id'):
+                                        if vca_status.get('status') == 'BROKEN':
+                                            # peer broken: remove relation from list
+                                            ns_relations.remove(r)
+                        except Exception:
+                            # ignore
+                            pass
+
+                # for each defined VNF relation, find the VCA's related
+                for r in vnf_relations:
+                    from_vca_ee_id = None
+                    to_vca_ee_id = None
+                    from_vca_endpoint = None
+                    to_vca_endpoint = None
+                    vca_list = deep_get(db_nsr, ('_admin', 'deployed', 'VCA'))
+                    for vca in vca_list:
+                        if vca.get('vdu_id') == r.get('entities')[0].get('id') and vca.get('config_sw_installed'):
+                            from_vca_ee_id = vca.get('ee_id')
+                            from_vca_endpoint = r.get('entities')[0].get('endpoint')
+                        if vca.get('vdu_id') == r.get('entities')[1].get('id') and vca.get('config_sw_installed'):
+                            to_vca_ee_id = vca.get('ee_id')
+                            to_vca_endpoint = r.get('entities')[1].get('endpoint')
+                    if from_vca_ee_id and to_vca_ee_id:
+                        # add relation
+                        await self.n2vc.add_relation(
+                            ee_id_1=from_vca_ee_id,
+                            ee_id_2=to_vca_ee_id,
+                            endpoint_1=from_vca_endpoint,
+                            endpoint_2=to_vca_endpoint)
+                        # remove entry from relations list
+                        vnf_relations.remove(r)
+                    else:
+                        # check failed peers
+                        try:
+                            vca_status_list = db_nsr.get('configurationStatus')
+                            if vca_status_list:
+                                for i in range(len(vca_list)):
+                                    vca = vca_list[i]
+                                    vca_status = vca_status_list[i]
+                                    if vca.get('vdu_id') == r.get('entities')[0].get('id'):
+                                        if vca_status.get('status') == 'BROKEN':
+                                            # peer broken: remove relation from list
+                                            ns_relations.remove(r)
+                                    if vca.get('vdu_id') == r.get('entities')[1].get('id'):
+                                        if vca_status.get('status') == 'BROKEN':
+                                            # peer broken: remove relation from list
+                                            ns_relations.remove(r)
+                        except Exception:
+                            # ignore
+                            pass
+
+                # wait for next try
+                await asyncio.sleep(5.0)
+
+                if not ns_relations and not vnf_relations:
+                    self.logger.debug('Relations added')
+                    break
+
+            return True
+
+        except Exception as e:
+            self.logger.warn(logging_text + ' ERROR adding relations: {}'.format(e))
+            return False
+
+    async def deploy_kdus(self, logging_text, nsr_id, db_nsr, db_vnfrs, db_vnfds):
         # Launch kdus if present in the descriptor
 
         deployed_ok = True
@@ -1892,6 +2057,8 @@ class NsLcm(LcmBase):
                     k8sclustertype = None
                     error_text = None
                     cluster_uuid = None
+                    vnfd_id = vnfr_data.get('vnfd-id')
+                    pkgdir = deep_get(db_vnfds.get(vnfd_id), ('_admin', 'storage', 'pkg-dir'))
                     if kdur.get("helm-chart"):
                         kdumodel = kdur["helm-chart"]
                         k8sclustertype = "chart"
@@ -1903,6 +2070,15 @@ class NsLcm(LcmBase):
                     else:
                         error_text = "kdu type is neither helm-chart nor juju-bundle. Maybe an old NBI version is" \
                                      " running"
+                    # check if kdumodel is a file and exists
+                    try:
+                        # path format: /vnfdid/pkkdir/kdumodel
+                        filename = '{}/{}/{}s/{}'.format(vnfd_id, pkgdir, k8sclustertype_full, kdumodel)
+                        if self.fs.file_exists(filename, mode='file') or self.fs.file_exists(filename, mode='dir'):
+                            kdumodel = self.fs.path + filename
+                    except Exception:
+                        # it is not a file
+                        pass
                     try:
                         if not error_text:
                             cluster_uuid = _get_cluster_id(kdur["k8s-cluster"]["id"], k8sclustertype_full)
@@ -1933,37 +2109,39 @@ class NsLcm(LcmBase):
                         task = asyncio.ensure_future(
                             self.k8sclusterjuju.install(cluster_uuid=cluster_uuid, kdu_model=kdumodel,
                                                         atomic=True, params=desc_params,
-                                                        db_dict=db_dict, timeout=600)
+                                                        db_dict=db_dict, timeout=600,
+                                                        kdu_name=kdur["kdu-name"])
                         )
 
                     pending_tasks[task] = "_admin.deployed.K8s.{}.".format(index)
                     index += 1
-            if not pending_tasks:
-                return
-            self.logger.debug(logging_text + 'Waiting for terminate pending tasks...')
-            pending_list = list(pending_tasks.keys())
-            while pending_list:
-                done_list, pending_list = await asyncio.wait(pending_list, timeout=30*60,
-                                                             return_when=asyncio.FIRST_COMPLETED)
-                if not done_list:   # timeout
-                    for task in pending_list:
-                        db_nsr_update[pending_tasks(task) + "detailed-status"] = "Timeout"
-                        deployed_ok = False
-                    break
-                for task in done_list:
-                    exc = task.exception()
-                    if exc:
-                        db_nsr_update[pending_tasks[task] + "detailed-status"] = "{}".format(exc)
-                        deployed_ok = False
-                    else:
-                        db_nsr_update[pending_tasks[task] + "kdu-instance"] = task.result()
+
+            if pending_tasks:
+                self.logger.debug(logging_text + 'Waiting for terminate pending tasks...')
+                pending_list = list(pending_tasks.keys())
+                while pending_list:
+                    done_list, pending_list = await asyncio.wait(pending_list, timeout=30*60,
+                                                                 return_when=asyncio.FIRST_COMPLETED)
+                    if not done_list:   # timeout
+                        for task in pending_list:
+                            db_nsr_update[pending_tasks(task) + "detailed-status"] = "Timeout"
+                            deployed_ok = False
+                        break
+                    for task in done_list:
+                        exc = task.exception()
+                        if exc:
+                            db_nsr_update[pending_tasks[task] + "detailed-status"] = "{}".format(exc)
+                            deployed_ok = False
+                        else:
+                            db_nsr_update[pending_tasks[task] + "kdu-instance"] = task.result()
 
             if not deployed_ok:
                 raise LcmException('Cannot deploy KDUs')
 
         except Exception as e:
-            self.logger.critical(logging_text + "Exit Exception {} while '{}': {}".format(type(e).__name__, step, e))
-            raise LcmException("{} Exit Exception {} while '{}': {}".format(logging_text, type(e).__name__, step, e))
+            msg = "{} Exit Exception {} while '{}': {}".format(logging_text, type(e).__name__, step, e)
+            self.logger.error(msg)
+            raise LcmException(msg)
         finally:
             if db_nsr_update:
                 self.update_db_2("nsrs", nsr_id, db_nsr_update)