1 # -*- coding: utf-8 -*-
3 __author__
='Sergio Gonzalez'
4 __date__
='$18-apr-2019 23:59:59$'
10 from uuid
import uuid4
12 from azure
.common
.credentials
import ServicePrincipalCredentials
13 from azure
.mgmt
.resource
import ResourceManagementClient
14 from azure
.mgmt
.network
import NetworkManagementClient
15 from azure
.mgmt
.compute
import ComputeManagementClient
17 class vimconnector(vimconn
.vimconnector
):
19 def __init__(self
, uuid
, name
, tenant_id
, tenant_name
, url
, url_admin
=None, user
=None, passwd
=None, log_level
=None,
20 config
={}, persistent_info
={}):
22 vimconn
.vimconnector
.__init
__(self
, uuid
, name
, tenant_id
, tenant_name
, url
, url_admin
, user
, passwd
, log_level
,
23 config
, persistent_info
)
26 self
.logger
= logging
.getLogger('openmano.vim.azure')
29 self
.logger
.setLevel(getattr(logging
, log_level
))
32 self
.credentials
= ServicePrincipalCredentials(
39 if 'subscription_id' in config
:
40 self
.subscription_id
= config
.get('subscription_id')
41 self
.logger
.debug('Setting subscription '+str(self
.subscription_id
))
43 raise vimconn
.vimconnException('Subscription not specified')
45 if 'region_name' in config
:
46 self
.region
= config
.get('region_name')
48 raise vimconn
.vimconnException('Azure region_name is not specified at config')
50 if 'resource_group' in config
:
51 self
.resource_group
= config
.get('resource_group')
53 raise vimconn
.vimconnException('Azure resource_group is not specified at config')
55 if 'vnet_name' in config
:
56 self
.vnet_name
= config
["vnet_name"]
59 self
.pub_key
= config
.get('pub_key')
61 def _reload_connection(self
):
62 '''Sets connections to work with Azure service APIs
64 self
.logger
.debug('Reloading API Connection')
66 self
.conn
= ResourceManagementClient(self
.credentials
, self
.subscription_id
)
67 self
.conn_compute
= ComputeManagementClient(self
.credentials
, self
.subscription_id
)
68 self
.conn_vnet
= NetworkManagementClient(self
.credentials
, self
.subscription_id
)
69 except Exception as e
:
70 self
.format_vimconn_exception(e
)
72 def _get_resource_name_from_resource_id(self
, resource_id
):
73 return str(resource_id
.split('/')[-1])
75 def _get_location_from_resource_group(self
, resource_group_name
):
76 return self
.conn
.resource_groups
.get(resource_group_name
).location
78 def _get_resource_group_name_from_resource_id(self
, resource_id
):
79 return str(resource_id
.split('/')[4])
81 def _check_subnets_for_vm(self
, net_list
):
82 #All subnets must belong to the same resource group and vnet
83 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:
84 raise self
.format_vimconn_exception('Azure VMs can only attach to subnets in same VNET')
86 def format_vimconn_exception(self
, e
):
87 '''Params: an Exception object
88 Returns: Raises the exception 'e' passed in mehtod parameters
92 raise vimconn
.vimconnConnectionException(type(e
).__name
__ + ': ' + str(e
))
94 def _check_or_create_resource_group(self
):
95 '''Creates a resource group in indicated region
97 self
.logger
.debug('Creating RG {} in location {}'.format(self
.resource_group
, self
.region
))
98 self
.conn
.resource_groups
.create_or_update(self
.resource_group
, {'location':self
.region
})
100 def new_network(self
, net_name
, net_type
, ip_profile
=None, shared
=False, vlan
=None):
101 '''Adds a tenant network to VIM
103 'net_name': name of the network
104 'ip_profile': is a dict containing the IP parameters of the network (Currently only IPv4 is implemented)
105 'ip-version': can be one of ['IPv4','IPv6']
106 'subnet-address': ip_prefix_schema, that is X.X.X.X/Y
107 'gateway-address': (Optional) ip_schema, that is X.X.X.X
108 'dns-address': (Optional) ip_schema,
109 'dhcp': (Optional) dict containing
110 'enabled': {'type': 'boolean'},
111 'start-address': ip_schema, first IP to grant
112 'count': number of IPs to grant.
113 Returns a tuple with the network identifier and created_items, or raises an exception on error
114 created_items can be None or a dictionary where this method can include key-values that will be passed to
115 the method delete_network. Can be used to store created segments, created l2gw connections, etc.
116 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
119 return self
._new
_subnet
(net_name
, ip_profile
)
121 def _new_subnet(self
, net_name
, ip_profile
):
122 '''Adds a tenant network to VIM
123 It creates a new VNET with a single subnet
125 self
.logger
.debug('Adding a subnet to VNET '+self
.vnet_name
)
126 self
._reload
_connection
()
127 self
._check
_or
_create
_resource
_group
()
129 if ip_profile
is None:
130 # TODO get a non used vnet ip range /24 and allocate automatically
131 raise vimconn
.vimconnException('Azure cannot create VNET with no CIDR')
135 'location': self
.region
,
137 'address_prefixes': [ip_profile
['subnet_address']]
141 'name': "{}-{}".format(net_name
[:24], uuid4()),
142 'address_prefix': ip_profile
['subnet_address']
146 self
.conn_vnet
.virtual_networks
.create_or_update(self
.resource_group
, self
.vnet_name
, vnet_params
)
147 # TODO return a tuple (subnet-ID, None)
148 except Exception as e
:
149 self
.format_vimconn_exception(e
)
151 def _create_nic(self
, subnet_id
, nic_name
, static_ip
=None):
152 self
._reload
_connection
()
154 resource_group_name
=self
._get
_resource
_group
_name
_from
_resource
_id
(subnet_id
)
155 location
= self
._get
_location
_from
_resource
_group
(resource_group_name
)
158 async_nic_creation
= self
.conn_vnet
.network_interfaces
.create_or_update(
162 'location': location
,
163 'ip_configurations': [{
164 'name': nic_name
+ 'ipconfiguration',
165 'privateIPAddress': static_ip
,
166 'privateIPAllocationMethod': 'Static',
174 async_nic_creation
= self
.conn_vnet
.network_interfaces
.create_or_update(
178 'location': location
,
179 'ip_configurations': [{
180 'name': nic_name
+ 'ipconfiguration',
188 return async_nic_creation
.result()
190 def get_network_list(self
, filter_dict
={}):
191 '''Obtain tenant networks of VIM
197 admin_state_up: boolean
199 Returns the network list of dictionaries
201 self
.logger
.debug('Getting all subnets from VIM')
203 self
._reload
_connection
()
204 vnet
= self
.conn_vnet
.virtual_networks
.get(self
.config
["resource_group"], self
.vnet_name
)
207 for subnet
in vnet
.subnets
:
208 # TODO implement filter_dict
210 if filter_dict
.get("id") and str(subnet
.id) != filter_dict
["id"]:
212 if filter_dict
.get("name") and \
213 self
._get
_resource
_name
_from
_resource
_id
(subnet
.id) != filter_dict
["name"]:
217 'id': str(subnet
.id),
218 'name': self
._get
_resource
_name
_from
_resource
_id
(subnet
.id),
219 'status': str(vnet
.provisioning_state
), # TODO Does subnet contains status???
220 'cidr_block': str(subnet
.address_prefix
)
224 except Exception as e
:
225 self
.format_vimconn_exception(e
)
227 def new_vminstance(self
, vm_name
, description
, start
, image_id
, flavor_id
, net_list
, cloud_config
=None,
228 disk_list
=None, availability_zone_index
=None, availability_zone_list
=None):
230 return self
._new
_vminstance
(vm_name
, image_id
, flavor_id
, net_list
)
232 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):
234 self
._check
_subnets
_for
_vm
(net_list
)
236 for idx
, net
in enumerate(net_list
):
237 subnet_id
=net
['subnet_id']
238 nic_name
= vm_name
+ '-nic-'+str(idx
)
239 vm_nic
= self
._create
_nic
(subnet_id
, nic_name
)
240 vm_nics
.append({ 'id': str(vm_nic
.id)})
244 'location': self
.region
,
246 'computer_name': vm_name
, # TODO if vm_name cannot be repeated add uuid4() suffix
247 'admin_username': 'sergio', # TODO is it mandatory???
248 'linuxConfiguration': {
249 'disablePasswordAuthentication': 'true',
253 'path': '/home/sergio/.ssh/authorized_keys',
254 'keyData': self
.pub_key
261 'hardware_profile': {
265 'image_reference': image_id
268 'network_interfaces': [
273 creation_result
= self
.conn_compute
.virtual_machines
.create_or_update(
279 run_command_parameters
= {
280 'command_id': 'RunShellScript', # For linux, don't change it
282 'date > /home/sergio/test.txt'
285 poller
= self
.conn_compute
.virtual_machines
.run_command(
288 run_command_parameters
290 # TODO return a tuple (vm-ID, None)
291 except Exception as e
:
292 self
.format_vimconn_exception(e
)
294 def get_flavor_id_from_data(self
, flavor_dict
):
295 self
.logger
.debug("Getting flavor id from data")
296 self
._reload
_connection
()
297 vm_sizes_list
= [vm_size
.serialize() for vm_size
in self
.conn_compute
.virtual_machine_sizes
.list(self
.region
)]
299 cpus
= flavor_dict
['vcpus']
300 memMB
= flavor_dict
['ram']
302 filteredSizes
= [size
for size
in vm_sizes_list
if size
['numberOfCores'] > cpus
and size
['memoryInMB'] > memMB
]
303 listedFilteredSizes
= sorted(filteredSizes
, key
=lambda k
: k
['numberOfCores'])
305 return listedFilteredSizes
[0]['name']
307 def check_vim_connectivity():
309 self
._reload
_connection
()
312 raise vimconn
.vimconnException("Connectivity issue with Azure API")
314 def get_network(self
, net_id
):
315 resGroup
= self
._get
_resource
_group
_name
_from
_resource
_id
(net_id
)
316 resName
= self
._get
_resource
_name
_from
_resource
_id
(net_id
)
318 self
._reload
_connection
()
319 vnet
= self
.conn_vnet
.virtual_networks
.get(resGroup
, resName
)
323 def delete_network(self
, net_id
):
324 resGroup
= self
._get
_resource
_group
_name
_from
_resource
_id
(net_id
)
325 resName
= self
._get
_resource
_name
_from
_resource
_id
(net_id
)
327 self
._reload
_connection
()
328 self
.conn_vnet
.virtual_networks
.delete(resGroup
, resName
)
330 def delete_vminstance(self
, vm_id
):
331 resGroup
= self
._get
_resource
_group
_name
_from
_resource
_id
(net_id
)
332 resName
= self
._get
_resource
_name
_from
_resource
_id
(net_id
)
334 self
._reload
_connection
()
335 self
.conn_compute
.virtual_machines
.delete(resGroup
, resName
)
337 def get_vminstance(self
, vm_id
):
338 resGroup
= self
._get
_resource
_group
_name
_from
_resource
_id
(net_id
)
339 resName
= self
._get
_resource
_name
_from
_resource
_id
(net_id
)
341 self
._reload
_connection
()
342 vm
=self
.conn_compute
.virtual_machines
.get(resGroup
, resName
)
346 def get_flavor(self
, flavor_id
):
347 self
._reload
_connection
()
348 for vm_size
in self
.conn_compute
.virtual_machine_sizes
.list(self
.region
):
349 if vm_size
.name
== flavor_id
:
353 # TODO refresh_nets_status ver estado activo
354 # TODO PRIORITARY get_image_list
355 # TODO refresh_vms_status ver estado activo
356 # TODO get_vminstance_console for getting console
358 if __name__
== "__main__":
360 # Making some basic test
363 needed_test_params
= {
364 "client_id": "AZURE_CLIENT_ID", # TODO delete private information
365 "secret": "AZURE_SECRET", # TODO delete private information
366 "tenant": "AZURE_TENANT", # TODO delete private information
367 "resource_group": "AZURE_RESOURCE_GROUP", # TODO delete private information # 'testOSMlive2',
368 # TODO maybe it should be created a resouce_group per VNFD
369 "subscription_id": "AZURE_SUBSCRIPTION_ID", # TODO delete private information 'ca3d18ab-d373-4afb-a5d6-7c44f098d16a'
370 "vnet_name": "AZURE_VNET_NAME",
374 for param
, env_var
in needed_test_params
.items():
375 value
= getenv(env_var
)
377 raise Exception("Provide a valid value for env '{}'".format(env_var
))
378 test_params
[param
] = value
381 'region_name': getenv("AZURE_REGION_NAME", 'westeurope'),
382 'resource_group': getenv("AZURE_RESOURCE_GROUP"),
383 'subscription_id': getenv("AZURE_SUBSCRIPTION_ID"),
384 'pub_key': getenv("AZURE_PUB_KEY", None), # TODO delete private information 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKlMlDqCEYmtD3NzHTzQXcu9Oj3U+CKYCU4D+kwEN5BuKs5J9lPFA9B2MsK9MYsyXoG4Gkt3ENHyzY+dgCN3eLdyiyOAtpHKddqO+5CG3mZoTlONTSofZm2pbnCoWh8UdKlBUvD467gFbw+HcBnXXY89zhdBIkhjQELcuZc0je8XsYrw++9DEJW9GBlREE8E/RustYlF5/MsNHvIxZqKNhBocX4Cj/nUdV+aGxTMa4pEnFi8gDA8xuYK9mDA/GNFd47TMa6kd+YLlojlfzp1GGDiwDK1px1TpjjzXan/dMMFbCsL5dgpuFul34U0yOdg7iEgoAUUwTGvHQsMyIl+BJ sergio@MININT-SCP2P2V',
385 'vnet_name': getenv("AZURE_VNET_NAME", 'myNetwork'),
390 'description':'new VM',
393 'publisher': 'Canonical',
394 'offer': 'UbuntuServer',
395 'sku': '16.04.0-LTS',
398 'hardware_profile': {
399 'vm_size': 'Standard_DS1_v2'
407 'subnet_address': '10.1.2.0/24',
408 #'subnet_name': 'subnet-oam'
410 ###########################
412 azure
=vimconnector(vim_id
, vim_name
, tenant_id
=test_params
["tenant"], tenant_name
=None, url
=None, url_admin
=None,
413 user
=test_params
["client_id"], passwd
=test_params
["secret"], log_level
=None, config
=config
)
415 #azure.get_flavor_id_from_data("here")
416 #subnets=azure.get_network_list()
417 #azure.new_vminstance(virtualMachine['name'], virtualMachine['description'], virtualMachine['status'], virtualMachine['image'], virtualMachine['hardware_profile']['vm_size'], subnets)
419 azure
.get_flavor("Standard_A11")