First set of OSM model changes for multi-disk/config-files/etc.
[osm/SO.git] / rwlaunchpad / plugins / rwvnfm / rift / tasklets / rwvnfmtasklet / rwvnfmtasklet.py
index eef4ded..e0de7b0 100755 (executable)
@@ -49,6 +49,8 @@ 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
 
 
 class VMResourceError(Exception):
@@ -270,6 +272,7 @@ class VirtualDeploymentUnitRecord(object):
                  vdud,
                  vnfr,
                  mgmt_intf,
+                 mgmt_network,
                  cloud_account_name,
                  vnfd_package_store,
                  vdur_id=None,
@@ -282,6 +285,7 @@ class VirtualDeploymentUnitRecord(object):
         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 = []
@@ -345,6 +349,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
@@ -379,12 +385,13 @@ 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",
+                      "volumes",
                       "name"]
         vdu_copy_dict = {k: v for k, v in
                          self._vdud.as_dict().items() if k in vdu_fields}
@@ -395,9 +402,10 @@ class VirtualDeploymentUnitRecord(object):
                      }
         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
@@ -407,6 +415,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 = []
 
@@ -429,21 +464,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_mac_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
@@ -538,21 +573,48 @@ 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,
                 }
 
+        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
 
@@ -564,11 +626,15 @@ 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,
+            cp_info = {"name": cp.name,
                        "virtual_link_id": vlr.network_id,
-                       "type_yang": intf.virtual_interface.type_yang}
+                       "type_yang": intf.virtual_interface.type_yang,
+                       "port_security_enabled": cp.port_security_enabled}
 
             if (intf.virtual_interface.has_field('vpci') and
                     intf.virtual_interface.vpci is not None):
@@ -589,17 +655,21 @@ class VirtualDeploymentUnitRecord(object):
             else:
                 cp_list.append({"name": cp,
                                 "virtual_link_id": vlr.network_id,
-                                "type_yang": intf.virtual_interface.type_yang})
+                                "type_yang": intf.virtual_interface.type_yang,
+                                "port_security_enabled": cp.port_security_enabled})
 
         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
@@ -711,7 +781,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)
@@ -1058,19 +1128,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
@@ -1083,6 +1154,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)
@@ -1108,11 +1181,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):
@@ -1194,7 +1292,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)
@@ -1215,7 +1313,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()
@@ -1229,7 +1327,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,
@@ -1239,6 +1336,10 @@ 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
 
         # Add all the VLRs  to  VNFR
@@ -1252,7 +1353,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:
@@ -1282,18 +1383,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
@@ -1320,7 +1421,7 @@ class VirtualNetworkFunctionRecord(object):
         """ 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
@@ -1391,7 +1492,7 @@ class VirtualNetworkFunctionRecord(object):
         nsr_config = yield from self.get_nsr_config()
 
         ### Step-3: Get VDU level placement groups
-        for group in self.vnfd.msg.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,
@@ -1433,7 +1534,7 @@ class VirtualNetworkFunctionRecord(object):
 
 
         self._log.info("Creating VDU's for vnfd id: %s", self.vnfd_id)
-        for vdu in self.vnfd.msg.vdu:
+        for vdu in self._rw_vnfd.vdu:
             self._log.debug("Creating vdu: %s", vdu)
             vdur_id = get_vdur_id(vdu)
 
@@ -1451,6 +1552,7 @@ class VirtualNetworkFunctionRecord(object):
                 vdud=vdu,
                 vnfr=vnfr,
                 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,
@@ -1536,7 +1638,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:
@@ -1588,7 +1690,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
 
@@ -1607,7 +1709,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,
@@ -1686,6 +1788,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():
@@ -1695,7 +1798,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)
@@ -1720,12 +1823,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)
@@ -1773,6 +1874,10 @@ class VirtualNetworkFunctionRecord(object):
 
         self._log.debug("VNFR-ID %s: Instantiation Done", self._vnfr_id)
 
+        # create task updating uptime for this vnfr
+        self._log.debug("VNFR-ID %s: Starting task to update uptime", self._vnfr_id)
+        self._loop.create_task(self.vnfr_uptime_update(xact))
+
     @asyncio.coroutine
     def terminate(self, xact):
         """ Terminate this virtual network function """
@@ -1810,6 +1915,19 @@ class VirtualNetworkFunctionRecord(object):
         self._log.debug("Terminated  VNF id %s", self.vnfr_id)
         self.set_state(VirtualNetworkFunctionRecordState.TERMINATED)
 
+    @asyncio.coroutine
+    def vnfr_uptime_update(self, xact):
+        while True:
+            # Return when vnfr state is FAILED or TERMINATED etc
+            if self._state not in [VirtualNetworkFunctionRecordState.INIT,
+                                   VirtualNetworkFunctionRecordState.VL_INIT_PHASE,
+                                   VirtualNetworkFunctionRecordState.VM_INIT_PHASE,
+                                   VirtualNetworkFunctionRecordState.READY]:
+                return
+            yield from self.publish(xact)
+            yield from asyncio.sleep(2, loop=self._loop)
+
+
 
 class VnfdDtsHandler(object):
     """ DTS handler for VNFD config changes """
@@ -1837,13 +1955,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):
@@ -1853,7 +1964,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)
@@ -1863,17 +1974,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)
 
@@ -2080,8 +2180,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)
 
@@ -2108,7 +2208,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)
@@ -2190,100 +2290,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"
@@ -2471,13 +2477,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):
@@ -2501,6 +2511,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 """
 
@@ -2518,11 +2557,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
@@ -2531,12 +2580,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
 
@@ -2555,39 +2609,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
@@ -2597,47 +2623,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/<id>
         try:
@@ -2647,10 +2647,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 """
@@ -2662,14 +2661,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