Bug 1169 Interface ID of a created VM is not supplied
[osm/RO.git] / RO-VIM-azure / osm_rovim_azure / vimconn_azure.py
index daf8463..3752f29 100755 (executable)
@@ -14,7 +14,7 @@
 ##
 
 import base64
 ##
 
 import base64
-from osm_ro import vimconn
+from osm_ro_plugin import vimconn
 import logging
 import netaddr
 import re
 import logging
 import netaddr
 import re
@@ -27,6 +27,7 @@ from azure.mgmt.compute import ComputeManagementClient
 from azure.mgmt.compute.models import DiskCreateOption
 from msrestazure.azure_exceptions import CloudError
 from msrest.exceptions import AuthenticationError
 from azure.mgmt.compute.models import DiskCreateOption
 from msrestazure.azure_exceptions import CloudError
 from msrest.exceptions import AuthenticationError
+import msrestazure.tools as azure_tools
 from requests.exceptions import ConnectionError
 
 __author__ = 'Isabel Lloret, Sergio Gonzalez, Alfonso Tierno'
 from requests.exceptions import ConnectionError
 
 __author__ = 'Isabel Lloret, Sergio Gonzalez, Alfonso Tierno'
@@ -40,7 +41,7 @@ if getenv('OSMRO_PDB_DEBUG'):
     pdb.set_trace()
 
 
     pdb.set_trace()
 
 
-class vimconnector(vimconn.vimconnector):
+class vimconnector(vimconn.VimConnector):
 
     # Translate azure provisioning state to OSM provision state
     # The first three ones are the transitional status once a user initiated action has been requested
 
     # Translate azure provisioning state to OSM provision state
     # The first three ones are the transitional status once a user initiated action has been requested
@@ -65,6 +66,8 @@ class vimconnector(vimconn.vimconnector):
         "deallocating": "BUILD"
     }
 
         "deallocating": "BUILD"
     }
 
+    AZURE_ZONES = ["1", "2", "3"]
+
     def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level=None,
                  config={}, persistent_info={}):
         """
     def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level=None,
                  config={}, persistent_info={}):
         """
@@ -82,7 +85,7 @@ class vimconnector(vimconn.vimconnector):
             "^Standard_B" will select a serie B maybe for test environment
         """
 
             "^Standard_B" will select a serie B maybe for test environment
         """
 
-        vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level,
+        vimconn.VimConnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level,
                                       config, persistent_info)
 
         # Variable that indicates if client must be reloaded or initialized
                                       config, persistent_info)
 
         # Variable that indicates if client must be reloaded or initialized
@@ -109,19 +112,19 @@ class vimconnector(vimconn.vimconnector):
             self._config["subscription_id"] = config.get('subscription_id')
             # self.logger.debug('Setting subscription to: %s', self.config["subscription_id"])
         else:
             self._config["subscription_id"] = config.get('subscription_id')
             # self.logger.debug('Setting subscription to: %s', self.config["subscription_id"])
         else:
-            raise vimconn.vimconnException('Subscription not specified')
+            raise vimconn.VimConnException('Subscription not specified')
 
         # REGION
         if 'region_name' in config:
             self.region = config.get('region_name')
         else:
 
         # REGION
         if 'region_name' in config:
             self.region = config.get('region_name')
         else:
-            raise vimconn.vimconnException('Azure region_name is not specified at config')
+            raise vimconn.VimConnException('Azure region_name is not specified at config')
 
         # RESOURCE_GROUP
         if 'resource_group' in config:
             self.resource_group = config.get('resource_group')
         else:
 
         # RESOURCE_GROUP
         if 'resource_group' in config:
             self.resource_group = config.get('resource_group')
         else:
-            raise vimconn.vimconnException('Azure resource_group is not specified at config')
+            raise vimconn.VimConnException('Azure resource_group is not specified at config')
 
         # VNET_NAME
         if 'vnet_name' in config:
 
         # VNET_NAME
         if 'vnet_name' in config:
@@ -165,23 +168,23 @@ class vimconnector(vimconn.vimconnector):
             resource = str(resource_id.split('/')[-1])
             return resource
         except Exception as e:
             resource = str(resource_id.split('/')[-1])
             return resource
         except Exception as e:
-            raise vimconn.vimconnException("Unable to get resource name from resource_id '{}' Error: '{}'".
+            raise vimconn.VimConnException("Unable to get resource name from resource_id '{}' Error: '{}'".
                                            format(resource_id, e))
 
     def _get_location_from_resource_group(self, resource_group_name):
         try:
             location = self.conn.resource_groups.get(resource_group_name).location
             return location
                                            format(resource_id, e))
 
     def _get_location_from_resource_group(self, resource_group_name):
         try:
             location = self.conn.resource_groups.get(resource_group_name).location
             return location
-        except Exception as e:
-            raise vimconn.vimconnNotFoundException("Location '{}' not found".format(resource_group_name))
+        except Exception:
+            raise vimconn.VimConnNotFoundException("Location '{}' not found".format(resource_group_name))
 
     def _get_resource_group_name_from_resource_id(self, resource_id):
 
         try:
             rg = str(resource_id.split('/')[4])
             return rg
 
     def _get_resource_group_name_from_resource_id(self, resource_id):
 
         try:
             rg = str(resource_id.split('/')[4])
             return rg
