Shorten VDU names
authorchamarty <ravi.chamarty@riftio.com>
Mon, 13 Mar 2017 18:28:18 +0000 (18:28 +0000)
committerchamarty <ravi.chamarty@riftio.com>
Mon, 13 Mar 2017 18:28:18 +0000 (18:28 +0000)
Change-Id: Ibad9e8abcb1deaf149f4990525518ae5df598ac8
Signed-off-by: chamarty <ravi.chamarty@riftio.com>
common/python/CMakeLists.txt
common/python/rift/mano/utils/short_name.py [new file with mode: 0644]
models/plugins/yang/vnfr.yang
rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/utils/compute.py
rwcal/test/test_rwcal_openstack.py
rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py
rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgrtasklet.py
rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py

index 8925885..22018c3 100644 (file)
@@ -129,6 +129,7 @@ rift_python_install_tree(
     rift/mano/utils/__init.py__
     rift/mano/utils/compare_desc.py
     rift/mano/utils/juju_api.py
+    rift/mano/utils/short_name.py
   COMPONENT ${PKG_LONG_NAME}
   PYTHON3_ONLY
   )
diff --git a/common/python/rift/mano/utils/short_name.py b/common/python/rift/mano/utils/short_name.py
new file mode 100644 (file)
index 0000000..e4dd8a8
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/python
+
+# 
+#   Copyright 2017 RIFT.IO Inc
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# Author: Aniruddha Atale
+
+import hashlib
+import basehash
+
+
+class StringShortner(object):
+    FOLDS = 3
+    STRING_LEN=9
+    def __init__(self, string = None):
+        self._string = string
+
+    @property
+    def string(self):
+        return self._string
+
+    @string.setter
+    def string(self, string):
+        self._string = string
+
+    @property
+    def short_string(self):
+        if self._string:
+            return StringShortner._get_short_string(self._string)
+        else:
+            return str()
+
+    @staticmethod
+    def _fold_hex_series(series):
+        length = len(series)
+        result = list()
+        for i in range(int(length/2)):
+            result.append(series[i] ^ series[(length - 1) - i])
+
+        if length % 2:
+            result.append(series[int(length/2) + 1])
+
+        return result
+
+    @staticmethod
+    def _num_from_hex_series(series):
+        result = 0
+        for i in range(len(series)):
+            result = result * 256
+            result += series[i]
+        return result
+    
+    @staticmethod
+    def _get_short_string(string):
+        sha = hashlib.sha384(string.encode())
+        digest = sha.digest()
+        for i in range(StringShortner.FOLDS):
+            digest = StringShortner._fold_hex_series(digest)
+
+        number = StringShortner._num_from_hex_series(digest)
+        base62 = basehash.base62(length=StringShortner.STRING_LEN)
+        return base62.hash(number)
index c0a6237..f228f1d 100644 (file)
@@ -298,6 +298,15 @@ module vnfr
           type string;
         }
 
