From 97b74b6a87ca8827749782653e89865d9d1108a4 Mon Sep 17 00:00:00 2001 From: chamarty Date: Tue, 29 Nov 2016 18:44:47 +0000 Subject: [PATCH] SO Multidisk changes Change-Id: I75a5029fbaa295c11af24cfa4fe8ca872a73c69f Signed-off-by: chamarty --- models/plugins/yang/mano-types.yang | 98 +++++- models/plugins/yang/vnfd.yang | 32 +- models/plugins/yang/vnfr.yang | 16 + .../rift/rwcal/openstack/openstack_drv.py | 44 ++- .../rift/rwcal/openstack/prepare_vm.py | 6 +- .../vala/rwcal_openstack/rwcal_openstack.py | 283 ++++++++++++++---- rwcal/plugins/yang/rwcal.yang | 28 ++ rwcal/test/test_rwcal_openstack.py | 17 +- .../rwmonitor/rift/tasklets/rwmonitor/core.py | 19 +- .../tasklets/rwresmgrtasklet/rwresmgr_core.py | 5 +- .../rwresmgrtasklet/rwresmgr_events.py | 10 +- .../tasklets/rwvnfmtasklet/rwvnfmtasklet.py | 27 +- 12 files changed, 479 insertions(+), 106 deletions(-) diff --git a/models/plugins/yang/mano-types.yang b/models/plugins/yang/mano-types.yang index e7c7d3d6..5f68b416 100644 --- a/models/plugins/yang/mano-types.yang +++ b/models/plugins/yang/mano-types.yang @@ -112,6 +112,28 @@ module mano-types } + grouping image-properties { + leaf image { + description + "Image name for the software image. + If the image name is found within the VNF packaage it will + be uploaded to all cloud accounts during onboarding process. + Otherwise, the image must be added to the cloud account with + the same name as entered here. + "; + type string; + } + + leaf image-checksum { + description + "Image md5sum for the software image. + The md5sum, if provided, along with the image name uniquely + identifies an image uploaded to the CAL. + "; + type string; + } + } + grouping vnf-configuration { container vnf-configuration { rwpb:msg-new VnfConfiguration; @@ -2002,5 +2024,79 @@ module mano-types uses ip-profile-info; } } - + + grouping volume-info { + description "Grouping for Volume-info"; + + leaf description { + description "Description for Volume"; + type string; + } + + leaf size { + description "Size of disk in GB"; + type uint64; + } + + choice volume-source { + description + "Defines the source of the volume. Possible options are + 1. Ephemeral -- Empty disk + 2. Image -- Refer to image to be used for volume + 3. Volume -- Reference of pre-existing volume to be used + "; + + case ephemeral { + leaf ephemeral { + type empty; + } + } + + case image { + uses image-properties; + } + + case volume { + leaf volume-ref { + description "Reference for pre-existing volume in VIM"; + type string; + } + } + } + + container boot-params { + leaf boot-volume { + description "This flag indicates if this is boot volume or not"; + type boolean; + } + leaf boot-priority { + description "Boot priority associated with volume"; + type int32; + } + } + + container guest-params { + description "Guest virtualization parameter associated with volume"; + + leaf device_bus { + description "Type of disk-bus on which this disk is exposed to guest"; + type enumeration { + enum ide; + enum usb; + enum virtio; + enum scsi; + } + } + + leaf device_type { + description "The type of device as exposed to guest"; + type enumeration { + enum disk; + enum cdrom; + enum floppy; + enum lun; + } + } + } + } } diff --git a/models/plugins/yang/vnfd.yang b/models/plugins/yang/vnfd.yang index 2cc43d3e..81bb2aa7 100644 --- a/models/plugins/yang/vnfd.yang +++ b/models/plugins/yang/vnfd.yang @@ -342,26 +342,7 @@ module vnfd uses manotypes:alarm; } - leaf image { - description - "Image name for the software image. - If the image name is found within the VNF packaage it will - be uploaded to all cloud accounts during onboarding process. - Otherwise, the image must be added to the cloud account with - the same name as entered here. - "; - mandatory true; - type string; - } - - leaf image-checksum { - description - "Image md5sum for the software image. - The md5sum, if provided, along with the image name uniquely - identifies an image uploaded to the CAL. - "; - type string; - } + uses manotypes:image-properties; choice cloud-init-input { description @@ -447,6 +428,17 @@ module vnfd } uses virtual-interface; } + + list volumes { + key "name"; + + leaf name { + description "Name of the disk-volumes, e.g. vda, vdb etc"; + type string; + } + + uses manotypes:volume-info; + } } list vdu-dependency { diff --git a/models/plugins/yang/vnfr.yang b/models/plugins/yang/vnfr.yang index a4419ce6..8f739f47 100644 --- a/models/plugins/yang/vnfr.yang +++ b/models/plugins/yang/vnfr.yang @@ -338,6 +338,22 @@ module vnfr uses manotypes:hypervisor-epa; uses manotypes:host-epa; + list volumes { + key "name"; + + leaf name { + description "Name of the disk-volumes, e.g. vda, vdb etc"; + type string; + } + + leaf volume-id { + description "VIM assigned volume id"; + type string; + } + + uses manotypes:volume-info; + } + list alarms { description "A list of the alarms that have been created for this VDU"; diff --git a/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/openstack_drv.py b/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/openstack_drv.py index 2b8ab7c2..943cdd54 100644 --- a/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/openstack_drv.py +++ b/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/openstack_drv.py @@ -529,7 +529,8 @@ class NovaDriver(object): { server_name(string) : Name of the VM/Server flavor_id (string) : UUID of the flavor to be used for VM - image_id (string) : UUID of the image to be used VM/Server instance + image_id (string) : UUID of the image to be used VM/Server instance, + This could be None if volumes (with images) are being used network_list(List) : A List of network_ids. A port will be created in these networks port_list (List) : A List of port-ids. These ports will be added to VM. metadata (dict) : A dictionary of arbitrary key-value pairs associated with VM/server @@ -552,6 +553,7 @@ class NovaDriver(object): nvconn = self._get_nova_connection() + try: server = nvconn.servers.create(kwargs['name'], kwargs['image_id'], @@ -564,7 +566,7 @@ class NovaDriver(object): userdata = kwargs['userdata'], security_groups = kwargs['security_groups'], availability_zone = kwargs['availability_zone'], - block_device_mapping = None, + block_device_mapping_v2 = kwargs['block_device_mapping_v2'], nics = nics, scheduler_hints = kwargs['scheduler_hints'], config_drive = None) @@ -836,6 +838,26 @@ class NovaDriver(object): logger.error("OpenstackDriver: Release Floating IP operation failed. Exception: %s" %str(e)) raise + def volume_list(self, server_id): + """ + List of volumes attached to the server + + Arguments: + None + Returns: + List of dictionary objects where dictionary is representation of class (novaclient.v2.volumes.Volume) + """ + nvconn = self._get_nova_connection() + try: + volumes = nvconn.volumes.get_server_volumes(server_id=server_id) + except Exception as e: + logger.error("OpenstackDriver: Get volume information failed. Exception: %s" %str(e)) + raise + + volume_info = [v.to_dict() for v in volumes] + return volume_info + + def group_list(self): """ List of Server Affinity and Anti-Affinity Groups @@ -1725,10 +1747,19 @@ class OpenstackDriver(object): return self.nova_drv.flavor_get(flavor_id) def nova_server_create(self, **kwargs): + def _verify_image(image_id): + image = self.glance_drv.image_get(image_id) + if image['status'] != 'active': + raise GlanceException.NotFound("Image with image_id: %s not found in active state. Current State: %s" %(image['id'], image['status'])) + assert kwargs['flavor_id'] == self.nova_drv.flavor_get(kwargs['flavor_id'])['id'] - image = self.glance_drv.image_get(kwargs['image_id']) - if image['status'] != 'active': - raise GlanceException.NotFound("Image with image_id: %s not found in active state. Current State: %s" %(image['id'], image['status'])) + + if kwargs['block_device_mapping_v2'] is not None: + for block_map in kwargs['block_device_mapping_v2']: + if 'uuid' in block_map: + _verify_image(block_map['uuid']) + else: + _verify_image(kwargs['image_id']) # if 'network_list' in kwargs: # kwargs['network_list'].append(self._mgmt_network_id) @@ -1794,6 +1825,9 @@ class OpenstackDriver(object): def nova_server_group_list(self): return self.nova_drv.group_list() + def nova_volume_list(self, server_id): + return self.nova_drv.volume_list(server_id) + def neutron_network_list(self): return self.neutron_drv.network_list() diff --git a/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/prepare_vm.py b/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/prepare_vm.py index 7acf0fde..0d658f12 100644 --- a/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/prepare_vm.py +++ b/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/prepare_vm.py @@ -96,9 +96,9 @@ def prepare_vm_after_boot(drv,argument): ### Important to call create_port_metadata before assign_floating_ip_address ### since assign_floating_ip_address can wait thus delaying port_metadata creation - ### Wait for 2 minute for server to come up -- Needs fine tuning - wait_time = 120 - sleep_time = 1 + ### Wait for a max of 5 minute for server to come up -- Needs fine tuning + wait_time = 300 + sleep_time = 2 for i in range(int(wait_time/sleep_time)): server = drv.nova_server_get(argument.server_id) if server['status'] == 'ACTIVE': diff --git a/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py b/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py index 8a2d275a..e5dcb67f 100644 --- a/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py +++ b/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py @@ -376,7 +376,8 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): kwargs = {} kwargs['name'] = vminfo.vm_name kwargs['flavor_id'] = vminfo.flavor_id - kwargs['image_id'] = vminfo.image_id + if vminfo.has_field('image_id'): + kwargs['image_id'] = vminfo.image_id with self._use_driver(account) as drv: ### If floating_ip is required and we don't have one, better fail before any further allocation @@ -1227,7 +1228,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): return link @staticmethod - def _fill_vdu_info(vm_info, flavor_info, mgmt_network, port_list, server_group): + def _fill_vdu_info(vm_info, flavor_info, mgmt_network, port_list, server_group, volume_list = None): """Create a GI object for VDUInfoParams Converts VM information dictionary object returned by openstack @@ -1284,6 +1285,19 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): if flavor_info is not None: RwcalOpenstackPlugin._fill_epa_attributes(vdu, flavor_info) + + # Fill the volume information + if volume_list is not None: + for os_volume in volume_list: + volr = vdu.volumes.add() + try: + " Device name is of format /dev/vda" + vol_name = (os_volume['device']).split('/')[2] + except: + continue + volr.name = vol_name + volr.volume_id = os_volume['volumeId'] + return vdu @rwcalstatus(ret_on_failure=[""]) @@ -1854,6 +1868,136 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): raise OpenstackCALOperationFailure("Create-flavor operation failed for cloud account: %s" %(account.name)) return flavor_id + def _create_vm(self, account, vduinfo, pci_assignement=None, server_group=None, port_list=None, network_list=None, imageinfo_list=None): + """Create a new virtual machine. + + Arguments: + account - a cloud account + vminfo - information that defines the type of VM to create + + Returns: + The image id + """ + kwargs = {} + kwargs['name'] = vduinfo.name + kwargs['flavor_id'] = vduinfo.flavor_id + if vduinfo.has_field('image_id'): + kwargs['image_id'] = vduinfo.image_id + else: + kwargs['image_id'] = "" + + with self._use_driver(account) as drv: + ### If floating_ip is required and we don't have one, better fail before any further allocation + if vduinfo.has_field('allocate_public_address') and vduinfo.allocate_public_address: + if account.openstack.has_field('floating_ip_pool'): + pool_name = account.openstack.floating_ip_pool + else: + pool_name = None + floating_ip = self._allocate_floating_ip(drv, pool_name) + else: + floating_ip = None + + if vduinfo.has_field('vdu_init') and vduinfo.vdu_init.has_field('userdata'): + kwargs['userdata'] = vduinfo.vdu_init.userdata + else: + kwargs['userdata'] = '' + + if account.openstack.security_groups: + kwargs['security_groups'] = account.openstack.security_groups + + kwargs['port_list'] = port_list + kwargs['network_list'] = network_list + + metadata = {} + # Add all metadata related fields + if vduinfo.has_field('node_id'): + metadata['node_id'] = vduinfo.node_id + if pci_assignement is not None: + metadata['pci_assignement'] = pci_assignement + kwargs['metadata'] = metadata + + if vduinfo.has_field('availability_zone') and vduinfo.availability_zone.has_field('name'): + kwargs['availability_zone'] = vduinfo.availability_zone + else: + kwargs['availability_zone'] = None + + if server_group is not None: + kwargs['scheduler_hints'] = {'group': server_group} + else: + kwargs['scheduler_hints'] = None + + kwargs['block_device_mapping_v2'] = None + if vduinfo.has_field('volumes') : + kwargs['block_device_mapping_v2'] = [] + with self._use_driver(account) as drv: + # Only support image->volume + for volume in vduinfo.volumes: + block_map = dict() + block_map['boot_index'] = volume.boot_params.boot_priority + if "image" in volume: + # Support image->volume + # Match retrived image info with volume based image name and checksum + if volume.image is not None: + matching_images = [img for img in imageinfo_list if img['name'] == volume.image] + if volume.image_checksum is not None: + matching_images = [img for img in matching_images if img['checksum'] == volume.image_checksum] + img_id = matching_images[0]['id'] + if img_id is None: + raise OpenstackCALOperationFailure("Create-vdu operation failed. Volume image not found for name {} checksum {}".format(volume.name, volume.checksum)) + block_map['uuid'] = img_id + block_map['source_type'] = "image" + else: + block_map['source_type'] = "blank" + + block_map['device_name'] = volume.name + block_map['destination_type'] = "volume" + block_map['volume_size'] = volume.size + block_map['delete_on_termination'] = True + if volume.guest_params.has_field('device_type') and volume.guest_params.device_type == 'cdrom': + block_map['device_type'] = 'cdrom' + if volume.guest_params.has_field('device_bus') and volume.guest_params.device_bus == 'ide': + block_map['disk_bus'] = 'ide' + kwargs['block_device_mapping_v2'].append(block_map) + + + with self._use_driver(account) as drv: + vm_id = drv.nova_server_create(**kwargs) + if floating_ip: + self.prepare_vdu_on_boot(account, vm_id, floating_ip) + + return vm_id + + def get_openstack_image_info(self, account, image_name, image_checksum=None): + self.log.debug("Looking up image id for image name %s and checksum %s on cloud account: %s", + image_name, image_checksum, account.name + ) + + image_list = [] + with self._use_driver(account) as drv: + image_list = drv.glance_image_list() + matching_images = [img for img in image_list if img['name'] == image_name] + + # If the image checksum was filled in then further filter the images by the checksum + if image_checksum is not None: + matching_images = [img for img in matching_images if img['checksum'] == image_checksum] + else: + self.log.warning("Image checksum not provided. Lookup using image name (%s) only.", + image_name) + + if len(matching_images) == 0: + raise ResMgrCALOperationFailure("Could not find image name {} (using checksum: {}) for cloud account: {}".format( + image_name, image_checksum, account.name + )) + + elif len(matching_images) > 1: + unique_checksums = {i.checksum for i in matching_images} + if len(unique_checksums) > 1: + msg = ("Too many images with different checksums matched " + "image name of %s for cloud account: %s" % (image_name, account.name)) + raise ResMgrCALOperationFailure(msg) + + return matching_images[0] + @rwcalstatus(ret_on_failure=[""]) def do_create_vdu(self, account, vdu_init): """Create a new virtual deployment unit @@ -1879,6 +2023,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): port_list = [] network_list = [] + imageinfo_list = [] for c_point in vdu_init.connection_points: if c_point.virtual_link_id in network_list: assert False, "Only one port per network supported. Refer: http://specs.openstack.org/openstack/nova-specs/specs/juno/implemented/nfv-multiple-if-1-net.html" @@ -1890,70 +2035,94 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): if not vdu_init.has_field('flavor_id'): vdu_init.flavor_id = self._select_resource_flavor(account,vdu_init) - ### Check VDU Virtual Interface type and make sure VM with property exists - if vdu_init.connection_points is not None: - ### All virtual interfaces need to be of the same type for Openstack Accounts - if not all(cp.type_yang == vdu_init.connection_points[0].type_yang for cp in vdu_init.connection_points): - ### We have a mix of E1000 & VIRTIO virtual interface types in the VDU, abort instantiation. - assert False, "Only one type of Virtual Intefaces supported for Openstack accounts. Found a mix of VIRTIO & E1000." + ### Obtain all images for volumes and perform validations + if vdu_init.has_field('volumes'): + for volume in vdu_init.volumes: + if "image" in volume: + image_checksum = volume.image_checksum if volume.has_field("image_checksum") else None + image_info = self.get_openstack_image_info(account, volume.image, image_checksum) + imageinfo_list.append(image_info) + elif vdu_init.has_field('image_id'): + with self._use_driver(account) as drv: + image_info = drv.glance_image_get(vdu_init.image_id) + imageinfo_list.append(image_info) - with self._use_driver(account) as drv: - img_info = drv.glance_image_get(vdu_init.image_id) + if not imageinfo_list: + err_str = ("VDU has no image information") + self.log.error(err_str) + raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str) - virt_intf_type = vdu_init.connection_points[0].type_yang - if virt_intf_type == 'E1000': - if 'hw_vif_model' in img_info and img_info.hw_vif_model == 'e1000': - self.log.debug("VDU has Virtual Interface E1000, found matching image with property hw_vif_model=e1000") + ### Check VDU Virtual Interface type and make sure VM with property exists + if vdu_init.connection_points: + ### All virtual interfaces need to be of the same type for Openstack Accounts + if not (all(cp.type_yang == 'E1000' for cp in vdu_init.connection_points) or all(cp.type_yang != 'E1000' for cp in vdu_init.connection_points)): + ### We have a mix of E1000 & VIRTIO/SR_IPOV virtual interface types in the VDU, abort instantiation. + assert False, "Only one type of Virtual Intefaces supported for Openstack accounts. Found a mix of VIRTIO/SR_IOV & E1000." + + ## It is not clear if all the images need to checked for HW properties. In the absence of model info describing each im age's properties, + ### we shall assume that all images need to have similar properties + for img_info in imageinfo_list: + + virt_intf_type = vdu_init.connection_points[0].type_yang + if virt_intf_type == 'E1000': + if 'hw_vif_model' in img_info and img_info.hw_vif_model == 'e1000': + self.log.debug("VDU has Virtual Interface E1000, found matching image with property hw_vif_model=e1000") + else: + err_str = ("VDU has Virtual Interface E1000, but image '%s' does not have property hw_vif_model=e1000" % img_info.name) + self.log.error(err_str) + raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str) + elif virt_intf_type == 'VIRTIO' or virt_intf_type == 'SR_IOV': + if 'hw_vif_model' in img_info: + err_str = ("VDU has Virtual Interface %s, but image '%s' has hw_vif_model mismatch" % virt_intf_type,img_info.name) + self.log.error(err_str) + raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str) + else: + self.log.debug("VDU has Virtual Interface %s, found matching image" % virt_intf_type) else: - err_str = ("VDU has Virtual Interface E1000, but image '%s' does not have property hw_vif_model=e1000" % img_info.name) + err_str = ("VDU Virtual Interface '%s' not supported yet" % virt_intf_type) self.log.error(err_str) - raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str) - elif virt_intf_type == 'VIRTIO': - if 'hw_vif_model' in img_info: - err_str = ("VDU has Virtual Interface VIRTIO, but image '%s' has hw_vif_model mismatch" % img_info.name) - self.log.error(err_str) - raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str) - else: - self.log.debug("VDU has Virtual Interface VIRTIO, found matching image") - else: - err_str = ("VDU Virtual Interface '%s' not supported yet" % virt_intf_type) - self.log.error(err_str) - raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str) + raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str) with self._use_driver(account) as drv: ### Now Create VM - vm = RwcalYang.VMInfoItem() - vm.vm_name = vdu_init.name - vm.flavor_id = vdu_init.flavor_id - vm.image_id = vdu_init.image_id - vm_network = vm.network_list.add() - vm_network.network_id = drv._mgmt_network_id - if vdu_init.has_field('vdu_init') and vdu_init.vdu_init.has_field('userdata'): - vm.cloud_init.userdata = vdu_init.vdu_init.userdata - - if vdu_init.has_field('node_id'): - vm.user_tags.node_id = vdu_init.node_id; - - if vdu_init.has_field('availability_zone') and vdu_init.availability_zone.has_field('name'): - vm.availability_zone = vdu_init.availability_zone.name - + vm_network_list = [] + vm_network_list.append(drv._mgmt_network_id) + + if vdu_init.has_field('volumes'): + # Only combination supported: Image->Volume + for volume in vdu_init.volumes: + if "volume" in volume: + err_str = ("VDU Volume source not supported yet") + self.log.error(err_str) + raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str) + if "guest_params" not in volume: + err_str = ("VDU Volume destination parameters '%s' not defined") + self.log.error(err_str) + raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str) + if not volume.guest_params.has_field('device_type'): + err_str = ("VDU Volume destination type '%s' not defined") + self.log.error(err_str) + raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str) + if volume.guest_params.device_type not in ['disk', 'cdrom'] : + err_str = ("VDU Volume destination type '%s' not supported" % volume.guest_params.device_type) + self.log.error(err_str) + raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str) + + + server_group = None if vdu_init.has_field('server_group'): - ### Get list of server group in openstack for name->id mapping - openstack_group_list = drv.nova_server_group_list() - group_id = [ i['id'] for i in openstack_group_list if i['name'] == vdu_init.server_group.name] - if len(group_id) != 1: - raise OpenstackServerGroupError("VM placement failed. Server Group %s not found in openstack. Available groups" %(vdu_init.server_group.name, [i['name'] for i in openstack_group_list])) - vm.server_group = group_id[0] - - for port_id in port_list: - port = vm.port_list.add() - port.port_id = port_id + ### Get list of server group in openstack for name->id mapping + openstack_group_list = drv.nova_server_group_list() + group_id = [ i['id'] for i in openstack_group_list if i['name'] == vdu_init.server_group.name] + if len(group_id) != 1: + raise OpenstackServerGroupError("VM placement failed. Server Group %s not found in openstack. Available groups" %(vdu_init.server_group.name, [i['name'] for i in openstack_group_list])) + server_group = group_id[0] pci_assignement = self.prepare_vpci_metadata(drv, vdu_init) if pci_assignement != '': vm.user_tags.pci_assignement = pci_assignement - vm_id = self.do_create_vm(account, vm, no_rwstatus=True) + vm_id = self._create_vm(account, vdu_init, pci_assignement=pci_assignement, server_group=server_group, port_list=port_list, network_list=vm_network_list, imageinfo_list = imageinfo_list) self.prepare_vdu_on_boot(account, vm_id, floating_ip) return vm_id @@ -2107,11 +2276,13 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): openstack_group_list = drv.nova_server_group_list() server_group = [ i['name'] for i in openstack_group_list if vm['id'] in i['members']] + openstack_srv_volume_list = drv.nova_volume_list(vm['id']) vdu_info = RwcalOpenstackPlugin._fill_vdu_info(vm, flavor_info, account.openstack.mgmt_network, port_list, - server_group) + server_group, + volume_list = openstack_srv_volume_list) if vdu_info.state == 'active': try: console_info = drv.nova_server_console(vdu_info.vdu_id) @@ -2155,11 +2326,13 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): openstack_group_list = drv.nova_server_group_list() server_group = [ i['name'] for i in openstack_group_list if vm['id'] in i['members']] + openstack_srv_volume_list = drv.nova_volume_list(vm['id']) vdu = RwcalOpenstackPlugin._fill_vdu_info(vm, flavor_info, account.openstack.mgmt_network, port_list, - server_group) + server_group, + volume_list = openstack_srv_volume_list) if vdu.state == 'active': try: console_info = drv.nova_server_console(vdu.vdu_id) diff --git a/rwcal/plugins/yang/rwcal.yang b/rwcal/plugins/yang/rwcal.yang index 6f45e7c1..76bd38b3 100644 --- a/rwcal/plugins/yang/rwcal.yang +++ b/rwcal/plugins/yang/rwcal.yang @@ -998,6 +998,16 @@ module rwcal type string; } } + + list volumes { + key "name"; + + leaf name { + description "Name of the disk-volumes, e.g. vda, vdb etc"; + type string; + } + uses manotypes:volume-info; + } } container vdu-init-params { @@ -1214,8 +1224,26 @@ module rwcal type string; description "Console URL from the VIM, if available"; } + + list volumes { + key "name"; + + leaf name { + description "Name of the disk-volumes, e.g. vda, vdb etc"; + type string; + } + + leaf volume-id { + description "CAL assigned volume-id "; + rwpb:field-inline "true"; + rwpb:field-string-max 64; + type string; + } + + } } + container vnf-resources { rwpb:msg-new VNFResources; config false; diff --git a/rwcal/test/test_rwcal_openstack.py b/rwcal/test/test_rwcal_openstack.py index 119c22b1..960beb9f 100644 --- a/rwcal/test/test_rwcal_openstack.py +++ b/rwcal/test/test_rwcal_openstack.py @@ -39,11 +39,11 @@ logger = logging.getLogger('rwcal-openstack') openstack_info = { 'username' : 'pluto', 'password' : 'mypasswd', - 'auth_url' : 'http://10.66.4.14:5000/v3/', + 'auth_url' : 'http://10.66.4.17:5000/v3/', 'project_name' : 'demo', 'mgmt_network' : 'private', 'reserved_flavor' : 'm1.medium', - 'reserved_image' : 'rift-root-latest.qcow2', + 'reserved_image' : 'Fedora-x86_64-20-20131211.1-sda-ping.qcow2', 'physical_network' : None, 'network_type' : None, 'segmentation_id' : None @@ -55,6 +55,7 @@ def get_cal_account(): Creates an object for class RwcalYang.CloudAccount() """ account = RwcalYang.CloudAccount() + account.name = "Gruntxx" account.account_type = "openstack" account.openstack.key = openstack_info['username'] account.openstack.secret = openstack_info['password'] @@ -126,8 +127,9 @@ class OpenStackTest(unittest.TestCase): rc, rs = self.cal.get_network_list(self._acct) self.assertEqual(rc, RwStatus.SUCCESS) - networks = [ network for network in rs.networkinfo_list if (network.network_name == 'rift.cal.unittest.network' or network.network_name == 'rift.cal.virtual_link') ] + networks = [ network for network in rs.networkinfo_list if ((network.network_name == 'rift.cal.unittest.network') or ('rift.cal.virtual_link' in network.network_name) ) ] for network in networks: + logger.debug("Openstack-CAL-Test: Deleting old VL %s", network.network_id) self.cal.delete_virtual_link(self._acct, network.network_id) def tearDown(self): @@ -858,7 +860,7 @@ class OpenStackTest(unittest.TestCase): vlink_req = self._get_virtual_link_request_info() rc, rsp = self.cal.create_virtual_link(self._acct, vlink_req) - self.assertEqual(rc, RwStatus.SUCCESS) + self.assertEqual(rc.status, RwStatus.SUCCESS) logger.info("Openstack-CAL-Test: Created virtual_link with Id: %s" %rsp) vlink_id = rsp @@ -872,7 +874,7 @@ class OpenStackTest(unittest.TestCase): logger.info("Openstack-CAL-Test: Test Create VDU API") rc, rsp = self.cal.create_vdu(self._acct, vdu_req) - self.assertEqual(rc, RwStatus.SUCCESS) + self.assertEqual(rc.status, RwStatus.SUCCESS) logger.info("Openstack-CAL-Test: Created vdu with Id: %s" %rsp) vdu_id = rsp @@ -898,7 +900,7 @@ class OpenStackTest(unittest.TestCase): ### Create another virtual_link rc, rsp = self.cal.create_virtual_link(self._acct, vlink_req) - self.assertEqual(rc, RwStatus.SUCCESS) + self.assertEqual(rc.status, RwStatus.SUCCESS) logger.info("Openstack-CAL-Test: Created virtual_link with Id: %s" %rsp) vlink_id2= rsp @@ -932,7 +934,6 @@ class OpenStackTest(unittest.TestCase): logger.info("Openstack-CAL-Test: VDU/Virtual Link create-delete test successfully completed") - class VmData(object): """A convenience class that provides all the stats and EPA Attributes from the VM provided @@ -1059,5 +1060,5 @@ class VmData(object): if __name__ == "__main__": - logging.basicConfig(level=logging.INFO) + logging.basicConfig(level=logging.DEBUG) unittest.main() diff --git a/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/core.py b/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/core.py index b97b2f53..b8abea74 100644 --- a/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/core.py +++ b/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/core.py @@ -317,9 +317,22 @@ class NfviMetrics(object): vdu_metrics.memory.utilization = 100 * vdu_metrics.memory.used / vdu_metrics.memory.total # Storage - vdu_metrics.storage.used = metrics.storage.used - vdu_metrics.storage.total = 1e9 * self.vdur.vm_flavor.storage_gb - vdu_metrics.storage.utilization = 100 * vdu_metrics.storage.used / vdu_metrics.storage.total + try: + vdu_metrics.storage.used = metrics.storage.used + if self.vdur.has_field('volumes'): + for volume in self.vdur.volumes: + if vdu_metrics.storage.total is None: + vdu_metrics.storage.total = 1e9 * volume.size + else: + vdu_metrics.storage.total += (1e9 * volume.size) + else: + vdu_metrics.storage.total = 1e9 * self.vdur.vm_flavor.storage_gb + utilization = 100 * vdu_metrics.storage.used / vdu_metrics.storage.total + if utilization > 100: + utilization = 100 + vdu_metrics.storage.utilization = utilization + except ZeroDivisionError: + vdu_metrics.storage.utilization = 0 # Network (incoming) vdu_metrics.network.incoming.packets = metrics.network.incoming.packets diff --git a/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgr_core.py b/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgr_core.py index 161d5a49..a9fed38f 100644 --- a/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgr_core.py +++ b/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgr_core.py @@ -196,8 +196,9 @@ class ResourceMgrCALHandler(object): params = RwcalYang.VDUInitParams() params.from_dict(req_params.as_dict()) - image_checksum = req_params.image_checksum if req_params.has_field("image_checksum") else None - params.image_id = yield from self.get_image_id_from_image_info(req_params.image_name, image_checksum) + if 'image_name' in req_params: + image_checksum = req_params.image_checksum if req_params.has_field("image_checksum") else None + params.image_id = yield from self.get_image_id_from_image_info(req_params.image_name, image_checksum) #rc, rs = self._rwcal.create_vdu(self._account, params) self._log.debug("Calling create_vdu API with params %s" %(str(params))) diff --git a/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgr_events.py b/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgr_events.py index 5f87c668..c80925c6 100755 --- a/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgr_events.py +++ b/rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgr_events.py @@ -178,10 +178,12 @@ class ResourceMgrEvent(object): def monitor_vdu_state(response_xpath, pathentry): self._log.info("Initiating VDU state monitoring for xpath: %s ", response_xpath) - loop_cnt = 180 + time_to_wait = 300 + sleep_time = 2 + loop_cnt = int(time_to_wait/sleep_time) for i in range(loop_cnt): - self._log.debug("VDU state monitoring for xpath: %s. Sleeping for 1 second", response_xpath) - yield from asyncio.sleep(1, loop = self._loop) + self._log.debug("VDU state monitoring for xpath: %s. Sleeping for 2 second", response_xpath) + yield from asyncio.sleep(2, loop = self._loop) try: response_info = yield from self._parent.read_virtual_compute_info(pathentry.key00.event_id) except Exception as e: @@ -203,7 +205,7 @@ class ResourceMgrEvent(object): return else: ### End of loop. This is only possible if VDU did not reach active state - err_msg = "VDU state monitoring: VDU at xpath :{} did not reached active state in {} seconds. Aborting monitoring".format(response_xpath, loop_cnt) + err_msg = "VDU state monitoring: VDU at xpath :{} did not reached active state in {} seconds. Aborting monitoring".format(response_xpath, time_to_wait) self._log.info(err_msg) response_info = RwResourceMgrYang.VDUEventData_ResourceInfo() response_info.resource_state = 'failed' diff --git a/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py b/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py index f7d457eb..c78c9a90 100755 --- a/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py +++ b/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py @@ -340,6 +340,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 @@ -380,6 +382,7 @@ class VirtualDeploymentUnitRecord(object): "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} @@ -390,9 +393,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 @@ -402,6 +406,13 @@ 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 + icp_list = [] ii_list = [] @@ -433,8 +444,8 @@ class VirtualDeploymentUnitRecord(object): 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 @@ -541,9 +552,11 @@ class VirtualDeploymentUnitRecord(object): 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 @@ -594,6 +607,10 @@ class VirtualDeploymentUnitRecord(object): msg.event_id = self._request_id msg.cloud_account = self.cloud_account_name msg.request_info.from_dict(vm_create_msg_dict) + + for volume in self._vdud.volumes: + v = msg.request_info.volumes.add() + v.from_dict(volume.as_dict()) return msg @asyncio.coroutine @@ -1533,7 +1550,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: -- 2.25.1