bug 331 Ensure neutron port is deleted upon vm creation failed 80/5580/8
authortierno <alfonso.tiernosepulveda@telefonica.com>
Sat, 14 Oct 2017 11:27:03 +0000 (13:27 +0200)
committertierno <alfonso.tiernosepulveda@telefonica.com>
Fri, 10 Nov 2017 12:17:30 +0000 (13:17 +0100)
Change-Id: I9fd63a5fd09495ba02b46c55337d2da68fcae7b5
Signed-off-by: tierno <alfonso.tiernosepulveda@telefonica.com>
openmanod
osm_ro/nfvo.py
osm_ro/vim_thread.py
osm_ro/vimconn.py
osm_ro/vimconn_aws.py
osm_ro/vimconn_openstack.py
osm_ro/vimconn_openvim.py
osm_ro/vimconn_vmware.py
osm_ro/vmwarecli.py
osm_ro/vmwarerecli.py
test/test_RO.py

index 14dba03..14f0777 100755 (executable)
--- a/openmanod
+++ b/openmanod
@@ -48,7 +48,7 @@ import osm_ro
 
 __author__ = "Alfonso Tierno, Gerardo Garcia, Pablo Montes"
 __date__ = "$26-aug-2014 11:09:29$"
-__version__ = "0.5.38-r548"
+__version__ = "0.5.39-r549"
 version_date = "Nov 2017"
 database_version = 27      # expected database schema version
 
index c864be3..69c4ac3 100644 (file)
@@ -2409,7 +2409,7 @@ def start_scenario(mydb, tenant_id, scenario_id, instance_scenario_name, instanc
                 else:
                     av_index = None
 
-                vm_id = myvim.new_vminstance(myVMDict['name'], myVMDict['description'], myVMDict.get('start', None),
+                vm_id, _ = myvim.new_vminstance(myVMDict['name'], myVMDict['description'], myVMDict.get('start', None),
                                              myVMDict['imageRef'], myVMDict['flavorRef'], myVMDict['networks'],
                                              availability_zone_index=av_index,
                                              availability_zone_list=vnf_availability_zones)
index 22a882e..2c30fb9 100644 (file)
@@ -43,6 +43,7 @@ The task content are (M: stored at memory, D: stored at database):
                 iface_id: uuid of intance_interfaces
                 sdn_port_id:
                 sdn_net_id:
+            created_items: dictionary with extra elements created that need to be deleted. e.g. ports, volumes,...
             created:    False if the VIM element is not created by other actions, and it should not be deleted
             vim_status: VIM status of the element. Stored also at database in the instance_XXX
     M   depends:    dict with task_index(from depends_on) to task class
@@ -533,6 +534,10 @@ class vim_thread(threading.Thread):
                             task["extra"]["sdn_vim_id"] = to_supersede["extra"]["sdn_vim_id"]
                         if to_supersede["extra"].get("interfaces"):
                             task["extra"]["interfaces"] = to_supersede["extra"]["interfaces"]
+                        if to_supersede["extra"].get("created_items"):
+                            if not task["extra"].get("created_items"):
+                                task["extra"]["created_items"] = {}
+                            task["extra"]["created_items"].update(to_supersede["extra"]["created_items"])
                     # Mark task as SUPERSEDED.
                     #   If task is in self.pending_tasks, it will be removed and database will be update
                     #   If task is in self.refresh_tasks, it will be removed
@@ -649,7 +654,7 @@ class vim_thread(threading.Thread):
                             "Cannot create VM because depends on a network not created or found: " +
                             str(task_net["error_msg"]))
                     net["net_id"] = network_id
-            vim_vm_id = self.vim.new_vminstance(*params)
+            vim_vm_id, created_items = self.vim.new_vminstance(*params)
 
             # fill task_interfaces. Look for snd_net_id at database for each interface
             task_interfaces = {}
@@ -669,6 +674,7 @@ class vim_thread(threading.Thread):
             task["vim_interfaces"] = {}
             task["extra"]["interfaces"] = task_interfaces
             task["extra"]["created"] = True
+            task["extra"]["created_items"] = created_items
             task["error_msg"] = None
             task["status"] = "DONE"
             task["vim_id"] = vim_vm_id
@@ -698,7 +704,7 @@ class vim_thread(threading.Thread):
                             iface["sdn_port_id"], vm_vim_id) + str(e), exc_info=True)
                         # TODO Set error_msg at instance_nets
 
-            self.vim.delete_vminstance(vm_vim_id)
+            self.vim.delete_vminstance(vm_vim_id, task["extra"].get("created_items"))
             task["status"] = "DONE"
             task["error_msg"] = None
             return True, None
@@ -713,6 +719,13 @@ class vim_thread(threading.Thread):
             return False, None
 
     def _get_net_internal(self, task, filter_param):
+        """
+        Common code for get_net and new_net. It looks for a network on VIM with the filter_params
+        :param task: task for this find or find-or-create action
+        :param filter_param: parameters to send to the vimconnector
+        :return: a dict with the content to update the instance_nets database table. Raises an exception on error, or
+            when network is not found or found more than one
+        """
         vim_nets = self.vim.get_network_list(filter_param)
         if not vim_nets:
             raise VimThreadExceptionNotFound("Network not found with this criteria: '{}'".format(filter))
index 1fbce19..1f60986 100644 (file)
@@ -519,7 +519,11 @@ class vimconnector():
             availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV required
             availability_zone_list: list of availability zones given by user in the VNFD descriptor.  Ignore if
                 availability_zone_index is None
-        Returns the instance identifier or raises an exception on error
+        Returns a tuple with the instance identifier and created_items or raises an exception on error
+            created_items can be None or a dictionary where this method can include key-values that will be passed to
+            the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc.
+            Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
+            as not present.
         """
         raise vimconnNotImplemented( "Should have implemented this" )
         
@@ -527,9 +531,14 @@ class vimconnector():
         """Returns the VM instance information from VIM"""
         raise vimconnNotImplemented( "Should have implemented this" )
         
-    def delete_vminstance(self, vm_id):
-        """Removes a VM instance from VIM
-        Returns the instance identifier"""
+    def delete_vminstance(self, vm_id, created_items=None):
+        """
+        Removes a VM instance from VIM and each associate elements
+        :param vm_id: VIM identifier of the VM, provided by method new_vminstance
+        :param created_items: dictionary with extra items to be deleted. provided by method new_vminstance and/or method
+            action_vminstance
+        :return: None or the same vm_id. Raises an exception on fail
+        """
         raise vimconnNotImplemented( "Should have implemented this" )
 
     def refresh_vms_status(self, vm_list):
@@ -560,9 +569,18 @@ class vimconnector():
         """
         raise vimconnNotImplemented( "Should have implemented this" )
     
-    def action_vminstance(self, vm_id, action_dict):
-        """Send and action over a VM instance from VIM
-        Returns the vm_id if the action was successfully sent to the VIM"""
+    def action_vminstance(self, vm_id, action_dict, created_items={}):
+        """
+        Send and action over a VM instance. Returns created_items if the action was successfully sent to the VIM.
+        created_items is a dictionary with items that
+        :param vm_id: VIM identifier of the VM, provided by method new_vminstance
+        :param action_dict: dictionary with the action to perform
+        :param created_items: provided by method new_vminstance is a dictionary with key-values that will be passed to
+            the method delete_vminstance. Can be used to store created ports, volumes, etc. Format is vimconnector
+            dependent, but do not use nested dictionaries and a value of None should be the same as not present. This
+            method can modify this value
+        :return: None, or a console dict
+        """
         raise vimconnNotImplemented( "Should have implemented this" )
     
     def get_vminstance_console(self, vm_id, console_type="vnc"):
index 3ccef44..0e1f573 100644 (file)
@@ -634,7 +634,11 @@ class vimconnector(vimconn.vimconnector):
                 disk_list': (optional) list with additional disks to the VM. Each item is a dict with:
                     image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted
                     size': (mandatory) string with the size of the disk in GB
-        Returns: instance identifier or raises an exception on error
+        Returns a tuple with the instance identifier and created_items or raises an exception on error
+            created_items can be None or a dictionary where this method can include key-values that will be passed to
+            the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc.
+            Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
+            as not present.
         """
 
         self.logger.debug("Creating a new VM instance")
@@ -682,7 +686,7 @@ class vimconnector(vimconn.vimconnector):
                     net_list[index]['vim_id'] = reservation.instances[0].interfaces[index].id
 
             instance = reservation.instances[0]
-            return instance.id
+            return instance.id, None
         except Exception as e:
             self.format_vimconn_exception(e)
 
@@ -696,7 +700,7 @@ class vimconnector(vimconn.vimconnector):
         except Exception as e:
             self.format_vimconn_exception(e)
 
-    def delete_vminstance(self, vm_id):
+    def delete_vminstance(self, vm_id, created_items=None):
         """Removes a VM instance from VIM
         Returns the instance identifier"""
 
@@ -772,7 +776,7 @@ class vimconnector(vimconn.vimconnector):
             self.logger.error("Exception getting vm status: %s", str(e), exc_info=True)
             self.format_vimconn_exception(e)
 
-    def action_vminstance(self, vm_id, action_dict):
+    def action_vminstance(self, vm_id, action_dict, created_items={}):
         """Send and action over a VM instance from VIM
         Returns the vm_id if the action was successfully sent to the VIM"""
 
@@ -787,6 +791,6 @@ class vimconnector(vimconn.vimconnector):
                 self.conn.terminate_instances(vm_id)
             elif "reboot" in action_dict:
                 self.conn.reboot_instances(vm_id)
-            return vm_id
+            return None
         except Exception as e:
             self.format_vimconn_exception(e)
index e90497a..24033da 100644 (file)
@@ -936,7 +936,7 @@ class vimconnector(vimconn.vimconnector):
 
     def new_vminstance(self, name, description, start, image_id, flavor_id, net_list, cloud_config=None, disk_list=None,
                        availability_zone_index=None, availability_zone_list=None):
-        '''Adds a VM instance to VIM
+        """Adds a VM instance to VIM
         Params:
             start: indicates if VM must start or boot in pause mode. Ignored
             image_id,flavor_id: iamge and flavor uuid
@@ -971,20 +971,25 @@ class vimconnector(vimconn.vimconnector):
             availability_zone_list: list of availability zones given by user in the VNFD descriptor.  Ignore if
                 availability_zone_index is None
                 #TODO ip, security groups