+        leaf unique-short-name {
+          description "Short Unique name of the VDU
+                This will be of the format NSR name-ShortnedString-VDUname
+                NSR name and VDU name shall be constrained to 10 characters";
+          rwpb:field-inline "true";
+          rwpb:field-string-max 64;
+          type string;
+        }
+
         leaf vdu-id-ref {
           type leafref {
             path "../../vnfd/vdu/id";
index fc053c2..5538201 100644 (file)
@@ -283,6 +283,12 @@ class ComputeUtils(object):
         }
         """
         kwargs = dict()
+        metadata = dict()
+
+        if vdu_params.has_field('node_id'):
+            metadata['rift_node_id'] = vdu_params.node_id
+            kwargs['metadata'] = metadata
+
         if vdu_params.has_field('vdu_init') and vdu_params.vdu_init.has_field('userdata'):
             kwargs['userdata'] = vdu_params.vdu_init.userdata
         else:
@@ -302,11 +308,15 @@ class ComputeUtils(object):
         else:
             kwargs['config_drive'] = False
 
-        if vdu_params.supplemental_boot_data.has_field('custom_meta_data'):
-            metadata = dict()
-            for cm in vdu_params.supplemental_boot_data.custom_meta_data:
-                metadata[cm] = cm.value                
-            kwargs['metadata'] = metadata
+        try:
+            # Rift model only
+            if vdu_params.supplemental_boot_data.has_field('custom_meta_data'):
+                metadata = dict()
+                for cm in vdu_params.supplemental_boot_data.custom_meta_data:
+                    metadata[cm.name] = cm.value
+                    kwargs['metadata'] = metadata
+        except Exception as e:
+            pass
 
         return kwargs
 
@@ -490,6 +500,38 @@ class ComputeUtils(object):
         else:
             return str()
 
+    def _parse_vdu_boot_config_data(self, vm_info):
+        """
+        Parses VDU supplemental boot data
+        Arguments:
+          vm_info : A dictionary returned by novaclient library listing VM attributes
+
+        Returns:
+          List of RwcalYang.VDUInfoParams_SupplementalBootData()
+        """
+        supplemental_boot_data = None
+        node_id = None
+        if 'config_drive' in vm_info:
+            supplemental_boot_data = RwcalYang.VDUInfoParams_SupplementalBootData()
+            supplemental_boot_data.boot_data_drive = vm_info['config_drive']
+        # Look for any metadata
+        if 'metadata' not in vm_info:
+            return node_id, supplemental_boot_data
+        if supplemental_boot_data is None:
+            supplemental_boot_data = RwcalYang.VDUInfoParams_SupplementalBootData()
+        for key, value in vm_info['metadata'].items():
+            if key == 'rift_node_id':
+                node_id = value
+            else:
+                try:
+                    # rift only
+                    cm = supplemental_boot_data.custom_meta_data.add()
+                    cm.name = key
+                    cm.value = str(value)
+                except Exception as e:
+                    pass
+        return node_id, supplemental_boot_data 
+
     def _parse_vdu_volume_info(self, vm_info):
         """
         Get VDU server group information
@@ -595,6 +637,8 @@ class ComputeUtils(object):
                 ha = vdu.host_aggregate.add()
                 ha.from_dict(aggr.as_dict())
 
+        vdu.node_id, vdu.supplemental_boot_data = self._parse_vdu_boot_config_data(vm_info)
+
         cp_list = self._parse_vdu_cp_info(vdu.vdu_id)
         for cp in cp_list:
             vdu.connection_points.append(cp)
index 6b0a247..654e1a3 100644 (file)
@@ -28,7 +28,7 @@ from keystoneclient import v3 as ksclient
 
 from gi.repository import RwcalYang
 from gi.repository.RwTypes import RwStatus
-from rift.rwcal.openstack.openstack_drv import KeystoneDriver, NovaDriver, KeystoneDriverV3, KeystoneDriverV2
+#from rift.rwcal.openstack.openstack_drv import KeystoneDriver, NovaDriver, KeystoneDriverV3, KeystoneDriverV2
 
 logger = logging.getLogger('rwcal-openstack')
 
@@ -43,10 +43,10 @@ ssh_pwauth: True
 # Important information about openstack installation. This needs to be manually verified
 #
 openstack_info = {
-    'username'           : 'pluto',
-    'password'           : 'mypasswd',
-    'auth_url'           : 'http://10.66.4.17:5000/v3/',
-    'project_name'       : 'demo',
+    'username'           : 'xxxxxx',
+    'password'           : 'xxxxxx',
+    'auth_url'           : 'http://10.66.4.102:5000/v2.0/',
+    'project_name'       : 'xxxxxx',
     'mgmt_network'       : 'private',
     'reserved_flavor'    : 'm1.medium',
     'reserved_image'     : 'Fedora-x86_64-20-20131211.1-sda-ping.qcow2',
@@ -544,6 +544,7 @@ class OpenStackTest(unittest.TestCase):
         rc = self.cal.do_delete_flavor(self._acct, flavor_id)
         self.assertEqual(rc, RwStatus.SUCCESS)
 
+    '''
     @unittest.skip("Skipping test_expiry_token")
     def test_expiry_token(self):
         """
@@ -664,6 +665,7 @@ class OpenStackTest(unittest.TestCase):
         except Exception: 
             auth_exp = True
         self.assertFalse(auth_exp)
+    '''
 
     @unittest.skip("Skipping test_vm_operations")
     def test_vm_operations(self):
@@ -929,17 +931,23 @@ class OpenStackTest(unittest.TestCase):
         vdu.name = "cal.vdu"
         vdu.node_id = OpenStackTest.NodeID
         vdu.image_id = self._image.id
+        vdu.vm_flavor.memory_mb = 512
+        vdu.vm_flavor.vcpu_count = 1
+        vdu.vm_flavor.storage_gb = 4 
         vdu.flavor_id = self._flavor.id
         vdu.vdu_init.userdata = PING_USERDATA
         vdu.allocate_public_address = True
-        meta1 = vdu.supplemental_boot_data.custom_meta_data.add()
-        meta1.name = "EMS_IP"
-        meta1.data_type = "STRING"
-        meta1.value = "10.5.6.6"
-        #meta2 = vdu.supplemental_boot_data.custom_meta_data.add()
-        #meta2.name = "Cluster_data"
-        #meta2.data_type = "JSON"
-        #meta2.value = '''{ "cluster_id": "12" , "vnfc_id": "112" }'''
+        try:
+            meta1 = vdu.supplemental_boot_data.custom_meta_data.add()
+            meta1.name = "EMS_IP"
+            meta1.data_type = "STRING"
+            meta1.value = "10.5.6.6"
+            #meta2 = vdu.supplemental_boot_data.custom_meta_data.add()
+            #meta2.name = "Cluster_data"
+            #meta2.data_type = "JSON"
+            #meta2.value = '''{ "cluster_id": "12" , "vnfc_id": "112" }'''
+        except Exception as e:
+            pass
         #vdu.supplemental_boot_data.boot_data_drive = True
         customfile1 = vdu.supplemental_boot_data.config_file.add()
         customfile1.source = "abcdef124"
@@ -965,7 +973,7 @@ class OpenStackTest(unittest.TestCase):
 
         return vdu
 
-    @unittest.skip("Skipping test_create_delete_virtual_link_and_vdu")
+    #@unittest.skip("Skipping test_create_delete_virtual_link_and_vdu")
     def test_create_delete_virtual_link_and_vdu(self):
         """
         Test to create VDU
@@ -1054,7 +1062,9 @@ class OpenStackTest(unittest.TestCase):
           """
           vdu = RwcalYang.VDUInitParams()
           vdu.name = "cal_vdu"
-          vdu.flavor_id = self._flavor.id
+          vdu.vm_flavor.memory_mb = 512
+          vdu.vm_flavor.vcpu_count = 1
+          vdu.vm_flavor.storage_gb = 4 
           vdu.allocate_public_address = True
           ctr = 0
           for vl in vlink_list:
@@ -1068,16 +1078,22 @@ class OpenStackTest(unittest.TestCase):
           vol0.name = "vda"
           vol0.image = openstack_info['reserved_image']
           vol0.size = 10
-          vol0.boot_priority = 0
+          try:
+              vol0.boot_priority = 0
+          except Exception as e:
+              pass
           vol0.device_type = "disk"
-          meta1 = vol0.custom_meta_data.add()
-          meta1.name = "fs_type"
-          meta1.data_type = "STRING"
-          meta1.value = "ext4"
+          try:
+             meta1 = vol0.custom_meta_data.add()
+             meta1.name = "fs_type"
+             meta1.data_type = "STRING"
+             meta1.value = "ext4"
+          except Exception as e:
+             pass
 
           return vdu
 
-    #@unittest.skip("Skipping test_create_vol_vdu")
+    @unittest.skip("Skipping test_create_vol_vdu")
     def test_create_vol_vdu(self):
           """
           Test to create VDU with mgmt port using Volumes
index 3153a48..da9807a 100755 (executable)
@@ -2026,10 +2026,11 @@ class NetworkServiceRecord(object):
         # Fetch the VNFD associated with this VNF
         placement_groups = self.get_placement_groups(vnfd_msg, const_vnfd)
         self._log.info("Cloud Account for VNF %d is %s",const_vnfd.member_vnf_index,cloud_account_name)
-        self._log.info("Launching VNF: %s (Member Index: %s) in NSD plancement Groups: %s",
+        self._log.info("Launching VNF: %s (Member Index: %s) in NSD plancement Groups: %s, restart mode self.restart_mode %s",
                        vnfd_msg.name,
                        const_vnfd.member_vnf_index,
-                       [ group.name for group in placement_groups])
+                       [ group.name for group in placement_groups],
+                       self.restart_mode)
         vnfr = yield from VirtualNetworkFunctionRecord.create_record(self._dts,
                                             self._log,
                                             self._loop,
@@ -3846,9 +3847,10 @@ class NsManager(object):
             self._log.error(msg)
             raise NetworkServiceRecordError(msg)
 
-        self._log.info("Create NetworkServiceRecord nsr id %s from nsd_id %s",
+        self._log.info("Create NetworkServiceRecord nsr id %s from nsd_id %s, restart mode %s",
                        nsr_msg.id,
-                       nsr_msg.nsd.id)
+                       nsr_msg.nsd.id,
+                       restart_mode)
 
         nsm_plugin = self._ro_plugin_selector.ro_plugin
         sdn_account_name = self._cloud_account_handler.get_cloud_account_sdn_name(nsr_msg.cloud_account)
index cdcadc7..44e7938 100755 (executable)
@@ -113,7 +113,7 @@ class ResourceManager(object):
 
     @asyncio.coroutine
     def reallocate_virtual_network(self, event_id, cloud_account_name, request, resource):
-        self._log.info("Received network resource allocation request with event-id: %s", event_id)
+        self._log.info("Received network resource reallocation request with event-id: %s", event_id)
         resource = yield from self.core.reallocate_virtual_resource(event_id, cloud_account_name, request, 'network', resource)
         return resource
 
@@ -140,7 +140,7 @@ class ResourceManager(object):
 
     @asyncio.coroutine
     def reallocate_virtual_compute(self, event_id, cloud_account_name, request, resource):
-        self._log.info("Received compute resource allocation request "
+        self._log.info("Received compute resource reallocation request "
                        "(cloud account: %s) with event-id: %s",
                        cloud_account_name, event_id)
         resource = yield from self.core.reallocate_virtual_resource(
index 14153c9..2cbe240 100755 (executable)
@@ -51,6 +51,7 @@ 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):
@@ -271,6 +272,7 @@ class VirtualDeploymentUnitRecord(object):
                  loop,
                  vdud,
                  vnfr,
+                 nsr_config,
                  mgmt_intf,
                  mgmt_network,
                  cloud_account_name,
@@ -282,6 +284,7 @@ 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
@@ -341,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 """
@@ -392,14 +420,17 @@ class VirtualDeploymentUnitRecord(object):
                       "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}
         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
@@ -610,7 +641,8 @@ class VirtualDeploymentUnitRecord(object):
         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,
+                "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:
@@ -1506,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:
@@ -1514,10 +1546,7 @@ 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
+        ### 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:
@@ -1564,16 +1593,22 @@ class VirtualNetworkFunctionRecord(object):
 
 
         self._log.info("Creating VDU's for vnfd id: %s", self.vnfd_id)
+
+        # 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,
@@ -1581,6 +1616,7 @@ 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,
@@ -1875,7 +1911,7 @@ class VirtualNetworkFunctionRecord(object):
 
 
         # 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:
@@ -1886,7 +1922,7 @@ 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:
@@ -2425,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
@@ -2453,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