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"):
46 class vimconnector(vimconn
.VimConnector
):
48 # Translate azure provisioning state to OSM provision state
49 # The first three ones are the transitional status once a user initiated action has been requested
50 # Once the operation is complete, it will transition into the states Succeeded or Failed
51 # https://docs.microsoft.com/en-us/azure/virtual-machines/windows/states-lifecycle
52 provision_state2osm
= {
55 "Deleting": "INACTIVE",
56 "Succeeded": "ACTIVE",
60 # Translate azure power state to OSM provision state
62 "starting": "INACTIVE",
64 "stopping": "INACTIVE",
65 "stopped": "INACTIVE",
67 "deallocated": "BUILD",
68 "deallocating": "BUILD",
71 AZURE_ZONES
= ["1", "2", "3"]
88 Constructor of VIM. Raise an exception is some needed parameter is missing, but it must not do any connectivity
89 checking against the VIM
90 Using common constructor parameters.
91 In this case: config must include the following parameters:
92 subscription_id: assigned azure subscription identifier
93 region_name: current region for azure network
94 resource_group: used for all azure created resources
95 vnet_name: base vnet for azure, created networks will be subnets from this base network
96 config may also include the following parameter:
97 flavors_pattern: pattern that will be used to select a range of vm sizes, for example
98 "^((?!Standard_B).)*$" will filter out Standard_B range that is cheap but is very overused
99 "^Standard_B" will select a serie B maybe for test environment
101 vimconn
.VimConnector
.__init
__(
116 # Variable that indicates if client must be reloaded or initialized
117 self
.reload_client
= True
119 self
.vnet_address_space
= None
121 self
.logger
= logging
.getLogger("ro.vim.azure")
124 logging
.basicConfig()
125 self
.logger
.setLevel(getattr(logging
, log_level
))
127 self
.tenant
= tenant_id
or tenant_name
129 # Store config to create azure subscription later
133 "tenant": tenant_id
or tenant_name
,
137 if "subscription_id" in config
:
138 self
._config
["subscription_id"] = config
.get("subscription_id")
139 # self.logger.debug("Setting subscription to: %s", self.config["subscription_id"])
141 raise vimconn
.VimConnException("Subscription not specified")
144 if "region_name" in config
:
145 self
.region
= config
.get("region_name")
147 raise vimconn
.VimConnException(
148 "Azure region_name is not specified at config"
152 if "resource_group" in config
:
153 self
.resource_group
= config
.get("resource_group")
155 raise vimconn
.VimConnException(
156 "Azure resource_group is not specified at config"
160 if "vnet_name" in config
:
161 self
.vnet_name
= config
["vnet_name"]
164 self
.pub_key
= config
.get("pub_key")
166 # flavor pattern regex
167 if "flavors_pattern" in config
:
168 self
._config
["flavors_pattern"] = config
["flavors_pattern"]
170 def _reload_connection(self
):
172 Called before any operation, checks python azure clients
174 if self
.reload_client
:
175 self
.logger
.debug("reloading azure client")
178 self
.credentials
= ServicePrincipalCredentials(
179 client_id
=self
._config
["user"],
180 secret
=self
._config
["passwd"],
181 tenant
=self
._config
["tenant"],
183 self
.conn
= ResourceManagementClient(
184 self
.credentials
, self
._config
["subscription_id"]
186 self
.conn_compute
= ComputeManagementClient(
187 self
.credentials
, self
._config
["subscription_id"]
189 self
.conn_vnet
= NetworkManagementClient(
190 self
.credentials
, self
._config
["subscription_id"]
192 self
._check
_or
_create
_resource
_group
()
193 self
._check
_or
_create
_vnet
()
195 # Set to client created
196 self
.reload_client
= False
197 except Exception as e
:
198 self
._format
_vimconn
_exception
(e
)
200 def _get_resource_name_from_resource_id(self
, resource_id
):
202 Obtains resource_name from the azure complete identifier: resource_name will always be last item
205 resource
= str(resource_id
.split("/")[-1])
208 except Exception as e
:
209 raise vimconn
.VimConnException(
210 "Unable to get resource name from resource_id '{}' Error: '{}'".format(
215 def _get_location_from_resource_group(self
, resource_group_name
):
217 location
= self
.conn
.resource_groups
.get(resource_group_name
).location
221 raise vimconn
.VimConnNotFoundException(
222 "Location '{}' not found".format(resource_group_name
)
225 def _get_resource_group_name_from_resource_id(self
, resource_id
):
227 rg
= str(resource_id
.split("/")[4])
231 raise vimconn
.VimConnException(
232 "Unable to get resource group from invalid resource_id format '{}'".format(
237 def _get_net_name_from_resource_id(self
, resource_id
):
239 net_name
= str(resource_id
.split("/")[8])
243 raise vimconn
.VimConnException(
244 "Unable to get azure net_name from invalid resource_id format '{}'".format(
249 def _check_subnets_for_vm(self
, net_list
):
250 # All subnets must belong to the same resource group and vnet
252 self
._get
_resource
_group
_name
_from
_resource
_id
(net
["net_id"])
253 + self
._get
_net
_name
_from
_resource
_id
(net
["net_id"])
257 if len(rg_vnet
) != 1:
258 raise self
._format
_vimconn
_exception
(
259 "Azure VMs can only attach to subnets in same VNET"
262 def _format_vimconn_exception(self
, e
):
264 Transforms a generic or azure exception to a vimcommException
266 if isinstance(e
, vimconn
.VimConnException
):
268 elif isinstance(e
, AuthenticationError
):
269 raise vimconn
.VimConnAuthException(type(e
).__name
__ + ": " + str(e
))
270 elif isinstance(e
, ConnectionError
):
271 raise vimconn
.VimConnConnectionException(type(e
).__name
__ + ": " + str(e
))
273 # In case of generic error recreate client
274 self
.reload_client
= True
276 raise vimconn
.VimConnException(type(e
).__name
__ + ": " + str(e
))
278 def _check_or_create_resource_group(self
):
280 Creates the base resource group if it does not exist
283 rg_exists
= self
.conn
.resource_groups
.check_existence(self
.resource_group
)
286 self
.logger
.debug("create base rgroup: %s", self
.resource_group
)
287 self
.conn
.resource_groups
.create_or_update(
288 self
.resource_group
, {"location": self
.region
}
290 except Exception as e
:
291 self
._format
_vimconn
_exception
(e
)
293 def _check_or_create_vnet(self
):
295 Try to get existent base vnet, in case it does not exist it creates it
298 vnet
= self
.conn_vnet
.virtual_networks
.get(
299 self
.resource_group
, self
.vnet_name
301 self
.vnet_address_space
= vnet
.address_space
.address_prefixes
[0]
302 self
.vnet_id
= vnet
.id
305 except CloudError
as e
:
306 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
308 # continue and create it
310 self
._format
_vimconn
_exception
(e
)
312 # if it does not exist, create it
315 "location": self
.region
,
316 "address_space": {"address_prefixes": ["10.0.0.0/8"]},
318 self
.vnet_address_space
= "10.0.0.0/8"
320 self
.logger
.debug("create base vnet: %s", self
.vnet_name
)
321 self
.conn_vnet
.virtual_networks
.create_or_update(
322 self
.resource_group
, self
.vnet_name
, vnet_params
324 vnet
= self
.conn_vnet
.virtual_networks
.get(
325 self
.resource_group
, self
.vnet_name
327 self
.vnet_id
= vnet
.id
328 except Exception as e
:
329 self
._format
_vimconn
_exception
(e
)
337 provider_network_profile
=None,
340 Adds a tenant network to VIM
341 :param net_name: name of the network
342 :param net_type: not used for azure networks
343 :param ip_profile: is a dict containing the IP parameters of the network (Currently only IPv4 is implemented)
344 'ip-version': can be one of ['IPv4','IPv6']
345 'subnet-address': ip_prefix_schema, that is X.X.X.X/Y
346 'gateway-address': (Optional) ip_schema, that is X.X.X.X, not implemented for azure connector
347 'dns-address': (Optional) ip_schema, not implemented for azure connector
348 'dhcp': (Optional) dict containing, not implemented for azure connector
349 'enabled': {'type': 'boolean'},
350 'start-address': ip_schema, first IP to grant
351 'count': number of IPs to grant.
352 :param shared: Not allowed for Azure Connector
353 :param provider_network_profile: (optional) contains {segmentation-id: vlan, provider-network: vim_netowrk}
354 :return: a tuple with the network identifier and created_items, or raises an exception on error
355 created_items can be None or a dictionary where this method can include key-values that will be passed to
356 the method delete_network. Can be used to store created segments, created l2gw connections, etc.
357 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
360 return self
._new
_subnet
(net_name
, ip_profile
)
362 def _new_subnet(self
, net_name
, ip_profile
):
364 Adds a tenant network to VIM. It creates a new subnet at existing base vnet
365 :param net_name: subnet name
367 subnet-address: if it is not provided a subnet/24 in the default vnet is created,
368 otherwise it creates a subnet in the indicated address
369 :return: a tuple with the network identifier and created_items, or raises an exception on error
371 self
.logger
.debug("create subnet name %s, ip_profile %s", net_name
, ip_profile
)
372 self
._reload
_connection
()
374 if ip_profile
is None:
375 # get a non used vnet ip range /24 and allocate automatically inside the range self.vnet_address_space
376 used_subnets
= self
.get_network_list()
377 for ip_range
in netaddr
.IPNetwork(self
.vnet_address_space
).subnet(24):
378 for used_subnet
in used_subnets
:
379 subnet_range
= netaddr
.IPNetwork(used_subnet
["cidr_block"])
381 if subnet_range
in ip_range
or ip_range
in subnet_range
:
382 # this range overlaps with an existing subnet ip range. Breaks and look for another
385 ip_profile
= {"subnet_address": str(ip_range
)}
386 self
.logger
.debug("dinamically obtained ip_profile: %s", ip_range
)
389 raise vimconn
.VimConnException(
390 "Cannot find a non-used subnet range in {}".format(
391 self
.vnet_address_space
395 ip_profile
= {"subnet_address": ip_profile
["subnet_address"]}
398 # subnet_name = "{}-{}".format(net_name[:24], uuid4())
399 subnet_params
= {"address_prefix": ip_profile
["subnet_address"]}
400 # Assign a not duplicated net name
401 subnet_name
= self
._get
_unused
_subnet
_name
(net_name
)
403 self
.logger
.debug("creating subnet_name: {}".format(subnet_name
))
404 async_creation
= self
.conn_vnet
.subnets
.create_or_update(
405 self
.resource_group
, self
.vnet_name
, subnet_name
, subnet_params
407 async_creation
.wait()
408 self
.logger
.debug("created subnet_name: {}".format(subnet_name
))
410 return "{}/subnets/{}".format(self
.vnet_id
, subnet_name
), None
411 except Exception as e
:
412 self
._format
_vimconn
_exception
(e
)
414 def _get_unused_subnet_name(self
, subnet_name
):
416 Adds a prefix to the subnet_name with a number in case the indicated name is repeated
417 Checks subnets with the indicated name (without suffix) and adds a suffix with a number
419 all_subnets
= self
.conn_vnet
.subnets
.list(self
.resource_group
, self
.vnet_name
)
420 # Filter to subnets starting with the indicated name
422 filter(lambda subnet
: (subnet
.name
.startswith(subnet_name
)), all_subnets
)
424 net_names
= [str(subnet
.name
) for subnet
in subnets
]
426 # get the name with the first not used suffix
428 # name = subnet_name + "-" + str(name_suffix)
429 name
= subnet_name
# first subnet created will have no prefix
430 while name
in net_names
:
432 name
= subnet_name
+ "-" + str(name_suffix
)
436 def _create_nic(self
, net
, nic_name
, static_ip
=None, created_items
={}):
437 self
.logger
.debug("create nic name %s, net_name %s", nic_name
, net
)
438 self
._reload
_connection
()
440 subnet_id
= net
["net_id"]
441 location
= self
._get
_location
_from
_resource
_group
(self
.resource_group
)
443 net_ifz
= {"location": location
}
445 "name": nic_name
+ "-ipconfiguration",
446 "subnet": {"id": subnet_id
},
450 net_ip_config
["privateIPAddress"] = static_ip
451 net_ip_config
["privateIPAllocationMethod"] = "Static"
453 net_ifz
["ip_configurations"] = [net_ip_config
]
454 mac_address
= net
.get("mac_address")
457 net_ifz
["mac_address"] = mac_address
459 async_nic_creation
= self
.conn_vnet
.network_interfaces
.create_or_update(
460 self
.resource_group
, nic_name
, net_ifz
462 nic_data
= async_nic_creation
.result()
463 created_items
[nic_data
.id] = True
464 self
.logger
.debug("created nic name %s", nic_name
)
466 public_ip
= net
.get("floating_ip")
468 public_ip_address_params
= {
469 "location": location
,
470 "public_ip_allocation_method": "Dynamic",
472 public_ip_name
= nic_name
+ "-public-ip"
473 async_public_ip
= self
.conn_vnet
.public_ip_addresses
.create_or_update(
474 self
.resource_group
, public_ip_name
, public_ip_address_params
476 public_ip
= async_public_ip
.result()
477 self
.logger
.debug("created public IP: {}".format(public_ip
))
479 # Associate NIC to Public IP
480 nic_data
= self
.conn_vnet
.network_interfaces
.get(
481 self
.resource_group
, nic_name
484 nic_data
.ip_configurations
[0].public_ip_address
= public_ip
485 created_items
[public_ip
.id] = True
487 self
.conn_vnet
.network_interfaces
.create_or_update(
488 self
.resource_group
, nic_name
, nic_data
491 except Exception as e
:
492 self
._format
_vimconn
_exception
(e
)
494 return nic_data
, created_items
496 def new_flavor(self
, flavor_data
):
498 It is not allowed to create new flavors in Azure, must always use an existing one
500 raise vimconn
.VimConnAuthException(
501 "It is not possible to create new flavors in AZURE"
504 def new_tenant(self
, tenant_name
, tenant_description
):
506 It is not allowed to create new tenants in azure
508 raise vimconn
.VimConnAuthException(
509 "It is not possible to create a TENANT in AZURE"
512 def new_image(self
, image_dict
):
514 It is not allowed to create new images in Azure, must always use an existing one
516 raise vimconn
.VimConnAuthException(
517 "It is not possible to create new images in AZURE"
520 def get_image_id_from_path(self
, path
):
521 """Get the image id from image path in the VIM database.
522 Returns the image_id or raises a vimconnNotFoundException
524 raise vimconn
.VimConnAuthException(
525 "It is not possible to obtain image from path in AZURE"
528 def get_image_list(self
, filter_dict
={}):
529 """Obtain tenant images from VIM
531 name: image name with the format: publisher:offer:sku:version
532 If some part of the name is provide ex: publisher:offer it will search all availables skus and version
533 for the provided publisher and offer
534 id: image uuid, currently not supported for azure
535 Returns the image list of dictionaries:
536 [{<the fields at Filter_dict plus some VIM specific>}, ...]
539 self
.logger
.debug("get_image_list filter {}".format(filter_dict
))
541 self
._reload
_connection
()
544 if filter_dict
.get("name"):
545 # name will have the format "publisher:offer:sku:version"
546 # publisher is required, offer sku and version will be searched if not provided
547 params
= filter_dict
["name"].split(":")
548 publisher
= params
[0]
551 offer_list
= self
._get
_offer
_list
(params
, publisher
)
553 for offer
in offer_list
:
555 sku_list
= self
._get
_sku
_list
(params
, publisher
, offer
)
558 # if version is defined get directly version, else list images
559 if len(params
) == 4 and params
[3]:
561 image_list
= self
._get
_version
_image
_list
(
562 publisher
, offer
, sku
, version
565 image_list
= self
._get
_sku
_image
_list
(
566 publisher
, offer
, sku
569 raise vimconn
.VimConnAuthException(
570 "List images in Azure must include name param with at least publisher"
573 raise vimconn
.VimConnAuthException(
574 "List images in Azure must include name param with at"
579 except Exception as e
:
580 self
._format
_vimconn
_exception
(e
)
582 def _get_offer_list(self
, params
, publisher
):
584 Helper method to obtain offer list for defined publisher
586 if len(params
) >= 2 and params
[1]:
590 # get list of offers from azure
591 result_offers
= self
.conn_compute
.virtual_machine_images
.list_offers(
592 self
.region
, publisher
595 return [offer
.name
for offer
in result_offers
]
596 except CloudError
as e
:
597 # azure raises CloudError when not found
599 "error listing offers for publisher {}, Error: {}".format(
606 def _get_sku_list(self
, params
, publisher
, offer
):
608 Helper method to obtain sku list for defined publisher and offer
610 if len(params
) >= 3 and params
[2]:
614 # get list of skus from azure
615 result_skus
= self
.conn_compute
.virtual_machine_images
.list_skus(
616 self
.region
, publisher
, offer
619 return [sku
.name
for sku
in result_skus
]
620 except CloudError
as e
:
621 # azure raises CloudError when not found
623 "error listing skus for publisher {}, offer {}, Error: {}".format(
630 def _get_sku_image_list(self
, publisher
, offer
, sku
):
632 Helper method to obtain image list for publisher, offer and sku
636 result_images
= self
.conn_compute
.virtual_machine_images
.list(
637 self
.region
, publisher
, offer
, sku
639 for result_image
in result_images
:
642 "id": str(result_image
.id),
643 "name": ":".join([publisher
, offer
, sku
, result_image
.name
]),
646 except CloudError
as e
:
648 "error listing skus for publisher {}, offer {}, Error: {}".format(
656 def _get_version_image_list(self
, publisher
, offer
, sku
, version
):
659 result_image
= self
.conn_compute
.virtual_machine_images
.get(
660 self
.region
, publisher
, offer
, sku
, version
666 "id": str(result_image
.id),
667 "name": ":".join([publisher
, offer
, sku
, version
]),
670 except CloudError
as e
:
671 # azure gives CloudError when not found
673 "error listing images for publisher {}, offer {}, sku {}, version {} Error: {}".format(
674 publisher
, offer
, sku
, version
, e
681 def get_network_list(self
, filter_dict
={}):
682 """Obtain tenant networks of VIM
686 shared: boolean, not implemented in Azure
687 tenant_id: tenant, not used in Azure, all networks same tenants
688 admin_state_up: boolean, not implemented in Azure
689 status: 'ACTIVE', not implemented in Azure #
690 Returns the network list of dictionaries
692 # self.logger.debug("getting network list for vim, filter %s", filter_dict)
694 self
._reload
_connection
()
696 vnet
= self
.conn_vnet
.virtual_networks
.get(
697 self
.resource_group
, self
.vnet_name
701 for subnet
in vnet
.subnets
:
703 if filter_dict
.get("id") and str(subnet
.id) != filter_dict
["id"]:
707 filter_dict
.get("name")
708 and str(subnet
.name
) != filter_dict
["name"]
712 name
= self
._get
_resource
_name
_from
_resource
_id
(subnet
.id)
716 "id": str(subnet
.id),
718 "status": self
.provision_state2osm
[subnet
.provisioning_state
],
719 "cidr_block": str(subnet
.address_prefix
),
726 except Exception as e
:
727 self
._format
_vimconn
_exception
(e
)
739 availability_zone_index
=None,
740 availability_zone_list
=None,
743 "new vm instance name: %s, image_id: %s, flavor_id: %s, net_list: %s, cloud_config: %s, "
744 "disk_list: %s, availability_zone_index: %s, availability_zone_list: %s",
751 availability_zone_index
,
752 availability_zone_list
,
754 self
._reload
_connection
()
756 # Validate input data is valid
757 # The virtual machine name must have less or 64 characters and it can not have the following
758 # characters: (~ ! @ # $ % ^ & * ( ) = + _ [ ] { } \ | ; : ' " , < > / ?.)
759 vm_name
= self
._check
_vm
_name
(name
)
760 # Obtain vm unused name
761 vm_name
= self
._get
_unused
_vm
_name
(vm_name
)
763 # At least one network must be provided
765 raise vimconn
.VimConnException(
766 "At least one net must be provided to create a new VM"
769 # image_id are several fields of the image_id
770 image_reference
= self
._get
_image
_reference
(image_id
)
773 virtual_machine
= None
776 # Create nics for each subnet
777 self
._check
_subnets
_for
_vm
(net_list
)
780 for idx
, net
in enumerate(net_list
):
781 # Fault with subnet_id
782 # subnet_id=net["subnet_id"]
783 # subnet_id=net["net_id"]
784 nic_name
= vm_name
+ "-nic-" + str(idx
)
785 vm_nic
, nic_items
= self
._create
_nic
(
786 net
, nic_name
, net
.get("ip_address"), created_items
788 vm_nics
.append({"id": str(vm_nic
.id)})
789 net
["vim_id"] = vm_nic
.id
791 # cloud-init configuration
794 config_drive
, userdata
= self
._create
_user
_data
(cloud_config
)
795 custom_data
= base64
.b64encode(userdata
.encode("utf-8")).decode(
799 key_pairs
= cloud_config
.get("key-pairs")
801 key_data
= key_pairs
[0]
803 if cloud_config
.get("users"):
804 user_name
= cloud_config
.get("users")[0].get("name", "osm")
806 user_name
= "osm" # DEFAULT USER IS OSM
809 "computer_name": vm_name
,
810 "admin_username": user_name
,
811 "linux_configuration": {
812 "disable_password_authentication": True,
816 "path": "/home/{}/.ssh/authorized_keys".format(
819 "key_data": key_data
,
824 "custom_data": custom_data
,
828 "computer_name": vm_name
,
829 "admin_username": "osm",
830 "admin_password": "Osm4u!",
834 "location": self
.region
,
835 "os_profile": os_profile
,
836 "hardware_profile": {"vm_size": flavor_id
},
837 "storage_profile": {"image_reference": image_reference
},
840 # If the machine has several networks one must be marked as primary
841 # As it is not indicated in the interface the first interface will be marked as primary
843 for idx
, vm_nic
in enumerate(vm_nics
):
845 vm_nics
[0]["Primary"] = True
847 vm_nics
[idx
]["Primary"] = False
849 vm_parameters
["network_profile"] = {"network_interfaces": vm_nics
}
851 # Obtain zone information
852 vm_zone
= self
._get
_vm
_zone
(availability_zone_index
, availability_zone_list
)
854 vm_parameters
["zones"] = [vm_zone
]
856 self
.logger
.debug("create vm name: %s", vm_name
)
857 creation_result
= self
.conn_compute
.virtual_machines
.create_or_update(
858 self
.resource_group
, vm_name
, vm_parameters
860 virtual_machine
= creation_result
.result()
861 self
.logger
.debug("created vm name: %s", vm_name
)
863 # Add disks if they are provided
865 for disk_index
, disk
in enumerate(disk_list
):
867 "add disk size: %s, image: %s",
871 self
._add
_newvm
_disk
(
872 virtual_machine
, vm_name
, disk_index
, disk
, created_items
876 self
.conn_compute
.virtual_machines
.start(self
.resource_group
, vm_name
)
877 # start_result.wait()
879 return virtual_machine
.id, created_items
881 # run_command_parameters = {
882 # "command_id": "RunShellScript", # For linux, don't change it
884 # "date > /tmp/test.txt"
887 except Exception as e
:
888 # Rollback vm creacion
892 vm_id
= virtual_machine
.id
895 self
.logger
.debug("exception creating vm try to rollback")
896 self
.delete_vminstance(vm_id
, created_items
)
897 except Exception as e2
:
898 self
.logger
.error("new_vminstance rollback fail {}".format(e2
))
900 self
.logger
.debug("Exception creating new vminstance: %s", e
, exc_info
=True)
901 self
._format
_vimconn
_exception
(e
)
903 def _get_unused_vm_name(self
, vm_name
):
905 Checks the vm name and in case it is used adds a suffix to the name to allow creation
908 all_vms
= self
.conn_compute
.virtual_machines
.list(self
.resource_group
)
909 # Filter to vms starting with the indicated name
910 vms
= list(filter(lambda vm
: (vm
.name
.startswith(vm_name
)), all_vms
))
911 vm_names
= [str(vm
.name
) for vm
in vms
]
913 # get the name with the first not used suffix
915 # name = subnet_name + "-" + str(name_suffix)
916 name
= vm_name
# first subnet created will have no prefix
918 while name
in vm_names
:
920 name
= vm_name
+ "-" + str(name_suffix
)
924 def _get_vm_zone(self
, availability_zone_index
, availability_zone_list
):
925 if availability_zone_index
is None:
928 vim_availability_zones
= self
._get
_azure
_availability
_zones
()
929 # check if VIM offer enough availability zones describe in the VNFD
930 if vim_availability_zones
and len(availability_zone_list
) <= len(
931 vim_availability_zones
933 # check if all the names of NFV AV match VIM AV names
934 match_by_index
= False
936 if not availability_zone_list
:
937 match_by_index
= True
939 for av
in availability_zone_list
:
940 if av
not in vim_availability_zones
:
941 match_by_index
= True
945 return vim_availability_zones
[availability_zone_index
]
947 return availability_zone_list
[availability_zone_index
]
949 raise vimconn
.VimConnConflictException(
950 "No enough availability zones at VIM for this deployment"
953 def _get_azure_availability_zones(self
):
954 return self
.AZURE_ZONES
957 self
, virtual_machine
, vm_name
, disk_index
, disk
, created_items
={}
962 # Check if must create empty disk or from image
963 if disk
.get("vim_id"):
964 # disk already exists, just get
965 parsed_id
= azure_tools
.parse_resource_id(disk
.get("vim_id"))
966 disk_name
= parsed_id
.get("name")
967 data_disk
= self
.conn_compute
.disks
.get(self
.resource_group
, disk_name
)
969 disk_name
= vm_name
+ "_DataDisk_" + str(disk_index
)
970 if not disk
.get("image_id"):
971 self
.logger
.debug("create new data disk name: %s", disk_name
)
972 async_disk_creation
= self
.conn_compute
.disks
.create_or_update(
976 "location": self
.region
,
977 "disk_size_gb": disk
.get("size"),
978 "creation_data": {"create_option": DiskCreateOption
.empty
},
981 data_disk
= async_disk_creation
.result()
982 created_items
[data_disk
.id] = True
984 image_id
= disk
.get("image_id")
986 if azure_tools
.is_valid_resource_id(image_id
):
987 parsed_id
= azure_tools
.parse_resource_id(image_id
)
989 # Check if image is snapshot or disk
990 image_name
= parsed_id
.get("name")
991 type = parsed_id
.get("resource_type")
993 if type == "snapshots" or type == "disks":
994 self
.logger
.debug("create disk from copy name: %s", image_name
)
995 # ¿Should check that snapshot exists?
996 async_disk_creation
= self
.conn_compute
.disks
.create_or_update(
1000 "location": self
.region
,
1002 "create_option": "Copy",
1003 "source_uri": image_id
,
1007 data_disk
= async_disk_creation
.result()
1008 created_items
[data_disk
.id] = True
1010 raise vimconn
.VimConnNotFoundException(
1011 "Invalid image_id: %s ", image_id
1014 raise vimconn
.VimConnNotFoundException(
1015 "Invalid image_id: %s ", image_id
1018 # Attach the disk created
1019 virtual_machine
.storage_profile
.data_disks
.append(
1023 "create_option": DiskCreateOption
.attach
,
1024 "managed_disk": {"id": data_disk
.id},
1025 "disk_size_gb": disk
.get("size"),
1028 self
.logger
.debug("attach disk name: %s", disk_name
)
1029 self
.conn_compute
.virtual_machines
.create_or_update(
1030 self
.resource_group
, virtual_machine
.name
, virtual_machine
1033 # It is necesary extract from image_id data to create the VM with this format
1034 # "image_reference": {
1035 # "publisher": vm_reference["publisher"],
1036 # "offer": vm_reference["offer"],
1037 # "sku": vm_reference["sku"],
1038 # "version": vm_reference["version"]
1040 def _get_image_reference(self
, image_id
):
1042 # The data input format example:
1043 # /Subscriptions/ca3d18ab-d373-4afb-a5d6-7c44f098d16a/Providers/Microsoft.Compute/Locations/westeurope/
1044 # Publishers/Canonical/ArtifactTypes/VMImage/
1045 # Offers/UbuntuServer/
1047 # Versions/18.04.201809110
1048 publisher
= str(image_id
.split("/")[8])
1049 offer
= str(image_id
.split("/")[12])
1050 sku
= str(image_id
.split("/")[14])
1051 version
= str(image_id
.split("/")[16])
1054 "publisher": publisher
,
1060 raise vimconn
.VimConnException(
1061 "Unable to get image_reference from invalid image_id format: '{}'".format(
1066 # Azure VM names can not have some special characters
1067 def _check_vm_name(self
, vm_name
):
1069 Checks vm name, in case the vm has not allowed characters they are removed, not error raised
1071 chars_not_allowed_list
= "~!@#$%^&*()=+_[]{}|;:<>/?."
1073 # First: the VM name max length is 64 characters
1074 vm_name_aux
= vm_name
[:64]
1076 # Second: replace not allowed characters
1077 for elem
in chars_not_allowed_list
:
1078 # Check if string is in the main string
1079 if elem
in vm_name_aux
:
1080 # self.logger.debug("Dentro del IF")
1081 # Replace the string
1082 vm_name_aux
= vm_name_aux
.replace(elem
, "-")
1086 def get_flavor_id_from_data(self
, flavor_dict
):
1087 self
.logger
.debug("getting flavor id from data, flavor_dict: %s", flavor_dict
)
1088 filter_dict
= flavor_dict
or {}
1091 self
._reload
_connection
()
1094 for vm_size
in self
.conn_compute
.virtual_machine_sizes
.list(self
.region
)
1097 cpus
= filter_dict
.get("vcpus") or 0
1098 memMB
= filter_dict
.get("ram") or 0
1101 if self
._config
.get("flavors_pattern"):
1104 for size
in vm_sizes_list
1105 if size
["numberOfCores"] >= cpus
1106 and size
["memoryInMB"] >= memMB
1107 and re
.search(self
._config
.get("flavors_pattern"), size
["name"])
1112 for size
in vm_sizes_list
1113 if size
["numberOfCores"] >= cpus
and size
["memoryInMB"] >= memMB
1117 listedFilteredSizes
= sorted(
1122 k
["resourceDiskSizeInMB"],
1126 if listedFilteredSizes
:
1127 return listedFilteredSizes
[0]["name"]
1129 raise vimconn
.VimConnNotFoundException(
1130 "Cannot find any flavor matching '{}'".format(str(flavor_dict
))
1132 except Exception as e
:
1133 self
._format
_vimconn
_exception
(e
)
1135 def _get_flavor_id_from_flavor_name(self
, flavor_name
):
1136 # self.logger.debug("getting flavor id from flavor name {}".format(flavor_name))
1138 self
._reload
_connection
()
1141 for vm_size
in self
.conn_compute
.virtual_machine_sizes
.list(self
.region
)
1144 output_flavor
= None
1145 for size
in vm_sizes_list
:
1146 if size
["name"] == flavor_name
:
1147 output_flavor
= size
1149 # None is returned if not found anything
1150 return output_flavor
1151 except Exception as e
:
1152 self
._format
_vimconn
_exception
(e
)
1154 def check_vim_connectivity(self
):
1156 self
._reload
_connection
()
1158 except Exception as e
:
1159 raise vimconn
.VimConnException(
1160 "Connectivity issue with Azure API: {}".format(e
)
1163 def get_network(self
, net_id
):
1164 # self.logger.debug("get network id: {}".format(net_id))
1165 # res_name = self._get_resource_name_from_resource_id(net_id)
1166 self
._reload
_connection
()
1168 filter_dict
= {"name": net_id
}
1169 network_list
= self
.get_network_list(filter_dict
)
1171 if not network_list
:
1172 raise vimconn
.VimConnNotFoundException(
1173 "network '{}' not found".format(net_id
)
1176 return network_list
[0]
1178 def delete_network(self
, net_id
, created_items
=None):
1180 "deleting network {} - {}".format(self
.resource_group
, net_id
)
1183 self
._reload
_connection
()
1184 res_name
= self
._get
_resource
_name
_from
_resource
_id
(net_id
)
1185 filter_dict
= {"name": res_name
}
1186 network_list
= self
.get_network_list(filter_dict
)
1187 if not network_list
:
1188 raise vimconn
.VimConnNotFoundException(
1189 "network '{}' not found".format(net_id
)
1193 # Subnet API fails (CloudError: Azure Error: ResourceNotFound)
1194 # Put the initial virtual_network API
1195 async_delete
= self
.conn_vnet
.subnets
.delete(
1196 self
.resource_group
, self
.vnet_name
, res_name
1201 except CloudError
as e
:
1202 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
1203 raise vimconn
.VimConnNotFoundException(
1204 "network '{}' not found".format(net_id
)
1207 self
._format
_vimconn
_exception
(e
)
1208 except Exception as e
:
1209 self
._format
_vimconn
_exception
(e
)
1211 def delete_vminstance(self
, vm_id
, created_items
=None):
1212 """Deletes a vm instance from the vim."""
1214 "deleting VM instance {} - {}".format(self
.resource_group
, vm_id
)
1216 self
._reload
_connection
()
1218 created_items
= created_items
or {}
1220 # Check vm exists, we can call delete_vm to clean created_items
1222 res_name
= self
._get
_resource
_name
_from
_resource
_id
(vm_id
)
1223 vm
= self
.conn_compute
.virtual_machines
.get(
1224 self
.resource_group
, res_name
1227 # Shuts down the virtual machine and releases the compute resources
1228 # vm_stop = self.conn_compute.virtual_machines.power_off(self.resource_group, resName)
1231 vm_delete
= self
.conn_compute
.virtual_machines
.delete(
1232 self
.resource_group
, res_name
1235 self
.logger
.debug("deleted VM name: %s", res_name
)
1238 os_disk_name
= vm
.storage_profile
.os_disk
.name
1239 self
.logger
.debug("delete OS DISK: %s", os_disk_name
)
1240 async_disk_delete
= self
.conn_compute
.disks
.delete(
1241 self
.resource_group
, os_disk_name
1243 async_disk_delete
.wait()
1244 # os disks are created always with the machine
1245 self
.logger
.debug("deleted OS DISK name: %s", os_disk_name
)
1247 for data_disk
in vm
.storage_profile
.data_disks
:
1248 self
.logger
.debug("delete data_disk: %s", data_disk
.name
)
1249 async_disk_delete
= self
.conn_compute
.disks
.delete(
1250 self
.resource_group
, data_disk
.name
1252 async_disk_delete
.wait()
1253 self
._markdel
_created
_item
(data_disk
.managed_disk
.id, created_items
)
1254 self
.logger
.debug("deleted OS DISK name: %s", data_disk
.name
)
1256 # After deleting VM, it is necessary to delete NIC, because if is not deleted delete_network
1257 # does not work because Azure says that is in use the subnet
1258 network_interfaces
= vm
.network_profile
.network_interfaces
1260 for network_interface
in network_interfaces
:
1261 nic_name
= self
._get
_resource
_name
_from
_resource
_id
(
1262 network_interface
.id
1264 nic_data
= self
.conn_vnet
.network_interfaces
.get(
1265 self
.resource_group
, nic_name
1268 public_ip_name
= None
1269 exist_public_ip
= nic_data
.ip_configurations
[0].public_ip_address
1271 public_ip_id
= nic_data
.ip_configurations
[
1273 ].public_ip_address
.id
1276 public_ip_name
= self
._get
_resource
_name
_from
_resource
_id
(
1280 # Public ip must be deleted afterwards of nic that is attached
1282 self
.logger
.debug("delete NIC name: %s", nic_name
)
1283 nic_delete
= self
.conn_vnet
.network_interfaces
.delete(
1284 self
.resource_group
, nic_name
1287 self
._markdel
_created
_item
(network_interface
.id, created_items
)
1288 self
.logger
.debug("deleted NIC name: %s", nic_name
)
1290 # Delete list of public ips
1292 self
.logger
.debug("delete PUBLIC IP - " + public_ip_name
)
1293 ip_delete
= self
.conn_vnet
.public_ip_addresses
.delete(
1294 self
.resource_group
, public_ip_name
1297 self
._markdel
_created
_item
(public_ip_id
, created_items
)
1299 # Delete created items
1300 self
._delete
_created
_items
(created_items
)
1302 except CloudError
as e
:
1303 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
1304 raise vimconn
.VimConnNotFoundException(
1305 "No vm instance found '{}'".format(vm_id
)
1308 self
._format
_vimconn
_exception
(e
)
1309 except Exception as e
:
1310 self
._format
_vimconn
_exception
(e
)
1312 def _markdel_created_item(self
, item_id
, created_items
):
1313 if item_id
in created_items
:
1314 created_items
[item_id
] = False
1316 def _delete_created_items(self
, created_items
):
1317 """Delete created_items elements that have not been deleted with the virtual machine
1318 Created_items may not be deleted correctly with the created machine if the
1319 virtual machine fails creating or in other cases of error
1321 self
.logger
.debug("Created items: %s", created_items
)
1322 # Must delete in order first nics, then public_ips
1323 # As dictionaries don't preserve order, first get items to be deleted then delete them
1325 publics_ip_to_delete
= []
1326 disks_to_delete
= []
1327 for item_id
, v
in created_items
.items():
1328 if not v
: # skip already deleted
1331 # self.logger.debug("Must delete item id: %s", item_id)
1332 # Obtain type, supported nic, disk or public ip
1333 parsed_id
= azure_tools
.parse_resource_id(item_id
)
1334 resource_type
= parsed_id
.get("resource_type")
1335 name
= parsed_id
.get("name")
1337 if resource_type
== "networkInterfaces":
1338 nics_to_delete
.append(name
)
1339 elif resource_type
== "publicIPAddresses":
1340 publics_ip_to_delete
.append(name
)
1341 elif resource_type
== "disks":
1342 disks_to_delete
.append(name
)
1345 for item_name
in nics_to_delete
:
1347 self
.logger
.debug("deleting nic name %s:", item_name
)
1348 nic_delete
= self
.conn_vnet
.network_interfaces
.delete(
1349 self
.resource_group
, item_name
1352 self
.logger
.debug("deleted nic name %s:", item_name
)
1353 except Exception as e
:
1355 "Error deleting item: {}: {}".format(type(e
).__name
__, e
)
1358 for item_name
in publics_ip_to_delete
:
1360 self
.logger
.debug("deleting public ip name %s:", item_name
)
1361 ip_delete
= self
.conn_vnet
.public_ip_addresses
.delete(
1362 self
.resource_group
, name
1365 self
.logger
.debug("deleted public ip name %s:", item_name
)
1366 except Exception as e
:
1368 "Error deleting item: {}: {}".format(type(e
).__name
__, e
)
1371 for item_name
in disks_to_delete
:
1373 self
.logger
.debug("deleting data disk name %s:", name
)
1374 async_disk_delete
= self
.conn_compute
.disks
.delete(
1375 self
.resource_group
, item_name
1377 async_disk_delete
.wait()
1378 self
.logger
.debug("deleted data disk name %s:", name
)
1379 except Exception as e
:
1381 "Error deleting item: {}: {}".format(type(e
).__name
__, e
)
1384 def action_vminstance(self
, vm_id
, action_dict
, created_items
={}):
1385 """Send and action over a VM instance from VIM
1386 Returns the vm_id if the action was successfully sent to the VIM
1388 self
.logger
.debug("Action over VM '%s': %s", vm_id
, str(action_dict
))
1391 self
._reload
_connection
()
1392 resName
= self
._get
_resource
_name
_from
_resource
_id
(vm_id
)
1394 if "start" in action_dict
:
1395 self
.conn_compute
.virtual_machines
.start(self
.resource_group
, resName
)
1397 "stop" in action_dict
1398 or "shutdown" in action_dict
1399 or "shutoff" in action_dict
1401 self
.conn_compute
.virtual_machines
.power_off(
1402 self
.resource_group
, resName
1404 elif "terminate" in action_dict
:
1405 self
.conn_compute
.virtual_machines
.delete(self
.resource_group
, resName
)
1406 elif "reboot" in action_dict
:
1407 self
.conn_compute
.virtual_machines
.restart(self
.resource_group
, resName
)
1410 except CloudError
as e
:
1411 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
1412 raise vimconn
.VimConnNotFoundException("No vm found '{}'".format(vm_id
))
1414 self
._format
_vimconn
_exception
(e
)
1415 except Exception as e
:
1416 self
._format
_vimconn
_exception
(e
)
1418 def delete_flavor(self
, flavor_id
):
1419 raise vimconn
.VimConnAuthException(
1420 "It is not possible to delete a FLAVOR in AZURE"
1423 def delete_tenant(self
, tenant_id
):
1424 raise vimconn
.VimConnAuthException(
1425 "It is not possible to delete a TENANT in AZURE"
1428 def delete_image(self
, image_id
):
1429 raise vimconn
.VimConnAuthException(
1430 "It is not possible to delete a IMAGE in AZURE"
1433 def get_vminstance(self
, vm_id
):
1435 Obtaing the vm instance data from v_id
1437 self
.logger
.debug("get vm instance: %s", vm_id
)
1438 self
._reload
_connection
()
1440 resName
= self
._get
_resource
_name
_from
_resource
_id
(vm_id
)
1441 vm
= self
.conn_compute
.virtual_machines
.get(self
.resource_group
, resName
)
1442 except CloudError
as e
:
1443 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
1444 raise vimconn
.VimConnNotFoundException(
1445 "No vminstance found '{}'".format(vm_id
)
1448 self
._format
_vimconn
_exception
(e
)
1449 except Exception as e
:
1450 self
._format
_vimconn
_exception
(e
)
1454 def get_flavor(self
, flavor_id
):
1456 Obtains the flavor_data from the flavor_id
1458 self
._reload
_connection
()
1459 self
.logger
.debug("get flavor from id: %s", flavor_id
)
1460 flavor_data
= self
._get
_flavor
_id
_from
_flavor
_name
(flavor_id
)
1466 "ram": flavor_data
["memoryInMB"],
1467 "vcpus": flavor_data
["numberOfCores"],
1468 "disk": flavor_data
["resourceDiskSizeInMB"] / 1024,
1473 raise vimconn
.VimConnNotFoundException(
1474 "flavor '{}' not found".format(flavor_id
)
1477 def get_tenant_list(self
, filter_dict
={}):
1478 """Obtains the list of tenants
1479 For the azure connector only the azure tenant will be returned if it is compatible
1482 tenants_azure
= [{"name": self
.tenant
, "id": self
.tenant
}]
1485 self
.logger
.debug("get tenant list: %s", filter_dict
)
1486 for tenant_azure
in tenants_azure
:
1489 filter_dict
.get("id")
1490 and str(tenant_azure
.get("id")) != filter_dict
["id"]
1495 filter_dict
.get("name")
1496 and str(tenant_azure
.get("name")) != filter_dict
["name"]
1500 tenant_list
.append(tenant_azure
)
1504 def refresh_nets_status(self
, net_list
):
1505 """Get the status of the networks
1506 Params: the list of network identifiers
1507 Returns a dictionary with:
1508 net_id: #VIM id of this network
1509 status: #Mandatory. Text with one of:
1510 # DELETED (not found at vim)
1511 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1512 # OTHER (Vim reported other status not understood)
1513 # ERROR (VIM indicates an ERROR status)
1514 # ACTIVE, INACTIVE, DOWN (admin down),
1515 # BUILD (on building process)
1517 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1518 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1521 self
._reload
_connection
()
1523 self
.logger
.debug("reload nets status net_list: %s", net_list
)
1524 for net_id
in net_list
:
1526 netName
= self
._get
_net
_name
_from
_resource
_id
(net_id
)
1527 resName
= self
._get
_resource
_name
_from
_resource
_id
(net_id
)
1529 net
= self
.conn_vnet
.subnets
.get(self
.resource_group
, netName
, resName
)
1531 out_nets
[net_id
] = {
1532 "status": self
.provision_state2osm
[net
.provisioning_state
],
1533 "vim_info": str(net
),
1535 except CloudError
as e
:
1536 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
1538 "Not found subnet net_name: %s, subnet_name: %s",
1542 out_nets
[net_id
] = {"status": "DELETED", "error_msg": str(e
)}
1545 "CloudError Exception %s when searching subnet", e
1547 out_nets
[net_id
] = {
1548 "status": "VIM_ERROR",
1549 "error_msg": str(e
),
1551 except vimconn
.VimConnNotFoundException
as e
:
1553 "VimConnNotFoundException %s when searching subnet", e
1555 out_nets
[net_id
] = {
1556 "status": "DELETED",
1557 "error_msg": str(e
),
1559 except Exception as e
:
1561 "Exception %s when searching subnet", e
, exc_info
=True
1563 out_nets
[net_id
] = {
1564 "status": "VIM_ERROR",
1565 "error_msg": str(e
),
1570 def refresh_vms_status(self
, vm_list
):
1571 """Get the status of the virtual machines and their interfaces/ports
1572 Params: the list of VM identifiers
1573 Returns a dictionary with:
1574 vm_id: # VIM id of this Virtual Machine
1575 status: # Mandatory. Text with one of:
1576 # DELETED (not found at vim)
1577 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1578 # OTHER (Vim reported other status not understood)
1579 # ERROR (VIM indicates an ERROR status)
1580 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1581 # BUILD (on building process), ERROR
1582 # ACTIVE:NoMgmtIP (Active but none of its interfaces has an IP address
1583 # (ACTIVE:NoMgmtIP is not returned for Azure)
1585 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1586 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1587 interfaces: list with interface info. Each item a dictionary with:
1588 vim_interface_id - The ID of the interface
1589 mac_address - The MAC address of the interface.
1590 ip_address - The IP address of the interface within the subnet.
1593 self
._reload
_connection
()
1595 self
.logger
.debug("refresh vm status vm_list: %s", vm_list
)
1596 search_vm_list
= vm_list
or {}
1598 for vm_id
in search_vm_list
:
1601 res_name
= self
._get
_resource
_name
_from
_resource
_id
(vm_id
)
1603 vm
= self
.conn_compute
.virtual_machines
.get(
1604 self
.resource_group
, res_name
1606 out_vm
["vim_info"] = str(vm
)
1607 out_vm
["status"] = self
.provision_state2osm
.get(
1608 vm
.provisioning_state
, "OTHER"
1611 if vm
.provisioning_state
== "Succeeded":
1612 # check if machine is running or stopped
1613 instance_view
= self
.conn_compute
.virtual_machines
.instance_view(
1614 self
.resource_group
, res_name
1617 for status
in instance_view
.statuses
:
1618 splitted_status
= status
.code
.split("/")
1620 len(splitted_status
) == 2
1621 and splitted_status
[0] == "PowerState"
1623 out_vm
["status"] = self
.power_state2osm
.get(
1624 splitted_status
[1], "OTHER"
1627 network_interfaces
= vm
.network_profile
.network_interfaces
1628 out_vm
["interfaces"] = self
._get
_vm
_interfaces
_status
(
1629 vm_id
, network_interfaces
1632 except CloudError
as e
:
1633 if e
.error
.error
and "notfound" in e
.error
.error
.lower():
1634 self
.logger
.debug("Not found vm id: %s", vm_id
)
1635 out_vm
["status"] = "DELETED"
1636 out_vm
["error_msg"] = str(e
)
1637 out_vm
["vim_info"] = None
1639 # maybe connection error or another type of error, return vim error
1640 self
.logger
.error("Exception %s refreshing vm_status", e
)
1641 out_vm
["status"] = "VIM_ERROR"
1642 out_vm
["error_msg"] = str(e
)
1643 out_vm
["vim_info"] = None
1644 except Exception as e
:
1645 self
.logger
.error("Exception %s refreshing vm_status", e
, exc_info
=True)
1646 out_vm
["status"] = "VIM_ERROR"
1647 out_vm
["error_msg"] = str(e
)
1648 out_vm
["vim_info"] = None
1650 out_vms
[vm_id
] = out_vm
1654 def _get_vm_interfaces_status(self
, vm_id
, interfaces
):
1656 Gets the interfaces detail for a vm
1657 :param interfaces: List of interfaces.
1658 :return: Dictionary with list of interfaces including, vim_interface_id, mac_address and ip_address
1662 for network_interface
in interfaces
:
1664 nic_name
= self
._get
_resource
_name
_from
_resource
_id
(
1665 network_interface
.id
1667 interface_dict
["vim_interface_id"] = network_interface
.id
1669 nic_data
= self
.conn_vnet
.network_interfaces
.get(
1670 self
.resource_group
,
1675 if nic_data
.ip_configurations
[0].public_ip_address
:
1676 self
.logger
.debug("Obtain public ip address")
1677 public_ip_name
= self
._get
_resource
_name
_from
_resource
_id
(
1678 nic_data
.ip_configurations
[0].public_ip_address
.id
1680 public_ip
= self
.conn_vnet
.public_ip_addresses
.get(
1681 self
.resource_group
, public_ip_name
1683 self
.logger
.debug("Public ip address is: %s", public_ip
.ip_address
)
1684 ips
.append(public_ip
.ip_address
)
1686 private_ip
= nic_data
.ip_configurations
[0].private_ip_address
1687 ips
.append(private_ip
)
1689 interface_dict
["mac_address"] = nic_data
.mac_address
1690 interface_dict
["ip_address"] = ";".join(ips
)
1691 interface_list
.append(interface_dict
)
1693 return interface_list
1694 except Exception as e
:
1696 "Exception %s obtaining interface data for vm: %s, error: %s",
1701 self
._format
_vimconn
_exception
(e
)
1704 if __name__
== "__main__":
1705 # Making some basic test
1708 needed_test_params
= {
1709 "client_id": "AZURE_CLIENT_ID",
1710 "secret": "AZURE_SECRET",
1711 "tenant": "AZURE_TENANT",
1712 "resource_group": "AZURE_RESOURCE_GROUP",
1713 "subscription_id": "AZURE_SUBSCRIPTION_ID",
1714 "vnet_name": "AZURE_VNET_NAME",
1718 for param
, env_var
in needed_test_params
.items():
1719 value
= getenv(env_var
)
1722 raise Exception("Provide a valid value for env '{}'".format(env_var
))
1724 test_params
[param
] = value
1727 "region_name": getenv("AZURE_REGION_NAME", "westeurope"),
1728 "resource_group": getenv("AZURE_RESOURCE_GROUP"),
1729 "subscription_id": getenv("AZURE_SUBSCRIPTION_ID"),
1730 "pub_key": getenv("AZURE_PUB_KEY", None),
1731 "vnet_name": getenv("AZURE_VNET_NAME", "myNetwork"),
1736 "description": "new VM",
1737 "status": "running",
1739 "publisher": "Canonical",
1740 "offer": "UbuntuServer",
1741 "sku": "16.04.0-LTS",
1742 "version": "latest",
1744 "hardware_profile": {"vm_size": "Standard_DS1_v2"},
1745 "networks": ["sergio"],
1749 "subnet_address": "10.1.2.0/24",
1750 # "subnet_name": "subnet-oam"
1752 ###########################
1754 azure
= vimconnector(
1757 tenant_id
=test_params
["tenant"],
1761 user
=test_params
["client_id"],
1762 passwd
=test_params
["secret"],
1767 # azure.get_flavor_id_from_data("here")
1768 # subnets=azure.get_network_list()
1769 # azure.new_vminstance(virtualMachine["name"], virtualMachine["description"], virtualMachine["status"],
1770 # virtualMachine["image"], virtualMachine["hardware_profile"]["vm_size"], subnets)
1772 azure
.new_network("mynet", None)
1774 "/subscriptions/82f80cc1-876b-4591-9911-1fb5788384fd/resourceGroups/osmRG/providers/Microsoft."
1775 "Network/virtualNetworks/test"
1777 net_id_not_found
= (
1778 "/subscriptions/82f80cc1-876b-4591-9911-1fb5788384fd/resourceGroups/osmRG/providers/"
1779 "Microsoft.Network/virtualNetworks/testALF"
1781 azure
.refresh_nets_status([net_id
, net_id_not_found
])