X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FRO.git;a=blobdiff_plain;f=osm_ro%2Fvimconn_azure.py;h=7f2b2ea33c17f9d56b5bc4795da15b7804092884;hp=59bd535f91d4875a06c9cb42098db6cac77f279d;hb=a85c54de5c5d1f951b27082a21e5654e15712529;hpb=45220151b16daf05b8faf72a963c6d40b9830747 diff --git a/osm_ro/vimconn_azure.py b/osm_ro/vimconn_azure.py index 59bd535f..7f2b2ea3 100755 --- a/osm_ro/vimconn_azure.py +++ b/osm_ro/vimconn_azure.py @@ -1,8 +1,19 @@ # -*- coding: utf-8 -*- -import base64 - - +## +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +## +import base64 import vimconn import logging import netaddr @@ -13,13 +24,13 @@ from azure.common.credentials import ServicePrincipalCredentials from azure.mgmt.resource import ResourceManagementClient from azure.mgmt.network import NetworkManagementClient from azure.mgmt.compute import ComputeManagementClient -from azure.mgmt.compute.v2019_03_01.models import DiskCreateOptionTypes +from azure.mgmt.compute.models import DiskCreateOption from msrestazure.azure_exceptions import CloudError from msrest.exceptions import AuthenticationError from requests.exceptions import ConnectionError -__author__ = 'Sergio Gonzalez' -__date__ = '$18-apr-2019 23:59:59$' +__author__ = 'Isabel Lloret, Sergio Gonzalez, Alfonso Tierno' +__date__ = '$18-apr-2019 23:59:59$' if getenv('OSMRO_PDB_DEBUG'): @@ -87,15 +98,16 @@ class vimconnector(vimconn.vimconnector): self.tenant = (tenant_id or tenant_name) # Store config to create azure subscription later - self._config = {} - self._config["user"] = user - self._config["passwd"] = passwd - self._config["tenant"] = (tenant_id or tenant_name) + self._config = { + "user": user, + "passwd": passwd, + "tenant": tenant_id or tenant_name + } # SUBSCRIPTION if 'subscription_id' in config: self._config["subscription_id"] = config.get('subscription_id') - #self.logger.debug('Setting subscription to: %s', self.config["subscription_id"]) + # self.logger.debug('Setting subscription to: %s', self.config["subscription_id"]) else: raise vimconn.vimconnException('Subscription not specified') @@ -153,7 +165,8 @@ 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 invalid resource_id format: '{}'".format(resource_id)) + 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: @@ -248,7 +261,7 @@ class vimconnector(vimconn.vimconnector): except Exception as e: self._format_vimconn_exception(e) - def new_network(self, net_name, net_type, ip_profile=None, shared=False, vlan=None): + def new_network(self, net_name, net_type, ip_profile=None, shared=False, provider_network_profile=None): """ Adds a tenant network to VIM :param net_name: name of the network @@ -263,7 +276,7 @@ class vimconnector(vimconn.vimconnector): 'start-address': ip_schema, first IP to grant 'count': number of IPs to grant. :param shared: Not allowed for Azure Connector - :param vlan: VLAN tagging is not allowed for Azure + :param provider_network_profile: (optional) contains {segmentation-id: vlan, provider-network: vim_netowrk} :return: a tuple with the network 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_network. Can be used to store created segments, created l2gw connections, etc. @@ -304,7 +317,7 @@ class vimconnector(vimconn.vimconnector): ip_profile = {"subnet_address": ip_profile['subnet_address']} try: - #subnet_name = "{}-{}".format(net_name[:24], uuid4()) + # subnet_name = "{}-{}".format(net_name[:24], uuid4()) subnet_params = { 'address_prefix': ip_profile['subnet_address'] } @@ -333,8 +346,8 @@ class vimconnector(vimconn.vimconnector): # get the name with the first not used suffix name_suffix = 0 - #name = subnet_name + "-" + str(name_suffix) - name = subnet_name # first subnet created will have no prefix + # name = subnet_name + "-" + str(name_suffix) + name = subnet_name # first subnet created will have no prefix while name in net_names: name_suffix += 1 name = subnet_name + "-" + str(name_suffix) @@ -348,39 +361,19 @@ class vimconnector(vimconn.vimconnector): subnet_id = net['net_id'] location = self._get_location_from_resource_group(self.resource_group) try: + net_ifz = {'location': location} + net_ip_config = {'name': nic_name + '-ipconfiguration', 'subnet': {'id': subnet_id}} if static_ip: - async_nic_creation = self.conn_vnet.network_interfaces.create_or_update( - self.resource_group, - nic_name, - { - 'location': location, - 'ip_configurations': [{ - 'name': nic_name + '-ipconfiguration', - 'privateIPAddress': static_ip, - 'privateIPAllocationMethod': 'Static', - 'subnet': { - 'id': subnet_id - } - }] - } - ) - async_nic_creation.wait() - else: - ip_configuration_name = nic_name + '-ipconfiguration' - async_nic_creation = self.conn_vnet.network_interfaces.create_or_update( - self.resource_group, - nic_name, - { - 'location': location, - 'ip_configurations': [{ - 'name': ip_configuration_name, - 'subnet': { - 'id': subnet_id - } - }] - } - ) - async_nic_creation.wait() + net_ip_config['privateIPAddress'] = static_ip + net_ip_config['privateIPAllocationMethod'] = 'Static' + net_ifz['ip_configurations'] = [net_ip_config] + mac_address = net.get('mac_address') + if mac_address: + net_ifz['mac_address'] = mac_address + + async_nic_creation = self.conn_vnet.network_interfaces.create_or_update(self.resource_group, nic_name, + net_ifz) + async_nic_creation.wait() self.logger.debug('created nic name %s', nic_name) public_ip = net.get('floating_ip') @@ -397,7 +390,7 @@ class vimconnector(vimconn.vimconnector): ) self.logger.debug('created public IP: {}'.format(public_ip.result())) - # Asociate NIC to Public IP + # Associate NIC to Public IP nic_data = self.conn_vnet.network_interfaces.get( self.resource_group, nic_name) @@ -497,7 +490,7 @@ class vimconnector(vimconn.vimconnector): return [offer.name for offer in result_offers] except CloudError as e: # azure raises CloudError when not found - self.logger.info("error listing offers for publisher {}, message: {}".format(publisher, e.message)) + self.logger.info("error listing offers for publisher {}, Error: {}".format(publisher, e)) return [] def _get_sku_list(self, params, publisher, offer): @@ -513,7 +506,7 @@ class vimconnector(vimconn.vimconnector): return [sku.name for sku in result_skus] except CloudError as e: # azure raises CloudError when not found - self.logger.info("error listing skus for publisher {}, offer {}, message: {}".format(publisher, offer, e.message)) + self.logger.info("error listing skus for publisher {}, offer {}, Error: {}".format(publisher, offer, e)) return [] def _get_sku_image_list(self, publisher, offer, sku): @@ -530,7 +523,7 @@ class vimconnector(vimconn.vimconnector): }) except CloudError as e: self.logger.info( - "error listing skus for publisher {}, offer {}, message: {}".format(publisher, offer, e.message)) + "error listing skus for publisher {}, offer {}, Error: {}".format(publisher, offer, e)) image_list = [] return image_list @@ -545,12 +538,8 @@ class vimconnector(vimconn.vimconnector): }) except CloudError as e: # azure gives CloudError when not found - self.logger.info( - "error listing images for publisher {}, offer {}, sku {}, vesion {} message: {}".format(publisher, - offer, - sku, - version, - e.message)) + self.logger.info("error listing images for publisher {}, offer {}, sku {}, version {} Error: {}". + format(publisher, offer, sku, version, e)) image_list = [] return image_list @@ -565,7 +554,7 @@ class vimconnector(vimconn.vimconnector): status: 'ACTIVE', not implemented in Azure # Returns the network list of dictionaries """ - #self.logger.debug('getting network list for vim, filter %s', filter_dict) + # self.logger.debug('getting network list for vim, filter %s', filter_dict) try: self._reload_connection() @@ -584,28 +573,22 @@ class vimconnector(vimconn.vimconnector): subnet_list.append({ 'id': str(subnet.id), - 'name': self._get_resource_name_from_resource_id(subnet.id), + 'name': name, 'status': self.provision_state2osm[subnet.provisioning_state], 'cidr_block': str(subnet.address_prefix), 'type': 'bridge', 'shared': False - } - ) + }) return subnet_list except Exception as e: self._format_vimconn_exception(e) - def new_vminstance(self, vm_name, description, start, image_id, flavor_id, net_list, cloud_config=None, - disk_list=None, availability_zone_index=None, availability_zone_list=None): - - return self._new_vminstance(vm_name, image_id, flavor_id, net_list) - 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): + disk_list=None, availability_zone_index=None, availability_zone_list=None): - self.logger.debug("new vm instance name: %s, image_id: %s, flavor_id: %s, net_list: %s, cloud_config: %s" - + "disk_list: %s, availability_zone_index: %s, availability_zone_list: %s", + self.logger.debug("new vm instance name: %s, image_id: %s, flavor_id: %s, net_list: %s, cloud_config: %s, " + "disk_list: %s, availability_zone_index: %s, availability_zone_list: %s", name, image_id, flavor_id, net_list, cloud_config, disk_list, availability_zone_index, availability_zone_list) @@ -632,7 +615,7 @@ class vimconnector(vimconn.vimconnector): # 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) + 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 @@ -651,7 +634,7 @@ class vimconnector(vimconn.vimconnector): if cloud_config.get("users"): user_name = cloud_config.get("users")[0].get("name", "osm") else: - user_name = "osm" # DEFAULT USER IS OSM + user_name = "osm" # DEFAULT USER IS OSM os_profile = { 'computer_name': vm_name, @@ -669,9 +652,9 @@ class vimconnector(vimconn.vimconnector): } else: os_profile = { - 'computer_name': vm_name - ,'admin_username': 'osm' - ,'admin_password': 'Osm4u!', + 'computer_name': vm_name, + 'admin_username': 'osm', + 'admin_password': 'Osm4u!', } vm_parameters = { @@ -694,11 +677,20 @@ class vimconnector(vimconn.vimconnector): 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': DiskCreateOptionTypes.empty, + '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") + # 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 @@ -720,25 +712,24 @@ class vimconnector(vimconn.vimconnector): vm_name, vm_parameters ) - self.logger.debug("created vm name: %s", vm_name) - - #creation_result.wait() + # creation_result.wait() result = creation_result.result() + self.logger.debug("created vm name: %s", vm_name) if start: - start_result = self.conn_compute.virtual_machines.start( + self.conn_compute.virtual_machines.start( self.resource_group, vm_name) - #start_result.wait() + # start_result.wait() return result.id, None - #run_command_parameters = { - # 'command_id': 'RunShellScript', # For linux, don't change it - # 'script': [ - # 'date > /tmp/test.txt' - # ] - #} + # run_command_parameters = { + # 'command_id': 'RunShellScript', # For linux, don't change it + # 'script': [ + # 'date > /tmp/test.txt' + # ] + # } except Exception as e: self.logger.debug('Exception creating new vminstance: %s', e, exc_info=True) self._format_vimconn_exception(e) @@ -762,7 +753,6 @@ class vimconnector(vimconn.vimconnector): name = vm_name + "-" + str(name_suffix) return name - # It is necesary extract from image_id data to create the VM with this format # 'image_reference': { # 'publisher': vm_reference['publisher'], @@ -785,10 +775,10 @@ class vimconnector(vimconn.vimconnector): version = str(image_id.split('/')[16]) return { - 'publisher': publisher, - 'offer': offer, - 'sku': sku, - 'version': version + 'publisher': publisher, + 'offer': offer, + 'sku': sku, + 'version': version } except Exception as e: raise vimconn.vimconnException( @@ -800,7 +790,6 @@ class vimconnector(vimconn.vimconnector): Checks vm name, in case the vm has not allowed characters they are removed, not error raised """ - #chars_not_allowed_list = ['~','!','@','#','$','%','^','&','*','(',')','=','+','_','[',']','{','}','|',';',':','<','>','/','?','.'] chars_not_allowed_list = "~!@#$%^&*()=+_[]{}|;:<>/?." # First: the VM name max length is 64 characters @@ -810,7 +799,7 @@ class vimconnector(vimconn.vimconnector): for elem in chars_not_allowed_list: # Check if string is in the main string if elem in vm_name_aux: - #self.logger.debug('Dentro del IF') + # self.logger.debug('Dentro del IF') # Replace the string vm_name_aux = vm_name_aux.replace(elem, '-') @@ -822,24 +811,24 @@ class vimconnector(vimconn.vimconnector): filter_dict = flavor_dict or {} try: self._reload_connection() - vm_sizes_list = [vm_size.serialize() for vm_size in self.conn_compute.virtual_machine_sizes.list(self.region)] + vm_sizes_list = [vm_size.serialize() for vm_size in + self.conn_compute.virtual_machine_sizes.list(self.region)] cpus = filter_dict.get('vcpus') or 0 memMB = filter_dict.get('ram') or 0 # Filter if self._config.get("flavors_pattern"): - filtered_sizes = [size for size in vm_sizes_list if size['numberOfCores'] >= cpus - and size['memoryInMB'] >= memMB - and re.search(self._config.get("flavors_pattern"), size["name"])] + filtered_sizes = [size for size in vm_sizes_list if size['numberOfCores'] >= cpus and + size['memoryInMB'] >= memMB and + re.search(self._config.get("flavors_pattern"), size["name"])] else: - filtered_sizes = [size for size in vm_sizes_list if size['numberOfCores'] >= cpus - and size['memoryInMB'] >= memMB] - - + filtered_sizes = [size for size in vm_sizes_list if size['numberOfCores'] >= cpus and + size['memoryInMB'] >= memMB] # Sort - listedFilteredSizes = sorted(filtered_sizes, key=lambda k: (k['numberOfCores'], k['memoryInMB'], k['resourceDiskSizeInMB'])) + listedFilteredSizes = sorted(filtered_sizes, key=lambda k: (k['numberOfCores'], k['memoryInMB'], + k['resourceDiskSizeInMB'])) if listedFilteredSizes: return listedFilteredSizes[0]['name'] @@ -850,17 +839,18 @@ class vimconnector(vimconn.vimconnector): def _get_flavor_id_from_flavor_name(self, flavor_name): - #self.logger.debug("getting flavor id from flavor name {}".format(flavor_name)) + # self.logger.debug("getting flavor id from flavor name {}".format(flavor_name)) try: self._reload_connection() - vm_sizes_list = [vm_size.serialize() for vm_size in self.conn_compute.virtual_machine_sizes.list(self.region)] + vm_sizes_list = [vm_size.serialize() for vm_size in + self.conn_compute.virtual_machine_sizes.list(self.region)] output_flavor = None for size in vm_sizes_list: if size['name'] == flavor_name: output_flavor = size - # Si no se encuentra ninguno, este metodo devuelve None + # None is returned if not found anything return output_flavor except Exception as e: @@ -875,8 +865,8 @@ class vimconnector(vimconn.vimconnector): def get_network(self, net_id): - #self.logger.debug('get network id: {}'.format(net_id)) - res_name = self._get_resource_name_from_resource_id(net_id) + # self.logger.debug('get network id: {}'.format(net_id)) + # res_name = self._get_resource_name_from_resource_id(net_id) self._reload_connection() filter_dict = {'name': net_id} @@ -925,8 +915,8 @@ class vimconnector(vimconn.vimconnector): 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_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() @@ -972,7 +962,7 @@ class vimconnector(vimconn.vimconnector): # Delete list of public ips if public_ip_name: self.logger.debug('delete PUBLIC IP - ' + public_ip_name) - public_ip = self.conn_vnet.public_ip_addresses.delete(self.resource_group, public_ip_name) + self.conn_vnet.public_ip_addresses.delete(self.resource_group, public_ip_name) except CloudError as e: if e.error.error and "notfound" in e.error.error.lower(): @@ -982,7 +972,7 @@ class vimconnector(vimconn.vimconnector): except Exception as e: self._format_vimconn_exception(e) - def action_vminstance(self, vm_id, action_dict, created_items = {}): + 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 """ @@ -1025,7 +1015,7 @@ class vimconnector(vimconn.vimconnector): self._reload_connection() try: resName = self._get_resource_name_from_resource_id(vm_id) - vm=self.conn_compute.virtual_machines.get(self.resource_group, resName) + 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)) @@ -1060,8 +1050,8 @@ class vimconnector(vimconn.vimconnector): For the azure connector only the azure tenant will be returned if it is compatible with filter_dict """ - tenants_azure=[{'name': self.tenant, 'id': self.tenant}] - tenant_list=[] + tenants_azure = [{'name': self.tenant, 'id': self.tenant}] + tenant_list = [] self.logger.debug("get tenant list: %s", filter_dict) for tenant_azure in tenants_azure: @@ -1223,10 +1213,20 @@ class vimconnector(vimconn.vimconnector): self.resource_group, nic_name) + ips = [] + if nic_data.ip_configurations[0].public_ip_address: + self.logger.debug("Obtain public ip address") + public_ip_name = self._get_resource_name_from_resource_id( + nic_data.ip_configurations[0].public_ip_address.id) + public_ip = self.conn_vnet.public_ip_addresses.get(self.resource_group, public_ip_name) + self.logger.debug("Public ip address is: %s", public_ip.ip_address) + ips.append(public_ip.ip_address) + private_ip = nic_data.ip_configurations[0].private_ip_address + ips.append(private_ip) interface_dict['mac_address'] = nic_data.mac_address - interface_dict['ip_address'] = private_ip + interface_dict['ip_address'] = ";".join(ips) interface_list.append(interface_dict) return interface_list @@ -1257,11 +1257,11 @@ if __name__ == "__main__": test_params[param] = value config = { - 'region_name': getenv("AZURE_REGION_NAME", 'westeurope'), - 'resource_group': getenv("AZURE_RESOURCE_GROUP"), - 'subscription_id': getenv("AZURE_SUBSCRIPTION_ID"), - 'pub_key': getenv("AZURE_PUB_KEY", None), - 'vnet_name': getenv("AZURE_VNET_NAME", 'myNetwork'), + 'region_name': getenv("AZURE_REGION_NAME", 'westeurope'), + 'resource_group': getenv("AZURE_RESOURCE_GROUP"), + 'subscription_id': getenv("AZURE_SUBSCRIPTION_ID"), + 'pub_key': getenv("AZURE_PUB_KEY", None), + 'vnet_name': getenv("AZURE_VNET_NAME", 'myNetwork'), } virtualMachine = { @@ -1284,7 +1284,7 @@ if __name__ == "__main__": vnet_config = { 'subnet_address': '10.1.2.0/24', - #'subnet_name': 'subnet-oam' + # 'subnet_name': 'subnet-oam' } ###########################