-        Returns the instance identifier
-        '''
+        Returns a tuple with the instance identifier and created_items or raises an exception on error
+            created_items can be None or a dictionary where this method can include key-values that will be passed to
+            the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc.
+            Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
+            as not present.
+        """
         self.logger.debug("new_vminstance input: image='%s' flavor='%s' nics='%s'",image_id, flavor_id,str(net_list))
         try:
             server = None
-            metadata={}
-            net_list_vim=[]
-            external_network=[]     # list of external networks to be connected to instance, later on used to create floating_ip
+            created_items = {}
+            metadata = {}
+            net_list_vim = []
+            external_network = []   # list of external networks to be connected to instance, later on used to create floating_ip
             no_secured_ports = []   # List of port-is with port-security disabled
             self._reload_connection()
-            metadata_vpci={}   # For a specific neutron plugin
+            metadata_vpci = {}   # For a specific neutron plugin
             block_device_mapping = None
             for net in net_list:
-                if not net.get("net_id"): #skip non connected iface
+                if not net.get("net_id"):   # skip non connected iface
                     continue
 
                 port_dict={
@@ -1024,6 +1029,7 @@ class vimconnector(vimconn.vimconnector):
                 if net.get("mac_address"):
                     port_dict["mac_address"]=net["mac_address"]
                 new_port = self.neutron.create_port({"port": port_dict })
+                created_items[("port", str(new_port["port"]["id"]))] = True
                 net["mac_adress"] = new_port["port"]["mac_address"]
                 net["vim_id"] = new_port["port"]["id"]
                 # if try to use a network without subnetwork, it will return a emtpy list
@@ -1064,10 +1070,10 @@ class vimconnector(vimconn.vimconnector):
             security_groups   = self.config.get('security_groups')
             if type(security_groups) is str:
                 security_groups = ( security_groups, )
-            #cloud config
+            # cloud config
             config_drive, userdata = self._create_user_data(cloud_config)
 
-            #Create additional volumes in case these are present in disk_list
+            # Create additional volumes in case these are present in disk_list
             base_disk_index = ord('b')
             if disk_list != None:
                 block_device_mapping = {}
@@ -1078,10 +1084,11 @@ class vimconnector(vimconn.vimconnector):
                     else:
                         volume = self.cinder.volumes.create(size=disk['size'], name=name + '_vd' +
                                     chr(base_disk_index))
+                    created_items[("volume", str(volume.id))] = True
                     block_device_mapping['_vd' +  chr(base_disk_index)] = volume.id
                     base_disk_index += 1
 
-                #wait until volumes are with status available
+                # wait until volumes are with status available
                 keep_waiting = True
                 elapsed_time = 0
                 while keep_waiting and elapsed_time < volume_timeout:
@@ -1093,17 +1100,8 @@ class vimconnector(vimconn.vimconnector):
                         time.sleep(1)
                         elapsed_time += 1
 
-                #if we exceeded the timeout rollback
+                # if we exceeded the timeout rollback
                 if elapsed_time >= volume_timeout:
-                    #delete the volumes we just created
-                    for volume_id in block_device_mapping.itervalues():
-                        self.cinder.volumes.delete(volume_id)
-
-                    #delete ports we just created
-                    for net_item  in net_list_vim:
-                        if 'port-id' in net_item:
-                            self.neutron.delete_port(net_item['port-id'])
-
                     raise vimconn.vimconnException('Timeout creating volumes for instance ' + name,
                                                    http_code=vimconn.HTTP_Request_Timeout)
             # get availability Zone
@@ -1130,23 +1128,21 @@ class vimconnector(vimconn.vimconnector):
             for port_id in no_secured_ports:
                 try:
                     self.neutron.update_port(port_id, {"port": {"port_security_enabled": False, "security_groups": None} })
-
                 except Exception as e:
                     self.logger.error("It was not possible to disable port security for port {}".format(port_id))
-                    self.delete_vminstance(server.id)
                     raise
 
-            #print "DONE :-)", server
-            pool_id = None
-            floating_ips = self.neutron.list_floatingips().get("floatingips", ())
+            # print "DONE :-)", server
 
+            pool_id = None
             if external_network:
+                floating_ips = self.neutron.list_floatingips().get("floatingips", ())
                 self.__wait_for_vm(server.id, 'ACTIVE')
 
             for floating_network in external_network:
                 try:
                     assigned = False
-                    while(assigned == False):
+                    while not assigned:
                         if floating_ips:
                             ip = floating_ips.pop(0)
                             if not ip.get("port_id", False) and ip.get('tenant_id') == server.tenant_id:
@@ -1190,7 +1186,7 @@ class vimconnector(vimconn.vimconnector):
                         continue
                     raise
 
-            return server.id
+            return server.id, created_items
 #        except nvExceptions.NotFound as e:
 #            error_value=-vimconn.HTTP_Not_Found
 #            error_text= "vm instance %s not found" % vm_id
@@ -1198,19 +1194,13 @@ class vimconnector(vimconn.vimconnector):
 #            raise vimconn.vimconnException(type(e).__name__ + ": "+  str(e), http_code=vimconn.HTTP_Bad_Request)
 
         except Exception as e:
-            # delete the volumes we just created
-            if block_device_mapping:
-                for volume_id in block_device_mapping.itervalues():
-                    self.cinder.volumes.delete(volume_id)
-
-            # Delete the VM
-            if server != None:
-                self.delete_vminstance(server.id)
-            else:
-                # delete ports we just created
-                for net_item in net_list_vim:
-                    if 'port-id' in net_item:
-                        self.neutron.delete_port(net_item['port-id'])
+            server_id = None
+            if server:
+                server_id = server.id
+            try:
+                self.delete_vminstance(server_id, created_items)
+            except Exception as e2:
+                self.logger.error("new_vminstance rollback fail {}".format(e2))
 
             self._format_exception(e)
 
@@ -1276,50 +1266,57 @@ class vimconnector(vimconn.vimconnector):
         except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.BadRequest, ConnectionError) as e:
             self._format_exception(e)
 
-    def delete_vminstance(self, vm_id):
+    def delete_vminstance(self, vm_id, created_items=None):
         '''Removes a VM instance from VIM. Returns the old identifier
         '''
         #print "osconnector: Getting VM from VIM"
+        if created_items == None:
+            created_items = {}
         try:
             self._reload_connection()
-            #delete VM ports attached to this networks before the virtual machine
-            ports = self.neutron.list_ports(device_id=vm_id)
-            for p in ports['ports']:
+            # delete VM ports attached to this networks before the virtual machine
+            for k, v in created_items.items():
+                if not v:  # skip already deleted
+                    continue
                 try:
-                    self.neutron.delete_port(p["id"])
+                    if k[0] == "port":
+                        self.neutron.delete_port(k[1])
                 except Exception as e:
                     self.logger.error("Error deleting port: " + type(e).__name__ + ": "+  str(e))
 
-            #commented because detaching the volumes makes the servers.delete not work properly ?!?
-            #dettach volumes attached
-            server = self.nova.servers.get(vm_id)
-            volumes_attached_dict = server._info['os-extended-volumes:volumes_attached']
-            #for volume in volumes_attached_dict:
-            #    self.cinder.volumes.detach(volume['id'])
+            # #commented because detaching the volumes makes the servers.delete not work properly ?!?
+            # #dettach volumes attached
+            server = self.nova.servers.get(vm_id)
+            # volumes_attached_dict = server._info['os-extended-volumes:volumes_attached']   #volume['id']
+            # #for volume in volumes_attached_dict:
+            #    self.cinder.volumes.detach(volume['id'])
 
-            self.nova.servers.delete(vm_id)
+            if vm_id:
+                self.nova.servers.delete(vm_id)
 
-            #delete volumes.
-            #Although having detached them should have them  in active status
-            #we ensure in this loop
+            # delete volumes. Although having detached, they should have in active status before deleting
+            # we ensure in this loop
             keep_waiting = True
             elapsed_time = 0
             while keep_waiting and elapsed_time < volume_timeout:
                 keep_waiting = False
-                for volume in volumes_attached_dict:
-                    if self.cinder.volumes.get(volume['id']).status != 'available':
-                        keep_waiting = True
-                    else:
-                        self.cinder.volumes.delete(volume['id'])
+                for k, v in created_items.items():
+                    if not v:  # skip already deleted
+                        continue
+                    try:
+                        if k[0] == "volume":
+                            if self.cinder.volumes.get(k[1]).status != 'available':
+                                keep_waiting = True
+                            else:
+                                self.cinder.volumes.delete(k[1])
+                    except Exception as e:
+                        self.logger.error("Error deleting volume: " + type(e).__name__ + ": " + str(e))
                 if keep_waiting:
                     time.sleep(1)
                     elapsed_time += 1
-
-            return vm_id
+            return None
         except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
             self._format_exception(e)
-        #TODO insert exception vimconn.HTTP_Unauthorized
-        #if reaching here is because an exception
 
     def refresh_vms_status(self, vm_list):
         '''Get the status of the virtual machines and their interfaces/ports