-        except Exception as e:
-            raise vimconn.vimconnException("Unable to get resource group from invalid resource_id format '{}'".
+        except Exception:
+            raise vimconn.VimConnException("Unable to get resource group from invalid resource_id format '{}'".
                                            format(resource_id))
 
     def _get_net_name_from_resource_id(self, resource_id):
                                            format(resource_id))
 
     def _get_net_name_from_resource_id(self, resource_id):
@@ -189,8 +192,8 @@ class vimconnector(vimconn.vimconnector):
         try:
             net_name = str(resource_id.split('/')[8])
             return net_name
         try:
             net_name = str(resource_id.split('/')[8])
             return net_name
-        except Exception as e:
-            raise vimconn.vimconnException("Unable to get azure net_name from invalid resource_id format '{}'".
+        except Exception:
+            raise vimconn.VimConnException("Unable to get azure net_name from invalid resource_id format '{}'".
                                            format(resource_id))
 
     def _check_subnets_for_vm(self, net_list):
                                            format(resource_id))
 
     def _check_subnets_for_vm(self, net_list):
@@ -205,16 +208,16 @@ class vimconnector(vimconn.vimconnector):
         """
         Transforms a generic or azure exception to a vimcommException
         """
         """
         Transforms a generic or azure exception to a vimcommException
         """
-        if isinstance(e, vimconn.vimconnException):
+        if isinstance(e, vimconn.VimConnException):
             raise
         elif isinstance(e, AuthenticationError):
             raise
         elif isinstance(e, AuthenticationError):
-            raise vimconn.vimconnAuthException(type(e).__name__ + ': ' + str(e))
+            raise vimconn.VimConnAuthException(type(e).__name__ + ': ' + str(e))
         elif isinstance(e, ConnectionError):
         elif isinstance(e, ConnectionError):
-            raise vimconn.vimconnConnectionException(type(e).__name__ + ': ' + str(e))
+            raise vimconn.VimConnConnectionException(type(e).__name__ + ': ' + str(e))
         else:
             # In case of generic error recreate client
             self.reload_client = True
         else:
             # In case of generic error recreate client
             self.reload_client = True
-            raise vimconn.vimconnException(type(e).__name__ + ': ' + str(e))
+            raise vimconn.VimConnException(type(e).__name__ + ': ' + str(e))
 
     def _check_or_create_resource_group(self):
         """
 
     def _check_or_create_resource_group(self):
         """
@@ -311,7 +314,7 @@ class vimconnector(vimconn.vimconnector):
                     self.logger.debug('dinamically obtained ip_profile: %s', ip_range)
                     break
             else:
                     self.logger.debug('dinamically obtained ip_profile: %s', ip_range)
                     break
             else:
-                raise vimconn.vimconnException("Cannot find a non-used subnet range in {}".
+                raise vimconn.VimConnException("Cannot find a non-used subnet range in {}".
                                                format(self.vnet_address_space))
         else:
             ip_profile = {"subnet_address": ip_profile['subnet_address']}
                                                format(self.vnet_address_space))
         else:
             ip_profile = {"subnet_address": ip_profile['subnet_address']}
@@ -353,7 +356,7 @@ class vimconnector(vimconn.vimconnector):
             name = subnet_name + "-" + str(name_suffix)
         return name
 
             name = subnet_name + "-" + str(name_suffix)
         return name
 
-    def _create_nic(self, net, nic_name, static_ip=None):
+    def _create_nic(self, net, nic_name, static_ip=None, created_items={}):
 
         self.logger.debug('create nic name %s, net_name %s', nic_name, net)
         self._reload_connection()
 
         self.logger.debug('create nic name %s, net_name %s', nic_name, net)
         self._reload_connection()
@@ -373,7 +376,8 @@ class vimconnector(vimconn.vimconnector):
 
             async_nic_creation = self.conn_vnet.network_interfaces.create_or_update(self.resource_group, nic_name,
                                                                                     net_ifz)
 
             async_nic_creation = self.conn_vnet.network_interfaces.create_or_update(self.resource_group, nic_name,
                                                                                     net_ifz)
-            async_nic_creation.wait()
+            nic_data = async_nic_creation.result()
+            created_items[nic_data.id] = True
             self.logger.debug('created nic name %s', nic_name)
 
             public_ip = net.get('floating_ip')
             self.logger.debug('created nic name %s', nic_name)
 
             public_ip = net.get('floating_ip')
@@ -383,19 +387,21 @@ class vimconnector(vimconn.vimconnector):
                     'public_ip_allocation_method': 'Dynamic'
                 }
                 public_ip_name = nic_name + '-public-ip'
                     'public_ip_allocation_method': 'Dynamic'
                 }
                 public_ip_name = nic_name + '-public-ip'
