X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=rwlaunchpad%2Fplugins%2Frwvnfm%2Frift%2Ftasklets%2Frwvnfmtasklet%2Frwvnfmtasklet.py;h=2cbe240429ad9e4a9afb72c9afd47a8b2fd81892;hb=ee71ccf6da85650a8fbd2019293535082f017b78;hp=1a896812c6b69c6125d597a160e5447e49ad4155;hpb=f027e7263e0c1e1dfa28d9b4cfd4a747e7f29db9;p=osm%2FSO.git diff --git a/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py b/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py index 1a896812..2cbe2404 100755 --- a/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py +++ b/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py @@ -49,6 +49,9 @@ from gi.repository import ( import rift.tasklets import rift.package.store import rift.package.cloud_init +import rift.package.script +import rift.mano.dts as mano_dts +import rift.mano.utils.short_name as mano_short_name class VMResourceError(Exception): @@ -269,7 +272,9 @@ class VirtualDeploymentUnitRecord(object): loop, vdud, vnfr, + nsr_config, mgmt_intf, + mgmt_network, cloud_account_name, vnfd_package_store, vdur_id=None, @@ -279,9 +284,11 @@ class VirtualDeploymentUnitRecord(object): self._loop = loop self._vdud = vdud self._vnfr = vnfr + self._nsr_config = nsr_config self._mgmt_intf = mgmt_intf self._cloud_account_name = cloud_account_name self._vnfd_package_store = vnfd_package_store + self._mgmt_network = mgmt_network self._vdur_id = vdur_id or str(uuid.uuid4()) self._int_intf = [] @@ -308,6 +315,14 @@ class VirtualDeploymentUnitRecord(object): return conn_point.ip_address return "0.0.0.0" + def cp_mac_addr(self, cp_name): + """ Find mac address by connection point name """ + if self._vm_resp is not None: + for conn_point in self._vm_resp.connection_points: + if conn_point.name == cp_name: + return conn_point.mac_addr + return "00:00:00:00:00:00" + def cp_id(self, cp_name): """ Find connection point id by connection point name """ if self._vm_resp is not None: @@ -329,6 +344,31 @@ class VirtualDeploymentUnitRecord(object): """ Return this VDUR's name """ return self._name + # Truncated name confirming to RFC 1123 + @property + def unique_short_name(self): + """ Return this VDUR's unique short name """ + # Impose these restrictions on Unique name + # Max 64 + # - Max 10 of NSR name (remove all specialcharacters, only numbers and alphabets) + # - 6 chars of shortened name + # - Max 10 of VDU name (remove all specialcharacters, only numbers and alphabets) + # + def _restrict_tag(input_str): + # Exclude all characters except a-zA-Z0-9 + outstr = re.sub('[^a-zA-Z0-9]', '', input_str) + # Take max of 10 chars + return outstr[-10:] + + # Use NSR name for part1 + part1 = _restrict_tag(self._nsr_config.name) + # Get unique short string (6 chars) + part2 = mano_short_name.StringShortner(self._name) + # Use VDU ID for part3 + part3 = _restrict_tag(self._vdud.id) + shortstr = part1 + "-" + part2.short_string + "-" + part3 + return shortstr + @property def cloud_account_name(self): """ Cloud account this VDU should be created in """ @@ -337,6 +377,8 @@ class VirtualDeploymentUnitRecord(object): @property def image_name(self): """ name that should be used to lookup the image on the CMP """ + if 'image' not in self._vdud: + return None return os.path.basename(self._vdud.image) @property @@ -371,25 +413,30 @@ class VirtualDeploymentUnitRecord(object): @property def msg(self): - """ VDU message """ + """ Process VDU message from resmgr""" vdu_fields = ["vm_flavor", "guest_epa", "vswitch_epa", "hypervisor_epa", "host_epa", - "name"] + "volumes", + ] vdu_copy_dict = {k: v for k, v in self._vdud.as_dict().items() if k in vdu_fields} vdur_dict = {"id": self._vdur_id, "vdu_id_ref": self._vdud.id, "operational_status": self.operational_status, "operational_status_details": self._state_failed_reason, + "name": self.name, + "unique_short_name": self.unique_short_name } + if self.vm_resp is not None: vdur_dict.update({"vim_id": self.vm_resp.vdu_id, - "flavor_id": self.vm_resp.flavor_id, - "image_id": self.vm_resp.image_id, + "flavor_id": self.vm_resp.flavor_id }) + if self._vm_resp.has_field('image_id'): + vdur_dict.update({ "image_id": self.vm_resp.image_id }) if self.management_ip is not None: vdur_dict["management_ip"] = self.management_ip @@ -399,6 +446,33 @@ class VirtualDeploymentUnitRecord(object): vdur_dict.update(vdu_copy_dict) + if self.vm_resp is not None: + if self._vm_resp.has_field('volumes'): + for opvolume in self._vm_resp.volumes: + vdurvol_data = [vduvol for vduvol in vdur_dict['volumes'] if vduvol['name'] == opvolume.name] + if len(vdurvol_data) == 1: + vdurvol_data[0]["volume_id"] = opvolume.volume_id + if opvolume.has_field('custom_meta_data'): + metadata_list = list() + for metadata_item in opvolume.custom_meta_data: + metadata_list.append(metadata_item.as_dict()) + vdurvol_data[0]['custom_meta_data'] = metadata_list + + if self._vm_resp.has_field('supplemental_boot_data'): + vdur_dict['supplemental_boot_data'] = dict() + if self._vm_resp.supplemental_boot_data.has_field('boot_data_drive'): + vdur_dict['supplemental_boot_data']['boot_data_drive'] = self._vm_resp.supplemental_boot_data.boot_data_drive + if self._vm_resp.supplemental_boot_data.has_field('custom_meta_data'): + metadata_list = list() + for metadata_item in self._vm_resp.supplemental_boot_data.custom_meta_data: + metadata_list.append(metadata_item.as_dict()) + vdur_dict['supplemental_boot_data']['custom_meta_data'] = metadata_list + if self._vm_resp.supplemental_boot_data.has_field('config_file'): + file_list = list() + for file_item in self._vm_resp.supplemental_boot_data.config_file: + file_list.append(file_item.as_dict()) + vdur_dict['supplemental_boot_data']['config_file'] = file_list + icp_list = [] ii_list = [] @@ -408,7 +482,8 @@ class VirtualDeploymentUnitRecord(object): icp_list.append({"name": cp.name, "id": cp.id, "type_yang": "VPORT", - "ip_address": self.cp_ip_addr(cp.id)}) + "ip_address": self.cp_ip_addr(cp.id), + "mac_address": self.cp_mac_addr(cp.id)}) ii_list.append({"name": intf.name, "vdur_internal_connection_point_ref": cp.id, @@ -420,18 +495,21 @@ class VirtualDeploymentUnitRecord(object): ei_list = [] for intf, cp, vlr in self._ext_intf: - ei_list.append({"name": cp, - "vnfd_connection_point_ref": cp, + ei_list.append({"name": cp.name, + "vnfd_connection_point_ref": cp.name, "virtual_interface": {}}) - self._vnfr.update_cp(cp, self.cp_ip_addr(cp), self.cp_id(cp)) + self._vnfr.update_cp(cp.name, + self.cp_ip_addr(cp.name), + self.cp_mac_addr(cp.name), + self.cp_id(cp.name)) vdur_dict["external_interface"] = ei_list placement_groups = [] for group in self._placement_groups: placement_groups.append(group.as_dict()) - vdur_dict['placement_groups_info'] = placement_groups + return RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vdur.from_dict(vdur_dict) @property @@ -474,6 +552,7 @@ class VirtualDeploymentUnitRecord(object): try: return cloud_init_extractor.read_script(stored_package, filename) except rift.package.cloud_init.CloudInitExtractionError as e: + self.instantiation_failed(str(e)) raise VirtualDeploymentUnitRecordError(e) else: self._log.debug("VDU Instantiation: cloud-init script not provided") @@ -526,21 +605,49 @@ class VirtualDeploymentUnitRecord(object): self._log.info("Ignoring placement group with cloud construct for cloud-type: %s", cloud_type) return + def process_custom_bootdata(self, vm_create_msg_dict): + """Process the custom boot data""" + if 'config_file' not in vm_create_msg_dict['supplemental_boot_data']: + return + + self._vnfd_package_store.refresh() + stored_package = self._vnfd_package_store.get_package(self._vnfr.vnfd_id) + cloud_init_extractor = rift.package.cloud_init.PackageCloudInitExtractor(self._log) + for file_item in vm_create_msg_dict['supplemental_boot_data']['config_file']: + if 'source' not in file_item or 'dest' not in file_item: + continue + source = file_item['source'] + # Find source file in scripts dir of VNFD + self._log.debug("Checking for source config file at %s", source) + try: + source_file_str = cloud_init_extractor.read_script(stored_package, source) + except rift.package.cloud_init.CloudInitExtractionError as e: + raise VirtualDeploymentUnitRecordError(e) + # Update source file location with file contents + file_item['source'] = source_file_str + + return + def resmgr_msg(self, config=None): vdu_fields = ["vm_flavor", "guest_epa", "vswitch_epa", "hypervisor_epa", - "host_epa"] + "host_epa", + "volumes", + "supplemental_boot_data"] self._log.debug("Creating params based on VDUD: %s", self._vdud) vdu_copy_dict = {k: v for k, v in self._vdud.as_dict().items() if k in vdu_fields} vm_create_msg_dict = { - "name": self.name, - "image_name": self.image_name, + "name": self.unique_short_name, # Truncated name confirming to RFC 1123 + "node_id": self.name, # Rift assigned Id } + if self.image_name is not None: + vm_create_msg_dict["image_name"] = self.image_name + if self.image_checksum is not None: vm_create_msg_dict["image_checksum"] = self.image_checksum @@ -552,11 +659,17 @@ class VirtualDeploymentUnitRecord(object): if config is not None: vm_create_msg_dict['vdu_init'] = {'userdata': config} + if self._mgmt_network: + vm_create_msg_dict['mgmt_network'] = self._mgmt_network + cp_list = [] for intf, cp, vlr in self._ext_intf: - cp_info = {"name": cp, - "virtual_link_id": vlr.network_id, - "type_yang": intf.virtual_interface.type_yang} + cp_info = { "name": cp.name, + "virtual_link_id": vlr.network_id, + "type_yang": intf.virtual_interface.type_yang } + + if cp.has_field('port_security_enabled'): + cp_info["port_security_enabled"] = cp.port_security_enabled if (intf.virtual_interface.has_field('vpci') and intf.virtual_interface.vpci is not None): @@ -575,19 +688,29 @@ class VirtualDeploymentUnitRecord(object): "type_yang": intf.virtual_interface.type_yang, "vpci": intf.virtual_interface.vpci}) else: - cp_list.append({"name": cp, - "virtual_link_id": vlr.network_id, - "type_yang": intf.virtual_interface.type_yang}) + if cp.has_field('port_security_enabled'): + cp_list.append({"name": cp, + "virtual_link_id": vlr.network_id, + "type_yang": intf.virtual_interface.type_yang, + "port_security_enabled": cp.port_security_enabled}) + else: + cp_list.append({"name": cp, + "virtual_link_id": vlr.network_id, + "type_yang": intf.virtual_interface.type_yang}) + vm_create_msg_dict["connection_points"] = cp_list vm_create_msg_dict.update(vdu_copy_dict) self.process_placement_groups(vm_create_msg_dict) + if 'supplemental_boot_data' in vm_create_msg_dict: + self.process_custom_bootdata(vm_create_msg_dict) msg = RwResourceMgrYang.VDUEventData() msg.event_id = self._request_id msg.cloud_account = self.cloud_account_name msg.request_info.from_dict(vm_create_msg_dict) + return msg @asyncio.coroutine @@ -699,7 +822,7 @@ class VirtualDeploymentUnitRecord(object): vlr = vnfr.ext_vlr_by_id(cp.vlr_ref) - etuple = (ext_intf, cp.name, vlr) + etuple = (ext_intf, cp, vlr) self._ext_intf.append(etuple) self._log.debug("Created external interface tuple : %s", etuple) @@ -860,8 +983,8 @@ class VirtualDeploymentUnitRecord(object): vm_resp = yield from self.create_resource(xact, vnfr, config) self._vm_resp = vm_resp - self._state = VDURecordState.RESOURCE_ALLOC_PENDING + self._log.debug("Requested VM from resource manager response %s", vm_resp) if vm_resp.resource_state == "active": @@ -904,13 +1027,14 @@ class VlRecordState(enum.Enum): class InternalVirtualLinkRecord(object): """ Internal Virtual Link record """ - def __init__(self, dts, log, loop, ivld_msg, vnfr_name, cloud_account_name): + def __init__(self, dts, log, loop, ivld_msg, vnfr_name, cloud_account_name, ip_profile=None): self._dts = dts self._log = log self._loop = loop self._ivld_msg = ivld_msg self._vnfr_name = vnfr_name self._cloud_account_name = cloud_account_name + self._ip_profile = ip_profile self._vlr_req = self.create_vlr() self._vlr = None @@ -924,7 +1048,10 @@ class InternalVirtualLinkRecord(object): @property def name(self): """ Name of this VL """ - return self._vnfr_name + "." + self._ivld_msg.name + if self._ivld_msg.vim_network_name: + return self._ivld_msg.vim_network_name + else: + return self._vnfr_name + "." + self._ivld_msg.name @property def network_id(self): @@ -943,6 +1070,7 @@ class InternalVirtualLinkRecord(object): "description", "version", "type_yang", + "vim_network_name", "provider_network"] vld_copy_dict = {k: v for k, v in self._ivld_msg.as_dict().items() if k in vld_fields} @@ -951,6 +1079,10 @@ class InternalVirtualLinkRecord(object): "name": self.name, "cloud_account": self._cloud_account_name, } + + if self._ip_profile and self._ip_profile.has_field('ip_profile_params'): + vlr_dict['ip_profile_params' ] = self._ip_profile.ip_profile_params.as_dict() + vlr_dict.update(vld_copy_dict) vlr = RwVlrYang.YangData_Vlr_VlrCatalog_Vlr.from_dict(vlr_dict) @@ -1046,19 +1178,20 @@ class InternalVirtualLinkRecord(object): class VirtualNetworkFunctionRecord(object): """ Virtual Network Function Record """ - def __init__(self, dts, log, loop, cluster_name, vnfm, vcs_handler, vnfr_msg): + def __init__(self, dts, log, loop, cluster_name, vnfm, vcs_handler, vnfr_msg, mgmt_network=None): self._dts = dts self._log = log self._loop = loop self._cluster_name = cluster_name self._vnfr_msg = vnfr_msg self._vnfr_id = vnfr_msg.id - self._vnfd_id = vnfr_msg.vnfd_ref + self._vnfd_id = vnfr_msg.vnfd.id self._vnfm = vnfm self._vcs_handler = vcs_handler self._vnfr = vnfr_msg + self._mgmt_network = mgmt_network - self._vnfd = None + self._vnfd = vnfr_msg.vnfd self._state = VirtualNetworkFunctionRecordState.INIT self._state_failed_reason = None self._ext_vlrs = {} # The list of external virtual links @@ -1071,6 +1204,8 @@ class VirtualNetworkFunctionRecord(object): self._vnf_mon = None self._config_status = vnfr_msg.config_status self._vnfd_package_store = rift.package.store.VnfdPackageFilesystemStore(self._log) + self._rw_vnfd = None + self._vnfd_ref_count = 0 def _get_vdur_from_vdu_id(self, vdu_id): self._log.debug("Finding vdur for vdu_id %s", vdu_id) @@ -1096,11 +1231,36 @@ class VirtualNetworkFunctionRecord(object): "FAILED": "failed", } return op_status_map[self._state.name] - @property - def vnfd_xpath(self): + @staticmethod + def vnfd_xpath(vnfd_id): """ VNFD xpath associated with this VNFR """ - return("C,/vnfd:vnfd-catalog/" - "vnfd:vnfd[vnfd:id = '{}']".format(self._vnfd_id)) + return "C,/vnfd:vnfd-catalog/vnfd:vnfd[vnfd:id = '{}']".format(vnfd_id) + + @property + def vnfd_ref_count(self): + """ Returns the VNFD reference count associated with this VNFR """ + return self._vnfd_ref_count + + def vnfd_in_use(self): + """ Returns whether vnfd is in use or not """ + return True if self._vnfd_ref_count > 0 else False + + def vnfd_ref(self): + """ Take a reference on this object """ + self._vnfd_ref_count += 1 + return self._vnfd_ref_count + + def vnfd_unref(self): + """ Release reference on this object """ + if self._vnfd_ref_count < 1: + msg = ("Unref on a VNFD object - vnfd id %s, vnfd_ref_count = %s" % + (self.vnfd.id, self._vnfd_ref_count)) + self._log.critical(msg) + raise VnfRecordError(msg) + self._log.debug("Releasing ref on VNFD %s - curr vnfd_ref_count:%s", + self.vnfd.id, self._vnfd_ref_count) + self._vnfd_ref_count -= 1 + return self._vnfd_ref_count @property def vnfd(self): @@ -1182,7 +1342,7 @@ class VirtualNetworkFunctionRecord(object): def mgmt_intf_info(self): """ Get Management interface info for this VNFR """ - mgmt_intf_desc = self.vnfd.msg.mgmt_interface + mgmt_intf_desc = self.vnfd.mgmt_interface ip_addr = None if mgmt_intf_desc.has_field("cp"): ip_addr = self.cp_ip_addr(mgmt_intf_desc.cp) @@ -1203,7 +1363,7 @@ class VirtualNetworkFunctionRecord(object): def msg(self): """ Message associated with this VNFR """ vnfd_fields = ["short_name", "vendor", "description", "version"] - vnfd_copy_dict = {k: v for k, v in self.vnfd.msg.as_dict().items() if k in vnfd_fields} + vnfd_copy_dict = {k: v for k, v in self.vnfd.as_dict().items() if k in vnfd_fields} mgmt_intf = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_MgmtInterface() ip_address, port = self.mgmt_intf_info() @@ -1217,7 +1377,6 @@ class VirtualNetworkFunctionRecord(object): "nsr_id_ref": self._vnfr_msg.nsr_id_ref, "name": self.name, "member_vnf_index_ref": self.member_vnf_index, - "vnfd_ref": self.vnfd_id, "operational_status": self.operational_status, "operational_status_details": self._state_failed_reason, "cloud_account": self.cloud_account_name, @@ -1227,6 +1386,9 @@ class VirtualNetworkFunctionRecord(object): vnfr_dict.update(vnfd_copy_dict) vnfr_msg = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr.from_dict(vnfr_dict) + vnfr_msg.vnfd = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vnfd.from_dict(self.vnfd.as_dict()) + + vnfr_msg.create_time = self._create_time vnfr_msg.uptime = int(time.time()) - self._create_time vnfr_msg.mgmt_interface = mgmt_intf @@ -1241,7 +1403,7 @@ class VirtualNetworkFunctionRecord(object): vdur = vnfr_msg.vdur.add() vdur.from_dict(vdu.msg.as_dict()) - if self.vnfd.msg.mgmt_interface.has_field('dashboard_params'): + if self.vnfd.mgmt_interface.has_field('dashboard_params'): vnfr_msg.dashboard_url = self.dashboard_url for cpr in self._cprs: @@ -1271,18 +1433,18 @@ class VirtualNetworkFunctionRecord(object): ip, cfg_port = self.mgmt_intf_info() protocol = 'http' http_port = 80 - if self.vnfd.msg.mgmt_interface.dashboard_params.has_field('https'): - if self.vnfd.msg.mgmt_interface.dashboard_params.https is True: + if self.vnfd.mgmt_interface.dashboard_params.has_field('https'): + if self.vnfd.mgmt_interface.dashboard_params.https is True: protocol = 'https' http_port = 443 - if self.vnfd.msg.mgmt_interface.dashboard_params.has_field('port'): - http_port = self.vnfd.msg.mgmt_interface.dashboard_params.port + if self.vnfd.mgmt_interface.dashboard_params.has_field('port'): + http_port = self.vnfd.mgmt_interface.dashboard_params.port url = "{protocol}://{ip_address}:{port}/{path}".format( protocol=protocol, ip_address=ip, port=http_port, - path=self.vnfd.msg.mgmt_interface.dashboard_params.path.lstrip("/"), + path=self.vnfd.mgmt_interface.dashboard_params.path.lstrip("/"), ) return url @@ -1304,12 +1466,19 @@ class VirtualNetworkFunctionRecord(object): self._log.debug("Published VNFR path = [%s], record = [%s]", self.xpath, self.msg) + def resolve_vld_ip_profile(self, vnfd_msg, vld): + self._log.debug("Receieved ip profile ref is %s",vld.ip_profile_ref) + if not vld.has_field('ip_profile_ref'): + return None + profile = [profile for profile in vnfd_msg.ip_profiles if profile.name == vld.ip_profile_ref] + return profile[0] if profile else None + @asyncio.coroutine def create_vls(self): """ Publish The VLs associated with this VNF """ self._log.debug("Publishing Internal Virtual Links for vnfd id: %s", self.vnfd_id) - for ivld_msg in self.vnfd.msg.internal_vld: + for ivld_msg in self.vnfd.internal_vld: self._log.debug("Creating internal vld:" " %s, int_cp_ref = %s", ivld_msg, ivld_msg.internal_connection_point @@ -1319,7 +1488,8 @@ class VirtualNetworkFunctionRecord(object): loop=self._loop, ivld_msg=ivld_msg, vnfr_name=self.name, - cloud_account_name=self.cloud_account_name + cloud_account_name=self.cloud_account_name, + ip_profile=self.resolve_vld_ip_profile(self.vnfd, ivld_msg) ) self._vlrs.append(vlr) @@ -1368,7 +1538,7 @@ class VirtualNetworkFunctionRecord(object): return None @asyncio.coroutine - def get_vdu_placement_groups(self, vdu): + def get_vdu_placement_groups(self, vdu, nsr_config): placement_groups = [] ### Step-1: Get VNF level placement groups for group in self._vnfr_msg.placement_groups_info: @@ -1376,11 +1546,8 @@ class VirtualNetworkFunctionRecord(object): #group_info.from_dict(group.as_dict()) placement_groups.append(group) - ### Step-2: Get NSR config. This is required for resolving placement_groups cloud constructs - nsr_config = yield from self.get_nsr_config() - - ### Step-3: Get VDU level placement groups - for group in self.vnfd.msg.placement_groups: + ### Step-2: Get VDU level placement groups + for group in self.vnfd.placement_groups: for member_vdu in group.member_vdus: if member_vdu.member_vdu_ref == vdu.id: group_info = self.resolve_placement_group_cloud_construct(group, @@ -1398,6 +1565,10 @@ class VirtualNetworkFunctionRecord(object): return placement_groups + @asyncio.coroutine + def vdu_cloud_init_instantiation(self): + [vdu.vdud_cloud_init for vdu in self._vdus] + @asyncio.coroutine def create_vdus(self, vnfr, restart_mode=False): """ Create the VDUs associated with this VNF """ @@ -1422,16 +1593,22 @@ class VirtualNetworkFunctionRecord(object): self._log.info("Creating VDU's for vnfd id: %s", self.vnfd_id) - for vdu in self.vnfd.msg.vdu: + + # Get NSR config - Needed for placement groups and to derive VDU short-name + nsr_config = yield from self.get_nsr_config() + + for vdu in self._rw_vnfd.vdu: self._log.debug("Creating vdu: %s", vdu) vdur_id = get_vdur_id(vdu) - placement_groups = yield from self.get_vdu_placement_groups(vdu) - self._log.info("Launching VDU: %s from VNFD :%s (Member Index: %s) with Placement Groups: %s", + + placement_groups = yield from self.get_vdu_placement_groups(vdu, nsr_config) + self._log.info("Launching VDU: %s from VNFD :%s (Member Index: %s) with Placement Groups: %s, Existing vdur_id %s", vdu.name, self.vnf_name, self.member_vnf_index, - [ group.name for group in placement_groups]) + [ group.name for group in placement_groups], + vdur_id) vdur = VirtualDeploymentUnitRecord( dts=self._dts, @@ -1439,7 +1616,9 @@ class VirtualNetworkFunctionRecord(object): loop=self._loop, vdud=vdu, vnfr=vnfr, + nsr_config=nsr_config, mgmt_intf=self.has_mgmt_interface(vdu), + mgmt_network=self._mgmt_network, cloud_account_name=self.cloud_account_name, vnfd_package_store=self._vnfd_package_store, vdur_id=vdur_id, @@ -1461,8 +1640,8 @@ class VirtualNetworkFunctionRecord(object): vdu_id_pattern = re.compile(r"\{\{ vdu\[([^]]+)\]\S* \}\}") for vdu in self._vdus: - if vdu.vdud_cloud_init is not None: - for vdu_id in vdu_id_pattern.findall(vdu.vdud_cloud_init): + if vdu._vdud_cloud_init is not None: + for vdu_id in vdu_id_pattern.findall(vdu._vdud_cloud_init): if vdu_id != vdu.vdu_id: # This means that vdu.vdu_id depends upon vdu_id, # i.e. vdu_id must be instantiated before @@ -1490,7 +1669,6 @@ class VirtualNetworkFunctionRecord(object): # wait for the VDUR to enter a terminal state while vdu._state not in terminal: yield from asyncio.sleep(1, loop=self._loop) - # update the datastore datastore.update(vdu) @@ -1525,7 +1703,7 @@ class VirtualNetworkFunctionRecord(object): datastore.add(vdu) # Substitute any variables contained in the cloud config script - config = str(vdu.vdud_cloud_init) + config = str(vdu.vdud_cloud_init) if vdu.vdud_cloud_init is not None else "" parts = re.split("\{\{ ([^\}]+) \}\}", config) if len(parts) > 1: @@ -1577,7 +1755,7 @@ class VirtualNetworkFunctionRecord(object): def has_mgmt_interface(self, vdu): # ## TODO: Support additional mgmt_interface type options - if self.vnfd.msg.mgmt_interface.vdu_id == vdu.id: + if self.vnfd.mgmt_interface.vdu_id == vdu.id: return True return False @@ -1596,7 +1774,7 @@ class VirtualNetworkFunctionRecord(object): """ Publish the inventory associated with this VNF """ self._log.debug("Publishing inventory for VNFR id: %s", self._vnfr_id) - for component in self.vnfd.msg.component: + for component in self._rw_vnfd.component: self._log.debug("Creating inventory component %s", component) mangled_name = VcsComponent.mangle_name(component.component_name, self.vnf_name, @@ -1652,13 +1830,14 @@ class VirtualNetworkFunctionRecord(object): # Update the VNFR with the changed status yield from self.publish(None) - def update_cp(self, cp_name, ip_address, cp_id): + def update_cp(self, cp_name, ip_address, mac_addr, cp_id): """Updated the connection point with ip address""" for cp in self._cprs: if cp.name == cp_name: self._log.debug("Setting ip address and id for cp %s, cpr %s with ip %s id %s", cp_name, cp, ip_address, cp_id) cp.ip_address = ip_address + cp.mac_address = mac_addr cp.connection_point_id = cp_id return @@ -1674,6 +1853,7 @@ class VirtualNetworkFunctionRecord(object): def instantiate(self, xact, restart_mode=False): """ instantiate this VNF """ self.set_state(VirtualNetworkFunctionRecordState.VL_INIT_PHASE) + self._rw_vnfd = yield from self._vnfm.fetch_vnfd(self._vnfd_id) @asyncio.coroutine def fetch_vlrs(): @@ -1683,7 +1863,7 @@ class VirtualNetworkFunctionRecord(object): def cpr_from_cp(cp): """ Creates a record level connection point from the desciptor cp""" - cp_fields = ["name", "image", "vm-flavor"] + cp_fields = ["name", "image", "vm-flavor", "port_security_enabled"] cp_copy_dict = {k: v for k, v in cp.as_dict().items() if k in cp_fields} cpr_dict = {} cpr_dict.update(cp_copy_dict) @@ -1708,12 +1888,10 @@ class VirtualNetworkFunctionRecord(object): cpr.vlr_ref = cp.vlr_ref self._log.debug("Fetched VLR [%s] with path = [%s]", d, vlr_path) - # Fetch the VNFD associated with the VNFR - self._log.debug("VNFR-ID %s: Fetching vnfds", self._vnfr_id) - self._vnfd = yield from self._vnfm.get_vnfd_ref(self._vnfd_id) - self._log.debug("VNFR-ID %s: Fetched vnfd:%s", self._vnfr_id, self._vnfd) + # Increase the VNFD reference count + self.vnfd_ref() - assert self.vnfd is not None + assert self.vnfd # Fetch External VLRs self._log.debug("VNFR-ID %s: Fetching vlrs", self._vnfr_id) @@ -1731,8 +1909,9 @@ class VirtualNetworkFunctionRecord(object): self._log.debug("VNFR-ID %s: Publish VNFR", self._vnfr_id) yield from self.publish(xact) + # instantiate VLs - self._log.debug("VNFR-ID %s: Instantiate VLs", self._vnfr_id) + self._log.debug("VNFR-ID %s: Instantiate VLs, restart mode %s", self._vnfr_id, restart_mode) try: yield from self.instantiate_vls(xact, restart_mode) except Exception as e: @@ -1743,9 +1922,16 @@ class VirtualNetworkFunctionRecord(object): self.set_state(VirtualNetworkFunctionRecordState.VM_INIT_PHASE) # instantiate VDUs - self._log.debug("VNFR-ID %s: Create VDUs", self._vnfr_id) + self._log.debug("VNFR-ID %s: Create VDUs, restart mode %s", self._vnfr_id, restart_mode) yield from self.create_vdus(self, restart_mode) + try: + yield from self.vdu_cloud_init_instantiation() + except Exception as e: + self.set_state(VirtualNetworkFunctionRecordState.FAILED) + self._state_failed_reason = str(e) + yield from self.publish(xact) + # publish the VNFR self._log.debug("VNFR-ID %s: Publish VNFR", self._vnfr_id) yield from self.publish(xact) @@ -1842,13 +2028,6 @@ class VnfdDtsHandler(object): xact, action, scratch) is_recovery = xact.xact is None and action == rwdts.AppconfAction.INSTALL - # Create/Update a VNFD record - for cfg in self._regh.get_xact_elements(xact): - # Only interested in those VNFD cfgs whose ID was received in prepare callback - if cfg.id in scratch.get('vnfds', []) or is_recovery: - self._vnfm.update_vnfd(cfg) - - scratch.pop('vnfds', None) @asyncio.coroutine def on_prepare(dts, acg, xact, xact_info, ks_path, msg, scratch): @@ -1858,7 +2037,7 @@ class VnfdDtsHandler(object): fref = ProtobufC.FieldReference.alloc() fref.goto_whole_message(msg.to_pbcm()) - # Handle deletes in prepare_callback, but adds/updates in apply_callback + # Handle deletes in prepare_callback if fref.is_field_deleted(): # Delete an VNFD record self._log.debug("Deleting VNFD with id %s", msg.id) @@ -1868,17 +2047,6 @@ class VnfdDtsHandler(object): raise VirtualNetworkFunctionDescriptorRefCountExists(err) # Delete a VNFD record yield from self._vnfm.delete_vnfd(msg.id) - else: - # Handle actual adds/updates in apply_callback, - # just check if VNFD in use in prepare_callback - if self._vnfm.vnfd_in_use(msg.id): - self._log.debug("Cannot modify an VNFD in use - %s", msg) - err = "Cannot modify an VNFD in use - %s" % msg - raise VirtualNetworkFunctionDescriptorRefCountExists(err) - - # Add this VNFD to scratch to create/update in apply callback - vnfds = scratch.setdefault('vnfds', []) - vnfds.append(msg.id) xact_info.respond_xpath(rwdts.XactRspCode.ACK) @@ -1970,7 +2138,7 @@ class VnfrConsoleOperdataDtsHandler(object): if action == rwdts.QueryAction.READ: schema = RwVnfrYang.YangData_RwVnfr_VnfrConsole_Vnfr_Vdur.schema() path_entry = schema.keyspec_to_entry(ks_path) - self._log.debug("VDU Opdata path is {}".format(path_entry)) + self._log.debug("VDU Opdata path is {}".format(path_entry.key00.id)) try: vnfr = self._vnfm.get_vnfr(self._vnfr_id) except VnfRecordError as e: @@ -2085,8 +2253,8 @@ class VnfrDtsHandler(object): ) if action == rwdts.QueryAction.CREATE: - if not msg.has_field("vnfd_ref"): - err = "Vnfd reference not provided" + if not msg.has_field("vnfd"): + err = "Vnfd not provided" self._log.error(err) raise VnfRecordError(err) @@ -2113,7 +2281,7 @@ class VnfrDtsHandler(object): try: yield from vnfr.terminate(xact_info.xact) # Unref the VNFD - vnfr.vnfd.unref() + vnfr.vnfd_unref() yield from self._vnfm.delete_vnfr(xact_info.xact, vnfr) except Exception as e: self._log.exception(e) @@ -2195,100 +2363,6 @@ class VnfrDtsHandler(object): self._log.debug("Deleted VNFR xact = %s, %s", xact, path) -class VirtualNetworkFunctionDescriptor(object): - """ - Virtual Network Function descriptor class - """ - - def __init__(self, dts, log, loop, vnfm, vnfd): - self._dts = dts - self._log = log - self._loop = loop - - self._vnfm = vnfm - self._vnfd = vnfd - self._ref_count = 0 - - @property - def ref_count(self): - """ Returns the reference count associated with - this Virtual Network Function Descriptor""" - return self._ref_count - - @property - def id(self): - """ Returns vnfd id """ - return self._vnfd.id - - @property - def name(self): - """ Returns vnfd name """ - return self._vnfd.name - - def in_use(self): - """ Returns whether vnfd is in use or not """ - return True if self._ref_count > 0 else False - - def ref(self): - """ Take a reference on this object """ - self._ref_count += 1 - return self._ref_count - - def unref(self): - """ Release reference on this object """ - if self.ref_count < 1: - msg = ("Unref on a VNFD object - vnfd id %s, ref_count = %s" % - (self.id, self._ref_count)) - self._log.critical(msg) - raise VnfRecordError(msg) - self._log.debug("Releasing ref on VNFD %s - curr ref_count:%s", - self.id, self.ref_count) - self._ref_count -= 1 - return self._ref_count - - @property - def msg(self): - """ Return the message associated with this NetworkServiceDescriptor""" - return self._vnfd - - @staticmethod - def path_for_id(vnfd_id): - """ Return path for the passed vnfd_id""" - return "C,/vnfd:vnfd-catalog/vnfd:vnfd[vnfd:id = '{}']".format(vnfd_id) - - def path(self): - """ Return the path associated with this NetworkServiceDescriptor""" - return VirtualNetworkFunctionDescriptor.path_for_id(self.id) - - def update(self, vnfd): - """ Update the Virtual Network Function Descriptor """ - if self.in_use(): - self._log.error("Cannot update descriptor %s in use refcnt=%d", - self.id, self.ref_count) - - # The following loop is added to debug RIFT-13284 - for vnf_rec in self._vnfm._vnfrs.values(): - if vnf_rec.vnfd_id == self.id: - self._log.error("descriptor %s in used by %s:%s", - self.id, vnf_rec.vnfr_id, vnf_rec.msg) - raise VirtualNetworkFunctionDescriptorRefCountExists("Cannot update descriptor in use %s" % self.id) - self._vnfd = vnfd - - def delete(self): - """ Delete the Virtual Network Function Descriptor """ - if self.in_use(): - self._log.error("Cannot delete descriptor %s in use refcnt=%d", - self.id) - - # The following loop is added to debug RIFT-13284 - for vnf_rec in self._vnfm._vnfrs.values(): - if vnf_rec.vnfd_id == self.id: - self._log.error("descriptor %s in used by %s:%s", - self.id, vnf_rec.vnfr_id, vnf_rec.msg) - raise VirtualNetworkFunctionDescriptorRefCountExists("Cannot delete descriptor in use %s" % self.id) - self._vnfm.delete_vnfd(self.id) - - class VnfdRefCountDtsHandler(object): """ The VNFD Ref Count DTS handler """ XPATH = "D,/vnfr:vnfr-catalog/rw-vnfr:vnfd-ref-count" @@ -2387,6 +2461,8 @@ class VdurDatastore(object): set_if_not_none('name', vdur._vdud.name) set_if_not_none('mgmt.ip', vdur.vm_management_ip) + # The below can be used for hostname + set_if_not_none('vdur_name', vdur.unique_short_name) def update(self, vdur): """Update the VDUR information in the datastore @@ -2415,6 +2491,8 @@ class VdurDatastore(object): set_or_delete('name', vdur._vdud.name) set_or_delete('mgmt.ip', vdur.vm_management_ip) + # The below can be used for hostname + set_or_delete('vdur_name', vdur.unique_short_name) def remove(self, vdur_id): """Remove all of the data associated with specified VDUR @@ -2476,13 +2554,17 @@ class VnfManager(object): self._vcs_handler = VcsComponentDtsHandler(dts, log, loop, self) self._vnfr_handler = VnfrDtsHandler(dts, log, loop, self) + self._vnfr_ref_handler = VnfdRefCountDtsHandler(dts, log, loop, self) + self._nsr_handler = mano_dts.NsInstanceConfigSubscriber(log, dts, loop, callback=self.handle_nsr) self._dts_handlers = [VnfdDtsHandler(dts, log, loop, self), self._vnfr_handler, self._vcs_handler, - VnfdRefCountDtsHandler(dts, log, loop, self)] + self._vnfr_ref_handler, + self._nsr_handler] self._vnfrs = {} - self._vnfds = {} + self._vnfds_to_vnfr = {} + self._nsrs = {} @property def vnfr_handler(self): @@ -2506,6 +2588,35 @@ class VnfManager(object): self._log.debug("Run VNFManager - registering static DTS handlers""") yield from self.register() + def handle_nsr(self, nsr, action): + if action in [rwdts.QueryAction.CREATE]: + self._nsrs[nsr.id] = nsr + elif action == rwdts.QueryAction.DELETE: + if nsr.id in self._nsrs: + del self._nsrs[nsr.id] + + def get_linked_mgmt_network(self, vnfr): + """For the given VNFR get the related mgmt network from the NSD, if + available. + """ + vnfd_id = vnfr.vnfd.id + nsr_id = vnfr.nsr_id_ref + + # for the given related VNFR, get the corresponding NSR-config + nsr_obj = None + try: + nsr_obj = self._nsrs[nsr_id] + except KeyError: + raise("Unable to find the NS with the ID: {}".format(nsr_id)) + + # for the related NSD check if a VLD exists such that it's a mgmt + # network + for vld in nsr_obj.nsd.vld: + if vld.mgmt_network: + return vld.name + + return None + def get_vnfr(self, vnfr_id): """ get VNFR by vnfr id """ @@ -2523,11 +2634,21 @@ class VnfManager(object): self._log.info("Create VirtualNetworkFunctionRecord %s from vnfd_id: %s", vnfr.id, - vnfr.vnfd_ref) + vnfr.vnfd.id) + + mgmt_network = self.get_linked_mgmt_network(vnfr) self._vnfrs[vnfr.id] = VirtualNetworkFunctionRecord( - self._dts, self._log, self._loop, self._cluster_name, self, self.vcs_handler, vnfr + self._dts, self._log, self._loop, self._cluster_name, self, self.vcs_handler, vnfr, + mgmt_network=mgmt_network ) + + #Update ref count + if vnfr.vnfd.id in self._vnfds_to_vnfr: + self._vnfds_to_vnfr[vnfr.vnfd.id] += 1 + else: + self._vnfds_to_vnfr[vnfr.vnfd.id] = 1 + return self._vnfrs[vnfr.id] @asyncio.coroutine @@ -2536,12 +2657,17 @@ class VnfManager(object): if vnfr.vnfr_id in self._vnfrs: self._log.debug("Deleting VNFR id %s", vnfr.vnfr_id) yield from self._vnfr_handler.delete(xact, vnfr.xpath) + + if vnfr.vnfd.id in self._vnfds_to_vnfr: + if self._vnfds_to_vnfr[vnfr.vnfd.id]: + self._vnfds_to_vnfr[vnfr.vnfd.id] -= 1 + del self._vnfrs[vnfr.vnfr_id] @asyncio.coroutine def fetch_vnfd(self, vnfd_id): """ Fetch VNFDs based with the vnfd id""" - vnfd_path = VirtualNetworkFunctionDescriptor.path_for_id(vnfd_id) + vnfd_path = VirtualNetworkFunctionRecord.vnfd_xpath(vnfd_id) self._log.debug("Fetch vnfd with path %s", vnfd_path) vnfd = None @@ -2560,39 +2686,11 @@ class VnfManager(object): return vnfd - @asyncio.coroutine - def get_vnfd_ref(self, vnfd_id): - """ Get Virtual Network Function descriptor for the passed vnfd_id""" - vnfd = yield from self.get_vnfd(vnfd_id) - vnfd.ref() - return vnfd - - @asyncio.coroutine - def get_vnfd(self, vnfd_id): - """ Get Virtual Network Function descriptor for the passed vnfd_id""" - vnfd = None - if vnfd_id not in self._vnfds: - self._log.error("Cannot find VNFD id:%s", vnfd_id) - vnfd = yield from self.fetch_vnfd(vnfd_id) - - if vnfd is None: - self._log.error("Cannot find VNFD id:%s", vnfd_id) - raise VirtualNetworkFunctionDescriptorError("Cannot find VNFD id:%s", vnfd_id) - - if vnfd.id != vnfd_id: - self._log.error("Bad Recovery state {} found for {}".format(vnfd.id, vnfd_id)) - raise VirtualNetworkFunctionDescriptorError("Bad Recovery state {} found for {}".format(vnfd.id, vnfd_id)) - - if vnfd.id not in self._vnfds: - self.create_vnfd(vnfd) - - return self._vnfds[vnfd_id] - def vnfd_in_use(self, vnfd_id): """ Is this VNFD in use """ self._log.debug("Is this VNFD in use - msg:%s", vnfd_id) - if vnfd_id in self._vnfds: - return self._vnfds[vnfd_id].in_use() + if vnfd_id in self._vnfds_to_vnfr: + return (self._vnfds_to_vnfr[vnfd_id] > 0) return False @asyncio.coroutine @@ -2602,47 +2700,21 @@ class VnfManager(object): path, msg) yield from self.vnfr_handler.update(xact, path, msg) - def create_vnfd(self, vnfd): - """ Create a virtual network function descriptor """ - self._log.debug("Create virtual networkfunction descriptor - %s", vnfd) - if vnfd.id in self._vnfds: - self._log.error("Cannot create VNFD %s -VNFD id already exists", vnfd) - raise VirtualNetworkFunctionDescriptorError("VNFD already exists-%s", vnfd.id) - - self._vnfds[vnfd.id] = VirtualNetworkFunctionDescriptor(self._dts, - self._log, - self._loop, - self, - vnfd) - return self._vnfds[vnfd.id] - - def update_vnfd(self, vnfd): - """ update the Virtual Network Function descriptor """ - self._log.debug("Update virtual network function descriptor - %s", vnfd) - - if vnfd.id not in self._vnfds: - self._log.debug("No VNFD found - creating VNFD id = %s", vnfd.id) - self.create_vnfd(vnfd) - else: - self._log.debug("Updating VNFD id = %s, vnfd = %s", vnfd.id, vnfd) - self._vnfds[vnfd.id].update(vnfd) - @asyncio.coroutine def delete_vnfd(self, vnfd_id): """ Delete the Virtual Network Function descriptor with the passed id """ self._log.debug("Deleting the virtual network function descriptor - %s", vnfd_id) - if vnfd_id not in self._vnfds: - self._log.debug("Delete VNFD failed - cannot find vnfd-id %s", vnfd_id) - raise VirtualNetworkFunctionDescriptorNotFound("Cannot find %s", vnfd_id) - - if self._vnfds[vnfd_id].in_use(): - self._log.debug("Cannot delete VNFD id %s reference exists %s", - vnfd_id, - self._vnfds[vnfd_id].ref_count) - raise VirtualNetworkFunctionDescriptorRefCountExists( - "Cannot delete :%s, ref_count:%s", - vnfd_id, - self._vnfds[vnfd_id].ref_count) + if vnfd_id in self._vnfds_to_vnfr: + if self._vnfds_to_vnfr[vnfd_id]: + self._log.debug("Cannot delete VNFD id %s reference exists %s", + vnfd_id, + self._vnfds_to_vnfr[vnfd_id].vnfd_ref_count) + raise VirtualNetworkFunctionDescriptorRefCountExists( + "Cannot delete :%s, ref_count:%s", + vnfd_id, + self._vnfds_to_vnfr[vnfd_id].vnfd_ref_count) + + del self._vnfds_to_vnfr[vnfd_id] # Remove any files uploaded with VNFD and stored under $RIFT_ARTIFACTS/libs/ try: @@ -2652,10 +2724,9 @@ class VnfManager(object): shutil.rmtree(vnfd_dir, ignore_errors=True) except Exception as e: self._log.error("Exception in cleaning up VNFD {}: {}". - format(self._vnfds[vnfd_id].name, e)) + format(self._vnfds_to_vnfr[vnfd_id].vnfd.name, e)) self._log.exception(e) - del self._vnfds[vnfd_id] def vnfd_refcount_xpath(self, vnfd_id): """ xpath for ref count entry """ @@ -2667,14 +2738,15 @@ class VnfManager(object): """ Get the vnfd_list from this VNFM""" vnfd_list = [] if vnfd_id is None or vnfd_id == "": - for vnfd in self._vnfds.values(): + for vnfd in self._vnfds_to_vnfr.keys(): + vnfd_msg = RwVnfrYang.YangData_Vnfr_VnfrCatalog_VnfdRefCount() + vnfd_msg.vnfd_id_ref = vnfd + vnfd_msg.instance_ref_count = self._vnfds_to_vnfr[vnfd] + vnfd_list.append((self.vnfd_refcount_xpath(vnfd), vnfd_msg)) + elif vnfd_id in self._vnfds_to_vnfr: vnfd_msg = RwVnfrYang.YangData_Vnfr_VnfrCatalog_VnfdRefCount() - vnfd_msg.vnfd_id_ref = vnfd.id - vnfd_msg.instance_ref_count = vnfd.ref_count - vnfd_list.append((self.vnfd_refcount_xpath(vnfd.id), vnfd_msg)) - elif vnfd_id in self._vnfds: - vnfd_msg.vnfd_id_ref = self._vnfds[vnfd_id].id - vnfd_msg.instance_ref_count = self._vnfds[vnfd_id].ref_count + vnfd_msg.vnfd_id_ref = vnfd_id + vnfd_msg.instance_ref_count = self._vnfds_to_vnfr[vnfd_id] vnfd_list.append((self.vnfd_refcount_xpath(vnfd_id), vnfd_msg)) return vnfd_list