blob: 59bd535f91d4875a06c9cb42098db6cac77f279d [file] [log] [blame]
seryio34478552019-05-23 14:50:49 +02001# -*- coding: utf-8 -*-
jamartinezv14a823d2019-08-01 11:45:15 +02002import base64
3
lloretgalleg45220152019-10-29 11:53:49 +01004
5
seryio34478552019-05-23 14:50:49 +02006import vimconn
7import logging
tierno24620412019-06-03 14:05:08 +00008import netaddr
lloretgalleg45220152019-10-29 11:53:49 +01009import re
seryio34478552019-05-23 14:50:49 +020010
11from os import getenv
seryio34478552019-05-23 14:50:49 +020012from azure.common.credentials import ServicePrincipalCredentials
13from azure.mgmt.resource import ResourceManagementClient
14from azure.mgmt.network import NetworkManagementClient
15from azure.mgmt.compute import ComputeManagementClient
lloretgalleg45220152019-10-29 11:53:49 +010016from azure.mgmt.compute.v2019_03_01.models import DiskCreateOptionTypes
tierno84efdc12019-05-29 09:29:01 +000017from msrestazure.azure_exceptions import CloudError
lloretgalleg45220152019-10-29 11:53:49 +010018from msrest.exceptions import AuthenticationError
19from requests.exceptions import ConnectionError
20
21__author__ = 'Sergio Gonzalez'
22__date__ = '$18-apr-2019 23:59:59$'
23
tiernodeb74b22019-05-27 10:24:50 +000024
jamartinezv14a823d2019-08-01 11:45:15 +020025if getenv('OSMRO_PDB_DEBUG'):
26 import sys
27 print(sys.path)
28 import pdb
29 pdb.set_trace()
30
31
seryio34478552019-05-23 14:50:49 +020032class vimconnector(vimconn.vimconnector):
33
lloretgalleg45220152019-10-29 11:53:49 +010034 # Translate azure provisioning state to OSM provision state
35 # The first three ones are the transitional status once a user initiated action has been requested
36 # Once the operation is complete, it will transition into the states Succeeded or Failed
37 # https://docs.microsoft.com/en-us/azure/virtual-machines/windows/states-lifecycle
tierno84efdc12019-05-29 09:29:01 +000038 provision_state2osm = {
lloretgalleg45220152019-10-29 11:53:49 +010039 "Creating": "BUILD",
tierno84efdc12019-05-29 09:29:01 +000040 "Updating": "BUILD",
lloretgalleg45220152019-10-29 11:53:49 +010041 "Deleting": "INACTIVE",
42 "Succeeded": "ACTIVE",
43 "Failed": "ERROR"
44 }
45
46 # Translate azure power state to OSM provision state
47 power_state2osm = {
48 "starting": "INACTIVE",
49 "running": "ACTIVE",
50 "stopping": "INACTIVE",
51 "stopped": "INACTIVE",
52 "unknown": "OTHER",
53 "deallocated": "BUILD",
54 "deallocating": "BUILD"
tierno84efdc12019-05-29 09:29:01 +000055 }
56
seryio34478552019-05-23 14:50:49 +020057 def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level=None,
58 config={}, persistent_info={}):
lloretgalleg45220152019-10-29 11:53:49 +010059 """
60 Constructor of VIM. Raise an exception is some needed parameter is missing, but it must not do any connectivity
61 checking against the VIM
62 Using common constructor parameters.
63 In this case: config must include the following parameters:
64 subscription_id: assigned azure subscription identifier
65 region_name: current region for azure network
66 resource_group: used for all azure created resources
67 vnet_name: base vnet for azure, created networks will be subnets from this base network
68 config may also include the following parameter:
69 flavors_pattern: pattern that will be used to select a range of vm sizes, for example
70 "^((?!Standard_B).)*$" will filter out Standard_B range that is cheap but is very overused
71 "^Standard_B" will select a serie B maybe for test environment
72 """
seryio34478552019-05-23 14:50:49 +020073
74 vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level,
75 config, persistent_info)
76
lloretgalleg45220152019-10-29 11:53:49 +010077 # Variable that indicates if client must be reloaded or initialized
78 self.reload_client = True
79
tierno84efdc12019-05-29 09:29:01 +000080 self.vnet_address_space = None
seryio34478552019-05-23 14:50:49 +020081 # LOGGER
82 self.logger = logging.getLogger('openmano.vim.azure')
83 if log_level:
84 logging.basicConfig()
85 self.logger.setLevel(getattr(logging, log_level))
86
lloretgalleg45220152019-10-29 11:53:49 +010087 self.tenant = (tenant_id or tenant_name)
tierno30d0d6d2019-05-27 08:14:01 +000088
lloretgalleg45220152019-10-29 11:53:49 +010089 # Store config to create azure subscription later
90 self._config = {}
91 self._config["user"] = user
92 self._config["passwd"] = passwd
93 self._config["tenant"] = (tenant_id or tenant_name)
jamartinezv14a823d2019-08-01 11:45:15 +020094
tierno30d0d6d2019-05-27 08:14:01 +000095 # SUBSCRIPTION
seryio34478552019-05-23 14:50:49 +020096 if 'subscription_id' in config:
lloretgalleg45220152019-10-29 11:53:49 +010097 self._config["subscription_id"] = config.get('subscription_id')
98 #self.logger.debug('Setting subscription to: %s', self.config["subscription_id"])
seryio34478552019-05-23 14:50:49 +020099 else:
100 raise vimconn.vimconnException('Subscription not specified')
lloretgalleg45220152019-10-29 11:53:49 +0100101
tierno30d0d6d2019-05-27 08:14:01 +0000102 # REGION
seryio34478552019-05-23 14:50:49 +0200103 if 'region_name' in config:
104 self.region = config.get('region_name')
105 else:
106 raise vimconn.vimconnException('Azure region_name is not specified at config')
lloretgalleg45220152019-10-29 11:53:49 +0100107
tierno30d0d6d2019-05-27 08:14:01 +0000108 # RESOURCE_GROUP
seryio34478552019-05-23 14:50:49 +0200109 if 'resource_group' in config:
110 self.resource_group = config.get('resource_group')
111 else:
112 raise vimconn.vimconnException('Azure resource_group is not specified at config')
lloretgalleg45220152019-10-29 11:53:49 +0100113
seryio34478552019-05-23 14:50:49 +0200114 # VNET_NAME
115 if 'vnet_name' in config:
116 self.vnet_name = config["vnet_name"]
117
118 # public ssh key
119 self.pub_key = config.get('pub_key')
lloretgalleg45220152019-10-29 11:53:49 +0100120
121 # flavor pattern regex
122 if 'flavors_pattern' in config:
123 self._config['flavors_pattern'] = config['flavors_pattern']
seryio34478552019-05-23 14:50:49 +0200124
125 def _reload_connection(self):
tiernodeb74b22019-05-27 10:24:50 +0000126 """
lloretgalleg45220152019-10-29 11:53:49 +0100127 Called before any operation, checks python azure clients
tiernodeb74b22019-05-27 10:24:50 +0000128 """
lloretgalleg45220152019-10-29 11:53:49 +0100129 if self.reload_client:
130 self.logger.debug('reloading azure client')
131 try:
132 self.credentials = ServicePrincipalCredentials(
133 client_id=self._config["user"],
134 secret=self._config["passwd"],
135 tenant=self._config["tenant"]
136 )
137 self.conn = ResourceManagementClient(self.credentials, self._config["subscription_id"])
138 self.conn_compute = ComputeManagementClient(self.credentials, self._config["subscription_id"])
139 self.conn_vnet = NetworkManagementClient(self.credentials, self._config["subscription_id"])
140 self._check_or_create_resource_group()
141 self._check_or_create_vnet()
142
143 # Set to client created
144 self.reload_client = False
145 except Exception as e:
146 self._format_vimconn_exception(e)
seryio34478552019-05-23 14:50:49 +0200147
148 def _get_resource_name_from_resource_id(self, resource_id):
lloretgalleg45220152019-10-29 11:53:49 +0100149 """
150 Obtains resource_name from the azure complete identifier: resource_name will always be last item
151 """
jamartinezv14a823d2019-08-01 11:45:15 +0200152 try:
lloretgalleg45220152019-10-29 11:53:49 +0100153 resource = str(resource_id.split('/')[-1])
jamartinezv14a823d2019-08-01 11:45:15 +0200154 return resource
155 except Exception as e:
lloretgalleg45220152019-10-29 11:53:49 +0100156 raise vimconn.vimconnException("Unable to get resource name from invalid resource_id format: '{}'".format(resource_id))
seryio34478552019-05-23 14:50:49 +0200157
158 def _get_location_from_resource_group(self, resource_group_name):
jamartinezv14a823d2019-08-01 11:45:15 +0200159 try:
lloretgalleg45220152019-10-29 11:53:49 +0100160 location = self.conn.resource_groups.get(resource_group_name).location
jamartinezv14a823d2019-08-01 11:45:15 +0200161 return location
162 except Exception as e:
163 raise vimconn.vimconnNotFoundException("Location '{}' not found".format(resource_group_name))
164
seryio34478552019-05-23 14:50:49 +0200165 def _get_resource_group_name_from_resource_id(self, resource_id):
jamartinezv14a823d2019-08-01 11:45:15 +0200166
167 try:
lloretgalleg45220152019-10-29 11:53:49 +0100168 rg = str(resource_id.split('/')[4])
jamartinezv14a823d2019-08-01 11:45:15 +0200169 return rg
170 except Exception as e:
lloretgalleg45220152019-10-29 11:53:49 +0100171 raise vimconn.vimconnException("Unable to get resource group from invalid resource_id format '{}'".
172 format(resource_id))
jamartinezv14a823d2019-08-01 11:45:15 +0200173
174 def _get_net_name_from_resource_id(self, resource_id):
175
176 try:
lloretgalleg45220152019-10-29 11:53:49 +0100177 net_name = str(resource_id.split('/')[8])
jamartinezv14a823d2019-08-01 11:45:15 +0200178 return net_name
179 except Exception as e:
lloretgalleg45220152019-10-29 11:53:49 +0100180 raise vimconn.vimconnException("Unable to get azure net_name from invalid resource_id format '{}'".
181 format(resource_id))
seryio34478552019-05-23 14:50:49 +0200182
183 def _check_subnets_for_vm(self, net_list):
tierno30d0d6d2019-05-27 08:14:01 +0000184 # All subnets must belong to the same resource group and vnet
lloretgalleg45220152019-10-29 11:53:49 +0100185 rg_vnet = set(self._get_resource_group_name_from_resource_id(net['net_id']) +
186 self._get_net_name_from_resource_id(net['net_id']) for net in net_list)
jamartinezv14a823d2019-08-01 11:45:15 +0200187
lloretgalleg45220152019-10-29 11:53:49 +0100188 if len(rg_vnet) != 1:
189 raise self._format_vimconn_exception('Azure VMs can only attach to subnets in same VNET')
seryio34478552019-05-23 14:50:49 +0200190
lloretgalleg45220152019-10-29 11:53:49 +0100191 def _format_vimconn_exception(self, e):
tiernodeb74b22019-05-27 10:24:50 +0000192 """
lloretgalleg45220152019-10-29 11:53:49 +0100193 Transforms a generic or azure exception to a vimcommException
tiernodeb74b22019-05-27 10:24:50 +0000194 """
lloretgalleg45220152019-10-29 11:53:49 +0100195 if isinstance(e, vimconn.vimconnException):
196 raise
197 elif isinstance(e, AuthenticationError):
198 raise vimconn.vimconnAuthException(type(e).__name__ + ': ' + str(e))
199 elif isinstance(e, ConnectionError):
200 raise vimconn.vimconnConnectionException(type(e).__name__ + ': ' + str(e))
201 else:
202 # In case of generic error recreate client
203 self.reload_client = True
204 raise vimconn.vimconnException(type(e).__name__ + ': ' + str(e))
seryio34478552019-05-23 14:50:49 +0200205
206 def _check_or_create_resource_group(self):
tiernodeb74b22019-05-27 10:24:50 +0000207 """
lloretgalleg45220152019-10-29 11:53:49 +0100208 Creates the base resource group if it does not exist
tiernodeb74b22019-05-27 10:24:50 +0000209 """
lloretgalleg45220152019-10-29 11:53:49 +0100210 try:
211 rg_exists = self.conn.resource_groups.check_existence(self.resource_group)
212 if not rg_exists:
213 self.logger.debug("create base rgroup: %s", self.resource_group)
214 self.conn.resource_groups.create_or_update(self.resource_group, {'location': self.region})
215 except Exception as e:
216 self._format_vimconn_exception(e)
tiernodeb74b22019-05-27 10:24:50 +0000217
218 def _check_or_create_vnet(self):
lloretgalleg45220152019-10-29 11:53:49 +0100219 """
220 Try to get existent base vnet, in case it does not exist it creates it
221 """
tiernodeb74b22019-05-27 10:24:50 +0000222 try:
tierno84efdc12019-05-29 09:29:01 +0000223 vnet = self.conn_vnet.virtual_networks.get(self.resource_group, self.vnet_name)
224 self.vnet_address_space = vnet.address_space.address_prefixes[0]
tierno24620412019-06-03 14:05:08 +0000225 self.vnet_id = vnet.id
tierno84efdc12019-05-29 09:29:01 +0000226 return
227 except CloudError as e:
lloretgalleg45220152019-10-29 11:53:49 +0100228 if e.error.error and "notfound" in e.error.error.lower():
tierno84efdc12019-05-29 09:29:01 +0000229 pass
lloretgalleg45220152019-10-29 11:53:49 +0100230 # continue and create it
tierno84efdc12019-05-29 09:29:01 +0000231 else:
lloretgalleg45220152019-10-29 11:53:49 +0100232 self._format_vimconn_exception(e)
233
234 # if it does not exist, create it
tierno84efdc12019-05-29 09:29:01 +0000235 try:
tiernodeb74b22019-05-27 10:24:50 +0000236 vnet_params = {
237 'location': self.region,
238 'address_space': {
tierno84efdc12019-05-29 09:29:01 +0000239 'address_prefixes': ["10.0.0.0/8"]
tiernodeb74b22019-05-27 10:24:50 +0000240 },
241 }
tierno84efdc12019-05-29 09:29:01 +0000242 self.vnet_address_space = "10.0.0.0/8"
jamartinezv14a823d2019-08-01 11:45:15 +0200243
lloretgalleg45220152019-10-29 11:53:49 +0100244 self.logger.debug("create base vnet: %s", self.vnet_name)
tiernodeb74b22019-05-27 10:24:50 +0000245 self.conn_vnet.virtual_networks.create_or_update(self.resource_group, self.vnet_name, vnet_params)
tierno24620412019-06-03 14:05:08 +0000246 vnet = self.conn_vnet.virtual_networks.get(self.resource_group, self.vnet_name)
247 self.vnet_id = vnet.id
tiernodeb74b22019-05-27 10:24:50 +0000248 except Exception as e:
lloretgalleg45220152019-10-29 11:53:49 +0100249 self._format_vimconn_exception(e)
seryio34478552019-05-23 14:50:49 +0200250
251 def new_network(self, net_name, net_type, ip_profile=None, shared=False, vlan=None):
tiernodeb74b22019-05-27 10:24:50 +0000252 """
253 Adds a tenant network to VIM
254 :param net_name: name of the network
lloretgalleg45220152019-10-29 11:53:49 +0100255 :param net_type: not used for azure networks
tiernodeb74b22019-05-27 10:24:50 +0000256 :param ip_profile: is a dict containing the IP parameters of the network (Currently only IPv4 is implemented)
seryio34478552019-05-23 14:50:49 +0200257 'ip-version': can be one of ['IPv4','IPv6']
258 'subnet-address': ip_prefix_schema, that is X.X.X.X/Y
lloretgalleg45220152019-10-29 11:53:49 +0100259 'gateway-address': (Optional) ip_schema, that is X.X.X.X, not implemented for azure connector
260 'dns-address': (Optional) ip_schema, not implemented for azure connector
261 'dhcp': (Optional) dict containing, not implemented for azure connector
seryio34478552019-05-23 14:50:49 +0200262 'enabled': {'type': 'boolean'},
263 'start-address': ip_schema, first IP to grant
264 'count': number of IPs to grant.
lloretgalleg45220152019-10-29 11:53:49 +0100265 :param shared: Not allowed for Azure Connector
266 :param vlan: VLAN tagging is not allowed for Azure
tiernodeb74b22019-05-27 10:24:50 +0000267 :return: a tuple with the network identifier and created_items, or raises an exception on error
seryio34478552019-05-23 14:50:49 +0200268 created_items can be None or a dictionary where this method can include key-values that will be passed to
269 the method delete_network. Can be used to store created segments, created l2gw connections, etc.
270 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
271 as not present.
tiernodeb74b22019-05-27 10:24:50 +0000272 """
seryio34478552019-05-23 14:50:49 +0200273 return self._new_subnet(net_name, ip_profile)
274
275 def _new_subnet(self, net_name, ip_profile):
tiernodeb74b22019-05-27 10:24:50 +0000276 """
lloretgalleg45220152019-10-29 11:53:49 +0100277 Adds a tenant network to VIM. It creates a new subnet at existing base vnet
278 :param net_name: subnet name
tiernodeb74b22019-05-27 10:24:50 +0000279 :param ip_profile:
lloretgalleg45220152019-10-29 11:53:49 +0100280 subnet-address: if it is not provided a subnet/24 in the default vnet is created,
281 otherwise it creates a subnet in the indicated address
282 :return: a tuple with the network identifier and created_items, or raises an exception on error
tiernodeb74b22019-05-27 10:24:50 +0000283 """
lloretgalleg45220152019-10-29 11:53:49 +0100284 self.logger.debug('create subnet name %s, ip_profile %s', net_name, ip_profile)
seryio34478552019-05-23 14:50:49 +0200285 self._reload_connection()
seryio34478552019-05-23 14:50:49 +0200286
287 if ip_profile is None:
tierno24620412019-06-03 14:05:08 +0000288 # get a non used vnet ip range /24 and allocate automatically inside the range self.vnet_address_space
289 used_subnets = self.get_network_list()
290 for ip_range in netaddr.IPNetwork(self.vnet_address_space).subnet(24):
291 for used_subnet in used_subnets:
292 subnet_range = netaddr.IPNetwork(used_subnet["cidr_block"])
293 if subnet_range in ip_range or ip_range in subnet_range:
294 # this range overlaps with an existing subnet ip range. Breaks and look for another
295 break
296 else:
297 ip_profile = {"subnet_address": str(ip_range)}
lloretgalleg45220152019-10-29 11:53:49 +0100298 self.logger.debug('dinamically obtained ip_profile: %s', ip_range)
tierno24620412019-06-03 14:05:08 +0000299 break
300 else:
lloretgalleg45220152019-10-29 11:53:49 +0100301 raise vimconn.vimconnException("Cannot find a non-used subnet range in {}".
302 format(self.vnet_address_space))
jamartinezv14a823d2019-08-01 11:45:15 +0200303 else:
304 ip_profile = {"subnet_address": ip_profile['subnet_address']}
305
seryio34478552019-05-23 14:50:49 +0200306 try:
jamartinezv14a823d2019-08-01 11:45:15 +0200307 #subnet_name = "{}-{}".format(net_name[:24], uuid4())
lloretgalleg45220152019-10-29 11:53:49 +0100308 subnet_params = {
tierno24620412019-06-03 14:05:08 +0000309 'address_prefix': ip_profile['subnet_address']
seryio34478552019-05-23 14:50:49 +0200310 }
lloretgalleg45220152019-10-29 11:53:49 +0100311 # Assign a not duplicated net name
312 subnet_name = self._get_unused_subnet_name(net_name)
jamartinezv14a823d2019-08-01 11:45:15 +0200313
lloretgalleg45220152019-10-29 11:53:49 +0100314 self.logger.debug('creating subnet_name: {}'.format(subnet_name))
315 async_creation = self.conn_vnet.subnets.create_or_update(self.resource_group, self.vnet_name,
316 subnet_name, subnet_params)
317 async_creation.wait()
318 self.logger.debug('created subnet_name: {}'.format(subnet_name))
319
jamartinezv14a823d2019-08-01 11:45:15 +0200320 return "{}/subnets/{}".format(self.vnet_id, subnet_name), None
seryio34478552019-05-23 14:50:49 +0200321 except Exception as e:
lloretgalleg45220152019-10-29 11:53:49 +0100322 self._format_vimconn_exception(e)
323
324 def _get_unused_subnet_name(self, subnet_name):
325 """
326 Adds a prefix to the subnet_name with a number in case the indicated name is repeated
327 Checks subnets with the indicated name (without suffix) and adds a suffix with a number
328 """
329 all_subnets = self.conn_vnet.subnets.list(self.resource_group, self.vnet_name)
330 # Filter to subnets starting with the indicated name
331 subnets = list(filter(lambda subnet: (subnet.name.startswith(subnet_name)), all_subnets))
332 net_names = [str(subnet.name) for subnet in subnets]
333
334 # get the name with the first not used suffix
335 name_suffix = 0
336 #name = subnet_name + "-" + str(name_suffix)
337 name = subnet_name # first subnet created will have no prefix
338 while name in net_names:
339 name_suffix += 1
340 name = subnet_name + "-" + str(name_suffix)
341 return name
seryio34478552019-05-23 14:50:49 +0200342
jamartinezv14a823d2019-08-01 11:45:15 +0200343 def _create_nic(self, net, nic_name, static_ip=None):
seryio34478552019-05-23 14:50:49 +0200344
lloretgalleg45220152019-10-29 11:53:49 +0100345 self.logger.debug('create nic name %s, net_name %s', nic_name, net)
jamartinezv14a823d2019-08-01 11:45:15 +0200346 self._reload_connection()
347
348 subnet_id = net['net_id']
349 location = self._get_location_from_resource_group(self.resource_group)
jamartinezv14a823d2019-08-01 11:45:15 +0200350 try:
351 if static_ip:
352 async_nic_creation = self.conn_vnet.network_interfaces.create_or_update(
353 self.resource_group,
354 nic_name,
355 {
356 'location': location,
357 'ip_configurations': [{
358 'name': nic_name + '-ipconfiguration',
359 'privateIPAddress': static_ip,
360 'privateIPAllocationMethod': 'Static',
361 'subnet': {
362 'id': subnet_id
363 }
364 }]
365 }
366 )
367 async_nic_creation.wait()
368 else:
369 ip_configuration_name = nic_name + '-ipconfiguration'
jamartinezv14a823d2019-08-01 11:45:15 +0200370 async_nic_creation = self.conn_vnet.network_interfaces.create_or_update(
371 self.resource_group,
372 nic_name,
373 {
374 'location': location,
375 'ip_configurations': [{
376 'name': ip_configuration_name,
377 'subnet': {
378 'id': subnet_id
379 }
380 }]
381 }
382 )
383 async_nic_creation.wait()
lloretgalleg45220152019-10-29 11:53:49 +0100384 self.logger.debug('created nic name %s', nic_name)
jamartinezv14a823d2019-08-01 11:45:15 +0200385
386 public_ip = net.get('floating_ip')
lloretgalleg45220152019-10-29 11:53:49 +0100387 if public_ip:
388 public_ip_address_params = {
jamartinezv14a823d2019-08-01 11:45:15 +0200389 'location': location,
390 'public_ip_allocation_method': 'Dynamic'
391 }
392 public_ip_name = nic_name + '-public-ip'
393 public_ip = self.conn_vnet.public_ip_addresses.create_or_update(
394 self.resource_group,
395 public_ip_name,
lloretgalleg45220152019-10-29 11:53:49 +0100396 public_ip_address_params
jamartinezv14a823d2019-08-01 11:45:15 +0200397 )
lloretgalleg45220152019-10-29 11:53:49 +0100398 self.logger.debug('created public IP: {}'.format(public_ip.result()))
jamartinezv14a823d2019-08-01 11:45:15 +0200399
400 # Asociate NIC to Public IP
jamartinezv14a823d2019-08-01 11:45:15 +0200401 nic_data = self.conn_vnet.network_interfaces.get(
402 self.resource_group,
403 nic_name)
404
405 nic_data.ip_configurations[0].public_ip_address = public_ip.result()
406
jamartinezv14a823d2019-08-01 11:45:15 +0200407 self.conn_vnet.network_interfaces.create_or_update(
408 self.resource_group,
409 nic_name,
410 nic_data)
411
412 except Exception as e:
lloretgalleg45220152019-10-29 11:53:49 +0100413 self._format_vimconn_exception(e)
jamartinezv14a823d2019-08-01 11:45:15 +0200414
seryio34478552019-05-23 14:50:49 +0200415 return async_nic_creation.result()
416
jamartinezv14a823d2019-08-01 11:45:15 +0200417 def new_flavor(self, flavor_data):
lloretgalleg45220152019-10-29 11:53:49 +0100418 """
419 It is not allowed to create new flavors in Azure, must always use an existing one
420 """
421 raise vimconn.vimconnAuthException("It is not possible to create new flavors in AZURE")
tiernodeb74b22019-05-27 10:24:50 +0000422
lloretgalleg45220152019-10-29 11:53:49 +0100423 def new_tenant(self, tenant_name, tenant_description):
424 """
425 It is not allowed to create new tenants in azure
426 """
jamartinezv14a823d2019-08-01 11:45:15 +0200427 raise vimconn.vimconnAuthException("It is not possible to create a TENANT in AZURE")
428
429 def new_image(self, image_dict):
lloretgalleg45220152019-10-29 11:53:49 +0100430 """
431 It is not allowed to create new images in Azure, must always use an existing one
432 """
433 raise vimconn.vimconnAuthException("It is not possible to create new images in AZURE")
jamartinezv14a823d2019-08-01 11:45:15 +0200434
435 def get_image_id_from_path(self, path):
436 """Get the image id from image path in the VIM database.
437 Returns the image_id or raises a vimconnNotFoundException
438 """
lloretgalleg45220152019-10-29 11:53:49 +0100439 raise vimconn.vimconnAuthException("It is not possible to obtain image from path in AZURE")
jamartinezv14a823d2019-08-01 11:45:15 +0200440
441 def get_image_list(self, filter_dict={}):
442 """Obtain tenant images from VIM
443 Filter_dict can be:
lloretgalleg45220152019-10-29 11:53:49 +0100444 name: image name with the format: publisher:offer:sku:version
445 If some part of the name is provide ex: publisher:offer it will search all availables skus and version
446 for the provided publisher and offer
447 id: image uuid, currently not supported for azure
jamartinezv14a823d2019-08-01 11:45:15 +0200448 Returns the image list of dictionaries:
449 [{<the fields at Filter_dict plus some VIM specific>}, ...]
450 List can be empty
451 """
lloretgalleg45220152019-10-29 11:53:49 +0100452
453 self.logger.debug("get_image_list filter {}".format(filter_dict))
454
jamartinezv14a823d2019-08-01 11:45:15 +0200455 self._reload_connection()
lloretgalleg45220152019-10-29 11:53:49 +0100456 try:
457 image_list = []
458 if filter_dict.get("name"):
459 # name will have the format 'publisher:offer:sku:version'
460 # publisher is required, offer sku and version will be searched if not provided
461 params = filter_dict["name"].split(":")
tiernodeb74b22019-05-27 10:24:50 +0000462 publisher = params[0]
lloretgalleg45220152019-10-29 11:53:49 +0100463 if publisher:
464 # obtain offer list
465 offer_list = self._get_offer_list(params, publisher)
466 for offer in offer_list:
467 # obtain skus
468 sku_list = self._get_sku_list(params, publisher, offer)
469 for sku in sku_list:
470 # if version is defined get directly version, else list images
471 if len(params) == 4 and params[3]:
472 version = params[3]
473 image_list = self._get_version_image_list(publisher, offer, sku, version)
474 else:
475 image_list = self._get_sku_image_list(publisher, offer, sku)
476 else:
477 raise vimconn.vimconnAuthException(
478 "List images in Azure must include name param with at least publisher")
479 else:
480 raise vimconn.vimconnAuthException("List images in Azure must include name param with at"
481 " least publisher")
tiernodeb74b22019-05-27 10:24:50 +0000482
lloretgalleg45220152019-10-29 11:53:49 +0100483 return image_list
484 except Exception as e:
485 self._format_vimconn_exception(e)
486
487 def _get_offer_list(self, params, publisher):
488 """
489 Helper method to obtain offer list for defined publisher
490 """
491 if len(params) >= 2 and params[1]:
492 return [params[1]]
493 else:
494 try:
495 # get list of offers from azure
496 result_offers = self.conn_compute.virtual_machine_images.list_offers(self.region, publisher)
497 return [offer.name for offer in result_offers]
498 except CloudError as e:
499 # azure raises CloudError when not found
500 self.logger.info("error listing offers for publisher {}, message: {}".format(publisher, e.message))
501 return []
502
503 def _get_sku_list(self, params, publisher, offer):
504 """
505 Helper method to obtain sku list for defined publisher and offer
506 """
507 if len(params) >= 3 and params[2]:
508 return [params[2]]
509 else:
510 try:
511 # get list of skus from azure
512 result_skus = self.conn_compute.virtual_machine_images.list_skus(self.region, publisher, offer)
513 return [sku.name for sku in result_skus]
514 except CloudError as e:
515 # azure raises CloudError when not found
516 self.logger.info("error listing skus for publisher {}, offer {}, message: {}".format(publisher, offer, e.message))
517 return []
518
519 def _get_sku_image_list(self, publisher, offer, sku):
520 """
521 Helper method to obtain image list for publisher, offer and sku
522 """
523 image_list = []
524 try:
525 result_images = self.conn_compute.virtual_machine_images.list(self.region, publisher, offer, sku)
526 for result_image in result_images:
527 image_list.append({
528 'id': str(result_image.id),
529 'name': ":".join([publisher, offer, sku, result_image.name])
530 })
531 except CloudError as e:
532 self.logger.info(
533 "error listing skus for publisher {}, offer {}, message: {}".format(publisher, offer, e.message))
534 image_list = []
535 return image_list
536
537 def _get_version_image_list(self, publisher, offer, sku, version):
538 image_list = []
539 try:
540 result_image = self.conn_compute.virtual_machine_images.get(self.region, publisher, offer, sku, version)
541 if result_image:
542 image_list.append({
543 'id': str(result_image.id),
544 'name': ":".join([publisher, offer, sku, version])
545 })
546 except CloudError as e:
547 # azure gives CloudError when not found
548 self.logger.info(
549 "error listing images for publisher {}, offer {}, sku {}, vesion {} message: {}".format(publisher,
550 offer,
551 sku,
552 version,
553 e.message))
554 image_list = []
tiernodeb74b22019-05-27 10:24:50 +0000555 return image_list
556
seryio34478552019-05-23 14:50:49 +0200557 def get_network_list(self, filter_dict={}):
tiernodeb74b22019-05-27 10:24:50 +0000558 """Obtain tenant networks of VIM
seryio34478552019-05-23 14:50:49 +0200559 Filter_dict can be:
560 name: network name
lloretgalleg45220152019-10-29 11:53:49 +0100561 id: network id
562 shared: boolean, not implemented in Azure
563 tenant_id: tenant, not used in Azure, all networks same tenants
564 admin_state_up: boolean, not implemented in Azure
565 status: 'ACTIVE', not implemented in Azure #
seryio34478552019-05-23 14:50:49 +0200566 Returns the network list of dictionaries
tiernodeb74b22019-05-27 10:24:50 +0000567 """
lloretgalleg45220152019-10-29 11:53:49 +0100568 #self.logger.debug('getting network list for vim, filter %s', filter_dict)
seryio34478552019-05-23 14:50:49 +0200569 try:
570 self._reload_connection()
jamartinezv14a823d2019-08-01 11:45:15 +0200571
572 vnet = self.conn_vnet.virtual_networks.get(self.resource_group, self.vnet_name)
seryio34478552019-05-23 14:50:49 +0200573 subnet_list = []
jamartinezv14a823d2019-08-01 11:45:15 +0200574
seryio34478552019-05-23 14:50:49 +0200575 for subnet in vnet.subnets:
seryio34478552019-05-23 14:50:49 +0200576 if filter_dict:
577 if filter_dict.get("id") and str(subnet.id) != filter_dict["id"]:
578 continue
579 if filter_dict.get("name") and \
lloretgalleg45220152019-10-29 11:53:49 +0100580 str(subnet.name) != filter_dict["name"]:
seryio34478552019-05-23 14:50:49 +0200581 continue
582
jamartinezv14a823d2019-08-01 11:45:15 +0200583 name = self._get_resource_name_from_resource_id(subnet.id)
584
seryio34478552019-05-23 14:50:49 +0200585 subnet_list.append({
586 'id': str(subnet.id),
jamartinezv14a823d2019-08-01 11:45:15 +0200587 'name': self._get_resource_name_from_resource_id(subnet.id),
lloretgalleg45220152019-10-29 11:53:49 +0100588 'status': self.provision_state2osm[subnet.provisioning_state],
jamartinezv14a823d2019-08-01 11:45:15 +0200589 'cidr_block': str(subnet.address_prefix),
590 'type': 'bridge',
591 'shared': False
seryio34478552019-05-23 14:50:49 +0200592 }
593 )
jamartinezv14a823d2019-08-01 11:45:15 +0200594
seryio34478552019-05-23 14:50:49 +0200595 return subnet_list
596 except Exception as e:
lloretgalleg45220152019-10-29 11:53:49 +0100597 self._format_vimconn_exception(e)
seryio34478552019-05-23 14:50:49 +0200598
599 def new_vminstance(self, vm_name, description, start, image_id, flavor_id, net_list, cloud_config=None,
600 disk_list=None, availability_zone_index=None, availability_zone_list=None):
601
602 return self._new_vminstance(vm_name, image_id, flavor_id, net_list)
lloretgalleg45220152019-10-29 11:53:49 +0100603
jamartinezv14a823d2019-08-01 11:45:15 +0200604 def new_vminstance(self, name, description, start, image_id, flavor_id, net_list, cloud_config=None,
lloretgalleg45220152019-10-29 11:53:49 +0100605 disk_list=None, availability_zone_index=None, availability_zone_list=None):
606
607 self.logger.debug("new vm instance name: %s, image_id: %s, flavor_id: %s, net_list: %s, cloud_config: %s"
608 + "disk_list: %s, availability_zone_index: %s, availability_zone_list: %s",
609 name, image_id, flavor_id, net_list, cloud_config, disk_list,
610 availability_zone_index, availability_zone_list)
611
612 self._reload_connection()
613
614 # Validate input data is valid
615 # The virtual machine name must have less or 64 characters and it can not have the following
616 # characters: (~ ! @ # $ % ^ & * ( ) = + _ [ ] { } \ | ; : ' " , < > / ?.)
617 vm_name = self._check_vm_name(name)
618 # Obtain vm unused name
619 vm_name = self._get_unused_vm_name(vm_name)
620
621 # At least one network must be provided
622 if not net_list:
623 raise vimconn.vimconnException("At least one net must be provided to create a new VM")
624
625 # image_id are several fields of the image_id
626 image_reference = self._get_image_reference(image_id)
jamartinezv14a823d2019-08-01 11:45:15 +0200627
seryio34478552019-05-23 14:50:49 +0200628 self._check_subnets_for_vm(net_list)
629 vm_nics = []
630 for idx, net in enumerate(net_list):
jamartinezv14a823d2019-08-01 11:45:15 +0200631 # Fault with subnet_id
632 # subnet_id=net['subnet_id']
633 # subnet_id=net['net_id']
lloretgalleg45220152019-10-29 11:53:49 +0100634 nic_name = vm_name + '-nic-'+str(idx)
jamartinezv14a823d2019-08-01 11:45:15 +0200635 vm_nic = self._create_nic(net, nic_name)
lloretgalleg45220152019-10-29 11:53:49 +0100636 vm_nics.append({'id': str(vm_nic.id)})
637 net['vim_id'] = vm_nic.id
seryio34478552019-05-23 14:50:49 +0200638
639 try:
jamartinezv14a823d2019-08-01 11:45:15 +0200640
641 # cloud-init configuration
642 # cloud config
643 if cloud_config:
644 config_drive, userdata = self._create_user_data(cloud_config)
645 custom_data = base64.b64encode(userdata.encode('utf-8')).decode('latin-1')
lloretgalleg45220152019-10-29 11:53:49 +0100646 key_data = None
647 key_pairs = cloud_config.get("key-pairs")
648 if key_pairs:
649 key_data = key_pairs[0]
650
651 if cloud_config.get("users"):
652 user_name = cloud_config.get("users")[0].get("name", "osm")
653 else:
654 user_name = "osm" # DEFAULT USER IS OSM
655
jamartinezv14a823d2019-08-01 11:45:15 +0200656 os_profile = {
lloretgalleg45220152019-10-29 11:53:49 +0100657 'computer_name': vm_name,
658 'admin_username': user_name,
659 'linux_configuration': {
660 "disable_password_authentication": True,
661 "ssh": {
662 "public_keys": [{
663 "path": "/home/{}/.ssh/authorized_keys".format(user_name),
664 "key_data": key_data
665 }]
666 }
667 },
jamartinezv14a823d2019-08-01 11:45:15 +0200668 'custom_data': custom_data
669 }
670 else:
671 os_profile = {
lloretgalleg45220152019-10-29 11:53:49 +0100672 'computer_name': vm_name
673 ,'admin_username': 'osm'
674 ,'admin_password': 'Osm4u!',
jamartinezv14a823d2019-08-01 11:45:15 +0200675 }
676
seryio34478552019-05-23 14:50:49 +0200677 vm_parameters = {
678 'location': self.region,
jamartinezv14a823d2019-08-01 11:45:15 +0200679 'os_profile': os_profile,
seryio34478552019-05-23 14:50:49 +0200680 'hardware_profile': {
jamartinezv14a823d2019-08-01 11:45:15 +0200681 'vm_size': flavor_id
seryio34478552019-05-23 14:50:49 +0200682 },
683 'storage_profile': {
jamartinezv14a823d2019-08-01 11:45:15 +0200684 'image_reference': image_reference
seryio34478552019-05-23 14:50:49 +0200685 }
686 }
jamartinezv14a823d2019-08-01 11:45:15 +0200687
lloretgalleg45220152019-10-29 11:53:49 +0100688 # Add data disks if they are provided
689 if disk_list:
690 data_disks = []
691 for lun_name, disk in enumerate(disk_list):
692 self.logger.debug("add disk size: %s, image: %s", disk.get("size"), disk.get("image_id"))
693 if not disk.get("image_id"):
694 data_disks.append({
695 'lun': lun_name, # You choose the value, depending of what is available for you
696 'name': vm_name + "_data_disk-" + str(lun_name),
697 'create_option': DiskCreateOptionTypes.empty,
698 'disk_size_gb': disk.get("size")
699 })
700 else:
701 self.logger.debug("currently not able to create data disks from image for azure, ignoring")
702
703 if data_disks:
704 vm_parameters["storage_profile"]["data_disks"] = data_disks
705
706 # If the machine has several networks one must be marked as primary
707 # As it is not indicated in the interface the first interface will be marked as primary
708 if len(vm_nics) > 1:
709 for idx, vm_nic in enumerate(vm_nics):
710 if idx == 0:
711 vm_nics[0]['Primary'] = True
712 else:
713 vm_nics[idx]['Primary'] = False
714
715 vm_parameters['network_profile'] = {'network_interfaces': vm_nics}
716
717 self.logger.debug("create vm name: %s", vm_name)
seryio34478552019-05-23 14:50:49 +0200718 creation_result = self.conn_compute.virtual_machines.create_or_update(
719 self.resource_group,
lloretgalleg45220152019-10-29 11:53:49 +0100720 vm_name,
seryio34478552019-05-23 14:50:49 +0200721 vm_parameters
722 )
lloretgalleg45220152019-10-29 11:53:49 +0100723 self.logger.debug("created vm name: %s", vm_name)
seryio34478552019-05-23 14:50:49 +0200724
jamartinezv14a823d2019-08-01 11:45:15 +0200725 #creation_result.wait()
726 result = creation_result.result()
727
lloretgalleg45220152019-10-29 11:53:49 +0100728 if start:
jamartinezv14a823d2019-08-01 11:45:15 +0200729 start_result = self.conn_compute.virtual_machines.start(
730 self.resource_group,
lloretgalleg45220152019-10-29 11:53:49 +0100731 vm_name)
jamartinezv14a823d2019-08-01 11:45:15 +0200732 #start_result.wait()
733
734 return result.id, None
735
736 #run_command_parameters = {
737 # 'command_id': 'RunShellScript', # For linux, don't change it
738 # 'script': [
739 # 'date > /tmp/test.txt'
740 # ]
741 #}
seryio34478552019-05-23 14:50:49 +0200742 except Exception as e:
lloretgalleg45220152019-10-29 11:53:49 +0100743 self.logger.debug('Exception creating new vminstance: %s', e, exc_info=True)
744 self._format_vimconn_exception(e)
745
746 def _get_unused_vm_name(self, vm_name):
747 """
748 Checks the vm name and in case it is used adds a suffix to the name to allow creation
749 :return:
750 """
751 all_vms = self.conn_compute.virtual_machines.list(self.resource_group)
752 # Filter to vms starting with the indicated name
753 vms = list(filter(lambda vm: (vm.name.startswith(vm_name)), all_vms))
754 vm_names = [str(vm.name) for vm in vms]
755
756 # get the name with the first not used suffix
757 name_suffix = 0
758 # name = subnet_name + "-" + str(name_suffix)
759 name = vm_name # first subnet created will have no prefix
760 while name in vm_names:
761 name_suffix += 1
762 name = vm_name + "-" + str(name_suffix)
763 return name
764
seryio34478552019-05-23 14:50:49 +0200765
jamartinezv14a823d2019-08-01 11:45:15 +0200766 # It is necesary extract from image_id data to create the VM with this format
767 # 'image_reference': {
768 # 'publisher': vm_reference['publisher'],
769 # 'offer': vm_reference['offer'],
770 # 'sku': vm_reference['sku'],
771 # 'version': vm_reference['version']
772 # },
lloretgalleg45220152019-10-29 11:53:49 +0100773 def _get_image_reference(self, image_id):
jamartinezv14a823d2019-08-01 11:45:15 +0200774
lloretgalleg45220152019-10-29 11:53:49 +0100775 try:
776 # The data input format example:
777 # /Subscriptions/ca3d18ab-d373-4afb-a5d6-7c44f098d16a/Providers/Microsoft.Compute/Locations/westeurope/
778 # Publishers/Canonical/ArtifactTypes/VMImage/
779 # Offers/UbuntuServer/
780 # Skus/18.04-LTS/
781 # Versions/18.04.201809110
782 publisher = str(image_id.split('/')[8])
783 offer = str(image_id.split('/')[12])
784 sku = str(image_id.split('/')[14])
785 version = str(image_id.split('/')[16])
jamartinezv14a823d2019-08-01 11:45:15 +0200786
lloretgalleg45220152019-10-29 11:53:49 +0100787 return {
788 'publisher': publisher,
789 'offer': offer,
790 'sku': sku,
791 'version': version
792 }
793 except Exception as e:
794 raise vimconn.vimconnException(
795 "Unable to get image_reference from invalid image_id format: '{}'".format(image_id))
jamartinezv14a823d2019-08-01 11:45:15 +0200796
797 # Azure VM names can not have some special characters
lloretgalleg45220152019-10-29 11:53:49 +0100798 def _check_vm_name(self, vm_name):
799 """
800 Checks vm name, in case the vm has not allowed characters they are removed, not error raised
801 """
jamartinezv14a823d2019-08-01 11:45:15 +0200802
803 #chars_not_allowed_list = ['~','!','@','#','$','%','^','&','*','(',')','=','+','_','[',']','{','}','|',';',':','<','>','/','?','.']
804 chars_not_allowed_list = "~!@#$%^&*()=+_[]{}|;:<>/?."
805
806 # First: the VM name max length is 64 characters
807 vm_name_aux = vm_name[:64]
808
809 # Second: replace not allowed characters
lloretgalleg45220152019-10-29 11:53:49 +0100810 for elem in chars_not_allowed_list:
jamartinezv14a823d2019-08-01 11:45:15 +0200811 # Check if string is in the main string
lloretgalleg45220152019-10-29 11:53:49 +0100812 if elem in vm_name_aux:
jamartinezv14a823d2019-08-01 11:45:15 +0200813 #self.logger.debug('Dentro del IF')
814 # Replace the string
815 vm_name_aux = vm_name_aux.replace(elem, '-')
816
817 return vm_name_aux
818
seryio34478552019-05-23 14:50:49 +0200819 def get_flavor_id_from_data(self, flavor_dict):
seryio34478552019-05-23 14:50:49 +0200820
lloretgalleg45220152019-10-29 11:53:49 +0100821 self.logger.debug("getting flavor id from data, flavor_dict: %s", flavor_dict)
822 filter_dict = flavor_dict or {}
jamartinezv14a823d2019-08-01 11:45:15 +0200823 try:
824 self._reload_connection()
825 vm_sizes_list = [vm_size.serialize() for vm_size in self.conn_compute.virtual_machine_sizes.list(self.region)]
tierno30d0d6d2019-05-27 08:14:01 +0000826
lloretgalleg45220152019-10-29 11:53:49 +0100827 cpus = filter_dict.get('vcpus') or 0
828 memMB = filter_dict.get('ram') or 0
tierno30d0d6d2019-05-27 08:14:01 +0000829
lloretgalleg45220152019-10-29 11:53:49 +0100830 # Filter
831 if self._config.get("flavors_pattern"):
832 filtered_sizes = [size for size in vm_sizes_list if size['numberOfCores'] >= cpus
833 and size['memoryInMB'] >= memMB
834 and re.search(self._config.get("flavors_pattern"), size["name"])]
835 else:
836 filtered_sizes = [size for size in vm_sizes_list if size['numberOfCores'] >= cpus
837 and size['memoryInMB'] >= memMB]
jamartinezv14a823d2019-08-01 11:45:15 +0200838
lloretgalleg45220152019-10-29 11:53:49 +0100839
840
841 # Sort
842 listedFilteredSizes = sorted(filtered_sizes, key=lambda k: (k['numberOfCores'], k['memoryInMB'], k['resourceDiskSizeInMB']))
843
844 if listedFilteredSizes:
845 return listedFilteredSizes[0]['name']
846 raise vimconn.vimconnNotFoundException("Cannot find any flavor matching '{}'".format(str(flavor_dict)))
jamartinezv14a823d2019-08-01 11:45:15 +0200847
848 except Exception as e:
lloretgalleg45220152019-10-29 11:53:49 +0100849 self._format_vimconn_exception(e)
jamartinezv14a823d2019-08-01 11:45:15 +0200850
851 def _get_flavor_id_from_flavor_name(self, flavor_name):
jamartinezv14a823d2019-08-01 11:45:15 +0200852
lloretgalleg45220152019-10-29 11:53:49 +0100853 #self.logger.debug("getting flavor id from flavor name {}".format(flavor_name))
jamartinezv14a823d2019-08-01 11:45:15 +0200854 try:
855 self._reload_connection()
856 vm_sizes_list = [vm_size.serialize() for vm_size in self.conn_compute.virtual_machine_sizes.list(self.region)]
857
858 output_flavor = None
859 for size in vm_sizes_list:
860 if size['name'] == flavor_name:
861 output_flavor = size
862
lloretgalleg45220152019-10-29 11:53:49 +0100863 # Si no se encuentra ninguno, este metodo devuelve None
jamartinezv14a823d2019-08-01 11:45:15 +0200864 return output_flavor
865
866 except Exception as e:
lloretgalleg45220152019-10-29 11:53:49 +0100867 self._format_vimconn_exception(e)
tierno30d0d6d2019-05-27 08:14:01 +0000868
869 def check_vim_connectivity(self):
seryio34478552019-05-23 14:50:49 +0200870 try:
871 self._reload_connection()
tierno30d0d6d2019-05-27 08:14:01 +0000872 return True
873 except Exception as e:
874 raise vimconn.vimconnException("Connectivity issue with Azure API: {}".format(e))
seryio34478552019-05-23 14:50:49 +0200875
876 def get_network(self, net_id):
seryio34478552019-05-23 14:50:49 +0200877
lloretgalleg45220152019-10-29 11:53:49 +0100878 #self.logger.debug('get network id: {}'.format(net_id))
879 res_name = self._get_resource_name_from_resource_id(net_id)
seryio34478552019-05-23 14:50:49 +0200880 self._reload_connection()
jamartinezv14a823d2019-08-01 11:45:15 +0200881
lloretgalleg45220152019-10-29 11:53:49 +0100882 filter_dict = {'name': net_id}
jamartinezv14a823d2019-08-01 11:45:15 +0200883 network_list = self.get_network_list(filter_dict)
884
885 if not network_list:
886 raise vimconn.vimconnNotFoundException("network '{}' not found".format(net_id))
887 else:
888 return network_list[0]
889
jamartinezv14a823d2019-08-01 11:45:15 +0200890 def delete_network(self, net_id, created_items=None):
891
lloretgalleg45220152019-10-29 11:53:49 +0100892 self.logger.debug('deleting network {} - {}'.format(self.resource_group, net_id))
jamartinezv14a823d2019-08-01 11:45:15 +0200893
894 self._reload_connection()
lloretgalleg45220152019-10-29 11:53:49 +0100895 res_name = self._get_resource_name_from_resource_id(net_id)
896 filter_dict = {'name': res_name}
jamartinezv14a823d2019-08-01 11:45:15 +0200897 network_list = self.get_network_list(filter_dict)
898 if not network_list:
899 raise vimconn.vimconnNotFoundException("network '{}' not found".format(net_id))
900
901 try:
902 # Subnet API fails (CloudError: Azure Error: ResourceNotFound)
903 # Put the initial virtual_network API
lloretgalleg45220152019-10-29 11:53:49 +0100904 async_delete = self.conn_vnet.subnets.delete(self.resource_group, self.vnet_name, res_name)
905 async_delete.wait()
jamartinezv14a823d2019-08-01 11:45:15 +0200906 return net_id
907
908 except CloudError as e:
lloretgalleg45220152019-10-29 11:53:49 +0100909 if e.error.error and "notfound" in e.error.error.lower():
jamartinezv14a823d2019-08-01 11:45:15 +0200910 raise vimconn.vimconnNotFoundException("network '{}' not found".format(net_id))
911 else:
lloretgalleg45220152019-10-29 11:53:49 +0100912 self._format_vimconn_exception(e)
jamartinezv14a823d2019-08-01 11:45:15 +0200913 except Exception as e:
lloretgalleg45220152019-10-29 11:53:49 +0100914 self._format_vimconn_exception(e)
jamartinezv14a823d2019-08-01 11:45:15 +0200915
jamartinezv14a823d2019-08-01 11:45:15 +0200916 def delete_vminstance(self, vm_id, created_items=None):
lloretgalleg45220152019-10-29 11:53:49 +0100917 """ Deletes a vm instance from the vim.
918 """
919 self.logger.debug('deleting VM instance {} - {}'.format(self.resource_group, vm_id))
jamartinezv14a823d2019-08-01 11:45:15 +0200920 self._reload_connection()
921
922 try:
923
lloretgalleg45220152019-10-29 11:53:49 +0100924 res_name = self._get_resource_name_from_resource_id(vm_id)
925 vm = self.conn_compute.virtual_machines.get(self.resource_group, res_name)
jamartinezv14a823d2019-08-01 11:45:15 +0200926
927 # Shuts down the virtual machine and releases the compute resources
928 #vm_stop = self.conn_compute.virtual_machines.power_off(self.resource_group, resName)
929 #vm_stop.wait()
930
lloretgalleg45220152019-10-29 11:53:49 +0100931 vm_delete = self.conn_compute.virtual_machines.delete(self.resource_group, res_name)
jamartinezv14a823d2019-08-01 11:45:15 +0200932 vm_delete.wait()
lloretgalleg45220152019-10-29 11:53:49 +0100933 self.logger.debug('deleted VM name: %s', res_name)
jamartinezv14a823d2019-08-01 11:45:15 +0200934
935 # Delete OS Disk
936 os_disk_name = vm.storage_profile.os_disk.name
lloretgalleg45220152019-10-29 11:53:49 +0100937 self.logger.debug('delete OS DISK: %s', os_disk_name)
jamartinezv14a823d2019-08-01 11:45:15 +0200938 self.conn_compute.disks.delete(self.resource_group, os_disk_name)
lloretgalleg45220152019-10-29 11:53:49 +0100939 self.logger.debug('deleted OS DISK name: %s', os_disk_name)
jamartinezv14a823d2019-08-01 11:45:15 +0200940
lloretgalleg45220152019-10-29 11:53:49 +0100941 for data_disk in vm.storage_profile.data_disks:
942 self.logger.debug('delete data_disk: %s', data_disk.name)
943 self.conn_compute.disks.delete(self.resource_group, data_disk.name)
944 self.logger.debug('deleted OS DISK name: %s', data_disk.name)
945
946 # After deleting VM, it is necessary to delete NIC, because if is not deleted delete_network
jamartinezv14a823d2019-08-01 11:45:15 +0200947 # does not work because Azure says that is in use the subnet
948 network_interfaces = vm.network_profile.network_interfaces
949
950 for network_interface in network_interfaces:
951
jamartinezv14a823d2019-08-01 11:45:15 +0200952 nic_name = self._get_resource_name_from_resource_id(network_interface.id)
jamartinezv14a823d2019-08-01 11:45:15 +0200953 nic_data = self.conn_vnet.network_interfaces.get(
954 self.resource_group,
955 nic_name)
956
lloretgalleg45220152019-10-29 11:53:49 +0100957 public_ip_name = None
jamartinezv14a823d2019-08-01 11:45:15 +0200958 exist_public_ip = nic_data.ip_configurations[0].public_ip_address
959 if exist_public_ip:
960 public_ip_id = nic_data.ip_configurations[0].public_ip_address.id
jamartinezv14a823d2019-08-01 11:45:15 +0200961
962 # Delete public_ip
963 public_ip_name = self._get_resource_name_from_resource_id(public_ip_id)
964
lloretgalleg45220152019-10-29 11:53:49 +0100965 # Public ip must be deleted afterwards of nic that is attached
jamartinezv14a823d2019-08-01 11:45:15 +0200966
lloretgalleg45220152019-10-29 11:53:49 +0100967 self.logger.debug('delete NIC name: %s', nic_name)
968 nic_delete = self.conn_vnet.network_interfaces.delete(self.resource_group, nic_name)
969 nic_delete.wait()
970 self.logger.debug('deleted NIC name: %s', nic_name)
971
972 # Delete list of public ips
973 if public_ip_name:
974 self.logger.debug('delete PUBLIC IP - ' + public_ip_name)
975 public_ip = self.conn_vnet.public_ip_addresses.delete(self.resource_group, public_ip_name)
976
977 except CloudError as e:
978 if e.error.error and "notfound" in e.error.error.lower():
979 raise vimconn.vimconnNotFoundException("No vm instance found '{}'".format(vm_id))
980 else:
981 self._format_vimconn_exception(e)
982 except Exception as e:
983 self._format_vimconn_exception(e)
984
985 def action_vminstance(self, vm_id, action_dict, created_items = {}):
jamartinezv14a823d2019-08-01 11:45:15 +0200986 """Send and action over a VM instance from VIM
lloretgalleg45220152019-10-29 11:53:49 +0100987 Returns the vm_id if the action was successfully sent to the VIM
988 """
jamartinezv14a823d2019-08-01 11:45:15 +0200989
990 self.logger.debug("Action over VM '%s': %s", vm_id, str(action_dict))
991 try:
992 self._reload_connection()
993 resName = self._get_resource_name_from_resource_id(vm_id)
994 if "start" in action_dict:
lloretgalleg45220152019-10-29 11:53:49 +0100995 self.conn_compute.virtual_machines.start(self.resource_group, resName)
jamartinezv14a823d2019-08-01 11:45:15 +0200996 elif "stop" in action_dict or "shutdown" in action_dict or "shutoff" in action_dict:
lloretgalleg45220152019-10-29 11:53:49 +0100997 self.conn_compute.virtual_machines.power_off(self.resource_group, resName)
jamartinezv14a823d2019-08-01 11:45:15 +0200998 elif "terminate" in action_dict:
lloretgalleg45220152019-10-29 11:53:49 +0100999 self.conn_compute.virtual_machines.delete(self.resource_group, resName)
jamartinezv14a823d2019-08-01 11:45:15 +02001000 elif "reboot" in action_dict:
lloretgalleg45220152019-10-29 11:53:49 +01001001 self.conn_compute.virtual_machines.restart(self.resource_group, resName)
jamartinezv14a823d2019-08-01 11:45:15 +02001002 return None
1003 except CloudError as e:
lloretgalleg45220152019-10-29 11:53:49 +01001004 if e.error.error and "notfound" in e.error.error.lower():
jamartinezv14a823d2019-08-01 11:45:15 +02001005 raise vimconn.vimconnNotFoundException("No vm found '{}'".format(vm_id))
1006 else:
lloretgalleg45220152019-10-29 11:53:49 +01001007 self._format_vimconn_exception(e)
jamartinezv14a823d2019-08-01 11:45:15 +02001008 except Exception as e:
lloretgalleg45220152019-10-29 11:53:49 +01001009 self._format_vimconn_exception(e)
jamartinezv14a823d2019-08-01 11:45:15 +02001010
1011 def delete_flavor(self, flavor_id):
jamartinezv14a823d2019-08-01 11:45:15 +02001012 raise vimconn.vimconnAuthException("It is not possible to delete a FLAVOR in AZURE")
1013
lloretgalleg45220152019-10-29 11:53:49 +01001014 def delete_tenant(self, tenant_id,):
jamartinezv14a823d2019-08-01 11:45:15 +02001015 raise vimconn.vimconnAuthException("It is not possible to delete a TENANT in AZURE")
1016
1017 def delete_image(self, image_id):
jamartinezv14a823d2019-08-01 11:45:15 +02001018 raise vimconn.vimconnAuthException("It is not possible to delete a IMAGE in AZURE")
seryio34478552019-05-23 14:50:49 +02001019
1020 def get_vminstance(self, vm_id):
lloretgalleg45220152019-10-29 11:53:49 +01001021 """
1022 Obtaing the vm instance data from v_id
1023 """
1024 self.logger.debug("get vm instance: %s", vm_id)
seryio34478552019-05-23 14:50:49 +02001025 self._reload_connection()
jamartinezv14a823d2019-08-01 11:45:15 +02001026 try:
1027 resName = self._get_resource_name_from_resource_id(vm_id)
1028 vm=self.conn_compute.virtual_machines.get(self.resource_group, resName)
1029 except CloudError as e:
lloretgalleg45220152019-10-29 11:53:49 +01001030 if e.error.error and "notfound" in e.error.error.lower():
jamartinezv14a823d2019-08-01 11:45:15 +02001031 raise vimconn.vimconnNotFoundException("No vminstance found '{}'".format(vm_id))
1032 else:
lloretgalleg45220152019-10-29 11:53:49 +01001033 self._format_vimconn_exception(e)
jamartinezv14a823d2019-08-01 11:45:15 +02001034 except Exception as e:
lloretgalleg45220152019-10-29 11:53:49 +01001035 self._format_vimconn_exception(e)
seryio34478552019-05-23 14:50:49 +02001036
1037 return vm
1038
1039 def get_flavor(self, flavor_id):
lloretgalleg45220152019-10-29 11:53:49 +01001040 """
1041 Obtains the flavor_data from the flavor_id
1042 """
seryio34478552019-05-23 14:50:49 +02001043 self._reload_connection()
lloretgalleg45220152019-10-29 11:53:49 +01001044 self.logger.debug("get flavor from id: %s", flavor_id)
jamartinezv14a823d2019-08-01 11:45:15 +02001045 flavor_data = self._get_flavor_id_from_flavor_name(flavor_id)
1046 if flavor_data:
1047 flavor = {
1048 'id': flavor_id,
1049 'name': flavor_id,
1050 'ram': flavor_data['memoryInMB'],
1051 'vcpus': flavor_data['numberOfCores'],
lloretgalleg45220152019-10-29 11:53:49 +01001052 'disk': flavor_data['resourceDiskSizeInMB']/1024
jamartinezv14a823d2019-08-01 11:45:15 +02001053 }
1054 return flavor
1055 else:
1056 raise vimconn.vimconnNotFoundException("flavor '{}' not found".format(flavor_id))
1057
jamartinezv14a823d2019-08-01 11:45:15 +02001058 def get_tenant_list(self, filter_dict={}):
lloretgalleg45220152019-10-29 11:53:49 +01001059 """ Obtains the list of tenants
1060 For the azure connector only the azure tenant will be returned if it is compatible
1061 with filter_dict
1062 """
jamartinezv14a823d2019-08-01 11:45:15 +02001063 tenants_azure=[{'name': self.tenant, 'id': self.tenant}]
1064 tenant_list=[]
1065
lloretgalleg45220152019-10-29 11:53:49 +01001066 self.logger.debug("get tenant list: %s", filter_dict)
jamartinezv14a823d2019-08-01 11:45:15 +02001067 for tenant_azure in tenants_azure:
1068 if filter_dict:
1069 if filter_dict.get("id") and str(tenant_azure.get("id")) != filter_dict["id"]:
1070 continue
lloretgalleg45220152019-10-29 11:53:49 +01001071 if filter_dict.get("name") and str(tenant_azure.get("name")) != filter_dict["name"]:
jamartinezv14a823d2019-08-01 11:45:15 +02001072 continue
1073
1074 tenant_list.append(tenant_azure)
1075
1076 return tenant_list
seryio34478552019-05-23 14:50:49 +02001077
tierno84efdc12019-05-29 09:29:01 +00001078 def refresh_nets_status(self, net_list):
lloretgalleg45220152019-10-29 11:53:49 +01001079 """Get the status of the networks
1080 Params: the list of network identifiers
1081 Returns a dictionary with:
1082 net_id: #VIM id of this network
1083 status: #Mandatory. Text with one of:
1084 # DELETED (not found at vim)
1085 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1086 # OTHER (Vim reported other status not understood)
1087 # ERROR (VIM indicates an ERROR status)
1088 # ACTIVE, INACTIVE, DOWN (admin down),
1089 # BUILD (on building process)
1090 #
1091 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1092 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1093
1094 """
jamartinezv14a823d2019-08-01 11:45:15 +02001095
tierno84efdc12019-05-29 09:29:01 +00001096 out_nets = {}
1097 self._reload_connection()
lloretgalleg45220152019-10-29 11:53:49 +01001098
1099 self.logger.debug("reload nets status net_list: %s", net_list)
tierno84efdc12019-05-29 09:29:01 +00001100 for net_id in net_list:
1101 try:
jamartinezv14a823d2019-08-01 11:45:15 +02001102 netName = self._get_net_name_from_resource_id(net_id)
tierno84efdc12019-05-29 09:29:01 +00001103 resName = self._get_resource_name_from_resource_id(net_id)
seryio34478552019-05-23 14:50:49 +02001104
jamartinezv14a823d2019-08-01 11:45:15 +02001105 net = self.conn_vnet.subnets.get(self.resource_group, netName, resName)
1106
lloretgalleg45220152019-10-29 11:53:49 +01001107 out_nets[net_id] = {
jamartinezv14a823d2019-08-01 11:45:15 +02001108 "status": self.provision_state2osm[net.provisioning_state],
1109 "vim_info": str(net)
tierno84efdc12019-05-29 09:29:01 +00001110 }
1111 except CloudError as e:
lloretgalleg45220152019-10-29 11:53:49 +01001112 if e.error.error and "notfound" in e.error.error.lower():
1113 self.logger.info("Not found subnet net_name: %s, subnet_name: %s", netName, resName)
tierno84efdc12019-05-29 09:29:01 +00001114 out_nets[net_id] = {
1115 "status": "DELETED",
jamartinezv14a823d2019-08-01 11:45:15 +02001116 "error_msg": str(e)
tierno84efdc12019-05-29 09:29:01 +00001117 }
1118 else:
lloretgalleg45220152019-10-29 11:53:49 +01001119 self.logger.error("CloudError Exception %s when searching subnet", e)
1120 out_nets[net_id] = {
1121 "status": "VIM_ERROR",
1122 "error_msg": str(e)
1123 }
jamartinezv14a823d2019-08-01 11:45:15 +02001124 except vimconn.vimconnNotFoundException as e:
lloretgalleg45220152019-10-29 11:53:49 +01001125 self.logger.error("VimConnNotFoundException %s when searching subnet", e)
jamartinezv14a823d2019-08-01 11:45:15 +02001126 out_nets[net_id] = {
1127 "status": "DELETED",
1128 "error_msg": str(e)
1129 }
tierno84efdc12019-05-29 09:29:01 +00001130 except Exception as e:
lloretgalleg45220152019-10-29 11:53:49 +01001131 self.logger.error("Exception %s when searching subnet", e, exc_info=True)
tierno84efdc12019-05-29 09:29:01 +00001132 out_nets[net_id] = {
1133 "status": "VIM_ERROR",
tierno84efdc12019-05-29 09:29:01 +00001134 "error_msg": str(e)
1135 }
tierno84efdc12019-05-29 09:29:01 +00001136 return out_nets
1137
1138 def refresh_vms_status(self, vm_list):
lloretgalleg45220152019-10-29 11:53:49 +01001139 """ Get the status of the virtual machines and their interfaces/ports
1140 Params: the list of VM identifiers
1141 Returns a dictionary with:
1142 vm_id: # VIM id of this Virtual Machine
1143 status: # Mandatory. Text with one of:
1144 # DELETED (not found at vim)
1145 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1146 # OTHER (Vim reported other status not understood)
1147 # ERROR (VIM indicates an ERROR status)
1148 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1149 # BUILD (on building process), ERROR
1150 # ACTIVE:NoMgmtIP (Active but none of its interfaces has an IP address
1151 # (ACTIVE:NoMgmtIP is not returned for Azure)
1152 #
1153 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1154 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1155 interfaces: list with interface info. Each item a dictionary with:
1156 vim_interface_id - The ID of the interface
1157 mac_address - The MAC address of the interface.
1158 ip_address - The IP address of the interface within the subnet.
1159 """
jamartinezv14a823d2019-08-01 11:45:15 +02001160
tierno84efdc12019-05-29 09:29:01 +00001161 out_vms = {}
1162 self._reload_connection()
jamartinezv14a823d2019-08-01 11:45:15 +02001163
lloretgalleg45220152019-10-29 11:53:49 +01001164 self.logger.debug("refresh vm status vm_list: %s", vm_list)
1165 search_vm_list = vm_list or {}
1166
1167 for vm_id in search_vm_list:
1168 out_vm = {}
tierno84efdc12019-05-29 09:29:01 +00001169 try:
lloretgalleg45220152019-10-29 11:53:49 +01001170 res_name = self._get_resource_name_from_resource_id(vm_id)
jamartinezv14a823d2019-08-01 11:45:15 +02001171
lloretgalleg45220152019-10-29 11:53:49 +01001172 vm = self.conn_compute.virtual_machines.get(self.resource_group, res_name)
1173 out_vm['vim_info'] = str(vm)
1174 out_vm['status'] = self.provision_state2osm.get(vm.provisioning_state, 'OTHER')
1175 if vm.provisioning_state == 'Succeeded':
1176 # check if machine is running or stopped
1177 instance_view = self.conn_compute.virtual_machines.instance_view(self.resource_group,
1178 res_name)
1179 for status in instance_view.statuses:
1180 splitted_status = status.code.split("/")
1181 if len(splitted_status) == 2 and splitted_status[0] == 'PowerState':
1182 out_vm['status'] = self.power_state2osm.get(splitted_status[1], 'OTHER')
jamartinezv14a823d2019-08-01 11:45:15 +02001183
1184 network_interfaces = vm.network_profile.network_interfaces
lloretgalleg45220152019-10-29 11:53:49 +01001185 out_vm['interfaces'] = self._get_vm_interfaces_status(vm_id, network_interfaces)
jamartinezv14a823d2019-08-01 11:45:15 +02001186
lloretgalleg45220152019-10-29 11:53:49 +01001187 except CloudError as e:
1188 if e.error.error and "notfound" in e.error.error.lower():
1189 self.logger.debug("Not found vm id: %s", vm_id)
1190 out_vm['status'] = "DELETED"
1191 out_vm['error_msg'] = str(e)
1192 out_vm['vim_info'] = None
1193 else:
1194 # maybe connection error or another type of error, return vim error
1195 self.logger.error("Exception %s refreshing vm_status", e)
1196 out_vm['status'] = "VIM_ERROR"
1197 out_vm['error_msg'] = str(e)
1198 out_vm['vim_info'] = None
tierno84efdc12019-05-29 09:29:01 +00001199 except Exception as e:
lloretgalleg45220152019-10-29 11:53:49 +01001200 self.logger.error("Exception %s refreshing vm_status", e, exc_info=True)
1201 out_vm['status'] = "VIM_ERROR"
1202 out_vm['error_msg'] = str(e)
1203 out_vm['vim_info'] = None
jamartinezv14a823d2019-08-01 11:45:15 +02001204
lloretgalleg45220152019-10-29 11:53:49 +01001205 out_vms[vm_id] = out_vm
tierno84efdc12019-05-29 09:29:01 +00001206
1207 return out_vms
seryio07ad1362019-05-29 09:16:24 +02001208
lloretgalleg45220152019-10-29 11:53:49 +01001209 def _get_vm_interfaces_status(self, vm_id, interfaces):
1210 """
1211 Gets the interfaces detail for a vm
1212 :param interfaces: List of interfaces.
1213 :return: Dictionary with list of interfaces including, vim_interface_id, mac_address and ip_address
1214 """
1215 try:
1216 interface_list = []
1217 for network_interface in interfaces:
1218 interface_dict = {}
1219 nic_name = self._get_resource_name_from_resource_id(network_interface.id)
1220 interface_dict['vim_interface_id'] = network_interface.id
1221
1222 nic_data = self.conn_vnet.network_interfaces.get(
1223 self.resource_group,
1224 nic_name)
1225
1226 private_ip = nic_data.ip_configurations[0].private_ip_address
1227
1228 interface_dict['mac_address'] = nic_data.mac_address
1229 interface_dict['ip_address'] = private_ip
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
seryio34478552019-05-23 14:50:49 +02001237
1238if __name__ == "__main__":
1239
1240 # Making some basic test
lloretgalleg45220152019-10-29 11:53:49 +01001241 vim_id = 'azure'
1242 vim_name = 'azure'
seryio34478552019-05-23 14:50:49 +02001243 needed_test_params = {
tiernodeb74b22019-05-27 10:24:50 +00001244 "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",
tierno30d0d6d2019-05-27 08:14:01 +00001249 "vnet_name": "AZURE_VNET_NAME",
seryio34478552019-05-23 14:50:49 +02001250 }
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"),
tierno30d0d6d2019-05-27 08:14:01 +00001263 'pub_key': getenv("AZURE_PUB_KEY", None),
seryio34478552019-05-23 14:50:49 +02001264 'vnet_name': getenv("AZURE_VNET_NAME", 'myNetwork'),
1265 }
tierno30d0d6d2019-05-27 08:14:01 +00001266
seryio34478552019-05-23 14:50:49 +02001267 virtualMachine = {
tierno30d0d6d2019-05-27 08:14:01 +00001268 'name': 'sergio',
1269 'description': 'new VM',
seryio34478552019-05-23 14:50:49 +02001270 '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
tierno30d0d6d2019-05-27 08:14:01 +00001291 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)
seryio34478552019-05-23 14:50:49 +02001293
tiernodeb74b22019-05-27 10:24:50 +00001294 # 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
tierno24620412019-06-03 14:05:08 +00001299 azure.new_network("mynet", None)
tierno84efdc12019-05-29 09:29:01 +00001300 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])