@@ -1423,9 +1420,9 @@ class vimconnector(vimconn.vimconnector):
             vm_dict[vm_id] = vm
         return vm_dict
 
-    def action_vminstance(self, vm_id, action_dict):
+    def action_vminstance(self, vm_id, action_dict, created_items={}):
         '''Send and action over a VM instance from VIM
-        Returns the vm_id if the action was successfully sent to the VIM'''
+        Returns None or the console dict if the action was successfully sent to the VIM'''
         self.logger.debug("Action over VM '%s': %s", vm_id, str(action_dict))
         try:
             self._reload_connection()
@@ -1492,7 +1489,7 @@ class vimconnector(vimconn.vimconnector):
                 except Exception as e:
                     raise vimconn.vimconnException("Unexpected response from VIM " + str(console_dict))
 
-            return vm_id
+            return None
         except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound, ConnectionError) as e:
             self._format_exception(e)
         #TODO insert exception vimconn.HTTP_Unauthorized
index 135cfc6..b45edf6 100644 (file)
@@ -800,7 +800,11 @@ class vimconnector(vimconn.vimconnector):
                 type: 'virtual', 'PF', 'VF', 'VFnotShared'
                 vim_id: filled/added by this function
                 #TODO ip, security groups
-        Returns the instance identifier
+        Returns a tuple with the instance identifier and created_items or raises an exception on error
+            created_items can be None or a dictionary where this method can include key-values that will be passed to
+            the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc.
+            Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
+            as not present.
         '''
         self.logger.debug("new_vminstance input: image='%s' flavor='%s' nics='%s'", image_id, flavor_id, str(net_list))
         try:
@@ -873,7 +877,7 @@ class vimconnector(vimconn.vimconnector):
                                 #        return result, error_text
                                 break
         
-            return vminstance_id
+            return vminstance_id, None
         except (requests.exceptions.RequestException, js_e.ValidationError) as e:
             self._format_request_exception(e)
         
