update from RIFT as of 696b75d2fe9fb046261b08c616f1bcf6c0b54a9b second try
[osm/SO.git] / rwcal / plugins / vala / rwcal_openstack / rift / rwcal / openstack / utils / compute.py
index d7658da..7152962 100644 (file)
@@ -60,12 +60,21 @@ class ComputeUtils(object):
         from already existing flavors
         
         Arguments:
-          vdu_params: Protobuf GI object RwcalYang.VDUInitParams()
+          vdu_params: Protobuf GI object RwcalYang.YangData_RwProject_Project_VduInitParams()
 
         Returns:
            flavor_id(string): Flavor id for VDU instantiation
            None if no flavor could be found
         """
+        
+        if vdu_params.vm_flavor.has_field('vm_flavor_name') and \
+                        vdu_params.vm_flavor.vm_flavor_name is not None:
+            nova_flavor_list = self.driver.nova_flavor_list()
+            for flavor in nova_flavor_list:
+                self.log.debug("Flavor {} ".format(flavor.get('name', '')))
+                if flavor.get('name', '') == vdu_params.vm_flavor.vm_flavor_name:
+                    return flavor['id']
+            
         kwargs = { 'vcpus': vdu_params.vm_flavor.vcpu_count,
                    'ram'  : vdu_params.vm_flavor.memory_mb,
                    'disk' : vdu_params.vm_flavor.storage_gb,}
@@ -85,7 +94,7 @@ class ComputeUtils(object):
         is created.
         
         Arguments:
-          vdu_params: Protobuf GI object RwcalYang.VDUInitParams()
+          vdu_params: Protobuf GI object RwcalYang.YangData_RwProject_Project_VduInitParams()
 
         Returns:
            flavor_id(string): Flavor id for VDU instantiation
@@ -96,7 +105,7 @@ class ComputeUtils(object):
                           flavor_id, vdu_params.name)
             return flavor_id
 
-        flavor = RwcalYang.FlavorInfoItem()
+        flavor = RwcalYang.YangData_RwProject_Project_VimResources_FlavorinfoList()
         flavor.name = str(uuid.uuid4())
         
         epa_dict = { k: v for k, v in vdu_params.as_dict().items()
@@ -115,7 +124,7 @@ class ComputeUtils(object):
         """
         Creates flavor related arguments for VDU operation
         Arguments:
-          vdu_params: Protobuf GI object RwcalYang.VDUInitParams()
+          vdu_params: Protobuf GI object RwcalYang.YangData_RwProject_Project_VduInitParams()
 
         Returns:
            A dictionary {'flavor_id': <flavor-id>}
@@ -127,7 +136,7 @@ class ComputeUtils(object):
         """
         Creates image related arguments for VDU operation
         Arguments:
-          vdu_params: Protobuf GI object RwcalYang.VDUInitParams()
+          vdu_params: Protobuf GI object RwcalYang.YangData_RwProject_Project_VduInitParams()
 
         Returns:
            A dictionary {'image_id': <image-id>}
@@ -173,11 +182,36 @@ class ComputeUtils(object):
                                      %(image_name, checksum))
         return image['id']
         
+    def resolve_volume_n_validate(self, volume_ref):
+        """
+        Resolve the volume reference
+        
+        Arguments:
+          volume_ref (string): Name of volume reference
+
+        Raises VolumeValidateError in case of Errors
+        """
+        
+        for vol in self.driver._cinder_volume_list:
+            voldict = vol.to_dict()
+            if 'display_name' in voldict and voldict['display_name'] == volume_ref:
+                if 'status' in voldict:
+                    if voldict['status'] == 'available':
+                        return voldict['id']
+                    else:
+                        self.log.error("Volume %s not in available state. Current state: %s",
+                               volume_ref, voldict['status'])
+                        raise VolumeValidateError("Volume with name %s found in incorrect (%s) state"
+                                         %(volume_ref, voldict['status']))
+
+        self.log.info("No volume found with matching name: %s ", volume_ref)
+        raise VolumeValidateError("No volume found with matching name: %s " %(volume_ref))
+        
     def make_vdu_volume_args(self, volume, vdu_params):
         """
         Arguments:
-           volume:   Protobuf GI object RwcalYang.VDUInitParams_Volumes()
-           vdu_params: Protobuf GI object RwcalYang.VDUInitParams()
+           volume:   Protobuf GI object RwcalYang.YangData_RwProject_Project_VduInitParams_Volumes()
+           vdu_params: Protobuf GI object RwcalYang.YangData_RwProject_Project_VduInitParams()
         
         Returns:
            A dictionary required to create volume for VDU
@@ -186,40 +220,46 @@ class ComputeUtils(object):
         """
         kwargs = dict()
 
-        if volume.has_field('volume_ref'):
-            self.log.error("Unsupported option <Volume Reference> found for volume: %s", volume.name)
-            raise VolumeValidateError("Unsupported option <Volume Reference> found for volume: %s"
-                                      %(volume.name))
-        
-        kwargs['boot_index'] = volume.boot_priority
-        if "image" in volume:
+        if 'boot_priority' in volume:
+            # Rift-only field
+            kwargs['boot_index'] = volume.boot_priority
+        if volume.has_field("image"):
             # Support image->volume
-            if volume.image is not None:
-                kwargs['source_type'] = "image"
-                kwargs['uuid'] = self.resolve_image_n_validate(volume.image, volume.image_checksum)
-            else:
-                # Support blank->volume
-                kwargs['source_type'] = "blank"
+            kwargs['source_type'] = "image"
+            kwargs['uuid'] = self.resolve_image_n_validate(volume.image, volume.image_checksum)
+            kwargs['delete_on_termination'] = True
+        elif "volume_ref" in volume:
+            # Support volume-ref->volume (only ref)
+            # Rift-only field
+            kwargs['source_type'] = "volume"
+            kwargs['uuid'] = self.resolve_volume_n_validate(volume.volume_ref)
+            kwargs['delete_on_termination'] = False
+        else:
+            # Support blank->volume
+            kwargs['source_type'] = "blank"
+            kwargs['delete_on_termination'] = True
         kwargs['device_name'] = volume.name
         kwargs['destination_type'] = "volume"
         kwargs['volume_size'] = volume.size
-        kwargs['delete_on_termination'] = True
 
         if volume.has_field('device_type'):
-            if volume.device_type == 'cdrom':
-                kwargs['device_type'] = 'cdrom'
-            elif volume.device_bus == 'ide':
-                kwargs['disk_bus'] = 'ide'
+            if volume.device_type in ['cdrom', 'disk']:
+                kwargs['device_type'] = volume.device_type
             else:
                 self.log.error("Unsupported device_type <%s> found for volume: %s",
                                volume.device_type, volume.name)
                 raise VolumeValidateError("Unsupported device_type <%s> found for volume: %s"
-                                          %(volume.device_type, volume.name))        
-        else:
-            self.log.error("Mandatory field <device_type> not specified for volume: %s",
-                           volume.name)
-            raise VolumeValidateError("Mandatory field <device_type> not specified for volume: %s"
-                                      %(volume.name))
+                                          %(volume.device_type, volume.name))
+
+        if volume.has_field('device_bus'):
+            if volume.device_bus in ['ide', 'virtio', 'scsi']:
+                kwargs['disk_bus'] = volume.device_bus
+            else:
+                self.log.error("Unsupported device_type <%s> found for volume: %s",
+                               volume.device_type, volume.name)
+                raise VolumeValidateError("Unsupported device_type <%s> found for volume: %s"
+                                          %(volume.device_type, volume.name))
+
         return kwargs
             
     def make_vdu_storage_args(self, vdu_params):
@@ -227,7 +267,7 @@ class ComputeUtils(object):
         Creates volume related arguments for VDU operation
         
         Arguments:
-          vdu_params: Protobuf GI object RwcalYang.VDUInitParams()
+          vdu_params: Protobuf GI object RwcalYang.YangData_RwProject_Project_VduInitParams()
 
         Returns:
            A dictionary required for volumes creation for VDU instantiation
@@ -235,15 +275,24 @@ class ComputeUtils(object):
         kwargs = dict()
         if vdu_params.has_field('volumes'):
             kwargs['block_device_mapping_v2'] = list()
+            bootvol_list = list()
+            othervol_list = list()
+            # Ignore top-level image
+            kwargs['image_id']  = ""
             for volume in vdu_params.volumes:
-                kwargs['block_device_mapping_v2'].append(self.make_vdu_volume_args(volume, vdu_params))
+                if 'boot_priority' in volume:
+                    bootvol_list.append(self.make_vdu_volume_args(volume, vdu_params))
+                else:
+                    othervol_list.append(self.make_vdu_volume_args(volume, vdu_params))
+            # Sort block_device_mapping_v2 list by boot index, Openstack does not seem to respecting order by boot index
+            kwargs['block_device_mapping_v2'] = sorted(bootvol_list, key=lambda k: k['boot_index']) + othervol_list
         return kwargs
 
     def make_vdu_network_args(self, vdu_params):
         """
         Creates VDU network related arguments for VDU operation
         Arguments:
-          vdu_params: Protobuf GI object RwcalYang.VDUInitParams()
+          vdu_params: Protobuf GI object RwcalYang.YangData_RwProject_Project_VduInitParams()
 
         Returns:
            A dictionary {'port_list' : [ports], 'network_list': [networks]}
@@ -251,6 +300,7 @@ class ComputeUtils(object):
         """
         kwargs = dict()
         kwargs['port_list'], kwargs['network_list'] = self.driver.utils.network.setup_vdu_networking(vdu_params)
+        
         return kwargs
 
     
@@ -258,7 +308,7 @@ class ComputeUtils(object):
         """
         Creates VDU boot config related arguments for VDU operation
         Arguments:
-          vdu_params: Protobuf GI object RwcalYang.VDUInitParams()
+          vdu_params: Protobuf GI object RwcalYang.YangData_RwProject_Project_VduInitParams()
 
         Returns:
           A dictionary {
@@ -269,6 +319,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:
@@ -288,11 +344,18 @@ 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'):
+                for cm in vdu_params.supplemental_boot_data.custom_meta_data:
+                    # Adding this condition as the list contains CLOUD_INIT Variables as 
+                    # well. CloudInit Variables such as password are visible on the OpenStack UI
+                    # if not removed from the custom_meta_data list.
+                    if cm.destination == 'CLOUD_METADATA':
+                        metadata[cm.name] = cm.value
+                        kwargs['metadata'] = metadata
+        except Exception as e:
+            pass
 
         return kwargs
 
@@ -318,7 +381,7 @@ class ComputeUtils(object):
         Function to create kwargs required for nova server placement
         
         Arguments:
-          vdu_params: Protobuf GI object RwcalYang.VDUInitParams()
+          vdu_params: Protobuf GI object RwcalYang.YangData_RwProject_Project_VduInitParams()
         
         Returns:
          A dictionary { 'availability_zone' : < Zone >, 'scheduler_hints': <group-id> } 
@@ -341,8 +404,8 @@ class ComputeUtils(object):
         Function to create kwargs required for nova security group
 
         Arguments:
-          vdu_params: Protobuf GI object RwcalYang.VDUInitParams()
-          account: Protobuf GI object RwcalYang.CloudAccount()
+          vdu_params: Protobuf GI object RwcalYang.YangData_RwProject_Project_VduInitParams()
+          account: Protobuf GI object RwcalYang.YangData_RwProject_Project_CloudAccounts_CloudAccountList()
         
         Returns:
           A dictionary {'security_groups' : < group > }
@@ -358,8 +421,8 @@ class ComputeUtils(object):
         Function to create kwargs required for nova_server_create API
         
         Arguments:
-          vdu_params: Protobuf GI object RwcalYang.VDUInitParams()
-          account: Protobuf GI object RwcalYang.CloudAccount()
+          vdu_params: Protobuf GI object RwcalYang.YangData_RwProject_Project_VduInitParams()
+          account: Protobuf GI object RwcalYang.YangData_RwProject_Project_CloudAccounts_CloudAccountList()
 
         Returns:
           A kwargs dictionary for VDU create operation
@@ -399,6 +462,7 @@ class ComputeUtils(object):
                                 mgmt_ip = interface['addr']
                             elif interface['OS-EXT-IPS:type'] == 'floating':
                                 public_ip = interface['addr']
+
         return (mgmt_ip, public_ip)
 
     def get_vdu_epa_info(self, vm_info):
@@ -424,7 +488,7 @@ class ComputeUtils(object):
         Arguments:
         vdu_id (string) : VDU Id (vm_info['id']) 
         Returns:
-        A List of object RwcalYang.VDUInfoParams_ConnectionPoints()
+        A List of object RwcalYang.YangData_RwProject_Project_VnfResources_VduInfoList_ConnectionPoints()
 
         """
         cp_list = []
@@ -432,7 +496,7 @@ class ComputeUtils(object):
         port_list = self.driver.neutron_port_list(**{'device_id': vdu_id})
         for port in port_list:
             cp_info = self.driver.utils.network._parse_cp(port)
-            cp = RwcalYang.VDUInfoParams_ConnectionPoints()
+            cp = RwcalYang.YangData_RwProject_Project_VnfResources_VduInfoList_ConnectionPoints()
             cp.from_dict(cp_info.as_dict())
             cp_list.append(cp)
         return cp_list
@@ -476,6 +540,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.YangData_RwProject_Project_VnfResources_VduInfoList_SupplementalBootData()
+        """
+        supplemental_boot_data = None
+        node_id = None
+        if 'config_drive' in vm_info:
+            supplemental_boot_data = RwcalYang.YangData_RwProject_Project_VnfResources_VduInfoList_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.YangData_RwProject_Project_VnfResources_VduInfoList_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
@@ -483,7 +579,7 @@ class ComputeUtils(object):
           vm_info : A dictionary returned by novaclient library listing VM attributes
 
         Returns:
-          List of RwcalYang.VDUInfoParams_Volumes()
+          List of RwcalYang.YangData_RwProject_Project_VnfResources_VduInfoList_Volumes()
         """
         volumes = list()
         
@@ -494,15 +590,21 @@ class ComputeUtils(object):
             return volumes
 
         for v in volume_list:
-            volume = RwcalYang.VDUInfoParams_Volumes()
+            volume = RwcalYang.YangData_RwProject_Project_VnfResources_VduInfoList_Volumes()
             try:
                 volume.name = (v['device']).split('/')[2]
                 volume.volume_id = v['volumeId']
                 details = self.driver.cinder_volume_get(volume.volume_id)
-                for k, v in details.metadata.items():
-                    vd = volume.custom_meta_data.add()
-                    vd.name = k
-                    vd.value = v
+                if details is None:
+                    continue
+                try:
+                    # Rift only
+                    for k, v in details.metadata.items():
+                        vd = volume.custom_meta_data.add()
+                        vd.name = k
+                        vd.value = v
+                except Exception as e:
+                    pass
             except Exception as e:
                 self.log.exception("Exception %s occured during volume list parsing", str(e))
                 continue
@@ -520,11 +622,17 @@ class ComputeUtils(object):
           console_url(string): Console URL for VM
         """
         console_url = None
-        if self._parse_vdu_state_info(vm_info) == 'ACTIVE':
+        if self._parse_vdu_state_info(vm_info) == 'active':
             try:
-                console_url = self.driver.nova_server_console(vm_info['id'])
+                serv_console_url = self.driver.nova_server_console(vm_info['id'])
+                if 'console' in serv_console_url:
+                    console_url = serv_console_url['console']['url']
+                else:
+                    self.log.error("Error fetching console url. This could be an Openstack issue. Console : " + str(serv_console_url))
+
+
             except Exception as e:
-                self.log.exception("Exception %s occured during volume list parsing", str(e))
+                self.log.warning("Exception %s occured during volume list parsing", str(e))
         return console_url
 
     def parse_cloud_vdu_info(self, vm_info):
@@ -535,16 +643,13 @@ class ComputeUtils(object):
            vm_info : A dictionary object return by novaclient library listing VM attributes
         
         Returns:
-           Protobuf GI Object of type RwcalYang.VDUInfoParams()
+           Protobuf GI Object of type RwcalYang.YangData_RwProject_Project_VnfResources_VduInfoList()
         """
-        vdu = RwcalYang.VDUInfoParams()
+        vdu = RwcalYang.YangData_RwProject_Project_VnfResources_VduInfoList()
         vdu.name = vm_info['name']
         vdu.vdu_id = vm_info['id']
         vdu.cloud_type  = 'openstack'
 
-        if 'config_drive' in vm_info:
-            vdu.supplemental_boot_data.boot_data_drive = vm_info['config_drive']
-
         if 'image' in vm_info and 'id' in vm_info['image']:
             vdu.image_id = vm_info['image']['id']
 
@@ -563,17 +668,24 @@ class ComputeUtils(object):
         if 'flavor' in vm_info and 'id' in vm_info['flavor']:
             vdu.flavor_id = vm_info['flavor']['id']
             flavor_info = self.get_vdu_epa_info(vm_info)
-            vm_flavor = self.driver.utils.flavor.parse_vm_flavor_epa_info(flavor_info)
-            guest_epa = self.driver.utils.flavor.parse_guest_epa_info(flavor_info)
-            host_epa = self.driver.utils.flavor.parse_host_epa_info(flavor_info)
-            host_aggregates = self.driver.utils.flavor.parse_host_aggregate_epa_info(flavor_info)
-
-            vdu.vm_flavor.from_dict(vm_flavor.as_dict())
-            vdu.guest_epa.from_dict(guest_epa.as_dict())
-            vdu.host_epa.from_dict(host_epa.as_dict())
-            for aggr in host_aggregates:
-                ha = vdu.host_aggregate.add()
-                ha.from_dict(aggr.as_dict())
+            if flavor_info is not None:
+                vm_flavor = self.driver.utils.flavor.parse_vm_flavor_epa_info(flavor_info)
+                guest_epa = self.driver.utils.flavor.parse_guest_epa_info(flavor_info)
+                host_epa = self.driver.utils.flavor.parse_host_epa_info(flavor_info)
+                host_aggregates = self.driver.utils.flavor.parse_host_aggregate_epa_info(flavor_info)
+
+                vdu.vm_flavor.from_dict(vm_flavor.as_dict())
+                vdu.guest_epa.from_dict(guest_epa.as_dict())
+                vdu.host_epa.from_dict(host_epa.as_dict())
+                for aggr in host_aggregates:
+                    ha = vdu.host_aggregate.add()
+                    ha.from_dict(aggr.as_dict())
+
+        node_id, boot_data = self._parse_vdu_boot_config_data(vm_info)
+        if node_id:
+            vdu.node_id = node_id
+        if boot_data:
+            vdu.supplemental_boot_data = boot_data
 
         cp_list = self._parse_vdu_cp_info(vdu.vdu_id)
         for cp in cp_list:
@@ -605,6 +717,5 @@ class ComputeUtils(object):
         port_list = self.driver.neutron_port_list(**{'device_id': vdu_id})
 
         for port in port_list:
-            if ((port['device_owner'] == 'compute:None') or (port['device_owner'] == '')):
-                self.driver.neutron_port_delete(port['id'])
+            self.driver.neutron_port_delete(port['id'])