| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 1 | # -*- coding: utf-8 -*- |
| 2 | |
| 3 | __author__='Sergio Gonzalez' |
| 4 | __date__ ='$18-apr-2019 23:59:59$' |
| 5 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 6 | import base64 |
| 7 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 8 | import vimconn |
| 9 | import logging |
| tierno | 2462041 | 2019-06-03 14:05:08 +0000 | [diff] [blame] | 10 | import netaddr |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 11 | |
| 12 | from os import getenv |
| 13 | from uuid import uuid4 |
| 14 | |
| 15 | from azure.common.credentials import ServicePrincipalCredentials |
| 16 | from azure.mgmt.resource import ResourceManagementClient |
| 17 | from azure.mgmt.network import NetworkManagementClient |
| 18 | from azure.mgmt.compute import ComputeManagementClient |
| 19 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 20 | |
| 21 | |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 22 | from msrestazure.azure_exceptions import CloudError |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 23 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 24 | if getenv('OSMRO_PDB_DEBUG'): |
| 25 | import sys |
| 26 | print(sys.path) |
| 27 | import pdb |
| 28 | pdb.set_trace() |
| 29 | |
| 30 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 31 | class vimconnector(vimconn.vimconnector): |
| 32 | |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 33 | provision_state2osm = { |
| 34 | "Deleting": "INACTIVE", |
| 35 | "Failed": "ERROR", |
| 36 | "Succeeded": "ACTIVE", |
| 37 | "Updating": "BUILD", |
| 38 | } |
| 39 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 40 | def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level=None, |
| 41 | config={}, persistent_info={}): |
| 42 | |
| 43 | vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level, |
| 44 | config, persistent_info) |
| 45 | |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 46 | self.vnet_address_space = None |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 47 | # LOGGER |
| 48 | self.logger = logging.getLogger('openmano.vim.azure') |
| 49 | if log_level: |
| 50 | logging.basicConfig() |
| 51 | self.logger.setLevel(getattr(logging, log_level)) |
| 52 | |
| 53 | # CREDENTIALS |
| 54 | self.credentials = ServicePrincipalCredentials( |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 55 | client_id=user, |
| 56 | secret=passwd, |
| 57 | tenant=(tenant_id or tenant_name) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 58 | ) |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 59 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 60 | self.tenant=(tenant_id or tenant_name) |
| 61 | |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 62 | # SUBSCRIPTION |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 63 | if 'subscription_id' in config: |
| 64 | self.subscription_id = config.get('subscription_id') |
| 65 | self.logger.debug('Setting subscription '+str(self.subscription_id)) |
| 66 | else: |
| 67 | raise vimconn.vimconnException('Subscription not specified') |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 68 | # REGION |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 69 | if 'region_name' in config: |
| 70 | self.region = config.get('region_name') |
| 71 | else: |
| 72 | raise vimconn.vimconnException('Azure region_name is not specified at config') |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 73 | # RESOURCE_GROUP |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 74 | if 'resource_group' in config: |
| 75 | self.resource_group = config.get('resource_group') |
| 76 | else: |
| 77 | raise vimconn.vimconnException('Azure resource_group is not specified at config') |
| 78 | # VNET_NAME |
| 79 | if 'vnet_name' in config: |
| 80 | self.vnet_name = config["vnet_name"] |
| 81 | |
| 82 | # public ssh key |
| 83 | self.pub_key = config.get('pub_key') |
| 84 | |
| 85 | def _reload_connection(self): |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 86 | """ |
| 87 | Sets connections to work with Azure service APIs |
| 88 | :return: |
| 89 | """ |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 90 | self.logger.debug('Reloading API Connection') |
| 91 | try: |
| 92 | self.conn = ResourceManagementClient(self.credentials, self.subscription_id) |
| 93 | self.conn_compute = ComputeManagementClient(self.credentials, self.subscription_id) |
| 94 | self.conn_vnet = NetworkManagementClient(self.credentials, self.subscription_id) |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 95 | self._check_or_create_resource_group() |
| 96 | self._check_or_create_vnet() |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 97 | except Exception as e: |
| 98 | self.format_vimconn_exception(e) |
| 99 | |
| 100 | def _get_resource_name_from_resource_id(self, resource_id): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 101 | |
| 102 | try: |
| 103 | resource=str(resource_id.split('/')[-1]) |
| 104 | return resource |
| 105 | except Exception as e: |
| 106 | raise vimconn.vimconnNotFoundException("Resource name '{}' not found".format(resource_id)) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 107 | |
| 108 | def _get_location_from_resource_group(self, resource_group_name): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 109 | |
| 110 | try: |
| 111 | location=self.conn.resource_groups.get(resource_group_name).location |
| 112 | return location |
| 113 | except Exception as e: |
| 114 | raise vimconn.vimconnNotFoundException("Location '{}' not found".format(resource_group_name)) |
| 115 | |
| 116 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 117 | def _get_resource_group_name_from_resource_id(self, resource_id): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 118 | |
| 119 | try: |
| 120 | rg=str(resource_id.split('/')[4]) |
| 121 | return rg |
| 122 | except Exception as e: |
| 123 | raise vimconn.vimconnNotFoundException("Resource group '{}' not found".format(resource_id)) |
| 124 | |
| 125 | |
| 126 | def _get_net_name_from_resource_id(self, resource_id): |
| 127 | |
| 128 | try: |
| 129 | net_name=str(resource_id.split('/')[8]) |
| 130 | return net_name |
| 131 | except Exception as e: |
| 132 | raise vimconn.vimconnNotFoundException("Net name '{}' not found".format(resource_id)) |
| 133 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 134 | |
| 135 | def _check_subnets_for_vm(self, net_list): |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 136 | # All subnets must belong to the same resource group and vnet |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 137 | # ERROR |
| 138 | # File "/root/RO/build/osm_ro/vimconn_azure.py", line 110, in <genexpr> |
| 139 | # self._get_resource_name_from_resource_id(net['id']) for net in net_list)) != 1: |
| 140 | #if len(set(self._get_resource_group_name_from_resource_id(net['net_id']) + |
| 141 | # self._get_resource_name_from_resource_id(net['net_id']) for net in net_list)) != 2: |
| 142 | # raise self.format_vimconn_exception('Azure VMs can only attach to subnets in same VNET') |
| 143 | self.logger.debug('Checking subnets for VM') |
| 144 | num_elem_set = len(set(self._get_resource_group_name_from_resource_id(net['net_id']) + |
| 145 | self._get_resource_name_from_resource_id(net['net_id']) for net in net_list)) |
| 146 | |
| 147 | if ( num_elem_set != 1 ): |
| 148 | raise self.format_vimconn_exception('Azure VMs can only attach to subnets in same VNET') |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 149 | |
| 150 | def format_vimconn_exception(self, e): |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 151 | """ |
| 152 | Params: an Exception object |
| 153 | :param e: |
| 154 | :return: Raises the proper vimconnException |
| 155 | """ |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 156 | self.conn = None |
| 157 | self.conn_vnet = None |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 158 | raise vimconn.vimconnException(type(e).__name__ + ': ' + str(e)) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 159 | |
| 160 | def _check_or_create_resource_group(self): |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 161 | """ |
| 162 | Creates a resource group in indicated region |
| 163 | :return: None |
| 164 | """ |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 165 | self.logger.debug('Creating RG {} in location {}'.format(self.resource_group, self.region)) |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 166 | self.conn.resource_groups.create_or_update(self.resource_group, {'location': self.region}) |
| 167 | |
| 168 | def _check_or_create_vnet(self): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 169 | |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 170 | try: |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 171 | vnet = self.conn_vnet.virtual_networks.get(self.resource_group, self.vnet_name) |
| 172 | self.vnet_address_space = vnet.address_space.address_prefixes[0] |
| tierno | 2462041 | 2019-06-03 14:05:08 +0000 | [diff] [blame] | 173 | self.vnet_id = vnet.id |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 174 | |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 175 | return |
| 176 | except CloudError as e: |
| 177 | if e.error.error == "ResourceNotFound": |
| 178 | pass |
| 179 | else: |
| 180 | raise |
| 181 | # if not exist, creates it |
| 182 | try: |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 183 | vnet_params = { |
| 184 | 'location': self.region, |
| 185 | 'address_space': { |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 186 | 'address_prefixes': ["10.0.0.0/8"] |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 187 | }, |
| 188 | } |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 189 | self.vnet_address_space = "10.0.0.0/8" |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 190 | |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 191 | self.conn_vnet.virtual_networks.create_or_update(self.resource_group, self.vnet_name, vnet_params) |
| tierno | 2462041 | 2019-06-03 14:05:08 +0000 | [diff] [blame] | 192 | vnet = self.conn_vnet.virtual_networks.get(self.resource_group, self.vnet_name) |
| 193 | self.vnet_id = vnet.id |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 194 | except Exception as e: |
| 195 | self.format_vimconn_exception(e) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 196 | |
| 197 | def new_network(self, net_name, net_type, ip_profile=None, shared=False, vlan=None): |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 198 | """ |
| 199 | Adds a tenant network to VIM |
| 200 | :param net_name: name of the network |
| 201 | :param net_type: |
| 202 | :param ip_profile: is a dict containing the IP parameters of the network (Currently only IPv4 is implemented) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 203 | 'ip-version': can be one of ['IPv4','IPv6'] |
| 204 | 'subnet-address': ip_prefix_schema, that is X.X.X.X/Y |
| 205 | 'gateway-address': (Optional) ip_schema, that is X.X.X.X |
| 206 | 'dns-address': (Optional) ip_schema, |
| 207 | 'dhcp': (Optional) dict containing |
| 208 | 'enabled': {'type': 'boolean'}, |
| 209 | 'start-address': ip_schema, first IP to grant |
| 210 | 'count': number of IPs to grant. |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 211 | :param shared: |
| 212 | :param vlan: |
| 213 | :return: a tuple with the network identifier and created_items, or raises an exception on error |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 214 | created_items can be None or a dictionary where this method can include key-values that will be passed to |
| 215 | the method delete_network. Can be used to store created segments, created l2gw connections, etc. |
| 216 | Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same |
| 217 | as not present. |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 218 | """ |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 219 | return self._new_subnet(net_name, ip_profile) |
| 220 | |
| 221 | def _new_subnet(self, net_name, ip_profile): |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 222 | """ |
| 223 | Adds a tenant network to VIM. It creates a new VNET with a single subnet |
| 224 | :param net_name: |
| 225 | :param ip_profile: |
| 226 | :return: |
| 227 | """ |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 228 | self.logger.debug('Adding a subnet to VNET '+self.vnet_name) |
| 229 | self._reload_connection() |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 230 | |
| 231 | if ip_profile is None: |
| tierno | 2462041 | 2019-06-03 14:05:08 +0000 | [diff] [blame] | 232 | # get a non used vnet ip range /24 and allocate automatically inside the range self.vnet_address_space |
| 233 | used_subnets = self.get_network_list() |
| 234 | for ip_range in netaddr.IPNetwork(self.vnet_address_space).subnet(24): |
| 235 | for used_subnet in used_subnets: |
| 236 | subnet_range = netaddr.IPNetwork(used_subnet["cidr_block"]) |
| 237 | if subnet_range in ip_range or ip_range in subnet_range: |
| 238 | # this range overlaps with an existing subnet ip range. Breaks and look for another |
| 239 | break |
| 240 | else: |
| 241 | ip_profile = {"subnet_address": str(ip_range)} |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 242 | self.logger.debug('ip_profile: ' + str(ip_range)) |
| tierno | 2462041 | 2019-06-03 14:05:08 +0000 | [diff] [blame] | 243 | break |
| 244 | else: |
| 245 | vimconn.vimconnException("Cannot find a non-used subnet range in {}".format(self.vnet_address_space)) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 246 | else: |
| 247 | ip_profile = {"subnet_address": ip_profile['subnet_address']} |
| 248 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 249 | try: |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 250 | #subnet_name = "{}-{}".format(net_name[:24], uuid4()) |
| 251 | subnet_name = net_name[:24] |
| tierno | 2462041 | 2019-06-03 14:05:08 +0000 | [diff] [blame] | 252 | subnet_params= { |
| 253 | 'address_prefix': ip_profile['subnet_address'] |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 254 | } |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 255 | self.logger.debug('subnet_name : {}'.format(subnet_name)) |
| 256 | async_creation=self.conn_vnet.subnets.create_or_update(self.resource_group, self.vnet_name, subnet_name, subnet_params) |
| 257 | async_creation.wait() |
| 258 | |
| 259 | #return "{}/subnet/{}".format(self.vnet_id, subnet_name), None |
| 260 | return "{}/subnets/{}".format(self.vnet_id, subnet_name), None |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 261 | except Exception as e: |
| 262 | self.format_vimconn_exception(e) |
| 263 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 264 | def _create_nic(self, net, nic_name, static_ip=None): |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 265 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 266 | self._reload_connection() |
| 267 | |
| 268 | subnet_id = net['net_id'] |
| 269 | location = self._get_location_from_resource_group(self.resource_group) |
| 270 | |
| 271 | try: |
| 272 | if static_ip: |
| 273 | async_nic_creation = self.conn_vnet.network_interfaces.create_or_update( |
| 274 | self.resource_group, |
| 275 | nic_name, |
| 276 | { |
| 277 | 'location': location, |
| 278 | 'ip_configurations': [{ |
| 279 | 'name': nic_name + '-ipconfiguration', |
| 280 | 'privateIPAddress': static_ip, |
| 281 | 'privateIPAllocationMethod': 'Static', |
| 282 | 'subnet': { |
| 283 | 'id': subnet_id |
| 284 | } |
| 285 | }] |
| 286 | } |
| 287 | ) |
| 288 | async_nic_creation.wait() |
| 289 | else: |
| 290 | ip_configuration_name = nic_name + '-ipconfiguration' |
| 291 | self.logger.debug('Create NIC') |
| 292 | async_nic_creation = self.conn_vnet.network_interfaces.create_or_update( |
| 293 | self.resource_group, |
| 294 | nic_name, |
| 295 | { |
| 296 | 'location': location, |
| 297 | 'ip_configurations': [{ |
| 298 | 'name': ip_configuration_name, |
| 299 | 'subnet': { |
| 300 | 'id': subnet_id |
| 301 | } |
| 302 | }] |
| 303 | } |
| 304 | ) |
| 305 | async_nic_creation.wait() |
| 306 | |
| 307 | public_ip = net.get('floating_ip') |
| 308 | if public_ip and public_ip == True: |
| 309 | self.logger.debug('Creating PUBLIC IP') |
| 310 | public_ip_addess_params = { |
| 311 | 'location': location, |
| 312 | 'public_ip_allocation_method': 'Dynamic' |
| 313 | } |
| 314 | public_ip_name = nic_name + '-public-ip' |
| 315 | public_ip = self.conn_vnet.public_ip_addresses.create_or_update( |
| 316 | self.resource_group, |
| 317 | public_ip_name, |
| 318 | public_ip_addess_params |
| 319 | ) |
| 320 | self.logger.debug('Create PUBLIC IP: {}'.format(public_ip.result())) |
| 321 | |
| 322 | # Asociate NIC to Public IP |
| 323 | self.logger.debug('Getting NIC DATA') |
| 324 | nic_data = self.conn_vnet.network_interfaces.get( |
| 325 | self.resource_group, |
| 326 | nic_name) |
| 327 | |
| 328 | nic_data.ip_configurations[0].public_ip_address = public_ip.result() |
| 329 | |
| 330 | self.logger.debug('Updating NIC with public IP') |
| 331 | self.conn_vnet.network_interfaces.create_or_update( |
| 332 | self.resource_group, |
| 333 | nic_name, |
| 334 | nic_data) |
| 335 | |
| 336 | except Exception as e: |
| 337 | self.format_vimconn_exception(e) |
| 338 | |
| 339 | result = async_nic_creation.result() |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 340 | return async_nic_creation.result() |
| 341 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 342 | def new_flavor(self, flavor_data): |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 343 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 344 | if flavor_data: |
| 345 | flavor_id = self.get_flavor_id_from_data(flavor_data) |
| 346 | |
| 347 | if flavor_id != []: |
| 348 | return flavor_id |
| 349 | else: |
| 350 | raise vimconn.vimconnNotFoundException("flavor '{}' not found".format(flavor_data)) |
| 351 | else: |
| 352 | vimconn.vimconnException("There is no data in the flavor_data input parameter") |
| 353 | |
| 354 | def new_tenant(self,tenant_name,tenant_description): |
| 355 | |
| 356 | raise vimconn.vimconnAuthException("It is not possible to create a TENANT in AZURE") |
| 357 | |
| 358 | def new_image(self, image_dict): |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 359 | |
| 360 | self._reload_connection() |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 361 | |
| 362 | try: |
| 363 | self.logger.debug('new_image - image_dict - {}'.format(image_dict)) |
| 364 | |
| 365 | if image_dict.get("name"): |
| 366 | image_name = image_dict.get("name") |
| 367 | else: |
| 368 | raise vimconn.vimconnException("There is no name in the image input data") |
| 369 | |
| 370 | if image_dict.get("location"): |
| 371 | params = image_dict["location"].split(":") |
| 372 | if len(params) >= 4: |
| 373 | publisher = params[0] |
| 374 | offer = params[1] |
| 375 | sku = params[2] |
| 376 | version = params[3] |
| 377 | #image_params = {'location': self.region, 'publisher': publisher, 'offer': offer, 'sku': sku, 'version': version } |
| 378 | image_params = {'location': self.region} |
| 379 | |
| 380 | self.conn_compute.images.create_or_update() |
| 381 | async_creation=self.conn_compute.images.create_or_update(self.resource_group, image_name, image_params) |
| 382 | image_id = async_creation.result().id |
| 383 | else: |
| 384 | raise vimconn.vimconnException("The image location is not correct: {}".format(image_dict["location"])) |
| 385 | return image_id |
| 386 | |
| 387 | except Exception as e: |
| 388 | self.format_vimconn_exception(e) |
| 389 | |
| 390 | def get_image_id_from_path(self, path): |
| 391 | """Get the image id from image path in the VIM database. |
| 392 | Returns the image_id or raises a vimconnNotFoundException |
| 393 | """ |
| 394 | |
| 395 | def get_image_list(self, filter_dict={}): |
| 396 | """Obtain tenant images from VIM |
| 397 | Filter_dict can be: |
| 398 | name: image name |
| 399 | id: image uuid |
| 400 | checksum: image checksum |
| 401 | location: image path |
| 402 | Returns the image list of dictionaries: |
| 403 | [{<the fields at Filter_dict plus some VIM specific>}, ...] |
| 404 | List can be empty |
| 405 | """ |
| 406 | self._reload_connection() |
| 407 | |
| 408 | image_list = [] |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 409 | if filter_dict.get("name"): |
| 410 | params = filter_dict["name"].split(":") |
| 411 | if len(params) >= 3: |
| 412 | publisher = params[0] |
| 413 | offer = params[1] |
| 414 | sku = params[2] |
| 415 | version = None |
| 416 | if len(params) == 4: |
| 417 | version = params[3] |
| 418 | images = self.conn_compute.virtual_machine_images.list(self.region, publisher, offer, sku) |
| 419 | for image in images: |
| 420 | if version: |
| 421 | image_version = str(image.id).split("/")[-1] |
| 422 | if image_version != version: |
| 423 | continue |
| 424 | image_list.append({ |
| 425 | 'id': str(image.id), |
| 426 | 'name': self._get_resource_name_from_resource_id(image.id) |
| 427 | }) |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 428 | |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 429 | return image_list |
| 430 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 431 | def get_network_list(self, filter_dict={}): |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 432 | """Obtain tenant networks of VIM |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 433 | Filter_dict can be: |
| 434 | name: network name |
| 435 | id: network uuid |
| 436 | shared: boolean |
| 437 | tenant_id: tenant |
| 438 | admin_state_up: boolean |
| 439 | status: 'ACTIVE' |
| 440 | Returns the network list of dictionaries |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 441 | """ |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 442 | self.logger.debug('Getting all subnets from VIM') |
| 443 | try: |
| 444 | self._reload_connection() |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 445 | |
| 446 | vnet = self.conn_vnet.virtual_networks.get(self.resource_group, self.vnet_name) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 447 | subnet_list = [] |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 448 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 449 | for subnet in vnet.subnets: |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 450 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 451 | if filter_dict: |
| 452 | if filter_dict.get("id") and str(subnet.id) != filter_dict["id"]: |
| 453 | continue |
| 454 | if filter_dict.get("name") and \ |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 455 | str(subnet.id) != filter_dict["name"]: |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 456 | continue |
| 457 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 458 | name = self._get_resource_name_from_resource_id(subnet.id) |
| 459 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 460 | subnet_list.append({ |
| 461 | 'id': str(subnet.id), |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 462 | 'name': self._get_resource_name_from_resource_id(subnet.id), |
| 463 | 'status' : self.provision_state2osm[subnet.provisioning_state], |
| 464 | 'cidr_block': str(subnet.address_prefix), |
| 465 | 'type': 'bridge', |
| 466 | 'shared': False |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 467 | } |
| 468 | ) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 469 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 470 | return subnet_list |
| 471 | except Exception as e: |
| 472 | self.format_vimconn_exception(e) |
| 473 | |
| 474 | def new_vminstance(self, vm_name, description, start, image_id, flavor_id, net_list, cloud_config=None, |
| 475 | disk_list=None, availability_zone_index=None, availability_zone_list=None): |
| 476 | |
| 477 | return self._new_vminstance(vm_name, image_id, flavor_id, net_list) |
| 478 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 479 | #def _new_vminstance(self, vm_name, image_id, flavor_id, net_list, cloud_config=None, disk_list=None, |
| 480 | # availability_zone_index=None, availability_zone_list=None): |
| 481 | def new_vminstance(self, name, description, start, image_id, flavor_id, net_list, cloud_config=None, |
| 482 | disk_list=None, |
| 483 | availability_zone_index=None, availability_zone_list=None): |
| 484 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 485 | self._check_subnets_for_vm(net_list) |
| 486 | vm_nics = [] |
| 487 | for idx, net in enumerate(net_list): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 488 | # Fault with subnet_id |
| 489 | # subnet_id=net['subnet_id'] |
| 490 | # subnet_id=net['net_id'] |
| 491 | |
| 492 | nic_name = name + '-nic-'+str(idx) |
| 493 | vm_nic = self._create_nic(net, nic_name) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 494 | vm_nics.append({ 'id': str(vm_nic.id)}) |
| 495 | |
| 496 | try: |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 497 | # image_id are several fields of the image_id |
| 498 | image_reference = self.get_image_reference(image_id) |
| 499 | |
| 500 | # The virtual machine name must have less or 64 characters and it can not have the following |
| 501 | # characters: (~ ! @ # $ % ^ & * ( ) = + _ [ ] { } \ | ; : ' " , < > / ?.) |
| 502 | vm_name_aux = self.check_vm_name(name) |
| 503 | |
| 504 | # cloud-init configuration |
| 505 | # cloud config |
| 506 | if cloud_config: |
| 507 | config_drive, userdata = self._create_user_data(cloud_config) |
| 508 | custom_data = base64.b64encode(userdata.encode('utf-8')).decode('latin-1') |
| 509 | os_profile = { |
| 510 | 'computer_name': vm_name_aux, # TODO if vm_name cannot be repeated add uuid4() suffix |
| 511 | 'admin_username': 'osm', # TODO is it mandatory??? |
| 512 | 'admin_password': 'Osm-osm', # TODO is it mandatory??? |
| 513 | 'custom_data': custom_data |
| 514 | } |
| 515 | else: |
| 516 | os_profile = { |
| 517 | 'computer_name': vm_name_aux, # TODO if vm_name cannot be repeated add uuid4() suffix |
| 518 | 'admin_username': 'osm', # TODO is it mandatory??? |
| 519 | 'admin_password': 'Osm-osm', # TODO is it mandatory??? |
| 520 | } |
| 521 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 522 | vm_parameters = { |
| 523 | 'location': self.region, |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 524 | 'os_profile': os_profile, |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 525 | 'hardware_profile': { |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 526 | 'vm_size': flavor_id |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 527 | }, |
| 528 | 'storage_profile': { |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 529 | 'image_reference': image_reference |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 530 | }, |
| 531 | 'network_profile': { |
| 532 | 'network_interfaces': [ |
| 533 | vm_nics[0] |
| 534 | ] |
| 535 | } |
| 536 | } |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 537 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 538 | creation_result = self.conn_compute.virtual_machines.create_or_update( |
| 539 | self.resource_group, |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 540 | vm_name_aux, |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 541 | vm_parameters |
| 542 | ) |
| 543 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 544 | #creation_result.wait() |
| 545 | result = creation_result.result() |
| 546 | |
| 547 | for index, subnet in enumerate(net_list): |
| 548 | net_list[index]['vim_id'] = result.id |
| 549 | |
| 550 | if start == True: |
| 551 | #self.logger.debug('Arrancamos VM y esperamos') |
| 552 | start_result = self.conn_compute.virtual_machines.start( |
| 553 | self.resource_group, |
| 554 | vm_name_aux) |
| 555 | #start_result.wait() |
| 556 | |
| 557 | return result.id, None |
| 558 | |
| 559 | #run_command_parameters = { |
| 560 | # 'command_id': 'RunShellScript', # For linux, don't change it |
| 561 | # 'script': [ |
| 562 | # 'date > /tmp/test.txt' |
| 563 | # ] |
| 564 | #} |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 565 | except Exception as e: |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 566 | #self.logger.debug('AZURE <=== EX: _new_vminstance', exc_info=True) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 567 | self.format_vimconn_exception(e) |
| 568 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 569 | # It is necesary extract from image_id data to create the VM with this format |
| 570 | # 'image_reference': { |
| 571 | # 'publisher': vm_reference['publisher'], |
| 572 | # 'offer': vm_reference['offer'], |
| 573 | # 'sku': vm_reference['sku'], |
| 574 | # 'version': vm_reference['version'] |
| 575 | # }, |
| 576 | def get_image_reference(self, imagen): |
| 577 | |
| 578 | # The data input format example: |
| 579 | # /Subscriptions/ca3d18ab-d373-4afb-a5d6-7c44f098d16a/Providers/Microsoft.Compute/Locations/westeurope/ |
| 580 | # Publishers/Canonical/ArtifactTypes/VMImage/ |
| 581 | # Offers/UbuntuServer/ |
| 582 | # Skus/18.04-LTS/ |
| 583 | # Versions/18.04.201809110 |
| 584 | publiser = str(imagen.split('/')[8]) |
| 585 | offer = str(imagen.split('/')[12]) |
| 586 | sku = str(imagen.split('/')[14]) |
| 587 | version = str(imagen.split('/')[16]) |
| 588 | |
| 589 | return { |
| 590 | 'publisher': publiser, |
| 591 | 'offer': offer, |
| 592 | 'sku': sku, |
| 593 | 'version': version |
| 594 | } |
| 595 | |
| 596 | # Azure VM names can not have some special characters |
| 597 | def check_vm_name( self, vm_name ): |
| 598 | |
| 599 | #chars_not_allowed_list = ['~','!','@','#','$','%','^','&','*','(',')','=','+','_','[',']','{','}','|',';',':','<','>','/','?','.'] |
| 600 | chars_not_allowed_list = "~!@#$%^&*()=+_[]{}|;:<>/?." |
| 601 | |
| 602 | # First: the VM name max length is 64 characters |
| 603 | vm_name_aux = vm_name[:64] |
| 604 | |
| 605 | # Second: replace not allowed characters |
| 606 | for elem in chars_not_allowed_list : |
| 607 | # Check if string is in the main string |
| 608 | if elem in vm_name_aux : |
| 609 | #self.logger.debug('Dentro del IF') |
| 610 | # Replace the string |
| 611 | vm_name_aux = vm_name_aux.replace(elem, '-') |
| 612 | |
| 613 | return vm_name_aux |
| 614 | |
| 615 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 616 | def get_flavor_id_from_data(self, flavor_dict): |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 617 | self.logger.debug("Getting flavor id from data") |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 618 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 619 | try: |
| 620 | self._reload_connection() |
| 621 | vm_sizes_list = [vm_size.serialize() for vm_size in self.conn_compute.virtual_machine_sizes.list(self.region)] |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 622 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 623 | cpus = flavor_dict['vcpus'] |
| 624 | memMB = flavor_dict['ram'] |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 625 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 626 | filteredSizes = [size for size in vm_sizes_list if size['numberOfCores'] >= cpus and size['memoryInMB'] >= memMB] |
| 627 | listedFilteredSizes = sorted(filteredSizes, key=lambda k: k['numberOfCores']) |
| 628 | |
| 629 | return listedFilteredSizes[0]['name'] |
| 630 | |
| 631 | except Exception as e: |
| 632 | self.format_vimconn_exception(e) |
| 633 | |
| 634 | def _get_flavor_id_from_flavor_name(self, flavor_name): |
| 635 | self.logger.debug("Getting flavor id from falvor name {}".format(flavor_name)) |
| 636 | |
| 637 | try: |
| 638 | self._reload_connection() |
| 639 | vm_sizes_list = [vm_size.serialize() for vm_size in self.conn_compute.virtual_machine_sizes.list(self.region)] |
| 640 | |
| 641 | output_flavor = None |
| 642 | for size in vm_sizes_list: |
| 643 | if size['name'] == flavor_name: |
| 644 | output_flavor = size |
| 645 | |
| 646 | return output_flavor |
| 647 | |
| 648 | except Exception as e: |
| 649 | self.format_vimconn_exception(e) |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 650 | |
| 651 | def check_vim_connectivity(self): |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 652 | try: |
| 653 | self._reload_connection() |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 654 | return True |
| 655 | except Exception as e: |
| 656 | raise vimconn.vimconnException("Connectivity issue with Azure API: {}".format(e)) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 657 | |
| 658 | def get_network(self, net_id): |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 659 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 660 | resName = self._get_resource_name_from_resource_id(net_id) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 661 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 662 | self._reload_connection() |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 663 | |
| 664 | filter_dict = {'name' : net_id} |
| 665 | network_list = self.get_network_list(filter_dict) |
| 666 | |
| 667 | if not network_list: |
| 668 | raise vimconn.vimconnNotFoundException("network '{}' not found".format(net_id)) |
| 669 | else: |
| 670 | return network_list[0] |
| 671 | |
| 672 | # Added created_items because it is neccesary |
| 673 | # self.vim.delete_network(net_vim_id, task["extra"].get("created_items")) |
| 674 | # TypeError: delete_network() takes exactly 2 arguments (3 given) |
| 675 | def delete_network(self, net_id, created_items=None): |
| 676 | |
| 677 | self.logger.debug('Deletting network {} - {}'.format(self.resource_group, net_id)) |
| 678 | |
| 679 | resName = self._get_resource_name_from_resource_id(net_id) |
| 680 | |
| 681 | self._reload_connection() |
| 682 | |
| 683 | filter_dict = {'name' : net_id} |
| 684 | network_list = self.get_network_list(filter_dict) |
| 685 | if not network_list: |
| 686 | raise vimconn.vimconnNotFoundException("network '{}' not found".format(net_id)) |
| 687 | |
| 688 | try: |
| 689 | # Subnet API fails (CloudError: Azure Error: ResourceNotFound) |
| 690 | # Put the initial virtual_network API |
| 691 | async_delete=self.conn_vnet.subnets.delete(self.resource_group, self.vnet_name, resName) |
| 692 | return net_id |
| 693 | |
| 694 | except CloudError as e: |
| 695 | if e.error.error == "ResourceNotFound": |
| 696 | raise vimconn.vimconnNotFoundException("network '{}' not found".format(net_id)) |
| 697 | else: |
| 698 | raise |
| 699 | except Exception as e: |
| 700 | self.format_vimconn_exception(e) |
| 701 | |
| 702 | |
| 703 | |
| 704 | # Added third parameter because it is necesary |
| 705 | def delete_vminstance(self, vm_id, created_items=None): |
| 706 | |
| 707 | self.logger.debug('Deletting VM instance {} - {}'.format(self.resource_group, vm_id)) |
| 708 | self._reload_connection() |
| 709 | |
| 710 | try: |
| 711 | |
| 712 | resName = self._get_resource_name_from_resource_id(vm_id) |
| 713 | vm = self.conn_compute.virtual_machines.get(self.resource_group, resName) |
| 714 | |
| 715 | # Shuts down the virtual machine and releases the compute resources |
| 716 | #vm_stop = self.conn_compute.virtual_machines.power_off(self.resource_group, resName) |
| 717 | #vm_stop.wait() |
| 718 | |
| 719 | vm_delete = self.conn_compute.virtual_machines.delete(self.resource_group, resName) |
| 720 | vm_delete.wait() |
| 721 | |
| 722 | # Delete OS Disk |
| 723 | os_disk_name = vm.storage_profile.os_disk.name |
| 724 | self.logger.debug('Delete OS DISK - ' + os_disk_name) |
| 725 | self.conn_compute.disks.delete(self.resource_group, os_disk_name) |
| 726 | |
| 727 | # After deletting VM, it is necessary delete NIC, because if is not deleted delete_network |
| 728 | # does not work because Azure says that is in use the subnet |
| 729 | network_interfaces = vm.network_profile.network_interfaces |
| 730 | |
| 731 | for network_interface in network_interfaces: |
| 732 | |
| 733 | #self.logger.debug('nic - {}'.format(network_interface)) |
| 734 | |
| 735 | nic_name = self._get_resource_name_from_resource_id(network_interface.id) |
| 736 | |
| 737 | #self.logger.debug('nic_name - {}'.format(nic_name)) |
| 738 | |
| 739 | nic_data = self.conn_vnet.network_interfaces.get( |
| 740 | self.resource_group, |
| 741 | nic_name) |
| 742 | |
| 743 | exist_public_ip = nic_data.ip_configurations[0].public_ip_address |
| 744 | if exist_public_ip: |
| 745 | public_ip_id = nic_data.ip_configurations[0].public_ip_address.id |
| 746 | self.logger.debug('Public ip id - ' + public_ip_id) |
| 747 | |
| 748 | self.logger.debug('Delete NIC - ' + nic_name) |
| 749 | nic_delete = self.conn_vnet.network_interfaces.delete(self.resource_group, nic_name) |
| 750 | nic_delete.wait() |
| 751 | |
| 752 | # Delete public_ip |
| 753 | public_ip_name = self._get_resource_name_from_resource_id(public_ip_id) |
| 754 | |
| 755 | self.logger.debug('Delete PUBLIC IP - ' + public_ip_name) |
| 756 | public_ip = self.conn_vnet.public_ip_addresses.delete(self.resource_group, public_ip_name) |
| 757 | except CloudError as e: |
| 758 | if e.error.error == "ResourceNotFound": |
| 759 | raise vimconn.vimconnNotFoundException("No vminstance found '{}'".format(vm_id)) |
| 760 | else: |
| 761 | raise |
| 762 | except Exception as e: |
| 763 | self.format_vimconn_exception(e) |
| 764 | |
| 765 | def action_vminstance(self, vm_id, action_dict, created_items={}): |
| 766 | """Send and action over a VM instance from VIM |
| 767 | Returns the vm_id if the action was successfully sent to the VIM""" |
| 768 | |
| 769 | self.logger.debug("Action over VM '%s': %s", vm_id, str(action_dict)) |
| 770 | try: |
| 771 | self._reload_connection() |
| 772 | resName = self._get_resource_name_from_resource_id(vm_id) |
| 773 | if "start" in action_dict: |
| 774 | self.conn_compute.virtual_machines.start(self.resource_group,resName) |
| 775 | elif "stop" in action_dict or "shutdown" in action_dict or "shutoff" in action_dict: |
| 776 | self.conn_compute.virtual_machines.power_off(self.resource_group,resName) |
| 777 | elif "terminate" in action_dict: |
| 778 | self.conn_compute.virtual_machines.delete(self.resource_group,resName) |
| 779 | elif "reboot" in action_dict: |
| 780 | self.conn_compute.virtual_machines.restart(self.resource_group,resName) |
| 781 | return None |
| 782 | except CloudError as e: |
| 783 | if e.error.error == "ResourceNotFound": |
| 784 | raise vimconn.vimconnNotFoundException("No vm found '{}'".format(vm_id)) |
| 785 | else: |
| 786 | raise |
| 787 | except Exception as e: |
| 788 | self.format_vimconn_exception(e) |
| 789 | |
| 790 | def delete_flavor(self, flavor_id): |
| 791 | |
| 792 | raise vimconn.vimconnAuthException("It is not possible to delete a FLAVOR in AZURE") |
| 793 | |
| 794 | def delete_tenant(self,tenant_id,): |
| 795 | |
| 796 | raise vimconn.vimconnAuthException("It is not possible to delete a TENANT in AZURE") |
| 797 | |
| 798 | def delete_image(self, image_id): |
| 799 | |
| 800 | raise vimconn.vimconnAuthException("It is not possible to delete a IMAGE in AZURE") |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 801 | |
| 802 | def get_vminstance(self, vm_id): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 803 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 804 | self._reload_connection() |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 805 | try: |
| 806 | resName = self._get_resource_name_from_resource_id(vm_id) |
| 807 | vm=self.conn_compute.virtual_machines.get(self.resource_group, resName) |
| 808 | except CloudError as e: |
| 809 | if e.error.error == "ResourceNotFound": |
| 810 | raise vimconn.vimconnNotFoundException("No vminstance found '{}'".format(vm_id)) |
| 811 | else: |
| 812 | raise |
| 813 | except Exception as e: |
| 814 | self.format_vimconn_exception(e) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 815 | |
| 816 | return vm |
| 817 | |
| 818 | def get_flavor(self, flavor_id): |
| 819 | self._reload_connection() |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 820 | |
| 821 | flavor_data = self._get_flavor_id_from_flavor_name(flavor_id) |
| 822 | if flavor_data: |
| 823 | flavor = { |
| 824 | 'id': flavor_id, |
| 825 | 'name': flavor_id, |
| 826 | 'ram': flavor_data['memoryInMB'], |
| 827 | 'vcpus': flavor_data['numberOfCores'], |
| 828 | 'disk': flavor_data['resourceDiskSizeInMB'] |
| 829 | } |
| 830 | return flavor |
| 831 | else: |
| 832 | raise vimconn.vimconnNotFoundException("flavor '{}' not found".format(flavor_id)) |
| 833 | |
| 834 | |
| 835 | def get_tenant_list(self, filter_dict={}): |
| 836 | |
| 837 | tenants_azure=[{'name': self.tenant, 'id': self.tenant}] |
| 838 | tenant_list=[] |
| 839 | |
| 840 | for tenant_azure in tenants_azure: |
| 841 | if filter_dict: |
| 842 | if filter_dict.get("id") and str(tenant_azure.get("id")) != filter_dict["id"]: |
| 843 | continue |
| 844 | if filter_dict.get("name") and str(tenant_azure.get("name")) != filter_dict["name"]: |
| 845 | continue |
| 846 | |
| 847 | tenant_list.append(tenant_azure) |
| 848 | |
| 849 | return tenant_list |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 850 | |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 851 | def refresh_nets_status(self, net_list): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 852 | |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 853 | out_nets = {} |
| 854 | self._reload_connection() |
| 855 | for net_id in net_list: |
| 856 | try: |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 857 | netName = self._get_net_name_from_resource_id(net_id) |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 858 | resName = self._get_resource_name_from_resource_id(net_id) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 859 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 860 | net = self.conn_vnet.subnets.get(self.resource_group, netName, resName) |
| 861 | |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 862 | out_nets[net_id] ={ |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 863 | "status": self.provision_state2osm[net.provisioning_state], |
| 864 | "vim_info": str(net) |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 865 | } |
| 866 | except CloudError as e: |
| 867 | if e.error.error == "ResourceNotFound": |
| 868 | out_nets[net_id] = { |
| 869 | "status": "DELETED", |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 870 | "error_msg": str(e) |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 871 | } |
| 872 | else: |
| 873 | raise |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 874 | except vimconn.vimconnNotFoundException as e: |
| 875 | out_nets[net_id] = { |
| 876 | "status": "DELETED", |
| 877 | "error_msg": str(e) |
| 878 | } |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 879 | except Exception as e: |
| 880 | # TODO distinguish when it is deleted |
| 881 | out_nets[net_id] = { |
| 882 | "status": "VIM_ERROR", |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 883 | "error_msg": str(e) |
| 884 | } |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 885 | return out_nets |
| 886 | |
| 887 | def refresh_vms_status(self, vm_list): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 888 | |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 889 | out_vms = {} |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 890 | out_vms_dict = {} |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 891 | self._reload_connection() |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 892 | |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 893 | for vm_id in vm_list: |
| 894 | try: |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 895 | |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 896 | resName = self._get_resource_name_from_resource_id(vm_id) |
| 897 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 898 | vm = self.conn_compute.virtual_machines.get(self.resource_group, resName) |
| 899 | out_vms_dict['status'] = self.provision_state2osm[vm.provisioning_state] |
| 900 | out_vms_dict['interfaces'] = [] |
| 901 | interface_dict = {} |
| 902 | |
| 903 | network_interfaces = vm.network_profile.network_interfaces |
| 904 | |
| 905 | for network_interface in network_interfaces: |
| 906 | |
| 907 | nic_name = self._get_resource_name_from_resource_id(network_interface.id) |
| 908 | interface_dict['vim_interface_id'] = vm_id |
| 909 | |
| 910 | nic_data = self.conn_vnet.network_interfaces.get( |
| 911 | self.resource_group, |
| 912 | nic_name) |
| 913 | |
| 914 | private_ip = nic_data.ip_configurations[0].private_ip_address |
| 915 | |
| 916 | interface_dict['mac_address'] = nic_data.mac_address |
| 917 | interface_dict['ip_address'] = private_ip |
| 918 | out_vms_dict['interfaces'].append(interface_dict) |
| 919 | |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 920 | except Exception as e: |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 921 | out_vms_dict['status'] = "DELETED" |
| 922 | out_vms_dict['error_msg'] = str(e) |
| 923 | vm = None |
| 924 | finally: |
| 925 | if vm: |
| 926 | out_vms_dict['vim_info'] = str(vm) |
| 927 | |
| 928 | out_vms[vm_id] = out_vms_dict |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 929 | |
| 930 | return out_vms |
| seryio | 07ad136 | 2019-05-29 09:16:24 +0200 | [diff] [blame] | 931 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 932 | |
| 933 | if __name__ == "__main__": |
| 934 | |
| 935 | # Making some basic test |
| 936 | vim_id='azure' |
| 937 | vim_name='azure' |
| 938 | needed_test_params = { |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 939 | "client_id": "AZURE_CLIENT_ID", |
| 940 | "secret": "AZURE_SECRET", |
| 941 | "tenant": "AZURE_TENANT", |
| 942 | "resource_group": "AZURE_RESOURCE_GROUP", |
| 943 | "subscription_id": "AZURE_SUBSCRIPTION_ID", |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 944 | "vnet_name": "AZURE_VNET_NAME", |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 945 | } |
| 946 | test_params = {} |
| 947 | |
| 948 | for param, env_var in needed_test_params.items(): |
| 949 | value = getenv(env_var) |
| 950 | if not value: |
| 951 | raise Exception("Provide a valid value for env '{}'".format(env_var)) |
| 952 | test_params[param] = value |
| 953 | |
| 954 | config = { |
| 955 | 'region_name': getenv("AZURE_REGION_NAME", 'westeurope'), |
| 956 | 'resource_group': getenv("AZURE_RESOURCE_GROUP"), |
| 957 | 'subscription_id': getenv("AZURE_SUBSCRIPTION_ID"), |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 958 | 'pub_key': getenv("AZURE_PUB_KEY", None), |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 959 | 'vnet_name': getenv("AZURE_VNET_NAME", 'myNetwork'), |
| 960 | } |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 961 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 962 | virtualMachine = { |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 963 | 'name': 'sergio', |
| 964 | 'description': 'new VM', |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 965 | 'status': 'running', |
| 966 | 'image': { |
| 967 | 'publisher': 'Canonical', |
| 968 | 'offer': 'UbuntuServer', |
| 969 | 'sku': '16.04.0-LTS', |
| 970 | 'version': 'latest' |
| 971 | }, |
| 972 | 'hardware_profile': { |
| 973 | 'vm_size': 'Standard_DS1_v2' |
| 974 | }, |
| 975 | 'networks': [ |
| 976 | 'sergio' |
| 977 | ] |
| 978 | } |
| 979 | |
| 980 | vnet_config = { |
| 981 | 'subnet_address': '10.1.2.0/24', |
| 982 | #'subnet_name': 'subnet-oam' |
| 983 | } |
| 984 | ########################### |
| 985 | |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 986 | azure = vimconnector(vim_id, vim_name, tenant_id=test_params["tenant"], tenant_name=None, url=None, url_admin=None, |
| 987 | user=test_params["client_id"], passwd=test_params["secret"], log_level=None, config=config) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 988 | |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 989 | # azure.get_flavor_id_from_data("here") |
| 990 | # subnets=azure.get_network_list() |
| 991 | # azure.new_vminstance(virtualMachine['name'], virtualMachine['description'], virtualMachine['status'], |
| 992 | # virtualMachine['image'], virtualMachine['hardware_profile']['vm_size'], subnets) |
| 993 | |
| tierno | 2462041 | 2019-06-03 14:05:08 +0000 | [diff] [blame] | 994 | azure.new_network("mynet", None) |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 995 | net_id = "/subscriptions/82f80cc1-876b-4591-9911-1fb5788384fd/resourceGroups/osmRG/providers/Microsoft."\ |
| 996 | "Network/virtualNetworks/test" |
| 997 | net_id_not_found = "/subscriptions/82f80cc1-876b-4591-9911-1fb5788384fd/resourceGroups/osmRG/providers/"\ |
| 998 | "Microsoft.Network/virtualNetworks/testALF" |
| 999 | azure.refresh_nets_status([net_id, net_id_not_found]) |