1 # -*- coding: utf-8 -*-
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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
17 from osm_ro
import vimconn
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 import msrestazure
.tools
as azure_tools
31 from requests
.exceptions
import ConnectionError
33 __author__
= 'Isabel Lloret, Sergio Gonzalez, Alfonso Tierno'
34 __date__
= '$18-apr-2019 23:59:59$'
37 if getenv('OSMRO_PDB_DEBUG'):
44 class vimconnector(vimconn
.vimconnector
):
46 # Translate azure provisioning state to OSM provision state
47 # The first three ones are the transitional status once a user initiated action has been requested
48 # Once the operation is complete, it will transition into the states Succeeded or Failed
49 # https://docs.microsoft.com/en-us/azure/virtual-machines/windows/states-lifecycle
50 provision_state2osm
= {
53 "Deleting": "INACTIVE",
54 "Succeeded": "ACTIVE",
58 # Translate azure power state to OSM provision state
60 "starting": "INACTIVE",
62 "stopping": "INACTIVE",
63 "stopped": "INACTIVE",
65 "deallocated": "BUILD",
66 "deallocating": "BUILD"
69 AZURE_ZONES
= ["1", "2", "3"]
71 def __init__(self
, uuid
, name
, tenant_id
, tenant_name
, url
, url_admin
=None, user
=None, passwd
=None, log_level
=None,
72 config
={}, persistent_info
={}):
74 Constructor of VIM. Raise an exception is some needed parameter is missing, but it must not do any connectivity
75 checking against the VIM
76 Using common constructor parameters.
77 In this case: config must include the following parameters:
78 subscription_id: assigned azure subscription identifier
79 region_name: current region for azure network
80 resource_group: used for all azure created resources
81 vnet_name: base vnet for azure, created networks will be subnets from this base network
82 config may also include the following parameter:
83 flavors_pattern: pattern that will be used to select a range of vm sizes, for example
84 "^((?!Standard_B).)*$" will filter out Standard_B range that is cheap but is very overused
85 "^Standard_B" will select a serie B maybe for test environment
88 vimconn
.vimconnector
.__init
__(self
, uuid
, name
, tenant_id
, tenant_name
, url
, url_admin
, user
, passwd
, log_level
,
89 config
, persistent_info
)
91 # Variable that indicates if client must be reloaded or initialized
92 self
.reload_client
= True
94 self
.vnet_address_space
= None
96 self
.logger
= logging
.getLogger('openmano.vim.azure')
99 self
.logger
.setLevel(getattr(logging
, log_level
))
101 self
.tenant
= (tenant_id
or tenant_name
)
103 # Store config to create azure subscription later
107 "tenant": tenant_id
or tenant_name
111 if 'subscription_id' in config
:
112 self
._config
["subscription_id"] = config
.get('subscription_id')
113 # self.logger.debug('Setting subscription to: %s', self.config["subscription_id"])
115 raise vimconn
.vimconnException('Subscription not specified')
118 if 'region_name' in config
:
119 self
.region
= config
.get('region_name')
121 raise vimconn
.vimconnException('Azure region_name is not specified at config')
124 if 'resource_group' in config
:
125 self
.resource_group
= config
.get('resource_group')
127 raise vimconn
.vimconnException('Azure resource_group is not specified at config')
130 if 'vnet_name' in config
:
131 self
.vnet_name
= config
["vnet_name"]
134 self
.pub_key
= config
.get('pub_key')
136 # flavor pattern regex
137 if 'flavors_pattern' in config
:
138 self
._config
['flavors_pattern'] = config
['flavors_pattern']
140 def _reload_connection(self
):
142 Called before any operation, checks python azure clients
144 if self
.reload_client
:
145 self
.logger
.debug('reloading azure client')
147 self
.credentials
= ServicePrincipalCredentials(
148 client_id
=self
._config
["user"],
149 secret
=self
._config
["passwd"],
150 tenant
=self
._config
["tenant"]
152 self
.conn
= ResourceManagementClient(self
.credentials
, self
._config
["subscription_id"])
153 self
.conn_compute
= ComputeManagementClient(self
.credentials
, self
._config
["subscription_id"])
154 self
.conn_vnet
= NetworkManagementClient(self
.credentials
, self
._config
["subscription_id"])
155 self
._check
_or
_create
_resource
_group
()
156 self
._check
_or
_create
_vnet
()
158 # Set to client created
159 self
.reload_client
= False
160 except Exception as e
:
161 self
._format
_vimconn
_exception
(e
)
163 def _get_resource_name_from_resource_id(self
, resource_id
):
165 Obtains resource_name from the azure complete identifier: resource_name will always be last item
168 resource
= str(resource_id
.split('/')[-1])
170 except Exception as e
:
171 raise vimconn
.vimconnException("Unable to get resource name from resource_id '{}' Error: '{}'".
172 format(resource_id
, e
))
174 def _get_location_from_resource_group(self
, resource_group_name
):
176 location
= self
.conn
.resource_groups
.get(resource_group_name
).location
178 except Exception as e
:
179 raise vimconn
.vimconnNotFoundException("Location '{}' not found".format(resource_group_name
))
181 def _get_resource_group_name_from_resource_id(self
, resource_id
):
184 rg
= str(resource_id
.split('/')[4])
186 except Exception as e
:
187 raise vimconn
.vimconnException("Unable to get resource group from invalid resource_id format '{}'".
190 def _get_net_name_from_resource_id(self
, resource_id
):
193 net_name
= str(resource_id
.split('/')[8])
195 except Exception as e
:
196 raise vimconn
.vimconnException("Unable to get azure net_name from invalid resource_id format '{}'".
199 def _check_subnets_for_vm(self
, net_list
):
200 # All subnets must belong to the same resource group and vnet
201 rg_vnet
= set(self
._get
_resource
_group
_name
_from
_resource
_id
(net
['net_id']) +
202 self
._get
_net
_name
_from
_resource
_id
(net
['net_id']) for net
in net_list
)
204 if len(rg_vnet
) != 1:
205 raise self
._format
_vimconn
_exception
('Azure VMs can only attach to subnets in same VNET')
207 def _format_vimconn_exception(self
, e
):
209 Transforms a generic or azure exception to a vimcommException
211 if isinstance(e
, vimconn
.vimconnException
):
213 elif isinstance(e
, AuthenticationError
):
214 raise vimconn
.vimconnAuthException(type(e
).__name
__ + ': ' + str(e
))
215 elif isinstance(e
, ConnectionError
):
216 raise vimconn
.vimconnConnectionException(type(e
).__name
__ + ': ' + str(e
))
218 # In case of generic error recreate client
219 self
.reload_client
= True
220 raise vimconn
.vimconnException(type(e
).__name
__ + ': ' + str(e
))
222 def _check_or_create_resource_group(self
):
224 Creates the base resource group if it does not exist
227 rg_exists
= self
.conn
.resource_groups
.check_existence(self
.resource_group
)
229 self
.logger
.debug("create base rgroup: %s", self
.resource_group
)
230 self
.conn
.resource_groups
.create_or_update(self
.resource_group
, {'location': self
.region
})
231 except Exception as e
:
232 self
._format
_vimconn
_exception
(e
)
234 def _check_or_create_vnet(self
):
236 Try to get existent base vnet, in case it does not exist it creates it
239 vnet
= self
.conn_vnet
.virtual_networks
.get(self
.resource_group
, self
.vnet_name
)
240 self
.vnet_address_space
= vnet
.address_space
.address_prefixes
[0]
241 self
.vnet_id
= vnet
.id
243 except CloudError
as e
:
244 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
246 # continue and create it
248 self
._format
_vimconn
_exception
(e
)
250 # if it does not exist, create it
253 'location': self
.region
,
255 'address_prefixes': ["10.0.0.0/8"]
258 self
.vnet_address_space
= "10.0.0.0/8"
260 self
.logger
.debug("create base vnet: %s", self
.vnet_name
)
261 self
.conn_vnet
.virtual_networks
.create_or_update(self
.resource_group
, self
.vnet_name
, vnet_params
)
262 vnet
= self
.conn_vnet
.virtual_networks
.get(self
.resource_group
, self
.vnet_name
)
263 self
.vnet_id
= vnet
.id
264 except Exception as e
:
265 self
._format
_vimconn
_exception
(e
)
267 def new_network(self
, net_name
, net_type
, ip_profile
=None, shared
=False, provider_network_profile
=None):
269 Adds a tenant network to VIM
270 :param net_name: name of the network
271 :param net_type: not used for azure networks
272 :param ip_profile: is a dict containing the IP parameters of the network (Currently only IPv4 is implemented)
273 'ip-version': can be one of ['IPv4','IPv6']
274 'subnet-address': ip_prefix_schema, that is X.X.X.X/Y
275 'gateway-address': (Optional) ip_schema, that is X.X.X.X, not implemented for azure connector
276 'dns-address': (Optional) ip_schema, not implemented for azure connector
277 'dhcp': (Optional) dict containing, not implemented for azure connector
278 'enabled': {'type': 'boolean'},
279 'start-address': ip_schema, first IP to grant
280 'count': number of IPs to grant.
281 :param shared: Not allowed for Azure Connector
282 :param provider_network_profile: (optional) contains {segmentation-id: vlan, provider-network: vim_netowrk}
283 :return: a tuple with the network identifier and created_items, or raises an exception on error
284 created_items can be None or a dictionary where this method can include key-values that will be passed to
285 the method delete_network. Can be used to store created segments, created l2gw connections, etc.
286 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
289 return self
._new
_subnet
(net_name
, ip_profile
)
291 def _new_subnet(self
, net_name
, ip_profile
):
293 Adds a tenant network to VIM. It creates a new subnet at existing base vnet
294 :param net_name: subnet name
296 subnet-address: if it is not provided a subnet/24 in the default vnet is created,
297 otherwise it creates a subnet in the indicated address
298 :return: a tuple with the network identifier and created_items, or raises an exception on error
300 self
.logger
.debug('create subnet name %s, ip_profile %s', net_name
, ip_profile
)
301 self
._reload
_connection
()
303 if ip_profile
is None:
304 # get a non used vnet ip range /24 and allocate automatically inside the range self.vnet_address_space
305 used_subnets
= self
.get_network_list()
306 for ip_range
in netaddr
.IPNetwork(self
.vnet_address_space
).subnet(24):
307 for used_subnet
in used_subnets
:
308 subnet_range
= netaddr
.IPNetwork(used_subnet
["cidr_block"])
309 if subnet_range
in ip_range
or ip_range
in subnet_range
:
310 # this range overlaps with an existing subnet ip range. Breaks and look for another
313 ip_profile
= {"subnet_address": str(ip_range
)}
314 self
.logger
.debug('dinamically obtained ip_profile: %s', ip_range
)
317 raise vimconn
.vimconnException("Cannot find a non-used subnet range in {}".
318 format(self
.vnet_address_space
))
320 ip_profile
= {"subnet_address": ip_profile
['subnet_address']}
323 # subnet_name = "{}-{}".format(net_name[:24], uuid4())
325 'address_prefix': ip_profile
['subnet_address']
327 # Assign a not duplicated net name
328 subnet_name
= self
._get
_unused
_subnet
_name
(net_name
)
330 self
.logger
.debug('creating subnet_name: {}'.format(subnet_name
))
331 async_creation
= self
.conn_vnet
.subnets
.create_or_update(self
.resource_group
, self
.vnet_name
,
332 subnet_name
, subnet_params
)
333 async_creation
.wait()
334 self
.logger
.debug('created subnet_name: {}'.format(subnet_name
))
336 return "{}/subnets/{}".format(self
.vnet_id
, subnet_name
), None
337 except Exception as e
:
338 self
._format
_vimconn
_exception
(e
)
340 def _get_unused_subnet_name(self
, subnet_name
):
342 Adds a prefix to the subnet_name with a number in case the indicated name is repeated
343 Checks subnets with the indicated name (without suffix) and adds a suffix with a number
345 all_subnets
= self
.conn_vnet
.subnets
.list(self
.resource_group
, self
.vnet_name
)
346 # Filter to subnets starting with the indicated name
347 subnets
= list(filter(lambda subnet
: (subnet
.name
.startswith(subnet_name
)), all_subnets
))
348 net_names
= [str(subnet
.name
) for subnet
in subnets
]
350 # get the name with the first not used suffix
352 # name = subnet_name + "-" + str(name_suffix)
353 name
= subnet_name
# first subnet created will have no prefix
354 while name
in net_names
:
356 name
= subnet_name
+ "-" + str(name_suffix
)
359 def _create_nic(self
, net
, nic_name
, static_ip
=None, created_items
={}):
361 self
.logger
.debug('create nic name %s, net_name %s', nic_name
, net
)
362 self
._reload
_connection
()
364 subnet_id
= net
['net_id']
365 location
= self
._get
_location
_from
_resource
_group
(self
.resource_group
)
367 net_ifz
= {'location': location
}
368 net_ip_config
= {'name': nic_name
+ '-ipconfiguration', 'subnet': {'id': subnet_id
}}
370 net_ip_config
['privateIPAddress'] = static_ip
371 net_ip_config
['privateIPAllocationMethod'] = 'Static'
372 net_ifz
['ip_configurations'] = [net_ip_config
]
373 mac_address
= net
.get('mac_address')
375 net_ifz
['mac_address'] = mac_address
377 async_nic_creation
= self
.conn_vnet
.network_interfaces
.create_or_update(self
.resource_group
, nic_name
, net_ifz
)
378 nic_data
= async_nic_creation
.result()
379 created_items
[nic_data
.id] = True
380 self
.logger
.debug('created nic name %s', nic_name
)
382 public_ip
= net
.get('floating_ip')
384 public_ip_address_params
= {
385 'location': location
,
386 'public_ip_allocation_method': 'Dynamic'
388 public_ip_name
= nic_name
+ '-public-ip'
389 async_public_ip
= self
.conn_vnet
.public_ip_addresses
.create_or_update(
392 public_ip_address_params
394 public_ip
= async_public_ip
.result()
395 self
.logger
.debug('created public IP: {}'.format(public_ip
))
397 # Associate NIC to Public IP
398 nic_data
= self
.conn_vnet
.network_interfaces
.get(
402 nic_data
.ip_configurations
[0].public_ip_address
= public_ip
403 created_items
[public_ip
.id] = True
405 self
.conn_vnet
.network_interfaces
.create_or_update(
410 except Exception as e
:
411 self
._format
_vimconn
_exception
(e
)
413 return nic_data
, created_items
415 def new_flavor(self
, flavor_data
):
417 It is not allowed to create new flavors in Azure, must always use an existing one
419 raise vimconn
.vimconnAuthException("It is not possible to create new flavors in AZURE")
421 def new_tenant(self
, tenant_name
, tenant_description
):
423 It is not allowed to create new tenants in azure
425 raise vimconn
.vimconnAuthException("It is not possible to create a TENANT in AZURE")
427 def new_image(self
, image_dict
):
429 It is not allowed to create new images in Azure, must always use an existing one
431 raise vimconn
.vimconnAuthException("It is not possible to create new images in AZURE")
433 def get_image_id_from_path(self
, path
):
434 """Get the image id from image path in the VIM database.
435 Returns the image_id or raises a vimconnNotFoundException
437 raise vimconn
.vimconnAuthException("It is not possible to obtain image from path in AZURE")
439 def get_image_list(self
, filter_dict
={}):
440 """Obtain tenant images from VIM
442 name: image name with the format: publisher:offer:sku:version
443 If some part of the name is provide ex: publisher:offer it will search all availables skus and version
444 for the provided publisher and offer
445 id: image uuid, currently not supported for azure
446 Returns the image list of dictionaries:
447 [{<the fields at Filter_dict plus some VIM specific>}, ...]
451 self
.logger
.debug("get_image_list filter {}".format(filter_dict
))
453 self
._reload
_connection
()
456 if filter_dict
.get("name"):
457 # name will have the format 'publisher:offer:sku:version'
458 # publisher is required, offer sku and version will be searched if not provided
459 params
= filter_dict
["name"].split(":")
460 publisher
= params
[0]
463 offer_list
= self
._get
_offer
_list
(params
, publisher
)
464 for offer
in offer_list
:
466 sku_list
= self
._get
_sku
_list
(params
, publisher
, offer
)
468 # if version is defined get directly version, else list images
469 if len(params
) == 4 and params
[3]:
471 image_list
= self
._get
_version
_image
_list
(publisher
, offer
, sku
, version
)
473 image_list
= self
._get
_sku
_image
_list
(publisher
, offer
, sku
)
475 raise vimconn
.vimconnAuthException(
476 "List images in Azure must include name param with at least publisher")
478 raise vimconn
.vimconnAuthException("List images in Azure must include name param with at"
482 except Exception as e
:
483 self
._format
_vimconn
_exception
(e
)
485 def _get_offer_list(self
, params
, publisher
):
487 Helper method to obtain offer list for defined publisher
489 if len(params
) >= 2 and params
[1]:
493 # get list of offers from azure
494 result_offers
= self
.conn_compute
.virtual_machine_images
.list_offers(self
.region
, publisher
)
495 return [offer
.name
for offer
in result_offers
]
496 except CloudError
as e
:
497 # azure raises CloudError when not found
498 self
.logger
.info("error listing offers for publisher {}, Error: {}".format(publisher
, e
))
501 def _get_sku_list(self
, params
, publisher
, offer
):
503 Helper method to obtain sku list for defined publisher and offer
505 if len(params
) >= 3 and params
[2]:
509 # get list of skus from azure
510 result_skus
= self
.conn_compute
.virtual_machine_images
.list_skus(self
.region
, publisher
, offer
)
511 return [sku
.name
for sku
in result_skus
]
512 except CloudError
as e
:
513 # azure raises CloudError when not found
514 self
.logger
.info("error listing skus for publisher {}, offer {}, Error: {}".format(publisher
, offer
, e
))
517 def _get_sku_image_list(self
, publisher
, offer
, sku
):
519 Helper method to obtain image list for publisher, offer and sku
523 result_images
= self
.conn_compute
.virtual_machine_images
.list(self
.region
, publisher
, offer
, sku
)
524 for result_image
in result_images
:
526 'id': str(result_image
.id),
527 'name': ":".join([publisher
, offer
, sku
, result_image
.name
])
529 except CloudError
as e
:
531 "error listing skus for publisher {}, offer {}, Error: {}".format(publisher
, offer
, e
))
535 def _get_version_image_list(self
, publisher
, offer
, sku
, version
):
538 result_image
= self
.conn_compute
.virtual_machine_images
.get(self
.region
, publisher
, offer
, sku
, version
)
541 'id': str(result_image
.id),
542 'name': ":".join([publisher
, offer
, sku
, version
])
544 except CloudError
as e
:
545 # azure gives CloudError when not found
546 self
.logger
.info("error listing images for publisher {}, offer {}, sku {}, version {} Error: {}".
547 format(publisher
, offer
, sku
, version
, e
))
551 def get_network_list(self
, filter_dict
={}):
552 """Obtain tenant networks of VIM
556 shared: boolean, not implemented in Azure
557 tenant_id: tenant, not used in Azure, all networks same tenants
558 admin_state_up: boolean, not implemented in Azure
559 status: 'ACTIVE', not implemented in Azure #
560 Returns the network list of dictionaries
562 # self.logger.debug('getting network list for vim, filter %s', filter_dict)
564 self
._reload
_connection
()
566 vnet
= self
.conn_vnet
.virtual_networks
.get(self
.resource_group
, self
.vnet_name
)
569 for subnet
in vnet
.subnets
:
571 if filter_dict
.get("id") and str(subnet
.id) != filter_dict
["id"]:
573 if filter_dict
.get("name") and \
574 str(subnet
.name
) != filter_dict
["name"]:
577 name
= self
._get
_resource
_name
_from
_resource
_id
(subnet
.id)
580 'id': str(subnet
.id),
582 'status': self
.provision_state2osm
[subnet
.provisioning_state
],
583 'cidr_block': str(subnet
.address_prefix
),
589 except Exception as e
:
590 self
._format
_vimconn
_exception
(e
)
592 def new_vminstance(self
, name
, description
, start
, image_id
, flavor_id
, net_list
, cloud_config
=None,
593 disk_list
=None, availability_zone_index
=None, availability_zone_list
=None):
595 self
.logger
.debug("new vm instance name: %s, image_id: %s, flavor_id: %s, net_list: %s, cloud_config: %s, "
596 "disk_list: %s, availability_zone_index: %s, availability_zone_list: %s",
597 name
, image_id
, flavor_id
, net_list
, cloud_config
, disk_list
,
598 availability_zone_index
, availability_zone_list
)
600 self
._reload
_connection
()
602 # Validate input data is valid
603 # The virtual machine name must have less or 64 characters and it can not have the following
604 # characters: (~ ! @ # $ % ^ & * ( ) = + _ [ ] { } \ | ; : ' " , < > / ?.)
605 vm_name
= self
._check
_vm
_name
(name
)
606 # Obtain vm unused name
607 vm_name
= self
._get
_unused
_vm
_name
(vm_name
)
609 # At least one network must be provided
611 raise vimconn
.vimconnException("At least one net must be provided to create a new VM")
613 # image_id are several fields of the image_id
614 image_reference
= self
._get
_image
_reference
(image_id
)
619 virtual_machine
= None
622 # Create nics for each subnet
623 self
._check
_subnets
_for
_vm
(net_list
)
625 for idx
, net
in enumerate(net_list
):
626 # Fault with subnet_id
627 # subnet_id=net['subnet_id']
628 # subnet_id=net['net_id']
629 nic_name
= vm_name
+ '-nic-' + str(idx
)
630 vm_nic
, nic_items
= self
._create
_nic
(net
, nic_name
, net
.get('ip_address'), created_items
)
631 vm_nics
.append({'id': str(vm_nic
.id)})
632 #net['vim_id'] = vm_nic.id
634 # cloud-init configuration
637 config_drive
, userdata
= self
._create
_user
_data
(cloud_config
)
638 custom_data
= base64
.b64encode(userdata
.encode('utf-8')).decode('latin-1')
640 key_pairs
= cloud_config
.get("key-pairs")
642 key_data
= key_pairs
[0]
644 if cloud_config
.get("users"):
645 user_name
= cloud_config
.get("users")[0].get("name", "osm")
647 user_name
= "osm" # DEFAULT USER IS OSM
650 'computer_name': vm_name
,
651 'admin_username': user_name
,
652 'linux_configuration': {
653 "disable_password_authentication": True,
656 "path": "/home/{}/.ssh/authorized_keys".format(user_name
),
661 'custom_data': custom_data
665 'computer_name': vm_name
,
666 'admin_username': 'osm',
667 'admin_password': 'Osm4u!',
671 'location': self
.region
,
672 'os_profile': os_profile
,
673 'hardware_profile': {
677 'image_reference': image_reference
681 # If the machine has several networks one must be marked as primary
682 # As it is not indicated in the interface the first interface will be marked as primary
684 for idx
, vm_nic
in enumerate(vm_nics
):
686 vm_nics
[0]['Primary'] = True
688 vm_nics
[idx
]['Primary'] = False
690 vm_parameters
['network_profile'] = {'network_interfaces': vm_nics
}
692 # Obtain zone information
693 vm_zone
= self
._get
_vm
_zone
(availability_zone_index
, availability_zone_list
)
695 vm_parameters
['zones'] = [vm_zone
]
697 self
.logger
.debug("create vm name: %s", vm_name
)
698 creation_result
= self
.conn_compute
.virtual_machines
.create_or_update(
703 virtual_machine
= creation_result
.result()
704 self
.logger
.debug("created vm name: %s", vm_name
)
706 # Add disks if they are provided
708 for disk_index
, disk
in enumerate(disk_list
):
709 self
.logger
.debug("add disk size: %s, image: %s", disk
.get("size"), disk
.get("image"))
710 self
._add
_newvm
_disk
(virtual_machine
, vm_name
, disk_index
, disk
, created_items
)
713 self
.conn_compute
.virtual_machines
.start(
716 # start_result.wait()
718 return virtual_machine
.id, created_items
720 # run_command_parameters = {
721 # 'command_id': 'RunShellScript', # For linux, don't change it
723 # 'date > /tmp/test.txt'
726 except Exception as e
:
727 # Rollback vm creacion
730 vm_id
= virtual_machine
.id
732 self
.logger
.debug("exception creating vm try to rollback")
733 self
.delete_vminstance(vm_id
, created_items
)
734 except Exception as e2
:
735 self
.logger
.error("new_vminstance rollback fail {}".format(e2
))
737 self
.logger
.debug('Exception creating new vminstance: %s', e
, exc_info
=True)
738 self
._format
_vimconn
_exception
(e
)
740 def _get_unused_vm_name(self
, vm_name
):
742 Checks the vm name and in case it is used adds a suffix to the name to allow creation
745 all_vms
= self
.conn_compute
.virtual_machines
.list(self
.resource_group
)
746 # Filter to vms starting with the indicated name
747 vms
= list(filter(lambda vm
: (vm
.name
.startswith(vm_name
)), all_vms
))
748 vm_names
= [str(vm
.name
) for vm
in vms
]
750 # get the name with the first not used suffix
752 # name = subnet_name + "-" + str(name_suffix)
753 name
= vm_name
# first subnet created will have no prefix
754 while name
in vm_names
:
756 name
= vm_name
+ "-" + str(name_suffix
)
759 def _get_vm_zone(self
, availability_zone_index
, availability_zone_list
):
761 if availability_zone_index
is None:
764 vim_availability_zones
= self
._get
_azure
_availability
_zones
()
765 # check if VIM offer enough availability zones describe in the VNFD
766 if vim_availability_zones
and len(availability_zone_list
) <= len(vim_availability_zones
):
767 # check if all the names of NFV AV match VIM AV names
768 match_by_index
= False
769 if not availability_zone_list
:
770 match_by_index
= True
772 for av
in availability_zone_list
:
773 if av
not in vim_availability_zones
:
774 match_by_index
= True
777 return vim_availability_zones
[availability_zone_index
]
779 return availability_zone_list
[availability_zone_index
]
781 raise vimconn
.vimconnConflictException("No enough availability zones at VIM for this deployment")
783 def _get_azure_availability_zones(self
):
784 return self
.AZURE_ZONES
786 def _add_newvm_disk(self
, virtual_machine
, vm_name
, disk_index
, disk
, created_items
={}):
791 # Check if must create empty disk or from image
792 if disk
.get('vim_id'):
793 # disk already exists, just get
794 parsed_id
= azure_tools
.parse_resource_id(disk
.get('vim_id'))
795 disk_name
= parsed_id
.get("name")
796 data_disk
= self
.conn_compute
.disks
.get(self
.resource_group
, disk_name
)
798 disk_name
= vm_name
+ "_DataDisk_" + str(disk_index
)
799 if not disk
.get("image_id"):
800 self
.logger
.debug("create new data disk name: %s", disk_name
)
801 async_disk_creation
= self
.conn_compute
.disks
.create_or_update(
805 'location': self
.region
,
806 'disk_size_gb': disk
.get("size"),
808 'create_option': DiskCreateOption
.empty
812 data_disk
= async_disk_creation
.result()
813 created_items
[data_disk
.id] = True
815 image_id
= disk
.get("image_id")
816 if azure_tools
.is_valid_resource_id(image_id
):
817 parsed_id
= azure_tools
.parse_resource_id(image_id
)
819 # Check if image is snapshot or disk
820 image_name
= parsed_id
.get("name")
821 type = parsed_id
.get("resource_type")
822 if type == 'snapshots' or type == 'disks':
824 self
.logger
.debug("create disk from copy name: %s", image_name
)
825 # ¿Should check that snapshot exists?
826 async_disk_creation
= self
.conn_compute
.disks
.create_or_update(
830 'location': self
.region
,
832 'create_option': 'Copy',
833 'source_uri': image_id
837 data_disk
= async_disk_creation
.result()
838 created_items
[data_disk
.id] = True
841 raise vimconn
.vimconnNotFoundException("Invalid image_id: %s ", image_id
)
843 raise vimconn
.vimconnNotFoundException("Invalid image_id: %s ", image_id
)
845 # Attach the disk created
846 virtual_machine
.storage_profile
.data_disks
.append({
849 'create_option': DiskCreateOption
.attach
,
853 'disk_size_gb': disk
.get('size')
855 self
.logger
.debug("attach disk name: %s", disk_name
)
856 async_disk_attach
= self
.conn_compute
.virtual_machines
.create_or_update(
858 virtual_machine
.name
,
862 # It is necesary extract from image_id data to create the VM with this format
863 # 'image_reference': {
864 # 'publisher': vm_reference['publisher'],
865 # 'offer': vm_reference['offer'],
866 # 'sku': vm_reference['sku'],
867 # 'version': vm_reference['version']
869 def _get_image_reference(self
, image_id
):
872 # The data input format example:
873 # /Subscriptions/ca3d18ab-d373-4afb-a5d6-7c44f098d16a/Providers/Microsoft.Compute/Locations/westeurope/
874 # Publishers/Canonical/ArtifactTypes/VMImage/
875 # Offers/UbuntuServer/
877 # Versions/18.04.201809110
878 publisher
= str(image_id
.split('/')[8])
879 offer
= str(image_id
.split('/')[12])
880 sku
= str(image_id
.split('/')[14])
881 version
= str(image_id
.split('/')[16])
884 'publisher': publisher
,
889 except Exception as e
:
890 raise vimconn
.vimconnException(
891 "Unable to get image_reference from invalid image_id format: '{}'".format(image_id
))
893 # Azure VM names can not have some special characters
894 def _check_vm_name(self
, vm_name
):
896 Checks vm name, in case the vm has not allowed characters they are removed, not error raised
899 chars_not_allowed_list
= "~!@#$%^&*()=+_[]{}|;:<>/?."
901 # First: the VM name max length is 64 characters
902 vm_name_aux
= vm_name
[:64]
904 # Second: replace not allowed characters
905 for elem
in chars_not_allowed_list
:
906 # Check if string is in the main string
907 if elem
in vm_name_aux
:
908 # self.logger.debug('Dentro del IF')
910 vm_name_aux
= vm_name_aux
.replace(elem
, '-')
914 def get_flavor_id_from_data(self
, flavor_dict
):
916 self
.logger
.debug("getting flavor id from data, flavor_dict: %s", flavor_dict
)
917 filter_dict
= flavor_dict
or {}
919 self
._reload
_connection
()
920 vm_sizes_list
= [vm_size
.serialize() for vm_size
in
921 self
.conn_compute
.virtual_machine_sizes
.list(self
.region
)]
923 cpus
= filter_dict
.get('vcpus') or 0
924 memMB
= filter_dict
.get('ram') or 0
927 if self
._config
.get("flavors_pattern"):
928 filtered_sizes
= [size
for size
in vm_sizes_list
if size
['numberOfCores'] >= cpus
and
929 size
['memoryInMB'] >= memMB
and
930 re
.search(self
._config
.get("flavors_pattern"), size
["name"])]
932 filtered_sizes
= [size
for size
in vm_sizes_list
if size
['numberOfCores'] >= cpus
and
933 size
['memoryInMB'] >= memMB
]
936 listedFilteredSizes
= sorted(filtered_sizes
, key
=lambda k
: (k
['numberOfCores'], k
['memoryInMB'],
937 k
['resourceDiskSizeInMB']))
939 if listedFilteredSizes
:
940 return listedFilteredSizes
[0]['name']
941 raise vimconn
.vimconnNotFoundException("Cannot find any flavor matching '{}'".format(str(flavor_dict
)))
943 except Exception as e
:
944 self
._format
_vimconn
_exception
(e
)
946 def _get_flavor_id_from_flavor_name(self
, flavor_name
):
948 # self.logger.debug("getting flavor id from flavor name {}".format(flavor_name))
950 self
._reload
_connection
()
951 vm_sizes_list
= [vm_size
.serialize() for vm_size
in
952 self
.conn_compute
.virtual_machine_sizes
.list(self
.region
)]
955 for size
in vm_sizes_list
:
956 if size
['name'] == flavor_name
:
959 # None is returned if not found anything
962 except Exception as e
:
963 self
._format
_vimconn
_exception
(e
)
965 def check_vim_connectivity(self
):
967 self
._reload
_connection
()
969 except Exception as e
:
970 raise vimconn
.vimconnException("Connectivity issue with Azure API: {}".format(e
))
972 def get_network(self
, net_id
):
974 # self.logger.debug('get network id: {}'.format(net_id))
975 # res_name = self._get_resource_name_from_resource_id(net_id)
976 self
._reload
_connection
()
978 filter_dict
= {'name': net_id
}
979 network_list
= self
.get_network_list(filter_dict
)
982 raise vimconn
.vimconnNotFoundException("network '{}' not found".format(net_id
))
984 return network_list
[0]
986 def delete_network(self
, net_id
, created_items
=None):
988 self
.logger
.debug('deleting network {} - {}'.format(self
.resource_group
, net_id
))
990 self
._reload
_connection
()
991 res_name
= self
._get
_resource
_name
_from
_resource
_id
(net_id
)
992 filter_dict
= {'name': res_name
}
993 network_list
= self
.get_network_list(filter_dict
)
995 raise vimconn
.vimconnNotFoundException("network '{}' not found".format(net_id
))
998 # Subnet API fails (CloudError: Azure Error: ResourceNotFound)
999 # Put the initial virtual_network API
1000 async_delete
= self
.conn_vnet
.subnets
.delete(self
.resource_group
, self
.vnet_name
, res_name
)
1004 except CloudError
as e
:
1005 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
1006 raise vimconn
.vimconnNotFoundException("network '{}' not found".format(net_id
))
1008 self
._format
_vimconn
_exception
(e
)
1009 except Exception as e
:
1010 self
._format
_vimconn
_exception
(e
)
1012 def delete_vminstance(self
, vm_id
, created_items
=None):
1013 """ Deletes a vm instance from the vim.
1015 self
.logger
.debug('deleting VM instance {} - {}'.format(self
.resource_group
, vm_id
))
1016 self
._reload
_connection
()
1018 created_items
= created_items
or {}
1020 # Check vm exists, we can call delete_vm to clean created_items
1022 res_name
= self
._get
_resource
_name
_from
_resource
_id
(vm_id
)
1023 vm
= self
.conn_compute
.virtual_machines
.get(self
.resource_group
, res_name
)
1025 # Shuts down the virtual machine and releases the compute resources
1026 # vm_stop = self.conn_compute.virtual_machines.power_off(self.resource_group, resName)
1029 vm_delete
= self
.conn_compute
.virtual_machines
.delete(self
.resource_group
, res_name
)
1031 self
.logger
.debug('deleted VM name: %s', res_name
)
1034 os_disk_name
= vm
.storage_profile
.os_disk
.name
1035 self
.logger
.debug('delete OS DISK: %s', os_disk_name
)
1036 async_disk_delete
= self
.conn_compute
.disks
.delete(self
.resource_group
, os_disk_name
)
1037 async_disk_delete
.wait()
1038 # os disks are created always with the machine
1039 self
.logger
.debug('deleted OS DISK name: %s', os_disk_name
)
1041 for data_disk
in vm
.storage_profile
.data_disks
:
1042 self
.logger
.debug('delete data_disk: %s', data_disk
.name
)
1043 async_disk_delete
= self
.conn_compute
.disks
.delete(self
.resource_group
, data_disk
.name
)
1044 async_disk_delete
.wait()
1045 self
._markdel
_created
_item
(data_disk
.managed_disk
.id, created_items
)
1046 self
.logger
.debug('deleted OS DISK name: %s', data_disk
.name
)
1048 # After deleting VM, it is necessary to delete NIC, because if is not deleted delete_network
1049 # does not work because Azure says that is in use the subnet
1050 network_interfaces
= vm
.network_profile
.network_interfaces
1052 for network_interface
in network_interfaces
:
1054 nic_name
= self
._get
_resource
_name
_from
_resource
_id
(network_interface
.id)
1055 nic_data
= self
.conn_vnet
.network_interfaces
.get(
1056 self
.resource_group
,
1059 public_ip_name
= None
1060 exist_public_ip
= nic_data
.ip_configurations
[0].public_ip_address
1062 public_ip_id
= nic_data
.ip_configurations
[0].public_ip_address
.id
1065 public_ip_name
= self
._get
_resource
_name
_from
_resource
_id
(public_ip_id
)
1067 # Public ip must be deleted afterwards of nic that is attached
1069 self
.logger
.debug('delete NIC name: %s', nic_name
)
1070 nic_delete
= self
.conn_vnet
.network_interfaces
.delete(self
.resource_group
, nic_name
)
1072 self
._markdel
_created
_item
(network_interface
.id, created_items
)
1073 self
.logger
.debug('deleted NIC name: %s', nic_name
)
1075 # Delete list of public ips
1077 self
.logger
.debug('delete PUBLIC IP - ' + public_ip_name
)
1078 ip_delete
= self
.conn_vnet
.public_ip_addresses
.delete(self
.resource_group
, public_ip_name
)
1080 self
._markdel
_created
_item
(public_ip_id
, created_items
)
1082 # Delete created items
1083 self
._delete
_created
_items
(created_items
)
1085 except CloudError
as e
:
1086 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
1087 raise vimconn
.vimconnNotFoundException("No vm instance found '{}'".format(vm_id
))
1089 self
._format
_vimconn
_exception
(e
)
1090 except Exception as e
:
1091 self
._format
_vimconn
_exception
(e
)
1093 def _markdel_created_item(self
, item_id
, created_items
):
1094 if item_id
in created_items
:
1095 created_items
[item_id
] = False
1097 def _delete_created_items(self
, created_items
):
1098 """ Delete created_items elements that have not been deleted with the virtual machine
1099 Created_items may not be deleted correctly with the created machine if the
1100 virtual machine fails creating or in other cases of error
1102 self
.logger
.debug("Created items: %s", created_items
)
1103 # Must delete in order first nics, then public_ips
1104 # As dictionaries don't preserve order, first get items to be deleted then delete them
1106 publics_ip_to_delete
= []
1107 disks_to_delete
= []
1108 for item_id
, v
in created_items
.items():
1109 if not v
: # skip already deleted
1112 #self.logger.debug("Must delete item id: %s", item_id)
1114 # Obtain type, supported nic, disk or public ip
1115 parsed_id
= azure_tools
.parse_resource_id(item_id
)
1116 resource_type
= parsed_id
.get("resource_type")
1117 name
= parsed_id
.get("name")
1119 if resource_type
== "networkInterfaces":
1120 nics_to_delete
.append(name
)
1121 elif resource_type
== "publicIPAddresses":
1122 publics_ip_to_delete
.append(name
)
1123 elif resource_type
== "disks":
1124 disks_to_delete
.append(name
)
1127 for item_name
in nics_to_delete
:
1129 self
.logger
.debug("deleting nic name %s:", item_name
)
1130 nic_delete
= self
.conn_vnet
.network_interfaces
.delete(self
.resource_group
, item_name
)
1132 self
.logger
.debug("deleted nic name %s:", item_name
)
1133 except Exception as e
:
1134 self
.logger
.error("Error deleting item: {}: {}".format(type(e
).__name
__, e
))
1136 for item_name
in publics_ip_to_delete
:
1138 self
.logger
.debug("deleting public ip name %s:", item_name
)
1139 ip_delete
= self
.conn_vnet
.public_ip_addresses
.delete(self
.resource_group
, name
)
1141 self
.logger
.debug("deleted public ip name %s:", item_name
)
1142 except Exception as e
:
1143 self
.logger
.error("Error deleting item: {}: {}".format(type(e
).__name
__, e
))
1145 for item_name
in disks_to_delete
:
1147 self
.logger
.debug("deleting data disk name %s:", name
)
1148 async_disk_delete
= self
.conn_compute
.disks
.delete(self
.resource_group
, item_name
)
1149 async_disk_delete
.wait()
1150 self
.logger
.debug("deleted data disk name %s:", name
)
1151 except Exception as e
:
1152 self
.logger
.error("Error deleting item: {}: {}".format(type(e
).__name
__, e
))
1154 def action_vminstance(self
, vm_id
, action_dict
, created_items
={}):
1155 """Send and action over a VM instance from VIM
1156 Returns the vm_id if the action was successfully sent to the VIM
1159 self
.logger
.debug("Action over VM '%s': %s", vm_id
, str(action_dict
))
1161 self
._reload
_connection
()
1162 resName
= self
._get
_resource
_name
_from
_resource
_id
(vm_id
)
1163 if "start" in action_dict
:
1164 self
.conn_compute
.virtual_machines
.start(self
.resource_group
, resName
)
1165 elif "stop" in action_dict
or "shutdown" in action_dict
or "shutoff" in action_dict
:
1166 self
.conn_compute
.virtual_machines
.power_off(self
.resource_group
, resName
)
1167 elif "terminate" in action_dict
:
1168 self
.conn_compute
.virtual_machines
.delete(self
.resource_group
, resName
)
1169 elif "reboot" in action_dict
:
1170 self
.conn_compute
.virtual_machines
.restart(self
.resource_group
, resName
)
1172 except CloudError
as e
:
1173 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
1174 raise vimconn
.vimconnNotFoundException("No vm found '{}'".format(vm_id
))
1176 self
._format
_vimconn
_exception
(e
)
1177 except Exception as e
:
1178 self
._format
_vimconn
_exception
(e
)
1180 def delete_flavor(self
, flavor_id
):
1181 raise vimconn
.vimconnAuthException("It is not possible to delete a FLAVOR in AZURE")
1183 def delete_tenant(self
, tenant_id
,):
1184 raise vimconn
.vimconnAuthException("It is not possible to delete a TENANT in AZURE")
1186 def delete_image(self
, image_id
):
1187 raise vimconn
.vimconnAuthException("It is not possible to delete a IMAGE in AZURE")
1189 def get_vminstance(self
, vm_id
):
1191 Obtaing the vm instance data from v_id
1193 self
.logger
.debug("get vm instance: %s", vm_id
)
1194 self
._reload
_connection
()
1196 resName
= self
._get
_resource
_name
_from
_resource
_id
(vm_id
)
1197 vm
= self
.conn_compute
.virtual_machines
.get(self
.resource_group
, resName
)
1198 except CloudError
as e
:
1199 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
1200 raise vimconn
.vimconnNotFoundException("No vminstance found '{}'".format(vm_id
))
1202 self
._format
_vimconn
_exception
(e
)
1203 except Exception as e
:
1204 self
._format
_vimconn
_exception
(e
)
1208 def get_flavor(self
, flavor_id
):
1210 Obtains the flavor_data from the flavor_id
1212 self
._reload
_connection
()
1213 self
.logger
.debug("get flavor from id: %s", flavor_id
)
1214 flavor_data
= self
._get
_flavor
_id
_from
_flavor
_name
(flavor_id
)
1219 'ram': flavor_data
['memoryInMB'],
1220 'vcpus': flavor_data
['numberOfCores'],
1221 'disk': flavor_data
['resourceDiskSizeInMB']/1024
1225 raise vimconn
.vimconnNotFoundException("flavor '{}' not found".format(flavor_id
))
1227 def get_tenant_list(self
, filter_dict
={}):
1228 """ Obtains the list of tenants
1229 For the azure connector only the azure tenant will be returned if it is compatible
1232 tenants_azure
= [{'name': self
.tenant
, 'id': self
.tenant
}]
1235 self
.logger
.debug("get tenant list: %s", filter_dict
)
1236 for tenant_azure
in tenants_azure
:
1238 if filter_dict
.get("id") and str(tenant_azure
.get("id")) != filter_dict
["id"]:
1240 if filter_dict
.get("name") and str(tenant_azure
.get("name")) != filter_dict
["name"]:
1243 tenant_list
.append(tenant_azure
)
1247 def refresh_nets_status(self
, net_list
):
1248 """Get the status of the networks
1249 Params: the list of network identifiers
1250 Returns a dictionary with:
1251 net_id: #VIM id of this network
1252 status: #Mandatory. Text with one of:
1253 # DELETED (not found at vim)
1254 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1255 # OTHER (Vim reported other status not understood)
1256 # ERROR (VIM indicates an ERROR status)
1257 # ACTIVE, INACTIVE, DOWN (admin down),
1258 # BUILD (on building process)
1260 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1261 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1266 self
._reload
_connection
()
1268 self
.logger
.debug("reload nets status net_list: %s", net_list
)
1269 for net_id
in net_list
:
1271 netName
= self
._get
_net
_name
_from
_resource
_id
(net_id
)
1272 resName
= self
._get
_resource
_name
_from
_resource
_id
(net_id
)
1274 net
= self
.conn_vnet
.subnets
.get(self
.resource_group
, netName
, resName
)
1276 out_nets
[net_id
] = {
1277 "status": self
.provision_state2osm
[net
.provisioning_state
],
1278 "vim_info": str(net
)
1280 except CloudError
as e
:
1281 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
1282 self
.logger
.info("Not found subnet net_name: %s, subnet_name: %s", netName
, resName
)
1283 out_nets
[net_id
] = {
1284 "status": "DELETED",
1288 self
.logger
.error("CloudError Exception %s when searching subnet", e
)
1289 out_nets
[net_id
] = {
1290 "status": "VIM_ERROR",
1293 except vimconn
.vimconnNotFoundException
as e
:
1294 self
.logger
.error("VimConnNotFoundException %s when searching subnet", e
)
1295 out_nets
[net_id
] = {
1296 "status": "DELETED",
1299 except Exception as e
:
1300 self
.logger
.error("Exception %s when searching subnet", e
, exc_info
=True)
1301 out_nets
[net_id
] = {
1302 "status": "VIM_ERROR",
1307 def refresh_vms_status(self
, vm_list
):
1308 """ Get the status of the virtual machines and their interfaces/ports
1309 Params: the list of VM identifiers
1310 Returns a dictionary with:
1311 vm_id: # VIM id of this Virtual Machine
1312 status: # Mandatory. Text with one of:
1313 # DELETED (not found at vim)
1314 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1315 # OTHER (Vim reported other status not understood)
1316 # ERROR (VIM indicates an ERROR status)
1317 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1318 # BUILD (on building process), ERROR
1319 # ACTIVE:NoMgmtIP (Active but none of its interfaces has an IP address
1320 # (ACTIVE:NoMgmtIP is not returned for Azure)
1322 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1323 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1324 interfaces: list with interface info. Each item a dictionary with:
1325 vim_interface_id - The ID of the interface
1326 mac_address - The MAC address of the interface.
1327 ip_address - The IP address of the interface within the subnet.
1331 self
._reload
_connection
()
1333 self
.logger
.debug("refresh vm status vm_list: %s", vm_list
)
1334 search_vm_list
= vm_list
or {}
1336 for vm_id
in search_vm_list
:
1339 res_name
= self
._get
_resource
_name
_from
_resource
_id
(vm_id
)
1341 vm
= self
.conn_compute
.virtual_machines
.get(self
.resource_group
, res_name
)
1342 out_vm
['vim_info'] = str(vm
)
1343 out_vm
['status'] = self
.provision_state2osm
.get(vm
.provisioning_state
, 'OTHER')
1344 if vm
.provisioning_state
== 'Succeeded':
1345 # check if machine is running or stopped
1346 instance_view
= self
.conn_compute
.virtual_machines
.instance_view(self
.resource_group
,
1348 for status
in instance_view
.statuses
:
1349 splitted_status
= status
.code
.split("/")
1350 if len(splitted_status
) == 2 and splitted_status
[0] == 'PowerState':
1351 out_vm
['status'] = self
.power_state2osm
.get(splitted_status
[1], 'OTHER')
1353 network_interfaces
= vm
.network_profile
.network_interfaces
1354 out_vm
['interfaces'] = self
._get
_vm
_interfaces
_status
(vm_id
, network_interfaces
)
1356 except CloudError
as e
:
1357 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
1358 self
.logger
.debug("Not found vm id: %s", vm_id
)
1359 out_vm
['status'] = "DELETED"
1360 out_vm
['error_msg'] = str(e
)
1361 out_vm
['vim_info'] = None
1363 # maybe connection error or another type of error, return vim error
1364 self
.logger
.error("Exception %s refreshing vm_status", e
)
1365 out_vm
['status'] = "VIM_ERROR"
1366 out_vm
['error_msg'] = str(e
)
1367 out_vm
['vim_info'] = None
1368 except Exception as e
:
1369 self
.logger
.error("Exception %s refreshing vm_status", e
, exc_info
=True)
1370 out_vm
['status'] = "VIM_ERROR"
1371 out_vm
['error_msg'] = str(e
)
1372 out_vm
['vim_info'] = None
1374 out_vms
[vm_id
] = out_vm
1378 def _get_vm_interfaces_status(self
, vm_id
, interfaces
):
1380 Gets the interfaces detail for a vm
1381 :param interfaces: List of interfaces.
1382 :return: Dictionary with list of interfaces including, vim_interface_id, mac_address and ip_address
1386 for network_interface
in interfaces
:
1388 nic_name
= self
._get
_resource
_name
_from
_resource
_id
(network_interface
.id)
1389 interface_dict
['vim_interface_id'] = network_interface
.id
1391 nic_data
= self
.conn_vnet
.network_interfaces
.get(
1392 self
.resource_group
,
1396 if nic_data
.ip_configurations
[0].public_ip_address
:
1397 self
.logger
.debug("Obtain public ip address")
1398 public_ip_name
= self
._get
_resource
_name
_from
_resource
_id
(
1399 nic_data
.ip_configurations
[0].public_ip_address
.id)
1400 public_ip
= self
.conn_vnet
.public_ip_addresses
.get(self
.resource_group
, public_ip_name
)
1401 self
.logger
.debug("Public ip address is: %s", public_ip
.ip_address
)
1402 ips
.append(public_ip
.ip_address
)
1404 private_ip
= nic_data
.ip_configurations
[0].private_ip_address
1405 ips
.append(private_ip
)
1407 interface_dict
['mac_address'] = nic_data
.mac_address
1408 interface_dict
['ip_address'] = ";".join(ips
)
1409 interface_list
.append(interface_dict
)
1411 return interface_list
1412 except Exception as e
:
1413 self
.logger
.error("Exception %s obtaining interface data for vm: %s, error: %s", vm_id
, e
, exc_info
=True)
1414 self
._format
_vimconn
_exception
(e
)
1417 if __name__
== "__main__":
1419 # Making some basic test
1422 needed_test_params
= {
1423 "client_id": "AZURE_CLIENT_ID",
1424 "secret": "AZURE_SECRET",
1425 "tenant": "AZURE_TENANT",
1426 "resource_group": "AZURE_RESOURCE_GROUP",
1427 "subscription_id": "AZURE_SUBSCRIPTION_ID",
1428 "vnet_name": "AZURE_VNET_NAME",
1432 for param
, env_var
in needed_test_params
.items():
1433 value
= getenv(env_var
)
1435 raise Exception("Provide a valid value for env '{}'".format(env_var
))
1436 test_params
[param
] = value
1439 'region_name': getenv("AZURE_REGION_NAME", 'westeurope'),
1440 'resource_group': getenv("AZURE_RESOURCE_GROUP"),
1441 'subscription_id': getenv("AZURE_SUBSCRIPTION_ID"),
1442 'pub_key': getenv("AZURE_PUB_KEY", None),
1443 'vnet_name': getenv("AZURE_VNET_NAME", 'myNetwork'),
1448 'description': 'new VM',
1449 'status': 'running',
1451 'publisher': 'Canonical',
1452 'offer': 'UbuntuServer',
1453 'sku': '16.04.0-LTS',
1456 'hardware_profile': {
1457 'vm_size': 'Standard_DS1_v2'
1465 'subnet_address': '10.1.2.0/24',
1466 # 'subnet_name': 'subnet-oam'
1468 ###########################
1470 azure
= vimconnector(vim_id
, vim_name
, tenant_id
=test_params
["tenant"], tenant_name
=None, url
=None, url_admin
=None,
1471 user
=test_params
["client_id"], passwd
=test_params
["secret"], log_level
=None, config
=config
)
1473 # azure.get_flavor_id_from_data("here")
1474 # subnets=azure.get_network_list()
1475 # azure.new_vminstance(virtualMachine['name'], virtualMachine['description'], virtualMachine['status'],
1476 # virtualMachine['image'], virtualMachine['hardware_profile']['vm_size'], subnets)
1478 azure
.new_network("mynet", None)
1479 net_id
= "/subscriptions/82f80cc1-876b-4591-9911-1fb5788384fd/resourceGroups/osmRG/providers/Microsoft."\
1480 "Network/virtualNetworks/test"
1481 net_id_not_found
= "/subscriptions/82f80cc1-876b-4591-9911-1fb5788384fd/resourceGroups/osmRG/providers/"\
1482 "Microsoft.Network/virtualNetworks/testALF"
1483 azure
.refresh_nets_status([net_id
, net_id_not_found
])