X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=NG-RO%2Fosm_ng_ro%2Fns.py;h=84a800e22b55e9db9a0e3a79c2b170edb81c4b41;hb=abaa1e028bf3ba4d8c3f2fa0b7201ce0a50f0e8b;hp=19ff791a6d161e2305d1204be0af8461c35ced47;hpb=0d8ce07a6fd75ce185e0b35826b0baedb60bfa43;p=osm%2FRO.git diff --git a/NG-RO/osm_ng_ro/ns.py b/NG-RO/osm_ng_ro/ns.py index 19ff791a..84a800e2 100644 --- a/NG-RO/osm_ng_ro/ns.py +++ b/NG-RO/osm_ng_ro/ns.py @@ -117,6 +117,7 @@ class Ns(object): "flavor": Ns._process_flavor_params, "vdu": Ns._process_vdu_params, "affinity-or-anti-affinity-group": Ns._process_affinity_group_params, + "shared-volumes": Ns._process_shared_volumes_params, } self.db_path_map = { "net": "vld", @@ -124,6 +125,7 @@ class Ns(object): "flavor": "flavor", "vdu": "vdur", "affinity-or-anti-affinity-group": "affinity-or-anti-affinity-group", + "shared-volumes": "shared-volumes", } def init_db(self, target_version): @@ -722,9 +724,12 @@ class Ns(object): guest_epa_quota.get("cpu-pinning-policy") == "DEDICATED" and not epa_vcpu_set ): + # Pinning policy "REQUIRE" uses threads as host should support SMT architecture + # Pinning policy "ISOLATE" uses cores as host should not support SMT architecture + # Pinning policy "PREFER" uses threads in case host supports SMT architecture numa[ "cores" - if guest_epa_quota.get("cpu-thread-pinning-policy") != "PREFER" + if guest_epa_quota.get("cpu-thread-pinning-policy") == "ISOLATE" else "threads" ] = max(vcpu_count, 1) local_epa_vcpu_set = True @@ -813,17 +818,23 @@ class Ns(object): db = kwargs.get("db") target_vdur = {} + for vnf in indata.get("vnf", []): + for vdur in vnf.get("vdur", []): + if vdur.get("ns-flavor-id") == target_flavor.get("id"): + target_vdur = vdur + + vim_flavor_id = ( + target_vdur.get("additionalParams", {}).get("OSM", {}).get("vim_flavor_id") + ) + if vim_flavor_id: # vim-flavor-id was passed so flavor won't be created + return {"find_params": {"vim_flavor_id": vim_flavor_id}} + flavor_data = { "disk": int(target_flavor["storage-gb"]), "ram": int(target_flavor["memory-mb"]), "vcpus": int(target_flavor["vcpu-count"]), } - for vnf in indata.get("vnf", []): - for vdur in vnf.get("vdur", []): - if vdur.get("ns-flavor-id") == target_flavor.get("id"): - target_vdur = vdur - if db and isinstance(indata.get("vnf"), list): vnfd_id = indata.get("vnf")[0].get("vnfd-id") vnfd = db.get_one("vnfds", {"_id": vnfd_id}) @@ -856,47 +867,8 @@ class Ns(object): flavor_data_name = flavor_data.copy() flavor_data_name["name"] = target_flavor["name"] extra_dict["params"] = {"flavor_data": flavor_data_name} - return extra_dict - @staticmethod - def _ip_profile_to_ro( - ip_profile: Dict[str, Any], - ) -> Dict[str, Any]: - """[summary] - - Args: - ip_profile (Dict[str, Any]): [description] - - Returns: - Dict[str, Any]: [description] - """ - if not ip_profile: - return None - - ro_ip_profile = { - "ip_version": "IPv4" - if "v4" in ip_profile.get("ip-version", "ipv4") - else "IPv6", - "subnet_address": ip_profile.get("subnet-address"), - "gateway_address": ip_profile.get("gateway-address"), - "dhcp_enabled": ip_profile.get("dhcp-params", {}).get("enabled", False), - "dhcp_start_address": ip_profile.get("dhcp-params", {}).get( - "start-address", None - ), - "dhcp_count": ip_profile.get("dhcp-params", {}).get("count", None), - } - - if ip_profile.get("dns-server"): - ro_ip_profile["dns_address"] = ";".join( - [v["address"] for v in ip_profile["dns-server"] if v.get("address")] - ) - - if ip_profile.get("security-group"): - ro_ip_profile["security_group"] = ip_profile["security-group"] - - return ro_ip_profile - @staticmethod def _process_net_params( target_vld: Dict[str, Any], @@ -960,7 +932,7 @@ class Ns(object): "net_name": ( f"{indata.get('name')[:16]}-{target_vld.get('name', target_vld.get('id'))[:16]}" ), - "ip_profile": Ns._ip_profile_to_ro(vim_info.get("ip_profile")), + "ip_profile": vim_info.get("ip_profile"), "provider_network_profile": vim_info.get("provider_network"), } @@ -1009,12 +981,10 @@ class Ns(object): == "persistent-storage:persistent-storage" ): for vdu_volume in vdu_instantiation_volumes_list: - if ( vdu_volume["vim-volume-id"] and root_disk["id"] == vdu_volume["name"] ): - persistent_root_disk[vsd["id"]] = { "vim_volume_id": vdu_volume["vim-volume-id"], "image_id": vdu.get("sw-image-desc"), @@ -1025,7 +995,6 @@ class Ns(object): return persistent_root_disk else: - if root_disk.get("size-of-storage"): persistent_root_disk[vsd["id"]] = { "image_id": vdu.get("sw-image-desc"), @@ -1036,6 +1005,7 @@ class Ns(object): disk_list.append(persistent_root_disk[vsd["id"]]) return persistent_root_disk + return persistent_root_disk @staticmethod def find_persistent_volumes( @@ -1062,9 +1032,7 @@ class Ns(object): and disk["id"] not in persistent_root_disk.keys() ): for vdu_volume in vdu_instantiation_volumes_list: - if vdu_volume["vim-volume-id"] and disk["id"] == vdu_volume["name"]: - persistent_disk[disk["id"]] = { "vim_volume_id": vdu_volume["vim-volume-id"], } @@ -1093,10 +1061,37 @@ class Ns(object): if not virtual_storage_desc.get("vdu-storage-requirements"): return False for item in virtual_storage_desc.get("vdu-storage-requirements", {}): - if item.get("key") == "keep-volume" and item.get("value") == "true": + if item.get("key") == "keep-volume" and item.get("value").lower() == "true": return True return False + @staticmethod + def is_shared_volume( + virtual_storage_desc: Dict[str, Any], vnfd_id: str + ) -> (str, bool): + """Function to decide if the volume type is multi attached or not . + + Args: + virtual_storage_desc (Dict[str, Any]): virtual storage description dictionary + vnfd_id (str): vnfd id + + Returns: + bool (True/False) + name (str) New name if it is a multiattach disk + """ + + if vdu_storage_requirements := virtual_storage_desc.get( + "vdu-storage-requirements", {} + ): + for item in vdu_storage_requirements: + if ( + item.get("key") == "multiattach" + and item.get("value").lower() == "true" + ): + name = f"shared-{virtual_storage_desc['id']}-{vnfd_id}" + return name, True + return virtual_storage_desc["id"], False + @staticmethod def _sort_vdu_interfaces(target_vdu: dict) -> None: """Sort the interfaces according to position number. @@ -1323,7 +1318,6 @@ class Ns(object): net_list (list): Net list of VDU """ for iface_index, interface in enumerate(target_vdu["interfaces"]): - net_text = Ns._check_vld_information_of_interfaces( interface, ns_preffix, vnf_preffix ) @@ -1426,7 +1420,6 @@ class Ns(object): "size": root_disk["size-of-storage"], "keep": Ns.is_volume_keeping_required(root_disk), } - disk_list.append(persistent_root_disk[vsd["id"]]) break @@ -1436,6 +1429,9 @@ class Ns(object): persistent_root_disk: dict, persistent_ordinary_disk: dict, disk_list: list, + extra_dict: dict, + vnf_id: str = None, + nsr_id: str = None, ) -> None: """Fill the disk list by adding persistent ordinary disks. @@ -1453,11 +1449,18 @@ class Ns(object): == "persistent-storage:persistent-storage" and disk["id"] not in persistent_root_disk.keys() ): + name, multiattach = Ns.is_shared_volume(disk, vnf_id) persistent_ordinary_disk[disk["id"]] = { + "name": name, "size": disk["size-of-storage"], "keep": Ns.is_volume_keeping_required(disk), + "multiattach": multiattach, } disk_list.append(persistent_ordinary_disk[disk["id"]]) + if multiattach: # VDU creation has to wait for shared volumes + extra_dict["depends_on"].append( + f"nsrs:{nsr_id}:shared-volumes.{name}" + ) @staticmethod def _prepare_vdu_affinity_group_list( @@ -1528,27 +1531,23 @@ class Ns(object): flavor_text = ns_preffix + ":flavor." + target_vdu["ns-flavor-id"] extra_dict = {"depends_on": [image_text, flavor_text]} net_list = [] - persistent_root_disk = {} persistent_ordinary_disk = {} vdu_instantiation_volumes_list = [] disk_list = [] vnfd_id = vnfr["vnfd-id"] vnfd = db.get_one("vnfds", {"_id": vnfd_id}) - # If the position info is provided for all the interfaces, it will be sorted # according to position number ascendingly. if all( True if i.get("position") is not None else False for i in target_vdu["interfaces"] ): - Ns._sort_vdu_interfaces(target_vdu) # If the position info is provided for some interfaces but not all of them, the interfaces # which has specific position numbers will be placed and others' positions will not be taken care. else: - Ns._partially_locate_vdu_interfaces(target_vdu) # If the position info is not provided for the interfaces, interfaces will be attached @@ -1571,11 +1570,10 @@ class Ns(object): if target_vdu.get("additionalParams"): vdu_instantiation_volumes_list = ( - target_vdu.get("additionalParams").get("OSM").get("vdu_volumes") + target_vdu.get("additionalParams").get("OSM", {}).get("vdu_volumes") ) if vdu_instantiation_volumes_list: - # Find the root volumes and add to the disk_list persistent_root_disk = Ns.find_persistent_root_volumes( vnfd, target_vdu, vdu_instantiation_volumes_list, disk_list @@ -1598,7 +1596,13 @@ class Ns(object): ) # Add the persistent non-root disks to disk_list Ns._add_persistent_ordinary_disks_to_disk_list( - target_vdu, persistent_root_disk, persistent_ordinary_disk, disk_list + target_vdu, + persistent_root_disk, + persistent_ordinary_disk, + disk_list, + extra_dict, + vnfd["id"], + nsr_id, ) affinity_group_list = Ns._prepare_vdu_affinity_group_list( @@ -1623,7 +1627,24 @@ class Ns(object): "availability_zone_index": None, # TODO "availability_zone_list": None, # TODO } + return extra_dict + @staticmethod + def _process_shared_volumes_params( + target_shared_volume: Dict[str, Any], + indata: Dict[str, Any], + vim_info: Dict[str, Any], + target_record_id: str, + **kwargs: Dict[str, Any], + ) -> Dict[str, Any]: + extra_dict = {} + shared_volume_data = { + "size": target_shared_volume["size-of-storage"], + "name": target_shared_volume["id"], + "type": target_shared_volume["type-of-storage"], + "keep": Ns.is_volume_keeping_required(target_shared_volume), + } + extra_dict["params"] = shared_volume_data return extra_dict @staticmethod @@ -1661,7 +1682,6 @@ class Ns(object): extra_dict["params"] = { "affinity_group_data": affinity_group_data, } - return extra_dict @staticmethod @@ -1697,11 +1717,11 @@ class Ns(object): vim_details = {} vim_details_text = existing_vdu["vim_info"][target_id].get("vim_details", None) + if vim_details_text: vim_details = yaml.safe_load(f"{vim_details_text}") for iface_index, interface in enumerate(existing_vdu["interfaces"]): - if "port-security-enabled" in interface: interface["port_security"] = interface.pop("port-security-enabled") @@ -1891,7 +1911,6 @@ class Ns(object): process_params = None vdu2cloud_init = indata.get("cloud_init_content") or {} ro_nsr_public_key = db_ro_nsr["public_key"] - # According to the type of item, the path, the target_list, # the existing_list and the method to process params are set db_path = self.db_path_map[item] @@ -1911,27 +1930,29 @@ class Ns(object): ) target_list = target_vnf.get(db_path, []) if target_vnf else [] existing_list = vnfr.get(db_path, []) - elif item in ("image", "flavor", "affinity-or-anti-affinity-group"): + elif item in ( + "image", + "flavor", + "affinity-or-anti-affinity-group", + "shared-volumes", + ): db_record = "nsrs:{}:{}".format(nsr_id, db_path) target_list = indata.get(item, []) existing_list = db_nsr.get(item, []) else: raise NsException("Item not supported: {}", item) - # ensure all the target_list elements has an "id". If not assign the index as id if target_list is None: target_list = [] for target_index, tl in enumerate(target_list): if tl and not tl.get("id"): tl["id"] = str(target_index) - # step 1 items (networks,vdus,...) to be deleted/updated for item_index, existing_item in enumerate(existing_list): target_item = next( (t for t in target_list if t["id"] == existing_item["id"]), None, ) - for target_vim, existing_viminfo in existing_item.get( "vim_info", {} ).items(): @@ -1975,7 +1996,6 @@ class Ns(object): # step 2 items (networks,vdus,...) to be created for target_item in target_list: item_index = -1 - for item_index, existing_item in enumerate(existing_list): if existing_item["id"] == target_item["id"]: break @@ -2032,7 +2052,6 @@ class Ns(object): } ) self.logger.debug("calculate_diff_items kwargs={}".format(kwargs)) - extra_dict = process_params( target_item, indata, @@ -2102,7 +2121,12 @@ class Ns(object): changes_list = [] # NS vld, image and flavor - for item in ["net", "image", "flavor", "affinity-or-anti-affinity-group"]: + for item in [ + "net", + "image", + "flavor", + "affinity-or-anti-affinity-group", + ]: self.logger.debug("process NS={} {}".format(nsr_id, item)) diff_items, task_index = self.calculate_diff_items( indata=indata, @@ -2121,7 +2145,7 @@ class Ns(object): # VNF vlds and vdus for vnfr_id, vnfr in db_vnfrs.items(): # vnfr_id need to be set as global variable for among others nested method _process_vdu_params - for item in ["net", "vdu"]: + for item in ["net", "vdu", "shared-volumes"]: self.logger.debug("process VNF={} {}".format(vnfr_id, item)) diff_items, task_index = self.calculate_diff_items( indata=indata, @@ -2348,30 +2372,39 @@ class Ns(object): # Check each VNF of the target for target_vnf in target_list: - # Find this VNF in the list from DB - vnfr_id = target_vnf.get("vnfInstanceId", None) - if vnfr_id: - existing_vnf = db_vnfrs.get(vnfr_id) - db_record = "vnfrs:{}:{}".format(vnfr_id, db_path) - # vim_account_id = existing_vnf.get("vim-account-id", "") + # Find this VNF in the list from DB, raise exception if vnfInstanceId is not found + vnfr_id = target_vnf["vnfInstanceId"] + existing_vnf = db_vnfrs.get(vnfr_id, {}) + db_record = "vnfrs:{}:{}".format(vnfr_id, db_path) + # vim_account_id = existing_vnf.get("vim-account-id", "") + target_vdus = target_vnf.get("additionalParams", {}).get("vdu", []) # Check each VDU of this VNF - for target_vdu in target_vnf["additionalParams"].get("vdu", None): + if not target_vdus: + # Create target_vdu_list from DB, if VDUs are not specified + target_vdus = [] + for existing_vdu in existing_vnf.get("vdur"): + vdu_name = existing_vdu.get("vdu-name", None) + vdu_index = existing_vdu.get("count-index", 0) + vdu_to_be_healed = {"vdu-id": vdu_name, "count-index": vdu_index} + target_vdus.append(vdu_to_be_healed) + for target_vdu in target_vdus: vdu_name = target_vdu.get("vdu-id", None) # For multi instance VDU count-index is mandatory # For single session VDU count-indes is 0 count_index = target_vdu.get("count-index", 0) item_index = 0 - existing_instance = None - for instance in existing_vnf.get("vdur", None): - if ( - instance["vdu-name"] == vdu_name - and instance["count-index"] == count_index - ): - existing_instance = instance - break - else: - item_index += 1 + existing_instance = {} + if existing_vnf: + for instance in existing_vnf.get("vdur", {}): + if ( + instance["vdu-name"] == vdu_name + and instance["count-index"] == count_index + ): + existing_instance = instance + break + else: + item_index += 1 target_record_id = "{}.{}".format(db_record, existing_instance["id"]) @@ -2860,7 +2893,9 @@ class Ns(object): extra_dict, ): self._assign_vim(target_vim) - target_record = "vnfrs:{}:vdur.{}".format(vnf_id, vdu_index) + target_record = "vnfrs:{}:vdur.{}.vim_info.{}".format( + vnf_id, vdu_index, target_vim + ) target_record_id = "vnfrs:{}:vdur.{}".format(vnf_id, vdu_id) deployment_info = { "action_id": action_id, @@ -2966,7 +3001,9 @@ class Ns(object): ): target_vim, vim_info = next(k_v for k_v in vdu["vim_info"].items()) self._assign_vim(target_vim) - target_record = "vnfrs:{}:vdur.{}".format(vnf["_id"], vdu_index) + target_record = "vnfrs:{}:vdur.{}.vim_info.{}".format( + vnf["_id"], vdu_index, target_vim + ) target_record_id = "vnfrs:{}:vdur.{}".format(vnf["_id"], vdu["id"]) deployment_info = { "action_id": action_id, @@ -3030,7 +3067,6 @@ class Ns(object): task_index += 1 break else: - for vdu_index, vdu in enumerate(db_vnfr["vdur"]): extra_dict["params"] = { "vim_vm_id": vdu["vim-id"], @@ -3081,7 +3117,9 @@ class Ns(object): ): target_vim, vim_info = next(k_v for k_v in vdu["vim_info"].items()) self._assign_vim(target_vim) - target_record = "vnfrs:{}:vdur.{}".format(vnf["_id"], vdu_index) + target_record = "vnfrs:{}:vdur.{}.vim_info.{}".format( + vnf["_id"], vdu_index, target_vim + ) target_record_id = "vnfrs:{}:vdur.{}".format(vnf["_id"], vdu["id"]) deployment_info = { "action_id": action_id,