blob: 5e74e966f7f5357510882e3deba9b8574ac2c757 [file] [log] [blame]
tierno7edb6752016-03-21 17:37:52 +01001# -*- coding: utf-8 -*-
2
3##
tierno92021022018-09-12 16:29:23 +02004# Copyright 2015 Telefonica Investigacion y Desarrollo, S.A.U.
tierno7edb6752016-03-21 17:37:52 +01005# This file is part of openmano
6# All Rights Reserved.
7#
8# Licensed under the Apache License, Version 2.0 (the "License"); you may
9# not use this file except in compliance with the License. You may obtain
10# a copy of the License at
11#
12# http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17# License for the specific language governing permissions and limitations
18# under the License.
tierno7edb6752016-03-21 17:37:52 +010019##
20
tierno1ec592d2020-06-16 15:29:47 +000021"""
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +000022osconnector implements all the methods to interact with openstack using the python-neutronclient.
23
24For the VNF forwarding graph, The OpenStack VIM connector calls the
25networking-sfc Neutron extension methods, whose resources are mapped
26to the VIM connector's SFC resources as follows:
27- Classification (OSM) -> Flow Classifier (Neutron)
28- Service Function Instance (OSM) -> Port Pair (Neutron)
29- Service Function (OSM) -> Port Pair Group (Neutron)
30- Service Function Path (OSM) -> Port Chain (Neutron)
tierno1ec592d2020-06-16 15:29:47 +000031"""
tierno7edb6752016-03-21 17:37:52 +010032
tierno72774862020-05-04 11:44:15 +000033from osm_ro_plugin import vimconn
sousaedu80135b92021-02-17 15:05:18 +010034
tierno69b590e2018-03-13 18:52:23 +010035# import json
tiernoae4a8d12016-07-08 12:30:39 +020036import logging
garciadeblas9f8456e2016-09-05 05:02:59 +020037import netaddr
montesmoreno0c8def02016-12-22 12:16:23 +000038import time
tierno36c0b172017-01-12 18:32:28 +010039import yaml
garciadeblas2299e3b2017-01-26 14:35:55 +000040import random
kate721d79b2017-06-24 04:21:38 -070041import re
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +000042import copy
Anderson Bravalheri0446cd52018-08-17 15:26:19 +010043from pprint import pformat
tiernob5cef372017-06-19 15:52:22 +020044from novaclient import client as nClient, exceptions as nvExceptions
45from keystoneauth1.identity import v2, v3
46from keystoneauth1 import session
tierno7edb6752016-03-21 17:37:52 +010047import keystoneclient.exceptions as ksExceptions
tiernof716aea2017-06-21 18:01:40 +020048import keystoneclient.v3.client as ksClient_v3
49import keystoneclient.v2_0.client as ksClient_v2
tiernob5cef372017-06-19 15:52:22 +020050from glanceclient import client as glClient
tierno7edb6752016-03-21 17:37:52 +010051import glanceclient.exc as gl1Exceptions
tierno1ec592d2020-06-16 15:29:47 +000052from cinderclient import client as cClient
sousaedu80135b92021-02-17 15:05:18 +010053
54# TODO py3 check that this base exception matches python2 httplib.HTTPException
55from http.client import HTTPException
tiernob5cef372017-06-19 15:52:22 +020056from neutronclient.neutron import client as neClient
tierno7edb6752016-03-21 17:37:52 +010057from neutronclient.common import exceptions as neExceptions
58from requests.exceptions import ConnectionError
59
tierno1ec592d2020-06-16 15:29:47 +000060__author__ = "Alfonso Tierno, Gerardo Garcia, Pablo Montes, xFlow Research, Igor D.C., Eduardo Sousa"
61__date__ = "$22-sep-2017 23:59:59$"
tierno40e1bce2017-08-09 09:12:04 +020062
63"""contain the openstack virtual machine status to openmano status"""
sousaedu80135b92021-02-17 15:05:18 +010064vmStatus2manoFormat = {
65 "ACTIVE": "ACTIVE",
66 "PAUSED": "PAUSED",
67 "SUSPENDED": "SUSPENDED",
68 "SHUTOFF": "INACTIVE",
69 "BUILD": "BUILD",
70 "ERROR": "ERROR",
71 "DELETED": "DELETED",
72}
73netStatus2manoFormat = {
74 "ACTIVE": "ACTIVE",
75 "PAUSED": "PAUSED",
76 "INACTIVE": "INACTIVE",
77 "BUILD": "BUILD",
78 "ERROR": "ERROR",
79 "DELETED": "DELETED",
80}
tierno7edb6752016-03-21 17:37:52 +010081
sousaedu80135b92021-02-17 15:05:18 +010082supportedClassificationTypes = ["legacy_flow_classifier"]
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +000083
tierno1ec592d2020-06-16 15:29:47 +000084# global var to have a timeout creating and deleting volumes
garciadeblas64b39c52020-05-21 08:07:25 +000085volume_timeout = 1800
86server_timeout = 1800
montesmoreno0c8def02016-12-22 12:16:23 +000087
Anderson Bravalheri0446cd52018-08-17 15:26:19 +010088
89class SafeDumper(yaml.SafeDumper):
90 def represent_data(self, data):
91 # Openstack APIs use custom subclasses of dict and YAML safe dumper
92 # is designed to not handle that (reference issue 142 of pyyaml)
93 if isinstance(data, dict) and data.__class__ != dict:
94 # A simple solution is to convert those items back to dicts
95 data = dict(data.items())
96
97 return super(SafeDumper, self).represent_data(data)
98
99
tierno72774862020-05-04 11:44:15 +0000100class vimconnector(vimconn.VimConnector):
sousaedu80135b92021-02-17 15:05:18 +0100101 def __init__(
102 self,
103 uuid,
104 name,
105 tenant_id,
106 tenant_name,
107 url,
108 url_admin=None,
109 user=None,
110 passwd=None,
111 log_level=None,
112 config={},
113 persistent_info={},
114 ):
tierno1ec592d2020-06-16 15:29:47 +0000115 """using common constructor parameters. In this case
tierno7edb6752016-03-21 17:37:52 +0100116 'url' is the keystone authorization url,
117 'url_admin' is not use
tierno1ec592d2020-06-16 15:29:47 +0000118 """
sousaedu80135b92021-02-17 15:05:18 +0100119 api_version = config.get("APIversion")
kate721d79b2017-06-24 04:21:38 -0700120
sousaedu80135b92021-02-17 15:05:18 +0100121 if api_version and api_version not in ("v3.3", "v2.0", "2", "3"):
122 raise vimconn.VimConnException(
123 "Invalid value '{}' for config:APIversion. "
124 "Allowed values are 'v3.3', 'v2.0', '2' or '3'".format(api_version)
125 )
126
127 vim_type = config.get("vim_type")
128
129 if vim_type and vim_type not in ("vio", "VIO"):
130 raise vimconn.VimConnException(
131 "Invalid value '{}' for config:vim_type."
132 "Allowed values are 'vio' or 'VIO'".format(vim_type)
133 )
134
135 if config.get("dataplane_net_vlan_range") is not None:
tierno1ec592d2020-06-16 15:29:47 +0000136 # validate vlan ranges provided by user
sousaedu80135b92021-02-17 15:05:18 +0100137 self._validate_vlan_ranges(
138 config.get("dataplane_net_vlan_range"), "dataplane_net_vlan_range"
139 )
garciadeblasebd66722019-01-31 16:01:31 +0000140
sousaedu80135b92021-02-17 15:05:18 +0100141 if config.get("multisegment_vlan_range") is not None:
tierno1ec592d2020-06-16 15:29:47 +0000142 # validate vlan ranges provided by user
sousaedu80135b92021-02-17 15:05:18 +0100143 self._validate_vlan_ranges(
144 config.get("multisegment_vlan_range"), "multisegment_vlan_range"
145 )
kate721d79b2017-06-24 04:21:38 -0700146
sousaedu80135b92021-02-17 15:05:18 +0100147 vimconn.VimConnector.__init__(
148 self,
149 uuid,
150 name,
151 tenant_id,
152 tenant_name,
153 url,
154 url_admin,
155 user,
156 passwd,
157 log_level,
158 config,
159 )
tiernob3d36742017-03-03 23:51:05 +0100160
tierno4d1ce222018-04-06 10:41:06 +0200161 if self.config.get("insecure") and self.config.get("ca_cert"):
sousaedu80135b92021-02-17 15:05:18 +0100162 raise vimconn.VimConnException(
163 "options insecure and ca_cert are mutually exclusive"
164 )
165
tierno4d1ce222018-04-06 10:41:06 +0200166 self.verify = True
sousaedu80135b92021-02-17 15:05:18 +0100167
tierno4d1ce222018-04-06 10:41:06 +0200168 if self.config.get("insecure"):
169 self.verify = False
sousaedu80135b92021-02-17 15:05:18 +0100170
tierno4d1ce222018-04-06 10:41:06 +0200171 if self.config.get("ca_cert"):
172 self.verify = self.config.get("ca_cert")
tierno4d1ce222018-04-06 10:41:06 +0200173
tierno7edb6752016-03-21 17:37:52 +0100174 if not url:
sousaedu80135b92021-02-17 15:05:18 +0100175 raise TypeError("url param can not be NoneType")
176
tiernob5cef372017-06-19 15:52:22 +0200177 self.persistent_info = persistent_info
sousaedu80135b92021-02-17 15:05:18 +0100178 self.availability_zone = persistent_info.get("availability_zone", None)
179 self.session = persistent_info.get("session", {"reload_client": True})
180 self.my_tenant_id = self.session.get("my_tenant_id")
181 self.nova = self.session.get("nova")
182 self.neutron = self.session.get("neutron")
183 self.cinder = self.session.get("cinder")
184 self.glance = self.session.get("glance")
185 # self.glancev1 = self.session.get("glancev1")
186 self.keystone = self.session.get("keystone")
187 self.api_version3 = self.session.get("api_version3")
kate721d79b2017-06-24 04:21:38 -0700188 self.vim_type = self.config.get("vim_type")
sousaedu80135b92021-02-17 15:05:18 +0100189
kate721d79b2017-06-24 04:21:38 -0700190 if self.vim_type:
191 self.vim_type = self.vim_type.upper()
sousaedu80135b92021-02-17 15:05:18 +0100192
kate721d79b2017-06-24 04:21:38 -0700193 if self.config.get("use_internal_endpoint"):
194 self.endpoint_type = "internalURL"
195 else:
196 self.endpoint_type = None
montesmoreno0c8def02016-12-22 12:16:23 +0000197
sousaedu80135b92021-02-17 15:05:18 +0100198 logging.getLogger("urllib3").setLevel(logging.WARNING)
199 logging.getLogger("keystoneauth").setLevel(logging.WARNING)
200 logging.getLogger("novaclient").setLevel(logging.WARNING)
201 self.logger = logging.getLogger("ro.vim.openstack")
kate721d79b2017-06-24 04:21:38 -0700202
tiernoa05b65a2019-02-01 12:30:27 +0000203 # allow security_groups to be a list or a single string
sousaedu80135b92021-02-17 15:05:18 +0100204 if isinstance(self.config.get("security_groups"), str):
205 self.config["security_groups"] = [self.config["security_groups"]]
206
tiernoa05b65a2019-02-01 12:30:27 +0000207 self.security_groups_id = None
208
tierno1ec592d2020-06-16 15:29:47 +0000209 # ###### VIO Specific Changes #########
kate721d79b2017-06-24 04:21:38 -0700210 if self.vim_type == "VIO":
sousaedu80135b92021-02-17 15:05:18 +0100211 self.logger = logging.getLogger("ro.vim.vio")
kate721d79b2017-06-24 04:21:38 -0700212
tiernofe789902016-09-29 14:20:44 +0000213 if log_level:
tierno1ec592d2020-06-16 15:29:47 +0000214 self.logger.setLevel(getattr(logging, log_level))
tiernof716aea2017-06-21 18:01:40 +0200215
216 def __getitem__(self, index):
217 """Get individuals parameters.
218 Throw KeyError"""
sousaedu80135b92021-02-17 15:05:18 +0100219 if index == "project_domain_id":
tiernof716aea2017-06-21 18:01:40 +0200220 return self.config.get("project_domain_id")
sousaedu80135b92021-02-17 15:05:18 +0100221 elif index == "user_domain_id":
tiernof716aea2017-06-21 18:01:40 +0200222 return self.config.get("user_domain_id")
223 else:
tierno72774862020-05-04 11:44:15 +0000224 return vimconn.VimConnector.__getitem__(self, index)
tiernof716aea2017-06-21 18:01:40 +0200225
226 def __setitem__(self, index, value):
227 """Set individuals parameters and it is marked as dirty so to force connection reload.
228 Throw KeyError"""
sousaedu80135b92021-02-17 15:05:18 +0100229 if index == "project_domain_id":
tiernof716aea2017-06-21 18:01:40 +0200230 self.config["project_domain_id"] = value
sousaedu80135b92021-02-17 15:05:18 +0100231 elif index == "user_domain_id":
tierno1ec592d2020-06-16 15:29:47 +0000232 self.config["user_domain_id"] = value
tiernof716aea2017-06-21 18:01:40 +0200233 else:
tierno72774862020-05-04 11:44:15 +0000234 vimconn.VimConnector.__setitem__(self, index, value)
sousaedu80135b92021-02-17 15:05:18 +0100235
236 self.session["reload_client"] = True
tiernof716aea2017-06-21 18:01:40 +0200237
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100238 def serialize(self, value):
239 """Serialization of python basic types.
240
241 In the case value is not serializable a message will be logged and a
242 simple representation of the data that cannot be converted back to
243 python is returned.
244 """
tierno7d782ef2019-10-04 12:56:31 +0000245 if isinstance(value, str):
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100246 return value
247
248 try:
sousaedu80135b92021-02-17 15:05:18 +0100249 return yaml.dump(
250 value, Dumper=SafeDumper, default_flow_style=True, width=256
251 )
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100252 except yaml.representer.RepresenterError:
sousaedu80135b92021-02-17 15:05:18 +0100253 self.logger.debug(
254 "The following entity cannot be serialized in YAML:\n\n%s\n\n",
255 pformat(value),
256 exc_info=True,
257 )
258
tierno1ec592d2020-06-16 15:29:47 +0000259 return str(value)
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100260
tierno7edb6752016-03-21 17:37:52 +0100261 def _reload_connection(self):
tierno1ec592d2020-06-16 15:29:47 +0000262 """Called before any operation, it check if credentials has changed
tierno7edb6752016-03-21 17:37:52 +0100263 Throw keystoneclient.apiclient.exceptions.AuthorizationFailure
tierno1ec592d2020-06-16 15:29:47 +0000264 """
265 # TODO control the timing and possible token timeout, but it seams that python client does this task for us :-)
sousaedu80135b92021-02-17 15:05:18 +0100266 if self.session["reload_client"]:
267 if self.config.get("APIversion"):
268 self.api_version3 = (
269 self.config["APIversion"] == "v3.3"
270 or self.config["APIversion"] == "3"
271 )
tiernof716aea2017-06-21 18:01:40 +0200272 else: # get from ending auth_url that end with v3 or with v2.0
sousaedu80135b92021-02-17 15:05:18 +0100273 self.api_version3 = self.url.endswith("/v3") or self.url.endswith(
274 "/v3/"
275 )
276
277 self.session["api_version3"] = self.api_version3
278
tiernof716aea2017-06-21 18:01:40 +0200279 if self.api_version3:
sousaedu80135b92021-02-17 15:05:18 +0100280 if self.config.get("project_domain_id") or self.config.get(
281 "project_domain_name"
282 ):
tierno3cb8dc32017-10-24 18:13:19 +0200283 project_domain_id_default = None
284 else:
sousaedu80135b92021-02-17 15:05:18 +0100285 project_domain_id_default = "default"
286
287 if self.config.get("user_domain_id") or self.config.get(
288 "user_domain_name"
289 ):
tierno3cb8dc32017-10-24 18:13:19 +0200290 user_domain_id_default = None
291 else:
sousaedu80135b92021-02-17 15:05:18 +0100292 user_domain_id_default = "default"
293 auth = v3.Password(
294 auth_url=self.url,
295 username=self.user,
296 password=self.passwd,
297 project_name=self.tenant_name,
298 project_id=self.tenant_id,
299 project_domain_id=self.config.get(
300 "project_domain_id", project_domain_id_default
301 ),
302 user_domain_id=self.config.get(
303 "user_domain_id", user_domain_id_default
304 ),
305 project_domain_name=self.config.get("project_domain_name"),
306 user_domain_name=self.config.get("user_domain_name"),
307 )
ahmadsa95baa272016-11-30 09:14:11 +0500308 else:
sousaedu80135b92021-02-17 15:05:18 +0100309 auth = v2.Password(
310 auth_url=self.url,
311 username=self.user,
312 password=self.passwd,
313 tenant_name=self.tenant_name,
314 tenant_id=self.tenant_id,
315 )
316
tierno4d1ce222018-04-06 10:41:06 +0200317 sess = session.Session(auth=auth, verify=self.verify)
tierno1ec592d2020-06-16 15:29:47 +0000318 # addedd region_name to keystone, nova, neutron and cinder to support distributed cloud for Wind River
319 # Titanium cloud and StarlingX
sousaedu80135b92021-02-17 15:05:18 +0100320 region_name = self.config.get("region_name")
321
tiernof716aea2017-06-21 18:01:40 +0200322 if self.api_version3:
sousaedu80135b92021-02-17 15:05:18 +0100323 self.keystone = ksClient_v3.Client(
324 session=sess,
325 endpoint_type=self.endpoint_type,
326 region_name=region_name,
327 )
tiernof716aea2017-06-21 18:01:40 +0200328 else:
sousaedu80135b92021-02-17 15:05:18 +0100329 self.keystone = ksClient_v2.Client(
330 session=sess, endpoint_type=self.endpoint_type
331 )
332
333 self.session["keystone"] = self.keystone
334 # In order to enable microversion functionality an explicit microversion must be specified in "config".
montesmoreno9317d302017-08-16 12:48:23 +0200335 # This implementation approach is due to the warning message in
336 # https://developer.openstack.org/api-guide/compute/microversions.html
337 # where it is stated that microversion backwards compatibility is not guaranteed and clients should
338 # always require an specific microversion.
sousaedu80135b92021-02-17 15:05:18 +0100339 # To be able to use "device role tagging" functionality define "microversion: 2.32" in datacenter config
montesmoreno9317d302017-08-16 12:48:23 +0200340 version = self.config.get("microversion")
sousaedu80135b92021-02-17 15:05:18 +0100341
montesmoreno9317d302017-08-16 12:48:23 +0200342 if not version:
343 version = "2.1"
sousaedu80135b92021-02-17 15:05:18 +0100344
tierno1ec592d2020-06-16 15:29:47 +0000345 # addedd region_name to keystone, nova, neutron and cinder to support distributed cloud for Wind River
346 # Titanium cloud and StarlingX
sousaedu80135b92021-02-17 15:05:18 +0100347 self.nova = self.session["nova"] = nClient.Client(
348 str(version),
349 session=sess,
350 endpoint_type=self.endpoint_type,
351 region_name=region_name,
352 )
353 self.neutron = self.session["neutron"] = neClient.Client(
354 "2.0",
355 session=sess,
356 endpoint_type=self.endpoint_type,
357 region_name=region_name,
358 )
359 self.cinder = self.session["cinder"] = cClient.Client(
360 2,
361 session=sess,
362 endpoint_type=self.endpoint_type,
363 region_name=region_name,
364 )
365
tiernoa05b65a2019-02-01 12:30:27 +0000366 try:
sousaedu80135b92021-02-17 15:05:18 +0100367 self.my_tenant_id = self.session["my_tenant_id"] = sess.get_project_id()
tierno1ec592d2020-06-16 15:29:47 +0000368 except Exception:
tiernoa05b65a2019-02-01 12:30:27 +0000369 self.logger.error("Cannot get project_id from session", exc_info=True)
sousaedu80135b92021-02-17 15:05:18 +0100370
kate721d79b2017-06-24 04:21:38 -0700371 if self.endpoint_type == "internalURL":
372 glance_service_id = self.keystone.services.list(name="glance")[0].id
sousaedu80135b92021-02-17 15:05:18 +0100373 glance_endpoint = self.keystone.endpoints.list(
374 glance_service_id, interface="internal"
375 )[0].url
kate721d79b2017-06-24 04:21:38 -0700376 else:
377 glance_endpoint = None
sousaedu80135b92021-02-17 15:05:18 +0100378
379 self.glance = self.session["glance"] = glClient.Client(
380 2, session=sess, endpoint=glance_endpoint
381 )
tiernoa05b65a2019-02-01 12:30:27 +0000382 # using version 1 of glance client in new_image()
sousaedu80135b92021-02-17 15:05:18 +0100383 # self.glancev1 = self.session["glancev1"] = glClient.Client("1", session=sess,
tierno1beea862018-07-11 15:47:37 +0200384 # endpoint=glance_endpoint)
sousaedu80135b92021-02-17 15:05:18 +0100385 self.session["reload_client"] = False
386 self.persistent_info["session"] = self.session
mirabal29356312017-07-27 12:21:22 +0200387 # add availablity zone info inside self.persistent_info
388 self._set_availablity_zones()
sousaedu80135b92021-02-17 15:05:18 +0100389 self.persistent_info["availability_zone"] = self.availability_zone
390 # force to get again security_groups_ids next time they are needed
391 self.security_groups_id = None
ahmadsa95baa272016-11-30 09:14:11 +0500392
tierno7edb6752016-03-21 17:37:52 +0100393 def __net_os2mano(self, net_list_dict):
tierno1ec592d2020-06-16 15:29:47 +0000394 """Transform the net openstack format to mano format
395 net_list_dict can be a list of dict or a single dict"""
tierno7edb6752016-03-21 17:37:52 +0100396 if type(net_list_dict) is dict:
tierno1ec592d2020-06-16 15:29:47 +0000397 net_list_ = (net_list_dict,)
tierno7edb6752016-03-21 17:37:52 +0100398 elif type(net_list_dict) is list:
tierno1ec592d2020-06-16 15:29:47 +0000399 net_list_ = net_list_dict
tierno7edb6752016-03-21 17:37:52 +0100400 else:
401 raise TypeError("param net_list_dict must be a list or a dictionary")
402 for net in net_list_:
sousaedu80135b92021-02-17 15:05:18 +0100403 if net.get("provider:network_type") == "vlan":
404 net["type"] = "data"
tierno7edb6752016-03-21 17:37:52 +0100405 else:
sousaedu80135b92021-02-17 15:05:18 +0100406 net["type"] = "bridge"
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +0200407
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000408 def __classification_os2mano(self, class_list_dict):
409 """Transform the openstack format (Flow Classifier) to mano format
410 (Classification) class_list_dict can be a list of dict or a single dict
411 """
412 if isinstance(class_list_dict, dict):
413 class_list_ = [class_list_dict]
414 elif isinstance(class_list_dict, list):
415 class_list_ = class_list_dict
416 else:
tierno1ec592d2020-06-16 15:29:47 +0000417 raise TypeError("param class_list_dict must be a list or a dictionary")
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000418 for classification in class_list_:
sousaedu80135b92021-02-17 15:05:18 +0100419 id = classification.pop("id")
420 name = classification.pop("name")
421 description = classification.pop("description")
422 project_id = classification.pop("project_id")
423 tenant_id = classification.pop("tenant_id")
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000424 original_classification = copy.deepcopy(classification)
425 classification.clear()
sousaedu80135b92021-02-17 15:05:18 +0100426 classification["ctype"] = "legacy_flow_classifier"
427 classification["definition"] = original_classification
428 classification["id"] = id
429 classification["name"] = name
430 classification["description"] = description
431 classification["project_id"] = project_id
432 classification["tenant_id"] = tenant_id
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000433
434 def __sfi_os2mano(self, sfi_list_dict):
435 """Transform the openstack format (Port Pair) to mano format (SFI)
436 sfi_list_dict can be a list of dict or a single dict
437 """
438 if isinstance(sfi_list_dict, dict):
439 sfi_list_ = [sfi_list_dict]
440 elif isinstance(sfi_list_dict, list):
441 sfi_list_ = sfi_list_dict
442 else:
sousaedu80135b92021-02-17 15:05:18 +0100443 raise TypeError("param sfi_list_dict must be a list or a dictionary")
444
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000445 for sfi in sfi_list_:
sousaedu80135b92021-02-17 15:05:18 +0100446 sfi["ingress_ports"] = []
447 sfi["egress_ports"] = []
448
449 if sfi.get("ingress"):
450 sfi["ingress_ports"].append(sfi["ingress"])
451
452 if sfi.get("egress"):
453 sfi["egress_ports"].append(sfi["egress"])
454
455 del sfi["ingress"]
456 del sfi["egress"]
457 params = sfi.get("service_function_parameters")
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000458 sfc_encap = False
sousaedu80135b92021-02-17 15:05:18 +0100459
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000460 if params:
sousaedu80135b92021-02-17 15:05:18 +0100461 correlation = params.get("correlation")
462
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000463 if correlation:
464 sfc_encap = True
sousaedu80135b92021-02-17 15:05:18 +0100465
466 sfi["sfc_encap"] = sfc_encap
467 del sfi["service_function_parameters"]
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000468
469 def __sf_os2mano(self, sf_list_dict):
470 """Transform the openstack format (Port Pair Group) to mano format (SF)
471 sf_list_dict can be a list of dict or a single dict
472 """
473 if isinstance(sf_list_dict, dict):
474 sf_list_ = [sf_list_dict]
475 elif isinstance(sf_list_dict, list):
476 sf_list_ = sf_list_dict
477 else:
sousaedu80135b92021-02-17 15:05:18 +0100478 raise TypeError("param sf_list_dict must be a list or a dictionary")
479
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000480 for sf in sf_list_:
sousaedu80135b92021-02-17 15:05:18 +0100481 del sf["port_pair_group_parameters"]
482 sf["sfis"] = sf["port_pairs"]
483 del sf["port_pairs"]
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000484
485 def __sfp_os2mano(self, sfp_list_dict):
486 """Transform the openstack format (Port Chain) to mano format (SFP)
487 sfp_list_dict can be a list of dict or a single dict
488 """
489 if isinstance(sfp_list_dict, dict):
490 sfp_list_ = [sfp_list_dict]
491 elif isinstance(sfp_list_dict, list):
492 sfp_list_ = sfp_list_dict
493 else:
sousaedu80135b92021-02-17 15:05:18 +0100494 raise TypeError("param sfp_list_dict must be a list or a dictionary")
495
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000496 for sfp in sfp_list_:
sousaedu80135b92021-02-17 15:05:18 +0100497 params = sfp.pop("chain_parameters")
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000498 sfc_encap = False
sousaedu80135b92021-02-17 15:05:18 +0100499
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000500 if params:
sousaedu80135b92021-02-17 15:05:18 +0100501 correlation = params.get("correlation")
502
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000503 if correlation:
504 sfc_encap = True
sousaedu80135b92021-02-17 15:05:18 +0100505
506 sfp["sfc_encap"] = sfc_encap
507 sfp["spi"] = sfp.pop("chain_id")
508 sfp["classifications"] = sfp.pop("flow_classifiers")
509 sfp["service_functions"] = sfp.pop("port_pair_groups")
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000510
511 # placeholder for now; read TODO note below
512 def _validate_classification(self, type, definition):
513 # only legacy_flow_classifier Type is supported at this point
514 return True
515 # TODO(igordcard): this method should be an abstract method of an
516 # abstract Classification class to be implemented by the specific
517 # Types. Also, abstract vimconnector should call the validation
518 # method before the implemented VIM connectors are called.
519
tiernoae4a8d12016-07-08 12:30:39 +0200520 def _format_exception(self, exception):
tierno69647792020-03-05 16:45:48 +0000521 """Transform a keystone, nova, neutron exception into a vimconn exception discovering the cause"""
tierno69647792020-03-05 16:45:48 +0000522 message_error = str(exception)
tierno5ad826a2020-08-11 11:19:44 +0000523 tip = ""
tiernode12f782019-04-05 12:46:42 +0000524
sousaedu80135b92021-02-17 15:05:18 +0100525 if isinstance(
526 exception,
527 (
528 neExceptions.NetworkNotFoundClient,
529 nvExceptions.NotFound,
530 ksExceptions.NotFound,
531 gl1Exceptions.HTTPNotFound,
532 ),
533 ):
534 raise vimconn.VimConnNotFoundException(
535 type(exception).__name__ + ": " + message_error
536 )
537 elif isinstance(
538 exception,
539 (
540 HTTPException,
541 gl1Exceptions.HTTPException,
542 gl1Exceptions.CommunicationError,
543 ConnectionError,
544 ksExceptions.ConnectionError,
545 neExceptions.ConnectionFailed,
546 ),
547 ):
tierno5ad826a2020-08-11 11:19:44 +0000548 if type(exception).__name__ == "SSLError":
549 tip = " (maybe option 'insecure' must be added to the VIM)"
sousaedu80135b92021-02-17 15:05:18 +0100550
551 raise vimconn.VimConnConnectionException(
552 "Invalid URL or credentials{}: {}".format(tip, message_error)
553 )
554 elif isinstance(
555 exception,
556 (
557 KeyError,
558 nvExceptions.BadRequest,
559 ksExceptions.BadRequest,
560 ),
561 ):
562 raise vimconn.VimConnException(
563 type(exception).__name__ + ": " + message_error
564 )
565 elif isinstance(
566 exception,
567 (
568 nvExceptions.ClientException,
569 ksExceptions.ClientException,
570 neExceptions.NeutronException,
571 ),
572 ):
573 raise vimconn.VimConnUnexpectedResponse(
574 type(exception).__name__ + ": " + message_error
575 )
tiernoae4a8d12016-07-08 12:30:39 +0200576 elif isinstance(exception, nvExceptions.Conflict):
sousaedu80135b92021-02-17 15:05:18 +0100577 raise vimconn.VimConnConflictException(
578 type(exception).__name__ + ": " + message_error
579 )
tierno72774862020-05-04 11:44:15 +0000580 elif isinstance(exception, vimconn.VimConnException):
tierno41a69812018-02-16 14:34:33 +0100581 raise exception
tiernof716aea2017-06-21 18:01:40 +0200582 else: # ()
tiernode12f782019-04-05 12:46:42 +0000583 self.logger.error("General Exception " + message_error, exc_info=True)
sousaedu80135b92021-02-17 15:05:18 +0100584
585 raise vimconn.VimConnConnectionException(
586 type(exception).__name__ + ": " + message_error
587 )
tiernoae4a8d12016-07-08 12:30:39 +0200588
tiernoa05b65a2019-02-01 12:30:27 +0000589 def _get_ids_from_name(self):
590 """
591 Obtain ids from name of tenant and security_groups. Store at self .security_groups_id"
592 :return: None
593 """
594 # get tenant_id if only tenant_name is supplied
595 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +0100596
tiernoa05b65a2019-02-01 12:30:27 +0000597 if not self.my_tenant_id:
sousaedu80135b92021-02-17 15:05:18 +0100598 raise vimconn.VimConnConnectionException(
599 "Error getting tenant information from name={} id={}".format(
600 self.tenant_name, self.tenant_id
601 )
602 )
603
604 if self.config.get("security_groups") and not self.security_groups_id:
tiernoa05b65a2019-02-01 12:30:27 +0000605 # convert from name to id
sousaedu80135b92021-02-17 15:05:18 +0100606 neutron_sg_list = self.neutron.list_security_groups(
607 tenant_id=self.my_tenant_id
608 )["security_groups"]
tiernoa05b65a2019-02-01 12:30:27 +0000609
610 self.security_groups_id = []
sousaedu80135b92021-02-17 15:05:18 +0100611 for sg in self.config.get("security_groups"):
tiernoa05b65a2019-02-01 12:30:27 +0000612 for neutron_sg in neutron_sg_list:
613 if sg in (neutron_sg["id"], neutron_sg["name"]):
614 self.security_groups_id.append(neutron_sg["id"])
615 break
616 else:
617 self.security_groups_id = None
sousaedu80135b92021-02-17 15:05:18 +0100618
619 raise vimconn.VimConnConnectionException(
620 "Not found security group {} for this tenant".format(sg)
621 )
tiernoa05b65a2019-02-01 12:30:27 +0000622
tierno5509c2e2019-07-04 16:23:20 +0000623 def check_vim_connectivity(self):
624 # just get network list to check connectivity and credentials
625 self.get_network_list(filter_dict={})
626
tiernoae4a8d12016-07-08 12:30:39 +0200627 def get_tenant_list(self, filter_dict={}):
tierno1ec592d2020-06-16 15:29:47 +0000628 """Obtain tenants of VIM
tiernoae4a8d12016-07-08 12:30:39 +0200629 filter_dict can contain the following keys:
630 name: filter by tenant name
631 id: filter by tenant uuid/id
632 <other VIM specific>
633 Returns the tenant list of dictionaries: [{'name':'<name>, 'id':'<id>, ...}, ...]
tierno1ec592d2020-06-16 15:29:47 +0000634 """
ahmadsa95baa272016-11-30 09:14:11 +0500635 self.logger.debug("Getting tenants from VIM filter: '%s'", str(filter_dict))
sousaedu80135b92021-02-17 15:05:18 +0100636
tiernoae4a8d12016-07-08 12:30:39 +0200637 try:
638 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +0100639
tiernof716aea2017-06-21 18:01:40 +0200640 if self.api_version3:
sousaedu80135b92021-02-17 15:05:18 +0100641 project_class_list = self.keystone.projects.list(
642 name=filter_dict.get("name")
643 )
ahmadsa95baa272016-11-30 09:14:11 +0500644 else:
tiernof716aea2017-06-21 18:01:40 +0200645 project_class_list = self.keystone.tenants.findall(**filter_dict)
sousaedu80135b92021-02-17 15:05:18 +0100646
tierno1ec592d2020-06-16 15:29:47 +0000647 project_list = []
sousaedu80135b92021-02-17 15:05:18 +0100648
ahmadsa95baa272016-11-30 09:14:11 +0500649 for project in project_class_list:
sousaedu80135b92021-02-17 15:05:18 +0100650 if filter_dict.get("id") and filter_dict["id"] != project.id:
tiernof716aea2017-06-21 18:01:40 +0200651 continue
sousaedu80135b92021-02-17 15:05:18 +0100652
ahmadsa95baa272016-11-30 09:14:11 +0500653 project_list.append(project.to_dict())
sousaedu80135b92021-02-17 15:05:18 +0100654
ahmadsa95baa272016-11-30 09:14:11 +0500655 return project_list
sousaedu80135b92021-02-17 15:05:18 +0100656 except (
657 ksExceptions.ConnectionError,
658 ksExceptions.ClientException,
659 ConnectionError,
660 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200661 self._format_exception(e)
662
663 def new_tenant(self, tenant_name, tenant_description):
tierno1ec592d2020-06-16 15:29:47 +0000664 """Adds a new tenant to openstack VIM. Returns the tenant identifier"""
tiernoae4a8d12016-07-08 12:30:39 +0200665 self.logger.debug("Adding a new tenant name: %s", tenant_name)
sousaedu80135b92021-02-17 15:05:18 +0100666
tiernoae4a8d12016-07-08 12:30:39 +0200667 try:
668 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +0100669
tiernof716aea2017-06-21 18:01:40 +0200670 if self.api_version3:
sousaedu80135b92021-02-17 15:05:18 +0100671 project = self.keystone.projects.create(
672 tenant_name,
673 self.config.get("project_domain_id", "default"),
674 description=tenant_description,
675 is_domain=False,
676 )
ahmadsa95baa272016-11-30 09:14:11 +0500677 else:
tiernof716aea2017-06-21 18:01:40 +0200678 project = self.keystone.tenants.create(tenant_name, tenant_description)
sousaedu80135b92021-02-17 15:05:18 +0100679
ahmadsa95baa272016-11-30 09:14:11 +0500680 return project.id
sousaedu80135b92021-02-17 15:05:18 +0100681 except (
682 ksExceptions.ConnectionError,
683 ksExceptions.ClientException,
684 ksExceptions.BadRequest,
685 ConnectionError,
686 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200687 self._format_exception(e)
688
689 def delete_tenant(self, tenant_id):
tierno1ec592d2020-06-16 15:29:47 +0000690 """Delete a tenant from openstack VIM. Returns the old tenant identifier"""
tiernoae4a8d12016-07-08 12:30:39 +0200691 self.logger.debug("Deleting tenant %s from VIM", tenant_id)
sousaedu80135b92021-02-17 15:05:18 +0100692
tiernoae4a8d12016-07-08 12:30:39 +0200693 try:
694 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +0100695
tiernof716aea2017-06-21 18:01:40 +0200696 if self.api_version3:
ahmadsa95baa272016-11-30 09:14:11 +0500697 self.keystone.projects.delete(tenant_id)
698 else:
699 self.keystone.tenants.delete(tenant_id)
sousaedu80135b92021-02-17 15:05:18 +0100700
tiernoae4a8d12016-07-08 12:30:39 +0200701 return tenant_id
sousaedu80135b92021-02-17 15:05:18 +0100702 except (
703 ksExceptions.ConnectionError,
704 ksExceptions.ClientException,
705 ksExceptions.NotFound,
706 ConnectionError,
707 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200708 self._format_exception(e)
ahmadsa95baa272016-11-30 09:14:11 +0500709
sousaedu80135b92021-02-17 15:05:18 +0100710 def new_network(
711 self,
712 net_name,
713 net_type,
714 ip_profile=None,
715 shared=False,
716 provider_network_profile=None,
717 ):
garciadeblasebd66722019-01-31 16:01:31 +0000718 """Adds a tenant network to VIM
719 Params:
720 'net_name': name of the network
721 'net_type': one of:
722 'bridge': overlay isolated network
723 'data': underlay E-LAN network for Passthrough and SRIOV interfaces
724 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces.
725 'ip_profile': is a dict containing the IP parameters of the network
726 'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented)
727 'subnet_address': ip_prefix_schema, that is X.X.X.X/Y
728 'gateway_address': (Optional) ip_schema, that is X.X.X.X
729 'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X]
730 'dhcp_enabled': True or False
731 'dhcp_start_address': ip_schema, first IP to grant
732 'dhcp_count': number of IPs to grant.
733 'shared': if this network can be seen/use by other tenants/organization
garciadeblas4af0d542020-02-18 16:01:13 +0100734 'provider_network_profile': (optional) contains {segmentation-id: vlan, network-type: vlan|vxlan,
735 physical-network: physnet-label}
garciadeblasebd66722019-01-31 16:01:31 +0000736 Returns a tuple with the network identifier and created_items, or raises an exception on error
737 created_items can be None or a dictionary where this method can include key-values that will be passed to
738 the method delete_network. Can be used to store created segments, created l2gw connections, etc.
739 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
740 as not present.
741 """
sousaedu80135b92021-02-17 15:05:18 +0100742 self.logger.debug(
743 "Adding a new network to VIM name '%s', type '%s'", net_name, net_type
744 )
garciadeblasebd66722019-01-31 16:01:31 +0000745 # self.logger.debug(">>>>>>>>>>>>>>>>>> IP profile %s", str(ip_profile))
kbsuba85c54d2019-10-17 16:30:32 +0000746
tierno7edb6752016-03-21 17:37:52 +0100747 try:
kbsuba85c54d2019-10-17 16:30:32 +0000748 vlan = None
sousaedu80135b92021-02-17 15:05:18 +0100749
kbsuba85c54d2019-10-17 16:30:32 +0000750 if provider_network_profile:
751 vlan = provider_network_profile.get("segmentation-id")
sousaedu80135b92021-02-17 15:05:18 +0100752
garciadeblasedca7b32016-09-29 14:01:52 +0000753 new_net = None
garciadeblasebd66722019-01-31 16:01:31 +0000754 created_items = {}
tierno7edb6752016-03-21 17:37:52 +0100755 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +0100756 network_dict = {"name": net_name, "admin_state_up": True}
757
tierno6869ae72020-01-09 17:37:34 +0000758 if net_type in ("data", "ptp"):
759 provider_physical_network = None
sousaedu80135b92021-02-17 15:05:18 +0100760
761 if provider_network_profile and provider_network_profile.get(
762 "physical-network"
763 ):
764 provider_physical_network = provider_network_profile.get(
765 "physical-network"
766 )
767
tierno6869ae72020-01-09 17:37:34 +0000768 # provider-network must be one of the dataplane_physcial_netowrk if this is a list. If it is string
769 # or not declared, just ignore the checking
sousaedu80135b92021-02-17 15:05:18 +0100770 if (
771 isinstance(
772 self.config.get("dataplane_physical_net"), (tuple, list)
773 )
774 and provider_physical_network
775 not in self.config["dataplane_physical_net"]
776 ):
tierno72774862020-05-04 11:44:15 +0000777 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +0100778 "Invalid parameter 'provider-network:physical-network' "
779 "for network creation. '{}' is not one of the declared "
780 "list at VIM_config:dataplane_physical_net".format(
781 provider_physical_network
782 )
783 )
784
785 # use the default dataplane_physical_net
786 if not provider_physical_network:
787 provider_physical_network = self.config.get(
788 "dataplane_physical_net"
789 )
790
tierno6869ae72020-01-09 17:37:34 +0000791 # if it is non empty list, use the first value. If it is a string use the value directly
sousaedu80135b92021-02-17 15:05:18 +0100792 if (
793 isinstance(provider_physical_network, (tuple, list))
794 and provider_physical_network
795 ):
tierno6869ae72020-01-09 17:37:34 +0000796 provider_physical_network = provider_physical_network[0]
797
798 if not provider_physical_network:
tierno5ad826a2020-08-11 11:19:44 +0000799 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +0100800 "missing information needed for underlay networks. Provide "
801 "'dataplane_physical_net' configuration at VIM or use the NS "
802 "instantiation parameter 'provider-network.physical-network'"
803 " for the VLD"
804 )
tierno6869ae72020-01-09 17:37:34 +0000805
sousaedu80135b92021-02-17 15:05:18 +0100806 if not self.config.get("multisegment_support"):
807 network_dict[
808 "provider:physical_network"
809 ] = provider_physical_network
810
811 if (
812 provider_network_profile
813 and "network-type" in provider_network_profile
814 ):
815 network_dict[
816 "provider:network_type"
817 ] = provider_network_profile["network-type"]
garciadeblas4af0d542020-02-18 16:01:13 +0100818 else:
sousaedu80135b92021-02-17 15:05:18 +0100819 network_dict["provider:network_type"] = self.config.get(
820 "dataplane_network_type", "vlan"
821 )
822
tierno6869ae72020-01-09 17:37:34 +0000823 if vlan:
824 network_dict["provider:segmentation_id"] = vlan
garciadeblasebd66722019-01-31 16:01:31 +0000825 else:
tierno6869ae72020-01-09 17:37:34 +0000826 # Multi-segment case
garciadeblasebd66722019-01-31 16:01:31 +0000827 segment_list = []
tierno6869ae72020-01-09 17:37:34 +0000828 segment1_dict = {
sousaedu80135b92021-02-17 15:05:18 +0100829 "provider:physical_network": "",
830 "provider:network_type": "vxlan",
tierno6869ae72020-01-09 17:37:34 +0000831 }
garciadeblasebd66722019-01-31 16:01:31 +0000832 segment_list.append(segment1_dict)
tierno6869ae72020-01-09 17:37:34 +0000833 segment2_dict = {
834 "provider:physical_network": provider_physical_network,
sousaedu80135b92021-02-17 15:05:18 +0100835 "provider:network_type": "vlan",
tierno6869ae72020-01-09 17:37:34 +0000836 }
sousaedu80135b92021-02-17 15:05:18 +0100837
tierno6869ae72020-01-09 17:37:34 +0000838 if vlan:
839 segment2_dict["provider:segmentation_id"] = vlan
sousaedu80135b92021-02-17 15:05:18 +0100840 elif self.config.get("multisegment_vlan_range"):
garciadeblasebd66722019-01-31 16:01:31 +0000841 vlanID = self._generate_multisegment_vlanID()
842 segment2_dict["provider:segmentation_id"] = vlanID
sousaedu80135b92021-02-17 15:05:18 +0100843
garciadeblasebd66722019-01-31 16:01:31 +0000844 # else
tierno72774862020-05-04 11:44:15 +0000845 # raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +0100846 # "You must provide "multisegment_vlan_range" at config dict before creating a multisegment
tierno1ec592d2020-06-16 15:29:47 +0000847 # network")
garciadeblasebd66722019-01-31 16:01:31 +0000848 segment_list.append(segment2_dict)
849 network_dict["segments"] = segment_list
kate721d79b2017-06-24 04:21:38 -0700850
tierno6869ae72020-01-09 17:37:34 +0000851 # VIO Specific Changes. It needs a concrete VLAN
852 if self.vim_type == "VIO" and vlan is None:
sousaedu80135b92021-02-17 15:05:18 +0100853 if self.config.get("dataplane_net_vlan_range") is None:
tierno72774862020-05-04 11:44:15 +0000854 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +0100855 "You must provide 'dataplane_net_vlan_range' in format "
856 "[start_ID - end_ID] at VIM_config for creating underlay "
857 "networks"
858 )
859
tierno6869ae72020-01-09 17:37:34 +0000860 network_dict["provider:segmentation_id"] = self._generate_vlanID()
kate721d79b2017-06-24 04:21:38 -0700861
garciadeblasebd66722019-01-31 16:01:31 +0000862 network_dict["shared"] = shared
sousaedu80135b92021-02-17 15:05:18 +0100863
anwarsff168192019-05-06 11:23:07 +0530864 if self.config.get("disable_network_port_security"):
865 network_dict["port_security_enabled"] = False
sousaedu80135b92021-02-17 15:05:18 +0100866
sousaedu2aa5f802021-06-17 15:39:29 +0100867 if self.config.get("neutron_availability_zone_hints"):
868 hints = self.config.get("neutron_availability_zone_hints")
869
870 if isinstance(hints, str):
871 hints = [hints]
872
873 network_dict["availability_zone_hints"] = hints
874
sousaedu80135b92021-02-17 15:05:18 +0100875 new_net = self.neutron.create_network({"network": network_dict})
garciadeblasebd66722019-01-31 16:01:31 +0000876 # print new_net
877 # create subnetwork, even if there is no profile
sousaedu80135b92021-02-17 15:05:18 +0100878
garciadeblas9f8456e2016-09-05 05:02:59 +0200879 if not ip_profile:
880 ip_profile = {}
sousaedu80135b92021-02-17 15:05:18 +0100881
882 if not ip_profile.get("subnet_address"):
tierno1ec592d2020-06-16 15:29:47 +0000883 # Fake subnet is required
garciadeblas2299e3b2017-01-26 14:35:55 +0000884 subnet_rand = random.randint(0, 255)
sousaedu80135b92021-02-17 15:05:18 +0100885 ip_profile["subnet_address"] = "192.168.{}.0/24".format(subnet_rand)
886
887 if "ip_version" not in ip_profile:
888 ip_profile["ip_version"] = "IPv4"
889
890 subnet = {
891 "name": net_name + "-subnet",
892 "network_id": new_net["network"]["id"],
893 "ip_version": 4 if ip_profile["ip_version"] == "IPv4" else 6,
894 "cidr": ip_profile["subnet_address"],
895 }
896
tiernoa1fb4462017-06-30 12:25:50 +0200897 # Gateway should be set to None if not needed. Otherwise openstack assigns one by default
sousaedu80135b92021-02-17 15:05:18 +0100898 if ip_profile.get("gateway_address"):
899 subnet["gateway_ip"] = ip_profile["gateway_address"]
tierno55d234c2018-07-04 18:29:21 +0200900 else:
sousaedu80135b92021-02-17 15:05:18 +0100901 subnet["gateway_ip"] = None
902
903 if ip_profile.get("dns_address"):
904 subnet["dns_nameservers"] = ip_profile["dns_address"].split(";")
905
906 if "dhcp_enabled" in ip_profile:
907 subnet["enable_dhcp"] = (
908 False
909 if ip_profile["dhcp_enabled"] == "false"
910 or ip_profile["dhcp_enabled"] is False
911 else True
912 )
913
914 if ip_profile.get("dhcp_start_address"):
915 subnet["allocation_pools"] = []
916 subnet["allocation_pools"].append(dict())
917 subnet["allocation_pools"][0]["start"] = ip_profile[
918 "dhcp_start_address"
919 ]
920
921 if ip_profile.get("dhcp_count"):
922 # parts = ip_profile["dhcp_start_address"].split(".")
tierno1ec592d2020-06-16 15:29:47 +0000923 # ip_int = (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3])
sousaedu80135b92021-02-17 15:05:18 +0100924 ip_int = int(netaddr.IPAddress(ip_profile["dhcp_start_address"]))
925 ip_int += ip_profile["dhcp_count"] - 1
garciadeblas9f8456e2016-09-05 05:02:59 +0200926 ip_str = str(netaddr.IPAddress(ip_int))
sousaedu80135b92021-02-17 15:05:18 +0100927 subnet["allocation_pools"][0]["end"] = ip_str
928
tierno1ec592d2020-06-16 15:29:47 +0000929 # self.logger.debug(">>>>>>>>>>>>>>>>>> Subnet: %s", str(subnet))
930 self.neutron.create_subnet({"subnet": subnet})
garciadeblasebd66722019-01-31 16:01:31 +0000931
sousaedu80135b92021-02-17 15:05:18 +0100932 if net_type == "data" and self.config.get("multisegment_support"):
933 if self.config.get("l2gw_support"):
garciadeblasebd66722019-01-31 16:01:31 +0000934 l2gw_list = self.neutron.list_l2_gateways().get("l2_gateways", ())
935 for l2gw in l2gw_list:
tierno1ec592d2020-06-16 15:29:47 +0000936 l2gw_conn = {
937 "l2_gateway_id": l2gw["id"],
938 "network_id": new_net["network"]["id"],
939 "segmentation_id": str(vlanID),
940 }
sousaedu80135b92021-02-17 15:05:18 +0100941 new_l2gw_conn = self.neutron.create_l2_gateway_connection(
942 {"l2_gateway_connection": l2gw_conn}
943 )
944 created_items[
945 "l2gwconn:"
946 + str(new_l2gw_conn["l2_gateway_connection"]["id"])
947 ] = True
948
garciadeblasebd66722019-01-31 16:01:31 +0000949 return new_net["network"]["id"], created_items
tierno41a69812018-02-16 14:34:33 +0100950 except Exception as e:
tierno1ec592d2020-06-16 15:29:47 +0000951 # delete l2gw connections (if any) before deleting the network
garciadeblasebd66722019-01-31 16:01:31 +0000952 for k, v in created_items.items():
953 if not v: # skip already deleted
954 continue
sousaedu80135b92021-02-17 15:05:18 +0100955
garciadeblasebd66722019-01-31 16:01:31 +0000956 try:
957 k_item, _, k_id = k.partition(":")
sousaedu80135b92021-02-17 15:05:18 +0100958
garciadeblasebd66722019-01-31 16:01:31 +0000959 if k_item == "l2gwconn":
960 self.neutron.delete_l2_gateway_connection(k_id)
961 except Exception as e2:
sousaedu80135b92021-02-17 15:05:18 +0100962 self.logger.error(
963 "Error deleting l2 gateway connection: {}: {}".format(
964 type(e2).__name__, e2
965 )
966 )
967
garciadeblasedca7b32016-09-29 14:01:52 +0000968 if new_net:
sousaedu80135b92021-02-17 15:05:18 +0100969 self.neutron.delete_network(new_net["network"]["id"])
970
tiernoae4a8d12016-07-08 12:30:39 +0200971 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100972
973 def get_network_list(self, filter_dict={}):
tierno1ec592d2020-06-16 15:29:47 +0000974 """Obtain tenant networks of VIM
tierno7edb6752016-03-21 17:37:52 +0100975 Filter_dict can be:
976 name: network name
977 id: network uuid
978 shared: boolean
979 tenant_id: tenant
980 admin_state_up: boolean
981 status: 'ACTIVE'
982 Returns the network list of dictionaries
tierno1ec592d2020-06-16 15:29:47 +0000983 """
tiernoae4a8d12016-07-08 12:30:39 +0200984 self.logger.debug("Getting network from VIM filter: '%s'", str(filter_dict))
sousaedu80135b92021-02-17 15:05:18 +0100985
tierno7edb6752016-03-21 17:37:52 +0100986 try:
987 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +0100988 filter_dict_os = filter_dict.copy()
sousaedu80135b92021-02-17 15:05:18 +0100989
tierno69b590e2018-03-13 18:52:23 +0100990 if self.api_version3 and "tenant_id" in filter_dict_os:
sousaedu80135b92021-02-17 15:05:18 +0100991 # TODO check
992 filter_dict_os["project_id"] = filter_dict_os.pop("tenant_id")
993
tierno69b590e2018-03-13 18:52:23 +0100994 net_dict = self.neutron.list_networks(**filter_dict_os)
tierno00e3df72017-11-29 17:20:13 +0100995 net_list = net_dict["networks"]
tierno7edb6752016-03-21 17:37:52 +0100996 self.__net_os2mano(net_list)
sousaedu80135b92021-02-17 15:05:18 +0100997
tiernoae4a8d12016-07-08 12:30:39 +0200998 return net_list
sousaedu80135b92021-02-17 15:05:18 +0100999 except (
1000 neExceptions.ConnectionFailed,
1001 ksExceptions.ClientException,
1002 neExceptions.NeutronException,
1003 ConnectionError,
1004 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001005 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001006
tiernoae4a8d12016-07-08 12:30:39 +02001007 def get_network(self, net_id):
tierno1ec592d2020-06-16 15:29:47 +00001008 """Obtain details of network from VIM
1009 Returns the network information from a network id"""
tiernoae4a8d12016-07-08 12:30:39 +02001010 self.logger.debug(" Getting tenant network %s from VIM", net_id)
tierno1ec592d2020-06-16 15:29:47 +00001011 filter_dict = {"id": net_id}
tiernoae4a8d12016-07-08 12:30:39 +02001012 net_list = self.get_network_list(filter_dict)
sousaedu80135b92021-02-17 15:05:18 +01001013
tierno1ec592d2020-06-16 15:29:47 +00001014 if len(net_list) == 0:
sousaedu80135b92021-02-17 15:05:18 +01001015 raise vimconn.VimConnNotFoundException(
1016 "Network '{}' not found".format(net_id)
1017 )
tierno1ec592d2020-06-16 15:29:47 +00001018 elif len(net_list) > 1:
sousaedu80135b92021-02-17 15:05:18 +01001019 raise vimconn.VimConnConflictException(
1020 "Found more than one network with this criteria"
1021 )
1022
tierno7edb6752016-03-21 17:37:52 +01001023 net = net_list[0]
tierno1ec592d2020-06-16 15:29:47 +00001024 subnets = []
1025 for subnet_id in net.get("subnets", ()):
tierno7edb6752016-03-21 17:37:52 +01001026 try:
1027 subnet = self.neutron.show_subnet(subnet_id)
1028 except Exception as e:
sousaedu80135b92021-02-17 15:05:18 +01001029 self.logger.error(
1030 "osconnector.get_network(): Error getting subnet %s %s"
1031 % (net_id, str(e))
1032 )
tiernoae4a8d12016-07-08 12:30:39 +02001033 subnet = {"id": subnet_id, "fault": str(e)}
sousaedu80135b92021-02-17 15:05:18 +01001034
tierno7edb6752016-03-21 17:37:52 +01001035 subnets.append(subnet)
sousaedu80135b92021-02-17 15:05:18 +01001036
tierno7edb6752016-03-21 17:37:52 +01001037 net["subnets"] = subnets
sousaedu80135b92021-02-17 15:05:18 +01001038 net["encapsulation"] = net.get("provider:network_type")
1039 net["encapsulation_type"] = net.get("provider:network_type")
1040 net["segmentation_id"] = net.get("provider:segmentation_id")
1041 net["encapsulation_id"] = net.get("provider:segmentation_id")
1042
tiernoae4a8d12016-07-08 12:30:39 +02001043 return net
tierno7edb6752016-03-21 17:37:52 +01001044
garciadeblasebd66722019-01-31 16:01:31 +00001045 def delete_network(self, net_id, created_items=None):
1046 """
1047 Removes a tenant network from VIM and its associated elements
1048 :param net_id: VIM identifier of the network, provided by method new_network
1049 :param created_items: dictionary with extra items to be deleted. provided by method new_network
1050 Returns the network identifier or raises an exception upon error or when network is not found
1051 """
tiernoae4a8d12016-07-08 12:30:39 +02001052 self.logger.debug("Deleting network '%s' from VIM", net_id)
sousaedu80135b92021-02-17 15:05:18 +01001053
tierno1ec592d2020-06-16 15:29:47 +00001054 if created_items is None:
garciadeblasebd66722019-01-31 16:01:31 +00001055 created_items = {}
sousaedu80135b92021-02-17 15:05:18 +01001056
tierno7edb6752016-03-21 17:37:52 +01001057 try:
1058 self._reload_connection()
tierno1ec592d2020-06-16 15:29:47 +00001059 # delete l2gw connections (if any) before deleting the network
garciadeblasebd66722019-01-31 16:01:31 +00001060 for k, v in created_items.items():
1061 if not v: # skip already deleted
1062 continue
sousaedu80135b92021-02-17 15:05:18 +01001063
garciadeblasebd66722019-01-31 16:01:31 +00001064 try:
1065 k_item, _, k_id = k.partition(":")
1066 if k_item == "l2gwconn":
1067 self.neutron.delete_l2_gateway_connection(k_id)
1068 except Exception as e:
sousaedu80135b92021-02-17 15:05:18 +01001069 self.logger.error(
1070 "Error deleting l2 gateway connection: {}: {}".format(
1071 type(e).__name__, e
1072 )
1073 )
1074
tierno1ec592d2020-06-16 15:29:47 +00001075 # delete VM ports attached to this networks before the network
tierno7edb6752016-03-21 17:37:52 +01001076 ports = self.neutron.list_ports(network_id=net_id)
sousaedu80135b92021-02-17 15:05:18 +01001077 for p in ports["ports"]:
tierno7edb6752016-03-21 17:37:52 +01001078 try:
1079 self.neutron.delete_port(p["id"])
1080 except Exception as e:
tiernoae4a8d12016-07-08 12:30:39 +02001081 self.logger.error("Error deleting port %s: %s", p["id"], str(e))
sousaedu80135b92021-02-17 15:05:18 +01001082
tierno7edb6752016-03-21 17:37:52 +01001083 self.neutron.delete_network(net_id)
sousaedu80135b92021-02-17 15:05:18 +01001084
tiernoae4a8d12016-07-08 12:30:39 +02001085 return net_id
sousaedu80135b92021-02-17 15:05:18 +01001086 except (
1087 neExceptions.ConnectionFailed,
1088 neExceptions.NetworkNotFoundClient,
1089 neExceptions.NeutronException,
1090 ksExceptions.ClientException,
1091 neExceptions.NeutronException,
1092 ConnectionError,
1093 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001094 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001095
tiernoae4a8d12016-07-08 12:30:39 +02001096 def refresh_nets_status(self, net_list):
tierno1ec592d2020-06-16 15:29:47 +00001097 """Get the status of the networks
sousaedu80135b92021-02-17 15:05:18 +01001098 Params: the list of network identifiers
1099 Returns a dictionary with:
1100 net_id: #VIM id of this network
1101 status: #Mandatory. Text with one of:
1102 # DELETED (not found at vim)
1103 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1104 # OTHER (Vim reported other status not understood)
1105 # ERROR (VIM indicates an ERROR status)
1106 # ACTIVE, INACTIVE, DOWN (admin down),
1107 # BUILD (on building process)
1108 #
1109 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1110 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
tierno1ec592d2020-06-16 15:29:47 +00001111 """
1112 net_dict = {}
sousaedu80135b92021-02-17 15:05:18 +01001113
tiernoae4a8d12016-07-08 12:30:39 +02001114 for net_id in net_list:
1115 net = {}
sousaedu80135b92021-02-17 15:05:18 +01001116
tiernoae4a8d12016-07-08 12:30:39 +02001117 try:
1118 net_vim = self.get_network(net_id)
sousaedu80135b92021-02-17 15:05:18 +01001119
1120 if net_vim["status"] in netStatus2manoFormat:
1121 net["status"] = netStatus2manoFormat[net_vim["status"]]
tiernoae4a8d12016-07-08 12:30:39 +02001122 else:
1123 net["status"] = "OTHER"
sousaedu80135b92021-02-17 15:05:18 +01001124 net["error_msg"] = "VIM status reported " + net_vim["status"]
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001125
sousaedu80135b92021-02-17 15:05:18 +01001126 if net["status"] == "ACTIVE" and not net_vim["admin_state_up"]:
1127 net["status"] = "DOWN"
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001128
sousaedu80135b92021-02-17 15:05:18 +01001129 net["vim_info"] = self.serialize(net_vim)
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001130
sousaedu80135b92021-02-17 15:05:18 +01001131 if net_vim.get("fault"): # TODO
1132 net["error_msg"] = str(net_vim["fault"])
tierno72774862020-05-04 11:44:15 +00001133 except vimconn.VimConnNotFoundException as e:
tiernoae4a8d12016-07-08 12:30:39 +02001134 self.logger.error("Exception getting net status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01001135 net["status"] = "DELETED"
1136 net["error_msg"] = str(e)
tierno72774862020-05-04 11:44:15 +00001137 except vimconn.VimConnException as e:
tiernoae4a8d12016-07-08 12:30:39 +02001138 self.logger.error("Exception getting net status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01001139 net["status"] = "VIM_ERROR"
1140 net["error_msg"] = str(e)
tiernoae4a8d12016-07-08 12:30:39 +02001141 net_dict[net_id] = net
1142 return net_dict
1143
1144 def get_flavor(self, flavor_id):
tierno1ec592d2020-06-16 15:29:47 +00001145 """Obtain flavor details from the VIM. Returns the flavor dict details"""
tiernoae4a8d12016-07-08 12:30:39 +02001146 self.logger.debug("Getting flavor '%s'", flavor_id)
sousaedu80135b92021-02-17 15:05:18 +01001147
tierno7edb6752016-03-21 17:37:52 +01001148 try:
1149 self._reload_connection()
1150 flavor = self.nova.flavors.find(id=flavor_id)
tierno1ec592d2020-06-16 15:29:47 +00001151 # TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
sousaedu80135b92021-02-17 15:05:18 +01001152
tiernoae4a8d12016-07-08 12:30:39 +02001153 return flavor.to_dict()
sousaedu80135b92021-02-17 15:05:18 +01001154 except (
1155 nvExceptions.NotFound,
1156 nvExceptions.ClientException,
1157 ksExceptions.ClientException,
1158 ConnectionError,
1159 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001160 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001161
tiernocf157a82017-01-30 14:07:06 +01001162 def get_flavor_id_from_data(self, flavor_dict):
1163 """Obtain flavor id that match the flavor description
sousaedu80135b92021-02-17 15:05:18 +01001164 Returns the flavor_id or raises a vimconnNotFoundException
1165 flavor_dict: contains the required ram, vcpus, disk
1166 If 'use_existing_flavors' is set to True at config, the closer flavor that provides same or more ram, vcpus
1167 and disk is returned. Otherwise a flavor with exactly same ram, vcpus and disk is returned or a
1168 vimconnNotFoundException is raised
tiernocf157a82017-01-30 14:07:06 +01001169 """
sousaedu80135b92021-02-17 15:05:18 +01001170 exact_match = False if self.config.get("use_existing_flavors") else True
1171
tiernocf157a82017-01-30 14:07:06 +01001172 try:
1173 self._reload_connection()
tiernoe26fc7a2017-05-30 14:43:03 +02001174 flavor_candidate_id = None
1175 flavor_candidate_data = (10000, 10000, 10000)
sousaedu80135b92021-02-17 15:05:18 +01001176 flavor_target = (
1177 flavor_dict["ram"],
1178 flavor_dict["vcpus"],
1179 flavor_dict["disk"],
1180 )
tiernoe26fc7a2017-05-30 14:43:03 +02001181 # numa=None
anwarsae5f52c2019-04-22 10:35:27 +05301182 extended = flavor_dict.get("extended", {})
1183 if extended:
tierno1ec592d2020-06-16 15:29:47 +00001184 # TODO
sousaedu80135b92021-02-17 15:05:18 +01001185 raise vimconn.VimConnNotFoundException(
1186 "Flavor with EPA still not implemented"
1187 )
tiernocf157a82017-01-30 14:07:06 +01001188 # if len(numas) > 1:
tierno72774862020-05-04 11:44:15 +00001189 # raise vimconn.VimConnNotFoundException("Cannot find any flavor with more than one numa")
tiernocf157a82017-01-30 14:07:06 +01001190 # numa=numas[0]
1191 # numas = extended.get("numas")
1192 for flavor in self.nova.flavors.list():
1193 epa = flavor.get_keys()
sousaedu80135b92021-02-17 15:05:18 +01001194
tiernocf157a82017-01-30 14:07:06 +01001195 if epa:
1196 continue
tiernoe26fc7a2017-05-30 14:43:03 +02001197 # TODO
sousaedu80135b92021-02-17 15:05:18 +01001198
tiernoe26fc7a2017-05-30 14:43:03 +02001199 flavor_data = (flavor.ram, flavor.vcpus, flavor.disk)
1200 if flavor_data == flavor_target:
1201 return flavor.id
sousaedu80135b92021-02-17 15:05:18 +01001202 elif (
1203 not exact_match
1204 and flavor_target < flavor_data < flavor_candidate_data
1205 ):
tiernoe26fc7a2017-05-30 14:43:03 +02001206 flavor_candidate_id = flavor.id
1207 flavor_candidate_data = flavor_data
sousaedu80135b92021-02-17 15:05:18 +01001208
tiernoe26fc7a2017-05-30 14:43:03 +02001209 if not exact_match and flavor_candidate_id:
1210 return flavor_candidate_id
sousaedu80135b92021-02-17 15:05:18 +01001211
1212 raise vimconn.VimConnNotFoundException(
1213 "Cannot find any flavor matching '{}'".format(flavor_dict)
1214 )
1215 except (
1216 nvExceptions.NotFound,
1217 nvExceptions.ClientException,
1218 ksExceptions.ClientException,
1219 ConnectionError,
1220 ) as e:
tiernocf157a82017-01-30 14:07:06 +01001221 self._format_exception(e)
1222
anwarsae5f52c2019-04-22 10:35:27 +05301223 def process_resource_quota(self, quota, prefix, extra_specs):
1224 """
1225 :param prefix:
borsatti8a2dda32019-12-18 15:08:57 +00001226 :param extra_specs:
anwarsae5f52c2019-04-22 10:35:27 +05301227 :return:
1228 """
sousaedu80135b92021-02-17 15:05:18 +01001229 if "limit" in quota:
1230 extra_specs["quota:" + prefix + "_limit"] = quota["limit"]
1231
1232 if "reserve" in quota:
1233 extra_specs["quota:" + prefix + "_reservation"] = quota["reserve"]
1234
1235 if "shares" in quota:
anwarsae5f52c2019-04-22 10:35:27 +05301236 extra_specs["quota:" + prefix + "_shares_level"] = "custom"
sousaedu80135b92021-02-17 15:05:18 +01001237 extra_specs["quota:" + prefix + "_shares_share"] = quota["shares"]
anwarsae5f52c2019-04-22 10:35:27 +05301238
tiernoae4a8d12016-07-08 12:30:39 +02001239 def new_flavor(self, flavor_data, change_name_if_used=True):
tierno1ec592d2020-06-16 15:29:47 +00001240 """Adds a tenant flavor to openstack VIM
1241 if change_name_if_used is True, it will change name in case of conflict, because it is not supported name
1242 repetition
tierno7edb6752016-03-21 17:37:52 +01001243 Returns the flavor identifier
tierno1ec592d2020-06-16 15:29:47 +00001244 """
tiernoae4a8d12016-07-08 12:30:39 +02001245 self.logger.debug("Adding flavor '%s'", str(flavor_data))
tierno1ec592d2020-06-16 15:29:47 +00001246 retry = 0
1247 max_retries = 3
tierno7edb6752016-03-21 17:37:52 +01001248 name_suffix = 0
sousaedu80135b92021-02-17 15:05:18 +01001249
anwarsc76a3ee2018-10-04 14:05:32 +05301250 try:
sousaedu80135b92021-02-17 15:05:18 +01001251 name = flavor_data["name"]
tierno1ec592d2020-06-16 15:29:47 +00001252 while retry < max_retries:
1253 retry += 1
anwarsc76a3ee2018-10-04 14:05:32 +05301254 try:
1255 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +01001256
anwarsc76a3ee2018-10-04 14:05:32 +05301257 if change_name_if_used:
tierno1ec592d2020-06-16 15:29:47 +00001258 # get used names
1259 fl_names = []
1260 fl = self.nova.flavors.list()
sousaedu80135b92021-02-17 15:05:18 +01001261
anwarsc76a3ee2018-10-04 14:05:32 +05301262 for f in fl:
1263 fl_names.append(f.name)
sousaedu80135b92021-02-17 15:05:18 +01001264
anwarsc76a3ee2018-10-04 14:05:32 +05301265 while name in fl_names:
1266 name_suffix += 1
sousaedu80135b92021-02-17 15:05:18 +01001267 name = flavor_data["name"] + "-" + str(name_suffix)
kate721d79b2017-06-24 04:21:38 -07001268
sousaedu80135b92021-02-17 15:05:18 +01001269 ram = flavor_data.get("ram", 64)
1270 vcpus = flavor_data.get("vcpus", 1)
tierno1ec592d2020-06-16 15:29:47 +00001271 extra_specs = {}
tierno7edb6752016-03-21 17:37:52 +01001272
anwarsc76a3ee2018-10-04 14:05:32 +05301273 extended = flavor_data.get("extended")
1274 if extended:
tierno1ec592d2020-06-16 15:29:47 +00001275 numas = extended.get("numas")
sousaedu80135b92021-02-17 15:05:18 +01001276
anwarsc76a3ee2018-10-04 14:05:32 +05301277 if numas:
1278 numa_nodes = len(numas)
sousaedu80135b92021-02-17 15:05:18 +01001279
anwarsc76a3ee2018-10-04 14:05:32 +05301280 if numa_nodes > 1:
1281 return -1, "Can not add flavor with more than one numa"
sousaedu80135b92021-02-17 15:05:18 +01001282
anwarsae5f52c2019-04-22 10:35:27 +05301283 extra_specs["hw:numa_nodes"] = str(numa_nodes)
1284 extra_specs["hw:mem_page_size"] = "large"
1285 extra_specs["hw:cpu_policy"] = "dedicated"
1286 extra_specs["hw:numa_mempolicy"] = "strict"
sousaedu80135b92021-02-17 15:05:18 +01001287
anwarsc76a3ee2018-10-04 14:05:32 +05301288 if self.vim_type == "VIO":
sousaedu80135b92021-02-17 15:05:18 +01001289 extra_specs[
1290 "vmware:extra_config"
1291 ] = '{"numa.nodeAffinity":"0"}'
anwarsae5f52c2019-04-22 10:35:27 +05301292 extra_specs["vmware:latency_sensitivity_level"] = "high"
sousaedu80135b92021-02-17 15:05:18 +01001293
anwarsc76a3ee2018-10-04 14:05:32 +05301294 for numa in numas:
tierno1ec592d2020-06-16 15:29:47 +00001295 # overwrite ram and vcpus
sousaedu80135b92021-02-17 15:05:18 +01001296 # check if key "memory" is present in numa else use ram value at flavor
1297 if "memory" in numa:
1298 ram = numa["memory"] * 1024
tierno1ec592d2020-06-16 15:29:47 +00001299 # See for reference: https://specs.openstack.org/openstack/nova-specs/specs/mitaka/
1300 # implemented/virt-driver-cpu-thread-pinning.html
garciadeblasfa35a722019-04-11 19:15:49 +02001301 extra_specs["hw:cpu_sockets"] = 1
sousaedu80135b92021-02-17 15:05:18 +01001302
1303 if "paired-threads" in numa:
1304 vcpus = numa["paired-threads"] * 2
1305 # cpu_thread_policy "require" implies that the compute node must have an
tierno1ec592d2020-06-16 15:29:47 +00001306 # STM architecture
anwarsae5f52c2019-04-22 10:35:27 +05301307 extra_specs["hw:cpu_thread_policy"] = "require"
1308 extra_specs["hw:cpu_policy"] = "dedicated"
sousaedu80135b92021-02-17 15:05:18 +01001309 elif "cores" in numa:
1310 vcpus = numa["cores"]
1311 # cpu_thread_policy "prefer" implies that the host must not have an SMT
tierno1ec592d2020-06-16 15:29:47 +00001312 # architecture, or a non-SMT architecture will be emulated
anwarsae5f52c2019-04-22 10:35:27 +05301313 extra_specs["hw:cpu_thread_policy"] = "isolate"
1314 extra_specs["hw:cpu_policy"] = "dedicated"
sousaedu80135b92021-02-17 15:05:18 +01001315 elif "threads" in numa:
1316 vcpus = numa["threads"]
tierno1ec592d2020-06-16 15:29:47 +00001317 # cpu_thread_policy "prefer" implies that the host may or may not have an SMT
1318 # architecture
anwarsae5f52c2019-04-22 10:35:27 +05301319 extra_specs["hw:cpu_thread_policy"] = "prefer"
1320 extra_specs["hw:cpu_policy"] = "dedicated"
anwarsc76a3ee2018-10-04 14:05:32 +05301321 # for interface in numa.get("interfaces",() ):
1322 # if interface["dedicated"]=="yes":
tierno1ec592d2020-06-16 15:29:47 +00001323 # raise vimconn.VimConnException("Passthrough interfaces are not supported
1324 # for the openstack connector", http_code=vimconn.HTTP_Service_Unavailable)
sousaedu80135b92021-02-17 15:05:18 +01001325 # #TODO, add the key 'pci_passthrough:alias"="<label at config>:<number ifaces>"'
tierno1ec592d2020-06-16 15:29:47 +00001326 # when a way to connect it is available
anwarsae5f52c2019-04-22 10:35:27 +05301327 elif extended.get("cpu-quota"):
sousaedu80135b92021-02-17 15:05:18 +01001328 self.process_resource_quota(
1329 extended.get("cpu-quota"), "cpu", extra_specs
1330 )
1331
anwarsae5f52c2019-04-22 10:35:27 +05301332 if extended.get("mem-quota"):
sousaedu80135b92021-02-17 15:05:18 +01001333 self.process_resource_quota(
1334 extended.get("mem-quota"), "memory", extra_specs
1335 )
1336
anwarsae5f52c2019-04-22 10:35:27 +05301337 if extended.get("vif-quota"):
sousaedu80135b92021-02-17 15:05:18 +01001338 self.process_resource_quota(
1339 extended.get("vif-quota"), "vif", extra_specs
1340 )
1341
anwarsae5f52c2019-04-22 10:35:27 +05301342 if extended.get("disk-io-quota"):
sousaedu80135b92021-02-17 15:05:18 +01001343 self.process_resource_quota(
1344 extended.get("disk-io-quota"), "disk_io", extra_specs
1345 )
1346
tierno1ec592d2020-06-16 15:29:47 +00001347 # create flavor
sousaedu80135b92021-02-17 15:05:18 +01001348 new_flavor = self.nova.flavors.create(
sousaeduf524da82021-11-22 14:02:17 +00001349 name=name,
1350 ram=ram,
1351 vcpus=vcpus,
1352 disk=flavor_data.get("disk", 0),
1353 ephemeral=flavor_data.get("ephemeral", 0),
sousaedu80135b92021-02-17 15:05:18 +01001354 is_public=flavor_data.get("is_public", True),
1355 )
tierno1ec592d2020-06-16 15:29:47 +00001356 # add metadata
anwarsae5f52c2019-04-22 10:35:27 +05301357 if extra_specs:
1358 new_flavor.set_keys(extra_specs)
sousaedu80135b92021-02-17 15:05:18 +01001359
anwarsc76a3ee2018-10-04 14:05:32 +05301360 return new_flavor.id
1361 except nvExceptions.Conflict as e:
1362 if change_name_if_used and retry < max_retries:
1363 continue
sousaedu80135b92021-02-17 15:05:18 +01001364
anwarsc76a3ee2018-10-04 14:05:32 +05301365 self._format_exception(e)
tierno1ec592d2020-06-16 15:29:47 +00001366 # except nvExceptions.BadRequest as e:
sousaedu80135b92021-02-17 15:05:18 +01001367 except (
1368 ksExceptions.ClientException,
1369 nvExceptions.ClientException,
1370 ConnectionError,
1371 KeyError,
1372 ) as e:
anwarsc76a3ee2018-10-04 14:05:32 +05301373 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001374
tierno1ec592d2020-06-16 15:29:47 +00001375 def delete_flavor(self, flavor_id):
sousaedu80135b92021-02-17 15:05:18 +01001376 """Deletes a tenant flavor from openstack VIM. Returns the old flavor_id"""
tiernoae4a8d12016-07-08 12:30:39 +02001377 try:
1378 self._reload_connection()
1379 self.nova.flavors.delete(flavor_id)
sousaedu80135b92021-02-17 15:05:18 +01001380
tiernoae4a8d12016-07-08 12:30:39 +02001381 return flavor_id
tierno1ec592d2020-06-16 15:29:47 +00001382 # except nvExceptions.BadRequest as e:
sousaedu80135b92021-02-17 15:05:18 +01001383 except (
1384 nvExceptions.NotFound,
1385 ksExceptions.ClientException,
1386 nvExceptions.ClientException,
1387 ConnectionError,
1388 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001389 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001390
tierno1ec592d2020-06-16 15:29:47 +00001391 def new_image(self, image_dict):
1392 """
tiernoae4a8d12016-07-08 12:30:39 +02001393 Adds a tenant image to VIM. imge_dict is a dictionary with:
1394 name: name
1395 disk_format: qcow2, vhd, vmdk, raw (by default), ...
1396 location: path or URI
1397 public: "yes" or "no"
1398 metadata: metadata of the image
1399 Returns the image_id
tierno1ec592d2020-06-16 15:29:47 +00001400 """
1401 retry = 0
1402 max_retries = 3
sousaedu80135b92021-02-17 15:05:18 +01001403
tierno1ec592d2020-06-16 15:29:47 +00001404 while retry < max_retries:
1405 retry += 1
tierno7edb6752016-03-21 17:37:52 +01001406 try:
1407 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +01001408
tierno1ec592d2020-06-16 15:29:47 +00001409 # determine format http://docs.openstack.org/developer/glance/formats.html
tierno7edb6752016-03-21 17:37:52 +01001410 if "disk_format" in image_dict:
tierno1ec592d2020-06-16 15:29:47 +00001411 disk_format = image_dict["disk_format"]
1412 else: # autodiscover based on extension
sousaedu80135b92021-02-17 15:05:18 +01001413 if image_dict["location"].endswith(".qcow2"):
tierno1ec592d2020-06-16 15:29:47 +00001414 disk_format = "qcow2"
sousaedu80135b92021-02-17 15:05:18 +01001415 elif image_dict["location"].endswith(".vhd"):
tierno1ec592d2020-06-16 15:29:47 +00001416 disk_format = "vhd"
sousaedu80135b92021-02-17 15:05:18 +01001417 elif image_dict["location"].endswith(".vmdk"):
tierno1ec592d2020-06-16 15:29:47 +00001418 disk_format = "vmdk"
sousaedu80135b92021-02-17 15:05:18 +01001419 elif image_dict["location"].endswith(".vdi"):
tierno1ec592d2020-06-16 15:29:47 +00001420 disk_format = "vdi"
sousaedu80135b92021-02-17 15:05:18 +01001421 elif image_dict["location"].endswith(".iso"):
tierno1ec592d2020-06-16 15:29:47 +00001422 disk_format = "iso"
sousaedu80135b92021-02-17 15:05:18 +01001423 elif image_dict["location"].endswith(".aki"):
tierno1ec592d2020-06-16 15:29:47 +00001424 disk_format = "aki"
sousaedu80135b92021-02-17 15:05:18 +01001425 elif image_dict["location"].endswith(".ari"):
tierno1ec592d2020-06-16 15:29:47 +00001426 disk_format = "ari"
sousaedu80135b92021-02-17 15:05:18 +01001427 elif image_dict["location"].endswith(".ami"):
tierno1ec592d2020-06-16 15:29:47 +00001428 disk_format = "ami"
tierno7edb6752016-03-21 17:37:52 +01001429 else:
tierno1ec592d2020-06-16 15:29:47 +00001430 disk_format = "raw"
sousaedu80135b92021-02-17 15:05:18 +01001431
1432 self.logger.debug(
1433 "new_image: '%s' loading from '%s'",
1434 image_dict["name"],
1435 image_dict["location"],
1436 )
shashankjain3c83a212018-10-04 13:05:46 +05301437 if self.vim_type == "VIO":
1438 container_format = "bare"
sousaedu80135b92021-02-17 15:05:18 +01001439 if "container_format" in image_dict:
1440 container_format = image_dict["container_format"]
1441
1442 new_image = self.glance.images.create(
1443 name=image_dict["name"],
1444 container_format=container_format,
1445 disk_format=disk_format,
1446 )
shashankjain3c83a212018-10-04 13:05:46 +05301447 else:
sousaedu80135b92021-02-17 15:05:18 +01001448 new_image = self.glance.images.create(name=image_dict["name"])
1449
1450 if image_dict["location"].startswith("http"):
tierno1beea862018-07-11 15:47:37 +02001451 # TODO there is not a method to direct download. It must be downloaded locally with requests
tierno72774862020-05-04 11:44:15 +00001452 raise vimconn.VimConnNotImplemented("Cannot create image from URL")
tierno1ec592d2020-06-16 15:29:47 +00001453 else: # local path
sousaedu80135b92021-02-17 15:05:18 +01001454 with open(image_dict["location"]) as fimage:
tierno1beea862018-07-11 15:47:37 +02001455 self.glance.images.upload(new_image.id, fimage)
sousaedu80135b92021-02-17 15:05:18 +01001456 # new_image = self.glancev1.images.create(name=image_dict["name"], is_public=
1457 # image_dict.get("public","yes")=="yes",
tierno1beea862018-07-11 15:47:37 +02001458 # container_format="bare", data=fimage, disk_format=disk_format)
sousaedu80135b92021-02-17 15:05:18 +01001459
1460 metadata_to_load = image_dict.get("metadata")
1461
1462 # TODO location is a reserved word for current openstack versions. fixed for VIO please check
tierno1ec592d2020-06-16 15:29:47 +00001463 # for openstack
shashankjain3c83a212018-10-04 13:05:46 +05301464 if self.vim_type == "VIO":
sousaedu80135b92021-02-17 15:05:18 +01001465 metadata_to_load["upload_location"] = image_dict["location"]
shashankjain3c83a212018-10-04 13:05:46 +05301466 else:
sousaedu80135b92021-02-17 15:05:18 +01001467 metadata_to_load["location"] = image_dict["location"]
1468
tierno1beea862018-07-11 15:47:37 +02001469 self.glance.images.update(new_image.id, **metadata_to_load)
sousaedu80135b92021-02-17 15:05:18 +01001470
tiernoae4a8d12016-07-08 12:30:39 +02001471 return new_image.id
sousaedu80135b92021-02-17 15:05:18 +01001472 except (
1473 nvExceptions.Conflict,
1474 ksExceptions.ClientException,
1475 nvExceptions.ClientException,
1476 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001477 self._format_exception(e)
sousaedu80135b92021-02-17 15:05:18 +01001478 except (
1479 HTTPException,
1480 gl1Exceptions.HTTPException,
1481 gl1Exceptions.CommunicationError,
1482 ConnectionError,
1483 ) as e:
tierno1ec592d2020-06-16 15:29:47 +00001484 if retry == max_retries:
tiernoae4a8d12016-07-08 12:30:39 +02001485 continue
sousaedu80135b92021-02-17 15:05:18 +01001486
tiernoae4a8d12016-07-08 12:30:39 +02001487 self._format_exception(e)
tierno1ec592d2020-06-16 15:29:47 +00001488 except IOError as e: # can not open the file
sousaedu80135b92021-02-17 15:05:18 +01001489 raise vimconn.VimConnConnectionException(
1490 "{}: {} for {}".format(type(e).__name__, e, image_dict["location"]),
1491 http_code=vimconn.HTTP_Bad_Request,
1492 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001493
tiernoae4a8d12016-07-08 12:30:39 +02001494 def delete_image(self, image_id):
sousaedu80135b92021-02-17 15:05:18 +01001495 """Deletes a tenant image from openstack VIM. Returns the old id"""
tiernoae4a8d12016-07-08 12:30:39 +02001496 try:
1497 self._reload_connection()
tierno1beea862018-07-11 15:47:37 +02001498 self.glance.images.delete(image_id)
sousaedu80135b92021-02-17 15:05:18 +01001499
tiernoae4a8d12016-07-08 12:30:39 +02001500 return image_id
sousaedu80135b92021-02-17 15:05:18 +01001501 except (
1502 nvExceptions.NotFound,
1503 ksExceptions.ClientException,
1504 nvExceptions.ClientException,
1505 gl1Exceptions.CommunicationError,
1506 gl1Exceptions.HTTPNotFound,
1507 ConnectionError,
1508 ) as e: # TODO remove
tiernoae4a8d12016-07-08 12:30:39 +02001509 self._format_exception(e)
1510
1511 def get_image_id_from_path(self, path):
tierno1ec592d2020-06-16 15:29:47 +00001512 """Get the image id from image path in the VIM database. Returns the image_id"""
tiernoae4a8d12016-07-08 12:30:39 +02001513 try:
1514 self._reload_connection()
tierno1beea862018-07-11 15:47:37 +02001515 images = self.glance.images.list()
sousaedu80135b92021-02-17 15:05:18 +01001516
tiernoae4a8d12016-07-08 12:30:39 +02001517 for image in images:
tierno1ec592d2020-06-16 15:29:47 +00001518 if image.metadata.get("location") == path:
tiernoae4a8d12016-07-08 12:30:39 +02001519 return image.id
sousaedu80135b92021-02-17 15:05:18 +01001520
1521 raise vimconn.VimConnNotFoundException(
1522 "image with location '{}' not found".format(path)
1523 )
1524 except (
1525 ksExceptions.ClientException,
1526 nvExceptions.ClientException,
1527 gl1Exceptions.CommunicationError,
1528 ConnectionError,
1529 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001530 self._format_exception(e)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001531
garciadeblasb69fa9f2016-09-28 12:04:10 +02001532 def get_image_list(self, filter_dict={}):
tierno1ec592d2020-06-16 15:29:47 +00001533 """Obtain tenant images from VIM
garciadeblasb69fa9f2016-09-28 12:04:10 +02001534 Filter_dict can be:
1535 id: image id
1536 name: image name
1537 checksum: image checksum
1538 Returns the image list of dictionaries:
1539 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1540 List can be empty
tierno1ec592d2020-06-16 15:29:47 +00001541 """
garciadeblasb69fa9f2016-09-28 12:04:10 +02001542 self.logger.debug("Getting image list from VIM filter: '%s'", str(filter_dict))
sousaedu80135b92021-02-17 15:05:18 +01001543
garciadeblasb69fa9f2016-09-28 12:04:10 +02001544 try:
1545 self._reload_connection()
tierno1ec592d2020-06-16 15:29:47 +00001546 # filter_dict_os = filter_dict.copy()
1547 # First we filter by the available filter fields: name, id. The others are removed.
tierno1beea862018-07-11 15:47:37 +02001548 image_list = self.glance.images.list()
garciadeblasb69fa9f2016-09-28 12:04:10 +02001549 filtered_list = []
sousaedu80135b92021-02-17 15:05:18 +01001550
garciadeblasb69fa9f2016-09-28 12:04:10 +02001551 for image in image_list:
tierno3cb8dc32017-10-24 18:13:19 +02001552 try:
tierno1beea862018-07-11 15:47:37 +02001553 if filter_dict.get("name") and image["name"] != filter_dict["name"]:
1554 continue
sousaedu80135b92021-02-17 15:05:18 +01001555
tierno1beea862018-07-11 15:47:37 +02001556 if filter_dict.get("id") and image["id"] != filter_dict["id"]:
1557 continue
sousaedu80135b92021-02-17 15:05:18 +01001558
1559 if (
1560 filter_dict.get("checksum")
1561 and image["checksum"] != filter_dict["checksum"]
1562 ):
tierno1beea862018-07-11 15:47:37 +02001563 continue
1564
1565 filtered_list.append(image.copy())
tierno3cb8dc32017-10-24 18:13:19 +02001566 except gl1Exceptions.HTTPNotFound:
1567 pass
sousaedu80135b92021-02-17 15:05:18 +01001568
garciadeblasb69fa9f2016-09-28 12:04:10 +02001569 return filtered_list
sousaedu80135b92021-02-17 15:05:18 +01001570 except (
1571 ksExceptions.ClientException,
1572 nvExceptions.ClientException,
1573 gl1Exceptions.CommunicationError,
1574 ConnectionError,
1575 ) as e:
garciadeblasb69fa9f2016-09-28 12:04:10 +02001576 self._format_exception(e)
1577
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001578 def __wait_for_vm(self, vm_id, status):
1579 """wait until vm is in the desired status and return True.
1580 If the VM gets in ERROR status, return false.
1581 If the timeout is reached generate an exception"""
1582 elapsed_time = 0
1583 while elapsed_time < server_timeout:
1584 vm_status = self.nova.servers.get(vm_id).status
sousaedu80135b92021-02-17 15:05:18 +01001585
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001586 if vm_status == status:
1587 return True
sousaedu80135b92021-02-17 15:05:18 +01001588
1589 if vm_status == "ERROR":
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001590 return False
sousaedu80135b92021-02-17 15:05:18 +01001591
tierno1df468d2018-07-06 14:25:16 +02001592 time.sleep(5)
1593 elapsed_time += 5
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001594
1595 # if we exceeded the timeout rollback
1596 if elapsed_time >= server_timeout:
sousaedu80135b92021-02-17 15:05:18 +01001597 raise vimconn.VimConnException(
1598 "Timeout waiting for instance " + vm_id + " to get " + status,
1599 http_code=vimconn.HTTP_Request_Timeout,
1600 )
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001601
mirabal29356312017-07-27 12:21:22 +02001602 def _get_openstack_availablity_zones(self):
1603 """
1604 Get from openstack availability zones available
1605 :return:
1606 """
1607 try:
1608 openstack_availability_zone = self.nova.availability_zones.list()
sousaedu80135b92021-02-17 15:05:18 +01001609 openstack_availability_zone = [
1610 str(zone.zoneName)
1611 for zone in openstack_availability_zone
1612 if zone.zoneName != "internal"
1613 ]
1614
mirabal29356312017-07-27 12:21:22 +02001615 return openstack_availability_zone
tierno1ec592d2020-06-16 15:29:47 +00001616 except Exception:
mirabal29356312017-07-27 12:21:22 +02001617 return None
1618
1619 def _set_availablity_zones(self):
1620 """
1621 Set vim availablity zone
1622 :return:
1623 """
sousaedu80135b92021-02-17 15:05:18 +01001624 if "availability_zone" in self.config:
1625 vim_availability_zones = self.config.get("availability_zone")
mirabal29356312017-07-27 12:21:22 +02001626
mirabal29356312017-07-27 12:21:22 +02001627 if isinstance(vim_availability_zones, str):
1628 self.availability_zone = [vim_availability_zones]
1629 elif isinstance(vim_availability_zones, list):
1630 self.availability_zone = vim_availability_zones
1631 else:
1632 self.availability_zone = self._get_openstack_availablity_zones()
1633
sousaedu80135b92021-02-17 15:05:18 +01001634 def _get_vm_availability_zone(
1635 self, availability_zone_index, availability_zone_list
1636 ):
mirabal29356312017-07-27 12:21:22 +02001637 """
tierno5a3273c2017-08-29 11:43:46 +02001638 Return thge availability zone to be used by the created VM.
1639 :return: The VIM availability zone to be used or None
mirabal29356312017-07-27 12:21:22 +02001640 """
tierno5a3273c2017-08-29 11:43:46 +02001641 if availability_zone_index is None:
sousaedu80135b92021-02-17 15:05:18 +01001642 if not self.config.get("availability_zone"):
tierno5a3273c2017-08-29 11:43:46 +02001643 return None
sousaedu80135b92021-02-17 15:05:18 +01001644 elif isinstance(self.config.get("availability_zone"), str):
1645 return self.config["availability_zone"]
tierno5a3273c2017-08-29 11:43:46 +02001646 else:
1647 # TODO consider using a different parameter at config for default AV and AV list match
sousaedu80135b92021-02-17 15:05:18 +01001648 return self.config["availability_zone"][0]
mirabal29356312017-07-27 12:21:22 +02001649
tierno5a3273c2017-08-29 11:43:46 +02001650 vim_availability_zones = self.availability_zone
1651 # check if VIM offer enough availability zones describe in the VNFD
sousaedu80135b92021-02-17 15:05:18 +01001652 if vim_availability_zones and len(availability_zone_list) <= len(
1653 vim_availability_zones
1654 ):
tierno5a3273c2017-08-29 11:43:46 +02001655 # check if all the names of NFV AV match VIM AV names
1656 match_by_index = False
1657 for av in availability_zone_list:
1658 if av not in vim_availability_zones:
1659 match_by_index = True
1660 break
sousaedu80135b92021-02-17 15:05:18 +01001661
tierno5a3273c2017-08-29 11:43:46 +02001662 if match_by_index:
1663 return vim_availability_zones[availability_zone_index]
1664 else:
1665 return availability_zone_list[availability_zone_index]
mirabal29356312017-07-27 12:21:22 +02001666 else:
sousaedu80135b92021-02-17 15:05:18 +01001667 raise vimconn.VimConnConflictException(
1668 "No enough availability zones at VIM for this deployment"
1669 )
mirabal29356312017-07-27 12:21:22 +02001670
sousaedu80135b92021-02-17 15:05:18 +01001671 def new_vminstance(
1672 self,
1673 name,
1674 description,
1675 start,
1676 image_id,
1677 flavor_id,
1678 net_list,
1679 cloud_config=None,
1680 disk_list=None,
1681 availability_zone_index=None,
1682 availability_zone_list=None,
1683 ):
tierno98e909c2017-10-14 13:27:03 +02001684 """Adds a VM instance to VIM
tierno7edb6752016-03-21 17:37:52 +01001685 Params:
1686 start: indicates if VM must start or boot in pause mode. Ignored
1687 image_id,flavor_id: iamge and flavor uuid
1688 net_list: list of interfaces, each one is a dictionary with:
1689 name:
1690 net_id: network uuid to connect
1691 vpci: virtual vcpi to assign, ignored because openstack lack #TODO
1692 model: interface model, ignored #TODO
1693 mac_address: used for SR-IOV ifaces #TODO for other types
1694 use: 'data', 'bridge', 'mgmt'
tierno66eba6e2017-11-10 17:09:18 +01001695 type: 'virtual', 'PCI-PASSTHROUGH'('PF'), 'SR-IOV'('VF'), 'VFnotShared'
tierno7edb6752016-03-21 17:37:52 +01001696 vim_id: filled/added by this function
ahmadsaf853d452016-12-22 11:33:47 +05001697 floating_ip: True/False (or it can be None)
tierno70eeb182020-10-19 16:38:00 +00001698 port_security: True/False
tierno41a69812018-02-16 14:34:33 +01001699 'cloud_config': (optional) dictionary with:
tierno1d213f42020-04-24 14:02:51 +00001700 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
1701 'users': (optional) list of users to be inserted, each item is a dict with:
1702 'name': (mandatory) user name,
1703 'key-pairs': (optional) list of strings with the public key to be inserted to the user
1704 'user-data': (optional) string is a text script to be passed directly to cloud-init
1705 'config-files': (optional). List of files to be transferred. Each item is a dict with:
1706 'dest': (mandatory) string with the destination absolute path
1707 'encoding': (optional, by default text). Can be one of:
1708 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
1709 'content' (mandatory): string with the content of the file
1710 'permissions': (optional) string with file permissions, typically octal notation '0644'
1711 'owner': (optional) file owner, string with the format 'owner:group'
1712 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk)
mirabal29356312017-07-27 12:21:22 +02001713 'disk_list': (optional) list with additional disks to the VM. Each item is a dict with:
1714 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted
1715 'size': (mandatory) string with the size of the disk in GB
tierno1df468d2018-07-06 14:25:16 +02001716 'vim_id' (optional) should use this existing volume id
tierno5a3273c2017-08-29 11:43:46 +02001717 availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV required
1718 availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if
1719 availability_zone_index is None
tierno7edb6752016-03-21 17:37:52 +01001720 #TODO ip, security groups
tierno98e909c2017-10-14 13:27:03 +02001721 Returns a tuple with the instance identifier and created_items or raises an exception on error
1722 created_items can be None or a dictionary where this method can include key-values that will be passed to
1723 the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc.
1724 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
1725 as not present.
1726 """
sousaedu80135b92021-02-17 15:05:18 +01001727 self.logger.debug(
1728 "new_vminstance input: image='%s' flavor='%s' nics='%s'",
1729 image_id,
1730 flavor_id,
1731 str(net_list),
1732 )
1733
tierno7edb6752016-03-21 17:37:52 +01001734 try:
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001735 server = None
tierno98e909c2017-10-14 13:27:03 +02001736 created_items = {}
tiernob0b9dab2017-10-14 14:25:20 +02001737 # metadata = {}
tierno98e909c2017-10-14 13:27:03 +02001738 net_list_vim = []
tierno1ec592d2020-06-16 15:29:47 +00001739 external_network = []
1740 # ^list of external networks to be connected to instance, later on used to create floating_ip
sousaedu80135b92021-02-17 15:05:18 +01001741 no_secured_ports = [] # List of port-is with port-security disabled
tierno7edb6752016-03-21 17:37:52 +01001742 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +01001743 # metadata_vpci = {} # For a specific neutron plugin
tiernob84cbdc2017-07-07 14:30:30 +02001744 block_device_mapping = None
tiernoa05b65a2019-02-01 12:30:27 +00001745
tierno7edb6752016-03-21 17:37:52 +01001746 for net in net_list:
sousaedu80135b92021-02-17 15:05:18 +01001747 if not net.get("net_id"): # skip non connected iface
tierno7edb6752016-03-21 17:37:52 +01001748 continue
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001749
tiernoa05b65a2019-02-01 12:30:27 +00001750 port_dict = {
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001751 "network_id": net["net_id"],
1752 "name": net.get("name"),
sousaedu80135b92021-02-17 15:05:18 +01001753 "admin_state_up": True,
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001754 }
sousaedu80135b92021-02-17 15:05:18 +01001755
1756 if (
1757 self.config.get("security_groups")
1758 and net.get("port_security") is not False
1759 and not self.config.get("no_port_security_extension")
1760 ):
tiernoa05b65a2019-02-01 12:30:27 +00001761 if not self.security_groups_id:
1762 self._get_ids_from_name()
sousaedu80135b92021-02-17 15:05:18 +01001763
tiernoa05b65a2019-02-01 12:30:27 +00001764 port_dict["security_groups"] = self.security_groups_id
1765
tierno1ec592d2020-06-16 15:29:47 +00001766 if net["type"] == "virtual":
tiernob0b9dab2017-10-14 14:25:20 +02001767 pass
1768 # if "vpci" in net:
1769 # metadata_vpci[ net["net_id"] ] = [[ net["vpci"], "" ]]
tierno66eba6e2017-11-10 17:09:18 +01001770 elif net["type"] == "VF" or net["type"] == "SR-IOV": # for VF
tiernob0b9dab2017-10-14 14:25:20 +02001771 # if "vpci" in net:
1772 # if "VF" not in metadata_vpci:
1773 # metadata_vpci["VF"]=[]
1774 # metadata_vpci["VF"].append([ net["vpci"], "" ])
tierno1ec592d2020-06-16 15:29:47 +00001775 port_dict["binding:vnic_type"] = "direct"
sousaedu80135b92021-02-17 15:05:18 +01001776
tiernob0b9dab2017-10-14 14:25:20 +02001777 # VIO specific Changes
kate721d79b2017-06-24 04:21:38 -07001778 if self.vim_type == "VIO":
tiernob0b9dab2017-10-14 14:25:20 +02001779 # Need to create port with port_security_enabled = False and no-security-groups
tierno1ec592d2020-06-16 15:29:47 +00001780 port_dict["port_security_enabled"] = False
1781 port_dict["provider_security_groups"] = []
1782 port_dict["security_groups"] = []
sousaedu80135b92021-02-17 15:05:18 +01001783 else: # For PT PCI-PASSTHROUGH
tiernob0b9dab2017-10-14 14:25:20 +02001784 # if "vpci" in net:
1785 # if "PF" not in metadata_vpci:
1786 # metadata_vpci["PF"]=[]
1787 # metadata_vpci["PF"].append([ net["vpci"], "" ])
tierno1ec592d2020-06-16 15:29:47 +00001788 port_dict["binding:vnic_type"] = "direct-physical"
sousaedu80135b92021-02-17 15:05:18 +01001789
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001790 if not port_dict["name"]:
tierno1ec592d2020-06-16 15:29:47 +00001791 port_dict["name"] = name
sousaedu80135b92021-02-17 15:05:18 +01001792
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001793 if net.get("mac_address"):
tierno1ec592d2020-06-16 15:29:47 +00001794 port_dict["mac_address"] = net["mac_address"]
sousaedu80135b92021-02-17 15:05:18 +01001795
tierno41a69812018-02-16 14:34:33 +01001796 if net.get("ip_address"):
sousaedu80135b92021-02-17 15:05:18 +01001797 port_dict["fixed_ips"] = [{"ip_address": net["ip_address"]}]
1798 # TODO add "subnet_id": <subnet_id>
1799
tierno1ec592d2020-06-16 15:29:47 +00001800 new_port = self.neutron.create_port({"port": port_dict})
tierno00e3df72017-11-29 17:20:13 +01001801 created_items["port:" + str(new_port["port"]["id"])] = True
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001802 net["mac_adress"] = new_port["port"]["mac_address"]
1803 net["vim_id"] = new_port["port"]["id"]
tiernob84cbdc2017-07-07 14:30:30 +02001804 # if try to use a network without subnetwork, it will return a emtpy list
1805 fixed_ips = new_port["port"].get("fixed_ips")
sousaedu80135b92021-02-17 15:05:18 +01001806
tiernob84cbdc2017-07-07 14:30:30 +02001807 if fixed_ips:
1808 net["ip"] = fixed_ips[0].get("ip_address")
1809 else:
1810 net["ip"] = None
montesmoreno994a29d2017-08-22 11:23:06 +02001811
1812 port = {"port-id": new_port["port"]["id"]}
1813 if float(self.nova.api_version.get_string()) >= 2.32:
1814 port["tag"] = new_port["port"]["name"]
sousaedu80135b92021-02-17 15:05:18 +01001815
montesmoreno994a29d2017-08-22 11:23:06 +02001816 net_list_vim.append(port)
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001817
sousaedu80135b92021-02-17 15:05:18 +01001818 if net.get("floating_ip", False):
1819 net["exit_on_floating_ip_error"] = True
ahmadsaf853d452016-12-22 11:33:47 +05001820 external_network.append(net)
sousaedu80135b92021-02-17 15:05:18 +01001821 elif net["use"] == "mgmt" and self.config.get("use_floating_ip"):
1822 net["exit_on_floating_ip_error"] = False
tiernof8383b82017-01-18 15:49:48 +01001823 external_network.append(net)
sousaedu80135b92021-02-17 15:05:18 +01001824 net["floating_ip"] = self.config.get("use_floating_ip")
tiernof8383b82017-01-18 15:49:48 +01001825
tierno1ec592d2020-06-16 15:29:47 +00001826 # If port security is disabled when the port has not yet been attached to the VM, then all vm traffic
1827 # is dropped.
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001828 # As a workaround we wait until the VM is active and then disable the port-security
sousaedu80135b92021-02-17 15:05:18 +01001829 if net.get("port_security") is False and not self.config.get(
1830 "no_port_security_extension"
1831 ):
1832 no_secured_ports.append(
1833 (
1834 new_port["port"]["id"],
1835 net.get("port_security_disable_strategy"),
1836 )
1837 )
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001838
tiernob0b9dab2017-10-14 14:25:20 +02001839 # if metadata_vpci:
1840 # metadata = {"pci_assignement": json.dumps(metadata_vpci)}
1841 # if len(metadata["pci_assignement"]) >255:
1842 # #limit the metadata size
1843 # #metadata["pci_assignement"] = metadata["pci_assignement"][0:255]
1844 # self.logger.warn("Metadata deleted since it exceeds the expected length (255) ")
1845 # metadata = {}
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001846
sousaedu80135b92021-02-17 15:05:18 +01001847 self.logger.debug(
1848 "name '%s' image_id '%s'flavor_id '%s' net_list_vim '%s' description '%s'",
1849 name,
1850 image_id,
1851 flavor_id,
1852 str(net_list_vim),
1853 description,
1854 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001855
tierno98e909c2017-10-14 13:27:03 +02001856 # cloud config
tierno0a1437e2017-10-02 00:17:43 +02001857 config_drive, userdata = self._create_user_data(cloud_config)
montesmoreno0c8def02016-12-22 12:16:23 +00001858
tierno98e909c2017-10-14 13:27:03 +02001859 # Create additional volumes in case these are present in disk_list
sousaedu80135b92021-02-17 15:05:18 +01001860 base_disk_index = ord("b")
tierno1df468d2018-07-06 14:25:16 +02001861 if disk_list:
tiernob84cbdc2017-07-07 14:30:30 +02001862 block_device_mapping = {}
montesmoreno0c8def02016-12-22 12:16:23 +00001863 for disk in disk_list:
sousaedu80135b92021-02-17 15:05:18 +01001864 if disk.get("vim_id"):
1865 block_device_mapping["_vd" + chr(base_disk_index)] = disk[
1866 "vim_id"
1867 ]
montesmoreno0c8def02016-12-22 12:16:23 +00001868 else:
sousaedu80135b92021-02-17 15:05:18 +01001869 if "image_id" in disk:
1870 volume = self.cinder.volumes.create(
1871 size=disk["size"],
1872 name=name + "_vd" + chr(base_disk_index),
1873 imageRef=disk["image_id"],
1874 )
tierno1df468d2018-07-06 14:25:16 +02001875 else:
sousaedu80135b92021-02-17 15:05:18 +01001876 volume = self.cinder.volumes.create(
1877 size=disk["size"],
1878 name=name + "_vd" + chr(base_disk_index),
1879 )
1880
tierno1df468d2018-07-06 14:25:16 +02001881 created_items["volume:" + str(volume.id)] = True
sousaedu80135b92021-02-17 15:05:18 +01001882 block_device_mapping["_vd" + chr(base_disk_index)] = volume.id
1883
montesmoreno0c8def02016-12-22 12:16:23 +00001884 base_disk_index += 1
1885
tierno1df468d2018-07-06 14:25:16 +02001886 # Wait until created volumes are with status available
montesmoreno0c8def02016-12-22 12:16:23 +00001887 elapsed_time = 0
tierno1df468d2018-07-06 14:25:16 +02001888 while elapsed_time < volume_timeout:
1889 for created_item in created_items:
1890 v, _, volume_id = created_item.partition(":")
sousaedu80135b92021-02-17 15:05:18 +01001891 if v == "volume":
1892 if self.cinder.volumes.get(volume_id).status != "available":
tierno1df468d2018-07-06 14:25:16 +02001893 break
1894 else: # all ready: break from while
1895 break
sousaedu80135b92021-02-17 15:05:18 +01001896
tierno1df468d2018-07-06 14:25:16 +02001897 time.sleep(5)
1898 elapsed_time += 5
sousaedu80135b92021-02-17 15:05:18 +01001899
tiernob0b9dab2017-10-14 14:25:20 +02001900 # If we exceeded the timeout rollback
montesmoreno0c8def02016-12-22 12:16:23 +00001901 if elapsed_time >= volume_timeout:
sousaedu80135b92021-02-17 15:05:18 +01001902 raise vimconn.VimConnException(
1903 "Timeout creating volumes for instance " + name,
1904 http_code=vimconn.HTTP_Request_Timeout,
1905 )
montesmoreno0c8def02016-12-22 12:16:23 +00001906
sousaedu80135b92021-02-17 15:05:18 +01001907 # get availability Zone
1908 vm_av_zone = self._get_vm_availability_zone(
1909 availability_zone_index, availability_zone_list
1910 )
1911
1912 self.logger.debug(
1913 "nova.servers.create({}, {}, {}, nics={}, security_groups={}, "
1914 "availability_zone={}, key_name={}, userdata={}, config_drive={}, "
1915 "block_device_mapping={})".format(
1916 name,
1917 image_id,
1918 flavor_id,
1919 net_list_vim,
1920 self.config.get("security_groups"),
1921 vm_av_zone,
1922 self.config.get("keypair"),
1923 userdata,
1924 config_drive,
1925 block_device_mapping,
1926 )
1927 )
1928 server = self.nova.servers.create(
1929 name,
1930 image_id,
1931 flavor_id,
1932 nics=net_list_vim,
1933 security_groups=self.config.get("security_groups"),
1934 # TODO remove security_groups in future versions. Already at neutron port
1935 availability_zone=vm_av_zone,
1936 key_name=self.config.get("keypair"),
1937 userdata=userdata,
1938 config_drive=config_drive,
1939 block_device_mapping=block_device_mapping,
1940 ) # , description=description)
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001941
tierno326fd5e2018-02-22 11:58:59 +01001942 vm_start_time = time.time()
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001943 # Previously mentioned workaround to wait until the VM is active and then disable the port-security
1944 if no_secured_ports:
sousaedu80135b92021-02-17 15:05:18 +01001945 self.__wait_for_vm(server.id, "ACTIVE")
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001946
bravof7a1f5252020-10-20 10:27:42 -03001947 for port in no_secured_ports:
1948 port_update = {
sousaedu80135b92021-02-17 15:05:18 +01001949 "port": {"port_security_enabled": False, "security_groups": None}
bravof7a1f5252020-10-20 10:27:42 -03001950 }
1951
1952 if port[1] == "allow-address-pairs":
1953 port_update = {
sousaedu80135b92021-02-17 15:05:18 +01001954 "port": {"allowed_address_pairs": [{"ip_address": "0.0.0.0/0"}]}
bravof7a1f5252020-10-20 10:27:42 -03001955 }
1956
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001957 try:
bravof7a1f5252020-10-20 10:27:42 -03001958 self.neutron.update_port(port[0], port_update)
tierno1ec592d2020-06-16 15:29:47 +00001959 except Exception:
bravof7a1f5252020-10-20 10:27:42 -03001960 raise vimconn.VimConnException(
sousaedu80135b92021-02-17 15:05:18 +01001961 "It was not possible to disable port security for port {}".format(
1962 port[0]
1963 )
bravof7a1f5252020-10-20 10:27:42 -03001964 )
1965
tierno98e909c2017-10-14 13:27:03 +02001966 # print "DONE :-)", server
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001967
tierno4d1ce222018-04-06 10:41:06 +02001968 # pool_id = None
ahmadsaf853d452016-12-22 11:33:47 +05001969 for floating_network in external_network:
tiernof8383b82017-01-18 15:49:48 +01001970 try:
tiernof8383b82017-01-18 15:49:48 +01001971 assigned = False
tiernocb66c7e2020-07-22 10:42:58 +00001972 floating_ip_retries = 3
1973 # In case of RO in HA there can be conflicts, two RO trying to assign same floating IP, so retry
1974 # several times
tierno98e909c2017-10-14 13:27:03 +02001975 while not assigned:
sousaedu80135b92021-02-17 15:05:18 +01001976 floating_ips = self.neutron.list_floatingips().get(
1977 "floatingips", ()
1978 )
1979 random.shuffle(floating_ips) # randomize
tiernocb66c7e2020-07-22 10:42:58 +00001980 for fip in floating_ips:
sousaedu80135b92021-02-17 15:05:18 +01001981 if (
1982 fip.get("port_id")
1983 or fip.get("tenant_id") != server.tenant_id
1984 ):
tierno326fd5e2018-02-22 11:58:59 +01001985 continue
sousaedu80135b92021-02-17 15:05:18 +01001986
1987 if isinstance(floating_network["floating_ip"], str):
1988 if (
1989 fip.get("floating_network_id")
1990 != floating_network["floating_ip"]
1991 ):
tierno326fd5e2018-02-22 11:58:59 +01001992 continue
sousaedu80135b92021-02-17 15:05:18 +01001993
tiernocb66c7e2020-07-22 10:42:58 +00001994 free_floating_ip = fip["id"]
1995 break
tiernof8383b82017-01-18 15:49:48 +01001996 else:
sousaedu80135b92021-02-17 15:05:18 +01001997 if (
1998 isinstance(floating_network["floating_ip"], str)
1999 and floating_network["floating_ip"].lower() != "true"
2000 ):
2001 pool_id = floating_network["floating_ip"]
tierno326fd5e2018-02-22 11:58:59 +01002002 else:
tierno4d1ce222018-04-06 10:41:06 +02002003 # Find the external network
tierno326fd5e2018-02-22 11:58:59 +01002004 external_nets = list()
sousaedu80135b92021-02-17 15:05:18 +01002005
2006 for net in self.neutron.list_networks()["networks"]:
2007 if net["router:external"]:
tierno1ec592d2020-06-16 15:29:47 +00002008 external_nets.append(net)
tiernof8383b82017-01-18 15:49:48 +01002009
tierno326fd5e2018-02-22 11:58:59 +01002010 if len(external_nets) == 0:
tierno1ec592d2020-06-16 15:29:47 +00002011 raise vimconn.VimConnException(
sousaedu80135b92021-02-17 15:05:18 +01002012 "Cannot create floating_ip automatically since "
2013 "no external network is present",
2014 http_code=vimconn.HTTP_Conflict,
2015 )
2016
tierno326fd5e2018-02-22 11:58:59 +01002017 if len(external_nets) > 1:
tierno1ec592d2020-06-16 15:29:47 +00002018 raise vimconn.VimConnException(
sousaedu80135b92021-02-17 15:05:18 +01002019 "Cannot create floating_ip automatically since "
2020 "multiple external networks are present",
2021 http_code=vimconn.HTTP_Conflict,
2022 )
tiernof8383b82017-01-18 15:49:48 +01002023
sousaedu80135b92021-02-17 15:05:18 +01002024 pool_id = external_nets[0].get("id")
2025
2026 param = {
2027 "floatingip": {
2028 "floating_network_id": pool_id,
2029 "tenant_id": server.tenant_id,
2030 }
2031 }
2032
ahmadsaf853d452016-12-22 11:33:47 +05002033 try:
tierno4d1ce222018-04-06 10:41:06 +02002034 # self.logger.debug("Creating floating IP")
tiernof8383b82017-01-18 15:49:48 +01002035 new_floating_ip = self.neutron.create_floatingip(param)
sousaedu80135b92021-02-17 15:05:18 +01002036 free_floating_ip = new_floating_ip["floatingip"]["id"]
2037 created_items[
2038 "floating_ip:" + str(free_floating_ip)
2039 ] = True
ahmadsaf853d452016-12-22 11:33:47 +05002040 except Exception as e:
sousaedu80135b92021-02-17 15:05:18 +01002041 raise vimconn.VimConnException(
2042 type(e).__name__
2043 + ": Cannot create new floating_ip "
2044 + str(e),
2045 http_code=vimconn.HTTP_Conflict,
2046 )
tierno326fd5e2018-02-22 11:58:59 +01002047
tiernocb66c7e2020-07-22 10:42:58 +00002048 try:
2049 # for race condition ensure not already assigned
2050 fip = self.neutron.show_floatingip(free_floating_ip)
sousaedu80135b92021-02-17 15:05:18 +01002051
2052 if fip["floatingip"]["port_id"]:
tiernocb66c7e2020-07-22 10:42:58 +00002053 continue
sousaedu80135b92021-02-17 15:05:18 +01002054
tiernocb66c7e2020-07-22 10:42:58 +00002055 # the vim_id key contains the neutron.port_id
sousaedu80135b92021-02-17 15:05:18 +01002056 self.neutron.update_floatingip(
2057 free_floating_ip,
2058 {"floatingip": {"port_id": floating_network["vim_id"]}},
2059 )
tiernocb66c7e2020-07-22 10:42:58 +00002060 # for race condition ensure not re-assigned to other VM after 5 seconds
2061 time.sleep(5)
2062 fip = self.neutron.show_floatingip(free_floating_ip)
sousaedu80135b92021-02-17 15:05:18 +01002063
2064 if (
2065 fip["floatingip"]["port_id"]
2066 != floating_network["vim_id"]
2067 ):
2068 self.logger.error(
2069 "floating_ip {} re-assigned to other port".format(
2070 free_floating_ip
2071 )
2072 )
tiernocb66c7e2020-07-22 10:42:58 +00002073 continue
sousaedu80135b92021-02-17 15:05:18 +01002074
2075 self.logger.debug(
2076 "Assigned floating_ip {} to VM {}".format(
2077 free_floating_ip, server.id
2078 )
2079 )
tiernocb66c7e2020-07-22 10:42:58 +00002080 assigned = True
2081 except Exception as e:
2082 # openstack need some time after VM creation to assign an IP. So retry if fails
2083 vm_status = self.nova.servers.get(server.id).status
sousaedu80135b92021-02-17 15:05:18 +01002084
2085 if vm_status not in ("ACTIVE", "ERROR"):
tiernocb66c7e2020-07-22 10:42:58 +00002086 if time.time() - vm_start_time < server_timeout:
2087 time.sleep(5)
2088 continue
2089 elif floating_ip_retries > 0:
2090 floating_ip_retries -= 1
2091 continue
sousaedu80135b92021-02-17 15:05:18 +01002092
tiernocb66c7e2020-07-22 10:42:58 +00002093 raise vimconn.VimConnException(
sousaedu80135b92021-02-17 15:05:18 +01002094 "Cannot create floating_ip: {} {}".format(
2095 type(e).__name__, e
2096 ),
2097 http_code=vimconn.HTTP_Conflict,
2098 )
tierno326fd5e2018-02-22 11:58:59 +01002099
tiernof8383b82017-01-18 15:49:48 +01002100 except Exception as e:
sousaedu80135b92021-02-17 15:05:18 +01002101 if not floating_network["exit_on_floating_ip_error"]:
tiernocb66c7e2020-07-22 10:42:58 +00002102 self.logger.error("Cannot create floating_ip. %s", str(e))
tiernof8383b82017-01-18 15:49:48 +01002103 continue
sousaedu80135b92021-02-17 15:05:18 +01002104
tiernof8383b82017-01-18 15:49:48 +01002105 raise
montesmoreno2a1fc4e2017-01-09 16:46:04 +00002106
tierno98e909c2017-10-14 13:27:03 +02002107 return server.id, created_items
tierno1ec592d2020-06-16 15:29:47 +00002108 # except nvExceptions.NotFound as e:
2109 # error_value=-vimconn.HTTP_Not_Found
2110 # error_text= "vm instance %s not found" % vm_id
2111 # except TypeError as e:
2112 # raise vimconn.VimConnException(type(e).__name__ + ": "+ str(e), http_code=vimconn.HTTP_Bad_Request)
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02002113
2114 except Exception as e:
tierno98e909c2017-10-14 13:27:03 +02002115 server_id = None
2116 if server:
2117 server_id = server.id
sousaedu80135b92021-02-17 15:05:18 +01002118
tierno98e909c2017-10-14 13:27:03 +02002119 try:
2120 self.delete_vminstance(server_id, created_items)
2121 except Exception as e2:
2122 self.logger.error("new_vminstance rollback fail {}".format(e2))
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02002123
tiernoae4a8d12016-07-08 12:30:39 +02002124 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01002125
tierno1ec592d2020-06-16 15:29:47 +00002126 def get_vminstance(self, vm_id):
2127 """Returns the VM instance information from VIM"""
2128 # self.logger.debug("Getting VM from VIM")
tierno7edb6752016-03-21 17:37:52 +01002129 try:
2130 self._reload_connection()
2131 server = self.nova.servers.find(id=vm_id)
tierno1ec592d2020-06-16 15:29:47 +00002132 # TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
sousaedu80135b92021-02-17 15:05:18 +01002133
tiernoae4a8d12016-07-08 12:30:39 +02002134 return server.to_dict()
sousaedu80135b92021-02-17 15:05:18 +01002135 except (
2136 ksExceptions.ClientException,
2137 nvExceptions.ClientException,
2138 nvExceptions.NotFound,
2139 ConnectionError,
2140 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02002141 self._format_exception(e)
2142
tierno1ec592d2020-06-16 15:29:47 +00002143 def get_vminstance_console(self, vm_id, console_type="vnc"):
2144 """
tierno7edb6752016-03-21 17:37:52 +01002145 Get a console for the virtual machine
2146 Params:
2147 vm_id: uuid of the VM
2148 console_type, can be:
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01002149 "novnc" (by default), "xvpvnc" for VNC types,
tierno7edb6752016-03-21 17:37:52 +01002150 "rdp-html5" for RDP types, "spice-html5" for SPICE types
tiernoae4a8d12016-07-08 12:30:39 +02002151 Returns dict with the console parameters:
2152 protocol: ssh, ftp, http, https, ...
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01002153 server: usually ip address
2154 port: the http, ssh, ... port
2155 suffix: extra text, e.g. the http path and query string
tierno1ec592d2020-06-16 15:29:47 +00002156 """
tiernoae4a8d12016-07-08 12:30:39 +02002157 self.logger.debug("Getting VM CONSOLE from VIM")
sousaedu80135b92021-02-17 15:05:18 +01002158
tierno7edb6752016-03-21 17:37:52 +01002159 try:
2160 self._reload_connection()
2161 server = self.nova.servers.find(id=vm_id)
sousaedu80135b92021-02-17 15:05:18 +01002162
tierno1ec592d2020-06-16 15:29:47 +00002163 if console_type is None or console_type == "novnc":
tierno7edb6752016-03-21 17:37:52 +01002164 console_dict = server.get_vnc_console("novnc")
2165 elif console_type == "xvpvnc":
2166 console_dict = server.get_vnc_console(console_type)
2167 elif console_type == "rdp-html5":
2168 console_dict = server.get_rdp_console(console_type)
2169 elif console_type == "spice-html5":
2170 console_dict = server.get_spice_console(console_type)
2171 else:
sousaedu80135b92021-02-17 15:05:18 +01002172 raise vimconn.VimConnException(
2173 "console type '{}' not allowed".format(console_type),
2174 http_code=vimconn.HTTP_Bad_Request,
2175 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002176
tierno7edb6752016-03-21 17:37:52 +01002177 console_dict1 = console_dict.get("console")
sousaedu80135b92021-02-17 15:05:18 +01002178
tierno7edb6752016-03-21 17:37:52 +01002179 if console_dict1:
2180 console_url = console_dict1.get("url")
sousaedu80135b92021-02-17 15:05:18 +01002181
tierno7edb6752016-03-21 17:37:52 +01002182 if console_url:
tierno1ec592d2020-06-16 15:29:47 +00002183 # parse console_url
tierno7edb6752016-03-21 17:37:52 +01002184 protocol_index = console_url.find("//")
sousaedu80135b92021-02-17 15:05:18 +01002185 suffix_index = (
2186 console_url[protocol_index + 2 :].find("/") + protocol_index + 2
2187 )
2188 port_index = (
2189 console_url[protocol_index + 2 : suffix_index].find(":")
2190 + protocol_index
2191 + 2
2192 )
2193
tierno1ec592d2020-06-16 15:29:47 +00002194 if protocol_index < 0 or port_index < 0 or suffix_index < 0:
sousaedu80135b92021-02-17 15:05:18 +01002195 return (
2196 -vimconn.HTTP_Internal_Server_Error,
2197 "Unexpected response from VIM",
2198 )
2199
2200 console_dict = {
2201 "protocol": console_url[0:protocol_index],
2202 "server": console_url[protocol_index + 2 : port_index],
2203 "port": console_url[port_index:suffix_index],
2204 "suffix": console_url[suffix_index + 1 :],
2205 }
tierno7edb6752016-03-21 17:37:52 +01002206 protocol_index += 2
sousaedu80135b92021-02-17 15:05:18 +01002207
tiernoae4a8d12016-07-08 12:30:39 +02002208 return console_dict
tierno72774862020-05-04 11:44:15 +00002209 raise vimconn.VimConnUnexpectedResponse("Unexpected response from VIM")
sousaedu80135b92021-02-17 15:05:18 +01002210 except (
2211 nvExceptions.NotFound,
2212 ksExceptions.ClientException,
2213 nvExceptions.ClientException,
2214 nvExceptions.BadRequest,
2215 ConnectionError,
2216 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02002217 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01002218
tierno98e909c2017-10-14 13:27:03 +02002219 def delete_vminstance(self, vm_id, created_items=None):
sousaedu80135b92021-02-17 15:05:18 +01002220 """Removes a VM instance from VIM. Returns the old identifier"""
tierno1ec592d2020-06-16 15:29:47 +00002221 # print "osconnector: Getting VM from VIM"
2222 if created_items is None:
tierno98e909c2017-10-14 13:27:03 +02002223 created_items = {}
sousaedu80135b92021-02-17 15:05:18 +01002224
tierno7edb6752016-03-21 17:37:52 +01002225 try:
2226 self._reload_connection()
tierno98e909c2017-10-14 13:27:03 +02002227 # delete VM ports attached to this networks before the virtual machine
2228 for k, v in created_items.items():
2229 if not v: # skip already deleted
2230 continue
sousaedu80135b92021-02-17 15:05:18 +01002231
tierno7edb6752016-03-21 17:37:52 +01002232 try:
tiernoad6bdd42018-01-10 10:43:46 +01002233 k_item, _, k_id = k.partition(":")
2234 if k_item == "port":
2235 self.neutron.delete_port(k_id)
tierno7edb6752016-03-21 17:37:52 +01002236 except Exception as e:
sousaedu80135b92021-02-17 15:05:18 +01002237 self.logger.error(
2238 "Error deleting port: {}: {}".format(type(e).__name__, e)
2239 )
montesmoreno0c8def02016-12-22 12:16:23 +00002240
tierno98e909c2017-10-14 13:27:03 +02002241 # #commented because detaching the volumes makes the servers.delete not work properly ?!?
2242 # #dettach volumes attached
2243 # server = self.nova.servers.get(vm_id)
sousaedu80135b92021-02-17 15:05:18 +01002244 # volumes_attached_dict = server._info["os-extended-volumes:volumes_attached"] #volume["id"]
tierno98e909c2017-10-14 13:27:03 +02002245 # #for volume in volumes_attached_dict:
sousaedu80135b92021-02-17 15:05:18 +01002246 # # self.cinder.volumes.detach(volume["id"])
montesmoreno0c8def02016-12-22 12:16:23 +00002247
tierno98e909c2017-10-14 13:27:03 +02002248 if vm_id:
2249 self.nova.servers.delete(vm_id)
montesmoreno0c8def02016-12-22 12:16:23 +00002250
tierno98e909c2017-10-14 13:27:03 +02002251 # delete volumes. Although having detached, they should have in active status before deleting
2252 # we ensure in this loop
montesmoreno0c8def02016-12-22 12:16:23 +00002253 keep_waiting = True
2254 elapsed_time = 0
sousaedu80135b92021-02-17 15:05:18 +01002255
montesmoreno0c8def02016-12-22 12:16:23 +00002256 while keep_waiting and elapsed_time < volume_timeout:
2257 keep_waiting = False
sousaedu80135b92021-02-17 15:05:18 +01002258
tierno98e909c2017-10-14 13:27:03 +02002259 for k, v in created_items.items():
2260 if not v: # skip already deleted
2261 continue
sousaedu80135b92021-02-17 15:05:18 +01002262
tierno98e909c2017-10-14 13:27:03 +02002263 try:
tiernoad6bdd42018-01-10 10:43:46 +01002264 k_item, _, k_id = k.partition(":")
2265 if k_item == "volume":
sousaedu80135b92021-02-17 15:05:18 +01002266 if self.cinder.volumes.get(k_id).status != "available":
tierno98e909c2017-10-14 13:27:03 +02002267 keep_waiting = True
2268 else:
tiernoad6bdd42018-01-10 10:43:46 +01002269 self.cinder.volumes.delete(k_id)
tiernocb66c7e2020-07-22 10:42:58 +00002270 created_items[k] = None
2271 elif k_item == "floating_ip": # floating ip
2272 self.neutron.delete_floatingip(k_id)
2273 created_items[k] = None
2274
tierno98e909c2017-10-14 13:27:03 +02002275 except Exception as e:
tiernocb66c7e2020-07-22 10:42:58 +00002276 self.logger.error("Error deleting {}: {}".format(k, e))
sousaedu80135b92021-02-17 15:05:18 +01002277
montesmoreno0c8def02016-12-22 12:16:23 +00002278 if keep_waiting:
2279 time.sleep(1)
2280 elapsed_time += 1
sousaedu80135b92021-02-17 15:05:18 +01002281
tierno98e909c2017-10-14 13:27:03 +02002282 return None
sousaedu80135b92021-02-17 15:05:18 +01002283 except (
2284 nvExceptions.NotFound,
2285 ksExceptions.ClientException,
2286 nvExceptions.ClientException,
2287 ConnectionError,
2288 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02002289 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01002290
tiernoae4a8d12016-07-08 12:30:39 +02002291 def refresh_vms_status(self, vm_list):
tierno1ec592d2020-06-16 15:29:47 +00002292 """Get the status of the virtual machines and their interfaces/ports
sousaedu80135b92021-02-17 15:05:18 +01002293 Params: the list of VM identifiers
2294 Returns a dictionary with:
2295 vm_id: #VIM id of this Virtual Machine
2296 status: #Mandatory. Text with one of:
2297 # DELETED (not found at vim)
2298 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
2299 # OTHER (Vim reported other status not understood)
2300 # ERROR (VIM indicates an ERROR status)
2301 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
2302 # CREATING (on building process), ERROR
2303 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
2304 #
2305 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
2306 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
2307 interfaces:
2308 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
2309 mac_address: #Text format XX:XX:XX:XX:XX:XX
2310 vim_net_id: #network id where this interface is connected
2311 vim_interface_id: #interface/port VIM id
2312 ip_address: #null, or text with IPv4, IPv6 address
2313 compute_node: #identification of compute node where PF,VF interface is allocated
2314 pci: #PCI address of the NIC that hosts the PF,VF
2315 vlan: #physical VLAN used for VF
tierno1ec592d2020-06-16 15:29:47 +00002316 """
2317 vm_dict = {}
sousaedu80135b92021-02-17 15:05:18 +01002318 self.logger.debug(
2319 "refresh_vms status: Getting tenant VM instance information from VIM"
2320 )
2321
tiernoae4a8d12016-07-08 12:30:39 +02002322 for vm_id in vm_list:
tierno1ec592d2020-06-16 15:29:47 +00002323 vm = {}
sousaedu80135b92021-02-17 15:05:18 +01002324
tiernoae4a8d12016-07-08 12:30:39 +02002325 try:
2326 vm_vim = self.get_vminstance(vm_id)
sousaedu80135b92021-02-17 15:05:18 +01002327
2328 if vm_vim["status"] in vmStatus2manoFormat:
2329 vm["status"] = vmStatus2manoFormat[vm_vim["status"]]
tierno7edb6752016-03-21 17:37:52 +01002330 else:
sousaedu80135b92021-02-17 15:05:18 +01002331 vm["status"] = "OTHER"
2332 vm["error_msg"] = "VIM status reported " + vm_vim["status"]
2333
tierno70eeb182020-10-19 16:38:00 +00002334 vm_vim.pop("OS-EXT-SRV-ATTR:user_data", None)
2335 vm_vim.pop("user_data", None)
sousaedu80135b92021-02-17 15:05:18 +01002336 vm["vim_info"] = self.serialize(vm_vim)
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01002337
tiernoae4a8d12016-07-08 12:30:39 +02002338 vm["interfaces"] = []
sousaedu80135b92021-02-17 15:05:18 +01002339 if vm_vim.get("fault"):
2340 vm["error_msg"] = str(vm_vim["fault"])
2341
tierno1ec592d2020-06-16 15:29:47 +00002342 # get interfaces
tierno7edb6752016-03-21 17:37:52 +01002343 try:
tiernoae4a8d12016-07-08 12:30:39 +02002344 self._reload_connection()
tiernob42fd9b2018-06-20 10:44:32 +02002345 port_dict = self.neutron.list_ports(device_id=vm_id)
sousaedu80135b92021-02-17 15:05:18 +01002346
tiernoae4a8d12016-07-08 12:30:39 +02002347 for port in port_dict["ports"]:
tierno1ec592d2020-06-16 15:29:47 +00002348 interface = {}
sousaedu80135b92021-02-17 15:05:18 +01002349 interface["vim_info"] = self.serialize(port)
tiernoae4a8d12016-07-08 12:30:39 +02002350 interface["mac_address"] = port.get("mac_address")
2351 interface["vim_net_id"] = port["network_id"]
2352 interface["vim_interface_id"] = port["id"]
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01002353 # check if OS-EXT-SRV-ATTR:host is there,
Mike Marchetti5b9da422017-05-02 15:35:47 -04002354 # in case of non-admin credentials, it will be missing
sousaedu80135b92021-02-17 15:05:18 +01002355
2356 if vm_vim.get("OS-EXT-SRV-ATTR:host"):
2357 interface["compute_node"] = vm_vim["OS-EXT-SRV-ATTR:host"]
2358
tierno867ffe92017-03-27 12:50:34 +02002359 interface["pci"] = None
Mike Marchetti5b9da422017-05-02 15:35:47 -04002360
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01002361 # check if binding:profile is there,
Mike Marchetti5b9da422017-05-02 15:35:47 -04002362 # in case of non-admin credentials, it will be missing
sousaedu80135b92021-02-17 15:05:18 +01002363 if port.get("binding:profile"):
2364 if port["binding:profile"].get("pci_slot"):
tierno1ec592d2020-06-16 15:29:47 +00002365 # TODO: At the moment sr-iov pci addresses are converted to PF pci addresses by setting
2366 # the slot to 0x00
Mike Marchetti5b9da422017-05-02 15:35:47 -04002367 # TODO: This is just a workaround valid for niantinc. Find a better way to do so
2368 # CHANGE DDDD:BB:SS.F to DDDD:BB:00.(F%2) assuming there are 2 ports per nic
sousaedu80135b92021-02-17 15:05:18 +01002369 pci = port["binding:profile"]["pci_slot"]
Mike Marchetti5b9da422017-05-02 15:35:47 -04002370 # interface["pci"] = pci[:-4] + "00." + str(int(pci[-1]) % 2)
2371 interface["pci"] = pci
sousaedu80135b92021-02-17 15:05:18 +01002372
tierno867ffe92017-03-27 12:50:34 +02002373 interface["vlan"] = None
sousaedu80135b92021-02-17 15:05:18 +01002374
2375 if port.get("binding:vif_details"):
2376 interface["vlan"] = port["binding:vif_details"].get("vlan")
2377
tierno1dfe9932020-06-18 08:50:10 +00002378 # Get vlan from network in case not present in port for those old openstacks and cases where
2379 # it is needed vlan at PT
2380 if not interface["vlan"]:
2381 # if network is of type vlan and port is of type direct (sr-iov) then set vlan id
2382 network = self.neutron.show_network(port["network_id"])
sousaedu80135b92021-02-17 15:05:18 +01002383
2384 if (
2385 network["network"].get("provider:network_type")
2386 == "vlan"
2387 ):
tierno1dfe9932020-06-18 08:50:10 +00002388 # and port.get("binding:vnic_type") in ("direct", "direct-physical"):
sousaedu80135b92021-02-17 15:05:18 +01002389 interface["vlan"] = network["network"].get(
2390 "provider:segmentation_id"
2391 )
2392
tierno1ec592d2020-06-16 15:29:47 +00002393 ips = []
2394 # look for floating ip address
tiernob42fd9b2018-06-20 10:44:32 +02002395 try:
sousaedu80135b92021-02-17 15:05:18 +01002396 floating_ip_dict = self.neutron.list_floatingips(
2397 port_id=port["id"]
2398 )
2399
tiernob42fd9b2018-06-20 10:44:32 +02002400 if floating_ip_dict.get("floatingips"):
sousaedu80135b92021-02-17 15:05:18 +01002401 ips.append(
2402 floating_ip_dict["floatingips"][0].get(
2403 "floating_ip_address"
2404 )
2405 )
tiernob42fd9b2018-06-20 10:44:32 +02002406 except Exception:
2407 pass
tierno7edb6752016-03-21 17:37:52 +01002408
tiernoae4a8d12016-07-08 12:30:39 +02002409 for subnet in port["fixed_ips"]:
2410 ips.append(subnet["ip_address"])
sousaedu80135b92021-02-17 15:05:18 +01002411
tiernoae4a8d12016-07-08 12:30:39 +02002412 interface["ip_address"] = ";".join(ips)
2413 vm["interfaces"].append(interface)
2414 except Exception as e:
sousaedu80135b92021-02-17 15:05:18 +01002415 self.logger.error(
2416 "Error getting vm interface information {}: {}".format(
2417 type(e).__name__, e
2418 ),
2419 exc_info=True,
2420 )
tierno72774862020-05-04 11:44:15 +00002421 except vimconn.VimConnNotFoundException as e:
tiernoae4a8d12016-07-08 12:30:39 +02002422 self.logger.error("Exception getting vm status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01002423 vm["status"] = "DELETED"
2424 vm["error_msg"] = str(e)
tierno72774862020-05-04 11:44:15 +00002425 except vimconn.VimConnException as e:
tiernoae4a8d12016-07-08 12:30:39 +02002426 self.logger.error("Exception getting vm status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01002427 vm["status"] = "VIM_ERROR"
2428 vm["error_msg"] = str(e)
2429
tiernoae4a8d12016-07-08 12:30:39 +02002430 vm_dict[vm_id] = vm
sousaedu80135b92021-02-17 15:05:18 +01002431
tiernoae4a8d12016-07-08 12:30:39 +02002432 return vm_dict
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002433
tierno98e909c2017-10-14 13:27:03 +02002434 def action_vminstance(self, vm_id, action_dict, created_items={}):
tierno1ec592d2020-06-16 15:29:47 +00002435 """Send and action over a VM instance from VIM
2436 Returns None or the console dict if the action was successfully sent to the VIM"""
tiernoae4a8d12016-07-08 12:30:39 +02002437 self.logger.debug("Action over VM '%s': %s", vm_id, str(action_dict))
sousaedu80135b92021-02-17 15:05:18 +01002438
tierno7edb6752016-03-21 17:37:52 +01002439 try:
2440 self._reload_connection()
2441 server = self.nova.servers.find(id=vm_id)
sousaedu80135b92021-02-17 15:05:18 +01002442
tierno7edb6752016-03-21 17:37:52 +01002443 if "start" in action_dict:
tierno1ec592d2020-06-16 15:29:47 +00002444 if action_dict["start"] == "rebuild":
tierno7edb6752016-03-21 17:37:52 +01002445 server.rebuild()
2446 else:
tierno1ec592d2020-06-16 15:29:47 +00002447 if server.status == "PAUSED":
tierno7edb6752016-03-21 17:37:52 +01002448 server.unpause()
tierno1ec592d2020-06-16 15:29:47 +00002449 elif server.status == "SUSPENDED":
tierno7edb6752016-03-21 17:37:52 +01002450 server.resume()
tierno1ec592d2020-06-16 15:29:47 +00002451 elif server.status == "SHUTOFF":
tierno7edb6752016-03-21 17:37:52 +01002452 server.start()
2453 elif "pause" in action_dict:
2454 server.pause()
2455 elif "resume" in action_dict:
2456 server.resume()
2457 elif "shutoff" in action_dict or "shutdown" in action_dict:
2458 server.stop()
2459 elif "forceOff" in action_dict:
tierno1ec592d2020-06-16 15:29:47 +00002460 server.stop() # TODO
tierno7edb6752016-03-21 17:37:52 +01002461 elif "terminate" in action_dict:
2462 server.delete()
2463 elif "createImage" in action_dict:
2464 server.create_image()
tierno1ec592d2020-06-16 15:29:47 +00002465 # "path":path_schema,
2466 # "description":description_schema,
2467 # "name":name_schema,
2468 # "metadata":metadata_schema,
2469 # "imageRef": id_schema,
2470 # "disk": {"oneOf":[{"type": "null"}, {"type":"string"}] },
tierno7edb6752016-03-21 17:37:52 +01002471 elif "rebuild" in action_dict:
sousaedu80135b92021-02-17 15:05:18 +01002472 server.rebuild(server.image["id"])
tierno7edb6752016-03-21 17:37:52 +01002473 elif "reboot" in action_dict:
sousaedu80135b92021-02-17 15:05:18 +01002474 server.reboot() # reboot_type="SOFT"
tierno7edb6752016-03-21 17:37:52 +01002475 elif "console" in action_dict:
2476 console_type = action_dict["console"]
sousaedu80135b92021-02-17 15:05:18 +01002477
tierno1ec592d2020-06-16 15:29:47 +00002478 if console_type is None or console_type == "novnc":
tierno7edb6752016-03-21 17:37:52 +01002479 console_dict = server.get_vnc_console("novnc")
2480 elif console_type == "xvpvnc":
2481 console_dict = server.get_vnc_console(console_type)
2482 elif console_type == "rdp-html5":
2483 console_dict = server.get_rdp_console(console_type)
2484 elif console_type == "spice-html5":
2485 console_dict = server.get_spice_console(console_type)
2486 else:
sousaedu80135b92021-02-17 15:05:18 +01002487 raise vimconn.VimConnException(
2488 "console type '{}' not allowed".format(console_type),
2489 http_code=vimconn.HTTP_Bad_Request,
2490 )
2491
tierno7edb6752016-03-21 17:37:52 +01002492 try:
2493 console_url = console_dict["console"]["url"]
tierno1ec592d2020-06-16 15:29:47 +00002494 # parse console_url
tierno7edb6752016-03-21 17:37:52 +01002495 protocol_index = console_url.find("//")
sousaedu80135b92021-02-17 15:05:18 +01002496 suffix_index = (
2497 console_url[protocol_index + 2 :].find("/") + protocol_index + 2
2498 )
2499 port_index = (
2500 console_url[protocol_index + 2 : suffix_index].find(":")
2501 + protocol_index
2502 + 2
2503 )
2504
tierno1ec592d2020-06-16 15:29:47 +00002505 if protocol_index < 0 or port_index < 0 or suffix_index < 0:
sousaedu80135b92021-02-17 15:05:18 +01002506 raise vimconn.VimConnException(
2507 "Unexpected response from VIM " + str(console_dict)
2508 )
2509
2510 console_dict2 = {
2511 "protocol": console_url[0:protocol_index],
2512 "server": console_url[protocol_index + 2 : port_index],
2513 "port": int(console_url[port_index + 1 : suffix_index]),
2514 "suffix": console_url[suffix_index + 1 :],
2515 }
2516
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002517 return console_dict2
tierno1ec592d2020-06-16 15:29:47 +00002518 except Exception:
sousaedu80135b92021-02-17 15:05:18 +01002519 raise vimconn.VimConnException(
2520 "Unexpected response from VIM " + str(console_dict)
2521 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002522
tierno98e909c2017-10-14 13:27:03 +02002523 return None
sousaedu80135b92021-02-17 15:05:18 +01002524 except (
2525 ksExceptions.ClientException,
2526 nvExceptions.ClientException,
2527 nvExceptions.NotFound,
2528 ConnectionError,
2529 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02002530 self._format_exception(e)
tierno1ec592d2020-06-16 15:29:47 +00002531 # TODO insert exception vimconn.HTTP_Unauthorized
tiernoae4a8d12016-07-08 12:30:39 +02002532
tierno1ec592d2020-06-16 15:29:47 +00002533 # ###### VIO Specific Changes #########
garciadeblasebd66722019-01-31 16:01:31 +00002534 def _generate_vlanID(self):
kate721d79b2017-06-24 04:21:38 -07002535 """
sousaedu80135b92021-02-17 15:05:18 +01002536 Method to get unused vlanID
kate721d79b2017-06-24 04:21:38 -07002537 Args:
2538 None
2539 Returns:
2540 vlanID
2541 """
tierno1ec592d2020-06-16 15:29:47 +00002542 # Get used VLAN IDs
kate721d79b2017-06-24 04:21:38 -07002543 usedVlanIDs = []
2544 networks = self.get_network_list()
sousaedu80135b92021-02-17 15:05:18 +01002545
kate721d79b2017-06-24 04:21:38 -07002546 for net in networks:
sousaedu80135b92021-02-17 15:05:18 +01002547 if net.get("provider:segmentation_id"):
2548 usedVlanIDs.append(net.get("provider:segmentation_id"))
2549
kate721d79b2017-06-24 04:21:38 -07002550 used_vlanIDs = set(usedVlanIDs)
2551
tierno1ec592d2020-06-16 15:29:47 +00002552 # find unused VLAN ID
sousaedu80135b92021-02-17 15:05:18 +01002553 for vlanID_range in self.config.get("dataplane_net_vlan_range"):
kate721d79b2017-06-24 04:21:38 -07002554 try:
sousaedu80135b92021-02-17 15:05:18 +01002555 start_vlanid, end_vlanid = map(
2556 int, vlanID_range.replace(" ", "").split("-")
2557 )
2558
tierno7d782ef2019-10-04 12:56:31 +00002559 for vlanID in range(start_vlanid, end_vlanid + 1):
kate721d79b2017-06-24 04:21:38 -07002560 if vlanID not in used_vlanIDs:
2561 return vlanID
2562 except Exception as exp:
sousaedu80135b92021-02-17 15:05:18 +01002563 raise vimconn.VimConnException(
2564 "Exception {} occurred while generating VLAN ID.".format(exp)
2565 )
kate721d79b2017-06-24 04:21:38 -07002566 else:
tierno1ec592d2020-06-16 15:29:47 +00002567 raise vimconn.VimConnConflictException(
2568 "Unable to create the SRIOV VLAN network. All given Vlan IDs {} are in use.".format(
sousaedu80135b92021-02-17 15:05:18 +01002569 self.config.get("dataplane_net_vlan_range")
2570 )
2571 )
kate721d79b2017-06-24 04:21:38 -07002572
garciadeblasebd66722019-01-31 16:01:31 +00002573 def _generate_multisegment_vlanID(self):
2574 """
sousaedu80135b92021-02-17 15:05:18 +01002575 Method to get unused vlanID
2576 Args:
2577 None
2578 Returns:
2579 vlanID
garciadeblasebd66722019-01-31 16:01:31 +00002580 """
tierno6869ae72020-01-09 17:37:34 +00002581 # Get used VLAN IDs
garciadeblasebd66722019-01-31 16:01:31 +00002582 usedVlanIDs = []
2583 networks = self.get_network_list()
2584 for net in networks:
sousaedu80135b92021-02-17 15:05:18 +01002585 if net.get("provider:network_type") == "vlan" and net.get(
2586 "provider:segmentation_id"
2587 ):
2588 usedVlanIDs.append(net.get("provider:segmentation_id"))
2589 elif net.get("segments"):
2590 for segment in net.get("segments"):
2591 if segment.get("provider:network_type") == "vlan" and segment.get(
2592 "provider:segmentation_id"
2593 ):
2594 usedVlanIDs.append(segment.get("provider:segmentation_id"))
2595
garciadeblasebd66722019-01-31 16:01:31 +00002596 used_vlanIDs = set(usedVlanIDs)
2597
tierno6869ae72020-01-09 17:37:34 +00002598 # find unused VLAN ID
sousaedu80135b92021-02-17 15:05:18 +01002599 for vlanID_range in self.config.get("multisegment_vlan_range"):
garciadeblasebd66722019-01-31 16:01:31 +00002600 try:
sousaedu80135b92021-02-17 15:05:18 +01002601 start_vlanid, end_vlanid = map(
2602 int, vlanID_range.replace(" ", "").split("-")
2603 )
2604
tierno7d782ef2019-10-04 12:56:31 +00002605 for vlanID in range(start_vlanid, end_vlanid + 1):
garciadeblasebd66722019-01-31 16:01:31 +00002606 if vlanID not in used_vlanIDs:
2607 return vlanID
2608 except Exception as exp:
sousaedu80135b92021-02-17 15:05:18 +01002609 raise vimconn.VimConnException(
2610 "Exception {} occurred while generating VLAN ID.".format(exp)
2611 )
garciadeblasebd66722019-01-31 16:01:31 +00002612 else:
tierno1ec592d2020-06-16 15:29:47 +00002613 raise vimconn.VimConnConflictException(
2614 "Unable to create the VLAN segment. All VLAN IDs {} are in use.".format(
sousaedu80135b92021-02-17 15:05:18 +01002615 self.config.get("multisegment_vlan_range")
2616 )
2617 )
garciadeblasebd66722019-01-31 16:01:31 +00002618
2619 def _validate_vlan_ranges(self, input_vlan_range, text_vlan_range):
kate721d79b2017-06-24 04:21:38 -07002620 """
2621 Method to validate user given vlanID ranges
2622 Args: None
2623 Returns: None
2624 """
garciadeblasebd66722019-01-31 16:01:31 +00002625 for vlanID_range in input_vlan_range:
kate721d79b2017-06-24 04:21:38 -07002626 vlan_range = vlanID_range.replace(" ", "")
tierno1ec592d2020-06-16 15:29:47 +00002627 # validate format
sousaedu80135b92021-02-17 15:05:18 +01002628 vlanID_pattern = r"(\d)*-(\d)*$"
kate721d79b2017-06-24 04:21:38 -07002629 match_obj = re.match(vlanID_pattern, vlan_range)
2630 if not match_obj:
tierno1ec592d2020-06-16 15:29:47 +00002631 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01002632 "Invalid VLAN range for {}: {}.You must provide "
2633 "'{}' in format [start_ID - end_ID].".format(
2634 text_vlan_range, vlanID_range, text_vlan_range
2635 )
2636 )
kate721d79b2017-06-24 04:21:38 -07002637
tierno1ec592d2020-06-16 15:29:47 +00002638 start_vlanid, end_vlanid = map(int, vlan_range.split("-"))
2639 if start_vlanid <= 0:
2640 raise vimconn.VimConnConflictException(
2641 "Invalid VLAN range for {}: {}. Start ID can not be zero. For VLAN "
sousaedu80135b92021-02-17 15:05:18 +01002642 "networks valid IDs are 1 to 4094 ".format(
2643 text_vlan_range, vlanID_range
2644 )
2645 )
2646
tierno1ec592d2020-06-16 15:29:47 +00002647 if end_vlanid > 4094:
2648 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01002649 "Invalid VLAN range for {}: {}. End VLAN ID can not be "
2650 "greater than 4094. For VLAN networks valid IDs are 1 to 4094 ".format(
2651 text_vlan_range, vlanID_range
2652 )
2653 )
kate721d79b2017-06-24 04:21:38 -07002654
2655 if start_vlanid > end_vlanid:
tierno1ec592d2020-06-16 15:29:47 +00002656 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01002657 "Invalid VLAN range for {}: {}. You must provide '{}'"
2658 " in format start_ID - end_ID and start_ID < end_ID ".format(
2659 text_vlan_range, vlanID_range, text_vlan_range
2660 )
2661 )
kate721d79b2017-06-24 04:21:38 -07002662
tierno1ec592d2020-06-16 15:29:47 +00002663 # NOT USED FUNCTIONS
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002664
tiernoae4a8d12016-07-08 12:30:39 +02002665 def new_external_port(self, port_data):
tierno1ec592d2020-06-16 15:29:47 +00002666 """Adds a external port to VIM
sousaedu80135b92021-02-17 15:05:18 +01002667 Returns the port identifier"""
tierno1ec592d2020-06-16 15:29:47 +00002668 # TODO openstack if needed
sousaedu80135b92021-02-17 15:05:18 +01002669 return (
2670 -vimconn.HTTP_Internal_Server_Error,
2671 "osconnector.new_external_port() not implemented",
2672 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002673
tiernoae4a8d12016-07-08 12:30:39 +02002674 def connect_port_network(self, port_id, network_id, admin=False):
tierno1ec592d2020-06-16 15:29:47 +00002675 """Connects a external port to a network
sousaedu80135b92021-02-17 15:05:18 +01002676 Returns status code of the VIM response"""
tierno1ec592d2020-06-16 15:29:47 +00002677 # TODO openstack if needed
sousaedu80135b92021-02-17 15:05:18 +01002678 return (
2679 -vimconn.HTTP_Internal_Server_Error,
2680 "osconnector.connect_port_network() not implemented",
2681 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002682
tiernoae4a8d12016-07-08 12:30:39 +02002683 def new_user(self, user_name, user_passwd, tenant_id=None):
tierno1ec592d2020-06-16 15:29:47 +00002684 """Adds a new user to openstack VIM
sousaedu80135b92021-02-17 15:05:18 +01002685 Returns the user identifier"""
tiernoae4a8d12016-07-08 12:30:39 +02002686 self.logger.debug("osconnector: Adding a new user to VIM")
sousaedu80135b92021-02-17 15:05:18 +01002687
tiernoae4a8d12016-07-08 12:30:39 +02002688 try:
2689 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +01002690 user = self.keystone.users.create(
2691 user_name, password=user_passwd, default_project=tenant_id
2692 )
tierno1ec592d2020-06-16 15:29:47 +00002693 # self.keystone.tenants.add_user(self.k_creds["username"], #role)
sousaedu80135b92021-02-17 15:05:18 +01002694
tiernoae4a8d12016-07-08 12:30:39 +02002695 return user.id
2696 except ksExceptions.ConnectionError as e:
tierno1ec592d2020-06-16 15:29:47 +00002697 error_value = -vimconn.HTTP_Bad_Request
sousaedu80135b92021-02-17 15:05:18 +01002698 error_text = (
2699 type(e).__name__
2700 + ": "
2701 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2702 )
tierno1ec592d2020-06-16 15:29:47 +00002703 except ksExceptions.ClientException as e: # TODO remove
2704 error_value = -vimconn.HTTP_Bad_Request
sousaedu80135b92021-02-17 15:05:18 +01002705 error_text = (
2706 type(e).__name__
2707 + ": "
2708 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2709 )
2710
tierno1ec592d2020-06-16 15:29:47 +00002711 # TODO insert exception vimconn.HTTP_Unauthorized
2712 # if reaching here is because an exception
tierno9c5c8322018-03-23 15:44:03 +01002713 self.logger.debug("new_user " + error_text)
sousaedu80135b92021-02-17 15:05:18 +01002714
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002715 return error_value, error_text
tiernoae4a8d12016-07-08 12:30:39 +02002716
2717 def delete_user(self, user_id):
tierno1ec592d2020-06-16 15:29:47 +00002718 """Delete a user from openstack VIM
sousaedu80135b92021-02-17 15:05:18 +01002719 Returns the user identifier"""
tiernoae4a8d12016-07-08 12:30:39 +02002720 if self.debug:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002721 print("osconnector: Deleting a user from VIM")
sousaedu80135b92021-02-17 15:05:18 +01002722
tiernoae4a8d12016-07-08 12:30:39 +02002723 try:
2724 self._reload_connection()
2725 self.keystone.users.delete(user_id)
sousaedu80135b92021-02-17 15:05:18 +01002726
tiernoae4a8d12016-07-08 12:30:39 +02002727 return 1, user_id
2728 except ksExceptions.ConnectionError as e:
tierno1ec592d2020-06-16 15:29:47 +00002729 error_value = -vimconn.HTTP_Bad_Request
sousaedu80135b92021-02-17 15:05:18 +01002730 error_text = (
2731 type(e).__name__
2732 + ": "
2733 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2734 )
tiernoae4a8d12016-07-08 12:30:39 +02002735 except ksExceptions.NotFound as e:
tierno1ec592d2020-06-16 15:29:47 +00002736 error_value = -vimconn.HTTP_Not_Found
sousaedu80135b92021-02-17 15:05:18 +01002737 error_text = (
2738 type(e).__name__
2739 + ": "
2740 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2741 )
tierno1ec592d2020-06-16 15:29:47 +00002742 except ksExceptions.ClientException as e: # TODO remove
2743 error_value = -vimconn.HTTP_Bad_Request
sousaedu80135b92021-02-17 15:05:18 +01002744 error_text = (
2745 type(e).__name__
2746 + ": "
2747 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2748 )
2749
tierno1ec592d2020-06-16 15:29:47 +00002750 # TODO insert exception vimconn.HTTP_Unauthorized
2751 # if reaching here is because an exception
2752 self.logger.debug("delete_tenant " + error_text)
sousaedu80135b92021-02-17 15:05:18 +01002753
tiernoae4a8d12016-07-08 12:30:39 +02002754 return error_value, error_text
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002755
tierno7edb6752016-03-21 17:37:52 +01002756 def get_hosts_info(self):
tierno1ec592d2020-06-16 15:29:47 +00002757 """Get the information of deployed hosts
2758 Returns the hosts content"""
tierno7edb6752016-03-21 17:37:52 +01002759 if self.debug:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002760 print("osconnector: Getting Host info from VIM")
sousaedu80135b92021-02-17 15:05:18 +01002761
tierno7edb6752016-03-21 17:37:52 +01002762 try:
tierno1ec592d2020-06-16 15:29:47 +00002763 h_list = []
tierno7edb6752016-03-21 17:37:52 +01002764 self._reload_connection()
2765 hypervisors = self.nova.hypervisors.list()
sousaedu80135b92021-02-17 15:05:18 +01002766
tierno7edb6752016-03-21 17:37:52 +01002767 for hype in hypervisors:
tierno1ec592d2020-06-16 15:29:47 +00002768 h_list.append(hype.to_dict())
sousaedu80135b92021-02-17 15:05:18 +01002769
tierno1ec592d2020-06-16 15:29:47 +00002770 return 1, {"hosts": h_list}
tierno7edb6752016-03-21 17:37:52 +01002771 except nvExceptions.NotFound as e:
tierno1ec592d2020-06-16 15:29:47 +00002772 error_value = -vimconn.HTTP_Not_Found
sousaedu80135b92021-02-17 15:05:18 +01002773 error_text = str(e) if len(e.args) == 0 else str(e.args[0])
tierno7edb6752016-03-21 17:37:52 +01002774 except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
tierno1ec592d2020-06-16 15:29:47 +00002775 error_value = -vimconn.HTTP_Bad_Request
sousaedu80135b92021-02-17 15:05:18 +01002776 error_text = (
2777 type(e).__name__
2778 + ": "
2779 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2780 )
2781
tierno1ec592d2020-06-16 15:29:47 +00002782 # TODO insert exception vimconn.HTTP_Unauthorized
2783 # if reaching here is because an exception
tierno9c5c8322018-03-23 15:44:03 +01002784 self.logger.debug("get_hosts_info " + error_text)
sousaedu80135b92021-02-17 15:05:18 +01002785
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002786 return error_value, error_text
tierno7edb6752016-03-21 17:37:52 +01002787
2788 def get_hosts(self, vim_tenant):
tierno1ec592d2020-06-16 15:29:47 +00002789 """Get the hosts and deployed instances
2790 Returns the hosts content"""
tierno7edb6752016-03-21 17:37:52 +01002791 r, hype_dict = self.get_hosts_info()
sousaedu80135b92021-02-17 15:05:18 +01002792
tierno1ec592d2020-06-16 15:29:47 +00002793 if r < 0:
tierno7edb6752016-03-21 17:37:52 +01002794 return r, hype_dict
sousaedu80135b92021-02-17 15:05:18 +01002795
tierno7edb6752016-03-21 17:37:52 +01002796 hypervisors = hype_dict["hosts"]
sousaedu80135b92021-02-17 15:05:18 +01002797
tierno7edb6752016-03-21 17:37:52 +01002798 try:
2799 servers = self.nova.servers.list()
2800 for hype in hypervisors:
2801 for server in servers:
sousaedu80135b92021-02-17 15:05:18 +01002802 if (
2803 server.to_dict()["OS-EXT-SRV-ATTR:hypervisor_hostname"]
2804 == hype["hypervisor_hostname"]
2805 ):
2806 if "vm" in hype:
2807 hype["vm"].append(server.id)
tierno7edb6752016-03-21 17:37:52 +01002808 else:
sousaedu80135b92021-02-17 15:05:18 +01002809 hype["vm"] = [server.id]
2810
tierno7edb6752016-03-21 17:37:52 +01002811 return 1, hype_dict
2812 except nvExceptions.NotFound as e:
tierno1ec592d2020-06-16 15:29:47 +00002813 error_value = -vimconn.HTTP_Not_Found
sousaedu80135b92021-02-17 15:05:18 +01002814 error_text = str(e) if len(e.args) == 0 else str(e.args[0])
tierno7edb6752016-03-21 17:37:52 +01002815 except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
tierno1ec592d2020-06-16 15:29:47 +00002816 error_value = -vimconn.HTTP_Bad_Request
sousaedu80135b92021-02-17 15:05:18 +01002817 error_text = (
2818 type(e).__name__
2819 + ": "
2820 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2821 )
2822
tierno1ec592d2020-06-16 15:29:47 +00002823 # TODO insert exception vimconn.HTTP_Unauthorized
2824 # if reaching here is because an exception
tierno9c5c8322018-03-23 15:44:03 +01002825 self.logger.debug("get_hosts " + error_text)
sousaedu80135b92021-02-17 15:05:18 +01002826
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002827 return error_value, error_text
tierno7edb6752016-03-21 17:37:52 +01002828
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002829 def new_classification(self, name, ctype, definition):
sousaedu80135b92021-02-17 15:05:18 +01002830 self.logger.debug(
2831 "Adding a new (Traffic) Classification to VIM, named %s", name
2832 )
2833
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002834 try:
2835 new_class = None
2836 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +01002837
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002838 if ctype not in supportedClassificationTypes:
tierno72774862020-05-04 11:44:15 +00002839 raise vimconn.VimConnNotSupportedException(
sousaedu80135b92021-02-17 15:05:18 +01002840 "OpenStack VIM connector does not support provided "
2841 "Classification Type {}, supported ones are: {}".format(
2842 ctype, supportedClassificationTypes
2843 )
2844 )
2845
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002846 if not self._validate_classification(ctype, definition):
tierno72774862020-05-04 11:44:15 +00002847 raise vimconn.VimConnException(
sousaedu80135b92021-02-17 15:05:18 +01002848 "Incorrect Classification definition for the type specified."
2849 )
tierno7edb6752016-03-21 17:37:52 +01002850
sousaedu80135b92021-02-17 15:05:18 +01002851 classification_dict = definition
2852 classification_dict["name"] = name
Igor D.Ccaadc442017-11-06 12:48:48 +00002853 new_class = self.neutron.create_sfc_flow_classifier(
sousaedu80135b92021-02-17 15:05:18 +01002854 {"flow_classifier": classification_dict}
2855 )
2856
2857 return new_class["flow_classifier"]["id"]
2858 except (
2859 neExceptions.ConnectionFailed,
2860 ksExceptions.ClientException,
2861 neExceptions.NeutronException,
2862 ConnectionError,
2863 ) as e:
2864 self.logger.error("Creation of Classification failed.")
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002865 self._format_exception(e)
2866
2867 def get_classification(self, class_id):
2868 self.logger.debug(" Getting Classification %s from VIM", class_id)
2869 filter_dict = {"id": class_id}
2870 class_list = self.get_classification_list(filter_dict)
sousaedu80135b92021-02-17 15:05:18 +01002871
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002872 if len(class_list) == 0:
tierno72774862020-05-04 11:44:15 +00002873 raise vimconn.VimConnNotFoundException(
sousaedu80135b92021-02-17 15:05:18 +01002874 "Classification '{}' not found".format(class_id)
2875 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002876 elif len(class_list) > 1:
tierno72774862020-05-04 11:44:15 +00002877 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01002878 "Found more than one Classification with this criteria"
2879 )
2880
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002881 classification = class_list[0]
sousaedu80135b92021-02-17 15:05:18 +01002882
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002883 return classification
2884
2885 def get_classification_list(self, filter_dict={}):
sousaedu80135b92021-02-17 15:05:18 +01002886 self.logger.debug(
2887 "Getting Classifications from VIM filter: '%s'", str(filter_dict)
2888 )
2889
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002890 try:
tierno69b590e2018-03-13 18:52:23 +01002891 filter_dict_os = filter_dict.copy()
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002892 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +01002893
tierno69b590e2018-03-13 18:52:23 +01002894 if self.api_version3 and "tenant_id" in filter_dict_os:
sousaedu80135b92021-02-17 15:05:18 +01002895 filter_dict_os["project_id"] = filter_dict_os.pop("tenant_id")
2896
Igor D.Ccaadc442017-11-06 12:48:48 +00002897 classification_dict = self.neutron.list_sfc_flow_classifiers(
sousaedu80135b92021-02-17 15:05:18 +01002898 **filter_dict_os
2899 )
tierno69b590e2018-03-13 18:52:23 +01002900 classification_list = classification_dict["flow_classifiers"]
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002901 self.__classification_os2mano(classification_list)
sousaedu80135b92021-02-17 15:05:18 +01002902
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002903 return classification_list
sousaedu80135b92021-02-17 15:05:18 +01002904 except (
2905 neExceptions.ConnectionFailed,
2906 ksExceptions.ClientException,
2907 neExceptions.NeutronException,
2908 ConnectionError,
2909 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002910 self._format_exception(e)
2911
2912 def delete_classification(self, class_id):
2913 self.logger.debug("Deleting Classification '%s' from VIM", class_id)
sousaedu80135b92021-02-17 15:05:18 +01002914
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002915 try:
2916 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00002917 self.neutron.delete_sfc_flow_classifier(class_id)
sousaedu80135b92021-02-17 15:05:18 +01002918
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002919 return class_id
sousaedu80135b92021-02-17 15:05:18 +01002920 except (
2921 neExceptions.ConnectionFailed,
2922 neExceptions.NeutronException,
2923 ksExceptions.ClientException,
2924 neExceptions.NeutronException,
2925 ConnectionError,
2926 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002927 self._format_exception(e)
2928
2929 def new_sfi(self, name, ingress_ports, egress_ports, sfc_encap=True):
sousaedu80135b92021-02-17 15:05:18 +01002930 self.logger.debug(
2931 "Adding a new Service Function Instance to VIM, named '%s'", name
2932 )
2933
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002934 try:
2935 new_sfi = None
2936 self._reload_connection()
2937 correlation = None
sousaedu80135b92021-02-17 15:05:18 +01002938
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002939 if sfc_encap:
sousaedu80135b92021-02-17 15:05:18 +01002940 correlation = "nsh"
2941
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002942 if len(ingress_ports) != 1:
tierno72774862020-05-04 11:44:15 +00002943 raise vimconn.VimConnNotSupportedException(
sousaedu80135b92021-02-17 15:05:18 +01002944 "OpenStack VIM connector can only have 1 ingress port per SFI"
2945 )
2946
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002947 if len(egress_ports) != 1:
tierno72774862020-05-04 11:44:15 +00002948 raise vimconn.VimConnNotSupportedException(
sousaedu80135b92021-02-17 15:05:18 +01002949 "OpenStack VIM connector can only have 1 egress port per SFI"
2950 )
2951
2952 sfi_dict = {
2953 "name": name,
2954 "ingress": ingress_ports[0],
2955 "egress": egress_ports[0],
2956 "service_function_parameters": {"correlation": correlation},
2957 }
2958 new_sfi = self.neutron.create_sfc_port_pair({"port_pair": sfi_dict})
2959
2960 return new_sfi["port_pair"]["id"]
2961 except (
2962 neExceptions.ConnectionFailed,
2963 ksExceptions.ClientException,
2964 neExceptions.NeutronException,
2965 ConnectionError,
2966 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002967 if new_sfi:
2968 try:
sousaedu80135b92021-02-17 15:05:18 +01002969 self.neutron.delete_sfc_port_pair(new_sfi["port_pair"]["id"])
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002970 except Exception:
2971 self.logger.error(
sousaedu80135b92021-02-17 15:05:18 +01002972 "Creation of Service Function Instance failed, with "
2973 "subsequent deletion failure as well."
2974 )
2975
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002976 self._format_exception(e)
2977
2978 def get_sfi(self, sfi_id):
sousaedu80135b92021-02-17 15:05:18 +01002979 self.logger.debug("Getting Service Function Instance %s from VIM", sfi_id)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002980 filter_dict = {"id": sfi_id}
2981 sfi_list = self.get_sfi_list(filter_dict)
sousaedu80135b92021-02-17 15:05:18 +01002982
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002983 if len(sfi_list) == 0:
sousaedu80135b92021-02-17 15:05:18 +01002984 raise vimconn.VimConnNotFoundException(
2985 "Service Function Instance '{}' not found".format(sfi_id)
2986 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002987 elif len(sfi_list) > 1:
tierno72774862020-05-04 11:44:15 +00002988 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01002989 "Found more than one Service Function Instance with this criteria"
2990 )
2991
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002992 sfi = sfi_list[0]
sousaedu80135b92021-02-17 15:05:18 +01002993
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002994 return sfi
2995
2996 def get_sfi_list(self, filter_dict={}):
sousaedu80135b92021-02-17 15:05:18 +01002997 self.logger.debug(
2998 "Getting Service Function Instances from VIM filter: '%s'", str(filter_dict)
2999 )
3000
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003001 try:
3002 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +01003003 filter_dict_os = filter_dict.copy()
sousaedu80135b92021-02-17 15:05:18 +01003004
tierno69b590e2018-03-13 18:52:23 +01003005 if self.api_version3 and "tenant_id" in filter_dict_os:
sousaedu80135b92021-02-17 15:05:18 +01003006 filter_dict_os["project_id"] = filter_dict_os.pop("tenant_id")
3007
tierno69b590e2018-03-13 18:52:23 +01003008 sfi_dict = self.neutron.list_sfc_port_pairs(**filter_dict_os)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003009 sfi_list = sfi_dict["port_pairs"]
3010 self.__sfi_os2mano(sfi_list)
sousaedu80135b92021-02-17 15:05:18 +01003011
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003012 return sfi_list
sousaedu80135b92021-02-17 15:05:18 +01003013 except (
3014 neExceptions.ConnectionFailed,
3015 ksExceptions.ClientException,
3016 neExceptions.NeutronException,
3017 ConnectionError,
3018 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003019 self._format_exception(e)
3020
3021 def delete_sfi(self, sfi_id):
sousaedu80135b92021-02-17 15:05:18 +01003022 self.logger.debug("Deleting Service Function Instance '%s' from VIM", sfi_id)
3023
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003024 try:
3025 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00003026 self.neutron.delete_sfc_port_pair(sfi_id)
sousaedu80135b92021-02-17 15:05:18 +01003027
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003028 return sfi_id
sousaedu80135b92021-02-17 15:05:18 +01003029 except (
3030 neExceptions.ConnectionFailed,
3031 neExceptions.NeutronException,
3032 ksExceptions.ClientException,
3033 neExceptions.NeutronException,
3034 ConnectionError,
3035 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003036 self._format_exception(e)
3037
3038 def new_sf(self, name, sfis, sfc_encap=True):
tierno7d782ef2019-10-04 12:56:31 +00003039 self.logger.debug("Adding a new Service Function to VIM, named '%s'", name)
sousaedu80135b92021-02-17 15:05:18 +01003040
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003041 try:
3042 new_sf = None
3043 self._reload_connection()
tierno9c5c8322018-03-23 15:44:03 +01003044 # correlation = None
3045 # if sfc_encap:
sousaedu80135b92021-02-17 15:05:18 +01003046 # correlation = "nsh"
3047
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003048 for instance in sfis:
3049 sfi = self.get_sfi(instance)
sousaedu80135b92021-02-17 15:05:18 +01003050
3051 if sfi.get("sfc_encap") != sfc_encap:
tierno72774862020-05-04 11:44:15 +00003052 raise vimconn.VimConnNotSupportedException(
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003053 "OpenStack VIM connector requires all SFIs of the "
sousaedu80135b92021-02-17 15:05:18 +01003054 "same SF to share the same SFC Encapsulation"
3055 )
3056
3057 sf_dict = {"name": name, "port_pairs": sfis}
3058 new_sf = self.neutron.create_sfc_port_pair_group(
3059 {"port_pair_group": sf_dict}
3060 )
3061
3062 return new_sf["port_pair_group"]["id"]
3063 except (
3064 neExceptions.ConnectionFailed,
3065 ksExceptions.ClientException,
3066 neExceptions.NeutronException,
3067 ConnectionError,
3068 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003069 if new_sf:
3070 try:
Igor D.Ccaadc442017-11-06 12:48:48 +00003071 self.neutron.delete_sfc_port_pair_group(
sousaedu80135b92021-02-17 15:05:18 +01003072 new_sf["port_pair_group"]["id"]
3073 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003074 except Exception:
3075 self.logger.error(
sousaedu80135b92021-02-17 15:05:18 +01003076 "Creation of Service Function failed, with "
3077 "subsequent deletion failure as well."
3078 )
3079
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003080 self._format_exception(e)
3081
3082 def get_sf(self, sf_id):
3083 self.logger.debug("Getting Service Function %s from VIM", sf_id)
3084 filter_dict = {"id": sf_id}
3085 sf_list = self.get_sf_list(filter_dict)
sousaedu80135b92021-02-17 15:05:18 +01003086
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003087 if len(sf_list) == 0:
tierno72774862020-05-04 11:44:15 +00003088 raise vimconn.VimConnNotFoundException(
sousaedu80135b92021-02-17 15:05:18 +01003089 "Service Function '{}' not found".format(sf_id)
3090 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003091 elif len(sf_list) > 1:
tierno72774862020-05-04 11:44:15 +00003092 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01003093 "Found more than one Service Function with this criteria"
3094 )
3095
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003096 sf = sf_list[0]
sousaedu80135b92021-02-17 15:05:18 +01003097
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003098 return sf
3099
3100 def get_sf_list(self, filter_dict={}):
sousaedu80135b92021-02-17 15:05:18 +01003101 self.logger.debug(
3102 "Getting Service Function from VIM filter: '%s'", str(filter_dict)
3103 )
3104
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003105 try:
3106 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +01003107 filter_dict_os = filter_dict.copy()
sousaedu80135b92021-02-17 15:05:18 +01003108
tierno69b590e2018-03-13 18:52:23 +01003109 if self.api_version3 and "tenant_id" in filter_dict_os:
sousaedu80135b92021-02-17 15:05:18 +01003110 filter_dict_os["project_id"] = filter_dict_os.pop("tenant_id")
3111
tierno69b590e2018-03-13 18:52:23 +01003112 sf_dict = self.neutron.list_sfc_port_pair_groups(**filter_dict_os)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003113 sf_list = sf_dict["port_pair_groups"]
3114 self.__sf_os2mano(sf_list)
sousaedu80135b92021-02-17 15:05:18 +01003115
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003116 return sf_list
sousaedu80135b92021-02-17 15:05:18 +01003117 except (
3118 neExceptions.ConnectionFailed,
3119 ksExceptions.ClientException,
3120 neExceptions.NeutronException,
3121 ConnectionError,
3122 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003123 self._format_exception(e)
3124
3125 def delete_sf(self, sf_id):
3126 self.logger.debug("Deleting Service Function '%s' from VIM", sf_id)
sousaedu80135b92021-02-17 15:05:18 +01003127
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003128 try:
3129 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00003130 self.neutron.delete_sfc_port_pair_group(sf_id)
sousaedu80135b92021-02-17 15:05:18 +01003131
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003132 return sf_id
sousaedu80135b92021-02-17 15:05:18 +01003133 except (
3134 neExceptions.ConnectionFailed,
3135 neExceptions.NeutronException,
3136 ksExceptions.ClientException,
3137 neExceptions.NeutronException,
3138 ConnectionError,
3139 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003140 self._format_exception(e)
3141
3142 def new_sfp(self, name, classifications, sfs, sfc_encap=True, spi=None):
tierno7d782ef2019-10-04 12:56:31 +00003143 self.logger.debug("Adding a new Service Function Path to VIM, named '%s'", name)
sousaedu80135b92021-02-17 15:05:18 +01003144
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003145 try:
3146 new_sfp = None
3147 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00003148 # In networking-sfc the MPLS encapsulation is legacy
3149 # should be used when no full SFC Encapsulation is intended
sousaedu80135b92021-02-17 15:05:18 +01003150 correlation = "mpls"
3151
Igor D.Ccaadc442017-11-06 12:48:48 +00003152 if sfc_encap:
sousaedu80135b92021-02-17 15:05:18 +01003153 correlation = "nsh"
3154
3155 sfp_dict = {
3156 "name": name,
3157 "flow_classifiers": classifications,
3158 "port_pair_groups": sfs,
3159 "chain_parameters": {"correlation": correlation},
3160 }
3161
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003162 if spi:
sousaedu80135b92021-02-17 15:05:18 +01003163 sfp_dict["chain_id"] = spi
3164
3165 new_sfp = self.neutron.create_sfc_port_chain({"port_chain": sfp_dict})
3166
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003167 return new_sfp["port_chain"]["id"]
sousaedu80135b92021-02-17 15:05:18 +01003168 except (
3169 neExceptions.ConnectionFailed,
3170 ksExceptions.ClientException,
3171 neExceptions.NeutronException,
3172 ConnectionError,
3173 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003174 if new_sfp:
3175 try:
sousaedu80135b92021-02-17 15:05:18 +01003176 self.neutron.delete_sfc_port_chain(new_sfp["port_chain"]["id"])
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003177 except Exception:
3178 self.logger.error(
sousaedu80135b92021-02-17 15:05:18 +01003179 "Creation of Service Function Path failed, with "
3180 "subsequent deletion failure as well."
3181 )
3182
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003183 self._format_exception(e)
3184
3185 def get_sfp(self, sfp_id):
3186 self.logger.debug(" Getting Service Function Path %s from VIM", sfp_id)
sousaedu80135b92021-02-17 15:05:18 +01003187
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003188 filter_dict = {"id": sfp_id}
3189 sfp_list = self.get_sfp_list(filter_dict)
sousaedu80135b92021-02-17 15:05:18 +01003190
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003191 if len(sfp_list) == 0:
tierno72774862020-05-04 11:44:15 +00003192 raise vimconn.VimConnNotFoundException(
sousaedu80135b92021-02-17 15:05:18 +01003193 "Service Function Path '{}' not found".format(sfp_id)
3194 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003195 elif len(sfp_list) > 1:
tierno72774862020-05-04 11:44:15 +00003196 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01003197 "Found more than one Service Function Path with this criteria"
3198 )
3199
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003200 sfp = sfp_list[0]
sousaedu80135b92021-02-17 15:05:18 +01003201
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003202 return sfp
3203
3204 def get_sfp_list(self, filter_dict={}):
sousaedu80135b92021-02-17 15:05:18 +01003205 self.logger.debug(
3206 "Getting Service Function Paths from VIM filter: '%s'", str(filter_dict)
3207 )
3208
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003209 try:
3210 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +01003211 filter_dict_os = filter_dict.copy()
sousaedu80135b92021-02-17 15:05:18 +01003212
tierno69b590e2018-03-13 18:52:23 +01003213 if self.api_version3 and "tenant_id" in filter_dict_os:
sousaedu80135b92021-02-17 15:05:18 +01003214 filter_dict_os["project_id"] = filter_dict_os.pop("tenant_id")
3215
tierno69b590e2018-03-13 18:52:23 +01003216 sfp_dict = self.neutron.list_sfc_port_chains(**filter_dict_os)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003217 sfp_list = sfp_dict["port_chains"]
3218 self.__sfp_os2mano(sfp_list)
sousaedu80135b92021-02-17 15:05:18 +01003219
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003220 return sfp_list
sousaedu80135b92021-02-17 15:05:18 +01003221 except (
3222 neExceptions.ConnectionFailed,
3223 ksExceptions.ClientException,
3224 neExceptions.NeutronException,
3225 ConnectionError,
3226 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003227 self._format_exception(e)
3228
3229 def delete_sfp(self, sfp_id):
tierno7d782ef2019-10-04 12:56:31 +00003230 self.logger.debug("Deleting Service Function Path '%s' from VIM", sfp_id)
sousaedu80135b92021-02-17 15:05:18 +01003231
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003232 try:
3233 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00003234 self.neutron.delete_sfc_port_chain(sfp_id)
sousaedu80135b92021-02-17 15:05:18 +01003235
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003236 return sfp_id
sousaedu80135b92021-02-17 15:05:18 +01003237 except (
3238 neExceptions.ConnectionFailed,
3239 neExceptions.NeutronException,
3240 ksExceptions.ClientException,
3241 neExceptions.NeutronException,
3242 ConnectionError,
3243 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003244 self._format_exception(e)
borsatti8a2dda32019-12-18 15:08:57 +00003245
borsatti8a2dda32019-12-18 15:08:57 +00003246 def refresh_sfps_status(self, sfp_list):
tierno1ec592d2020-06-16 15:29:47 +00003247 """Get the status of the service function path
sousaedu80135b92021-02-17 15:05:18 +01003248 Params: the list of sfp identifiers
3249 Returns a dictionary with:
3250 vm_id: #VIM id of this service function path
3251 status: #Mandatory. Text with one of:
3252 # DELETED (not found at vim)
3253 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
3254 # OTHER (Vim reported other status not understood)
3255 # ERROR (VIM indicates an ERROR status)
3256 # ACTIVE,
3257 # CREATING (on building process)
3258 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
3259 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)F
tierno1ec592d2020-06-16 15:29:47 +00003260 """
3261 sfp_dict = {}
sousaedu80135b92021-02-17 15:05:18 +01003262 self.logger.debug(
3263 "refresh_sfps status: Getting tenant SFP information from VIM"
3264 )
3265
borsatti8a2dda32019-12-18 15:08:57 +00003266 for sfp_id in sfp_list:
tierno1ec592d2020-06-16 15:29:47 +00003267 sfp = {}
sousaedu80135b92021-02-17 15:05:18 +01003268
borsatti8a2dda32019-12-18 15:08:57 +00003269 try:
3270 sfp_vim = self.get_sfp(sfp_id)
sousaedu80135b92021-02-17 15:05:18 +01003271
3272 if sfp_vim["spi"]:
3273 sfp["status"] = vmStatus2manoFormat["ACTIVE"]
borsatti8a2dda32019-12-18 15:08:57 +00003274 else:
sousaedu80135b92021-02-17 15:05:18 +01003275 sfp["status"] = "OTHER"
3276 sfp["error_msg"] = "VIM status reported " + sfp["status"]
borsatti8a2dda32019-12-18 15:08:57 +00003277
sousaedu80135b92021-02-17 15:05:18 +01003278 sfp["vim_info"] = self.serialize(sfp_vim)
borsatti8a2dda32019-12-18 15:08:57 +00003279
sousaedu80135b92021-02-17 15:05:18 +01003280 if sfp_vim.get("fault"):
3281 sfp["error_msg"] = str(sfp_vim["fault"])
tierno72774862020-05-04 11:44:15 +00003282 except vimconn.VimConnNotFoundException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003283 self.logger.error("Exception getting sfp status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003284 sfp["status"] = "DELETED"
3285 sfp["error_msg"] = str(e)
tierno72774862020-05-04 11:44:15 +00003286 except vimconn.VimConnException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003287 self.logger.error("Exception getting sfp status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003288 sfp["status"] = "VIM_ERROR"
3289 sfp["error_msg"] = str(e)
3290
borsatti8a2dda32019-12-18 15:08:57 +00003291 sfp_dict[sfp_id] = sfp
sousaedu80135b92021-02-17 15:05:18 +01003292
borsatti8a2dda32019-12-18 15:08:57 +00003293 return sfp_dict
3294
borsatti8a2dda32019-12-18 15:08:57 +00003295 def refresh_sfis_status(self, sfi_list):
tierno1ec592d2020-06-16 15:29:47 +00003296 """Get the status of the service function instances
sousaedu80135b92021-02-17 15:05:18 +01003297 Params: the list of sfi identifiers
3298 Returns a dictionary with:
3299 vm_id: #VIM id of this service function instance
3300 status: #Mandatory. Text with one of:
3301 # DELETED (not found at vim)
3302 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
3303 # OTHER (Vim reported other status not understood)
3304 # ERROR (VIM indicates an ERROR status)
3305 # ACTIVE,
3306 # CREATING (on building process)
3307 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
3308 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
tierno1ec592d2020-06-16 15:29:47 +00003309 """
3310 sfi_dict = {}
sousaedu80135b92021-02-17 15:05:18 +01003311 self.logger.debug(
3312 "refresh_sfis status: Getting tenant sfi information from VIM"
3313 )
3314
borsatti8a2dda32019-12-18 15:08:57 +00003315 for sfi_id in sfi_list:
tierno1ec592d2020-06-16 15:29:47 +00003316 sfi = {}
sousaedu80135b92021-02-17 15:05:18 +01003317
borsatti8a2dda32019-12-18 15:08:57 +00003318 try:
3319 sfi_vim = self.get_sfi(sfi_id)
sousaedu80135b92021-02-17 15:05:18 +01003320
borsatti8a2dda32019-12-18 15:08:57 +00003321 if sfi_vim:
sousaedu80135b92021-02-17 15:05:18 +01003322 sfi["status"] = vmStatus2manoFormat["ACTIVE"]
borsatti8a2dda32019-12-18 15:08:57 +00003323 else:
sousaedu80135b92021-02-17 15:05:18 +01003324 sfi["status"] = "OTHER"
3325 sfi["error_msg"] = "VIM status reported " + sfi["status"]
borsatti8a2dda32019-12-18 15:08:57 +00003326
sousaedu80135b92021-02-17 15:05:18 +01003327 sfi["vim_info"] = self.serialize(sfi_vim)
borsatti8a2dda32019-12-18 15:08:57 +00003328
sousaedu80135b92021-02-17 15:05:18 +01003329 if sfi_vim.get("fault"):
3330 sfi["error_msg"] = str(sfi_vim["fault"])
tierno72774862020-05-04 11:44:15 +00003331 except vimconn.VimConnNotFoundException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003332 self.logger.error("Exception getting sfi status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003333 sfi["status"] = "DELETED"
3334 sfi["error_msg"] = str(e)
tierno72774862020-05-04 11:44:15 +00003335 except vimconn.VimConnException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003336 self.logger.error("Exception getting sfi status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003337 sfi["status"] = "VIM_ERROR"
3338 sfi["error_msg"] = str(e)
3339
borsatti8a2dda32019-12-18 15:08:57 +00003340 sfi_dict[sfi_id] = sfi
sousaedu80135b92021-02-17 15:05:18 +01003341
borsatti8a2dda32019-12-18 15:08:57 +00003342 return sfi_dict
3343
borsatti8a2dda32019-12-18 15:08:57 +00003344 def refresh_sfs_status(self, sf_list):
tierno1ec592d2020-06-16 15:29:47 +00003345 """Get the status of the service functions
sousaedu80135b92021-02-17 15:05:18 +01003346 Params: the list of sf identifiers
3347 Returns a dictionary with:
3348 vm_id: #VIM id of this service function
3349 status: #Mandatory. Text with one of:
3350 # DELETED (not found at vim)
3351 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
3352 # OTHER (Vim reported other status not understood)
3353 # ERROR (VIM indicates an ERROR status)
3354 # ACTIVE,
3355 # CREATING (on building process)
3356 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
3357 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
tierno1ec592d2020-06-16 15:29:47 +00003358 """
3359 sf_dict = {}
borsatti8a2dda32019-12-18 15:08:57 +00003360 self.logger.debug("refresh_sfs status: Getting tenant sf information from VIM")
sousaedu80135b92021-02-17 15:05:18 +01003361
borsatti8a2dda32019-12-18 15:08:57 +00003362 for sf_id in sf_list:
tierno1ec592d2020-06-16 15:29:47 +00003363 sf = {}
sousaedu80135b92021-02-17 15:05:18 +01003364
borsatti8a2dda32019-12-18 15:08:57 +00003365 try:
3366 sf_vim = self.get_sf(sf_id)
sousaedu80135b92021-02-17 15:05:18 +01003367
borsatti8a2dda32019-12-18 15:08:57 +00003368 if sf_vim:
sousaedu80135b92021-02-17 15:05:18 +01003369 sf["status"] = vmStatus2manoFormat["ACTIVE"]
borsatti8a2dda32019-12-18 15:08:57 +00003370 else:
sousaedu80135b92021-02-17 15:05:18 +01003371 sf["status"] = "OTHER"
3372 sf["error_msg"] = "VIM status reported " + sf_vim["status"]
borsatti8a2dda32019-12-18 15:08:57 +00003373
sousaedu80135b92021-02-17 15:05:18 +01003374 sf["vim_info"] = self.serialize(sf_vim)
borsatti8a2dda32019-12-18 15:08:57 +00003375
sousaedu80135b92021-02-17 15:05:18 +01003376 if sf_vim.get("fault"):
3377 sf["error_msg"] = str(sf_vim["fault"])
tierno72774862020-05-04 11:44:15 +00003378 except vimconn.VimConnNotFoundException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003379 self.logger.error("Exception getting sf status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003380 sf["status"] = "DELETED"
3381 sf["error_msg"] = str(e)
tierno72774862020-05-04 11:44:15 +00003382 except vimconn.VimConnException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003383 self.logger.error("Exception getting sf status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003384 sf["status"] = "VIM_ERROR"
3385 sf["error_msg"] = str(e)
3386
borsatti8a2dda32019-12-18 15:08:57 +00003387 sf_dict[sf_id] = sf
sousaedu80135b92021-02-17 15:05:18 +01003388
borsatti8a2dda32019-12-18 15:08:57 +00003389 return sf_dict
3390
borsatti8a2dda32019-12-18 15:08:57 +00003391 def refresh_classifications_status(self, classification_list):
tierno1ec592d2020-06-16 15:29:47 +00003392 """Get the status of the classifications
sousaedu80135b92021-02-17 15:05:18 +01003393 Params: the list of classification identifiers
3394 Returns a dictionary with:
3395 vm_id: #VIM id of this classifier
3396 status: #Mandatory. Text with one of:
3397 # DELETED (not found at vim)
3398 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
3399 # OTHER (Vim reported other status not understood)
3400 # ERROR (VIM indicates an ERROR status)
3401 # ACTIVE,
3402 # CREATING (on building process)
3403 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
3404 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
tierno1ec592d2020-06-16 15:29:47 +00003405 """
3406 classification_dict = {}
sousaedu80135b92021-02-17 15:05:18 +01003407 self.logger.debug(
3408 "refresh_classifications status: Getting tenant classification information from VIM"
3409 )
3410
borsatti8a2dda32019-12-18 15:08:57 +00003411 for classification_id in classification_list:
tierno1ec592d2020-06-16 15:29:47 +00003412 classification = {}
sousaedu80135b92021-02-17 15:05:18 +01003413
borsatti8a2dda32019-12-18 15:08:57 +00003414 try:
3415 classification_vim = self.get_classification(classification_id)
sousaedu80135b92021-02-17 15:05:18 +01003416
borsatti8a2dda32019-12-18 15:08:57 +00003417 if classification_vim:
sousaedu80135b92021-02-17 15:05:18 +01003418 classification["status"] = vmStatus2manoFormat["ACTIVE"]
borsatti8a2dda32019-12-18 15:08:57 +00003419 else:
sousaedu80135b92021-02-17 15:05:18 +01003420 classification["status"] = "OTHER"
3421 classification["error_msg"] = (
3422 "VIM status reported " + classification["status"]
3423 )
borsatti8a2dda32019-12-18 15:08:57 +00003424
sousaedu80135b92021-02-17 15:05:18 +01003425 classification["vim_info"] = self.serialize(classification_vim)
borsatti8a2dda32019-12-18 15:08:57 +00003426
sousaedu80135b92021-02-17 15:05:18 +01003427 if classification_vim.get("fault"):
3428 classification["error_msg"] = str(classification_vim["fault"])
tierno72774862020-05-04 11:44:15 +00003429 except vimconn.VimConnNotFoundException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003430 self.logger.error("Exception getting classification status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003431 classification["status"] = "DELETED"
3432 classification["error_msg"] = str(e)
tierno72774862020-05-04 11:44:15 +00003433 except vimconn.VimConnException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003434 self.logger.error("Exception getting classification status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003435 classification["status"] = "VIM_ERROR"
3436 classification["error_msg"] = str(e)
3437
borsatti8a2dda32019-12-18 15:08:57 +00003438 classification_dict[classification_id] = classification
sousaedu80135b92021-02-17 15:05:18 +01003439
borsatti8a2dda32019-12-18 15:08:57 +00003440 return classification_dict