-                public_ip = self.conn_vnet.public_ip_addresses.create_or_update(
+                async_public_ip = self.conn_vnet.public_ip_addresses.create_or_update(
                     self.resource_group,
                     public_ip_name,
                     public_ip_address_params
                 )
                     self.resource_group,
                     public_ip_name,
                     public_ip_address_params
                 )
-                self.logger.debug('created public IP: {}'.format(public_ip.result()))
+                public_ip = async_public_ip.result()
+                self.logger.debug('created public IP: {}'.format(public_ip))
 
                 # Associate NIC to Public IP
                 nic_data = self.conn_vnet.network_interfaces.get(
                     self.resource_group,
                     nic_name)
 
 
                 # Associate NIC to Public IP
                 nic_data = self.conn_vnet.network_interfaces.get(
                     self.resource_group,
                     nic_name)
 
-                nic_data.ip_configurations[0].public_ip_address = public_ip.result()
+                nic_data.ip_configurations[0].public_ip_address = public_ip
+                created_items[public_ip.id] = True
 
                 self.conn_vnet.network_interfaces.create_or_update(
                     self.resource_group,
 
                 self.conn_vnet.network_interfaces.create_or_update(
                     self.resource_group,
@@ -405,31 +411,31 @@ class vimconnector(vimconn.vimconnector):
         except Exception as e:
             self._format_vimconn_exception(e)
 
         except Exception as e:
             self._format_vimconn_exception(e)
 
-        return async_nic_creation.result()
+        return nic_data, created_items
 
     def new_flavor(self, flavor_data):
         """
         It is not allowed to create new flavors in Azure, must always use an existing one
         """
 
     def new_flavor(self, flavor_data):
         """
         It is not allowed to create new flavors in Azure, must always use an existing one
         """
-        raise vimconn.vimconnAuthException("It is not possible to create new flavors in AZURE")
+        raise vimconn.VimConnAuthException("It is not possible to create new flavors in AZURE")
 
     def new_tenant(self, tenant_name, tenant_description):
         """
         It is not allowed to create new tenants in azure
         """
 
     def new_tenant(self, tenant_name, tenant_description):
         """
         It is not allowed to create new tenants in azure
         """
-        raise vimconn.vimconnAuthException("It is not possible to create a TENANT in AZURE")
+        raise vimconn.VimConnAuthException("It is not possible to create a TENANT in AZURE")
 
     def new_image(self, image_dict):
         """
         It is not allowed to create new images in Azure, must always use an existing one
         """
 
     def new_image(self, image_dict):
         """
         It is not allowed to create new images in Azure, must always use an existing one
         """
-        raise vimconn.vimconnAuthException("It is not possible to create new images in AZURE")
+        raise vimconn.VimConnAuthException("It is not possible to create new images in AZURE")
 
     def get_image_id_from_path(self, path):
         """Get the image id from image path in the VIM database.
            Returns the image_id or raises a vimconnNotFoundException
         """
 
     def get_image_id_from_path(self, path):
         """Get the image id from image path in the VIM database.
            Returns the image_id or raises a vimconnNotFoundException
         """
-        raise vimconn.vimconnAuthException("It is not possible to obtain image from path in AZURE")
+        raise vimconn.VimConnAuthException("It is not possible to obtain image from path in AZURE")
 
     def get_image_list(self, filter_dict={}):
         """Obtain tenant images from VIM
 
     def get_image_list(self, filter_dict={}):
         """Obtain tenant images from VIM
@@ -467,10 +473,10 @@ class vimconnector(vimconn.vimconnector):
                             else:
                                 image_list = self._get_sku_image_list(publisher, offer, sku)
                 else:
                             else:
                                 image_list = self._get_sku_image_list(publisher, offer, sku)
                 else:
-                    raise vimconn.vimconnAuthException(
+                    raise vimconn.VimConnAuthException(
                         "List images in Azure must include name param with at least publisher")
             else:
                         "List images in Azure must include name param with at least publisher")
             else:
-                raise vimconn.vimconnAuthException("List images in Azure must include name param with at"
+                raise vimconn.VimConnAuthException("List images in Azure must include name param with at"
                                                    " least publisher")
 
             return image_list
                                                    " least publisher")
 
             return image_list
@@ -603,23 +609,26 @@ class vimconnector(vimconn.vimconnector):
 
         # At least one network must be provided
         if not net_list:
 
         # At least one network must be provided
         if not net_list:
-            raise vimconn.vimconnException("At least one net must be provided to create a new VM")
+            raise vimconn.VimConnException("At least one net must be provided to create a new VM")
 
         # image_id are several fields of the image_id
         image_reference = self._get_image_reference(image_id)
 
 
         # image_id are several fields of the image_id
         image_reference = self._get_image_reference(image_id)
 
-        self._check_subnets_for_vm(net_list)
-        vm_nics = []
-        for idx, net in enumerate(net_list):
-            # Fault with subnet_id
-            # subnet_id=net['subnet_id']
-            # subnet_id=net['net_id']
-            nic_name = vm_name + '-nic-'+str(idx)
-            vm_nic = self._create_nic(net, nic_name, net.get('ip_address'))
-            vm_nics.append({'id': str(vm_nic.id)})
-            net['vim_id'] = vm_nic.id
-
         try:
         try:
+            virtual_machine = None
+            created_items = {}
+
+            # Create nics for each subnet
+            self._check_subnets_for_vm(net_list)
+            vm_nics = []
+            for idx, net in enumerate(net_list):
+                # Fault with subnet_id
+                # subnet_id=net['subnet_id']
+                # subnet_id=net['net_id']
+                nic_name = vm_name + '-nic-' + str(idx)
+                vm_nic, nic_items = self._create_nic(net, nic_name, net.get('ip_address'), created_items)
+                vm_nics.append({'id': str(vm_nic.id)})
+                net['vim_id'] = vm_nic.id
 
             # cloud-init configuration
             # cloud config
 
             # cloud-init configuration
             # cloud config
@@ -668,33 +677,6 @@ class vimconnector(vimconn.vimconnector):
                 }
             }
 
                 }
             }
 
