daf84631719fc1c13debec2f6341af28bfc77416
[osm/RO.git] / RO-VIM-azure / osm_rovim_azure / vimconn_azure.py
1 # -*- coding: utf-8 -*-
2 ##
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
4 # not use this file except in compliance with the License. You may obtain
5 # a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations
13 # under the License.
14 ##
15
16 import base64
17 from osm_ro import vimconn
18 import logging
19 import netaddr
20 import re
21
22 from os import getenv
23 from azure.common.credentials import ServicePrincipalCredentials
24 from azure.mgmt.resource import ResourceManagementClient
25 from azure.mgmt.network import NetworkManagementClient
26 from azure.mgmt.compute import ComputeManagementClient
27 from azure.mgmt.compute.models import DiskCreateOption
28 from msrestazure.azure_exceptions import CloudError
29 from msrest.exceptions import AuthenticationError
30 from requests.exceptions import ConnectionError
31
32 __author__ = 'Isabel Lloret, Sergio Gonzalez, Alfonso Tierno'
33 __date__ = '$18-apr-2019 23:59:59$'
34
35
36 if getenv('OSMRO_PDB_DEBUG'):
37 import sys
38 print(sys.path)
39 import pdb
40 pdb.set_trace()
41
42
43 class vimconnector(vimconn.vimconnector):
44
45 # Translate azure provisioning state to OSM provision state
46 # The first three ones are the transitional status once a user initiated action has been requested
47 # Once the operation is complete, it will transition into the states Succeeded or Failed
48 # https://docs.microsoft.com/en-us/azure/virtual-machines/windows/states-lifecycle
49 provision_state2osm = {
50 "Creating": "BUILD",
51 "Updating": "BUILD",
52 "Deleting": "INACTIVE",
53 "Succeeded": "ACTIVE",
54 "Failed": "ERROR"
55 }
56
57 # Translate azure power state to OSM provision state
58 power_state2osm = {
59 "starting": "INACTIVE",
60 "running": "ACTIVE",
61 "stopping": "INACTIVE",
62 "stopped": "INACTIVE",
63 "unknown": "OTHER",
64 "deallocated": "BUILD",
65 "deallocating": "BUILD"
66 }
67
68 def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level=None,
69 config={}, persistent_info={}):
70 """
71 Constructor of VIM. Raise an exception is some needed parameter is missing, but it must not do any connectivity
72 checking against the VIM
73 Using common constructor parameters.
74 In this case: config must include the following parameters:
75 subscription_id: assigned azure subscription identifier
76 region_name: current region for azure network
77 resource_group: used for all azure created resources
78 vnet_name: base vnet for azure, created networks will be subnets from this base network
79 config may also include the following parameter:
80 flavors_pattern: pattern that will be used to select a range of vm sizes, for example
81 "^((?!Standard_B).)*$" will filter out Standard_B range that is cheap but is very overused
82 "^Standard_B" will select a serie B maybe for test environment
83 """
84
85 vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level,
86 config, persistent_info)
87
88 # Variable that indicates if client must be reloaded or initialized
89 self.reload_client = True
90
91 self.vnet_address_space = None
92 # LOGGER
93 self.logger = logging.getLogger('openmano.vim.azure')
94 if log_level:
95 logging.basicConfig()
96 self.logger.setLevel(getattr(logging, log_level))
97
98 self.tenant = (tenant_id or tenant_name)
99
100 # Store config to create azure subscription later
101 self._config = {
102 "user": user,
103 "passwd": passwd,
104 "tenant": tenant_id or tenant_name
105 }
106
107 # SUBSCRIPTION
108 if 'subscription_id' in config:
109 self._config["subscription_id"] = config.get('subscription_id')
110 # self.logger.debug('Setting subscription to: %s', self.config["subscription_id"])
111 else:
112 raise vimconn.vimconnException('Subscription not specified')
113
114 # REGION
115 if 'region_name' in config:
116 self.region = config.get('region_name')
117 else:
118 raise vimconn.vimconnException('Azure region_name is not specified at config')
119
120 # RESOURCE_GROUP
121 if 'resource_group' in config:
122 self.resource_group = config.get('resource_group')
123 else:
124 raise vimconn.vimconnException('Azure resource_group is not specified at config')
125
126 # VNET_NAME
127 if 'vnet_name' in config:
128 self.vnet_name = config["vnet_name"]
129
130 # public ssh key
131 self.pub_key = config.get('pub_key')
132
133 # flavor pattern regex
134 if 'flavors_pattern' in config:
135 self._config['flavors_pattern'] = config['flavors_pattern']
136
137 def _reload_connection(self):
138 """
139 Called before any operation, checks python azure clients
140 """
141 if self.reload_client:
142 self.logger.debug('reloading azure client')
143 try:
144 self.credentials = ServicePrincipalCredentials(
145 client_id=self._config["user"],
146 secret=self._config["passwd"],
147 tenant=self._config["tenant"]
148 )
149 self.conn = ResourceManagementClient(self.credentials, self._config["subscription_id"])
150 self.conn_compute = ComputeManagementClient(self.credentials, self._config["subscription_id"])
151 self.conn_vnet = NetworkManagementClient(self.credentials, self._config["subscription_id"])
152 self._check_or_create_resource_group()
153 self._check_or_create_vnet()
154
155 # Set to client created
156 self.reload_client = False
157 except Exception as e:
158 self._format_vimconn_exception(e)
159
160 def _get_resource_name_from_resource_id(self, resource_id):
161 """
162 Obtains resource_name from the azure complete identifier: resource_name will always be last item
163 """
164 try:
165 resource = str(resource_id.split('/')[-1])
166 return resource
167 except Exception as e:
168 raise vimconn.vimconnException("Unable to get resource name from resource_id '{}' Error: '{}'".
169 format(resource_id, e))
170
171 def _get_location_from_resource_group(self, resource_group_name):
172 try:
173 location = self.conn.resource_groups.get(resource_group_name).location
174 return location
175 except Exception as e:
176 raise vimconn.vimconnNotFoundException("Location '{}' not found".format(resource_group_name))
177
178 def _get_resource_group_name_from_resource_id(self, resource_id):
179
180 try:
181 rg = str(resource_id.split('/')[4])
182 return rg
183 except Exception as e:
184 raise vimconn.vimconnException("Unable to get resource group from invalid resource_id format '{}'".
185 format(resource_id))
186
187 def _get_net_name_from_resource_id(self, resource_id):
188
189 try:
190 net_name = str(resource_id.split('/')[8])
191 return net_name
192 except Exception as e:
193 raise vimconn.vimconnException("Unable to get azure net_name from invalid resource_id format '{}'".
194 format(resource_id))
195
196 def _check_subnets_for_vm(self, net_list):
197 # All subnets must belong to the same resource group and vnet
198 rg_vnet = set(self._get_resource_group_name_from_resource_id(net['net_id']) +
199 self._get_net_name_from_resource_id(net['net_id']) for net in net_list)
200
201 if len(rg_vnet) != 1:
202 raise self._format_vimconn_exception('Azure VMs can only attach to subnets in same VNET')
203
204 def _format_vimconn_exception(self, e):
205 """
206 Transforms a generic or azure exception to a vimcommException
207 """
208 if isinstance(e, vimconn.vimconnException):
209 raise
210 elif isinstance(e, AuthenticationError):
211 raise vimconn.vimconnAuthException(type(e).__name__ + ': ' + str(e))
212 elif isinstance(e, ConnectionError):
213 raise vimconn.vimconnConnectionException(type(e).__name__ + ': ' + str(e))
214 else:
215 # In case of generic error recreate client
216 self.reload_client = True
217 raise vimconn.vimconnException(type(e).__name__ + ': ' + str(e))
218
219 def _check_or_create_resource_group(self):
220 """
221 Creates the base resource group if it does not exist
222 """
223 try:
224 rg_exists = self.conn.resource_groups.check_existence(self.resource_group)
225 if not rg_exists:
226 self.logger.debug("create base rgroup: %s", self.resource_group)
227 self.conn.resource_groups.create_or_update(self.resource_group, {'location': self.region})
228 except Exception as e:
229 self._format_vimconn_exception(e)
230
231 def _check_or_create_vnet(self):
232 """
233 Try to get existent base vnet, in case it does not exist it creates it
234 """
235 try:
236 vnet = self.conn_vnet.virtual_networks.get(self.resource_group, self.vnet_name)
237 self.vnet_address_space = vnet.address_space.address_prefixes[0]
238 self.vnet_id = vnet.id
239 return
240 except CloudError as e:
241 if e.error.error and "notfound" in e.error.error.lower():
242 pass
243 # continue and create it
244 else:
245 self._format_vimconn_exception(e)
246
247 # if it does not exist, create it
248 try:
249 vnet_params = {
250 'location': self.region,
251 'address_space': {
252 'address_prefixes': ["10.0.0.0/8"]
253 },
254 }
255 self.vnet_address_space = "10.0.0.0/8"
256
257 self.logger.debug("create base vnet: %s", self.vnet_name)
258 self.conn_vnet.virtual_networks.create_or_update(self.resource_group, self.vnet_name, vnet_params)
259 vnet = self.conn_vnet.virtual_networks.get(self.resource_group, self.vnet_name)
260 self.vnet_id = vnet.id
261 except Exception as e:
262 self._format_vimconn_exception(e)
263
264 def new_network(self, net_name, net_type, ip_profile=None, shared=False, provider_network_profile=None):
265 """
266 Adds a tenant network to VIM
267 :param net_name: name of the network
268 :param net_type: not used for azure networks
269 :param ip_profile: is a dict containing the IP parameters of the network (Currently only IPv4 is implemented)
270 'ip-version': can be one of ['IPv4','IPv6']
271 'subnet-address': ip_prefix_schema, that is X.X.X.X/Y
272 'gateway-address': (Optional) ip_schema, that is X.X.X.X, not implemented for azure connector
273 'dns-address': (Optional) ip_schema, not implemented for azure connector
274 'dhcp': (Optional) dict containing, not implemented for azure connector
275 'enabled': {'type': 'boolean'},
276 'start-address': ip_schema, first IP to grant
277 'count': number of IPs to grant.
278 :param shared: Not allowed for Azure Connector
279 :param provider_network_profile: (optional) contains {segmentation-id: vlan, provider-network: vim_netowrk}
280 :return: a tuple with the network identifier and created_items, or raises an exception on error
281 created_items can be None or a dictionary where this method can include key-values that will be passed to
282 the method delete_network. Can be used to store created segments, created l2gw connections, etc.
283 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
284 as not present.
285 """
286 return self._new_subnet(net_name, ip_profile)
287
288 def _new_subnet(self, net_name, ip_profile):
289 """
290 Adds a tenant network to VIM. It creates a new subnet at existing base vnet
291 :param net_name: subnet name
292 :param ip_profile:
293 subnet-address: if it is not provided a subnet/24 in the default vnet is created,
294 otherwise it creates a subnet in the indicated address
295 :return: a tuple with the network identifier and created_items, or raises an exception on error
296 """
297 self.logger.debug('create subnet name %s, ip_profile %s', net_name, ip_profile)
298 self._reload_connection()
299
300 if ip_profile is None:
301 # get a non used vnet ip range /24 and allocate automatically inside the range self.vnet_address_space
302 used_subnets = self.get_network_list()
303 for ip_range in netaddr.IPNetwork(self.vnet_address_space).subnet(24):
304 for used_subnet in used_subnets:
305 subnet_range = netaddr.IPNetwork(used_subnet["cidr_block"])
306 if subnet_range in ip_range or ip_range in subnet_range:
307 # this range overlaps with an existing subnet ip range. Breaks and look for another
308 break
309 else:
310 ip_profile = {"subnet_address": str(ip_range)}
311 self.logger.debug('dinamically obtained ip_profile: %s', ip_range)
312 break
313 else:
314 raise vimconn.vimconnException("Cannot find a non-used subnet range in {}".
315 format(self.vnet_address_space))
316 else:
317 ip_profile = {"subnet_address": ip_profile['subnet_address']}
318
319 try:
320 # subnet_name = "{}-{}".format(net_name[:24], uuid4())
321 subnet_params = {
322 'address_prefix': ip_profile['subnet_address']
323 }
324 # Assign a not duplicated net name
325 subnet_name = self._get_unused_subnet_name(net_name)
326
327 self.logger.debug('creating subnet_name: {}'.format(subnet_name))
328 async_creation = self.conn_vnet.subnets.create_or_update(self.resource_group, self.vnet_name,
329 subnet_name, subnet_params)
330 async_creation.wait()
331 self.logger.debug('created subnet_name: {}'.format(subnet_name))
332
333 return "{}/subnets/{}".format(self.vnet_id, subnet_name), None
334 except Exception as e:
335 self._format_vimconn_exception(e)
336
337 def _get_unused_subnet_name(self, subnet_name):
338 """
339 Adds a prefix to the subnet_name with a number in case the indicated name is repeated
340 Checks subnets with the indicated name (without suffix) and adds a suffix with a number
341 """
342 all_subnets = self.conn_vnet.subnets.list(self.resource_group, self.vnet_name)
343 # Filter to subnets starting with the indicated name
344 subnets = list(filter(lambda subnet: (subnet.name.startswith(subnet_name)), all_subnets))
345 net_names = [str(subnet.name) for subnet in subnets]
346
347 # get the name with the first not used suffix
348 name_suffix = 0
349 # name = subnet_name + "-" + str(name_suffix)
350 name = subnet_name # first subnet created will have no prefix
351 while name in net_names:
352 name_suffix += 1
353 name = subnet_name + "-" + str(name_suffix)
354 return name
355
356 def _create_nic(self, net, nic_name, static_ip=None):
357
358 self.logger.debug('create nic name %s, net_name %s', nic_name, net)
359 self._reload_connection()
360
361 subnet_id = net['net_id']
362 location = self._get_location_from_resource_group(self.resource_group)
363 try:
364 net_ifz = {'location': location}
365 net_ip_config = {'name': nic_name + '-ipconfiguration', 'subnet': {'id': subnet_id}}
366 if static_ip:
367 net_ip_config['privateIPAddress'] = static_ip
368 net_ip_config['privateIPAllocationMethod'] = 'Static'
369 net_ifz['ip_configurations'] = [net_ip_config]
370 mac_address = net.get('mac_address')
371 if mac_address:
372 net_ifz['mac_address'] = mac_address
373
374 async_nic_creation = self.conn_vnet.network_interfaces.create_or_update(self.resource_group, nic_name,
375 net_ifz)
376 async_nic_creation.wait()
377 self.logger.debug('created nic name %s', nic_name)
378
379 public_ip = net.get('floating_ip')
380 if public_ip:
381 public_ip_address_params = {
382 'location': location,
383 'public_ip_allocation_method': 'Dynamic'
384 }
385 public_ip_name = nic_name + '-public-ip'
386 public_ip = self.conn_vnet.public_ip_addresses.create_or_update(
387 self.resource_group,
388 public_ip_name,
389 public_ip_address_params
390 )
391 self.logger.debug('created public IP: {}'.format(public_ip.result()))
392
393 # Associate NIC to Public IP
394 nic_data = self.conn_vnet.network_interfaces.get(
395 self.resource_group,
396 nic_name)
397
398 nic_data.ip_configurations[0].public_ip_address = public_ip.result()
399
400 self.conn_vnet.network_interfaces.create_or_update(
401 self.resource_group,
402 nic_name,
403 nic_data)
404
405 except Exception as e:
406 self._format_vimconn_exception(e)
407
408 return async_nic_creation.result()
409
410 def new_flavor(self, flavor_data):
411 """
412 It is not allowed to create new flavors in Azure, must always use an existing one
413 """
414 raise vimconn.vimconnAuthException("It is not possible to create new flavors in AZURE")
415
416 def new_tenant(self, tenant_name, tenant_description):
417 """
418 It is not allowed to create new tenants in azure
419 """
420 raise vimconn.vimconnAuthException("It is not possible to create a TENANT in AZURE")
421
422 def new_image(self, image_dict):
423 """
424 It is not allowed to create new images in Azure, must always use an existing one
425 """
426 raise vimconn.vimconnAuthException("It is not possible to create new images in AZURE")
427
428 def get_image_id_from_path(self, path):
429 """Get the image id from image path in the VIM database.
430 Returns the image_id or raises a vimconnNotFoundException
431 """
432 raise vimconn.vimconnAuthException("It is not possible to obtain image from path in AZURE")
433
434 def get_image_list(self, filter_dict={}):
435 """Obtain tenant images from VIM
436 Filter_dict can be:
437 name: image name with the format: publisher:offer:sku:version
438 If some part of the name is provide ex: publisher:offer it will search all availables skus and version
439 for the provided publisher and offer
440 id: image uuid, currently not supported for azure
441 Returns the image list of dictionaries:
442 [{<the fields at Filter_dict plus some VIM specific>}, ...]
443 List can be empty
444 """
445
446 self.logger.debug("get_image_list filter {}".format(filter_dict))
447
448 self._reload_connection()
449 try:
450 image_list = []
451 if filter_dict.get("name"):
452 # name will have the format 'publisher:offer:sku:version'
453 # publisher is required, offer sku and version will be searched if not provided
454 params = filter_dict["name"].split(":")
455 publisher = params[0]
456 if publisher:
457 # obtain offer list
458 offer_list = self._get_offer_list(params, publisher)
459 for offer in offer_list:
460 # obtain skus
461 sku_list = self._get_sku_list(params, publisher, offer)
462 for sku in sku_list:
463 # if version is defined get directly version, else list images
464 if len(params) == 4 and params[3]:
465 version = params[3]
466 image_list = self._get_version_image_list(publisher, offer, sku, version)
467 else:
468 image_list = self._get_sku_image_list(publisher, offer, sku)
469 else:
470 raise vimconn.vimconnAuthException(
471 "List images in Azure must include name param with at least publisher")
472 else:
473 raise vimconn.vimconnAuthException("List images in Azure must include name param with at"
474 " least publisher")
475
476 return image_list
477 except Exception as e:
478 self._format_vimconn_exception(e)
479
480 def _get_offer_list(self, params, publisher):
481 """
482 Helper method to obtain offer list for defined publisher
483 """
484 if len(params) >= 2 and params[1]:
485 return [params[1]]
486 else:
487 try:
488 # get list of offers from azure
489 result_offers = self.conn_compute.virtual_machine_images.list_offers(self.region, publisher)
490 return [offer.name for offer in result_offers]
491 except CloudError as e:
492 # azure raises CloudError when not found
493 self.logger.info("error listing offers for publisher {}, Error: {}".format(publisher, e))
494 return []
495
496 def _get_sku_list(self, params, publisher, offer):
497 """
498 Helper method to obtain sku list for defined publisher and offer
499 """
500 if len(params) >= 3 and params[2]:
501 return [params[2]]
502 else:
503 try:
504 # get list of skus from azure
505 result_skus = self.conn_compute.virtual_machine_images.list_skus(self.region, publisher, offer)
506 return [sku.name for sku in result_skus]
507 except CloudError as e:
508 # azure raises CloudError when not found
509 self.logger.info("error listing skus for publisher {}, offer {}, Error: {}".format(publisher, offer, e))
510 return []
511
512 def _get_sku_image_list(self, publisher, offer, sku):
513 """
514 Helper method to obtain image list for publisher, offer and sku
515 """
516 image_list = []
517 try:
518 result_images = self.conn_compute.virtual_machine_images.list(self.region, publisher, offer, sku)
519 for result_image in result_images:
520 image_list.append({
521 'id': str(result_image.id),
522 'name': ":".join([publisher, offer, sku, result_image.name])
523 })
524 except CloudError as e:
525 self.logger.info(
526 "error listing skus for publisher {}, offer {}, Error: {}".format(publisher, offer, e))
527 image_list = []
528 return image_list
529
530 def _get_version_image_list(self, publisher, offer, sku, version):
531 image_list = []
532 try:
533 result_image = self.conn_compute.virtual_machine_images.get(self.region, publisher, offer, sku, version)
534 if result_image:
535 image_list.append({
536 'id': str(result_image.id),
537 'name': ":".join([publisher, offer, sku, version])
538 })
539 except CloudError as e:
540 # azure gives CloudError when not found
541 self.logger.info("error listing images for publisher {}, offer {}, sku {}, version {} Error: {}".
542 format(publisher, offer, sku, version, e))
543 image_list = []
544 return image_list
545
546 def get_network_list(self, filter_dict={}):
547 """Obtain tenant networks of VIM
548 Filter_dict can be:
549 name: network name
550 id: network id
551 shared: boolean, not implemented in Azure
552 tenant_id: tenant, not used in Azure, all networks same tenants
553 admin_state_up: boolean, not implemented in Azure
554 status: 'ACTIVE', not implemented in Azure #
555 Returns the network list of dictionaries
556 """
557 # self.logger.debug('getting network list for vim, filter %s', filter_dict)
558 try:
559 self._reload_connection()
560
561 vnet = self.conn_vnet.virtual_networks.get(self.resource_group, self.vnet_name)
562 subnet_list = []
563
564 for subnet in vnet.subnets:
565 if filter_dict:
566 if filter_dict.get("id") and str(subnet.id) != filter_dict["id"]:
567 continue
568 if filter_dict.get("name") and \
569 str(subnet.name) != filter_dict["name"]:
570 continue
571
572 name = self._get_resource_name_from_resource_id(subnet.id)
573
574 subnet_list.append({
575 'id': str(subnet.id),
576 'name': name,
577 'status': self.provision_state2osm[subnet.provisioning_state],
578 'cidr_block': str(subnet.address_prefix),
579 'type': 'bridge',
580 'shared': False
581 })
582
583 return subnet_list
584 except Exception as e:
585 self._format_vimconn_exception(e)
586
587 def new_vminstance(self, name, description, start, image_id, flavor_id, net_list, cloud_config=None,
588 disk_list=None, availability_zone_index=None, availability_zone_list=None):
589
590 self.logger.debug("new vm instance name: %s, image_id: %s, flavor_id: %s, net_list: %s, cloud_config: %s, "
591 "disk_list: %s, availability_zone_index: %s, availability_zone_list: %s",
592 name, image_id, flavor_id, net_list, cloud_config, disk_list,
593 availability_zone_index, availability_zone_list)
594
595 self._reload_connection()
596
597 # Validate input data is valid
598 # The virtual machine name must have less or 64 characters and it can not have the following
599 # characters: (~ ! @ # $ % ^ & * ( ) = + _ [ ] { } \ | ; : ' " , < > / ?.)
600 vm_name = self._check_vm_name(name)
601 # Obtain vm unused name
602 vm_name = self._get_unused_vm_name(vm_name)
603
604 # At least one network must be provided
605 if not net_list:
606 raise vimconn.vimconnException("At least one net must be provided to create a new VM")
607
608 # image_id are several fields of the image_id
609 image_reference = self._get_image_reference(image_id)
610
611 self._check_subnets_for_vm(net_list)
612 vm_nics = []
613 for idx, net in enumerate(net_list):
614 # Fault with subnet_id
615 # subnet_id=net['subnet_id']
616 # subnet_id=net['net_id']
617 nic_name = vm_name + '-nic-'+str(idx)
618 vm_nic = self._create_nic(net, nic_name, net.get('ip_address'))
619 vm_nics.append({'id': str(vm_nic.id)})
620 net['vim_id'] = vm_nic.id
621
622 try:
623
624 # cloud-init configuration
625 # cloud config
626 if cloud_config:
627 config_drive, userdata = self._create_user_data(cloud_config)
628 custom_data = base64.b64encode(userdata.encode('utf-8')).decode('latin-1')
629 key_data = None
630 key_pairs = cloud_config.get("key-pairs")
631 if key_pairs:
632 key_data = key_pairs[0]
633
634 if cloud_config.get("users"):
635 user_name = cloud_config.get("users")[0].get("name", "osm")
636 else:
637 user_name = "osm" # DEFAULT USER IS OSM
638
639 os_profile = {
640 'computer_name': vm_name,
641 'admin_username': user_name,
642 'linux_configuration': {
643 "disable_password_authentication": True,
644 "ssh": {
645 "public_keys": [{
646 "path": "/home/{}/.ssh/authorized_keys".format(user_name),
647 "key_data": key_data
648 }]
649 }
650 },
651 'custom_data': custom_data
652 }
653 else:
654 os_profile = {
655 'computer_name': vm_name,
656 'admin_username': 'osm',
657 'admin_password': 'Osm4u!',
658 }
659
660 vm_parameters = {
661 'location': self.region,
662 'os_profile': os_profile,
663 'hardware_profile': {
664 'vm_size': flavor_id
665 },
666 'storage_profile': {
667 'image_reference': image_reference
668 }
669 }
670
671 # Add data disks if they are provided
672 if disk_list:
673 data_disks = []
674 for lun_name, disk in enumerate(disk_list):
675 self.logger.debug("add disk size: %s, image: %s", disk.get("size"), disk.get("image_id"))
676 if not disk.get("image_id"):
677 data_disks.append({
678 'lun': lun_name, # You choose the value, depending of what is available for you
679 'name': vm_name + "_data_disk-" + str(lun_name),
680 'create_option': DiskCreateOption.empty,
681 'disk_size_gb': disk.get("size")
682 })
683 else:
684 # self.logger.debug("currently not able to create data disks from image for azure, ignoring")
685 data_disks.append({
686 'lun': lun_name, # You choose the value, depending of what is available for you
687 'name': vm_name + "_data_disk-" + str(lun_name),
688 'create_option': 'Attach',
689 'disk_size_gb': disk.get("size"),
690 'managed_disk': {
691 'id': disk.get("image_id")
692 }
693 })
694
695 if data_disks:
696 vm_parameters["storage_profile"]["data_disks"] = data_disks
697
698 # If the machine has several networks one must be marked as primary
699 # As it is not indicated in the interface the first interface will be marked as primary
700 if len(vm_nics) > 1:
701 for idx, vm_nic in enumerate(vm_nics):
702 if idx == 0:
703 vm_nics[0]['Primary'] = True
704 else:
705 vm_nics[idx]['Primary'] = False
706
707 vm_parameters['network_profile'] = {'network_interfaces': vm_nics}
708
709 self.logger.debug("create vm name: %s", vm_name)
710 creation_result = self.conn_compute.virtual_machines.create_or_update(
711 self.resource_group,
712 vm_name,
713 vm_parameters
714 )
715 # creation_result.wait()
716 result = creation_result.result()
717 self.logger.debug("created vm name: %s", vm_name)
718
719 if start:
720 self.conn_compute.virtual_machines.start(
721 self.resource_group,
722 vm_name)
723 # start_result.wait()
724
725 return result.id, None
726
727 # run_command_parameters = {
728 # 'command_id': 'RunShellScript', # For linux, don't change it
729 # 'script': [
730 # 'date > /tmp/test.txt'
731 # ]
732 # }
733 except Exception as e:
734 self.logger.debug('Exception creating new vminstance: %s', e, exc_info=True)
735 self._format_vimconn_exception(e)
736
737 def _get_unused_vm_name(self, vm_name):
738 """
739 Checks the vm name and in case it is used adds a suffix to the name to allow creation
740 :return:
741 """
742 all_vms = self.conn_compute.virtual_machines.list(self.resource_group)
743 # Filter to vms starting with the indicated name
744 vms = list(filter(lambda vm: (vm.name.startswith(vm_name)), all_vms))
745 vm_names = [str(vm.name) for vm in vms]
746
747 # get the name with the first not used suffix
748 name_suffix = 0
749 # name = subnet_name + "-" + str(name_suffix)
750 name = vm_name # first subnet created will have no prefix
751 while name in vm_names:
752 name_suffix += 1
753 name = vm_name + "-" + str(name_suffix)
754 return name
755
756 # It is necesary extract from image_id data to create the VM with this format
757 # 'image_reference': {
758 # 'publisher': vm_reference['publisher'],
759 # 'offer': vm_reference['offer'],
760 # 'sku': vm_reference['sku'],
761 # 'version': vm_reference['version']
762 # },
763 def _get_image_reference(self, image_id):
764
765 try:
766 # The data input format example:
767 # /Subscriptions/ca3d18ab-d373-4afb-a5d6-7c44f098d16a/Providers/Microsoft.Compute/Locations/westeurope/
768 # Publishers/Canonical/ArtifactTypes/VMImage/
769 # Offers/UbuntuServer/
770 # Skus/18.04-LTS/
771 # Versions/18.04.201809110
772 publisher = str(image_id.split('/')[8])
773 offer = str(image_id.split('/')[12])
774 sku = str(image_id.split('/')[14])
775 version = str(image_id.split('/')[16])
776
777 return {
778 'publisher': publisher,
779 'offer': offer,
780 'sku': sku,
781 'version': version
782 }
783 except Exception as e:
784 raise vimconn.vimconnException(
785 "Unable to get image_reference from invalid image_id format: '{}'".format(image_id))
786
787 # Azure VM names can not have some special characters
788 def _check_vm_name(self, vm_name):
789 """
790 Checks vm name, in case the vm has not allowed characters they are removed, not error raised
791 """
792
793 chars_not_allowed_list = "~!@#$%^&*()=+_[]{}|;:<>/?."
794
795 # First: the VM name max length is 64 characters
796 vm_name_aux = vm_name[:64]
797
798 # Second: replace not allowed characters
799 for elem in chars_not_allowed_list:
800 # Check if string is in the main string
801 if elem in vm_name_aux:
802 # self.logger.debug('Dentro del IF')
803 # Replace the string
804 vm_name_aux = vm_name_aux.replace(elem, '-')
805
806 return vm_name_aux
807
808 def get_flavor_id_from_data(self, flavor_dict):
809
810 self.logger.debug("getting flavor id from data, flavor_dict: %s", flavor_dict)
811 filter_dict = flavor_dict or {}
812 try:
813 self._reload_connection()
814 vm_sizes_list = [vm_size.serialize() for vm_size in
815 self.conn_compute.virtual_machine_sizes.list(self.region)]
816
817 cpus = filter_dict.get('vcpus') or 0
818 memMB = filter_dict.get('ram') or 0
819
820 # Filter
821 if self._config.get("flavors_pattern"):
822 filtered_sizes = [size for size in vm_sizes_list if size['numberOfCores'] >= cpus and
823 size['memoryInMB'] >= memMB and
824 re.search(self._config.get("flavors_pattern"), size["name"])]
825 else:
826 filtered_sizes = [size for size in vm_sizes_list if size['numberOfCores'] >= cpus and
827 size['memoryInMB'] >= memMB]
828
829 # Sort
830 listedFilteredSizes = sorted(filtered_sizes, key=lambda k: (k['numberOfCores'], k['memoryInMB'],
831 k['resourceDiskSizeInMB']))
832
833 if listedFilteredSizes:
834 return listedFilteredSizes[0]['name']
835 raise vimconn.vimconnNotFoundException("Cannot find any flavor matching '{}'".format(str(flavor_dict)))
836
837 except Exception as e:
838 self._format_vimconn_exception(e)
839
840 def _get_flavor_id_from_flavor_name(self, flavor_name):
841
842 # self.logger.debug("getting flavor id from flavor name {}".format(flavor_name))
843 try:
844 self._reload_connection()
845 vm_sizes_list = [vm_size.serialize() for vm_size in
846 self.conn_compute.virtual_machine_sizes.list(self.region)]
847
848 output_flavor = None
849 for size in vm_sizes_list:
850 if size['name'] == flavor_name:
851 output_flavor = size
852
853 # None is returned if not found anything
854 return output_flavor
855
856 except Exception as e:
857 self._format_vimconn_exception(e)
858
859 def check_vim_connectivity(self):
860 try:
861 self._reload_connection()
862 return True
863 except Exception as e:
864 raise vimconn.vimconnException("Connectivity issue with Azure API: {}".format(e))
865
866 def get_network(self, net_id):
867
868 # self.logger.debug('get network id: {}'.format(net_id))
869 # res_name = self._get_resource_name_from_resource_id(net_id)
870 self._reload_connection()
871
872 filter_dict = {'name': net_id}
873 network_list = self.get_network_list(filter_dict)
874
875 if not network_list:
876 raise vimconn.vimconnNotFoundException("network '{}' not found".format(net_id))
877 else:
878 return network_list[0]
879
880 def delete_network(self, net_id, created_items=None):
881
882 self.logger.debug('deleting network {} - {}'.format(self.resource_group, net_id))
883
884 self._reload_connection()
885 res_name = self._get_resource_name_from_resource_id(net_id)
886 filter_dict = {'name': res_name}
887 network_list = self.get_network_list(filter_dict)
888 if not network_list:
889 raise vimconn.vimconnNotFoundException("network '{}' not found".format(net_id))
890
891 try:
892 # Subnet API fails (CloudError: Azure Error: ResourceNotFound)
893 # Put the initial virtual_network API
894 async_delete = self.conn_vnet.subnets.delete(self.resource_group, self.vnet_name, res_name)
895 async_delete.wait()
896 return net_id
897
898 except CloudError as e:
899 if e.error.error and "notfound" in e.error.error.lower():
900 raise vimconn.vimconnNotFoundException("network '{}' not found".format(net_id))
901 else:
902 self._format_vimconn_exception(e)
903 except Exception as e:
904 self._format_vimconn_exception(e)
905
906 def delete_vminstance(self, vm_id, created_items=None):
907 """ Deletes a vm instance from the vim.
908 """
909 self.logger.debug('deleting VM instance {} - {}'.format(self.resource_group, vm_id))
910 self._reload_connection()
911
912 try:
913
914 res_name = self._get_resource_name_from_resource_id(vm_id)
915 vm = self.conn_compute.virtual_machines.get(self.resource_group, res_name)
916
917 # Shuts down the virtual machine and releases the compute resources
918 # vm_stop = self.conn_compute.virtual_machines.power_off(self.resource_group, resName)
919 # vm_stop.wait()
920
921 vm_delete = self.conn_compute.virtual_machines.delete(self.resource_group, res_name)
922 vm_delete.wait()
923 self.logger.debug('deleted VM name: %s', res_name)
924
925 # Delete OS Disk
926 os_disk_name = vm.storage_profile.os_disk.name
927 self.logger.debug('delete OS DISK: %s', os_disk_name)
928 self.conn_compute.disks.delete(self.resource_group, os_disk_name)
929 self.logger.debug('deleted OS DISK name: %s', os_disk_name)
930
931 for data_disk in vm.storage_profile.data_disks:
932 self.logger.debug('delete data_disk: %s', data_disk.name)
933 self.conn_compute.disks.delete(self.resource_group, data_disk.name)
934 self.logger.debug('deleted OS DISK name: %s', data_disk.name)
935
936 # After deleting VM, it is necessary to delete NIC, because if is not deleted delete_network
937 # does not work because Azure says that is in use the subnet
938 network_interfaces = vm.network_profile.network_interfaces
939
940 for network_interface in network_interfaces:
941
942 nic_name = self._get_resource_name_from_resource_id(network_interface.id)
943 nic_data = self.conn_vnet.network_interfaces.get(
944 self.resource_group,
945 nic_name)
946
947 public_ip_name = None
948 exist_public_ip = nic_data.ip_configurations[0].public_ip_address
949 if exist_public_ip:
950 public_ip_id = nic_data.ip_configurations[0].public_ip_address.id
951
952 # Delete public_ip
953 public_ip_name = self._get_resource_name_from_resource_id(public_ip_id)
954
955 # Public ip must be deleted afterwards of nic that is attached
956
957 self.logger.debug('delete NIC name: %s', nic_name)
958 nic_delete = self.conn_vnet.network_interfaces.delete(self.resource_group, nic_name)
959 nic_delete.wait()
960 self.logger.debug('deleted NIC name: %s', nic_name)
961
962 # Delete list of public ips
963 if public_ip_name:
964 self.logger.debug('delete PUBLIC IP - ' + public_ip_name)
965 self.conn_vnet.public_ip_addresses.delete(self.resource_group, public_ip_name)
966
967 except CloudError as e:
968 if e.error.error and "notfound" in e.error.error.lower():
969 raise vimconn.vimconnNotFoundException("No vm instance found '{}'".format(vm_id))
970 else:
971 self._format_vimconn_exception(e)
972 except Exception as e:
973 self._format_vimconn_exception(e)
974
975 def action_vminstance(self, vm_id, action_dict, created_items={}):
976 """Send and action over a VM instance from VIM
977 Returns the vm_id if the action was successfully sent to the VIM
978 """
979
980 self.logger.debug("Action over VM '%s': %s", vm_id, str(action_dict))
981 try:
982 self._reload_connection()
983 resName = self._get_resource_name_from_resource_id(vm_id)
984 if "start" in action_dict:
985 self.conn_compute.virtual_machines.start(self.resource_group, resName)
986 elif "stop" in action_dict or "shutdown" in action_dict or "shutoff" in action_dict:
987 self.conn_compute.virtual_machines.power_off(self.resource_group, resName)
988 elif "terminate" in action_dict:
989 self.conn_compute.virtual_machines.delete(self.resource_group, resName)
990 elif "reboot" in action_dict:
991 self.conn_compute.virtual_machines.restart(self.resource_group, resName)
992 return None
993 except CloudError as e:
994 if e.error.error and "notfound" in e.error.error.lower():
995 raise vimconn.vimconnNotFoundException("No vm found '{}'".format(vm_id))
996 else:
997 self._format_vimconn_exception(e)
998 except Exception as e:
999 self._format_vimconn_exception(e)
1000
1001 def delete_flavor(self, flavor_id):
1002 raise vimconn.vimconnAuthException("It is not possible to delete a FLAVOR in AZURE")
1003
1004 def delete_tenant(self, tenant_id,):
1005 raise vimconn.vimconnAuthException("It is not possible to delete a TENANT in AZURE")
1006
1007 def delete_image(self, image_id):
1008 raise vimconn.vimconnAuthException("It is not possible to delete a IMAGE in AZURE")
1009
1010 def get_vminstance(self, vm_id):
1011 """
1012 Obtaing the vm instance data from v_id
1013 """
1014 self.logger.debug("get vm instance: %s", vm_id)
1015 self._reload_connection()
1016 try:
1017 resName = self._get_resource_name_from_resource_id(vm_id)
1018 vm = self.conn_compute.virtual_machines.get(self.resource_group, resName)
1019 except CloudError as e:
1020 if e.error.error and "notfound" in e.error.error.lower():
1021 raise vimconn.vimconnNotFoundException("No vminstance found '{}'".format(vm_id))
1022 else:
1023 self._format_vimconn_exception(e)
1024 except Exception as e:
1025 self._format_vimconn_exception(e)
1026
1027 return vm
1028
1029 def get_flavor(self, flavor_id):
1030 """
1031 Obtains the flavor_data from the flavor_id
1032 """
1033 self._reload_connection()
1034 self.logger.debug("get flavor from id: %s", flavor_id)
1035 flavor_data = self._get_flavor_id_from_flavor_name(flavor_id)
1036 if flavor_data:
1037 flavor = {
1038 'id': flavor_id,
1039 'name': flavor_id,
1040 'ram': flavor_data['memoryInMB'],
1041 'vcpus': flavor_data['numberOfCores'],
1042 'disk': flavor_data['resourceDiskSizeInMB']/1024
1043 }
1044 return flavor
1045 else:
1046 raise vimconn.vimconnNotFoundException("flavor '{}' not found".format(flavor_id))
1047
1048 def get_tenant_list(self, filter_dict={}):
1049 """ Obtains the list of tenants
1050 For the azure connector only the azure tenant will be returned if it is compatible
1051 with filter_dict
1052 """
1053 tenants_azure = [{'name': self.tenant, 'id': self.tenant}]
1054 tenant_list = []
1055
1056 self.logger.debug("get tenant list: %s", filter_dict)
1057 for tenant_azure in tenants_azure:
1058 if filter_dict:
1059 if filter_dict.get("id") and str(tenant_azure.get("id")) != filter_dict["id"]:
1060 continue
1061 if filter_dict.get("name") and str(tenant_azure.get("name")) != filter_dict["name"]:
1062 continue
1063
1064 tenant_list.append(tenant_azure)
1065
1066 return tenant_list
1067
1068 def refresh_nets_status(self, net_list):
1069 """Get the status of the networks
1070 Params: the list of network identifiers
1071 Returns a dictionary with:
1072 net_id: #VIM id of this network
1073 status: #Mandatory. Text with one of:
1074 # DELETED (not found at vim)
1075 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1076 # OTHER (Vim reported other status not understood)
1077 # ERROR (VIM indicates an ERROR status)
1078 # ACTIVE, INACTIVE, DOWN (admin down),
1079 # BUILD (on building process)
1080 #
1081 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1082 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1083
1084 """
1085
1086 out_nets = {}
1087 self._reload_connection()
1088
1089 self.logger.debug("reload nets status net_list: %s", net_list)
1090 for net_id in net_list:
1091 try:
1092 netName = self._get_net_name_from_resource_id(net_id)
1093 resName = self._get_resource_name_from_resource_id(net_id)
1094
1095 net = self.conn_vnet.subnets.get(self.resource_group, netName, resName)
1096
1097 out_nets[net_id] = {
1098 "status": self.provision_state2osm[net.provisioning_state],
1099 "vim_info": str(net)
1100 }
1101 except CloudError as e:
1102 if e.error.error and "notfound" in e.error.error.lower():
1103 self.logger.info("Not found subnet net_name: %s, subnet_name: %s", netName, resName)
1104 out_nets[net_id] = {
1105 "status": "DELETED",
1106 "error_msg": str(e)
1107 }
1108 else:
1109 self.logger.error("CloudError Exception %s when searching subnet", e)
1110 out_nets[net_id] = {
1111 "status": "VIM_ERROR",
1112 "error_msg": str(e)
1113 }
1114 except vimconn.vimconnNotFoundException as e:
1115 self.logger.error("VimConnNotFoundException %s when searching subnet", e)
1116 out_nets[net_id] = {
1117 "status": "DELETED",
1118 "error_msg": str(e)
1119 }
1120 except Exception as e:
1121 self.logger.error("Exception %s when searching subnet", e, exc_info=True)
1122 out_nets[net_id] = {
1123 "status": "VIM_ERROR",
1124 "error_msg": str(e)
1125 }
1126 return out_nets
1127
1128 def refresh_vms_status(self, vm_list):
1129 """ Get the status of the virtual machines and their interfaces/ports
1130 Params: the list of VM identifiers
1131 Returns a dictionary with:
1132 vm_id: # VIM id of this Virtual Machine
1133 status: # Mandatory. Text with one of:
1134 # DELETED (not found at vim)
1135 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1136 # OTHER (Vim reported other status not understood)
1137 # ERROR (VIM indicates an ERROR status)
1138 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1139 # BUILD (on building process), ERROR
1140 # ACTIVE:NoMgmtIP (Active but none of its interfaces has an IP address
1141 # (ACTIVE:NoMgmtIP is not returned for Azure)
1142 #
1143 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1144 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1145 interfaces: list with interface info. Each item a dictionary with:
1146 vim_interface_id - The ID of the interface
1147 mac_address - The MAC address of the interface.
1148 ip_address - The IP address of the interface within the subnet.
1149 """
1150
1151 out_vms = {}
1152 self._reload_connection()
1153
1154 self.logger.debug("refresh vm status vm_list: %s", vm_list)
1155 search_vm_list = vm_list or {}
1156
1157 for vm_id in search_vm_list:
1158 out_vm = {}
1159 try:
1160 res_name = self._get_resource_name_from_resource_id(vm_id)
1161
1162 vm = self.conn_compute.virtual_machines.get(self.resource_group, res_name)
1163 out_vm['vim_info'] = str(vm)
1164 out_vm['status'] = self.provision_state2osm.get(vm.provisioning_state, 'OTHER')
1165 if vm.provisioning_state == 'Succeeded':
1166 # check if machine is running or stopped
1167 instance_view = self.conn_compute.virtual_machines.instance_view(self.resource_group,
1168 res_name)
1169 for status in instance_view.statuses:
1170 splitted_status = status.code.split("/")
1171 if len(splitted_status) == 2 and splitted_status[0] == 'PowerState':
1172 out_vm['status'] = self.power_state2osm.get(splitted_status[1], 'OTHER')
1173
1174 network_interfaces = vm.network_profile.network_interfaces
1175 out_vm['interfaces'] = self._get_vm_interfaces_status(vm_id, network_interfaces)
1176
1177 except CloudError as e:
1178 if e.error.error and "notfound" in e.error.error.lower():
1179 self.logger.debug("Not found vm id: %s", vm_id)
1180 out_vm['status'] = "DELETED"
1181 out_vm['error_msg'] = str(e)
1182 out_vm['vim_info'] = None
1183 else:
1184 # maybe connection error or another type of error, return vim error
1185 self.logger.error("Exception %s refreshing vm_status", e)
1186 out_vm['status'] = "VIM_ERROR"
1187 out_vm['error_msg'] = str(e)
1188 out_vm['vim_info'] = None
1189 except Exception as e:
1190 self.logger.error("Exception %s refreshing vm_status", e, exc_info=True)
1191 out_vm['status'] = "VIM_ERROR"
1192 out_vm['error_msg'] = str(e)
1193 out_vm['vim_info'] = None
1194
1195 out_vms[vm_id] = out_vm
1196
1197 return out_vms
1198
1199 def _get_vm_interfaces_status(self, vm_id, interfaces):
1200 """
1201 Gets the interfaces detail for a vm
1202 :param interfaces: List of interfaces.
1203 :return: Dictionary with list of interfaces including, vim_interface_id, mac_address and ip_address
1204 """
1205 try:
1206 interface_list = []
1207 for network_interface in interfaces:
1208 interface_dict = {}
1209 nic_name = self._get_resource_name_from_resource_id(network_interface.id)
1210 interface_dict['vim_interface_id'] = network_interface.id
1211
1212 nic_data = self.conn_vnet.network_interfaces.get(
1213 self.resource_group,
1214 nic_name)
1215
1216 ips = []
1217 if nic_data.ip_configurations[0].public_ip_address:
1218 self.logger.debug("Obtain public ip address")
1219 public_ip_name = self._get_resource_name_from_resource_id(
1220 nic_data.ip_configurations[0].public_ip_address.id)
1221 public_ip = self.conn_vnet.public_ip_addresses.get(self.resource_group, public_ip_name)
1222 self.logger.debug("Public ip address is: %s", public_ip.ip_address)
1223 ips.append(public_ip.ip_address)
1224
1225 private_ip = nic_data.ip_configurations[0].private_ip_address
1226 ips.append(private_ip)
1227
1228 interface_dict['mac_address'] = nic_data.mac_address
1229 interface_dict['ip_address'] = ";".join(ips)
1230 interface_list.append(interface_dict)
1231
1232 return interface_list
1233 except Exception as e:
1234 self.logger.error("Exception %s obtaining interface data for vm: %s, error: %s", vm_id, e, exc_info=True)
1235 self._format_vimconn_exception(e)
1236
1237
1238 if __name__ == "__main__":
1239
1240 # Making some basic test
1241 vim_id = 'azure'
1242 vim_name = 'azure'
1243 needed_test_params = {
1244 "client_id": "AZURE_CLIENT_ID",
1245 "secret": "AZURE_SECRET",
1246 "tenant": "AZURE_TENANT",
1247 "resource_group": "AZURE_RESOURCE_GROUP",
1248 "subscription_id": "AZURE_SUBSCRIPTION_ID",
1249 "vnet_name": "AZURE_VNET_NAME",
1250 }
1251 test_params = {}
1252
1253 for param, env_var in needed_test_params.items():
1254 value = getenv(env_var)
1255 if not value:
1256 raise Exception("Provide a valid value for env '{}'".format(env_var))
1257 test_params[param] = value
1258
1259 config = {
1260 'region_name': getenv("AZURE_REGION_NAME", 'westeurope'),
1261 'resource_group': getenv("AZURE_RESOURCE_GROUP"),
1262 'subscription_id': getenv("AZURE_SUBSCRIPTION_ID"),
1263 'pub_key': getenv("AZURE_PUB_KEY", None),
1264 'vnet_name': getenv("AZURE_VNET_NAME", 'myNetwork'),
1265 }
1266
1267 virtualMachine = {
1268 'name': 'sergio',
1269 'description': 'new VM',
1270 'status': 'running',
1271 'image': {
1272 'publisher': 'Canonical',
1273 'offer': 'UbuntuServer',
1274 'sku': '16.04.0-LTS',
1275 'version': 'latest'
1276 },
1277 'hardware_profile': {
1278 'vm_size': 'Standard_DS1_v2'
1279 },
1280 'networks': [
1281 'sergio'
1282 ]
1283 }
1284
1285 vnet_config = {
1286 'subnet_address': '10.1.2.0/24',
1287 # 'subnet_name': 'subnet-oam'
1288 }
1289 ###########################
1290
1291 azure = vimconnector(vim_id, vim_name, tenant_id=test_params["tenant"], tenant_name=None, url=None, url_admin=None,
1292 user=test_params["client_id"], passwd=test_params["secret"], log_level=None, config=config)
1293
1294 # azure.get_flavor_id_from_data("here")
1295 # subnets=azure.get_network_list()
1296 # azure.new_vminstance(virtualMachine['name'], virtualMachine['description'], virtualMachine['status'],
1297 # virtualMachine['image'], virtualMachine['hardware_profile']['vm_size'], subnets)
1298
1299 azure.new_network("mynet", None)
1300 net_id = "/subscriptions/82f80cc1-876b-4591-9911-1fb5788384fd/resourceGroups/osmRG/providers/Microsoft."\
1301 "Network/virtualNetworks/test"
1302 net_id_not_found = "/subscriptions/82f80cc1-876b-4591-9911-1fb5788384fd/resourceGroups/osmRG/providers/"\
1303 "Microsoft.Network/virtualNetworks/testALF"
1304 azure.refresh_nets_status([net_id, net_id_not_found])