+++ /dev/null
-# -*- coding: utf-8 -*-
-
-__author__='Sergio Gonzalez'
-__date__ ='$18-apr-2019 23:59:59$'
-
-import vimconn
-import logging
-
-from os import getenv
-from uuid import uuid4
-
-from azure.common.credentials import ServicePrincipalCredentials
-from azure.mgmt.resource import ResourceManagementClient
-from azure.mgmt.network import NetworkManagementClient
-from azure.mgmt.compute import ComputeManagementClient
-
-
-class vimconnector(vimconn.vimconnector):
-
- def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level=None,
- config={}, persistent_info={}):
-
- vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level,
- config, persistent_info)
-
- # LOGGER
- self.logger = logging.getLogger('openmano.vim.azure')
- if log_level:
- logging.basicConfig()
- self.logger.setLevel(getattr(logging, log_level))
-
- # CREDENTIALS
- self.credentials = ServicePrincipalCredentials(
- client_id=user,
- secret=passwd,
- tenant=(tenant_id or tenant_name)
- )
-
- # SUBSCRIPTION
- if 'subscription_id' in config:
- self.subscription_id = config.get('subscription_id')
- self.logger.debug('Setting subscription '+str(self.subscription_id))
- else:
- 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')
- # 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')
- # VNET_NAME
- if 'vnet_name' in config:
- self.vnet_name = config["vnet_name"]
-
- # public ssh key
- self.pub_key = config.get('pub_key')
-
- def _reload_connection(self):
- """
- Sets connections to work with Azure service APIs
- :return:
- """
- self.logger.debug('Reloading API Connection')
- try:
- self.conn = ResourceManagementClient(self.credentials, self.subscription_id)
- self.conn_compute = ComputeManagementClient(self.credentials, self.subscription_id)
- self.conn_vnet = NetworkManagementClient(self.credentials, self.subscription_id)
- self._check_or_create_resource_group()
- self._check_or_create_vnet()
- except Exception as e:
- self.format_vimconn_exception(e)
-
- def _get_resource_name_from_resource_id(self, resource_id):
- return str(resource_id.split('/')[-1])
-
- def _get_location_from_resource_group(self, resource_group_name):
- return self.conn.resource_groups.get(resource_group_name).location
-
- def _get_resource_group_name_from_resource_id(self, resource_id):
- return str(resource_id.split('/')[4])
-
- def _check_subnets_for_vm(self, net_list):
- # All subnets must belong to the same resource group and vnet
- if len(set(self._get_resource_group_name_from_resource_id(net['id']) +
- self._get_resource_name_from_resource_id(net['id']) for net in net_list)) != 1:
- raise self.format_vimconn_exception('Azure VMs can only attach to subnets in same VNET')
-
- def format_vimconn_exception(self, e):
- """
- Params: an Exception object
- :param e:
- :return: Raises the proper vimconnException
- """
- self.conn = None
- self.conn_vnet = None
- raise vimconn.vimconnConnectionException(type(e).__name__ + ': ' + str(e))
-
- def _check_or_create_resource_group(self):
- """
- Creates a resource group in indicated region
- :return: None
- """
- self.logger.debug('Creating RG {} in location {}'.format(self.resource_group, self.region))
- self.conn.resource_groups.create_or_update(self.resource_group, {'location': self.region})
-
- def _check_or_create_vnet(self):
- try:
- vnet_params = {
- 'location': self.region,
- 'address_space': {
- 'address_prefixes': "10.0.0.0/8"
- },
- }
- self.conn_vnet.virtual_networks.create_or_update(self.resource_group, self.vnet_name, vnet_params)
- except Exception as e:
- self.format_vimconn_exception(e)
-
- def new_network(self, net_name, net_type, ip_profile=None, shared=False, vlan=None):
- """
- Adds a tenant network to VIM
- :param net_name: name of the network
- :param net_type:
- :param ip_profile: is a dict containing the IP parameters of the network (Currently only IPv4 is implemented)
- 'ip-version': can be one of ['IPv4','IPv6']
- 'subnet-address': ip_prefix_schema, that is X.X.X.X/Y
- 'gateway-address': (Optional) ip_schema, that is X.X.X.X
- 'dns-address': (Optional) ip_schema,
- 'dhcp': (Optional) dict containing
- 'enabled': {'type': 'boolean'},
- 'start-address': ip_schema, first IP to grant
- 'count': number of IPs to grant.
- :param shared:
- :param vlan:
- :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.
- Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
- as not present.
- """
-
- return self._new_subnet(net_name, ip_profile)
-
- def _new_subnet(self, net_name, ip_profile):
- """
- Adds a tenant network to VIM. It creates a new VNET with a single subnet
- :param net_name:
- :param ip_profile:
- :return:
- """
- self.logger.debug('Adding a subnet to VNET '+self.vnet_name)
- self._reload_connection()
-
- if ip_profile is None:
- # TODO get a non used vnet ip range /24 and allocate automatically
- raise vimconn.vimconnException('Azure cannot create VNET with no CIDR')
-
- try:
- vnet_params= {
- 'location': self.region,
- 'address_space': {
- 'address_prefixes': [ip_profile['subnet_address']]
- },
- 'subnets': [
- {
- 'name': "{}-{}".format(net_name[:24], uuid4()),
- 'address_prefix': ip_profile['subnet_address']
- }
- ]
- }
- self.conn_vnet.virtual_networks.create_or_update(self.resource_group, self.vnet_name, vnet_params)
- # TODO return a tuple (subnet-ID, None)
- except Exception as e:
- self.format_vimconn_exception(e)
-
- def _create_nic(self, subnet_id, nic_name, static_ip=None):
- self._reload_connection()
-
- resource_group_name=self._get_resource_group_name_from_resource_id(subnet_id)
- location = self._get_location_from_resource_group(resource_group_name)
-
- if static_ip:
- async_nic_creation = self.conn_vnet.network_interfaces.create_or_update(
- resource_group_name,
- nic_name,
- {
- 'location': location,
- 'ip_configurations': [{
- 'name': nic_name + 'ipconfiguration',
- 'privateIPAddress': static_ip,
- 'privateIPAllocationMethod': 'Static',
- 'subnet': {
- 'id': subnet_id
- }
- }]
- }
- )
- else:
- async_nic_creation = self.conn_vnet.network_interfaces.create_or_update(
- resource_group_name,
- nic_name,
- {
- 'location': location,
- 'ip_configurations': [{
- 'name': nic_name + 'ipconfiguration',
- 'subnet': {
- 'id': subnet_id
- }
- }]
- }
- )
-
- return async_nic_creation.result()
-
- def get_image_list(self, filter_dict={}):
- """
- The urn contains for marketplace 'publisher:offer:sku:version'
-
- :param filter_dict:
- :return:
- """
- image_list = []
-
- self._reload_connection()
- if filter_dict.get("name"):
- params = filter_dict["name"].split(":")
- if len(params) >= 3:
- publisher = params[0]
- offer = params[1]
- sku = params[2]
- version = None
- if len(params) == 4:
- version = params[3]
- images = self.conn_compute.virtual_machine_images.list(self.region, publisher, offer, sku)
- for image in images:
- if version:
- image_version = str(image.id).split("/")[-1]
- if image_version != version:
- continue
- image_list.append({
- 'id': str(image.id),
- 'name': self._get_resource_name_from_resource_id(image.id)
- })
- return image_list
-
- images = self.conn_compute.virtual_machine_images.list()
-
- for image in images:
- # TODO implement filter_dict
- if filter_dict:
- if filter_dict.get("id") and str(image.id) != filter_dict["id"]:
- continue
- if filter_dict.get("name") and \
- self._get_resource_name_from_resource_id(image.id) != filter_dict["name"]:
- continue
- # TODO add checksum
- image_list.append({
- 'id': str(image.id),
- 'name': self._get_resource_name_from_resource_id(image.id),
- })
- return image_list
-
- def get_network_list(self, filter_dict={}):
- """Obtain tenant networks of VIM
- Filter_dict can be:
- name: network name
- id: network uuid
- shared: boolean
- tenant_id: tenant
- admin_state_up: boolean
- status: 'ACTIVE'
- Returns the network list of dictionaries
- """
- self.logger.debug('Getting all subnets from VIM')
- try:
- self._reload_connection()
- vnet = self.conn_vnet.virtual_networks.get(self.config["resource_group"], self.vnet_name)
- subnet_list = []
-
- for subnet in vnet.subnets:
- # TODO implement filter_dict
- if filter_dict:
- if filter_dict.get("id") and str(subnet.id) != filter_dict["id"]:
- continue
- if filter_dict.get("name") and \
- self._get_resource_name_from_resource_id(subnet.id) != filter_dict["name"]:
- continue
-
- subnet_list.append({
- 'id': str(subnet.id),
- 'name': self._get_resource_name_from_resource_id(subnet.id),
- 'status': str(vnet.provisioning_state), # TODO Does subnet contains status???
- 'cidr_block': str(subnet.address_prefix)
- }
- )
- 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, vm_name, image_id, flavor_id, net_list, cloud_config=None, disk_list=None,
- availability_zone_index=None, availability_zone_list=None):
- #Create NICs
- self._check_subnets_for_vm(net_list)
- vm_nics = []
- for idx, net in enumerate(net_list):
- subnet_id=net['subnet_id']
- nic_name = vm_name + '-nic-'+str(idx)
- vm_nic = self._create_nic(subnet_id, nic_name)
- vm_nics.append({ 'id': str(vm_nic.id)})
-
- try:
- vm_parameters = {
- 'location': self.region,
- 'os_profile': {
- 'computer_name': vm_name, # TODO if vm_name cannot be repeated add uuid4() suffix
- 'admin_username': 'sergio', # TODO is it mandatory???
- 'linuxConfiguration': {
- 'disablePasswordAuthentication': 'true',
- 'ssh': {
- 'publicKeys': [
- {
- 'path': '/home/sergio/.ssh/authorized_keys',
- 'keyData': self.pub_key
- }
- ]
- }
- }
-
- },
- 'hardware_profile': {
- 'vm_size':flavor_id
- },
- 'storage_profile': {
- 'image_reference': image_id
- },
- 'network_profile': {
- 'network_interfaces': [
- vm_nics[0]
- ]
- }
- }
- creation_result = self.conn_compute.virtual_machines.create_or_update(
- self.resource_group,
- vm_name,
- vm_parameters
- )
-
- run_command_parameters = {
- 'command_id': 'RunShellScript', # For linux, don't change it
- 'script': [
- 'date > /home/sergio/test.txt'
- ]
- }
- poller = self.conn_compute.virtual_machines.run_command(
- self.resource_group,
- vm_name,
- run_command_parameters
- )
- # TODO return a tuple (vm-ID, None)
- except Exception as e:
- self.format_vimconn_exception(e)
-
- def get_flavor_id_from_data(self, flavor_dict):
- self.logger.debug("Getting flavor id from data")
- self._reload_connection()
- vm_sizes_list = [vm_size.serialize() for vm_size in self.conn_compute.virtual_machine_sizes.list(self.region)]
-
- cpus = flavor_dict['vcpus']
- memMB = flavor_dict['ram']
-
- filteredSizes = [size for size in vm_sizes_list if size['numberOfCores'] > cpus and size['memoryInMB'] > memMB]
- listedFilteredSizes = sorted(filteredSizes, key=lambda k: k['numberOfCores'])
-
- return listedFilteredSizes[0]['name']
-
- def check_vim_connectivity(self):
- try:
- self._reload_connection()
- return True
- except Exception as e:
- raise vimconn.vimconnException("Connectivity issue with Azure API: {}".format(e))
-
- def get_network(self, net_id):
- resGroup = self._get_resource_group_name_from_resource_id(net_id)
- resName = self._get_resource_name_from_resource_id(net_id)
-
- self._reload_connection()
- vnet = self.conn_vnet.virtual_networks.get(resGroup, resName)
-
- return vnet
-
- def delete_network(self, net_id):
- resGroup = self._get_resource_group_name_from_resource_id(net_id)
- resName = self._get_resource_name_from_resource_id(net_id)
-
- self._reload_connection()
- self.conn_vnet.virtual_networks.delete(resGroup, resName)
-
- def delete_vminstance(self, vm_id):
- resGroup = self._get_resource_group_name_from_resource_id(net_id)
- resName = self._get_resource_name_from_resource_id(net_id)
-
- self._reload_connection()
- self.conn_compute.virtual_machines.delete(resGroup, resName)
-
- def get_vminstance(self, vm_id):
- resGroup = self._get_resource_group_name_from_resource_id(net_id)
- resName = self._get_resource_name_from_resource_id(net_id)
-
- self._reload_connection()
- vm=self.conn_compute.virtual_machines.get(resGroup, resName)
-
- return vm
-
- def get_flavor(self, flavor_id):
- self._reload_connection()
- for vm_size in self.conn_compute.virtual_machine_sizes.list(self.region):
- if vm_size.name == flavor_id :
- return vm_size
-
-
-# TODO refresh_nets_status ver estado activo
-# TODO refresh_vms_status ver estado activo
-# TODO get_vminstance_console for getting console
-
-if __name__ == "__main__":
-
- # Making some basic test
- vim_id='azure'
- vim_name='azure'
- needed_test_params = {
- "client_id": "AZURE_CLIENT_ID",
- "secret": "AZURE_SECRET",
- "tenant": "AZURE_TENANT",
- "resource_group": "AZURE_RESOURCE_GROUP",
- "subscription_id": "AZURE_SUBSCRIPTION_ID",
- "vnet_name": "AZURE_VNET_NAME",
- }
- test_params = {}
-
- for param, env_var in needed_test_params.items():
- value = getenv(env_var)
- if not value:
- raise Exception("Provide a valid value for env '{}'".format(env_var))
- 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'),
- }
-
- virtualMachine = {
- 'name': 'sergio',
- 'description': 'new VM',
- 'status': 'running',
- 'image': {
- 'publisher': 'Canonical',
- 'offer': 'UbuntuServer',
- 'sku': '16.04.0-LTS',
- 'version': 'latest'
- },
- 'hardware_profile': {
- 'vm_size': 'Standard_DS1_v2'
- },
- 'networks': [
- 'sergio'
- ]
- }
-
- vnet_config = {
- 'subnet_address': '10.1.2.0/24',
- #'subnet_name': 'subnet-oam'
- }
- ###########################
-
- azure = vimconnector(vim_id, vim_name, tenant_id=test_params["tenant"], tenant_name=None, url=None, url_admin=None,
- user=test_params["client_id"], passwd=test_params["secret"], log_level=None, config=config)
-
- # azure.get_flavor_id_from_data("here")
- # subnets=azure.get_network_list()
- # azure.new_vminstance(virtualMachine['name'], virtualMachine['description'], virtualMachine['status'],
- # virtualMachine['image'], virtualMachine['hardware_profile']['vm_size'], subnets)
-
- azure.get_flavor("Standard_A11")
\ No newline at end of file