-            # Add data disks if they are provided
-            if disk_list:
-                data_disks = []
-                for lun_name, disk in enumerate(disk_list):
-                    self.logger.debug("add disk size: %s, image: %s", disk.get("size"), disk.get("image_id"))
-                    if not disk.get("image_id"):
-                        data_disks.append({
-                            'lun': lun_name,  # You choose the value, depending of what is available for you
-                            'name': vm_name + "_data_disk-" + str(lun_name),
-                            'create_option': DiskCreateOption.empty,
-                            'disk_size_gb': disk.get("size")
-                        })
-                    else:
-                        # self.logger.debug("currently not able to create data disks from image for azure, ignoring")
-                        data_disks.append({
-                            'lun': lun_name,  # You choose the value, depending of what is available for you
-                            'name': vm_name + "_data_disk-" + str(lun_name),
-                            'create_option': 'Attach',
-                            'disk_size_gb': disk.get("size"),
-                            'managed_disk': {
-                                'id': disk.get("image_id")
-                            }
-                        })
-
-                if data_disks:
-                    vm_parameters["storage_profile"]["data_disks"] = data_disks
-
             # If the machine has several networks one must be marked as primary
             # As it is not indicated in the interface the first interface will be marked as primary
             if len(vm_nics) > 1:
             # If the machine has several networks one must be marked as primary
             # As it is not indicated in the interface the first interface will be marked as primary
             if len(vm_nics) > 1:
@@ -706,23 +688,33 @@ class vimconnector(vimconn.vimconnector):
 
             vm_parameters['network_profile'] = {'network_interfaces': vm_nics}
 
 
             vm_parameters['network_profile'] = {'network_interfaces': vm_nics}
 
+            # Obtain zone information
+            vm_zone = self._get_vm_zone(availability_zone_index, availability_zone_list)
+            if vm_zone:
+                vm_parameters['zones'] = [vm_zone]
+
             self.logger.debug("create vm name: %s", vm_name)
             creation_result = self.conn_compute.virtual_machines.create_or_update(
                 self.resource_group, 
                 vm_name,
                 vm_parameters
             )
             self.logger.debug("create vm name: %s", vm_name)
             creation_result = self.conn_compute.virtual_machines.create_or_update(
                 self.resource_group, 
                 vm_name,
                 vm_parameters
             )
-            # creation_result.wait()
-            result = creation_result.result()
+            virtual_machine = creation_result.result()
             self.logger.debug("created vm name: %s", vm_name)
 
             self.logger.debug("created vm name: %s", vm_name)
 
+            # Add disks if they are provided
+            if disk_list:
+                for disk_index, disk in enumerate(disk_list):
+                    self.logger.debug("add disk size: %s, image: %s", disk.get("size"), disk.get("image"))
+                    self._add_newvm_disk(virtual_machine, vm_name, disk_index, disk, created_items)
+
             if start:
                 self.conn_compute.virtual_machines.start(
                     self.resource_group,
                     vm_name)
             # start_result.wait()
 
             if start:
                 self.conn_compute.virtual_machines.start(
                     self.resource_group,
                     vm_name)
             # start_result.wait()
 
-            return result.id, None
+            return virtual_machine.id, created_items
             
             # run_command_parameters = {
             #     'command_id': 'RunShellScript', # For linux, don't change it
             
             # run_command_parameters = {
             #     'command_id': 'RunShellScript', # For linux, don't change it
@@ -731,6 +723,16 @@ class vimconnector(vimconn.vimconnector):
             #     ]
             # }
         except Exception as e:
             #     ]
             # }
         except Exception as e:
+            # Rollback vm creacion
+            vm_id = None
+            if virtual_machine:
+                vm_id = virtual_machine.id
+            try:
+                self.logger.debug("exception creating vm try to rollback")
+                self.delete_vminstance(vm_id, created_items)
+            except Exception as e2:
+                self.logger.error("new_vminstance rollback fail {}".format(e2))
+
             self.logger.debug('Exception creating new vminstance: %s', e, exc_info=True)
             self._format_vimconn_exception(e)
 
             self.logger.debug('Exception creating new vminstance: %s', e, exc_info=True)
             self._format_vimconn_exception(e)
 