@@ -897,7 +901,7 @@ class vimconnector(vimconn.vimconnector):
         except (requests.exceptions.RequestException, js_e.ValidationError) as e:
             self._format_request_exception(e)
         
-    def delete_vminstance(self, vm_id):
+    def delete_vminstance(self, vm_id, created_items=None):
         '''Removes a VM instance from VIM, returns the deleted vm_id'''
         try:
             self._get_my_tenant()
@@ -1026,7 +1030,7 @@ class vimconnector(vimconn.vimconnector):
             net_dict[net_id] = net
         return net_dict
     
-    def action_vminstance(self, vm_id, action_dict):
+    def action_vminstance(self, vm_id, action_dict, created_items={}):
         '''Send and action over a VM instance from VIM'''
         '''Returns the status'''
         try:
@@ -1037,7 +1041,7 @@ class vimconnector(vimconn.vimconnector):
             self.logger.info("Action over VM instance POST %s", url)
             vim_response = requests.post(url, headers = self.headers_req, data=json.dumps(action_dict) )
             self._check_http_request_response(vim_response)
-            return vm_id
+            return None
         except (requests.exceptions.RequestException, js_e.ValidationError) as e:
             self._format_request_exception(e)
 
index 82d6a69..f99ef65 100644 (file)
@@ -1420,7 +1420,11 @@ class vimconnector(vimconn.vimconnector):
             availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV required
             availability_zone_list: list of availability zones given by user in the VNFD descriptor.  Ignore if
                 availability_zone_index is None
-        Returns the instance identifier or raises an exception on error
+        Returns a tuple with the instance identifier and created_items or raises an exception on error
+            created_items can be None or a dictionary where this method can include key-values that will be passed to
+            the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc.
+            Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
+            as not present.
         """
         self.logger.info("Creating new instance for entry {}".format(name))
         self.logger.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {} disk_list {}".format(
@@ -1767,7 +1771,7 @@ class vimconnector(vimconn.vimconnector):
             wait_time +=INTERVAL_TIME
 
         if vapp_uuid is not None:
-            return vapp_uuid
+            return vapp_uuid, None
         else:
             raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
 
@@ -1825,7 +1829,7 @@ class vimconnector(vimconn.vimconnector):
 
         return vm_dict
 
-    def delete_vminstance(self, vm__vim_uuid):
+    def delete_vminstance(self, vm__vim_uuid, created_items=None):
         """Method poweroff and remove VM instance from vcloud director network.
 
         Args:
@@ -2129,7 +2133,7 @@ class vimconnector(vimconn.vimconnector):
             self.logger.debug("ParseError in response from NSX Manager {}".format(Err.message), exc_info=True)
 
 
-    def action_vminstance(self, vm__vim_uuid=None, action_dict=None):
+    def action_vminstance(self, vm__vim_uuid=None, action_dict=None, created_items={}):
         """Send and action over a VM instance from VIM
         Returns the vm_id if the action was successfully sent to the VIM"""
 
@@ -2139,7 +2143,7 @@ class vimconnector(vimconn.vimconnector):
 
         vdc = self.get_vdc_details()
         if vdc is None:
-            return -1, "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)
+            raise  vimconn.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
 
         vapp_name = self.get_namebyvappid(vdc, vm__vim_uuid)
         if vapp_name is None:
@@ -2191,7 +2195,7 @@ class vimconnector(vimconn.vimconnector):
                 reboot_task = the_vapp.reboot()
             else:
                 raise vimconn.vimconnException("action_vminstance: Invalid action {} or action is None.".format(action_dict))
-            return vm__vim_uuid
+            return None
         except Exception as exp :
             self.logger.debug("action_vminstance: Failed with Exception {}".format(exp))
             raise vimconn.vimconnException("action_vminstance: Failed with Exception {}".format(exp))
index 4583a65..80fe394 100755 (executable)
@@ -554,7 +554,7 @@ def boot_image(vim=None, image_name=None, vm_name=None):
                 return None
 
         print (" Booting {} image id {} ".format(vm_name, vim_catalog))
-        vm_uuid = vim.new_vminstance(name=vm_name, image_id=vim_catalog)
+        vm_uuid, _ = vim.new_vminstance(name=vm_name, image_id=vim_catalog)
         if vm_uuid is not None and validate_uuid4(vm_uuid):
             print("Image booted and vm uuid {}".format(vm_uuid))
             vapp_dict = vim.get_vapp(vdc_name=namespace.vcdvdc, vapp_name=vm_uuid, isuuid=True)
