X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FRO.git;a=blobdiff_plain;f=RO-VIM-azure%2Fosm_rovim_azure%2Fvimconn_azure.py;h=3752f294cad9184707023655b1c0175f048c405e;hp=daf84631719fc1c13debec2f6341af28bfc77416;hb=981eeacdffc7a18deed85b0c440382be3ac9eb5f;hpb=17587e31d62b42076f347a884b83ccd889edc5a8 diff --git a/RO-VIM-azure/osm_rovim_azure/vimconn_azure.py b/RO-VIM-azure/osm_rovim_azure/vimconn_azure.py index daf84631..3752f294 100755 --- a/RO-VIM-azure/osm_rovim_azure/vimconn_azure.py +++ b/RO-VIM-azure/osm_rovim_azure/vimconn_azure.py @@ -14,7 +14,7 @@ ## import base64 -from osm_ro import vimconn +from osm_ro_plugin import vimconn 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 +import msrestazure.tools as azure_tools from requests.exceptions import ConnectionError __author__ = 'Isabel Lloret, Sergio Gonzalez, Alfonso Tierno' @@ -40,7 +41,7 @@ if getenv('OSMRO_PDB_DEBUG'): 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 @@ -65,6 +66,8 @@ class vimconnector(vimconn.vimconnector): "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={}): """ @@ -82,7 +85,7 @@ class vimconnector(vimconn.vimconnector): "^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 @@ -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: - 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: - 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: - 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: @@ -165,23 +168,23 @@ class vimconnector(vimconn.vimconnector): 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 - 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 - 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): @@ -189,8 +192,8 @@ class vimconnector(vimconn.vimconnector): 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): @@ -205,16 +208,16 @@ class vimconnector(vimconn.vimconnector): """ 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 vimconn.vimconnAuthException(type(e).__name__ + ': ' + str(e)) + raise vimconn.VimConnAuthException(type(e).__name__ + ': ' + str(e)) 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 - raise vimconn.vimconnException(type(e).__name__ + ': ' + str(e)) + raise vimconn.VimConnException(type(e).__name__ + ': ' + str(e)) 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: - 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']} @@ -353,7 +356,7 @@ class vimconnector(vimconn.vimconnector): 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() @@ -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.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') @@ -383,19 +387,21 @@ class vimconnector(vimconn.vimconnector): '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.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) - 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, @@ -405,31 +411,31 @@ class vimconnector(vimconn.vimconnector): 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 """ - 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 """ - 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 """ - 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 """ - 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 @@ -467,10 +473,10 @@ class vimconnector(vimconn.vimconnector): 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: - 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 @@ -603,23 +609,26 @@ class vimconnector(vimconn.vimconnector): # 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) - 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: + 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 @@ -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: @@ -706,23 +688,33 @@ class vimconnector(vimconn.vimconnector): 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 ) - # creation_result.wait() - result = creation_result.result() + virtual_machine = creation_result.result() 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() - return result.id, None + return virtual_machine.id, created_items # run_command_parameters = { # 'command_id': 'RunShellScript', # For linux, don't change it @@ -731,6 +723,16 @@ class vimconnector(vimconn.vimconnector): # ] # } 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) @@ -753,6 +755,109 @@ class vimconnector(vimconn.vimconnector): 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'], @@ -780,8 +885,8 @@ class vimconnector(vimconn.vimconnector): '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 @@ -832,7 +937,7 @@ class vimconnector(vimconn.vimconnector): 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) @@ -861,7 +966,7 @@ class vimconnector(vimconn.vimconnector): 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): @@ -873,7 +978,7 @@ class vimconnector(vimconn.vimconnector): 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] @@ -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: - 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) @@ -897,7 +1002,7 @@ class vimconnector(vimconn.vimconnector): 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: @@ -909,69 +1014,142 @@ class vimconnector(vimconn.vimconnector): self.logger.debug('deleting VM instance {} - {}'.format(self.resource_group, vm_id)) self._reload_connection() + created_items = created_items or {} 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(): - 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) + 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 @@ -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(): - 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): - 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,): - 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): - 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): """ @@ -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(): - 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: @@ -1043,7 +1221,7 @@ class vimconnector(vimconn.vimconnector): } 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 @@ -1111,7 +1289,7 @@ class vimconnector(vimconn.vimconnector): "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",