@@ -753,6 +755,109 @@ class vimconnector(vimconn.vimconnector):
             name = vm_name + "-" + str(name_suffix)
         return name
 
             name = vm_name + "-" + str(name_suffix)
         return name
 
+    def _get_vm_zone(self, availability_zone_index, availability_zone_list):
+
+        if availability_zone_index is None:
+            return None
+
+        vim_availability_zones = self._get_azure_availability_zones()
+        # check if VIM offer enough availability zones describe in the VNFD
+        if vim_availability_zones and len(availability_zone_list) <= len(vim_availability_zones):
+            # check if all the names of NFV AV match VIM AV names
+            match_by_index = False
+            if not availability_zone_list:
+                match_by_index = True
+            else:
+                for av in availability_zone_list:
+                    if av not in vim_availability_zones:
+                        match_by_index = True
+                        break
+            if match_by_index:
+                return vim_availability_zones[availability_zone_index]
+            else:
+                return availability_zone_list[availability_zone_index]
+        else:
+            raise vimconn.VimConnConflictException("No enough availability zones at VIM for this deployment")
+
+    def _get_azure_availability_zones(self):
+        return self.AZURE_ZONES
+
+    def _add_newvm_disk(self, virtual_machine, vm_name, disk_index, disk, created_items={}):
+
+        disk_name = None
+        data_disk = None
+
+        # Check if must create empty disk or from image
+        if disk.get('vim_id'):
+            # disk already exists, just get
+            parsed_id = azure_tools.parse_resource_id(disk.get('vim_id'))
+            disk_name = parsed_id.get("name")
+            data_disk = self.conn_compute.disks.get(self.resource_group, disk_name)
+        else:
+            disk_name = vm_name + "_DataDisk_" + str(disk_index)
+            if not disk.get("image_id"):
+                self.logger.debug("create new data disk name: %s", disk_name)
+                async_disk_creation = self.conn_compute.disks.create_or_update(
+                    self.resource_group,
+                    disk_name,
+                    {
+                        'location': self.region,
+                        'disk_size_gb': disk.get("size"),
+                        'creation_data': {
+                            'create_option': DiskCreateOption.empty
+                        }
+                    }
+                )
+                data_disk = async_disk_creation.result()
+                created_items[data_disk.id] = True
+            else:
+                image_id = disk.get("image_id")
+                if azure_tools.is_valid_resource_id(image_id):
+                    parsed_id = azure_tools.parse_resource_id(image_id)
+
+                    # Check if image is snapshot or disk
+                    image_name = parsed_id.get("name")
+                    type = parsed_id.get("resource_type")
+                    if type == 'snapshots' or type == 'disks':
+
+                        self.logger.debug("create disk from copy name: %s", image_name)
+                        # Â¿Should check that snapshot exists?
+                        async_disk_creation = self.conn_compute.disks.create_or_update(
+                            self.resource_group,
+                            disk_name,
+                            {
+                                'location': self.region,
+                                'creation_data': {
+                                    'create_option': 'Copy',
+                                    'source_uri': image_id
+                                }
+                            }
+                        )
+                        data_disk = async_disk_creation.result()
+                        created_items[data_disk.id] = True
+
+                    else:
+                        raise vimconn.VimConnNotFoundException("Invalid image_id: %s ", image_id)
+                else:
+                    raise vimconn.VimConnNotFoundException("Invalid image_id: %s ", image_id)
+
+        # Attach the disk created
+        virtual_machine.storage_profile.data_disks.append({
+            'lun': disk_index,
+            'name': disk_name,
+            'create_option': DiskCreateOption.attach,
+            'managed_disk': {
+                'id': data_disk.id
+            },
+            'disk_size_gb': disk.get('size')
+        })
+        self.logger.debug("attach disk name: %s", disk_name)
+        self.conn_compute.virtual_machines.create_or_update(
+            self.resource_group,
+            virtual_machine.name,
+            virtual_machine
+        )
+
     # It is necesary extract from image_id data to create the VM with this format
     #        'image_reference': {
     #           'publisher': vm_reference['publisher'],
     # It is necesary extract from image_id data to create the VM with this format
     #        'image_reference': {
     #           'publisher': vm_reference['publisher'],
@@ -780,8 +885,8 @@ class vimconnector(vimconn.vimconnector):
                 'sku': sku,
                 'version': version
             }
                 'sku': sku,
                 'version': version
             }
-        except Exception as e:
-            raise vimconn.vimconnException(
+        except Exception:
+            raise vimconn.VimConnException(
                 "Unable to get image_reference from invalid image_id format: '{}'".format(image_id))
 
     # Azure VM names can not have some special characters
                 "Unable to get image_reference from invalid image_id format: '{}'".format(image_id))
 
     # Azure VM names can not have some special characters
@@ -832,7 +937,7 @@ class vimconnector(vimconn.vimconnector):
 
             if listedFilteredSizes:
                 return listedFilteredSizes[0]['name']
 
             if listedFilteredSizes:
                 return listedFilteredSizes[0]['name']
