SO Multidisk changes 51/751/1
authorchamarty <ravi.chamarty@riftio.com>
Tue, 29 Nov 2016 18:44:47 +0000 (18:44 +0000)
committerchamarty <ravi.chamarty@riftio.com>
Tue, 29 Nov 2016 18:45:49 +0000 (18:45 +0000)
Change-Id: I75a5029fbaa295c11af24cfa4fe8ca872a73c69f
Signed-off-by: chamarty <ravi.chamarty@riftio.com>
12 files changed:
models/plugins/yang/mano-types.yang
models/plugins/yang/vnfd.yang
models/plugins/yang/vnfr.yang
rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/openstack_drv.py
rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/prepare_vm.py
rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py
rwcal/plugins/yang/rwcal.yang
rwcal/test/test_rwcal_openstack.py
rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/core.py
rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgr_core.py
rwlaunchpad/plugins/rwresmgr/rift/tasklets/rwresmgrtasklet/rwresmgr_events.py
rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py

index e7c7d3d..5f68b41 100644 (file)
@@ -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;
+        }
+      }
+    }
+  }
 }
index 2cc43d3..81bb2aa 100644 (file)
@@ -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 {
index a4419ce..8f739f4 100644 (file)
@@ -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";
index 2b8ab7c..943cdd5 100644 (file)
@@ -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()
 
index 7acf0fd..0d658f1 100644 (file)
@@ -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':
index 8a2d275..e5dcb67 100644 (file)
@@ -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)
index 6f45e7c..76bd38b 100644 (file)
@@ -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;
index 119c22b..960beb9 100644 (file)
@@ -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()
index b97b2f5..b8abea7 100644 (file)
@@ -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
index 161d5a4..a9fed38 100644 (file)
@@ -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)))
index 5f87c66..c80925c 100755 (executable)
@@ -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'
index f7d457e..c78c9a9 100755 (executable)
@@ -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: