| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 1 | # -*- coding: utf-8 -*- |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 2 | ## |
| 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 |
| 6 | # |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | # |
| 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 |
| 13 | # under the License. |
| 14 | ## |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 15 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 16 | import base64 |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 17 | import vimconn |
| 18 | import logging |
| tierno | 2462041 | 2019-06-03 14:05:08 +0000 | [diff] [blame] | 19 | import netaddr |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 20 | import re |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 21 | |
| 22 | from os import getenv |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 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 |
| lloretgalleg | bfc28b0 | 2019-11-21 09:31:16 +0100 | [diff] [blame] | 27 | from azure.mgmt.compute.models import DiskCreateOption |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 28 | from msrestazure.azure_exceptions import CloudError |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 29 | from msrest.exceptions import AuthenticationError |
| 30 | from requests.exceptions import ConnectionError |
| 31 | |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 32 | __author__ = 'Isabel Lloret, Sergio Gonzalez, Alfonso Tierno' |
| 33 | __date__ = '$18-apr-2019 23:59:59$' |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 34 | |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 35 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 36 | if getenv('OSMRO_PDB_DEBUG'): |
| 37 | import sys |
| 38 | print(sys.path) |
| 39 | import pdb |
| 40 | pdb.set_trace() |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 41 | |
| 42 | |
| 43 | class vimconnector(vimconn.vimconnector): |
| 44 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 45 | # Translate azure provisioning state to OSM provision state |
| 46 | # The first three ones are the transitional status once a user initiated action has been requested |
| 47 | # Once the operation is complete, it will transition into the states Succeeded or Failed |
| 48 | # https://docs.microsoft.com/en-us/azure/virtual-machines/windows/states-lifecycle |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 49 | provision_state2osm = { |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 50 | "Creating": "BUILD", |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 51 | "Updating": "BUILD", |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 52 | "Deleting": "INACTIVE", |
| 53 | "Succeeded": "ACTIVE", |
| 54 | "Failed": "ERROR" |
| 55 | } |
| 56 | |
| 57 | # Translate azure power state to OSM provision state |
| 58 | power_state2osm = { |
| 59 | "starting": "INACTIVE", |
| 60 | "running": "ACTIVE", |
| 61 | "stopping": "INACTIVE", |
| 62 | "stopped": "INACTIVE", |
| 63 | "unknown": "OTHER", |
| 64 | "deallocated": "BUILD", |
| 65 | "deallocating": "BUILD" |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 66 | } |
| 67 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 68 | def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level=None, |
| 69 | config={}, persistent_info={}): |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 70 | """ |
| 71 | Constructor of VIM. Raise an exception is some needed parameter is missing, but it must not do any connectivity |
| 72 | checking against the VIM |
| 73 | Using common constructor parameters. |
| 74 | In this case: config must include the following parameters: |
| 75 | subscription_id: assigned azure subscription identifier |
| 76 | region_name: current region for azure network |
| 77 | resource_group: used for all azure created resources |
| 78 | vnet_name: base vnet for azure, created networks will be subnets from this base network |
| 79 | config may also include the following parameter: |
| 80 | flavors_pattern: pattern that will be used to select a range of vm sizes, for example |
| 81 | "^((?!Standard_B).)*$" will filter out Standard_B range that is cheap but is very overused |
| 82 | "^Standard_B" will select a serie B maybe for test environment |
| 83 | """ |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 84 | |
| 85 | vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level, |
| 86 | config, persistent_info) |
| 87 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 88 | # Variable that indicates if client must be reloaded or initialized |
| 89 | self.reload_client = True |
| 90 | |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 91 | self.vnet_address_space = None |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 92 | # LOGGER |
| 93 | self.logger = logging.getLogger('openmano.vim.azure') |
| 94 | if log_level: |
| 95 | logging.basicConfig() |
| 96 | self.logger.setLevel(getattr(logging, log_level)) |
| 97 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 98 | self.tenant = (tenant_id or tenant_name) |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 99 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 100 | # Store config to create azure subscription later |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 101 | self._config = { |
| 102 | "user": user, |
| 103 | "passwd": passwd, |
| 104 | "tenant": tenant_id or tenant_name |
| 105 | } |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 106 | |
| 107 | # SUBSCRIPTION |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 108 | if 'subscription_id' in config: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 109 | self._config["subscription_id"] = config.get('subscription_id') |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 110 | # self.logger.debug('Setting subscription to: %s', self.config["subscription_id"]) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 111 | else: |
| 112 | raise vimconn.vimconnException('Subscription not specified') |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 113 | |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 114 | # REGION |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 115 | if 'region_name' in config: |
| 116 | self.region = config.get('region_name') |
| 117 | else: |
| 118 | raise vimconn.vimconnException('Azure region_name is not specified at config') |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 119 | |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 120 | # RESOURCE_GROUP |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 121 | if 'resource_group' in config: |
| 122 | self.resource_group = config.get('resource_group') |
| 123 | else: |
| 124 | raise vimconn.vimconnException('Azure resource_group is not specified at config') |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 125 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 126 | # VNET_NAME |
| 127 | if 'vnet_name' in config: |
| 128 | self.vnet_name = config["vnet_name"] |
| 129 | |
| 130 | # public ssh key |
| 131 | self.pub_key = config.get('pub_key') |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 132 | |
| 133 | # flavor pattern regex |
| 134 | if 'flavors_pattern' in config: |
| 135 | self._config['flavors_pattern'] = config['flavors_pattern'] |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 136 | |
| 137 | def _reload_connection(self): |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 138 | """ |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 139 | Called before any operation, checks python azure clients |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 140 | """ |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 141 | if self.reload_client: |
| 142 | self.logger.debug('reloading azure client') |
| 143 | try: |
| 144 | self.credentials = ServicePrincipalCredentials( |
| 145 | client_id=self._config["user"], |
| 146 | secret=self._config["passwd"], |
| 147 | tenant=self._config["tenant"] |
| 148 | ) |
| 149 | self.conn = ResourceManagementClient(self.credentials, self._config["subscription_id"]) |
| 150 | self.conn_compute = ComputeManagementClient(self.credentials, self._config["subscription_id"]) |
| 151 | self.conn_vnet = NetworkManagementClient(self.credentials, self._config["subscription_id"]) |
| 152 | self._check_or_create_resource_group() |
| 153 | self._check_or_create_vnet() |
| 154 | |
| 155 | # Set to client created |
| 156 | self.reload_client = False |
| 157 | except Exception as e: |
| 158 | self._format_vimconn_exception(e) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 159 | |
| 160 | def _get_resource_name_from_resource_id(self, resource_id): |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 161 | """ |
| 162 | Obtains resource_name from the azure complete identifier: resource_name will always be last item |
| 163 | """ |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 164 | try: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 165 | resource = str(resource_id.split('/')[-1]) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 166 | return resource |
| 167 | except Exception as e: |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 168 | raise vimconn.vimconnException("Unable to get resource name from resource_id '{}' Error: '{}'". |
| 169 | format(resource_id, e)) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 170 | |
| 171 | def _get_location_from_resource_group(self, resource_group_name): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 172 | try: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 173 | location = self.conn.resource_groups.get(resource_group_name).location |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 174 | return location |
| 175 | except Exception as e: |
| 176 | raise vimconn.vimconnNotFoundException("Location '{}' not found".format(resource_group_name)) |
| 177 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 178 | def _get_resource_group_name_from_resource_id(self, resource_id): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 179 | |
| 180 | try: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 181 | rg = str(resource_id.split('/')[4]) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 182 | return rg |
| 183 | except Exception as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 184 | raise vimconn.vimconnException("Unable to get resource group from invalid resource_id format '{}'". |
| 185 | format(resource_id)) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 186 | |
| 187 | def _get_net_name_from_resource_id(self, resource_id): |
| 188 | |
| 189 | try: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 190 | net_name = str(resource_id.split('/')[8]) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 191 | return net_name |
| 192 | except Exception as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 193 | raise vimconn.vimconnException("Unable to get azure net_name from invalid resource_id format '{}'". |
| 194 | format(resource_id)) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 195 | |
| 196 | def _check_subnets_for_vm(self, net_list): |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 197 | # All subnets must belong to the same resource group and vnet |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 198 | rg_vnet = set(self._get_resource_group_name_from_resource_id(net['net_id']) + |
| 199 | self._get_net_name_from_resource_id(net['net_id']) for net in net_list) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 200 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 201 | if len(rg_vnet) != 1: |
| 202 | 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] | 203 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 204 | def _format_vimconn_exception(self, e): |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 205 | """ |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 206 | Transforms a generic or azure exception to a vimcommException |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 207 | """ |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 208 | if isinstance(e, vimconn.vimconnException): |
| 209 | raise |
| 210 | elif isinstance(e, AuthenticationError): |
| 211 | raise vimconn.vimconnAuthException(type(e).__name__ + ': ' + str(e)) |
| 212 | elif isinstance(e, ConnectionError): |
| 213 | raise vimconn.vimconnConnectionException(type(e).__name__ + ': ' + str(e)) |
| 214 | else: |
| 215 | # In case of generic error recreate client |
| 216 | self.reload_client = True |
| 217 | raise vimconn.vimconnException(type(e).__name__ + ': ' + str(e)) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 218 | |
| 219 | def _check_or_create_resource_group(self): |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 220 | """ |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 221 | Creates the base resource group if it does not exist |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 222 | """ |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 223 | try: |
| 224 | rg_exists = self.conn.resource_groups.check_existence(self.resource_group) |
| 225 | if not rg_exists: |
| 226 | self.logger.debug("create base rgroup: %s", self.resource_group) |
| 227 | self.conn.resource_groups.create_or_update(self.resource_group, {'location': self.region}) |
| 228 | except Exception as e: |
| 229 | self._format_vimconn_exception(e) |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 230 | |
| 231 | def _check_or_create_vnet(self): |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 232 | """ |
| 233 | Try to get existent base vnet, in case it does not exist it creates it |
| 234 | """ |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 235 | try: |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 236 | vnet = self.conn_vnet.virtual_networks.get(self.resource_group, self.vnet_name) |
| 237 | self.vnet_address_space = vnet.address_space.address_prefixes[0] |
| tierno | 2462041 | 2019-06-03 14:05:08 +0000 | [diff] [blame] | 238 | self.vnet_id = vnet.id |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 239 | return |
| 240 | except CloudError as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 241 | if e.error.error and "notfound" in e.error.error.lower(): |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 242 | pass |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 243 | # continue and create it |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 244 | else: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 245 | self._format_vimconn_exception(e) |
| 246 | |
| 247 | # if it does not exist, create it |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 248 | try: |
| 249 | vnet_params = { |
| 250 | 'location': self.region, |
| 251 | 'address_space': { |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 252 | 'address_prefixes': ["10.0.0.0/8"] |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 253 | }, |
| 254 | } |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 255 | self.vnet_address_space = "10.0.0.0/8" |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 256 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 257 | self.logger.debug("create base vnet: %s", self.vnet_name) |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 258 | 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] | 259 | vnet = self.conn_vnet.virtual_networks.get(self.resource_group, self.vnet_name) |
| 260 | self.vnet_id = vnet.id |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 261 | except Exception as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 262 | self._format_vimconn_exception(e) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 263 | |
| kbsub | a85c54d | 2019-10-17 16:30:32 +0000 | [diff] [blame^] | 264 | def new_network(self, net_name, net_type, ip_profile=None, shared=False, provider_network_profile=None): |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 265 | """ |
| 266 | Adds a tenant network to VIM |
| 267 | :param net_name: name of the network |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 268 | :param net_type: not used for azure networks |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 269 | :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] | 270 | 'ip-version': can be one of ['IPv4','IPv6'] |
| 271 | 'subnet-address': ip_prefix_schema, that is X.X.X.X/Y |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 272 | 'gateway-address': (Optional) ip_schema, that is X.X.X.X, not implemented for azure connector |
| 273 | 'dns-address': (Optional) ip_schema, not implemented for azure connector |
| 274 | 'dhcp': (Optional) dict containing, not implemented for azure connector |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 275 | 'enabled': {'type': 'boolean'}, |
| 276 | 'start-address': ip_schema, first IP to grant |
| 277 | 'count': number of IPs to grant. |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 278 | :param shared: Not allowed for Azure Connector |
| kbsub | a85c54d | 2019-10-17 16:30:32 +0000 | [diff] [blame^] | 279 | :param provider_network_profile: (optional) contains {segmentation-id: vlan, provider-network: vim_netowrk} |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 280 | :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] | 281 | created_items can be None or a dictionary where this method can include key-values that will be passed to |
| 282 | the method delete_network. Can be used to store created segments, created l2gw connections, etc. |
| 283 | Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same |
| 284 | as not present. |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 285 | """ |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 286 | return self._new_subnet(net_name, ip_profile) |
| 287 | |
| 288 | def _new_subnet(self, net_name, ip_profile): |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 289 | """ |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 290 | Adds a tenant network to VIM. It creates a new subnet at existing base vnet |
| 291 | :param net_name: subnet name |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 292 | :param ip_profile: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 293 | subnet-address: if it is not provided a subnet/24 in the default vnet is created, |
| 294 | otherwise it creates a subnet in the indicated address |
| 295 | :return: a tuple with the network identifier and created_items, or raises an exception on error |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 296 | """ |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 297 | self.logger.debug('create subnet name %s, ip_profile %s', net_name, ip_profile) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 298 | self._reload_connection() |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 299 | |
| 300 | if ip_profile is None: |
| tierno | 2462041 | 2019-06-03 14:05:08 +0000 | [diff] [blame] | 301 | # get a non used vnet ip range /24 and allocate automatically inside the range self.vnet_address_space |
| 302 | used_subnets = self.get_network_list() |
| 303 | for ip_range in netaddr.IPNetwork(self.vnet_address_space).subnet(24): |
| 304 | for used_subnet in used_subnets: |
| 305 | subnet_range = netaddr.IPNetwork(used_subnet["cidr_block"]) |
| 306 | if subnet_range in ip_range or ip_range in subnet_range: |
| 307 | # this range overlaps with an existing subnet ip range. Breaks and look for another |
| 308 | break |
| 309 | else: |
| 310 | ip_profile = {"subnet_address": str(ip_range)} |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 311 | self.logger.debug('dinamically obtained ip_profile: %s', ip_range) |
| tierno | 2462041 | 2019-06-03 14:05:08 +0000 | [diff] [blame] | 312 | break |
| 313 | else: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 314 | raise vimconn.vimconnException("Cannot find a non-used subnet range in {}". |
| 315 | format(self.vnet_address_space)) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 316 | else: |
| 317 | ip_profile = {"subnet_address": ip_profile['subnet_address']} |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 318 | |
| 319 | try: |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 320 | # subnet_name = "{}-{}".format(net_name[:24], uuid4()) |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 321 | subnet_params = { |
| tierno | 2462041 | 2019-06-03 14:05:08 +0000 | [diff] [blame] | 322 | 'address_prefix': ip_profile['subnet_address'] |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 323 | } |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 324 | # Assign a not duplicated net name |
| 325 | subnet_name = self._get_unused_subnet_name(net_name) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 326 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 327 | self.logger.debug('creating subnet_name: {}'.format(subnet_name)) |
| 328 | async_creation = self.conn_vnet.subnets.create_or_update(self.resource_group, self.vnet_name, |
| 329 | subnet_name, subnet_params) |
| 330 | async_creation.wait() |
| 331 | self.logger.debug('created subnet_name: {}'.format(subnet_name)) |
| 332 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 333 | return "{}/subnets/{}".format(self.vnet_id, subnet_name), None |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 334 | except Exception as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 335 | self._format_vimconn_exception(e) |
| 336 | |
| 337 | def _get_unused_subnet_name(self, subnet_name): |
| 338 | """ |
| 339 | Adds a prefix to the subnet_name with a number in case the indicated name is repeated |
| 340 | Checks subnets with the indicated name (without suffix) and adds a suffix with a number |
| 341 | """ |
| 342 | all_subnets = self.conn_vnet.subnets.list(self.resource_group, self.vnet_name) |
| 343 | # Filter to subnets starting with the indicated name |
| 344 | subnets = list(filter(lambda subnet: (subnet.name.startswith(subnet_name)), all_subnets)) |
| 345 | net_names = [str(subnet.name) for subnet in subnets] |
| 346 | |
| 347 | # get the name with the first not used suffix |
| 348 | name_suffix = 0 |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 349 | # name = subnet_name + "-" + str(name_suffix) |
| 350 | name = subnet_name # first subnet created will have no prefix |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 351 | while name in net_names: |
| 352 | name_suffix += 1 |
| 353 | name = subnet_name + "-" + str(name_suffix) |
| 354 | return name |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 355 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 356 | def _create_nic(self, net, nic_name, static_ip=None): |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 357 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 358 | self.logger.debug('create nic name %s, net_name %s', nic_name, net) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 359 | self._reload_connection() |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 360 | |
| 361 | subnet_id = net['net_id'] |
| 362 | location = self._get_location_from_resource_group(self.resource_group) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 363 | try: |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 364 | net_ifz = {'location': location} |
| lloretgalleg | bfc28b0 | 2019-11-21 09:31:16 +0100 | [diff] [blame] | 365 | net_ip_config = {'name': nic_name + '-ipconfiguration', 'subnet': {'id': subnet_id}} |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 366 | if static_ip: |
| lloretgalleg | bfc28b0 | 2019-11-21 09:31:16 +0100 | [diff] [blame] | 367 | net_ip_config['privateIPAddress'] = static_ip |
| 368 | net_ip_config['privateIPAllocationMethod'] = 'Static' |
| 369 | net_ifz['ip_configurations'] = [net_ip_config] |
| 370 | mac_address = net.get('mac_address') |
| 371 | if mac_address: |
| 372 | net_ifz['mac_address'] = mac_address |
| 373 | |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 374 | async_nic_creation = self.conn_vnet.network_interfaces.create_or_update(self.resource_group, nic_name, |
| 375 | net_ifz) |
| lloretgalleg | bfc28b0 | 2019-11-21 09:31:16 +0100 | [diff] [blame] | 376 | async_nic_creation.wait() |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 377 | self.logger.debug('created nic name %s', nic_name) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 378 | |
| 379 | public_ip = net.get('floating_ip') |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 380 | if public_ip: |
| 381 | public_ip_address_params = { |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 382 | 'location': location, |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 383 | 'public_ip_allocation_method': 'Dynamic' |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 384 | } |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 385 | public_ip_name = nic_name + '-public-ip' |
| 386 | public_ip = self.conn_vnet.public_ip_addresses.create_or_update( |
| 387 | self.resource_group, |
| 388 | public_ip_name, |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 389 | public_ip_address_params |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 390 | ) |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 391 | self.logger.debug('created public IP: {}'.format(public_ip.result())) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 392 | |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 393 | # Associate NIC to Public IP |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 394 | nic_data = self.conn_vnet.network_interfaces.get( |
| 395 | self.resource_group, |
| 396 | nic_name) |
| 397 | |
| 398 | nic_data.ip_configurations[0].public_ip_address = public_ip.result() |
| 399 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 400 | self.conn_vnet.network_interfaces.create_or_update( |
| 401 | self.resource_group, |
| 402 | nic_name, |
| 403 | nic_data) |
| 404 | |
| 405 | except Exception as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 406 | self._format_vimconn_exception(e) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 407 | |
| 408 | return async_nic_creation.result() |
| 409 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 410 | def new_flavor(self, flavor_data): |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 411 | """ |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 412 | It is not allowed to create new flavors in Azure, must always use an existing one |
| 413 | """ |
| 414 | raise vimconn.vimconnAuthException("It is not possible to create new flavors in AZURE") |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 415 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 416 | def new_tenant(self, tenant_name, tenant_description): |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 417 | """ |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 418 | It is not allowed to create new tenants in azure |
| 419 | """ |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 420 | raise vimconn.vimconnAuthException("It is not possible to create a TENANT in AZURE") |
| 421 | |
| 422 | def new_image(self, image_dict): |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 423 | """ |
| 424 | It is not allowed to create new images in Azure, must always use an existing one |
| 425 | """ |
| 426 | raise vimconn.vimconnAuthException("It is not possible to create new images in AZURE") |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 427 | |
| 428 | def get_image_id_from_path(self, path): |
| 429 | """Get the image id from image path in the VIM database. |
| 430 | Returns the image_id or raises a vimconnNotFoundException |
| 431 | """ |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 432 | raise vimconn.vimconnAuthException("It is not possible to obtain image from path in AZURE") |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 433 | |
| 434 | def get_image_list(self, filter_dict={}): |
| 435 | """Obtain tenant images from VIM |
| 436 | Filter_dict can be: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 437 | name: image name with the format: publisher:offer:sku:version |
| 438 | If some part of the name is provide ex: publisher:offer it will search all availables skus and version |
| 439 | for the provided publisher and offer |
| 440 | id: image uuid, currently not supported for azure |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 441 | Returns the image list of dictionaries: |
| 442 | [{<the fields at Filter_dict plus some VIM specific>}, ...] |
| 443 | List can be empty |
| 444 | """ |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 445 | |
| 446 | self.logger.debug("get_image_list filter {}".format(filter_dict)) |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 447 | |
| 448 | self._reload_connection() |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 449 | try: |
| 450 | image_list = [] |
| 451 | if filter_dict.get("name"): |
| 452 | # name will have the format 'publisher:offer:sku:version' |
| 453 | # publisher is required, offer sku and version will be searched if not provided |
| 454 | params = filter_dict["name"].split(":") |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 455 | publisher = params[0] |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 456 | if publisher: |
| 457 | # obtain offer list |
| 458 | offer_list = self._get_offer_list(params, publisher) |
| 459 | for offer in offer_list: |
| 460 | # obtain skus |
| 461 | sku_list = self._get_sku_list(params, publisher, offer) |
| 462 | for sku in sku_list: |
| 463 | # if version is defined get directly version, else list images |
| 464 | if len(params) == 4 and params[3]: |
| 465 | version = params[3] |
| 466 | image_list = self._get_version_image_list(publisher, offer, sku, version) |
| 467 | else: |
| 468 | image_list = self._get_sku_image_list(publisher, offer, sku) |
| 469 | else: |
| 470 | raise vimconn.vimconnAuthException( |
| 471 | "List images in Azure must include name param with at least publisher") |
| 472 | else: |
| 473 | raise vimconn.vimconnAuthException("List images in Azure must include name param with at" |
| 474 | " least publisher") |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 475 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 476 | return image_list |
| 477 | except Exception as e: |
| 478 | self._format_vimconn_exception(e) |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 479 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 480 | def _get_offer_list(self, params, publisher): |
| 481 | """ |
| 482 | Helper method to obtain offer list for defined publisher |
| 483 | """ |
| 484 | if len(params) >= 2 and params[1]: |
| 485 | return [params[1]] |
| 486 | else: |
| 487 | try: |
| 488 | # get list of offers from azure |
| 489 | result_offers = self.conn_compute.virtual_machine_images.list_offers(self.region, publisher) |
| 490 | return [offer.name for offer in result_offers] |
| 491 | except CloudError as e: |
| 492 | # azure raises CloudError when not found |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 493 | self.logger.info("error listing offers for publisher {}, Error: {}".format(publisher, e)) |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 494 | return [] |
| 495 | |
| 496 | def _get_sku_list(self, params, publisher, offer): |
| 497 | """ |
| 498 | Helper method to obtain sku list for defined publisher and offer |
| 499 | """ |
| 500 | if len(params) >= 3 and params[2]: |
| 501 | return [params[2]] |
| 502 | else: |
| 503 | try: |
| 504 | # get list of skus from azure |
| 505 | result_skus = self.conn_compute.virtual_machine_images.list_skus(self.region, publisher, offer) |
| 506 | return [sku.name for sku in result_skus] |
| 507 | except CloudError as e: |
| 508 | # azure raises CloudError when not found |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 509 | self.logger.info("error listing skus for publisher {}, offer {}, Error: {}".format(publisher, offer, e)) |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 510 | return [] |
| 511 | |
| 512 | def _get_sku_image_list(self, publisher, offer, sku): |
| 513 | """ |
| 514 | Helper method to obtain image list for publisher, offer and sku |
| 515 | """ |
| 516 | image_list = [] |
| 517 | try: |
| 518 | result_images = self.conn_compute.virtual_machine_images.list(self.region, publisher, offer, sku) |
| 519 | for result_image in result_images: |
| 520 | image_list.append({ |
| 521 | 'id': str(result_image.id), |
| 522 | 'name': ":".join([publisher, offer, sku, result_image.name]) |
| 523 | }) |
| 524 | except CloudError as e: |
| 525 | self.logger.info( |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 526 | "error listing skus for publisher {}, offer {}, Error: {}".format(publisher, offer, e)) |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 527 | image_list = [] |
| 528 | return image_list |
| 529 | |
| 530 | def _get_version_image_list(self, publisher, offer, sku, version): |
| 531 | image_list = [] |
| 532 | try: |
| 533 | result_image = self.conn_compute.virtual_machine_images.get(self.region, publisher, offer, sku, version) |
| 534 | if result_image: |
| 535 | image_list.append({ |
| 536 | 'id': str(result_image.id), |
| 537 | 'name': ":".join([publisher, offer, sku, version]) |
| 538 | }) |
| 539 | except CloudError as e: |
| 540 | # azure gives CloudError when not found |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 541 | self.logger.info("error listing images for publisher {}, offer {}, sku {}, version {} Error: {}". |
| 542 | format(publisher, offer, sku, version, e)) |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 543 | image_list = [] |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 544 | return image_list |
| 545 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 546 | def get_network_list(self, filter_dict={}): |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 547 | """Obtain tenant networks of VIM |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 548 | Filter_dict can be: |
| 549 | name: network name |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 550 | id: network id |
| 551 | shared: boolean, not implemented in Azure |
| 552 | tenant_id: tenant, not used in Azure, all networks same tenants |
| 553 | admin_state_up: boolean, not implemented in Azure |
| 554 | status: 'ACTIVE', not implemented in Azure # |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 555 | Returns the network list of dictionaries |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 556 | """ |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 557 | # self.logger.debug('getting network list for vim, filter %s', filter_dict) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 558 | try: |
| 559 | self._reload_connection() |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 560 | |
| 561 | vnet = self.conn_vnet.virtual_networks.get(self.resource_group, self.vnet_name) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 562 | subnet_list = [] |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 563 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 564 | for subnet in vnet.subnets: |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 565 | if filter_dict: |
| 566 | if filter_dict.get("id") and str(subnet.id) != filter_dict["id"]: |
| 567 | continue |
| 568 | if filter_dict.get("name") and \ |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 569 | str(subnet.name) != filter_dict["name"]: |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 570 | continue |
| 571 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 572 | name = self._get_resource_name_from_resource_id(subnet.id) |
| 573 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 574 | subnet_list.append({ |
| 575 | 'id': str(subnet.id), |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 576 | 'name': name, |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 577 | 'status': self.provision_state2osm[subnet.provisioning_state], |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 578 | 'cidr_block': str(subnet.address_prefix), |
| 579 | 'type': 'bridge', |
| 580 | 'shared': False |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 581 | }) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 582 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 583 | return subnet_list |
| 584 | except Exception as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 585 | self._format_vimconn_exception(e) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 586 | |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 587 | def new_vminstance(self, name, description, start, image_id, flavor_id, net_list, cloud_config=None, |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 588 | disk_list=None, availability_zone_index=None, availability_zone_list=None): |
| 589 | |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 590 | self.logger.debug("new vm instance name: %s, image_id: %s, flavor_id: %s, net_list: %s, cloud_config: %s, " |
| 591 | "disk_list: %s, availability_zone_index: %s, availability_zone_list: %s", |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 592 | name, image_id, flavor_id, net_list, cloud_config, disk_list, |
| 593 | availability_zone_index, availability_zone_list) |
| 594 | |
| 595 | self._reload_connection() |
| 596 | |
| 597 | # Validate input data is valid |
| 598 | # The virtual machine name must have less or 64 characters and it can not have the following |
| 599 | # characters: (~ ! @ # $ % ^ & * ( ) = + _ [ ] { } \ | ; : ' " , < > / ?.) |
| 600 | vm_name = self._check_vm_name(name) |
| 601 | # Obtain vm unused name |
| 602 | vm_name = self._get_unused_vm_name(vm_name) |
| 603 | |
| 604 | # At least one network must be provided |
| 605 | if not net_list: |
| 606 | raise vimconn.vimconnException("At least one net must be provided to create a new VM") |
| 607 | |
| 608 | # image_id are several fields of the image_id |
| 609 | image_reference = self._get_image_reference(image_id) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 610 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 611 | self._check_subnets_for_vm(net_list) |
| 612 | vm_nics = [] |
| 613 | for idx, net in enumerate(net_list): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 614 | # Fault with subnet_id |
| 615 | # subnet_id=net['subnet_id'] |
| 616 | # subnet_id=net['net_id'] |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 617 | nic_name = vm_name + '-nic-'+str(idx) |
| lloretgalleg | bfc28b0 | 2019-11-21 09:31:16 +0100 | [diff] [blame] | 618 | vm_nic = self._create_nic(net, nic_name, net.get('ip_address')) |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 619 | vm_nics.append({'id': str(vm_nic.id)}) |
| 620 | net['vim_id'] = vm_nic.id |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 621 | |
| 622 | try: |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 623 | |
| 624 | # cloud-init configuration |
| 625 | # cloud config |
| 626 | if cloud_config: |
| 627 | config_drive, userdata = self._create_user_data(cloud_config) |
| 628 | custom_data = base64.b64encode(userdata.encode('utf-8')).decode('latin-1') |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 629 | key_data = None |
| 630 | key_pairs = cloud_config.get("key-pairs") |
| 631 | if key_pairs: |
| 632 | key_data = key_pairs[0] |
| 633 | |
| 634 | if cloud_config.get("users"): |
| 635 | user_name = cloud_config.get("users")[0].get("name", "osm") |
| 636 | else: |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 637 | user_name = "osm" # DEFAULT USER IS OSM |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 638 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 639 | os_profile = { |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 640 | 'computer_name': vm_name, |
| 641 | 'admin_username': user_name, |
| 642 | 'linux_configuration': { |
| 643 | "disable_password_authentication": True, |
| 644 | "ssh": { |
| 645 | "public_keys": [{ |
| 646 | "path": "/home/{}/.ssh/authorized_keys".format(user_name), |
| 647 | "key_data": key_data |
| 648 | }] |
| 649 | } |
| 650 | }, |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 651 | 'custom_data': custom_data |
| 652 | } |
| 653 | else: |
| 654 | os_profile = { |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 655 | 'computer_name': vm_name, |
| 656 | 'admin_username': 'osm', |
| 657 | 'admin_password': 'Osm4u!', |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 658 | } |
| 659 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 660 | vm_parameters = { |
| 661 | 'location': self.region, |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 662 | 'os_profile': os_profile, |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 663 | 'hardware_profile': { |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 664 | 'vm_size': flavor_id |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 665 | }, |
| 666 | 'storage_profile': { |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 667 | 'image_reference': image_reference |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 668 | } |
| 669 | } |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 670 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 671 | # Add data disks if they are provided |
| 672 | if disk_list: |
| 673 | data_disks = [] |
| 674 | for lun_name, disk in enumerate(disk_list): |
| 675 | self.logger.debug("add disk size: %s, image: %s", disk.get("size"), disk.get("image_id")) |
| 676 | if not disk.get("image_id"): |
| 677 | data_disks.append({ |
| 678 | 'lun': lun_name, # You choose the value, depending of what is available for you |
| 679 | 'name': vm_name + "_data_disk-" + str(lun_name), |
| lloretgalleg | bfc28b0 | 2019-11-21 09:31:16 +0100 | [diff] [blame] | 680 | 'create_option': DiskCreateOption.empty, |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 681 | 'disk_size_gb': disk.get("size") |
| 682 | }) |
| 683 | else: |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 684 | # self.logger.debug("currently not able to create data disks from image for azure, ignoring") |
| lloretgalleg | bfc28b0 | 2019-11-21 09:31:16 +0100 | [diff] [blame] | 685 | data_disks.append({ |
| 686 | 'lun': lun_name, # You choose the value, depending of what is available for you |
| 687 | 'name': vm_name + "_data_disk-" + str(lun_name), |
| 688 | 'create_option': 'Attach', |
| 689 | 'disk_size_gb': disk.get("size"), |
| 690 | 'managed_disk': { |
| 691 | 'id': disk.get("image_id") |
| 692 | } |
| 693 | }) |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 694 | |
| 695 | if data_disks: |
| 696 | vm_parameters["storage_profile"]["data_disks"] = data_disks |
| 697 | |
| 698 | # If the machine has several networks one must be marked as primary |
| 699 | # As it is not indicated in the interface the first interface will be marked as primary |
| 700 | if len(vm_nics) > 1: |
| 701 | for idx, vm_nic in enumerate(vm_nics): |
| 702 | if idx == 0: |
| 703 | vm_nics[0]['Primary'] = True |
| 704 | else: |
| 705 | vm_nics[idx]['Primary'] = False |
| 706 | |
| 707 | vm_parameters['network_profile'] = {'network_interfaces': vm_nics} |
| 708 | |
| 709 | self.logger.debug("create vm name: %s", vm_name) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 710 | creation_result = self.conn_compute.virtual_machines.create_or_update( |
| 711 | self.resource_group, |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 712 | vm_name, |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 713 | vm_parameters |
| 714 | ) |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 715 | # creation_result.wait() |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 716 | result = creation_result.result() |
| lloretgalleg | bfc28b0 | 2019-11-21 09:31:16 +0100 | [diff] [blame] | 717 | self.logger.debug("created vm name: %s", vm_name) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 718 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 719 | if start: |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 720 | self.conn_compute.virtual_machines.start( |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 721 | self.resource_group, |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 722 | vm_name) |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 723 | # start_result.wait() |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 724 | |
| 725 | return result.id, None |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 726 | |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 727 | # run_command_parameters = { |
| 728 | # 'command_id': 'RunShellScript', # For linux, don't change it |
| 729 | # 'script': [ |
| 730 | # 'date > /tmp/test.txt' |
| 731 | # ] |
| 732 | # } |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 733 | except Exception as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 734 | self.logger.debug('Exception creating new vminstance: %s', e, exc_info=True) |
| 735 | self._format_vimconn_exception(e) |
| 736 | |
| 737 | def _get_unused_vm_name(self, vm_name): |
| 738 | """ |
| 739 | Checks the vm name and in case it is used adds a suffix to the name to allow creation |
| 740 | :return: |
| 741 | """ |
| 742 | all_vms = self.conn_compute.virtual_machines.list(self.resource_group) |
| 743 | # Filter to vms starting with the indicated name |
| 744 | vms = list(filter(lambda vm: (vm.name.startswith(vm_name)), all_vms)) |
| 745 | vm_names = [str(vm.name) for vm in vms] |
| 746 | |
| 747 | # get the name with the first not used suffix |
| 748 | name_suffix = 0 |
| 749 | # name = subnet_name + "-" + str(name_suffix) |
| 750 | name = vm_name # first subnet created will have no prefix |
| 751 | while name in vm_names: |
| 752 | name_suffix += 1 |
| 753 | name = vm_name + "-" + str(name_suffix) |
| 754 | return name |
| 755 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 756 | # It is necesary extract from image_id data to create the VM with this format |
| 757 | # 'image_reference': { |
| 758 | # 'publisher': vm_reference['publisher'], |
| 759 | # 'offer': vm_reference['offer'], |
| 760 | # 'sku': vm_reference['sku'], |
| 761 | # 'version': vm_reference['version'] |
| 762 | # }, |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 763 | def _get_image_reference(self, image_id): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 764 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 765 | try: |
| 766 | # The data input format example: |
| 767 | # /Subscriptions/ca3d18ab-d373-4afb-a5d6-7c44f098d16a/Providers/Microsoft.Compute/Locations/westeurope/ |
| 768 | # Publishers/Canonical/ArtifactTypes/VMImage/ |
| 769 | # Offers/UbuntuServer/ |
| 770 | # Skus/18.04-LTS/ |
| 771 | # Versions/18.04.201809110 |
| 772 | publisher = str(image_id.split('/')[8]) |
| 773 | offer = str(image_id.split('/')[12]) |
| 774 | sku = str(image_id.split('/')[14]) |
| 775 | version = str(image_id.split('/')[16]) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 776 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 777 | return { |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 778 | 'publisher': publisher, |
| 779 | 'offer': offer, |
| 780 | 'sku': sku, |
| 781 | 'version': version |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 782 | } |
| 783 | except Exception as e: |
| 784 | raise vimconn.vimconnException( |
| 785 | "Unable to get image_reference from invalid image_id format: '{}'".format(image_id)) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 786 | |
| 787 | # Azure VM names can not have some special characters |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 788 | def _check_vm_name(self, vm_name): |
| 789 | """ |
| 790 | Checks vm name, in case the vm has not allowed characters they are removed, not error raised |
| 791 | """ |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 792 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 793 | chars_not_allowed_list = "~!@#$%^&*()=+_[]{}|;:<>/?." |
| 794 | |
| 795 | # First: the VM name max length is 64 characters |
| 796 | vm_name_aux = vm_name[:64] |
| 797 | |
| 798 | # Second: replace not allowed characters |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 799 | for elem in chars_not_allowed_list: |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 800 | # Check if string is in the main string |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 801 | if elem in vm_name_aux: |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 802 | # self.logger.debug('Dentro del IF') |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 803 | # Replace the string |
| 804 | vm_name_aux = vm_name_aux.replace(elem, '-') |
| 805 | |
| 806 | return vm_name_aux |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 807 | |
| 808 | def get_flavor_id_from_data(self, flavor_dict): |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 809 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 810 | self.logger.debug("getting flavor id from data, flavor_dict: %s", flavor_dict) |
| 811 | filter_dict = flavor_dict or {} |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 812 | try: |
| 813 | self._reload_connection() |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 814 | vm_sizes_list = [vm_size.serialize() for vm_size in |
| 815 | self.conn_compute.virtual_machine_sizes.list(self.region)] |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 816 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 817 | cpus = filter_dict.get('vcpus') or 0 |
| 818 | memMB = filter_dict.get('ram') or 0 |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 819 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 820 | # Filter |
| 821 | if self._config.get("flavors_pattern"): |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 822 | filtered_sizes = [size for size in vm_sizes_list if size['numberOfCores'] >= cpus and |
| 823 | size['memoryInMB'] >= memMB and |
| 824 | re.search(self._config.get("flavors_pattern"), size["name"])] |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 825 | else: |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 826 | filtered_sizes = [size for size in vm_sizes_list if size['numberOfCores'] >= cpus and |
| 827 | size['memoryInMB'] >= memMB] |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 828 | |
| 829 | # Sort |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 830 | listedFilteredSizes = sorted(filtered_sizes, key=lambda k: (k['numberOfCores'], k['memoryInMB'], |
| 831 | k['resourceDiskSizeInMB'])) |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 832 | |
| 833 | if listedFilteredSizes: |
| 834 | return listedFilteredSizes[0]['name'] |
| 835 | raise vimconn.vimconnNotFoundException("Cannot find any flavor matching '{}'".format(str(flavor_dict))) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 836 | |
| 837 | except Exception as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 838 | self._format_vimconn_exception(e) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 839 | |
| 840 | def _get_flavor_id_from_flavor_name(self, flavor_name): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 841 | |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 842 | # self.logger.debug("getting flavor id from flavor name {}".format(flavor_name)) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 843 | try: |
| 844 | self._reload_connection() |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 845 | vm_sizes_list = [vm_size.serialize() for vm_size in |
| 846 | self.conn_compute.virtual_machine_sizes.list(self.region)] |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 847 | |
| 848 | output_flavor = None |
| 849 | for size in vm_sizes_list: |
| 850 | if size['name'] == flavor_name: |
| 851 | output_flavor = size |
| 852 | |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 853 | # None is returned if not found anything |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 854 | return output_flavor |
| 855 | |
| 856 | except Exception as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 857 | self._format_vimconn_exception(e) |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 858 | |
| 859 | def check_vim_connectivity(self): |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 860 | try: |
| 861 | self._reload_connection() |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 862 | return True |
| 863 | except Exception as e: |
| 864 | raise vimconn.vimconnException("Connectivity issue with Azure API: {}".format(e)) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 865 | |
| 866 | def get_network(self, net_id): |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 867 | |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 868 | # self.logger.debug('get network id: {}'.format(net_id)) |
| 869 | # res_name = self._get_resource_name_from_resource_id(net_id) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 870 | self._reload_connection() |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 871 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 872 | filter_dict = {'name': net_id} |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 873 | network_list = self.get_network_list(filter_dict) |
| 874 | |
| 875 | if not network_list: |
| 876 | raise vimconn.vimconnNotFoundException("network '{}' not found".format(net_id)) |
| 877 | else: |
| 878 | return network_list[0] |
| 879 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 880 | def delete_network(self, net_id, created_items=None): |
| 881 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 882 | self.logger.debug('deleting network {} - {}'.format(self.resource_group, net_id)) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 883 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 884 | self._reload_connection() |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 885 | res_name = self._get_resource_name_from_resource_id(net_id) |
| 886 | filter_dict = {'name': res_name} |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 887 | network_list = self.get_network_list(filter_dict) |
| 888 | if not network_list: |
| 889 | raise vimconn.vimconnNotFoundException("network '{}' not found".format(net_id)) |
| 890 | |
| 891 | try: |
| 892 | # Subnet API fails (CloudError: Azure Error: ResourceNotFound) |
| 893 | # Put the initial virtual_network API |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 894 | async_delete = self.conn_vnet.subnets.delete(self.resource_group, self.vnet_name, res_name) |
| 895 | async_delete.wait() |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 896 | return net_id |
| 897 | |
| 898 | except CloudError as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 899 | if e.error.error and "notfound" in e.error.error.lower(): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 900 | raise vimconn.vimconnNotFoundException("network '{}' not found".format(net_id)) |
| 901 | else: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 902 | self._format_vimconn_exception(e) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 903 | except Exception as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 904 | self._format_vimconn_exception(e) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 905 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 906 | def delete_vminstance(self, vm_id, created_items=None): |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 907 | """ Deletes a vm instance from the vim. |
| 908 | """ |
| 909 | self.logger.debug('deleting VM instance {} - {}'.format(self.resource_group, vm_id)) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 910 | self._reload_connection() |
| 911 | |
| 912 | try: |
| 913 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 914 | res_name = self._get_resource_name_from_resource_id(vm_id) |
| 915 | vm = self.conn_compute.virtual_machines.get(self.resource_group, res_name) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 916 | |
| 917 | # Shuts down the virtual machine and releases the compute resources |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 918 | # vm_stop = self.conn_compute.virtual_machines.power_off(self.resource_group, resName) |
| 919 | # vm_stop.wait() |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 920 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 921 | vm_delete = self.conn_compute.virtual_machines.delete(self.resource_group, res_name) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 922 | vm_delete.wait() |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 923 | self.logger.debug('deleted VM name: %s', res_name) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 924 | |
| 925 | # Delete OS Disk |
| 926 | os_disk_name = vm.storage_profile.os_disk.name |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 927 | self.logger.debug('delete OS DISK: %s', os_disk_name) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 928 | self.conn_compute.disks.delete(self.resource_group, os_disk_name) |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 929 | self.logger.debug('deleted OS DISK name: %s', os_disk_name) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 930 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 931 | for data_disk in vm.storage_profile.data_disks: |
| 932 | self.logger.debug('delete data_disk: %s', data_disk.name) |
| 933 | self.conn_compute.disks.delete(self.resource_group, data_disk.name) |
| 934 | self.logger.debug('deleted OS DISK name: %s', data_disk.name) |
| 935 | |
| 936 | # After deleting VM, it is necessary to delete NIC, because if is not deleted delete_network |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 937 | # does not work because Azure says that is in use the subnet |
| 938 | network_interfaces = vm.network_profile.network_interfaces |
| 939 | |
| 940 | for network_interface in network_interfaces: |
| 941 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 942 | nic_name = self._get_resource_name_from_resource_id(network_interface.id) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 943 | nic_data = self.conn_vnet.network_interfaces.get( |
| 944 | self.resource_group, |
| 945 | nic_name) |
| 946 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 947 | public_ip_name = None |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 948 | exist_public_ip = nic_data.ip_configurations[0].public_ip_address |
| 949 | if exist_public_ip: |
| 950 | public_ip_id = nic_data.ip_configurations[0].public_ip_address.id |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 951 | |
| 952 | # Delete public_ip |
| 953 | public_ip_name = self._get_resource_name_from_resource_id(public_ip_id) |
| 954 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 955 | # Public ip must be deleted afterwards of nic that is attached |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 956 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 957 | self.logger.debug('delete NIC name: %s', nic_name) |
| 958 | nic_delete = self.conn_vnet.network_interfaces.delete(self.resource_group, nic_name) |
| 959 | nic_delete.wait() |
| 960 | self.logger.debug('deleted NIC name: %s', nic_name) |
| 961 | |
| 962 | # Delete list of public ips |
| 963 | if public_ip_name: |
| 964 | self.logger.debug('delete PUBLIC IP - ' + public_ip_name) |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 965 | self.conn_vnet.public_ip_addresses.delete(self.resource_group, public_ip_name) |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 966 | |
| 967 | except CloudError as e: |
| 968 | if e.error.error and "notfound" in e.error.error.lower(): |
| 969 | raise vimconn.vimconnNotFoundException("No vm instance found '{}'".format(vm_id)) |
| 970 | else: |
| 971 | self._format_vimconn_exception(e) |
| 972 | except Exception as e: |
| 973 | self._format_vimconn_exception(e) |
| 974 | |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 975 | def action_vminstance(self, vm_id, action_dict, created_items={}): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 976 | """Send and action over a VM instance from VIM |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 977 | Returns the vm_id if the action was successfully sent to the VIM |
| 978 | """ |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 979 | |
| 980 | self.logger.debug("Action over VM '%s': %s", vm_id, str(action_dict)) |
| 981 | try: |
| 982 | self._reload_connection() |
| 983 | resName = self._get_resource_name_from_resource_id(vm_id) |
| 984 | if "start" in action_dict: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 985 | self.conn_compute.virtual_machines.start(self.resource_group, resName) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 986 | elif "stop" in action_dict or "shutdown" in action_dict or "shutoff" in action_dict: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 987 | self.conn_compute.virtual_machines.power_off(self.resource_group, resName) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 988 | elif "terminate" in action_dict: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 989 | self.conn_compute.virtual_machines.delete(self.resource_group, resName) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 990 | elif "reboot" in action_dict: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 991 | self.conn_compute.virtual_machines.restart(self.resource_group, resName) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 992 | return None |
| 993 | except CloudError as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 994 | if e.error.error and "notfound" in e.error.error.lower(): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 995 | raise vimconn.vimconnNotFoundException("No vm found '{}'".format(vm_id)) |
| 996 | else: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 997 | self._format_vimconn_exception(e) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 998 | except Exception as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 999 | self._format_vimconn_exception(e) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1000 | |
| 1001 | def delete_flavor(self, flavor_id): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1002 | raise vimconn.vimconnAuthException("It is not possible to delete a FLAVOR in AZURE") |
| 1003 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1004 | def delete_tenant(self, tenant_id,): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1005 | raise vimconn.vimconnAuthException("It is not possible to delete a TENANT in AZURE") |
| 1006 | |
| 1007 | def delete_image(self, image_id): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1008 | raise vimconn.vimconnAuthException("It is not possible to delete a IMAGE in AZURE") |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 1009 | |
| 1010 | def get_vminstance(self, vm_id): |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1011 | """ |
| 1012 | Obtaing the vm instance data from v_id |
| 1013 | """ |
| 1014 | self.logger.debug("get vm instance: %s", vm_id) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 1015 | self._reload_connection() |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1016 | try: |
| 1017 | resName = self._get_resource_name_from_resource_id(vm_id) |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 1018 | vm = self.conn_compute.virtual_machines.get(self.resource_group, resName) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1019 | except CloudError as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1020 | if e.error.error and "notfound" in e.error.error.lower(): |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1021 | raise vimconn.vimconnNotFoundException("No vminstance found '{}'".format(vm_id)) |
| 1022 | else: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1023 | self._format_vimconn_exception(e) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1024 | except Exception as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1025 | self._format_vimconn_exception(e) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 1026 | |
| 1027 | return vm |
| 1028 | |
| 1029 | def get_flavor(self, flavor_id): |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1030 | """ |
| 1031 | Obtains the flavor_data from the flavor_id |
| 1032 | """ |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 1033 | self._reload_connection() |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1034 | self.logger.debug("get flavor from id: %s", flavor_id) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1035 | flavor_data = self._get_flavor_id_from_flavor_name(flavor_id) |
| 1036 | if flavor_data: |
| 1037 | flavor = { |
| 1038 | 'id': flavor_id, |
| 1039 | 'name': flavor_id, |
| 1040 | 'ram': flavor_data['memoryInMB'], |
| 1041 | 'vcpus': flavor_data['numberOfCores'], |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1042 | 'disk': flavor_data['resourceDiskSizeInMB']/1024 |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1043 | } |
| 1044 | return flavor |
| 1045 | else: |
| 1046 | raise vimconn.vimconnNotFoundException("flavor '{}' not found".format(flavor_id)) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 1047 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1048 | def get_tenant_list(self, filter_dict={}): |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1049 | """ Obtains the list of tenants |
| 1050 | For the azure connector only the azure tenant will be returned if it is compatible |
| 1051 | with filter_dict |
| 1052 | """ |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 1053 | tenants_azure = [{'name': self.tenant, 'id': self.tenant}] |
| 1054 | tenant_list = [] |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 1055 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1056 | self.logger.debug("get tenant list: %s", filter_dict) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1057 | for tenant_azure in tenants_azure: |
| 1058 | if filter_dict: |
| 1059 | if filter_dict.get("id") and str(tenant_azure.get("id")) != filter_dict["id"]: |
| 1060 | continue |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1061 | if filter_dict.get("name") and str(tenant_azure.get("name")) != filter_dict["name"]: |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1062 | continue |
| 1063 | |
| 1064 | tenant_list.append(tenant_azure) |
| 1065 | |
| 1066 | return tenant_list |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 1067 | |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 1068 | def refresh_nets_status(self, net_list): |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1069 | """Get the status of the networks |
| 1070 | Params: the list of network identifiers |
| 1071 | Returns a dictionary with: |
| 1072 | net_id: #VIM id of this network |
| 1073 | status: #Mandatory. Text with one of: |
| 1074 | # DELETED (not found at vim) |
| 1075 | # VIM_ERROR (Cannot connect to VIM, VIM response error, ...) |
| 1076 | # OTHER (Vim reported other status not understood) |
| 1077 | # ERROR (VIM indicates an ERROR status) |
| 1078 | # ACTIVE, INACTIVE, DOWN (admin down), |
| 1079 | # BUILD (on building process) |
| 1080 | # |
| 1081 | error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR |
| 1082 | vim_info: #Text with plain information obtained from vim (yaml.safe_dump) |
| 1083 | |
| 1084 | """ |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1085 | |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 1086 | out_nets = {} |
| 1087 | self._reload_connection() |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1088 | |
| 1089 | self.logger.debug("reload nets status net_list: %s", net_list) |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 1090 | for net_id in net_list: |
| 1091 | try: |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1092 | netName = self._get_net_name_from_resource_id(net_id) |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 1093 | resName = self._get_resource_name_from_resource_id(net_id) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 1094 | |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1095 | net = self.conn_vnet.subnets.get(self.resource_group, netName, resName) |
| 1096 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1097 | out_nets[net_id] = { |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1098 | "status": self.provision_state2osm[net.provisioning_state], |
| 1099 | "vim_info": str(net) |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 1100 | } |
| 1101 | except CloudError as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1102 | if e.error.error and "notfound" in e.error.error.lower(): |
| 1103 | self.logger.info("Not found subnet net_name: %s, subnet_name: %s", netName, resName) |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 1104 | out_nets[net_id] = { |
| 1105 | "status": "DELETED", |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1106 | "error_msg": str(e) |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 1107 | } |
| 1108 | else: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1109 | self.logger.error("CloudError Exception %s when searching subnet", e) |
| 1110 | out_nets[net_id] = { |
| 1111 | "status": "VIM_ERROR", |
| 1112 | "error_msg": str(e) |
| 1113 | } |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1114 | except vimconn.vimconnNotFoundException as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1115 | self.logger.error("VimConnNotFoundException %s when searching subnet", e) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1116 | out_nets[net_id] = { |
| 1117 | "status": "DELETED", |
| 1118 | "error_msg": str(e) |
| 1119 | } |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 1120 | except Exception as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1121 | self.logger.error("Exception %s when searching subnet", e, exc_info=True) |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 1122 | out_nets[net_id] = { |
| 1123 | "status": "VIM_ERROR", |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 1124 | "error_msg": str(e) |
| 1125 | } |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 1126 | return out_nets |
| 1127 | |
| 1128 | def refresh_vms_status(self, vm_list): |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1129 | """ Get the status of the virtual machines and their interfaces/ports |
| 1130 | Params: the list of VM identifiers |
| 1131 | Returns a dictionary with: |
| 1132 | vm_id: # VIM id of this Virtual Machine |
| 1133 | status: # Mandatory. Text with one of: |
| 1134 | # DELETED (not found at vim) |
| 1135 | # VIM_ERROR (Cannot connect to VIM, VIM response error, ...) |
| 1136 | # OTHER (Vim reported other status not understood) |
| 1137 | # ERROR (VIM indicates an ERROR status) |
| 1138 | # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running), |
| 1139 | # BUILD (on building process), ERROR |
| 1140 | # ACTIVE:NoMgmtIP (Active but none of its interfaces has an IP address |
| 1141 | # (ACTIVE:NoMgmtIP is not returned for Azure) |
| 1142 | # |
| 1143 | error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR |
| 1144 | vim_info: #Text with plain information obtained from vim (yaml.safe_dump) |
| 1145 | interfaces: list with interface info. Each item a dictionary with: |
| 1146 | vim_interface_id - The ID of the interface |
| 1147 | mac_address - The MAC address of the interface. |
| 1148 | ip_address - The IP address of the interface within the subnet. |
| 1149 | """ |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1150 | |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 1151 | out_vms = {} |
| 1152 | self._reload_connection() |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1153 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1154 | self.logger.debug("refresh vm status vm_list: %s", vm_list) |
| 1155 | search_vm_list = vm_list or {} |
| 1156 | |
| 1157 | for vm_id in search_vm_list: |
| 1158 | out_vm = {} |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 1159 | try: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1160 | res_name = self._get_resource_name_from_resource_id(vm_id) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1161 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1162 | vm = self.conn_compute.virtual_machines.get(self.resource_group, res_name) |
| 1163 | out_vm['vim_info'] = str(vm) |
| 1164 | out_vm['status'] = self.provision_state2osm.get(vm.provisioning_state, 'OTHER') |
| 1165 | if vm.provisioning_state == 'Succeeded': |
| 1166 | # check if machine is running or stopped |
| 1167 | instance_view = self.conn_compute.virtual_machines.instance_view(self.resource_group, |
| 1168 | res_name) |
| 1169 | for status in instance_view.statuses: |
| 1170 | splitted_status = status.code.split("/") |
| 1171 | if len(splitted_status) == 2 and splitted_status[0] == 'PowerState': |
| 1172 | out_vm['status'] = self.power_state2osm.get(splitted_status[1], 'OTHER') |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1173 | |
| 1174 | network_interfaces = vm.network_profile.network_interfaces |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1175 | out_vm['interfaces'] = self._get_vm_interfaces_status(vm_id, network_interfaces) |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1176 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1177 | except CloudError as e: |
| 1178 | if e.error.error and "notfound" in e.error.error.lower(): |
| 1179 | self.logger.debug("Not found vm id: %s", vm_id) |
| 1180 | out_vm['status'] = "DELETED" |
| 1181 | out_vm['error_msg'] = str(e) |
| 1182 | out_vm['vim_info'] = None |
| 1183 | else: |
| 1184 | # maybe connection error or another type of error, return vim error |
| 1185 | self.logger.error("Exception %s refreshing vm_status", e) |
| 1186 | out_vm['status'] = "VIM_ERROR" |
| 1187 | out_vm['error_msg'] = str(e) |
| 1188 | out_vm['vim_info'] = None |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 1189 | except Exception as e: |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1190 | self.logger.error("Exception %s refreshing vm_status", e, exc_info=True) |
| 1191 | out_vm['status'] = "VIM_ERROR" |
| 1192 | out_vm['error_msg'] = str(e) |
| 1193 | out_vm['vim_info'] = None |
| jamartinezv | 14a823d | 2019-08-01 11:45:15 +0200 | [diff] [blame] | 1194 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1195 | out_vms[vm_id] = out_vm |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 1196 | |
| 1197 | return out_vms |
| seryio | 07ad136 | 2019-05-29 09:16:24 +0200 | [diff] [blame] | 1198 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1199 | def _get_vm_interfaces_status(self, vm_id, interfaces): |
| 1200 | """ |
| 1201 | Gets the interfaces detail for a vm |
| 1202 | :param interfaces: List of interfaces. |
| 1203 | :return: Dictionary with list of interfaces including, vim_interface_id, mac_address and ip_address |
| 1204 | """ |
| 1205 | try: |
| 1206 | interface_list = [] |
| 1207 | for network_interface in interfaces: |
| 1208 | interface_dict = {} |
| 1209 | nic_name = self._get_resource_name_from_resource_id(network_interface.id) |
| 1210 | interface_dict['vim_interface_id'] = network_interface.id |
| 1211 | |
| 1212 | nic_data = self.conn_vnet.network_interfaces.get( |
| 1213 | self.resource_group, |
| 1214 | nic_name) |
| 1215 | |
| lloretgalleg | bfc28b0 | 2019-11-21 09:31:16 +0100 | [diff] [blame] | 1216 | ips = [] |
| 1217 | if nic_data.ip_configurations[0].public_ip_address: |
| 1218 | self.logger.debug("Obtain public ip address") |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 1219 | public_ip_name = self._get_resource_name_from_resource_id( |
| 1220 | nic_data.ip_configurations[0].public_ip_address.id) |
| lloretgalleg | bfc28b0 | 2019-11-21 09:31:16 +0100 | [diff] [blame] | 1221 | public_ip = self.conn_vnet.public_ip_addresses.get(self.resource_group, public_ip_name) |
| 1222 | self.logger.debug("Public ip address is: %s", public_ip.ip_address) |
| 1223 | ips.append(public_ip.ip_address) |
| 1224 | |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1225 | private_ip = nic_data.ip_configurations[0].private_ip_address |
| lloretgalleg | bfc28b0 | 2019-11-21 09:31:16 +0100 | [diff] [blame] | 1226 | ips.append(private_ip) |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1227 | |
| 1228 | interface_dict['mac_address'] = nic_data.mac_address |
| lloretgalleg | bfc28b0 | 2019-11-21 09:31:16 +0100 | [diff] [blame] | 1229 | interface_dict['ip_address'] = ";".join(ips) |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1230 | interface_list.append(interface_dict) |
| 1231 | |
| 1232 | return interface_list |
| 1233 | except Exception as e: |
| 1234 | self.logger.error("Exception %s obtaining interface data for vm: %s, error: %s", vm_id, e, exc_info=True) |
| 1235 | self._format_vimconn_exception(e) |
| 1236 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 1237 | |
| 1238 | if __name__ == "__main__": |
| 1239 | |
| 1240 | # Making some basic test |
| lloretgalleg | 4522015 | 2019-10-29 11:53:49 +0100 | [diff] [blame] | 1241 | vim_id = 'azure' |
| 1242 | vim_name = 'azure' |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 1243 | needed_test_params = { |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 1244 | "client_id": "AZURE_CLIENT_ID", |
| 1245 | "secret": "AZURE_SECRET", |
| 1246 | "tenant": "AZURE_TENANT", |
| 1247 | "resource_group": "AZURE_RESOURCE_GROUP", |
| 1248 | "subscription_id": "AZURE_SUBSCRIPTION_ID", |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 1249 | "vnet_name": "AZURE_VNET_NAME", |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 1250 | } |
| 1251 | test_params = {} |
| 1252 | |
| 1253 | for param, env_var in needed_test_params.items(): |
| 1254 | value = getenv(env_var) |
| 1255 | if not value: |
| 1256 | raise Exception("Provide a valid value for env '{}'".format(env_var)) |
| 1257 | test_params[param] = value |
| 1258 | |
| 1259 | config = { |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 1260 | 'region_name': getenv("AZURE_REGION_NAME", 'westeurope'), |
| 1261 | 'resource_group': getenv("AZURE_RESOURCE_GROUP"), |
| 1262 | 'subscription_id': getenv("AZURE_SUBSCRIPTION_ID"), |
| 1263 | 'pub_key': getenv("AZURE_PUB_KEY", None), |
| 1264 | 'vnet_name': getenv("AZURE_VNET_NAME", 'myNetwork'), |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 1265 | } |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 1266 | |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 1267 | virtualMachine = { |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 1268 | 'name': 'sergio', |
| 1269 | 'description': 'new VM', |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 1270 | 'status': 'running', |
| 1271 | 'image': { |
| 1272 | 'publisher': 'Canonical', |
| 1273 | 'offer': 'UbuntuServer', |
| 1274 | 'sku': '16.04.0-LTS', |
| 1275 | 'version': 'latest' |
| 1276 | }, |
| 1277 | 'hardware_profile': { |
| 1278 | 'vm_size': 'Standard_DS1_v2' |
| 1279 | }, |
| 1280 | 'networks': [ |
| 1281 | 'sergio' |
| 1282 | ] |
| 1283 | } |
| 1284 | |
| 1285 | vnet_config = { |
| 1286 | 'subnet_address': '10.1.2.0/24', |
| tierno | b569e4b | 2019-11-21 16:10:32 +0000 | [diff] [blame] | 1287 | # 'subnet_name': 'subnet-oam' |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 1288 | } |
| 1289 | ########################### |
| 1290 | |
| tierno | 30d0d6d | 2019-05-27 08:14:01 +0000 | [diff] [blame] | 1291 | azure = vimconnector(vim_id, vim_name, tenant_id=test_params["tenant"], tenant_name=None, url=None, url_admin=None, |
| 1292 | user=test_params["client_id"], passwd=test_params["secret"], log_level=None, config=config) |
| seryio | 3447855 | 2019-05-23 14:50:49 +0200 | [diff] [blame] | 1293 | |
| tierno | deb74b2 | 2019-05-27 10:24:50 +0000 | [diff] [blame] | 1294 | # azure.get_flavor_id_from_data("here") |
| 1295 | # subnets=azure.get_network_list() |
| 1296 | # azure.new_vminstance(virtualMachine['name'], virtualMachine['description'], virtualMachine['status'], |
| 1297 | # virtualMachine['image'], virtualMachine['hardware_profile']['vm_size'], subnets) |
| 1298 | |
| tierno | 2462041 | 2019-06-03 14:05:08 +0000 | [diff] [blame] | 1299 | azure.new_network("mynet", None) |
| tierno | 84efdc1 | 2019-05-29 09:29:01 +0000 | [diff] [blame] | 1300 | net_id = "/subscriptions/82f80cc1-876b-4591-9911-1fb5788384fd/resourceGroups/osmRG/providers/Microsoft."\ |
| 1301 | "Network/virtualNetworks/test" |
| 1302 | net_id_not_found = "/subscriptions/82f80cc1-876b-4591-9911-1fb5788384fd/resourceGroups/osmRG/providers/"\ |
| 1303 | "Microsoft.Network/virtualNetworks/testALF" |
| 1304 | azure.refresh_nets_status([net_id, net_id_not_found]) |