-            raise vimconn.vimconnNotFoundException("Cannot find any flavor matching '{}'".format(str(flavor_dict)))
+            raise vimconn.VimConnNotFoundException("Cannot find any flavor matching '{}'".format(str(flavor_dict)))
 
         except Exception as e:
             self._format_vimconn_exception(e)
 
         except Exception as e:
             self._format_vimconn_exception(e)
@@ -861,7 +966,7 @@ class vimconnector(vimconn.vimconnector):
             self._reload_connection()
             return True
         except Exception as e:
             self._reload_connection()
             return True
         except Exception as e:
-            raise vimconn.vimconnException("Connectivity issue with Azure API: {}".format(e))
+            raise vimconn.VimConnException("Connectivity issue with Azure API: {}".format(e))
 
     def get_network(self, net_id):
 
 
     def get_network(self, net_id):
 
@@ -873,7 +978,7 @@ class vimconnector(vimconn.vimconnector):
         network_list = self.get_network_list(filter_dict)
 
         if not network_list:
         network_list = self.get_network_list(filter_dict)
 
         if not network_list:
-            raise vimconn.vimconnNotFoundException("network '{}' not found".format(net_id))
+            raise vimconn.VimConnNotFoundException("network '{}' not found".format(net_id))
         else:
             return network_list[0]
 
         else:
             return network_list[0]
 
@@ -886,7 +991,7 @@ class vimconnector(vimconn.vimconnector):
         filter_dict = {'name': res_name}
         network_list = self.get_network_list(filter_dict)
         if not network_list:
         filter_dict = {'name': res_name}
         network_list = self.get_network_list(filter_dict)
         if not network_list:
-            raise vimconn.vimconnNotFoundException("network '{}' not found".format(net_id))
+            raise vimconn.VimConnNotFoundException("network '{}' not found".format(net_id))
 
         try:
             # Subnet API fails (CloudError: Azure Error: ResourceNotFound)
 
         try:
             # Subnet API fails (CloudError: Azure Error: ResourceNotFound)
@@ -897,7 +1002,7 @@ class vimconnector(vimconn.vimconnector):
 
         except CloudError as e:
             if e.error.error and "notfound" in e.error.error.lower():
 
         except CloudError as e:
             if e.error.error and "notfound" in e.error.error.lower():
-                raise vimconn.vimconnNotFoundException("network '{}' not found".format(net_id))
+                raise vimconn.VimConnNotFoundException("network '{}' not found".format(net_id))
             else:
                 self._format_vimconn_exception(e)
         except Exception as e:
             else:
                 self._format_vimconn_exception(e)
         except Exception as e:
@@ -909,69 +1014,142 @@ class vimconnector(vimconn.vimconnector):
         self.logger.debug('deleting VM instance {} - {}'.format(self.resource_group, vm_id))
         self._reload_connection()
 
         self.logger.debug('deleting VM instance {} - {}'.format(self.resource_group, vm_id))
         self._reload_connection()
 
+        created_items = created_items or {}
         try:
         try:
+            # Check vm exists, we can call delete_vm to clean created_items
+            if vm_id:
+                res_name = self._get_resource_name_from_resource_id(vm_id)
+                vm = self.conn_compute.virtual_machines.get(self.resource_group, res_name)
 
 
-            res_name = self._get_resource_name_from_resource_id(vm_id)
-            vm = self.conn_compute.virtual_machines.get(self.resource_group, res_name)
-
-            # Shuts down the virtual machine and releases the compute resources
-            # vm_stop = self.conn_compute.virtual_machines.power_off(self.resource_group, resName)
-            # vm_stop.wait()
-
-            vm_delete = self.conn_compute.virtual_machines.delete(self.resource_group, res_name)
-            vm_delete.wait()
-            self.logger.debug('deleted VM name: %s', res_name)
+                # Shuts down the virtual machine and releases the compute resources
+                # vm_stop = self.conn_compute.virtual_machines.power_off(self.resource_group, resName)
+                # vm_stop.wait()
+
+                vm_delete = self.conn_compute.virtual_machines.delete(self.resource_group, res_name)
+                vm_delete.wait()
+                self.logger.debug('deleted VM name: %s', res_name)
+
+                # Delete OS Disk
+                os_disk_name = vm.storage_profile.os_disk.name
+                self.logger.debug('delete OS DISK: %s', os_disk_name)
+                async_disk_delete = self.conn_compute.disks.delete(self.resource_group, os_disk_name)
+                async_disk_delete.wait()
+                # os disks are created always with the machine
+                self.logger.debug('deleted OS DISK name: %s', os_disk_name)
+
+                for data_disk in vm.storage_profile.data_disks:
+                    self.logger.debug('delete data_disk: %s', data_disk.name)
+                    async_disk_delete = self.conn_compute.disks.delete(self.resource_group, data_disk.name)
+                    async_disk_delete.wait()
+                    self._markdel_created_item(data_disk.managed_disk.id, created_items)
+                    self.logger.debug('deleted OS DISK name: %s', data_disk.name)
+
+                # After deleting VM, it is necessary to delete NIC, because if is not deleted delete_network
+                # does not work because Azure says that is in use the subnet
+                network_interfaces = vm.network_profile.network_interfaces
 
 
-            # Delete OS Disk
-            os_disk_name = vm.storage_profile.os_disk.name
-            self.logger.debug('delete OS DISK: %s', os_disk_name)
-            self.conn_compute.disks.delete(self.resource_group, os_disk_name)
-            self.logger.debug('deleted OS DISK name: %s', os_disk_name)
+                for network_interface in network_interfaces:
 
 
-            for data_disk in vm.storage_profile.data_disks:
-                self.logger.debug('delete data_disk: %s', data_disk.name)
-                self.conn_compute.disks.delete(self.resource_group, data_disk.name)
-                self.logger.debug('deleted OS DISK name: %s', data_disk.name)
+                    nic_name = self._get_resource_name_from_resource_id(network_interface.id)
+                    nic_data = self.conn_vnet.network_interfaces.get(
+                        self.resource_group,
+                        nic_name)
 
 
-            # After deleting VM, it is necessary to delete NIC, because if is not deleted delete_network
-            # does not work because Azure says that is in use the subnet
-            network_interfaces = vm.network_profile.network_interfaces
+                    public_ip_name = None
+                    exist_public_ip = nic_data.ip_configurations[0].public_ip_address
+                    if exist_public_ip:
+                        public_ip_id = nic_data.ip_configurations[0].public_ip_address.id
 
 
-            for network_interface in network_interfaces:
+                        # Delete public_ip
+                        public_ip_name = self._get_resource_name_from_resource_id(public_ip_id)
 
 
-                nic_name = self._get_resource_name_from_resource_id(network_interface.id)
-                nic_data = self.conn_vnet.network_interfaces.get(
-                    self.resource_group,
-                    nic_name)
+                        # Public ip must be deleted afterwards of nic that is attached
 
 
-                public_ip_name = None
-                exist_public_ip = nic_data.ip_configurations[0].public_ip_address
-                if exist_public_ip:
-                    public_ip_id = nic_data.ip_configurations[0].public_ip_address.id
+                    self.logger.debug('delete NIC name: %s', nic_name)
+                    nic_delete = self.conn_vnet.network_interfaces.delete(self.resource_group, nic_name)
+                    nic_delete.wait()
+                    self._markdel_created_item(network_interface.id, created_items)
+                    self.logger.debug('deleted NIC name: %s', nic_name)
 
 
-                    # Delete public_ip
-                    public_ip_name = self._get_resource_name_from_resource_id(public_ip_id)
+                    # Delete list of public ips
+                    if public_ip_name:
+                        self.logger.debug('delete PUBLIC IP - ' + public_ip_name)
+                        ip_delete = self.conn_vnet.public_ip_addresses.delete(self.resource_group, public_ip_name)
+                        ip_delete.wait()
+                        self._markdel_created_item(public_ip_id, created_items)
 
 
-                    # Public ip must be deleted afterwards of nic that is attached
-
-                self.logger.debug('delete NIC name: %s', nic_name)
-                nic_delete = self.conn_vnet.network_interfaces.delete(self.resource_group, nic_name)
-                nic_delete.wait()
-                self.logger.debug('deleted NIC name: %s', nic_name)
-
-                # Delete list of public ips
-                if public_ip_name:
-                    self.logger.debug('delete PUBLIC IP - ' + public_ip_name)
-                    self.conn_vnet.public_ip_addresses.delete(self.resource_group, public_ip_name)
+            # Delete created items
+            self._delete_created_items(created_items)
 
         except CloudError as e:
             if e.error.error and "notfound" in e.error.error.lower():
 
         except CloudError as e:
             if e.error.error and "notfound" in e.error.error.lower():
-                raise vimconn.vimconnNotFoundException("No vm instance found '{}'".format(vm_id))
+                raise vimconn.VimConnNotFoundException("No vm instance found '{}'".format(vm_id))
             else:
                 self._format_vimconn_exception(e)
         except Exception as e:
             self._format_vimconn_exception(e)
 
             else:
                 self._format_vimconn_exception(e)
         except Exception as e:
             self._format_vimconn_exception(e)
 