index acd4240..b2f1752 100755 (executable)
@@ -553,7 +553,7 @@ def boot_image(vim=None, image_name=None, vm_name=None):
                 return None
 
         print (" Booting {} image id {} ".format(vm_name, vim_catalog))
-        vm_uuid = vim.new_vminstance(name=vm_name, image_id=vim_catalog)
+        vm_uuid, _ = vim.new_vminstance(name=vm_name, image_id=vim_catalog)
         if vm_uuid is not None and validate_uuid4(vm_uuid):
             print("Image booted and vm uuid {}".format(vm_uuid))
             vapp_dict = vim.get_vapp(vdc_name=namespace.vcdvdc, vapp_name=vm_uuid, isuuid=True)
index 0e7a5e6..2684892 100755 (executable)
@@ -1005,7 +1005,7 @@ class test_vimconn_new_vminstance(test_base):
 
         net_list = [{'use': self.__class__.net_type, 'name': name, 'floating_ip': False, 'vpci': vpci, 'port_security': True, 'type': 'virtual', 'net_id': self.__class__.network_id}]
 
-        self.__class__.instance_id = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id, flavor_id=flavor_id, net_list=net_list)
+        self.__class__.instance_id, _ = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id, flavor_id=flavor_id, net_list=net_list)
 
         self.assertEqual(type(self.__class__.instance_id),str)
 
@@ -1024,7 +1024,7 @@ class test_vimconn_new_vminstance(test_base):
 
         net_list = [{'use': self.__class__.net_type, 'name': name, 'floating_ip': False, 'port_security': True, 'model': model_name, 'type': 'virtual', 'net_id': self.__class__.network_id}]
 
-        instance_id = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id,
+        instance_id, _ = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id,
                                                                                            flavor_id=flavor_id,
                                                                                              net_list=net_list)
         self.assertEqual(type(instance_id),str)
@@ -1048,7 +1048,7 @@ class test_vimconn_new_vminstance(test_base):
 
         net_list = [{'use': net_use, 'name': name, 'floating_ip': False, 'port_security': True, 'type': 'virtual', 'net_id': self.__class__.network_id}]
 
-        instance_id = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id,
+        instance_id, _ = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id,
                                                                                            flavor_id=flavor_id,
                                                                                              net_list=net_list)
         self.assertEqual(type(instance_id),str)
@@ -1072,7 +1072,7 @@ class test_vimconn_new_vminstance(test_base):
 
         net_list = [{'use': self.__class__.net_type, 'name': name, 'floating_ip': False, 'port_security': True, 'type': _type, 'net_id': self.__class__.network_id}]
 
-        instance_id = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id,
+        instance_id, _ = test_config["vim_conn"].new_vminstance(name='Test1_vm', image_id=self.__class__.image_id,
                                                                                            flavor_id=flavor_id,
                                                                                              net_list=net_list)
         self.assertEqual(type(instance_id),str)
@@ -1102,7 +1102,7 @@ class test_vimconn_new_vminstance(test_base):
 
         net_list = [{'use': self.__class__.net_type, 'name': name, 'floating_ip': False, 'port_security': True, 'type': 'virtual', 'net_id': self.__class__.network_id}]
 
-        instance_id = test_config["vim_conn"].new_vminstance(name='Cloud_vm', image_id=self.__class__.image_id,
+        instance_id, _ = test_config["vim_conn"].new_vminstance(name='Cloud_vm', image_id=self.__class__.image_id,
                                                                                            flavor_id=flavor_id,
                                                                                              net_list=net_list,
                                                                                        cloud_config=cloud_data)
@@ -1128,7 +1128,7 @@ class test_vimconn_new_vminstance(test_base):
 
         net_list = [{'use': self.__class__.net_type, 'name': name, 'floating_ip': False, 'port_security': True, 'type': 'virtual', 'net_id': self.__class__.network_id}]
 
-        instance_id = test_config["vim_conn"].new_vminstance(name='VM_test1', image_id=self.__class__.image_id,
+        instance_id, _ = test_config["vim_conn"].new_vminstance(name='VM_test1', image_id=self.__class__.image_id,
                                                                                            flavor_id=flavor_id,
                                                                                              net_list=net_list,
                                                                                          disk_list=device_data)