+def deep_get(target_dict, key_list, default_value=None):
+ """
+ Get a value from target_dict entering in the nested keys. If keys does not exist, it returns None
+ Example target_dict={a: {b: 5}}; key_list=[a,b] returns 5; both key_list=[a,b,c] and key_list=[f,h] return None
+ :param target_dict: dictionary to be read
+ :param key_list: list of keys to read from target_dict
+ :param default_value: value to return if key is not present in the nested dictionary
+ :return: The wanted value if exist, None otherwise
+ """
+ for key in key_list:
+ if not isinstance(target_dict, dict) or key not in target_dict:
+ return default_value
+ target_dict = target_dict[key]
+ return target_dict
+
+
+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 check_juju_bundle_existence(vnfd: dict) -> str:
+ """Checks the existence of juju-bundle in the descriptor
+
+ Args:
+ vnfd: Descriptor as a dictionary
+
+ Returns:
+ Juju bundle if dictionary has juju-bundle else None
+
+ """
+ if vnfd.get("vnfd"):
+ vnfd = vnfd["vnfd"]
+
+ for kdu in vnfd.get("kdu", []):
+ return kdu.get("juju-bundle", None)
+
+
+def get_charm_artifact_path(base_folder, charm_name, charm_type, revision=str()) -> str:
+ """Finds the charm artifact paths
+
+ Args:
+ base_folder: Main folder which will be looked up for charm
+ charm_name: Charm name
+ charm_type: Type of charm native_charm, lxc_proxy_charm or k8s_proxy_charm
+ revision: vnf package revision number if there is
+
+ Returns:
+ artifact_path: (str)
+
+ """
+ extension = ""
+ if revision:
+ extension = ":" + str(revision)
+
+ if base_folder.get("pkg-dir"):
+ artifact_path = "{}/{}/{}/{}".format(
+ base_folder["folder"].split(":")[0] + extension,
+ base_folder["pkg-dir"],
+ "charms"
+ if charm_type in ("native_charm", "lxc_proxy_charm", "k8s_proxy_charm")
+ else "helm-charts",
+ charm_name,
+ )
+
+ else:
+ # For SOL004 packages
+ artifact_path = "{}/Scripts/{}/{}".format(
+ base_folder["folder"].split(":")[0] + extension,
+ "charms"
+ if charm_type in ("native_charm", "lxc_proxy_charm", "k8s_proxy_charm")
+ else "helm-charts",
+ charm_name,
+ )
+
+ return artifact_path
+
+
+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 LcmBase:
+ def __init__(self, msg, logger):
+ """
+
+ :param db: database connection
+ """
+ self.db = Database().instance.db
+ self.msg = msg
+ self.fs = Filesystem().instance.fs
+ self.logger = logger
+
+ def update_db_2(self, item, _id, _desc):
+ """
+ Updates database with _desc information. If success _desc is cleared
+ :param item: collection
+ :param _id: the _id to use in the query filter
+ :param _desc: dictionary with the content to update. Keys are dot separated keys for
+ :return: None. Exception is raised on error
+ """
+ if not _desc:
+ return
+ now = time()
+ _desc["_admin.modified"] = now
+ self.db.set_one(item, {"_id": _id}, _desc)
+ _desc.clear()
+ # except DbException as e:
+ # self.logger.error("Updating {} _id={} with '{}'. Error: {}".format(item, _id, _desc, e))
+
+ def check_charm_hash_changed(
+ self, current_charm_path: str, target_charm_path: str
+ ) -> bool:
+ """Find the target charm has changed or not by checking the hash of
+ old and new charm packages
+
+ Args:
+ current_charm_path (str): Existing charm package artifact path
+ target_charm_path (str): Target charm package artifact path
+
+ Returns:
+ True/False (bool): if charm has changed it returns True
+
+ """
+ # Check if the charm artifacts are available
+ if os.path.exists(self.fs.path + current_charm_path) and os.path.exists(
+ self.fs.path + target_charm_path
+ ):
+ # Compare the hash of charm folders
+ if checksumdir.dirhash(
+ self.fs.path + current_charm_path
+ ) != checksumdir.dirhash(self.fs.path + target_charm_path):
+
+ return True
+
+ return False
+
+ else:
+ raise LcmException(
+ "Charm artifact {} does not exist in the VNF Package".format(
+ self.fs.path + target_charm_path
+ )
+ )
+
+
+class TaskRegistry(LcmBase):