+    def _markdel_created_item(self, item_id, created_items):
+        if item_id in created_items:
+            created_items[item_id] = False
+
+    def _delete_created_items(self, created_items):
+        """ Delete created_items elements that have not been deleted with the virtual machine
+            Created_items may not be deleted correctly with the created machine if the
+            virtual machine fails creating or in other cases of error
+        """
+        self.logger.debug("Created items: %s", created_items)
+        # Must delete in order first nics, then public_ips
+        # As dictionaries don't preserve order, first get items to be deleted then delete them
+        nics_to_delete = []
+        publics_ip_to_delete = []
+        disks_to_delete = []
+        for item_id, v in created_items.items():
+            if not v:  # skip already deleted
+                continue
+
+            # self.logger.debug("Must delete item id: %s", item_id)
+
+            # Obtain type, supported nic, disk or public ip
+            parsed_id = azure_tools.parse_resource_id(item_id)
+            resource_type = parsed_id.get("resource_type")
+            name = parsed_id.get("name")
+
+            if resource_type == "networkInterfaces":
+                nics_to_delete.append(name)
+            elif resource_type == "publicIPAddresses":
+                publics_ip_to_delete.append(name)
+            elif resource_type == "disks":
+                disks_to_delete.append(name)
+
+        # Now delete
+        for item_name in nics_to_delete:
+            try:
+                self.logger.debug("deleting nic name %s:", item_name)
+                nic_delete = self.conn_vnet.network_interfaces.delete(self.resource_group, item_name)
+                nic_delete.wait()
+                self.logger.debug("deleted nic name %s:", item_name)
+            except Exception as e:
+                self.logger.error("Error deleting item: {}: {}".format(type(e).__name__, e))
+
+        for item_name in publics_ip_to_delete:
+            try:
+                self.logger.debug("deleting public ip name %s:", item_name)
+                ip_delete = self.conn_vnet.public_ip_addresses.delete(self.resource_group, name)
+                ip_delete.wait()
+                self.logger.debug("deleted public ip name %s:", item_name)
+            except Exception as e:
+                self.logger.error("Error deleting item: {}: {}".format(type(e).__name__, e))
+
+        for item_name in disks_to_delete:
+            try:
+                self.logger.debug("deleting data disk name %s:", name)
+                async_disk_delete = self.conn_compute.disks.delete(self.resource_group, item_name)
+                async_disk_delete.wait()
+                self.logger.debug("deleted data disk name %s:", name)
+            except Exception as e:
+                self.logger.error("Error deleting item: {}: {}".format(type(e).__name__, e))
+
     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
     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
@@ -992,20 +1170,20 @@ class vimconnector(vimconn.vimconnector):
             return None
         except CloudError as e:
             if e.error.error and "notfound" in e.error.error.lower():
             return None
         except CloudError as e:
             if e.error.error and "notfound" in e.error.error.lower():
-                raise vimconn.vimconnNotFoundException("No vm found '{}'".format(vm_id))
+                raise vimconn.VimConnNotFoundException("No vm found '{}'".format(vm_id))
             else:
                 self._format_vimconn_exception(e)
         except Exception as e:
             self._format_vimconn_exception(e)
 
     def delete_flavor(self, flavor_id):
             else:
                 self._format_vimconn_exception(e)
         except Exception as e:
             self._format_vimconn_exception(e)
 
     def delete_flavor(self, flavor_id):
-        raise vimconn.vimconnAuthException("It is not possible to delete a FLAVOR in AZURE")
+        raise vimconn.VimConnAuthException("It is not possible to delete a FLAVOR in AZURE")
 
     def delete_tenant(self, tenant_id,):
 
     def delete_tenant(self, tenant_id,):
-        raise vimconn.vimconnAuthException("It is not possible to delete a TENANT in AZURE")
+        raise vimconn.VimConnAuthException("It is not possible to delete a TENANT in AZURE")
 
     def delete_image(self, image_id):
 
     def delete_image(self, image_id):
-        raise vimconn.vimconnAuthException("It is not possible to delete a IMAGE in AZURE")
+        raise vimconn.VimConnAuthException("It is not possible to delete a IMAGE in AZURE")
 
     def get_vminstance(self, vm_id):
         """
 
     def get_vminstance(self, vm_id):
         """
@@ -1018,7 +1196,7 @@ class vimconnector(vimconn.vimconnector):
             vm = self.conn_compute.virtual_machines.get(self.resource_group, resName)
         except CloudError as e:
             if e.error.error and "notfound" in e.error.error.lower():
             vm = self.conn_compute.virtual_machines.get(self.resource_group, resName)
         except CloudError as e:
             if e.error.error and "notfound" in e.error.error.lower():
-                raise vimconn.vimconnNotFoundException("No vminstance found '{}'".format(vm_id))
+                raise vimconn.VimConnNotFoundException("No vminstance found '{}'".format(vm_id))
             else:
                 self._format_vimconn_exception(e)
         except Exception as e:
             else:
                 self._format_vimconn_exception(e)
         except Exception as e:
@@ -1043,7 +1221,7 @@ class vimconnector(vimconn.vimconnector):
             }
             return flavor
         else:
             }
             return flavor
         else:
-            raise vimconn.vimconnNotFoundException("flavor '{}' not found".format(flavor_id))
+            raise vimconn.VimConnNotFoundException("flavor '{}' not found".format(flavor_id))
 
     def get_tenant_list(self, filter_dict={}):
         """ Obtains the list of tenants
 
     def get_tenant_list(self, filter_dict={}):
         """ Obtains the list of tenants
@@ -1111,7 +1289,7 @@ class vimconnector(vimconn.vimconnector):
                         "status": "VIM_ERROR",
                         "error_msg": str(e)
                     }
                         "status": "VIM_ERROR",
                         "error_msg": str(e)
                     }
-            except vimconn.vimconnNotFoundException as e:
+            except vimconn.VimConnNotFoundException as e:
                 self.logger.error("VimConnNotFoundException %s when searching subnet", e)
                 out_nets[net_id] = {
                     "status": "DELETED",
                 self.logger.error("VimConnNotFoundException %s when searching subnet", e)
                 out_nets[net_id] = {
                     "status": "DELETED",