From ee71ccf6da85650a8fbd2019293535082f017b78 Mon Sep 17 00:00:00 2001 From: chamarty Date: Mon, 13 Mar 2017 18:28:18 +0000 Subject: [PATCH] Shorten VDU names Change-Id: Ibad9e8abcb1deaf149f4990525518ae5df598ac8 Signed-off-by: chamarty --- common/python/CMakeLists.txt | 1 + common/python/rift/mano/utils/short_name.py | 74 +++++++++++++++++++ models/plugins/yang/vnfr.yang | 9 +++ .../rift/rwcal/openstack/utils/compute.py | 54 ++++++++++++-- rwcal/test/test_rwcal_openstack.py | 58 +++++++++------ .../tasklets/rwnsmtasklet/rwnsmtasklet.py | 10 ++- .../rwresmgrtasklet/rwresmgrtasklet.py | 4 +- .../tasklets/rwvnfmtasklet/rwvnfmtasklet.py | 64 +++++++++++++--- 8 files changed, 230 insertions(+), 44 deletions(-) create mode 100644 common/python/rift/mano/utils/short_name.py diff --git a/common/python/CMakeLists.txt b/common/python/CMakeLists.txt index 89258853..22018c39 100644 --- a/common/python/CMakeLists.txt +++ b/common/python/CMakeLists.txt @@ -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 index 00000000..e4dd8a82 --- /dev/null +++ b/common/python/rift/mano/utils/short_name.py @@ -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) diff --git a/models/plugins/yang/vnfr.yang b/models/plugins/yang/vnfr.yang index c0a6237a..f228f1d7 100644 --- a/models/plugins/yang/vnfr.yang +++ b/models/plugins/yang/vnfr.yang @@ -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"; diff --git a/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/utils/compute.py b/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/utils/compute.py index fc053c22..55382017 100644 --- a/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/utils/compute.py +++ b/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/utils/compute.py @@ -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) diff --git a/rwcal/test/test_rwcal_openstack.py b/rwcal/test/test_rwcal_openstack.py index 6b0a2472..654e1a30 100644 --- a/rwcal/test/test_rwcal_openstack.py +++ b/rwcal/test/test_rwcal_openstack.py @@ -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 diff --git a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py index 3153a48c..da9807a6 100755 --- a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py +++ b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py @@ -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) diff --git a/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgrtasklet.py b/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgrtasklet.py index cdcadc73..44e79381 100755 --- a/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgrtasklet.py +++ b/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgrtasklet.py @@ -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( diff --git a/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py b/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py index 14153c9b..2cbe2404 100755 --- a/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py +++ b/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py @@ -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 -- 2.25.1