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_plugin
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
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])
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])
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
,
379 nic_data
= async_nic_creation
.result()
380 created_items
[nic_data
.id] = True
381 self
.logger
.debug('created nic name %s', nic_name
)
383 public_ip
= net
.get('floating_ip')
385 public_ip_address_params
= {
386 'location': location
,
387 'public_ip_allocation_method': 'Dynamic'
389 public_ip_name
= nic_name
+ '-public-ip'
390 async_public_ip
= self
.conn_vnet
.public_ip_addresses
.create_or_update(
393 public_ip_address_params
395 public_ip
= async_public_ip
.result()
396 self
.logger
.debug('created public IP: {}'.format(public_ip
))
398 # Associate NIC to Public IP
399 nic_data
= self
.conn_vnet
.network_interfaces
.get(
403 nic_data
.ip_configurations
[0].public_ip_address
= public_ip
404 created_items
[public_ip
.id] = True
406 self
.conn_vnet
.network_interfaces
.create_or_update(
411 except Exception as e
:
412 self
._format
_vimconn
_exception
(e
)
414 return nic_data
, created_items
416 def new_flavor(self
, flavor_data
):
418 It is not allowed to create new flavors in Azure, must always use an existing one
420 raise vimconn
.VimConnAuthException("It is not possible to create new flavors in AZURE")
422 def new_tenant(self
, tenant_name
, tenant_description
):
424 It is not allowed to create new tenants in azure
426 raise vimconn
.VimConnAuthException("It is not possible to create a TENANT in AZURE")
428 def new_image(self
, image_dict
):
430 It is not allowed to create new images in Azure, must always use an existing one
432 raise vimconn
.VimConnAuthException("It is not possible to create new images in AZURE")
434 def get_image_id_from_path(self
, path
):
435 """Get the image id from image path in the VIM database.
436 Returns the image_id or raises a vimconnNotFoundException
438 raise vimconn
.VimConnAuthException("It is not possible to obtain image from path in AZURE")
440 def get_image_list(self
, filter_dict
={}):
441 """Obtain tenant images from VIM
443 name: image name with the format: publisher:offer:sku:version
444 If some part of the name is provide ex: publisher:offer it will search all availables skus and version
445 for the provided publisher and offer
446 id: image uuid, currently not supported for azure
447 Returns the image list of dictionaries:
448 [{<the fields at Filter_dict plus some VIM specific>}, ...]
452 self
.logger
.debug("get_image_list filter {}".format(filter_dict
))
454 self
._reload
_connection
()
457 if filter_dict
.get("name"):
458 # name will have the format 'publisher:offer:sku:version'
459 # publisher is required, offer sku and version will be searched if not provided
460 params
= filter_dict
["name"].split(":")
461 publisher
= params
[0]
464 offer_list
= self
._get
_offer
_list
(params
, publisher
)
465 for offer
in offer_list
:
467 sku_list
= self
._get
_sku
_list
(params
, publisher
, offer
)
469 # if version is defined get directly version, else list images
470 if len(params
) == 4 and params
[3]:
472 image_list
= self
._get
_version
_image
_list
(publisher
, offer
, sku
, version
)
474 image_list
= self
._get
_sku
_image
_list
(publisher
, offer
, sku
)
476 raise vimconn
.VimConnAuthException(
477 "List images in Azure must include name param with at least publisher")
479 raise vimconn
.VimConnAuthException("List images in Azure must include name param with at"
483 except Exception as e
:
484 self
._format
_vimconn
_exception
(e
)
486 def _get_offer_list(self
, params
, publisher
):
488 Helper method to obtain offer list for defined publisher
490 if len(params
) >= 2 and params
[1]:
494 # get list of offers from azure
495 result_offers
= self
.conn_compute
.virtual_machine_images
.list_offers(self
.region
, publisher
)
496 return [offer
.name
for offer
in result_offers
]
497 except CloudError
as e
:
498 # azure raises CloudError when not found
499 self
.logger
.info("error listing offers for publisher {}, Error: {}".format(publisher
, e
))
502 def _get_sku_list(self
, params
, publisher
, offer
):
504 Helper method to obtain sku list for defined publisher and offer
506 if len(params
) >= 3 and params
[2]:
510 # get list of skus from azure
511 result_skus
= self
.conn_compute
.virtual_machine_images
.list_skus(self
.region
, publisher
, offer
)
512 return [sku
.name
for sku
in result_skus
]
513 except CloudError
as e
:
514 # azure raises CloudError when not found
515 self
.logger
.info("error listing skus for publisher {}, offer {}, Error: {}".format(publisher
, offer
, e
))
518 def _get_sku_image_list(self
, publisher
, offer
, sku
):
520 Helper method to obtain image list for publisher, offer and sku
524 result_images
= self
.conn_compute
.virtual_machine_images
.list(self
.region
, publisher
, offer
, sku
)
525 for result_image
in result_images
:
527 'id': str(result_image
.id),
528 'name': ":".join([publisher
, offer
, sku
, result_image
.name
])
530 except CloudError
as e
:
532 "error listing skus for publisher {}, offer {}, Error: {}".format(publisher
, offer
, e
))
536 def _get_version_image_list(self
, publisher
, offer
, sku
, version
):
539 result_image
= self
.conn_compute
.virtual_machine_images
.get(self
.region
, publisher
, offer
, sku
, version
)
542 'id': str(result_image
.id),
543 'name': ":".join([publisher
, offer
, sku
, version
])
545 except CloudError
as e
:
546 # azure gives CloudError when not found
547 self
.logger
.info("error listing images for publisher {}, offer {}, sku {}, version {} Error: {}".
548 format(publisher
, offer
, sku
, version
, e
))
552 def get_network_list(self
, filter_dict
={}):
553 """Obtain tenant networks of VIM
557 shared: boolean, not implemented in Azure
558 tenant_id: tenant, not used in Azure, all networks same tenants
559 admin_state_up: boolean, not implemented in Azure
560 status: 'ACTIVE', not implemented in Azure #
561 Returns the network list of dictionaries
563 # self.logger.debug('getting network list for vim, filter %s', filter_dict)
565 self
._reload
_connection
()
567 vnet
= self
.conn_vnet
.virtual_networks
.get(self
.resource_group
, self
.vnet_name
)
570 for subnet
in vnet
.subnets
:
572 if filter_dict
.get("id") and str(subnet
.id) != filter_dict
["id"]:
574 if filter_dict
.get("name") and \
575 str(subnet
.name
) != filter_dict
["name"]:
578 name
= self
._get
_resource
_name
_from
_resource
_id
(subnet
.id)
581 'id': str(subnet
.id),
583 'status': self
.provision_state2osm
[subnet
.provisioning_state
],
584 'cidr_block': str(subnet
.address_prefix
),
590 except Exception as e
:
591 self
._format
_vimconn
_exception
(e
)
593 def new_vminstance(self
, name
, description
, start
, image_id
, flavor_id
, net_list
, cloud_config
=None,
594 disk_list
=None, availability_zone_index
=None, availability_zone_list
=None):
596 self
.logger
.debug("new vm instance name: %s, image_id: %s, flavor_id: %s, net_list: %s, cloud_config: %s, "
597 "disk_list: %s, availability_zone_index: %s, availability_zone_list: %s",
598 name
, image_id
, flavor_id
, net_list
, cloud_config
, disk_list
,
599 availability_zone_index
, availability_zone_list
)
601 self
._reload
_connection
()
603 # Validate input data is valid
604 # The virtual machine name must have less or 64 characters and it can not have the following
605 # characters: (~ ! @ # $ % ^ & * ( ) = + _ [ ] { } \ | ; : ' " , < > / ?.)
606 vm_name
= self
._check
_vm
_name
(name
)
607 # Obtain vm unused name
608 vm_name
= self
._get
_unused
_vm
_name
(vm_name
)
610 # At least one network must be provided
612 raise vimconn
.VimConnException("At least one net must be provided to create a new VM")
614 # image_id are several fields of the image_id
615 image_reference
= self
._get
_image
_reference
(image_id
)
618 virtual_machine
= None
621 # Create nics for each subnet
622 self
._check
_subnets
_for
_vm
(net_list
)
624 for idx
, net
in enumerate(net_list
):
625 # Fault with subnet_id
626 # subnet_id=net['subnet_id']
627 # subnet_id=net['net_id']
628 nic_name
= vm_name
+ '-nic-' + str(idx
)
629 vm_nic
, nic_items
= self
._create
_nic
(net
, nic_name
, net
.get('ip_address'), created_items
)
630 vm_nics
.append({'id': str(vm_nic
.id)})
631 net
['vim_id'] = vm_nic
.id
633 # cloud-init configuration
636 config_drive
, userdata
= self
._create
_user
_data
(cloud_config
)
637 custom_data
= base64
.b64encode(userdata
.encode('utf-8')).decode('latin-1')
639 key_pairs
= cloud_config
.get("key-pairs")
641 key_data
= key_pairs
[0]
643 if cloud_config
.get("users"):
644 user_name
= cloud_config
.get("users")[0].get("name", "osm")
646 user_name
= "osm" # DEFAULT USER IS OSM
649 'computer_name': vm_name
,
650 'admin_username': user_name
,
651 'linux_configuration': {
652 "disable_password_authentication": True,
655 "path": "/home/{}/.ssh/authorized_keys".format(user_name
),
660 'custom_data': custom_data
664 'computer_name': vm_name
,
665 'admin_username': 'osm',
666 'admin_password': 'Osm4u!',
670 'location': self
.region
,
671 'os_profile': os_profile
,
672 'hardware_profile': {
676 'image_reference': image_reference
680 # If the machine has several networks one must be marked as primary
681 # As it is not indicated in the interface the first interface will be marked as primary
683 for idx
, vm_nic
in enumerate(vm_nics
):
685 vm_nics
[0]['Primary'] = True
687 vm_nics
[idx
]['Primary'] = False
689 vm_parameters
['network_profile'] = {'network_interfaces': vm_nics
}
691 # Obtain zone information
692 vm_zone
= self
._get
_vm
_zone
(availability_zone_index
, availability_zone_list
)
694 vm_parameters
['zones'] = [vm_zone
]
696 self
.logger
.debug("create vm name: %s", vm_name
)
697 creation_result
= self
.conn_compute
.virtual_machines
.create_or_update(
702 virtual_machine
= creation_result
.result()
703 self
.logger
.debug("created vm name: %s", vm_name
)
705 # Add disks if they are provided
707 for disk_index
, disk
in enumerate(disk_list
):
708 self
.logger
.debug("add disk size: %s, image: %s", disk
.get("size"), disk
.get("image"))
709 self
._add
_newvm
_disk
(virtual_machine
, vm_name
, disk_index
, disk
, created_items
)
712 self
.conn_compute
.virtual_machines
.start(
715 # start_result.wait()
717 return virtual_machine
.id, created_items
719 # run_command_parameters = {
720 # 'command_id': 'RunShellScript', # For linux, don't change it
722 # 'date > /tmp/test.txt'
725 except Exception as e
:
726 # Rollback vm creacion
729 vm_id
= virtual_machine
.id
731 self
.logger
.debug("exception creating vm try to rollback")
732 self
.delete_vminstance(vm_id
, created_items
)
733 except Exception as e2
:
734 self
.logger
.error("new_vminstance rollback fail {}".format(e2
))
736 self
.logger
.debug('Exception creating new vminstance: %s', e
, exc_info
=True)
737 self
._format
_vimconn
_exception
(e
)
739 def _get_unused_vm_name(self
, vm_name
):
741 Checks the vm name and in case it is used adds a suffix to the name to allow creation
744 all_vms
= self
.conn_compute
.virtual_machines
.list(self
.resource_group
)
745 # Filter to vms starting with the indicated name
746 vms
= list(filter(lambda vm
: (vm
.name
.startswith(vm_name
)), all_vms
))
747 vm_names
= [str(vm
.name
) for vm
in vms
]
749 # get the name with the first not used suffix
751 # name = subnet_name + "-" + str(name_suffix)
752 name
= vm_name
# first subnet created will have no prefix
753 while name
in vm_names
:
755 name
= vm_name
+ "-" + str(name_suffix
)
758 def _get_vm_zone(self
, availability_zone_index
, availability_zone_list
):
760 if availability_zone_index
is None:
763 vim_availability_zones
= self
._get
_azure
_availability
_zones
()
764 # check if VIM offer enough availability zones describe in the VNFD
765 if vim_availability_zones
and len(availability_zone_list
) <= len(vim_availability_zones
):
766 # check if all the names of NFV AV match VIM AV names
767 match_by_index
= False
768 if not availability_zone_list
:
769 match_by_index
= True
771 for av
in availability_zone_list
:
772 if av
not in vim_availability_zones
:
773 match_by_index
= True
776 return vim_availability_zones
[availability_zone_index
]
778 return availability_zone_list
[availability_zone_index
]
780 raise vimconn
.VimConnConflictException("No enough availability zones at VIM for this deployment")
782 def _get_azure_availability_zones(self
):
783 return self
.AZURE_ZONES
785 def _add_newvm_disk(self
, virtual_machine
, vm_name
, disk_index
, disk
, created_items
={}):
790 # Check if must create empty disk or from image
791 if disk
.get('vim_id'):
792 # disk already exists, just get
793 parsed_id
= azure_tools
.parse_resource_id(disk
.get('vim_id'))
794 disk_name
= parsed_id
.get("name")
795 data_disk
= self
.conn_compute
.disks
.get(self
.resource_group
, disk_name
)
797 disk_name
= vm_name
+ "_DataDisk_" + str(disk_index
)
798 if not disk
.get("image_id"):
799 self
.logger
.debug("create new data disk name: %s", disk_name
)
800 async_disk_creation
= self
.conn_compute
.disks
.create_or_update(
804 'location': self
.region
,
805 'disk_size_gb': disk
.get("size"),
807 'create_option': DiskCreateOption
.empty
811 data_disk
= async_disk_creation
.result()
812 created_items
[data_disk
.id] = True
814 image_id
= disk
.get("image_id")
815 if azure_tools
.is_valid_resource_id(image_id
):
816 parsed_id
= azure_tools
.parse_resource_id(image_id
)
818 # Check if image is snapshot or disk
819 image_name
= parsed_id
.get("name")
820 type = parsed_id
.get("resource_type")
821 if type == 'snapshots' or type == 'disks':
823 self
.logger
.debug("create disk from copy name: %s", image_name
)
824 # ¿Should check that snapshot exists?
825 async_disk_creation
= self
.conn_compute
.disks
.create_or_update(
829 'location': self
.region
,
831 'create_option': 'Copy',
832 'source_uri': image_id
836 data_disk
= async_disk_creation
.result()
837 created_items
[data_disk
.id] = True
840 raise vimconn
.VimConnNotFoundException("Invalid image_id: %s ", image_id
)
842 raise vimconn
.VimConnNotFoundException("Invalid image_id: %s ", image_id
)
844 # Attach the disk created
845 virtual_machine
.storage_profile
.data_disks
.append({
848 'create_option': DiskCreateOption
.attach
,
852 'disk_size_gb': disk
.get('size')
854 self
.logger
.debug("attach disk name: %s", disk_name
)
855 self
.conn_compute
.virtual_machines
.create_or_update(
857 virtual_machine
.name
,
861 # It is necesary extract from image_id data to create the VM with this format
862 # 'image_reference': {
863 # 'publisher': vm_reference['publisher'],
864 # 'offer': vm_reference['offer'],
865 # 'sku': vm_reference['sku'],
866 # 'version': vm_reference['version']
868 def _get_image_reference(self
, image_id
):
871 # The data input format example:
872 # /Subscriptions/ca3d18ab-d373-4afb-a5d6-7c44f098d16a/Providers/Microsoft.Compute/Locations/westeurope/
873 # Publishers/Canonical/ArtifactTypes/VMImage/
874 # Offers/UbuntuServer/
876 # Versions/18.04.201809110
877 publisher
= str(image_id
.split('/')[8])
878 offer
= str(image_id
.split('/')[12])
879 sku
= str(image_id
.split('/')[14])
880 version
= str(image_id
.split('/')[16])
883 'publisher': publisher
,
889 raise vimconn
.VimConnException(
890 "Unable to get image_reference from invalid image_id format: '{}'".format(image_id
))
892 # Azure VM names can not have some special characters
893 def _check_vm_name(self
, vm_name
):
895 Checks vm name, in case the vm has not allowed characters they are removed, not error raised
898 chars_not_allowed_list
= "~!@#$%^&*()=+_[]{}|;:<>/?."
900 # First: the VM name max length is 64 characters
901 vm_name_aux
= vm_name
[:64]
903 # Second: replace not allowed characters
904 for elem
in chars_not_allowed_list
:
905 # Check if string is in the main string
906 if elem
in vm_name_aux
:
907 # self.logger.debug('Dentro del IF')
909 vm_name_aux
= vm_name_aux
.replace(elem
, '-')
913 def get_flavor_id_from_data(self
, flavor_dict
):
915 self
.logger
.debug("getting flavor id from data, flavor_dict: %s", flavor_dict
)
916 filter_dict
= flavor_dict
or {}
918 self
._reload
_connection
()
919 vm_sizes_list
= [vm_size
.serialize() for vm_size
in
920 self
.conn_compute
.virtual_machine_sizes
.list(self
.region
)]
922 cpus
= filter_dict
.get('vcpus') or 0
923 memMB
= filter_dict
.get('ram') or 0
926 if self
._config
.get("flavors_pattern"):
927 filtered_sizes
= [size
for size
in vm_sizes_list
if size
['numberOfCores'] >= cpus
and
928 size
['memoryInMB'] >= memMB
and
929 re
.search(self
._config
.get("flavors_pattern"), size
["name"])]
931 filtered_sizes
= [size
for size
in vm_sizes_list
if size
['numberOfCores'] >= cpus
and
932 size
['memoryInMB'] >= memMB
]
935 listedFilteredSizes
= sorted(filtered_sizes
, key
=lambda k
: (k
['numberOfCores'], k
['memoryInMB'],
936 k
['resourceDiskSizeInMB']))
938 if listedFilteredSizes
:
939 return listedFilteredSizes
[0]['name']
940 raise vimconn
.VimConnNotFoundException("Cannot find any flavor matching '{}'".format(str(flavor_dict
)))
942 except Exception as e
:
943 self
._format
_vimconn
_exception
(e
)
945 def _get_flavor_id_from_flavor_name(self
, flavor_name
):
947 # self.logger.debug("getting flavor id from flavor name {}".format(flavor_name))
949 self
._reload
_connection
()
950 vm_sizes_list
= [vm_size
.serialize() for vm_size
in
951 self
.conn_compute
.virtual_machine_sizes
.list(self
.region
)]
954 for size
in vm_sizes_list
:
955 if size
['name'] == flavor_name
:
958 # None is returned if not found anything
961 except Exception as e
:
962 self
._format
_vimconn
_exception
(e
)
964 def check_vim_connectivity(self
):
966 self
._reload
_connection
()
968 except Exception as e
:
969 raise vimconn
.VimConnException("Connectivity issue with Azure API: {}".format(e
))
971 def get_network(self
, net_id
):
973 # self.logger.debug('get network id: {}'.format(net_id))
974 # res_name = self._get_resource_name_from_resource_id(net_id)
975 self
._reload
_connection
()
977 filter_dict
= {'name': net_id
}
978 network_list
= self
.get_network_list(filter_dict
)
981 raise vimconn
.VimConnNotFoundException("network '{}' not found".format(net_id
))
983 return network_list
[0]
985 def delete_network(self
, net_id
, created_items
=None):
987 self
.logger
.debug('deleting network {} - {}'.format(self
.resource_group
, net_id
))
989 self
._reload
_connection
()
990 res_name
= self
._get
_resource
_name
_from
_resource
_id
(net_id
)
991 filter_dict
= {'name': res_name
}
992 network_list
= self
.get_network_list(filter_dict
)
994 raise vimconn
.VimConnNotFoundException("network '{}' not found".format(net_id
))
997 # Subnet API fails (CloudError: Azure Error: ResourceNotFound)
998 # Put the initial virtual_network API
999 async_delete
= self
.conn_vnet
.subnets
.delete(self
.resource_group
, self
.vnet_name
, res_name
)
1003 except CloudError
as e
:
1004 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
1005 raise vimconn
.VimConnNotFoundException("network '{}' not found".format(net_id
))
1007 self
._format
_vimconn
_exception
(e
)
1008 except Exception as e
:
1009 self
._format
_vimconn
_exception
(e
)
1011 def delete_vminstance(self
, vm_id
, created_items
=None):
1012 """ Deletes a vm instance from the vim.
1014 self
.logger
.debug('deleting VM instance {} - {}'.format(self
.resource_group
, vm_id
))
1015 self
._reload
_connection
()
1017 created_items
= created_items
or {}
1019 # Check vm exists, we can call delete_vm to clean created_items
1021 res_name
= self
._get
_resource
_name
_from
_resource
_id
(vm_id
)
1022 vm
= self
.conn_compute
.virtual_machines
.get(self
.resource_group
, res_name
)
1024 # Shuts down the virtual machine and releases the compute resources
1025 # vm_stop = self.conn_compute.virtual_machines.power_off(self.resource_group, resName)
1028 vm_delete
= self
.conn_compute
.virtual_machines
.delete(self
.resource_group
, res_name
)
1030 self
.logger
.debug('deleted VM name: %s', res_name
)
1033 os_disk_name
= vm
.storage_profile
.os_disk
.name
1034 self
.logger
.debug('delete OS DISK: %s', os_disk_name
)
1035 async_disk_delete
= self
.conn_compute
.disks
.delete(self
.resource_group
, os_disk_name
)
1036 async_disk_delete
.wait()
1037 # os disks are created always with the machine
1038 self
.logger
.debug('deleted OS DISK name: %s', os_disk_name
)
1040 for data_disk
in vm
.storage_profile
.data_disks
:
1041 self
.logger
.debug('delete data_disk: %s', data_disk
.name
)
1042 async_disk_delete
= self
.conn_compute
.disks
.delete(self
.resource_group
, data_disk
.name
)
1043 async_disk_delete
.wait()
1044 self
._markdel
_created
_item
(data_disk
.managed_disk
.id, created_items
)
1045 self
.logger
.debug('deleted OS DISK name: %s', data_disk
.name
)
1047 # After deleting VM, it is necessary to delete NIC, because if is not deleted delete_network
1048 # does not work because Azure says that is in use the subnet
1049 network_interfaces
= vm
.network_profile
.network_interfaces
1051 for network_interface
in network_interfaces
:
1053 nic_name
= self
._get
_resource
_name
_from
_resource
_id
(network_interface
.id)
1054 nic_data
= self
.conn_vnet
.network_interfaces
.get(
1055 self
.resource_group
,
1058 public_ip_name
= None
1059 exist_public_ip
= nic_data
.ip_configurations
[0].public_ip_address
1061 public_ip_id
= nic_data
.ip_configurations
[0].public_ip_address
.id
1064 public_ip_name
= self
._get
_resource
_name
_from
_resource
_id
(public_ip_id
)
1066 # Public ip must be deleted afterwards of nic that is attached
1068 self
.logger
.debug('delete NIC name: %s', nic_name
)
1069 nic_delete
= self
.conn_vnet
.network_interfaces
.delete(self
.resource_group
, nic_name
)
1071 self
._markdel
_created
_item
(network_interface
.id, created_items
)
1072 self
.logger
.debug('deleted NIC name: %s', nic_name
)
1074 # Delete list of public ips
1076 self
.logger
.debug('delete PUBLIC IP - ' + public_ip_name
)
1077 ip_delete
= self
.conn_vnet
.public_ip_addresses
.delete(self
.resource_group
, public_ip_name
)
1079 self
._markdel
_created
_item
(public_ip_id
, created_items
)
1081 # Delete created items
1082 self
._delete
_created
_items
(created_items
)
1084 except CloudError
as e
:
1085 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
1086 raise vimconn
.VimConnNotFoundException("No vm instance found '{}'".format(vm_id
))
1088 self
._format
_vimconn
_exception
(e
)
1089 except Exception as e
:
1090 self
._format
_vimconn
_exception
(e
)
1092 def _markdel_created_item(self
, item_id
, created_items
):
1093 if item_id
in created_items
:
1094 created_items
[item_id
] = False
1096 def _delete_created_items(self
, created_items
):
1097 """ Delete created_items elements that have not been deleted with the virtual machine
1098 Created_items may not be deleted correctly with the created machine if the
1099 virtual machine fails creating or in other cases of error
1101 self
.logger
.debug("Created items: %s", created_items
)
1102 # Must delete in order first nics, then public_ips
1103 # As dictionaries don't preserve order, first get items to be deleted then delete them
1105 publics_ip_to_delete
= []
1106 disks_to_delete
= []
1107 for item_id
, v
in created_items
.items():
1108 if not v
: # skip already deleted
1111 # self.logger.debug("Must delete item id: %s", item_id)
1113 # Obtain type, supported nic, disk or public ip
1114 parsed_id
= azure_tools
.parse_resource_id(item_id
)
1115 resource_type
= parsed_id
.get("resource_type")
1116 name
= parsed_id
.get("name")
1118 if resource_type
== "networkInterfaces":
1119 nics_to_delete
.append(name
)
1120 elif resource_type
== "publicIPAddresses":
1121 publics_ip_to_delete
.append(name
)
1122 elif resource_type
== "disks":
1123 disks_to_delete
.append(name
)
1126 for item_name
in nics_to_delete
:
1128 self
.logger
.debug("deleting nic name %s:", item_name
)
1129 nic_delete
= self
.conn_vnet
.network_interfaces
.delete(self
.resource_group
, item_name
)
1131 self
.logger
.debug("deleted nic name %s:", item_name
)
1132 except Exception as e
:
1133 self
.logger
.error("Error deleting item: {}: {}".format(type(e
).__name
__, e
))
1135 for item_name
in publics_ip_to_delete
:
1137 self
.logger
.debug("deleting public ip name %s:", item_name
)
1138 ip_delete
= self
.conn_vnet
.public_ip_addresses
.delete(self
.resource_group
, name
)
1140 self
.logger
.debug("deleted public ip name %s:", item_name
)
1141 except Exception as e
:
1142 self
.logger
.error("Error deleting item: {}: {}".format(type(e
).__name
__, e
))
1144 for item_name
in disks_to_delete
:
1146 self
.logger
.debug("deleting data disk name %s:", name
)
1147 async_disk_delete
= self
.conn_compute
.disks
.delete(self
.resource_group
, item_name
)
1148 async_disk_delete
.wait()
1149 self
.logger
.debug("deleted data disk name %s:", name
)
1150 except Exception as e
:
1151 self
.logger
.error("Error deleting item: {}: {}".format(type(e
).__name
__, e
))
1153 def action_vminstance(self
, vm_id
, action_dict
, created_items
={}):
1154 """Send and action over a VM instance from VIM
1155 Returns the vm_id if the action was successfully sent to the VIM
1158 self
.logger
.debug("Action over VM '%s': %s", vm_id
, str(action_dict
))
1160 self
._reload
_connection
()
1161 resName
= self
._get
_resource
_name
_from
_resource
_id
(vm_id
)
1162 if "start" in action_dict
:
1163 self
.conn_compute
.virtual_machines
.start(self
.resource_group
, resName
)
1164 elif "stop" in action_dict
or "shutdown" in action_dict
or "shutoff" in action_dict
:
1165 self
.conn_compute
.virtual_machines
.power_off(self
.resource_group
, resName
)
1166 elif "terminate" in action_dict
:
1167 self
.conn_compute
.virtual_machines
.delete(self
.resource_group
, resName
)
1168 elif "reboot" in action_dict
:
1169 self
.conn_compute
.virtual_machines
.restart(self
.resource_group
, resName
)
1171 except CloudError
as e
:
1172 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
1173 raise vimconn
.VimConnNotFoundException("No vm found '{}'".format(vm_id
))
1175 self
._format
_vimconn
_exception
(e
)
1176 except Exception as e
:
1177 self
._format
_vimconn
_exception
(e
)
1179 def delete_flavor(self
, flavor_id
):
1180 raise vimconn
.VimConnAuthException("It is not possible to delete a FLAVOR in AZURE")
1182 def delete_tenant(self
, tenant_id
,):
1183 raise vimconn
.VimConnAuthException("It is not possible to delete a TENANT in AZURE")
1185 def delete_image(self
, image_id
):
1186 raise vimconn
.VimConnAuthException("It is not possible to delete a IMAGE in AZURE")
1188 def get_vminstance(self
, vm_id
):
1190 Obtaing the vm instance data from v_id
1192 self
.logger
.debug("get vm instance: %s", vm_id
)
1193 self
._reload
_connection
()
1195 resName
= self
._get
_resource
_name
_from
_resource
_id
(vm_id
)
1196 vm
= self
.conn_compute
.virtual_machines
.get(self
.resource_group
, resName
)
1197 except CloudError
as e
:
1198 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
1199 raise vimconn
.VimConnNotFoundException("No vminstance found '{}'".format(vm_id
))
1201 self
._format
_vimconn
_exception
(e
)
1202 except Exception as e
:
1203 self
._format
_vimconn
_exception
(e
)
1207 def get_flavor(self
, flavor_id
):
1209 Obtains the flavor_data from the flavor_id
1211 self
._reload
_connection
()
1212 self
.logger
.debug("get flavor from id: %s", flavor_id
)
1213 flavor_data
= self
._get
_flavor
_id
_from
_flavor
_name
(flavor_id
)
1218 'ram': flavor_data
['memoryInMB'],
1219 'vcpus': flavor_data
['numberOfCores'],
1220 'disk': flavor_data
['resourceDiskSizeInMB']/1024
1224 raise vimconn
.VimConnNotFoundException("flavor '{}' not found".format(flavor_id
))
1226 def get_tenant_list(self
, filter_dict
={}):
1227 """ Obtains the list of tenants
1228 For the azure connector only the azure tenant will be returned if it is compatible
1231 tenants_azure
= [{'name': self
.tenant
, 'id': self
.tenant
}]
1234 self
.logger
.debug("get tenant list: %s", filter_dict
)
1235 for tenant_azure
in tenants_azure
:
1237 if filter_dict
.get("id") and str(tenant_azure
.get("id")) != filter_dict
["id"]:
1239 if filter_dict
.get("name") and str(tenant_azure
.get("name")) != filter_dict
["name"]:
1242 tenant_list
.append(tenant_azure
)
1246 def refresh_nets_status(self
, net_list
):
1247 """Get the status of the networks
1248 Params: the list of network identifiers
1249 Returns a dictionary with:
1250 net_id: #VIM id of this network
1251 status: #Mandatory. Text with one of:
1252 # DELETED (not found at vim)
1253 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1254 # OTHER (Vim reported other status not understood)
1255 # ERROR (VIM indicates an ERROR status)
1256 # ACTIVE, INACTIVE, DOWN (admin down),
1257 # BUILD (on building process)
1259 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1260 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1265 self
._reload
_connection
()
1267 self
.logger
.debug("reload nets status net_list: %s", net_list
)
1268 for net_id
in net_list
:
1270 netName
= self
._get
_net
_name
_from
_resource
_id
(net_id
)
1271 resName
= self
._get
_resource
_name
_from
_resource
_id
(net_id
)
1273 net
= self
.conn_vnet
.subnets
.get(self
.resource_group
, netName
, resName
)
1275 out_nets
[net_id
] = {
1276 "status": self
.provision_state2osm
[net
.provisioning_state
],
1277 "vim_info": str(net
)
1279 except CloudError
as e
:
1280 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
1281 self
.logger
.info("Not found subnet net_name: %s, subnet_name: %s", netName
, resName
)
1282 out_nets
[net_id
] = {
1283 "status": "DELETED",
1287 self
.logger
.error("CloudError Exception %s when searching subnet", e
)
1288 out_nets
[net_id
] = {
1289 "status": "VIM_ERROR",
1292 except vimconn
.VimConnNotFoundException
as e
:
1293 self
.logger
.error("VimConnNotFoundException %s when searching subnet", e
)
1294 out_nets
[net_id
] = {
1295 "status": "DELETED",
1298 except Exception as e
:
1299 self
.logger
.error("Exception %s when searching subnet", e
, exc_info
=True)
1300 out_nets
[net_id
] = {
1301 "status": "VIM_ERROR",
1306 def refresh_vms_status(self
, vm_list
):
1307 """ Get the status of the virtual machines and their interfaces/ports
1308 Params: the list of VM identifiers
1309 Returns a dictionary with:
1310 vm_id: # VIM id of this Virtual Machine
1311 status: # Mandatory. Text with one of:
1312 # DELETED (not found at vim)
1313 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1314 # OTHER (Vim reported other status not understood)
1315 # ERROR (VIM indicates an ERROR status)
1316 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1317 # BUILD (on building process), ERROR
1318 # ACTIVE:NoMgmtIP (Active but none of its interfaces has an IP address
1319 # (ACTIVE:NoMgmtIP is not returned for Azure)
1321 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1322 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1323 interfaces: list with interface info. Each item a dictionary with:
1324 vim_interface_id - The ID of the interface
1325 mac_address - The MAC address of the interface.
1326 ip_address - The IP address of the interface within the subnet.
1330 self
._reload
_connection
()
1332 self
.logger
.debug("refresh vm status vm_list: %s", vm_list
)
1333 search_vm_list
= vm_list
or {}
1335 for vm_id
in search_vm_list
:
1338 res_name
= self
._get
_resource
_name
_from
_resource
_id
(vm_id
)
1340 vm
= self
.conn_compute
.virtual_machines
.get(self
.resource_group
, res_name
)
1341 out_vm
['vim_info'] = str(vm
)
1342 out_vm
['status'] = self
.provision_state2osm
.get(vm
.provisioning_state
, 'OTHER')
1343 if vm
.provisioning_state
== 'Succeeded':
1344 # check if machine is running or stopped
1345 instance_view
= self
.conn_compute
.virtual_machines
.instance_view(self
.resource_group
,
1347 for status
in instance_view
.statuses
:
1348 splitted_status
= status
.code
.split("/")
1349 if len(splitted_status
) == 2 and splitted_status
[0] == 'PowerState':
1350 out_vm
['status'] = self
.power_state2osm
.get(splitted_status
[1], 'OTHER')
1352 network_interfaces
= vm
.network_profile
.network_interfaces
1353 out_vm
['interfaces'] = self
._get
_vm
_interfaces
_status
(vm_id
, network_interfaces
)
1355 except CloudError
as e
:
1356 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
1357 self
.logger
.debug("Not found vm id: %s", vm_id
)
1358 out_vm
['status'] = "DELETED"
1359 out_vm
['error_msg'] = str(e
)
1360 out_vm
['vim_info'] = None
1362 # maybe connection error or another type of error, return vim error
1363 self
.logger
.error("Exception %s refreshing vm_status", e
)
1364 out_vm
['status'] = "VIM_ERROR"
1365 out_vm
['error_msg'] = str(e
)
1366 out_vm
['vim_info'] = None
1367 except Exception as e
:
1368 self
.logger
.error("Exception %s refreshing vm_status", e
, exc_info
=True)
1369 out_vm
['status'] = "VIM_ERROR"
1370 out_vm
['error_msg'] = str(e
)
1371 out_vm
['vim_info'] = None
1373 out_vms
[vm_id
] = out_vm
1377 def _get_vm_interfaces_status(self
, vm_id
, interfaces
):
1379 Gets the interfaces detail for a vm
1380 :param interfaces: List of interfaces.
1381 :return: Dictionary with list of interfaces including, vim_interface_id, mac_address and ip_address
1385 for network_interface
in interfaces
:
1387 nic_name
= self
._get
_resource
_name
_from
_resource
_id
(network_interface
.id)
1388 interface_dict
['vim_interface_id'] = network_interface
.id
1390 nic_data
= self
.conn_vnet
.network_interfaces
.get(
1391 self
.resource_group
,
1395 if nic_data
.ip_configurations
[0].public_ip_address
:
1396 self
.logger
.debug("Obtain public ip address")
1397 public_ip_name
= self
._get
_resource
_name
_from
_resource
_id
(
1398 nic_data
.ip_configurations
[0].public_ip_address
.id)
1399 public_ip
= self
.conn_vnet
.public_ip_addresses
.get(self
.resource_group
, public_ip_name
)
1400 self
.logger
.debug("Public ip address is: %s", public_ip
.ip_address
)
1401 ips
.append(public_ip
.ip_address
)
1403 private_ip
= nic_data
.ip_configurations
[0].private_ip_address
1404 ips
.append(private_ip
)
1406 interface_dict
['mac_address'] = nic_data
.mac_address
1407 interface_dict
['ip_address'] = ";".join(ips
)
1408 interface_list
.append(interface_dict
)
1410 return interface_list
1411 except Exception as e
:
1412 self
.logger
.error("Exception %s obtaining interface data for vm: %s, error: %s", vm_id
, e
, exc_info
=True)
1413 self
._format
_vimconn
_exception
(e
)
1416 if __name__
== "__main__":
1418 # Making some basic test
1421 needed_test_params
= {
1422 "client_id": "AZURE_CLIENT_ID",
1423 "secret": "AZURE_SECRET",
1424 "tenant": "AZURE_TENANT",
1425 "resource_group": "AZURE_RESOURCE_GROUP",
1426 "subscription_id": "AZURE_SUBSCRIPTION_ID",
1427 "vnet_name": "AZURE_VNET_NAME",
1431 for param
, env_var
in needed_test_params
.items():
1432 value
= getenv(env_var
)
1434 raise Exception("Provide a valid value for env '{}'".format(env_var
))
1435 test_params
[param
] = value
1438 'region_name': getenv("AZURE_REGION_NAME", 'westeurope'),
1439 'resource_group': getenv("AZURE_RESOURCE_GROUP"),
1440 'subscription_id': getenv("AZURE_SUBSCRIPTION_ID"),
1441 'pub_key': getenv("AZURE_PUB_KEY", None),
1442 'vnet_name': getenv("AZURE_VNET_NAME", 'myNetwork'),
1447 'description': 'new VM',
1448 'status': 'running',
1450 'publisher': 'Canonical',
1451 'offer': 'UbuntuServer',
1452 'sku': '16.04.0-LTS',
1455 'hardware_profile': {
1456 'vm_size': 'Standard_DS1_v2'
1464 'subnet_address': '10.1.2.0/24',
1465 # 'subnet_name': 'subnet-oam'
1467 ###########################
1469 azure
= vimconnector(vim_id
, vim_name
, tenant_id
=test_params
["tenant"], tenant_name
=None, url
=None, url_admin
=None,
1470 user
=test_params
["client_id"], passwd
=test_params
["secret"], log_level
=None, config
=config
)
1472 # azure.get_flavor_id_from_data("here")
1473 # subnets=azure.get_network_list()
1474 # azure.new_vminstance(virtualMachine['name'], virtualMachine['description'], virtualMachine['status'],
1475 # virtualMachine['image'], virtualMachine['hardware_profile']['vm_size'], subnets)
1477 azure
.new_network("mynet", None)
1478 net_id
= "/subscriptions/82f80cc1-876b-4591-9911-1fb5788384fd/resourceGroups/osmRG/providers/Microsoft."\
1479 "Network/virtualNetworks/test"
1480 net_id_not_found
= "/subscriptions/82f80cc1-876b-4591-9911-1fb5788384fd/resourceGroups/osmRG/providers/"\
1481 "Microsoft.Network/virtualNetworks/testALF"
1482 azure
.refresh_nets_status([net_id
, net_id_not_found
])