blob: c59bf9045cc65ccebd663af569e2d474d4465c33 [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
867 new_net = self.neutron.create_network({"network": network_dict})
garciadeblasebd66722019-01-31 16:01:31 +0000868 # print new_net
869 # create subnetwork, even if there is no profile
sousaedu80135b92021-02-17 15:05:18 +0100870
garciadeblas9f8456e2016-09-05 05:02:59 +0200871 if not ip_profile:
872 ip_profile = {}
sousaedu80135b92021-02-17 15:05:18 +0100873
874 if not ip_profile.get("subnet_address"):
tierno1ec592d2020-06-16 15:29:47 +0000875 # Fake subnet is required
garciadeblas2299e3b2017-01-26 14:35:55 +0000876 subnet_rand = random.randint(0, 255)
sousaedu80135b92021-02-17 15:05:18 +0100877 ip_profile["subnet_address"] = "192.168.{}.0/24".format(subnet_rand)
878
879 if "ip_version" not in ip_profile:
880 ip_profile["ip_version"] = "IPv4"
881
882 subnet = {
883 "name": net_name + "-subnet",
884 "network_id": new_net["network"]["id"],
885 "ip_version": 4 if ip_profile["ip_version"] == "IPv4" else 6,
886 "cidr": ip_profile["subnet_address"],
887 }
888
tiernoa1fb4462017-06-30 12:25:50 +0200889 # Gateway should be set to None if not needed. Otherwise openstack assigns one by default
sousaedu80135b92021-02-17 15:05:18 +0100890 if ip_profile.get("gateway_address"):
891 subnet["gateway_ip"] = ip_profile["gateway_address"]
tierno55d234c2018-07-04 18:29:21 +0200892 else:
sousaedu80135b92021-02-17 15:05:18 +0100893 subnet["gateway_ip"] = None
894
895 if ip_profile.get("dns_address"):
896 subnet["dns_nameservers"] = ip_profile["dns_address"].split(";")
897
898 if "dhcp_enabled" in ip_profile:
899 subnet["enable_dhcp"] = (
900 False
901 if ip_profile["dhcp_enabled"] == "false"
902 or ip_profile["dhcp_enabled"] is False
903 else True
904 )
905
906 if ip_profile.get("dhcp_start_address"):
907 subnet["allocation_pools"] = []
908 subnet["allocation_pools"].append(dict())
909 subnet["allocation_pools"][0]["start"] = ip_profile[
910 "dhcp_start_address"
911 ]
912
913 if ip_profile.get("dhcp_count"):
914 # parts = ip_profile["dhcp_start_address"].split(".")
tierno1ec592d2020-06-16 15:29:47 +0000915 # ip_int = (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3])
sousaedu80135b92021-02-17 15:05:18 +0100916 ip_int = int(netaddr.IPAddress(ip_profile["dhcp_start_address"]))
917 ip_int += ip_profile["dhcp_count"] - 1
garciadeblas9f8456e2016-09-05 05:02:59 +0200918 ip_str = str(netaddr.IPAddress(ip_int))
sousaedu80135b92021-02-17 15:05:18 +0100919 subnet["allocation_pools"][0]["end"] = ip_str
920
tierno1ec592d2020-06-16 15:29:47 +0000921 # self.logger.debug(">>>>>>>>>>>>>>>>>> Subnet: %s", str(subnet))
922 self.neutron.create_subnet({"subnet": subnet})
garciadeblasebd66722019-01-31 16:01:31 +0000923
sousaedu80135b92021-02-17 15:05:18 +0100924 if net_type == "data" and self.config.get("multisegment_support"):
925 if self.config.get("l2gw_support"):
garciadeblasebd66722019-01-31 16:01:31 +0000926 l2gw_list = self.neutron.list_l2_gateways().get("l2_gateways", ())
927 for l2gw in l2gw_list:
tierno1ec592d2020-06-16 15:29:47 +0000928 l2gw_conn = {
929 "l2_gateway_id": l2gw["id"],
930 "network_id": new_net["network"]["id"],
931 "segmentation_id": str(vlanID),
932 }
sousaedu80135b92021-02-17 15:05:18 +0100933 new_l2gw_conn = self.neutron.create_l2_gateway_connection(
934 {"l2_gateway_connection": l2gw_conn}
935 )
936 created_items[
937 "l2gwconn:"
938 + str(new_l2gw_conn["l2_gateway_connection"]["id"])
939 ] = True
940
garciadeblasebd66722019-01-31 16:01:31 +0000941 return new_net["network"]["id"], created_items
tierno41a69812018-02-16 14:34:33 +0100942 except Exception as e:
tierno1ec592d2020-06-16 15:29:47 +0000943 # delete l2gw connections (if any) before deleting the network
garciadeblasebd66722019-01-31 16:01:31 +0000944 for k, v in created_items.items():
945 if not v: # skip already deleted
946 continue
sousaedu80135b92021-02-17 15:05:18 +0100947
garciadeblasebd66722019-01-31 16:01:31 +0000948 try:
949 k_item, _, k_id = k.partition(":")
sousaedu80135b92021-02-17 15:05:18 +0100950
garciadeblasebd66722019-01-31 16:01:31 +0000951 if k_item == "l2gwconn":
952 self.neutron.delete_l2_gateway_connection(k_id)
953 except Exception as e2:
sousaedu80135b92021-02-17 15:05:18 +0100954 self.logger.error(
955 "Error deleting l2 gateway connection: {}: {}".format(
956 type(e2).__name__, e2
957 )
958 )
959
garciadeblasedca7b32016-09-29 14:01:52 +0000960 if new_net:
sousaedu80135b92021-02-17 15:05:18 +0100961 self.neutron.delete_network(new_net["network"]["id"])
962
tiernoae4a8d12016-07-08 12:30:39 +0200963 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100964
965 def get_network_list(self, filter_dict={}):
tierno1ec592d2020-06-16 15:29:47 +0000966 """Obtain tenant networks of VIM
tierno7edb6752016-03-21 17:37:52 +0100967 Filter_dict can be:
968 name: network name
969 id: network uuid
970 shared: boolean
971 tenant_id: tenant
972 admin_state_up: boolean
973 status: 'ACTIVE'
974 Returns the network list of dictionaries
tierno1ec592d2020-06-16 15:29:47 +0000975 """
tiernoae4a8d12016-07-08 12:30:39 +0200976 self.logger.debug("Getting network from VIM filter: '%s'", str(filter_dict))
sousaedu80135b92021-02-17 15:05:18 +0100977
tierno7edb6752016-03-21 17:37:52 +0100978 try:
979 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +0100980 filter_dict_os = filter_dict.copy()
sousaedu80135b92021-02-17 15:05:18 +0100981
tierno69b590e2018-03-13 18:52:23 +0100982 if self.api_version3 and "tenant_id" in filter_dict_os:
sousaedu80135b92021-02-17 15:05:18 +0100983 # TODO check
984 filter_dict_os["project_id"] = filter_dict_os.pop("tenant_id")
985
tierno69b590e2018-03-13 18:52:23 +0100986 net_dict = self.neutron.list_networks(**filter_dict_os)
tierno00e3df72017-11-29 17:20:13 +0100987 net_list = net_dict["networks"]
tierno7edb6752016-03-21 17:37:52 +0100988 self.__net_os2mano(net_list)
sousaedu80135b92021-02-17 15:05:18 +0100989
tiernoae4a8d12016-07-08 12:30:39 +0200990 return net_list
sousaedu80135b92021-02-17 15:05:18 +0100991 except (
992 neExceptions.ConnectionFailed,
993 ksExceptions.ClientException,
994 neExceptions.NeutronException,
995 ConnectionError,
996 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200997 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100998
tiernoae4a8d12016-07-08 12:30:39 +0200999 def get_network(self, net_id):
tierno1ec592d2020-06-16 15:29:47 +00001000 """Obtain details of network from VIM
1001 Returns the network information from a network id"""
tiernoae4a8d12016-07-08 12:30:39 +02001002 self.logger.debug(" Getting tenant network %s from VIM", net_id)
tierno1ec592d2020-06-16 15:29:47 +00001003 filter_dict = {"id": net_id}
tiernoae4a8d12016-07-08 12:30:39 +02001004 net_list = self.get_network_list(filter_dict)
sousaedu80135b92021-02-17 15:05:18 +01001005
tierno1ec592d2020-06-16 15:29:47 +00001006 if len(net_list) == 0:
sousaedu80135b92021-02-17 15:05:18 +01001007 raise vimconn.VimConnNotFoundException(
1008 "Network '{}' not found".format(net_id)
1009 )
tierno1ec592d2020-06-16 15:29:47 +00001010 elif len(net_list) > 1:
sousaedu80135b92021-02-17 15:05:18 +01001011 raise vimconn.VimConnConflictException(
1012 "Found more than one network with this criteria"
1013 )
1014
tierno7edb6752016-03-21 17:37:52 +01001015 net = net_list[0]
tierno1ec592d2020-06-16 15:29:47 +00001016 subnets = []
1017 for subnet_id in net.get("subnets", ()):
tierno7edb6752016-03-21 17:37:52 +01001018 try:
1019 subnet = self.neutron.show_subnet(subnet_id)
1020 except Exception as e:
sousaedu80135b92021-02-17 15:05:18 +01001021 self.logger.error(
1022 "osconnector.get_network(): Error getting subnet %s %s"
1023 % (net_id, str(e))
1024 )
tiernoae4a8d12016-07-08 12:30:39 +02001025 subnet = {"id": subnet_id, "fault": str(e)}
sousaedu80135b92021-02-17 15:05:18 +01001026
tierno7edb6752016-03-21 17:37:52 +01001027 subnets.append(subnet)
sousaedu80135b92021-02-17 15:05:18 +01001028
tierno7edb6752016-03-21 17:37:52 +01001029 net["subnets"] = subnets
sousaedu80135b92021-02-17 15:05:18 +01001030 net["encapsulation"] = net.get("provider:network_type")
1031 net["encapsulation_type"] = net.get("provider:network_type")
1032 net["segmentation_id"] = net.get("provider:segmentation_id")
1033 net["encapsulation_id"] = net.get("provider:segmentation_id")
1034
tiernoae4a8d12016-07-08 12:30:39 +02001035 return net
tierno7edb6752016-03-21 17:37:52 +01001036
garciadeblasebd66722019-01-31 16:01:31 +00001037 def delete_network(self, net_id, created_items=None):
1038 """
1039 Removes a tenant network from VIM and its associated elements
1040 :param net_id: VIM identifier of the network, provided by method new_network
1041 :param created_items: dictionary with extra items to be deleted. provided by method new_network
1042 Returns the network identifier or raises an exception upon error or when network is not found
1043 """
tiernoae4a8d12016-07-08 12:30:39 +02001044 self.logger.debug("Deleting network '%s' from VIM", net_id)
sousaedu80135b92021-02-17 15:05:18 +01001045
tierno1ec592d2020-06-16 15:29:47 +00001046 if created_items is None:
garciadeblasebd66722019-01-31 16:01:31 +00001047 created_items = {}
sousaedu80135b92021-02-17 15:05:18 +01001048
tierno7edb6752016-03-21 17:37:52 +01001049 try:
1050 self._reload_connection()
tierno1ec592d2020-06-16 15:29:47 +00001051 # delete l2gw connections (if any) before deleting the network
garciadeblasebd66722019-01-31 16:01:31 +00001052 for k, v in created_items.items():
1053 if not v: # skip already deleted
1054 continue
sousaedu80135b92021-02-17 15:05:18 +01001055
garciadeblasebd66722019-01-31 16:01:31 +00001056 try:
1057 k_item, _, k_id = k.partition(":")
1058 if k_item == "l2gwconn":
1059 self.neutron.delete_l2_gateway_connection(k_id)
1060 except Exception as e:
sousaedu80135b92021-02-17 15:05:18 +01001061 self.logger.error(
1062 "Error deleting l2 gateway connection: {}: {}".format(
1063 type(e).__name__, e
1064 )
1065 )
1066
tierno1ec592d2020-06-16 15:29:47 +00001067 # delete VM ports attached to this networks before the network
tierno7edb6752016-03-21 17:37:52 +01001068 ports = self.neutron.list_ports(network_id=net_id)
sousaedu80135b92021-02-17 15:05:18 +01001069 for p in ports["ports"]:
tierno7edb6752016-03-21 17:37:52 +01001070 try:
1071 self.neutron.delete_port(p["id"])
1072 except Exception as e:
tiernoae4a8d12016-07-08 12:30:39 +02001073 self.logger.error("Error deleting port %s: %s", p["id"], str(e))
sousaedu80135b92021-02-17 15:05:18 +01001074
tierno7edb6752016-03-21 17:37:52 +01001075 self.neutron.delete_network(net_id)
sousaedu80135b92021-02-17 15:05:18 +01001076
tiernoae4a8d12016-07-08 12:30:39 +02001077 return net_id
sousaedu80135b92021-02-17 15:05:18 +01001078 except (
1079 neExceptions.ConnectionFailed,
1080 neExceptions.NetworkNotFoundClient,
1081 neExceptions.NeutronException,
1082 ksExceptions.ClientException,
1083 neExceptions.NeutronException,
1084 ConnectionError,
1085 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001086 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001087
tiernoae4a8d12016-07-08 12:30:39 +02001088 def refresh_nets_status(self, net_list):
tierno1ec592d2020-06-16 15:29:47 +00001089 """Get the status of the networks
sousaedu80135b92021-02-17 15:05:18 +01001090 Params: the list of network identifiers
1091 Returns a dictionary with:
1092 net_id: #VIM id of this network
1093 status: #Mandatory. Text with one of:
1094 # DELETED (not found at vim)
1095 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1096 # OTHER (Vim reported other status not understood)
1097 # ERROR (VIM indicates an ERROR status)
1098 # ACTIVE, INACTIVE, DOWN (admin down),
1099 # BUILD (on building process)
1100 #
1101 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1102 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
tierno1ec592d2020-06-16 15:29:47 +00001103 """
1104 net_dict = {}
sousaedu80135b92021-02-17 15:05:18 +01001105
tiernoae4a8d12016-07-08 12:30:39 +02001106 for net_id in net_list:
1107 net = {}
sousaedu80135b92021-02-17 15:05:18 +01001108
tiernoae4a8d12016-07-08 12:30:39 +02001109 try:
1110 net_vim = self.get_network(net_id)
sousaedu80135b92021-02-17 15:05:18 +01001111
1112 if net_vim["status"] in netStatus2manoFormat:
1113 net["status"] = netStatus2manoFormat[net_vim["status"]]
tiernoae4a8d12016-07-08 12:30:39 +02001114 else:
1115 net["status"] = "OTHER"
sousaedu80135b92021-02-17 15:05:18 +01001116 net["error_msg"] = "VIM status reported " + net_vim["status"]
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001117
sousaedu80135b92021-02-17 15:05:18 +01001118 if net["status"] == "ACTIVE" and not net_vim["admin_state_up"]:
1119 net["status"] = "DOWN"
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001120
sousaedu80135b92021-02-17 15:05:18 +01001121 net["vim_info"] = self.serialize(net_vim)
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001122
sousaedu80135b92021-02-17 15:05:18 +01001123 if net_vim.get("fault"): # TODO
1124 net["error_msg"] = str(net_vim["fault"])
tierno72774862020-05-04 11:44:15 +00001125 except vimconn.VimConnNotFoundException as e:
tiernoae4a8d12016-07-08 12:30:39 +02001126 self.logger.error("Exception getting net status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01001127 net["status"] = "DELETED"
1128 net["error_msg"] = str(e)
tierno72774862020-05-04 11:44:15 +00001129 except vimconn.VimConnException as e:
tiernoae4a8d12016-07-08 12:30:39 +02001130 self.logger.error("Exception getting net status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01001131 net["status"] = "VIM_ERROR"
1132 net["error_msg"] = str(e)
tiernoae4a8d12016-07-08 12:30:39 +02001133 net_dict[net_id] = net
1134 return net_dict
1135
1136 def get_flavor(self, flavor_id):
tierno1ec592d2020-06-16 15:29:47 +00001137 """Obtain flavor details from the VIM. Returns the flavor dict details"""
tiernoae4a8d12016-07-08 12:30:39 +02001138 self.logger.debug("Getting flavor '%s'", flavor_id)
sousaedu80135b92021-02-17 15:05:18 +01001139
tierno7edb6752016-03-21 17:37:52 +01001140 try:
1141 self._reload_connection()
1142 flavor = self.nova.flavors.find(id=flavor_id)
tierno1ec592d2020-06-16 15:29:47 +00001143 # TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
sousaedu80135b92021-02-17 15:05:18 +01001144
tiernoae4a8d12016-07-08 12:30:39 +02001145 return flavor.to_dict()
sousaedu80135b92021-02-17 15:05:18 +01001146 except (
1147 nvExceptions.NotFound,
1148 nvExceptions.ClientException,
1149 ksExceptions.ClientException,
1150 ConnectionError,
1151 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001152 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001153
tiernocf157a82017-01-30 14:07:06 +01001154 def get_flavor_id_from_data(self, flavor_dict):
1155 """Obtain flavor id that match the flavor description
sousaedu80135b92021-02-17 15:05:18 +01001156 Returns the flavor_id or raises a vimconnNotFoundException
1157 flavor_dict: contains the required ram, vcpus, disk
1158 If 'use_existing_flavors' is set to True at config, the closer flavor that provides same or more ram, vcpus
1159 and disk is returned. Otherwise a flavor with exactly same ram, vcpus and disk is returned or a
1160 vimconnNotFoundException is raised
tiernocf157a82017-01-30 14:07:06 +01001161 """
sousaedu80135b92021-02-17 15:05:18 +01001162 exact_match = False if self.config.get("use_existing_flavors") else True
1163
tiernocf157a82017-01-30 14:07:06 +01001164 try:
1165 self._reload_connection()
tiernoe26fc7a2017-05-30 14:43:03 +02001166 flavor_candidate_id = None
1167 flavor_candidate_data = (10000, 10000, 10000)
sousaedu80135b92021-02-17 15:05:18 +01001168 flavor_target = (
1169 flavor_dict["ram"],
1170 flavor_dict["vcpus"],
1171 flavor_dict["disk"],
1172 )
tiernoe26fc7a2017-05-30 14:43:03 +02001173 # numa=None
anwarsae5f52c2019-04-22 10:35:27 +05301174 extended = flavor_dict.get("extended", {})
1175 if extended:
tierno1ec592d2020-06-16 15:29:47 +00001176 # TODO
sousaedu80135b92021-02-17 15:05:18 +01001177 raise vimconn.VimConnNotFoundException(
1178 "Flavor with EPA still not implemented"
1179 )
tiernocf157a82017-01-30 14:07:06 +01001180 # if len(numas) > 1:
tierno72774862020-05-04 11:44:15 +00001181 # raise vimconn.VimConnNotFoundException("Cannot find any flavor with more than one numa")
tiernocf157a82017-01-30 14:07:06 +01001182 # numa=numas[0]
1183 # numas = extended.get("numas")
1184 for flavor in self.nova.flavors.list():
1185 epa = flavor.get_keys()
sousaedu80135b92021-02-17 15:05:18 +01001186
tiernocf157a82017-01-30 14:07:06 +01001187 if epa:
1188 continue
tiernoe26fc7a2017-05-30 14:43:03 +02001189 # TODO
sousaedu80135b92021-02-17 15:05:18 +01001190
tiernoe26fc7a2017-05-30 14:43:03 +02001191 flavor_data = (flavor.ram, flavor.vcpus, flavor.disk)
1192 if flavor_data == flavor_target:
1193 return flavor.id
sousaedu80135b92021-02-17 15:05:18 +01001194 elif (
1195 not exact_match
1196 and flavor_target < flavor_data < flavor_candidate_data
1197 ):
tiernoe26fc7a2017-05-30 14:43:03 +02001198 flavor_candidate_id = flavor.id
1199 flavor_candidate_data = flavor_data
sousaedu80135b92021-02-17 15:05:18 +01001200
tiernoe26fc7a2017-05-30 14:43:03 +02001201 if not exact_match and flavor_candidate_id:
1202 return flavor_candidate_id
sousaedu80135b92021-02-17 15:05:18 +01001203
1204 raise vimconn.VimConnNotFoundException(
1205 "Cannot find any flavor matching '{}'".format(flavor_dict)
1206 )
1207 except (
1208 nvExceptions.NotFound,
1209 nvExceptions.ClientException,
1210 ksExceptions.ClientException,
1211 ConnectionError,
1212 ) as e:
tiernocf157a82017-01-30 14:07:06 +01001213 self._format_exception(e)
1214
anwarsae5f52c2019-04-22 10:35:27 +05301215 def process_resource_quota(self, quota, prefix, extra_specs):
1216 """
1217 :param prefix:
borsatti8a2dda32019-12-18 15:08:57 +00001218 :param extra_specs:
anwarsae5f52c2019-04-22 10:35:27 +05301219 :return:
1220 """
sousaedu80135b92021-02-17 15:05:18 +01001221 if "limit" in quota:
1222 extra_specs["quota:" + prefix + "_limit"] = quota["limit"]
1223
1224 if "reserve" in quota:
1225 extra_specs["quota:" + prefix + "_reservation"] = quota["reserve"]
1226
1227 if "shares" in quota:
anwarsae5f52c2019-04-22 10:35:27 +05301228 extra_specs["quota:" + prefix + "_shares_level"] = "custom"
sousaedu80135b92021-02-17 15:05:18 +01001229 extra_specs["quota:" + prefix + "_shares_share"] = quota["shares"]
anwarsae5f52c2019-04-22 10:35:27 +05301230
tiernoae4a8d12016-07-08 12:30:39 +02001231 def new_flavor(self, flavor_data, change_name_if_used=True):
tierno1ec592d2020-06-16 15:29:47 +00001232 """Adds a tenant flavor to openstack VIM
1233 if change_name_if_used is True, it will change name in case of conflict, because it is not supported name
1234 repetition
tierno7edb6752016-03-21 17:37:52 +01001235 Returns the flavor identifier
tierno1ec592d2020-06-16 15:29:47 +00001236 """
tiernoae4a8d12016-07-08 12:30:39 +02001237 self.logger.debug("Adding flavor '%s'", str(flavor_data))
tierno1ec592d2020-06-16 15:29:47 +00001238 retry = 0
1239 max_retries = 3
tierno7edb6752016-03-21 17:37:52 +01001240 name_suffix = 0
sousaedu80135b92021-02-17 15:05:18 +01001241
anwarsc76a3ee2018-10-04 14:05:32 +05301242 try:
sousaedu80135b92021-02-17 15:05:18 +01001243 name = flavor_data["name"]
tierno1ec592d2020-06-16 15:29:47 +00001244 while retry < max_retries:
1245 retry += 1
anwarsc76a3ee2018-10-04 14:05:32 +05301246 try:
1247 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +01001248
anwarsc76a3ee2018-10-04 14:05:32 +05301249 if change_name_if_used:
tierno1ec592d2020-06-16 15:29:47 +00001250 # get used names
1251 fl_names = []
1252 fl = self.nova.flavors.list()
sousaedu80135b92021-02-17 15:05:18 +01001253
anwarsc76a3ee2018-10-04 14:05:32 +05301254 for f in fl:
1255 fl_names.append(f.name)
sousaedu80135b92021-02-17 15:05:18 +01001256
anwarsc76a3ee2018-10-04 14:05:32 +05301257 while name in fl_names:
1258 name_suffix += 1
sousaedu80135b92021-02-17 15:05:18 +01001259 name = flavor_data["name"] + "-" + str(name_suffix)
kate721d79b2017-06-24 04:21:38 -07001260
sousaedu80135b92021-02-17 15:05:18 +01001261 ram = flavor_data.get("ram", 64)
1262 vcpus = flavor_data.get("vcpus", 1)
tierno1ec592d2020-06-16 15:29:47 +00001263 extra_specs = {}
tierno7edb6752016-03-21 17:37:52 +01001264
anwarsc76a3ee2018-10-04 14:05:32 +05301265 extended = flavor_data.get("extended")
1266 if extended:
tierno1ec592d2020-06-16 15:29:47 +00001267 numas = extended.get("numas")
sousaedu80135b92021-02-17 15:05:18 +01001268
anwarsc76a3ee2018-10-04 14:05:32 +05301269 if numas:
1270 numa_nodes = len(numas)
sousaedu80135b92021-02-17 15:05:18 +01001271
anwarsc76a3ee2018-10-04 14:05:32 +05301272 if numa_nodes > 1:
1273 return -1, "Can not add flavor with more than one numa"
sousaedu80135b92021-02-17 15:05:18 +01001274
anwarsae5f52c2019-04-22 10:35:27 +05301275 extra_specs["hw:numa_nodes"] = str(numa_nodes)
1276 extra_specs["hw:mem_page_size"] = "large"
1277 extra_specs["hw:cpu_policy"] = "dedicated"
1278 extra_specs["hw:numa_mempolicy"] = "strict"
sousaedu80135b92021-02-17 15:05:18 +01001279
anwarsc76a3ee2018-10-04 14:05:32 +05301280 if self.vim_type == "VIO":
sousaedu80135b92021-02-17 15:05:18 +01001281 extra_specs[
1282 "vmware:extra_config"
1283 ] = '{"numa.nodeAffinity":"0"}'
anwarsae5f52c2019-04-22 10:35:27 +05301284 extra_specs["vmware:latency_sensitivity_level"] = "high"
sousaedu80135b92021-02-17 15:05:18 +01001285
anwarsc76a3ee2018-10-04 14:05:32 +05301286 for numa in numas:
tierno1ec592d2020-06-16 15:29:47 +00001287 # overwrite ram and vcpus
sousaedu80135b92021-02-17 15:05:18 +01001288 # check if key "memory" is present in numa else use ram value at flavor
1289 if "memory" in numa:
1290 ram = numa["memory"] * 1024
tierno1ec592d2020-06-16 15:29:47 +00001291 # See for reference: https://specs.openstack.org/openstack/nova-specs/specs/mitaka/
1292 # implemented/virt-driver-cpu-thread-pinning.html
garciadeblasfa35a722019-04-11 19:15:49 +02001293 extra_specs["hw:cpu_sockets"] = 1
sousaedu80135b92021-02-17 15:05:18 +01001294
1295 if "paired-threads" in numa:
1296 vcpus = numa["paired-threads"] * 2
1297 # cpu_thread_policy "require" implies that the compute node must have an
tierno1ec592d2020-06-16 15:29:47 +00001298 # STM architecture
anwarsae5f52c2019-04-22 10:35:27 +05301299 extra_specs["hw:cpu_thread_policy"] = "require"
1300 extra_specs["hw:cpu_policy"] = "dedicated"
sousaedu80135b92021-02-17 15:05:18 +01001301 elif "cores" in numa:
1302 vcpus = numa["cores"]
1303 # cpu_thread_policy "prefer" implies that the host must not have an SMT
tierno1ec592d2020-06-16 15:29:47 +00001304 # architecture, or a non-SMT architecture will be emulated
anwarsae5f52c2019-04-22 10:35:27 +05301305 extra_specs["hw:cpu_thread_policy"] = "isolate"
1306 extra_specs["hw:cpu_policy"] = "dedicated"
sousaedu80135b92021-02-17 15:05:18 +01001307 elif "threads" in numa:
1308 vcpus = numa["threads"]
tierno1ec592d2020-06-16 15:29:47 +00001309 # cpu_thread_policy "prefer" implies that the host may or may not have an SMT
1310 # architecture
anwarsae5f52c2019-04-22 10:35:27 +05301311 extra_specs["hw:cpu_thread_policy"] = "prefer"
1312 extra_specs["hw:cpu_policy"] = "dedicated"
anwarsc76a3ee2018-10-04 14:05:32 +05301313 # for interface in numa.get("interfaces",() ):
1314 # if interface["dedicated"]=="yes":
tierno1ec592d2020-06-16 15:29:47 +00001315 # raise vimconn.VimConnException("Passthrough interfaces are not supported
1316 # for the openstack connector", http_code=vimconn.HTTP_Service_Unavailable)
sousaedu80135b92021-02-17 15:05:18 +01001317 # #TODO, add the key 'pci_passthrough:alias"="<label at config>:<number ifaces>"'
tierno1ec592d2020-06-16 15:29:47 +00001318 # when a way to connect it is available
anwarsae5f52c2019-04-22 10:35:27 +05301319 elif extended.get("cpu-quota"):
sousaedu80135b92021-02-17 15:05:18 +01001320 self.process_resource_quota(
1321 extended.get("cpu-quota"), "cpu", extra_specs
1322 )
1323
anwarsae5f52c2019-04-22 10:35:27 +05301324 if extended.get("mem-quota"):
sousaedu80135b92021-02-17 15:05:18 +01001325 self.process_resource_quota(
1326 extended.get("mem-quota"), "memory", extra_specs
1327 )
1328
anwarsae5f52c2019-04-22 10:35:27 +05301329 if extended.get("vif-quota"):
sousaedu80135b92021-02-17 15:05:18 +01001330 self.process_resource_quota(
1331 extended.get("vif-quota"), "vif", extra_specs
1332 )
1333
anwarsae5f52c2019-04-22 10:35:27 +05301334 if extended.get("disk-io-quota"):
sousaedu80135b92021-02-17 15:05:18 +01001335 self.process_resource_quota(
1336 extended.get("disk-io-quota"), "disk_io", extra_specs
1337 )
1338
tierno1ec592d2020-06-16 15:29:47 +00001339 # create flavor
sousaedu80135b92021-02-17 15:05:18 +01001340 new_flavor = self.nova.flavors.create(
1341 name,
1342 ram,
1343 vcpus,
1344 flavor_data.get("disk", 0),
1345 is_public=flavor_data.get("is_public", True),
1346 )
tierno1ec592d2020-06-16 15:29:47 +00001347 # add metadata
anwarsae5f52c2019-04-22 10:35:27 +05301348 if extra_specs:
1349 new_flavor.set_keys(extra_specs)
sousaedu80135b92021-02-17 15:05:18 +01001350
anwarsc76a3ee2018-10-04 14:05:32 +05301351 return new_flavor.id
1352 except nvExceptions.Conflict as e:
1353 if change_name_if_used and retry < max_retries:
1354 continue
sousaedu80135b92021-02-17 15:05:18 +01001355
anwarsc76a3ee2018-10-04 14:05:32 +05301356 self._format_exception(e)
tierno1ec592d2020-06-16 15:29:47 +00001357 # except nvExceptions.BadRequest as e:
sousaedu80135b92021-02-17 15:05:18 +01001358 except (
1359 ksExceptions.ClientException,
1360 nvExceptions.ClientException,
1361 ConnectionError,
1362 KeyError,
1363 ) as e:
anwarsc76a3ee2018-10-04 14:05:32 +05301364 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001365
tierno1ec592d2020-06-16 15:29:47 +00001366 def delete_flavor(self, flavor_id):
sousaedu80135b92021-02-17 15:05:18 +01001367 """Deletes a tenant flavor from openstack VIM. Returns the old flavor_id"""
tiernoae4a8d12016-07-08 12:30:39 +02001368 try:
1369 self._reload_connection()
1370 self.nova.flavors.delete(flavor_id)
sousaedu80135b92021-02-17 15:05:18 +01001371
tiernoae4a8d12016-07-08 12:30:39 +02001372 return flavor_id
tierno1ec592d2020-06-16 15:29:47 +00001373 # except nvExceptions.BadRequest as e:
sousaedu80135b92021-02-17 15:05:18 +01001374 except (
1375 nvExceptions.NotFound,
1376 ksExceptions.ClientException,
1377 nvExceptions.ClientException,
1378 ConnectionError,
1379 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001380 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001381
tierno1ec592d2020-06-16 15:29:47 +00001382 def new_image(self, image_dict):
1383 """
tiernoae4a8d12016-07-08 12:30:39 +02001384 Adds a tenant image to VIM. imge_dict is a dictionary with:
1385 name: name
1386 disk_format: qcow2, vhd, vmdk, raw (by default), ...
1387 location: path or URI
1388 public: "yes" or "no"
1389 metadata: metadata of the image
1390 Returns the image_id
tierno1ec592d2020-06-16 15:29:47 +00001391 """
1392 retry = 0
1393 max_retries = 3
sousaedu80135b92021-02-17 15:05:18 +01001394
tierno1ec592d2020-06-16 15:29:47 +00001395 while retry < max_retries:
1396 retry += 1
tierno7edb6752016-03-21 17:37:52 +01001397 try:
1398 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +01001399
tierno1ec592d2020-06-16 15:29:47 +00001400 # determine format http://docs.openstack.org/developer/glance/formats.html
tierno7edb6752016-03-21 17:37:52 +01001401 if "disk_format" in image_dict:
tierno1ec592d2020-06-16 15:29:47 +00001402 disk_format = image_dict["disk_format"]
1403 else: # autodiscover based on extension
sousaedu80135b92021-02-17 15:05:18 +01001404 if image_dict["location"].endswith(".qcow2"):
tierno1ec592d2020-06-16 15:29:47 +00001405 disk_format = "qcow2"
sousaedu80135b92021-02-17 15:05:18 +01001406 elif image_dict["location"].endswith(".vhd"):
tierno1ec592d2020-06-16 15:29:47 +00001407 disk_format = "vhd"
sousaedu80135b92021-02-17 15:05:18 +01001408 elif image_dict["location"].endswith(".vmdk"):
tierno1ec592d2020-06-16 15:29:47 +00001409 disk_format = "vmdk"
sousaedu80135b92021-02-17 15:05:18 +01001410 elif image_dict["location"].endswith(".vdi"):
tierno1ec592d2020-06-16 15:29:47 +00001411 disk_format = "vdi"
sousaedu80135b92021-02-17 15:05:18 +01001412 elif image_dict["location"].endswith(".iso"):
tierno1ec592d2020-06-16 15:29:47 +00001413 disk_format = "iso"
sousaedu80135b92021-02-17 15:05:18 +01001414 elif image_dict["location"].endswith(".aki"):
tierno1ec592d2020-06-16 15:29:47 +00001415 disk_format = "aki"
sousaedu80135b92021-02-17 15:05:18 +01001416 elif image_dict["location"].endswith(".ari"):
tierno1ec592d2020-06-16 15:29:47 +00001417 disk_format = "ari"
sousaedu80135b92021-02-17 15:05:18 +01001418 elif image_dict["location"].endswith(".ami"):
tierno1ec592d2020-06-16 15:29:47 +00001419 disk_format = "ami"
tierno7edb6752016-03-21 17:37:52 +01001420 else:
tierno1ec592d2020-06-16 15:29:47 +00001421 disk_format = "raw"
sousaedu80135b92021-02-17 15:05:18 +01001422
1423 self.logger.debug(
1424 "new_image: '%s' loading from '%s'",
1425 image_dict["name"],
1426 image_dict["location"],
1427 )
shashankjain3c83a212018-10-04 13:05:46 +05301428 if self.vim_type == "VIO":
1429 container_format = "bare"
sousaedu80135b92021-02-17 15:05:18 +01001430 if "container_format" in image_dict:
1431 container_format = image_dict["container_format"]
1432
1433 new_image = self.glance.images.create(
1434 name=image_dict["name"],
1435 container_format=container_format,
1436 disk_format=disk_format,
1437 )
shashankjain3c83a212018-10-04 13:05:46 +05301438 else:
sousaedu80135b92021-02-17 15:05:18 +01001439 new_image = self.glance.images.create(name=image_dict["name"])
1440
1441 if image_dict["location"].startswith("http"):
tierno1beea862018-07-11 15:47:37 +02001442 # TODO there is not a method to direct download. It must be downloaded locally with requests
tierno72774862020-05-04 11:44:15 +00001443 raise vimconn.VimConnNotImplemented("Cannot create image from URL")
tierno1ec592d2020-06-16 15:29:47 +00001444 else: # local path
sousaedu80135b92021-02-17 15:05:18 +01001445 with open(image_dict["location"]) as fimage:
tierno1beea862018-07-11 15:47:37 +02001446 self.glance.images.upload(new_image.id, fimage)
sousaedu80135b92021-02-17 15:05:18 +01001447 # new_image = self.glancev1.images.create(name=image_dict["name"], is_public=
1448 # image_dict.get("public","yes")=="yes",
tierno1beea862018-07-11 15:47:37 +02001449 # container_format="bare", data=fimage, disk_format=disk_format)
sousaedu80135b92021-02-17 15:05:18 +01001450
1451 metadata_to_load = image_dict.get("metadata")
1452
1453 # TODO location is a reserved word for current openstack versions. fixed for VIO please check
tierno1ec592d2020-06-16 15:29:47 +00001454 # for openstack
shashankjain3c83a212018-10-04 13:05:46 +05301455 if self.vim_type == "VIO":
sousaedu80135b92021-02-17 15:05:18 +01001456 metadata_to_load["upload_location"] = image_dict["location"]
shashankjain3c83a212018-10-04 13:05:46 +05301457 else:
sousaedu80135b92021-02-17 15:05:18 +01001458 metadata_to_load["location"] = image_dict["location"]
1459
tierno1beea862018-07-11 15:47:37 +02001460 self.glance.images.update(new_image.id, **metadata_to_load)
sousaedu80135b92021-02-17 15:05:18 +01001461
tiernoae4a8d12016-07-08 12:30:39 +02001462 return new_image.id
sousaedu80135b92021-02-17 15:05:18 +01001463 except (
1464 nvExceptions.Conflict,
1465 ksExceptions.ClientException,
1466 nvExceptions.ClientException,
1467 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001468 self._format_exception(e)
sousaedu80135b92021-02-17 15:05:18 +01001469 except (
1470 HTTPException,
1471 gl1Exceptions.HTTPException,
1472 gl1Exceptions.CommunicationError,
1473 ConnectionError,
1474 ) as e:
tierno1ec592d2020-06-16 15:29:47 +00001475 if retry == max_retries:
tiernoae4a8d12016-07-08 12:30:39 +02001476 continue
sousaedu80135b92021-02-17 15:05:18 +01001477
tiernoae4a8d12016-07-08 12:30:39 +02001478 self._format_exception(e)
tierno1ec592d2020-06-16 15:29:47 +00001479 except IOError as e: # can not open the file
sousaedu80135b92021-02-17 15:05:18 +01001480 raise vimconn.VimConnConnectionException(
1481 "{}: {} for {}".format(type(e).__name__, e, image_dict["location"]),
1482 http_code=vimconn.HTTP_Bad_Request,
1483 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001484
tiernoae4a8d12016-07-08 12:30:39 +02001485 def delete_image(self, image_id):
sousaedu80135b92021-02-17 15:05:18 +01001486 """Deletes a tenant image from openstack VIM. Returns the old id"""
tiernoae4a8d12016-07-08 12:30:39 +02001487 try:
1488 self._reload_connection()
tierno1beea862018-07-11 15:47:37 +02001489 self.glance.images.delete(image_id)
sousaedu80135b92021-02-17 15:05:18 +01001490
tiernoae4a8d12016-07-08 12:30:39 +02001491 return image_id
sousaedu80135b92021-02-17 15:05:18 +01001492 except (
1493 nvExceptions.NotFound,
1494 ksExceptions.ClientException,
1495 nvExceptions.ClientException,
1496 gl1Exceptions.CommunicationError,
1497 gl1Exceptions.HTTPNotFound,
1498 ConnectionError,
1499 ) as e: # TODO remove
tiernoae4a8d12016-07-08 12:30:39 +02001500 self._format_exception(e)
1501
1502 def get_image_id_from_path(self, path):
tierno1ec592d2020-06-16 15:29:47 +00001503 """Get the image id from image path in the VIM database. Returns the image_id"""
tiernoae4a8d12016-07-08 12:30:39 +02001504 try:
1505 self._reload_connection()
tierno1beea862018-07-11 15:47:37 +02001506 images = self.glance.images.list()
sousaedu80135b92021-02-17 15:05:18 +01001507
tiernoae4a8d12016-07-08 12:30:39 +02001508 for image in images:
tierno1ec592d2020-06-16 15:29:47 +00001509 if image.metadata.get("location") == path:
tiernoae4a8d12016-07-08 12:30:39 +02001510 return image.id
sousaedu80135b92021-02-17 15:05:18 +01001511
1512 raise vimconn.VimConnNotFoundException(
1513 "image with location '{}' not found".format(path)
1514 )
1515 except (
1516 ksExceptions.ClientException,
1517 nvExceptions.ClientException,
1518 gl1Exceptions.CommunicationError,
1519 ConnectionError,
1520 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001521 self._format_exception(e)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001522
garciadeblasb69fa9f2016-09-28 12:04:10 +02001523 def get_image_list(self, filter_dict={}):
tierno1ec592d2020-06-16 15:29:47 +00001524 """Obtain tenant images from VIM
garciadeblasb69fa9f2016-09-28 12:04:10 +02001525 Filter_dict can be:
1526 id: image id
1527 name: image name
1528 checksum: image checksum
1529 Returns the image list of dictionaries:
1530 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1531 List can be empty
tierno1ec592d2020-06-16 15:29:47 +00001532 """
garciadeblasb69fa9f2016-09-28 12:04:10 +02001533 self.logger.debug("Getting image list from VIM filter: '%s'", str(filter_dict))
sousaedu80135b92021-02-17 15:05:18 +01001534
garciadeblasb69fa9f2016-09-28 12:04:10 +02001535 try:
1536 self._reload_connection()
tierno1ec592d2020-06-16 15:29:47 +00001537 # filter_dict_os = filter_dict.copy()
1538 # First we filter by the available filter fields: name, id. The others are removed.
tierno1beea862018-07-11 15:47:37 +02001539 image_list = self.glance.images.list()
garciadeblasb69fa9f2016-09-28 12:04:10 +02001540 filtered_list = []
sousaedu80135b92021-02-17 15:05:18 +01001541
garciadeblasb69fa9f2016-09-28 12:04:10 +02001542 for image in image_list:
tierno3cb8dc32017-10-24 18:13:19 +02001543 try:
tierno1beea862018-07-11 15:47:37 +02001544 if filter_dict.get("name") and image["name"] != filter_dict["name"]:
1545 continue
sousaedu80135b92021-02-17 15:05:18 +01001546
tierno1beea862018-07-11 15:47:37 +02001547 if filter_dict.get("id") and image["id"] != filter_dict["id"]:
1548 continue
sousaedu80135b92021-02-17 15:05:18 +01001549
1550 if (
1551 filter_dict.get("checksum")
1552 and image["checksum"] != filter_dict["checksum"]
1553 ):
tierno1beea862018-07-11 15:47:37 +02001554 continue
1555
1556 filtered_list.append(image.copy())
tierno3cb8dc32017-10-24 18:13:19 +02001557 except gl1Exceptions.HTTPNotFound:
1558 pass
sousaedu80135b92021-02-17 15:05:18 +01001559
garciadeblasb69fa9f2016-09-28 12:04:10 +02001560 return filtered_list
sousaedu80135b92021-02-17 15:05:18 +01001561 except (
1562 ksExceptions.ClientException,
1563 nvExceptions.ClientException,
1564 gl1Exceptions.CommunicationError,
1565 ConnectionError,
1566 ) as e:
garciadeblasb69fa9f2016-09-28 12:04:10 +02001567 self._format_exception(e)
1568
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001569 def __wait_for_vm(self, vm_id, status):
1570 """wait until vm is in the desired status and return True.
1571 If the VM gets in ERROR status, return false.
1572 If the timeout is reached generate an exception"""
1573 elapsed_time = 0
1574 while elapsed_time < server_timeout:
1575 vm_status = self.nova.servers.get(vm_id).status
sousaedu80135b92021-02-17 15:05:18 +01001576
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001577 if vm_status == status:
1578 return True
sousaedu80135b92021-02-17 15:05:18 +01001579
1580 if vm_status == "ERROR":
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001581 return False
sousaedu80135b92021-02-17 15:05:18 +01001582
tierno1df468d2018-07-06 14:25:16 +02001583 time.sleep(5)
1584 elapsed_time += 5
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001585
1586 # if we exceeded the timeout rollback
1587 if elapsed_time >= server_timeout:
sousaedu80135b92021-02-17 15:05:18 +01001588 raise vimconn.VimConnException(
1589 "Timeout waiting for instance " + vm_id + " to get " + status,
1590 http_code=vimconn.HTTP_Request_Timeout,
1591 )
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001592
mirabal29356312017-07-27 12:21:22 +02001593 def _get_openstack_availablity_zones(self):
1594 """
1595 Get from openstack availability zones available
1596 :return:
1597 """
1598 try:
1599 openstack_availability_zone = self.nova.availability_zones.list()
sousaedu80135b92021-02-17 15:05:18 +01001600 openstack_availability_zone = [
1601 str(zone.zoneName)
1602 for zone in openstack_availability_zone
1603 if zone.zoneName != "internal"
1604 ]
1605
mirabal29356312017-07-27 12:21:22 +02001606 return openstack_availability_zone
tierno1ec592d2020-06-16 15:29:47 +00001607 except Exception:
mirabal29356312017-07-27 12:21:22 +02001608 return None
1609
1610 def _set_availablity_zones(self):
1611 """
1612 Set vim availablity zone
1613 :return:
1614 """
sousaedu80135b92021-02-17 15:05:18 +01001615 if "availability_zone" in self.config:
1616 vim_availability_zones = self.config.get("availability_zone")
mirabal29356312017-07-27 12:21:22 +02001617
mirabal29356312017-07-27 12:21:22 +02001618 if isinstance(vim_availability_zones, str):
1619 self.availability_zone = [vim_availability_zones]
1620 elif isinstance(vim_availability_zones, list):
1621 self.availability_zone = vim_availability_zones
1622 else:
1623 self.availability_zone = self._get_openstack_availablity_zones()
1624
sousaedu80135b92021-02-17 15:05:18 +01001625 def _get_vm_availability_zone(
1626 self, availability_zone_index, availability_zone_list
1627 ):
mirabal29356312017-07-27 12:21:22 +02001628 """
tierno5a3273c2017-08-29 11:43:46 +02001629 Return thge availability zone to be used by the created VM.
1630 :return: The VIM availability zone to be used or None
mirabal29356312017-07-27 12:21:22 +02001631 """
tierno5a3273c2017-08-29 11:43:46 +02001632 if availability_zone_index is None:
sousaedu80135b92021-02-17 15:05:18 +01001633 if not self.config.get("availability_zone"):
tierno5a3273c2017-08-29 11:43:46 +02001634 return None
sousaedu80135b92021-02-17 15:05:18 +01001635 elif isinstance(self.config.get("availability_zone"), str):
1636 return self.config["availability_zone"]
tierno5a3273c2017-08-29 11:43:46 +02001637 else:
1638 # TODO consider using a different parameter at config for default AV and AV list match
sousaedu80135b92021-02-17 15:05:18 +01001639 return self.config["availability_zone"][0]
mirabal29356312017-07-27 12:21:22 +02001640
tierno5a3273c2017-08-29 11:43:46 +02001641 vim_availability_zones = self.availability_zone
1642 # check if VIM offer enough availability zones describe in the VNFD
sousaedu80135b92021-02-17 15:05:18 +01001643 if vim_availability_zones and len(availability_zone_list) <= len(
1644 vim_availability_zones
1645 ):
tierno5a3273c2017-08-29 11:43:46 +02001646 # check if all the names of NFV AV match VIM AV names
1647 match_by_index = False
1648 for av in availability_zone_list:
1649 if av not in vim_availability_zones:
1650 match_by_index = True
1651 break
sousaedu80135b92021-02-17 15:05:18 +01001652
tierno5a3273c2017-08-29 11:43:46 +02001653 if match_by_index:
1654 return vim_availability_zones[availability_zone_index]
1655 else:
1656 return availability_zone_list[availability_zone_index]
mirabal29356312017-07-27 12:21:22 +02001657 else:
sousaedu80135b92021-02-17 15:05:18 +01001658 raise vimconn.VimConnConflictException(
1659 "No enough availability zones at VIM for this deployment"
1660 )
mirabal29356312017-07-27 12:21:22 +02001661
sousaedu80135b92021-02-17 15:05:18 +01001662 def new_vminstance(
1663 self,
1664 name,
1665 description,
1666 start,
1667 image_id,
1668 flavor_id,
1669 net_list,
1670 cloud_config=None,
1671 disk_list=None,
1672 availability_zone_index=None,
1673 availability_zone_list=None,
1674 ):
tierno98e909c2017-10-14 13:27:03 +02001675 """Adds a VM instance to VIM
tierno7edb6752016-03-21 17:37:52 +01001676 Params:
1677 start: indicates if VM must start or boot in pause mode. Ignored
1678 image_id,flavor_id: iamge and flavor uuid
1679 net_list: list of interfaces, each one is a dictionary with:
1680 name:
1681 net_id: network uuid to connect
1682 vpci: virtual vcpi to assign, ignored because openstack lack #TODO
1683 model: interface model, ignored #TODO
1684 mac_address: used for SR-IOV ifaces #TODO for other types
1685 use: 'data', 'bridge', 'mgmt'
tierno66eba6e2017-11-10 17:09:18 +01001686 type: 'virtual', 'PCI-PASSTHROUGH'('PF'), 'SR-IOV'('VF'), 'VFnotShared'
tierno7edb6752016-03-21 17:37:52 +01001687 vim_id: filled/added by this function
ahmadsaf853d452016-12-22 11:33:47 +05001688 floating_ip: True/False (or it can be None)
tierno70eeb182020-10-19 16:38:00 +00001689 port_security: True/False
tierno41a69812018-02-16 14:34:33 +01001690 'cloud_config': (optional) dictionary with:
tierno1d213f42020-04-24 14:02:51 +00001691 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
1692 'users': (optional) list of users to be inserted, each item is a dict with:
1693 'name': (mandatory) user name,
1694 'key-pairs': (optional) list of strings with the public key to be inserted to the user
1695 'user-data': (optional) string is a text script to be passed directly to cloud-init
1696 'config-files': (optional). List of files to be transferred. Each item is a dict with:
1697 'dest': (mandatory) string with the destination absolute path
1698 'encoding': (optional, by default text). Can be one of:
1699 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
1700 'content' (mandatory): string with the content of the file
1701 'permissions': (optional) string with file permissions, typically octal notation '0644'
1702 'owner': (optional) file owner, string with the format 'owner:group'
1703 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk)
mirabal29356312017-07-27 12:21:22 +02001704 'disk_list': (optional) list with additional disks to the VM. Each item is a dict with:
1705 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted
1706 'size': (mandatory) string with the size of the disk in GB
tierno1df468d2018-07-06 14:25:16 +02001707 'vim_id' (optional) should use this existing volume id
tierno5a3273c2017-08-29 11:43:46 +02001708 availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV required
1709 availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if
1710 availability_zone_index is None
tierno7edb6752016-03-21 17:37:52 +01001711 #TODO ip, security groups
tierno98e909c2017-10-14 13:27:03 +02001712 Returns a tuple with the instance identifier and created_items or raises an exception on error
1713 created_items can be None or a dictionary where this method can include key-values that will be passed to
1714 the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc.
1715 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
1716 as not present.
1717 """
sousaedu80135b92021-02-17 15:05:18 +01001718 self.logger.debug(
1719 "new_vminstance input: image='%s' flavor='%s' nics='%s'",
1720 image_id,
1721 flavor_id,
1722 str(net_list),
1723 )
1724
tierno7edb6752016-03-21 17:37:52 +01001725 try:
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001726 server = None
tierno98e909c2017-10-14 13:27:03 +02001727 created_items = {}
tiernob0b9dab2017-10-14 14:25:20 +02001728 # metadata = {}
tierno98e909c2017-10-14 13:27:03 +02001729 net_list_vim = []
tierno1ec592d2020-06-16 15:29:47 +00001730 external_network = []
1731 # ^list of external networks to be connected to instance, later on used to create floating_ip
sousaedu80135b92021-02-17 15:05:18 +01001732 no_secured_ports = [] # List of port-is with port-security disabled
tierno7edb6752016-03-21 17:37:52 +01001733 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +01001734 # metadata_vpci = {} # For a specific neutron plugin
tiernob84cbdc2017-07-07 14:30:30 +02001735 block_device_mapping = None
tiernoa05b65a2019-02-01 12:30:27 +00001736
tierno7edb6752016-03-21 17:37:52 +01001737 for net in net_list:
sousaedu80135b92021-02-17 15:05:18 +01001738 if not net.get("net_id"): # skip non connected iface
tierno7edb6752016-03-21 17:37:52 +01001739 continue
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001740
tiernoa05b65a2019-02-01 12:30:27 +00001741 port_dict = {
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001742 "network_id": net["net_id"],
1743 "name": net.get("name"),
sousaedu80135b92021-02-17 15:05:18 +01001744 "admin_state_up": True,
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001745 }
sousaedu80135b92021-02-17 15:05:18 +01001746
1747 if (
1748 self.config.get("security_groups")
1749 and net.get("port_security") is not False
1750 and not self.config.get("no_port_security_extension")
1751 ):
tiernoa05b65a2019-02-01 12:30:27 +00001752 if not self.security_groups_id:
1753 self._get_ids_from_name()
sousaedu80135b92021-02-17 15:05:18 +01001754
tiernoa05b65a2019-02-01 12:30:27 +00001755 port_dict["security_groups"] = self.security_groups_id
1756
tierno1ec592d2020-06-16 15:29:47 +00001757 if net["type"] == "virtual":
tiernob0b9dab2017-10-14 14:25:20 +02001758 pass
1759 # if "vpci" in net:
1760 # metadata_vpci[ net["net_id"] ] = [[ net["vpci"], "" ]]
tierno66eba6e2017-11-10 17:09:18 +01001761 elif net["type"] == "VF" or net["type"] == "SR-IOV": # for VF
tiernob0b9dab2017-10-14 14:25:20 +02001762 # if "vpci" in net:
1763 # if "VF" not in metadata_vpci:
1764 # metadata_vpci["VF"]=[]
1765 # metadata_vpci["VF"].append([ net["vpci"], "" ])
tierno1ec592d2020-06-16 15:29:47 +00001766 port_dict["binding:vnic_type"] = "direct"
sousaedu80135b92021-02-17 15:05:18 +01001767
tiernob0b9dab2017-10-14 14:25:20 +02001768 # VIO specific Changes
kate721d79b2017-06-24 04:21:38 -07001769 if self.vim_type == "VIO":
tiernob0b9dab2017-10-14 14:25:20 +02001770 # Need to create port with port_security_enabled = False and no-security-groups
tierno1ec592d2020-06-16 15:29:47 +00001771 port_dict["port_security_enabled"] = False
1772 port_dict["provider_security_groups"] = []
1773 port_dict["security_groups"] = []
sousaedu80135b92021-02-17 15:05:18 +01001774 else: # For PT PCI-PASSTHROUGH
tiernob0b9dab2017-10-14 14:25:20 +02001775 # if "vpci" in net:
1776 # if "PF" not in metadata_vpci:
1777 # metadata_vpci["PF"]=[]
1778 # metadata_vpci["PF"].append([ net["vpci"], "" ])
tierno1ec592d2020-06-16 15:29:47 +00001779 port_dict["binding:vnic_type"] = "direct-physical"
sousaedu80135b92021-02-17 15:05:18 +01001780
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001781 if not port_dict["name"]:
tierno1ec592d2020-06-16 15:29:47 +00001782 port_dict["name"] = name
sousaedu80135b92021-02-17 15:05:18 +01001783
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001784 if net.get("mac_address"):
tierno1ec592d2020-06-16 15:29:47 +00001785 port_dict["mac_address"] = net["mac_address"]
sousaedu80135b92021-02-17 15:05:18 +01001786
tierno41a69812018-02-16 14:34:33 +01001787 if net.get("ip_address"):
sousaedu80135b92021-02-17 15:05:18 +01001788 port_dict["fixed_ips"] = [{"ip_address": net["ip_address"]}]
1789 # TODO add "subnet_id": <subnet_id>
1790
tierno1ec592d2020-06-16 15:29:47 +00001791 new_port = self.neutron.create_port({"port": port_dict})
tierno00e3df72017-11-29 17:20:13 +01001792 created_items["port:" + str(new_port["port"]["id"])] = True
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001793 net["mac_adress"] = new_port["port"]["mac_address"]
1794 net["vim_id"] = new_port["port"]["id"]
tiernob84cbdc2017-07-07 14:30:30 +02001795 # if try to use a network without subnetwork, it will return a emtpy list
1796 fixed_ips = new_port["port"].get("fixed_ips")
sousaedu80135b92021-02-17 15:05:18 +01001797
tiernob84cbdc2017-07-07 14:30:30 +02001798 if fixed_ips:
1799 net["ip"] = fixed_ips[0].get("ip_address")
1800 else:
1801 net["ip"] = None
montesmoreno994a29d2017-08-22 11:23:06 +02001802
1803 port = {"port-id": new_port["port"]["id"]}
1804 if float(self.nova.api_version.get_string()) >= 2.32:
1805 port["tag"] = new_port["port"]["name"]
sousaedu80135b92021-02-17 15:05:18 +01001806
montesmoreno994a29d2017-08-22 11:23:06 +02001807 net_list_vim.append(port)
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001808
sousaedu80135b92021-02-17 15:05:18 +01001809 if net.get("floating_ip", False):
1810 net["exit_on_floating_ip_error"] = True
ahmadsaf853d452016-12-22 11:33:47 +05001811 external_network.append(net)
sousaedu80135b92021-02-17 15:05:18 +01001812 elif net["use"] == "mgmt" and self.config.get("use_floating_ip"):
1813 net["exit_on_floating_ip_error"] = False
tiernof8383b82017-01-18 15:49:48 +01001814 external_network.append(net)
sousaedu80135b92021-02-17 15:05:18 +01001815 net["floating_ip"] = self.config.get("use_floating_ip")
tiernof8383b82017-01-18 15:49:48 +01001816
tierno1ec592d2020-06-16 15:29:47 +00001817 # If port security is disabled when the port has not yet been attached to the VM, then all vm traffic
1818 # is dropped.
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001819 # As a workaround we wait until the VM is active and then disable the port-security
sousaedu80135b92021-02-17 15:05:18 +01001820 if net.get("port_security") is False and not self.config.get(
1821 "no_port_security_extension"
1822 ):
1823 no_secured_ports.append(
1824 (
1825 new_port["port"]["id"],
1826 net.get("port_security_disable_strategy"),
1827 )
1828 )
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001829
tiernob0b9dab2017-10-14 14:25:20 +02001830 # if metadata_vpci:
1831 # metadata = {"pci_assignement": json.dumps(metadata_vpci)}
1832 # if len(metadata["pci_assignement"]) >255:
1833 # #limit the metadata size
1834 # #metadata["pci_assignement"] = metadata["pci_assignement"][0:255]
1835 # self.logger.warn("Metadata deleted since it exceeds the expected length (255) ")
1836 # metadata = {}
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001837
sousaedu80135b92021-02-17 15:05:18 +01001838 self.logger.debug(
1839 "name '%s' image_id '%s'flavor_id '%s' net_list_vim '%s' description '%s'",
1840 name,
1841 image_id,
1842 flavor_id,
1843 str(net_list_vim),
1844 description,
1845 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001846
tierno98e909c2017-10-14 13:27:03 +02001847 # cloud config
tierno0a1437e2017-10-02 00:17:43 +02001848 config_drive, userdata = self._create_user_data(cloud_config)
montesmoreno0c8def02016-12-22 12:16:23 +00001849
tierno98e909c2017-10-14 13:27:03 +02001850 # Create additional volumes in case these are present in disk_list
sousaedu80135b92021-02-17 15:05:18 +01001851 base_disk_index = ord("b")
tierno1df468d2018-07-06 14:25:16 +02001852 if disk_list:
tiernob84cbdc2017-07-07 14:30:30 +02001853 block_device_mapping = {}
montesmoreno0c8def02016-12-22 12:16:23 +00001854 for disk in disk_list:
sousaedu80135b92021-02-17 15:05:18 +01001855 if disk.get("vim_id"):
1856 block_device_mapping["_vd" + chr(base_disk_index)] = disk[
1857 "vim_id"
1858 ]
montesmoreno0c8def02016-12-22 12:16:23 +00001859 else:
sousaedu80135b92021-02-17 15:05:18 +01001860 if "image_id" in disk:
1861 volume = self.cinder.volumes.create(
1862 size=disk["size"],
1863 name=name + "_vd" + chr(base_disk_index),
1864 imageRef=disk["image_id"],
1865 )
tierno1df468d2018-07-06 14:25:16 +02001866 else:
sousaedu80135b92021-02-17 15:05:18 +01001867 volume = self.cinder.volumes.create(
1868 size=disk["size"],
1869 name=name + "_vd" + chr(base_disk_index),
1870 )
1871
tierno1df468d2018-07-06 14:25:16 +02001872 created_items["volume:" + str(volume.id)] = True
sousaedu80135b92021-02-17 15:05:18 +01001873 block_device_mapping["_vd" + chr(base_disk_index)] = volume.id
1874
montesmoreno0c8def02016-12-22 12:16:23 +00001875 base_disk_index += 1
1876
tierno1df468d2018-07-06 14:25:16 +02001877 # Wait until created volumes are with status available
montesmoreno0c8def02016-12-22 12:16:23 +00001878 elapsed_time = 0
tierno1df468d2018-07-06 14:25:16 +02001879 while elapsed_time < volume_timeout:
1880 for created_item in created_items:
1881 v, _, volume_id = created_item.partition(":")
sousaedu80135b92021-02-17 15:05:18 +01001882 if v == "volume":
1883 if self.cinder.volumes.get(volume_id).status != "available":
tierno1df468d2018-07-06 14:25:16 +02001884 break
1885 else: # all ready: break from while
1886 break
sousaedu80135b92021-02-17 15:05:18 +01001887
tierno1df468d2018-07-06 14:25:16 +02001888 time.sleep(5)
1889 elapsed_time += 5
sousaedu80135b92021-02-17 15:05:18 +01001890
tiernob0b9dab2017-10-14 14:25:20 +02001891 # If we exceeded the timeout rollback
montesmoreno0c8def02016-12-22 12:16:23 +00001892 if elapsed_time >= volume_timeout:
sousaedu80135b92021-02-17 15:05:18 +01001893 raise vimconn.VimConnException(
1894 "Timeout creating volumes for instance " + name,
1895 http_code=vimconn.HTTP_Request_Timeout,
1896 )
montesmoreno0c8def02016-12-22 12:16:23 +00001897
sousaedu80135b92021-02-17 15:05:18 +01001898 # get availability Zone
1899 vm_av_zone = self._get_vm_availability_zone(
1900 availability_zone_index, availability_zone_list
1901 )
1902
1903 self.logger.debug(
1904 "nova.servers.create({}, {}, {}, nics={}, security_groups={}, "
1905 "availability_zone={}, key_name={}, userdata={}, config_drive={}, "
1906 "block_device_mapping={})".format(
1907 name,
1908 image_id,
1909 flavor_id,
1910 net_list_vim,
1911 self.config.get("security_groups"),
1912 vm_av_zone,
1913 self.config.get("keypair"),
1914 userdata,
1915 config_drive,
1916 block_device_mapping,
1917 )
1918 )
1919 server = self.nova.servers.create(
1920 name,
1921 image_id,
1922 flavor_id,
1923 nics=net_list_vim,
1924 security_groups=self.config.get("security_groups"),
1925 # TODO remove security_groups in future versions. Already at neutron port
1926 availability_zone=vm_av_zone,
1927 key_name=self.config.get("keypair"),
1928 userdata=userdata,
1929 config_drive=config_drive,
1930 block_device_mapping=block_device_mapping,
1931 ) # , description=description)
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001932
tierno326fd5e2018-02-22 11:58:59 +01001933 vm_start_time = time.time()
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001934 # Previously mentioned workaround to wait until the VM is active and then disable the port-security
1935 if no_secured_ports:
sousaedu80135b92021-02-17 15:05:18 +01001936 self.__wait_for_vm(server.id, "ACTIVE")
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001937
bravof7a1f5252020-10-20 10:27:42 -03001938 for port in no_secured_ports:
1939 port_update = {
sousaedu80135b92021-02-17 15:05:18 +01001940 "port": {"port_security_enabled": False, "security_groups": None}
bravof7a1f5252020-10-20 10:27:42 -03001941 }
1942
1943 if port[1] == "allow-address-pairs":
1944 port_update = {
sousaedu80135b92021-02-17 15:05:18 +01001945 "port": {"allowed_address_pairs": [{"ip_address": "0.0.0.0/0"}]}
bravof7a1f5252020-10-20 10:27:42 -03001946 }
1947
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001948 try:
bravof7a1f5252020-10-20 10:27:42 -03001949 self.neutron.update_port(port[0], port_update)
tierno1ec592d2020-06-16 15:29:47 +00001950 except Exception:
bravof7a1f5252020-10-20 10:27:42 -03001951 raise vimconn.VimConnException(
sousaedu80135b92021-02-17 15:05:18 +01001952 "It was not possible to disable port security for port {}".format(
1953 port[0]
1954 )
bravof7a1f5252020-10-20 10:27:42 -03001955 )
1956
tierno98e909c2017-10-14 13:27:03 +02001957 # print "DONE :-)", server
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001958
tierno4d1ce222018-04-06 10:41:06 +02001959 # pool_id = None
ahmadsaf853d452016-12-22 11:33:47 +05001960 for floating_network in external_network:
tiernof8383b82017-01-18 15:49:48 +01001961 try:
tiernof8383b82017-01-18 15:49:48 +01001962 assigned = False
tiernocb66c7e2020-07-22 10:42:58 +00001963 floating_ip_retries = 3
1964 # In case of RO in HA there can be conflicts, two RO trying to assign same floating IP, so retry
1965 # several times
tierno98e909c2017-10-14 13:27:03 +02001966 while not assigned:
sousaedu80135b92021-02-17 15:05:18 +01001967 floating_ips = self.neutron.list_floatingips().get(
1968 "floatingips", ()
1969 )
1970 random.shuffle(floating_ips) # randomize
tiernocb66c7e2020-07-22 10:42:58 +00001971 for fip in floating_ips:
sousaedu80135b92021-02-17 15:05:18 +01001972 if (
1973 fip.get("port_id")
1974 or fip.get("tenant_id") != server.tenant_id
1975 ):
tierno326fd5e2018-02-22 11:58:59 +01001976 continue
sousaedu80135b92021-02-17 15:05:18 +01001977
1978 if isinstance(floating_network["floating_ip"], str):
1979 if (
1980 fip.get("floating_network_id")
1981 != floating_network["floating_ip"]
1982 ):
tierno326fd5e2018-02-22 11:58:59 +01001983 continue
sousaedu80135b92021-02-17 15:05:18 +01001984
tiernocb66c7e2020-07-22 10:42:58 +00001985 free_floating_ip = fip["id"]
1986 break
tiernof8383b82017-01-18 15:49:48 +01001987 else:
sousaedu80135b92021-02-17 15:05:18 +01001988 if (
1989 isinstance(floating_network["floating_ip"], str)
1990 and floating_network["floating_ip"].lower() != "true"
1991 ):
1992 pool_id = floating_network["floating_ip"]
tierno326fd5e2018-02-22 11:58:59 +01001993 else:
tierno4d1ce222018-04-06 10:41:06 +02001994 # Find the external network
tierno326fd5e2018-02-22 11:58:59 +01001995 external_nets = list()
sousaedu80135b92021-02-17 15:05:18 +01001996
1997 for net in self.neutron.list_networks()["networks"]:
1998 if net["router:external"]:
tierno1ec592d2020-06-16 15:29:47 +00001999 external_nets.append(net)
tiernof8383b82017-01-18 15:49:48 +01002000
tierno326fd5e2018-02-22 11:58:59 +01002001 if len(external_nets) == 0:
tierno1ec592d2020-06-16 15:29:47 +00002002 raise vimconn.VimConnException(
sousaedu80135b92021-02-17 15:05:18 +01002003 "Cannot create floating_ip automatically since "
2004 "no external network is present",
2005 http_code=vimconn.HTTP_Conflict,
2006 )
2007
tierno326fd5e2018-02-22 11:58:59 +01002008 if len(external_nets) > 1:
tierno1ec592d2020-06-16 15:29:47 +00002009 raise vimconn.VimConnException(
sousaedu80135b92021-02-17 15:05:18 +01002010 "Cannot create floating_ip automatically since "
2011 "multiple external networks are present",
2012 http_code=vimconn.HTTP_Conflict,
2013 )
tiernof8383b82017-01-18 15:49:48 +01002014
sousaedu80135b92021-02-17 15:05:18 +01002015 pool_id = external_nets[0].get("id")
2016
2017 param = {
2018 "floatingip": {
2019 "floating_network_id": pool_id,
2020 "tenant_id": server.tenant_id,
2021 }
2022 }
2023
ahmadsaf853d452016-12-22 11:33:47 +05002024 try:
tierno4d1ce222018-04-06 10:41:06 +02002025 # self.logger.debug("Creating floating IP")
tiernof8383b82017-01-18 15:49:48 +01002026 new_floating_ip = self.neutron.create_floatingip(param)
sousaedu80135b92021-02-17 15:05:18 +01002027 free_floating_ip = new_floating_ip["floatingip"]["id"]
2028 created_items[
2029 "floating_ip:" + str(free_floating_ip)
2030 ] = True
ahmadsaf853d452016-12-22 11:33:47 +05002031 except Exception as e:
sousaedu80135b92021-02-17 15:05:18 +01002032 raise vimconn.VimConnException(
2033 type(e).__name__
2034 + ": Cannot create new floating_ip "
2035 + str(e),
2036 http_code=vimconn.HTTP_Conflict,
2037 )
tierno326fd5e2018-02-22 11:58:59 +01002038
tiernocb66c7e2020-07-22 10:42:58 +00002039 try:
2040 # for race condition ensure not already assigned
2041 fip = self.neutron.show_floatingip(free_floating_ip)
sousaedu80135b92021-02-17 15:05:18 +01002042
2043 if fip["floatingip"]["port_id"]:
tiernocb66c7e2020-07-22 10:42:58 +00002044 continue
sousaedu80135b92021-02-17 15:05:18 +01002045
tiernocb66c7e2020-07-22 10:42:58 +00002046 # the vim_id key contains the neutron.port_id
sousaedu80135b92021-02-17 15:05:18 +01002047 self.neutron.update_floatingip(
2048 free_floating_ip,
2049 {"floatingip": {"port_id": floating_network["vim_id"]}},
2050 )
tiernocb66c7e2020-07-22 10:42:58 +00002051 # for race condition ensure not re-assigned to other VM after 5 seconds
2052 time.sleep(5)
2053 fip = self.neutron.show_floatingip(free_floating_ip)
sousaedu80135b92021-02-17 15:05:18 +01002054
2055 if (
2056 fip["floatingip"]["port_id"]
2057 != floating_network["vim_id"]
2058 ):
2059 self.logger.error(
2060 "floating_ip {} re-assigned to other port".format(
2061 free_floating_ip
2062 )
2063 )
tiernocb66c7e2020-07-22 10:42:58 +00002064 continue
sousaedu80135b92021-02-17 15:05:18 +01002065
2066 self.logger.debug(
2067 "Assigned floating_ip {} to VM {}".format(
2068 free_floating_ip, server.id
2069 )
2070 )
tiernocb66c7e2020-07-22 10:42:58 +00002071 assigned = True
2072 except Exception as e:
2073 # openstack need some time after VM creation to assign an IP. So retry if fails
2074 vm_status = self.nova.servers.get(server.id).status
sousaedu80135b92021-02-17 15:05:18 +01002075
2076 if vm_status not in ("ACTIVE", "ERROR"):
tiernocb66c7e2020-07-22 10:42:58 +00002077 if time.time() - vm_start_time < server_timeout:
2078 time.sleep(5)
2079 continue
2080 elif floating_ip_retries > 0:
2081 floating_ip_retries -= 1
2082 continue
sousaedu80135b92021-02-17 15:05:18 +01002083
tiernocb66c7e2020-07-22 10:42:58 +00002084 raise vimconn.VimConnException(
sousaedu80135b92021-02-17 15:05:18 +01002085 "Cannot create floating_ip: {} {}".format(
2086 type(e).__name__, e
2087 ),
2088 http_code=vimconn.HTTP_Conflict,
2089 )
tierno326fd5e2018-02-22 11:58:59 +01002090
tiernof8383b82017-01-18 15:49:48 +01002091 except Exception as e:
sousaedu80135b92021-02-17 15:05:18 +01002092 if not floating_network["exit_on_floating_ip_error"]:
tiernocb66c7e2020-07-22 10:42:58 +00002093 self.logger.error("Cannot create floating_ip. %s", str(e))
tiernof8383b82017-01-18 15:49:48 +01002094 continue
sousaedu80135b92021-02-17 15:05:18 +01002095
tiernof8383b82017-01-18 15:49:48 +01002096 raise
montesmoreno2a1fc4e2017-01-09 16:46:04 +00002097
tierno98e909c2017-10-14 13:27:03 +02002098 return server.id, created_items
tierno1ec592d2020-06-16 15:29:47 +00002099 # except nvExceptions.NotFound as e:
2100 # error_value=-vimconn.HTTP_Not_Found
2101 # error_text= "vm instance %s not found" % vm_id
2102 # except TypeError as e:
2103 # raise vimconn.VimConnException(type(e).__name__ + ": "+ str(e), http_code=vimconn.HTTP_Bad_Request)
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02002104
2105 except Exception as e:
tierno98e909c2017-10-14 13:27:03 +02002106 server_id = None
2107 if server:
2108 server_id = server.id
sousaedu80135b92021-02-17 15:05:18 +01002109
tierno98e909c2017-10-14 13:27:03 +02002110 try:
2111 self.delete_vminstance(server_id, created_items)
2112 except Exception as e2:
2113 self.logger.error("new_vminstance rollback fail {}".format(e2))
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02002114
tiernoae4a8d12016-07-08 12:30:39 +02002115 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01002116
tierno1ec592d2020-06-16 15:29:47 +00002117 def get_vminstance(self, vm_id):
2118 """Returns the VM instance information from VIM"""
2119 # self.logger.debug("Getting VM from VIM")
tierno7edb6752016-03-21 17:37:52 +01002120 try:
2121 self._reload_connection()
2122 server = self.nova.servers.find(id=vm_id)
tierno1ec592d2020-06-16 15:29:47 +00002123 # TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
sousaedu80135b92021-02-17 15:05:18 +01002124
tiernoae4a8d12016-07-08 12:30:39 +02002125 return server.to_dict()
sousaedu80135b92021-02-17 15:05:18 +01002126 except (
2127 ksExceptions.ClientException,
2128 nvExceptions.ClientException,
2129 nvExceptions.NotFound,
2130 ConnectionError,
2131 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02002132 self._format_exception(e)
2133
tierno1ec592d2020-06-16 15:29:47 +00002134 def get_vminstance_console(self, vm_id, console_type="vnc"):
2135 """
tierno7edb6752016-03-21 17:37:52 +01002136 Get a console for the virtual machine
2137 Params:
2138 vm_id: uuid of the VM
2139 console_type, can be:
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01002140 "novnc" (by default), "xvpvnc" for VNC types,
tierno7edb6752016-03-21 17:37:52 +01002141 "rdp-html5" for RDP types, "spice-html5" for SPICE types
tiernoae4a8d12016-07-08 12:30:39 +02002142 Returns dict with the console parameters:
2143 protocol: ssh, ftp, http, https, ...
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01002144 server: usually ip address
2145 port: the http, ssh, ... port
2146 suffix: extra text, e.g. the http path and query string
tierno1ec592d2020-06-16 15:29:47 +00002147 """
tiernoae4a8d12016-07-08 12:30:39 +02002148 self.logger.debug("Getting VM CONSOLE from VIM")
sousaedu80135b92021-02-17 15:05:18 +01002149
tierno7edb6752016-03-21 17:37:52 +01002150 try:
2151 self._reload_connection()
2152 server = self.nova.servers.find(id=vm_id)
sousaedu80135b92021-02-17 15:05:18 +01002153
tierno1ec592d2020-06-16 15:29:47 +00002154 if console_type is None or console_type == "novnc":
tierno7edb6752016-03-21 17:37:52 +01002155 console_dict = server.get_vnc_console("novnc")
2156 elif console_type == "xvpvnc":
2157 console_dict = server.get_vnc_console(console_type)
2158 elif console_type == "rdp-html5":
2159 console_dict = server.get_rdp_console(console_type)
2160 elif console_type == "spice-html5":
2161 console_dict = server.get_spice_console(console_type)
2162 else:
sousaedu80135b92021-02-17 15:05:18 +01002163 raise vimconn.VimConnException(
2164 "console type '{}' not allowed".format(console_type),
2165 http_code=vimconn.HTTP_Bad_Request,
2166 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002167
tierno7edb6752016-03-21 17:37:52 +01002168 console_dict1 = console_dict.get("console")
sousaedu80135b92021-02-17 15:05:18 +01002169
tierno7edb6752016-03-21 17:37:52 +01002170 if console_dict1:
2171 console_url = console_dict1.get("url")
sousaedu80135b92021-02-17 15:05:18 +01002172
tierno7edb6752016-03-21 17:37:52 +01002173 if console_url:
tierno1ec592d2020-06-16 15:29:47 +00002174 # parse console_url
tierno7edb6752016-03-21 17:37:52 +01002175 protocol_index = console_url.find("//")
sousaedu80135b92021-02-17 15:05:18 +01002176 suffix_index = (
2177 console_url[protocol_index + 2 :].find("/") + protocol_index + 2
2178 )
2179 port_index = (
2180 console_url[protocol_index + 2 : suffix_index].find(":")
2181 + protocol_index
2182 + 2
2183 )
2184
tierno1ec592d2020-06-16 15:29:47 +00002185 if protocol_index < 0 or port_index < 0 or suffix_index < 0:
sousaedu80135b92021-02-17 15:05:18 +01002186 return (
2187 -vimconn.HTTP_Internal_Server_Error,
2188 "Unexpected response from VIM",
2189 )
2190
2191 console_dict = {
2192 "protocol": console_url[0:protocol_index],
2193 "server": console_url[protocol_index + 2 : port_index],
2194 "port": console_url[port_index:suffix_index],
2195 "suffix": console_url[suffix_index + 1 :],
2196 }
tierno7edb6752016-03-21 17:37:52 +01002197 protocol_index += 2
sousaedu80135b92021-02-17 15:05:18 +01002198
tiernoae4a8d12016-07-08 12:30:39 +02002199 return console_dict
tierno72774862020-05-04 11:44:15 +00002200 raise vimconn.VimConnUnexpectedResponse("Unexpected response from VIM")
sousaedu80135b92021-02-17 15:05:18 +01002201 except (
2202 nvExceptions.NotFound,
2203 ksExceptions.ClientException,
2204 nvExceptions.ClientException,
2205 nvExceptions.BadRequest,
2206 ConnectionError,
2207 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02002208 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01002209
tierno98e909c2017-10-14 13:27:03 +02002210 def delete_vminstance(self, vm_id, created_items=None):
sousaedu80135b92021-02-17 15:05:18 +01002211 """Removes a VM instance from VIM. Returns the old identifier"""
tierno1ec592d2020-06-16 15:29:47 +00002212 # print "osconnector: Getting VM from VIM"
2213 if created_items is None:
tierno98e909c2017-10-14 13:27:03 +02002214 created_items = {}
sousaedu80135b92021-02-17 15:05:18 +01002215
tierno7edb6752016-03-21 17:37:52 +01002216 try:
2217 self._reload_connection()
tierno98e909c2017-10-14 13:27:03 +02002218 # delete VM ports attached to this networks before the virtual machine
2219 for k, v in created_items.items():
2220 if not v: # skip already deleted
2221 continue
sousaedu80135b92021-02-17 15:05:18 +01002222
tierno7edb6752016-03-21 17:37:52 +01002223 try:
tiernoad6bdd42018-01-10 10:43:46 +01002224 k_item, _, k_id = k.partition(":")
2225 if k_item == "port":
2226 self.neutron.delete_port(k_id)
tierno7edb6752016-03-21 17:37:52 +01002227 except Exception as e:
sousaedu80135b92021-02-17 15:05:18 +01002228 self.logger.error(
2229 "Error deleting port: {}: {}".format(type(e).__name__, e)
2230 )
montesmoreno0c8def02016-12-22 12:16:23 +00002231
tierno98e909c2017-10-14 13:27:03 +02002232 # #commented because detaching the volumes makes the servers.delete not work properly ?!?
2233 # #dettach volumes attached
2234 # server = self.nova.servers.get(vm_id)
sousaedu80135b92021-02-17 15:05:18 +01002235 # volumes_attached_dict = server._info["os-extended-volumes:volumes_attached"] #volume["id"]
tierno98e909c2017-10-14 13:27:03 +02002236 # #for volume in volumes_attached_dict:
sousaedu80135b92021-02-17 15:05:18 +01002237 # # self.cinder.volumes.detach(volume["id"])
montesmoreno0c8def02016-12-22 12:16:23 +00002238
tierno98e909c2017-10-14 13:27:03 +02002239 if vm_id:
2240 self.nova.servers.delete(vm_id)
montesmoreno0c8def02016-12-22 12:16:23 +00002241
tierno98e909c2017-10-14 13:27:03 +02002242 # delete volumes. Although having detached, they should have in active status before deleting
2243 # we ensure in this loop
montesmoreno0c8def02016-12-22 12:16:23 +00002244 keep_waiting = True
2245 elapsed_time = 0
sousaedu80135b92021-02-17 15:05:18 +01002246
montesmoreno0c8def02016-12-22 12:16:23 +00002247 while keep_waiting and elapsed_time < volume_timeout:
2248 keep_waiting = False
sousaedu80135b92021-02-17 15:05:18 +01002249
tierno98e909c2017-10-14 13:27:03 +02002250 for k, v in created_items.items():
2251 if not v: # skip already deleted
2252 continue
sousaedu80135b92021-02-17 15:05:18 +01002253
tierno98e909c2017-10-14 13:27:03 +02002254 try:
tiernoad6bdd42018-01-10 10:43:46 +01002255 k_item, _, k_id = k.partition(":")
2256 if k_item == "volume":
sousaedu80135b92021-02-17 15:05:18 +01002257 if self.cinder.volumes.get(k_id).status != "available":
tierno98e909c2017-10-14 13:27:03 +02002258 keep_waiting = True
2259 else:
tiernoad6bdd42018-01-10 10:43:46 +01002260 self.cinder.volumes.delete(k_id)
tiernocb66c7e2020-07-22 10:42:58 +00002261 created_items[k] = None
2262 elif k_item == "floating_ip": # floating ip
2263 self.neutron.delete_floatingip(k_id)
2264 created_items[k] = None
2265
tierno98e909c2017-10-14 13:27:03 +02002266 except Exception as e:
tiernocb66c7e2020-07-22 10:42:58 +00002267 self.logger.error("Error deleting {}: {}".format(k, e))
sousaedu80135b92021-02-17 15:05:18 +01002268
montesmoreno0c8def02016-12-22 12:16:23 +00002269 if keep_waiting:
2270 time.sleep(1)
2271 elapsed_time += 1
sousaedu80135b92021-02-17 15:05:18 +01002272
tierno98e909c2017-10-14 13:27:03 +02002273 return None
sousaedu80135b92021-02-17 15:05:18 +01002274 except (
2275 nvExceptions.NotFound,
2276 ksExceptions.ClientException,
2277 nvExceptions.ClientException,
2278 ConnectionError,
2279 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02002280 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01002281
tiernoae4a8d12016-07-08 12:30:39 +02002282 def refresh_vms_status(self, vm_list):
tierno1ec592d2020-06-16 15:29:47 +00002283 """Get the status of the virtual machines and their interfaces/ports
sousaedu80135b92021-02-17 15:05:18 +01002284 Params: the list of VM identifiers
2285 Returns a dictionary with:
2286 vm_id: #VIM id of this Virtual Machine
2287 status: #Mandatory. Text with one of:
2288 # DELETED (not found at vim)
2289 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
2290 # OTHER (Vim reported other status not understood)
2291 # ERROR (VIM indicates an ERROR status)
2292 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
2293 # CREATING (on building process), ERROR
2294 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
2295 #
2296 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
2297 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
2298 interfaces:
2299 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
2300 mac_address: #Text format XX:XX:XX:XX:XX:XX
2301 vim_net_id: #network id where this interface is connected
2302 vim_interface_id: #interface/port VIM id
2303 ip_address: #null, or text with IPv4, IPv6 address
2304 compute_node: #identification of compute node where PF,VF interface is allocated
2305 pci: #PCI address of the NIC that hosts the PF,VF
2306 vlan: #physical VLAN used for VF
tierno1ec592d2020-06-16 15:29:47 +00002307 """
2308 vm_dict = {}
sousaedu80135b92021-02-17 15:05:18 +01002309 self.logger.debug(
2310 "refresh_vms status: Getting tenant VM instance information from VIM"
2311 )
2312
tiernoae4a8d12016-07-08 12:30:39 +02002313 for vm_id in vm_list:
tierno1ec592d2020-06-16 15:29:47 +00002314 vm = {}
sousaedu80135b92021-02-17 15:05:18 +01002315
tiernoae4a8d12016-07-08 12:30:39 +02002316 try:
2317 vm_vim = self.get_vminstance(vm_id)
sousaedu80135b92021-02-17 15:05:18 +01002318
2319 if vm_vim["status"] in vmStatus2manoFormat:
2320 vm["status"] = vmStatus2manoFormat[vm_vim["status"]]
tierno7edb6752016-03-21 17:37:52 +01002321 else:
sousaedu80135b92021-02-17 15:05:18 +01002322 vm["status"] = "OTHER"
2323 vm["error_msg"] = "VIM status reported " + vm_vim["status"]
2324
tierno70eeb182020-10-19 16:38:00 +00002325 vm_vim.pop("OS-EXT-SRV-ATTR:user_data", None)
2326 vm_vim.pop("user_data", None)
sousaedu80135b92021-02-17 15:05:18 +01002327 vm["vim_info"] = self.serialize(vm_vim)
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01002328
tiernoae4a8d12016-07-08 12:30:39 +02002329 vm["interfaces"] = []
sousaedu80135b92021-02-17 15:05:18 +01002330 if vm_vim.get("fault"):
2331 vm["error_msg"] = str(vm_vim["fault"])
2332
tierno1ec592d2020-06-16 15:29:47 +00002333 # get interfaces
tierno7edb6752016-03-21 17:37:52 +01002334 try:
tiernoae4a8d12016-07-08 12:30:39 +02002335 self._reload_connection()
tiernob42fd9b2018-06-20 10:44:32 +02002336 port_dict = self.neutron.list_ports(device_id=vm_id)
sousaedu80135b92021-02-17 15:05:18 +01002337
tiernoae4a8d12016-07-08 12:30:39 +02002338 for port in port_dict["ports"]:
tierno1ec592d2020-06-16 15:29:47 +00002339 interface = {}
sousaedu80135b92021-02-17 15:05:18 +01002340 interface["vim_info"] = self.serialize(port)
tiernoae4a8d12016-07-08 12:30:39 +02002341 interface["mac_address"] = port.get("mac_address")
2342 interface["vim_net_id"] = port["network_id"]
2343 interface["vim_interface_id"] = port["id"]
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01002344 # check if OS-EXT-SRV-ATTR:host is there,
Mike Marchetti5b9da422017-05-02 15:35:47 -04002345 # in case of non-admin credentials, it will be missing
sousaedu80135b92021-02-17 15:05:18 +01002346
2347 if vm_vim.get("OS-EXT-SRV-ATTR:host"):
2348 interface["compute_node"] = vm_vim["OS-EXT-SRV-ATTR:host"]
2349
tierno867ffe92017-03-27 12:50:34 +02002350 interface["pci"] = None
Mike Marchetti5b9da422017-05-02 15:35:47 -04002351
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01002352 # check if binding:profile is there,
Mike Marchetti5b9da422017-05-02 15:35:47 -04002353 # in case of non-admin credentials, it will be missing
sousaedu80135b92021-02-17 15:05:18 +01002354 if port.get("binding:profile"):
2355 if port["binding:profile"].get("pci_slot"):
tierno1ec592d2020-06-16 15:29:47 +00002356 # TODO: At the moment sr-iov pci addresses are converted to PF pci addresses by setting
2357 # the slot to 0x00
Mike Marchetti5b9da422017-05-02 15:35:47 -04002358 # TODO: This is just a workaround valid for niantinc. Find a better way to do so
2359 # CHANGE DDDD:BB:SS.F to DDDD:BB:00.(F%2) assuming there are 2 ports per nic
sousaedu80135b92021-02-17 15:05:18 +01002360 pci = port["binding:profile"]["pci_slot"]
Mike Marchetti5b9da422017-05-02 15:35:47 -04002361 # interface["pci"] = pci[:-4] + "00." + str(int(pci[-1]) % 2)
2362 interface["pci"] = pci
sousaedu80135b92021-02-17 15:05:18 +01002363
tierno867ffe92017-03-27 12:50:34 +02002364 interface["vlan"] = None
sousaedu80135b92021-02-17 15:05:18 +01002365
2366 if port.get("binding:vif_details"):
2367 interface["vlan"] = port["binding:vif_details"].get("vlan")
2368
tierno1dfe9932020-06-18 08:50:10 +00002369 # Get vlan from network in case not present in port for those old openstacks and cases where
2370 # it is needed vlan at PT
2371 if not interface["vlan"]:
2372 # if network is of type vlan and port is of type direct (sr-iov) then set vlan id
2373 network = self.neutron.show_network(port["network_id"])
sousaedu80135b92021-02-17 15:05:18 +01002374
2375 if (
2376 network["network"].get("provider:network_type")
2377 == "vlan"
2378 ):
tierno1dfe9932020-06-18 08:50:10 +00002379 # and port.get("binding:vnic_type") in ("direct", "direct-physical"):
sousaedu80135b92021-02-17 15:05:18 +01002380 interface["vlan"] = network["network"].get(
2381 "provider:segmentation_id"
2382 )
2383
tierno1ec592d2020-06-16 15:29:47 +00002384 ips = []
2385 # look for floating ip address
tiernob42fd9b2018-06-20 10:44:32 +02002386 try:
sousaedu80135b92021-02-17 15:05:18 +01002387 floating_ip_dict = self.neutron.list_floatingips(
2388 port_id=port["id"]
2389 )
2390
tiernob42fd9b2018-06-20 10:44:32 +02002391 if floating_ip_dict.get("floatingips"):
sousaedu80135b92021-02-17 15:05:18 +01002392 ips.append(
2393 floating_ip_dict["floatingips"][0].get(
2394 "floating_ip_address"
2395 )
2396 )
tiernob42fd9b2018-06-20 10:44:32 +02002397 except Exception:
2398 pass
tierno7edb6752016-03-21 17:37:52 +01002399
tiernoae4a8d12016-07-08 12:30:39 +02002400 for subnet in port["fixed_ips"]:
2401 ips.append(subnet["ip_address"])
sousaedu80135b92021-02-17 15:05:18 +01002402
tiernoae4a8d12016-07-08 12:30:39 +02002403 interface["ip_address"] = ";".join(ips)
2404 vm["interfaces"].append(interface)
2405 except Exception as e:
sousaedu80135b92021-02-17 15:05:18 +01002406 self.logger.error(
2407 "Error getting vm interface information {}: {}".format(
2408 type(e).__name__, e
2409 ),
2410 exc_info=True,
2411 )
tierno72774862020-05-04 11:44:15 +00002412 except vimconn.VimConnNotFoundException as e:
tiernoae4a8d12016-07-08 12:30:39 +02002413 self.logger.error("Exception getting vm status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01002414 vm["status"] = "DELETED"
2415 vm["error_msg"] = str(e)
tierno72774862020-05-04 11:44:15 +00002416 except vimconn.VimConnException as e:
tiernoae4a8d12016-07-08 12:30:39 +02002417 self.logger.error("Exception getting vm status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01002418 vm["status"] = "VIM_ERROR"
2419 vm["error_msg"] = str(e)
2420
tiernoae4a8d12016-07-08 12:30:39 +02002421 vm_dict[vm_id] = vm
sousaedu80135b92021-02-17 15:05:18 +01002422
tiernoae4a8d12016-07-08 12:30:39 +02002423 return vm_dict
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002424
tierno98e909c2017-10-14 13:27:03 +02002425 def action_vminstance(self, vm_id, action_dict, created_items={}):
tierno1ec592d2020-06-16 15:29:47 +00002426 """Send and action over a VM instance from VIM
2427 Returns None or the console dict if the action was successfully sent to the VIM"""
tiernoae4a8d12016-07-08 12:30:39 +02002428 self.logger.debug("Action over VM '%s': %s", vm_id, str(action_dict))
sousaedu80135b92021-02-17 15:05:18 +01002429
tierno7edb6752016-03-21 17:37:52 +01002430 try:
2431 self._reload_connection()
2432 server = self.nova.servers.find(id=vm_id)
sousaedu80135b92021-02-17 15:05:18 +01002433
tierno7edb6752016-03-21 17:37:52 +01002434 if "start" in action_dict:
tierno1ec592d2020-06-16 15:29:47 +00002435 if action_dict["start"] == "rebuild":
tierno7edb6752016-03-21 17:37:52 +01002436 server.rebuild()
2437 else:
tierno1ec592d2020-06-16 15:29:47 +00002438 if server.status == "PAUSED":
tierno7edb6752016-03-21 17:37:52 +01002439 server.unpause()
tierno1ec592d2020-06-16 15:29:47 +00002440 elif server.status == "SUSPENDED":
tierno7edb6752016-03-21 17:37:52 +01002441 server.resume()
tierno1ec592d2020-06-16 15:29:47 +00002442 elif server.status == "SHUTOFF":
tierno7edb6752016-03-21 17:37:52 +01002443 server.start()
2444 elif "pause" in action_dict:
2445 server.pause()
2446 elif "resume" in action_dict:
2447 server.resume()
2448 elif "shutoff" in action_dict or "shutdown" in action_dict:
2449 server.stop()
2450 elif "forceOff" in action_dict:
tierno1ec592d2020-06-16 15:29:47 +00002451 server.stop() # TODO
tierno7edb6752016-03-21 17:37:52 +01002452 elif "terminate" in action_dict:
2453 server.delete()
2454 elif "createImage" in action_dict:
2455 server.create_image()
tierno1ec592d2020-06-16 15:29:47 +00002456 # "path":path_schema,
2457 # "description":description_schema,
2458 # "name":name_schema,
2459 # "metadata":metadata_schema,
2460 # "imageRef": id_schema,
2461 # "disk": {"oneOf":[{"type": "null"}, {"type":"string"}] },
tierno7edb6752016-03-21 17:37:52 +01002462 elif "rebuild" in action_dict:
sousaedu80135b92021-02-17 15:05:18 +01002463 server.rebuild(server.image["id"])
tierno7edb6752016-03-21 17:37:52 +01002464 elif "reboot" in action_dict:
sousaedu80135b92021-02-17 15:05:18 +01002465 server.reboot() # reboot_type="SOFT"
tierno7edb6752016-03-21 17:37:52 +01002466 elif "console" in action_dict:
2467 console_type = action_dict["console"]
sousaedu80135b92021-02-17 15:05:18 +01002468
tierno1ec592d2020-06-16 15:29:47 +00002469 if console_type is None or console_type == "novnc":
tierno7edb6752016-03-21 17:37:52 +01002470 console_dict = server.get_vnc_console("novnc")
2471 elif console_type == "xvpvnc":
2472 console_dict = server.get_vnc_console(console_type)
2473 elif console_type == "rdp-html5":
2474 console_dict = server.get_rdp_console(console_type)
2475 elif console_type == "spice-html5":
2476 console_dict = server.get_spice_console(console_type)
2477 else:
sousaedu80135b92021-02-17 15:05:18 +01002478 raise vimconn.VimConnException(
2479 "console type '{}' not allowed".format(console_type),
2480 http_code=vimconn.HTTP_Bad_Request,
2481 )
2482
tierno7edb6752016-03-21 17:37:52 +01002483 try:
2484 console_url = console_dict["console"]["url"]
tierno1ec592d2020-06-16 15:29:47 +00002485 # parse console_url
tierno7edb6752016-03-21 17:37:52 +01002486 protocol_index = console_url.find("//")
sousaedu80135b92021-02-17 15:05:18 +01002487 suffix_index = (
2488 console_url[protocol_index + 2 :].find("/") + protocol_index + 2
2489 )
2490 port_index = (
2491 console_url[protocol_index + 2 : suffix_index].find(":")
2492 + protocol_index
2493 + 2
2494 )
2495
tierno1ec592d2020-06-16 15:29:47 +00002496 if protocol_index < 0 or port_index < 0 or suffix_index < 0:
sousaedu80135b92021-02-17 15:05:18 +01002497 raise vimconn.VimConnException(
2498 "Unexpected response from VIM " + str(console_dict)
2499 )
2500
2501 console_dict2 = {
2502 "protocol": console_url[0:protocol_index],
2503 "server": console_url[protocol_index + 2 : port_index],
2504 "port": int(console_url[port_index + 1 : suffix_index]),
2505 "suffix": console_url[suffix_index + 1 :],
2506 }
2507
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002508 return console_dict2
tierno1ec592d2020-06-16 15:29:47 +00002509 except Exception:
sousaedu80135b92021-02-17 15:05:18 +01002510 raise vimconn.VimConnException(
2511 "Unexpected response from VIM " + str(console_dict)
2512 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002513
tierno98e909c2017-10-14 13:27:03 +02002514 return None
sousaedu80135b92021-02-17 15:05:18 +01002515 except (
2516 ksExceptions.ClientException,
2517 nvExceptions.ClientException,
2518 nvExceptions.NotFound,
2519 ConnectionError,
2520 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02002521 self._format_exception(e)
tierno1ec592d2020-06-16 15:29:47 +00002522 # TODO insert exception vimconn.HTTP_Unauthorized
tiernoae4a8d12016-07-08 12:30:39 +02002523
tierno1ec592d2020-06-16 15:29:47 +00002524 # ###### VIO Specific Changes #########
garciadeblasebd66722019-01-31 16:01:31 +00002525 def _generate_vlanID(self):
kate721d79b2017-06-24 04:21:38 -07002526 """
sousaedu80135b92021-02-17 15:05:18 +01002527 Method to get unused vlanID
kate721d79b2017-06-24 04:21:38 -07002528 Args:
2529 None
2530 Returns:
2531 vlanID
2532 """
tierno1ec592d2020-06-16 15:29:47 +00002533 # Get used VLAN IDs
kate721d79b2017-06-24 04:21:38 -07002534 usedVlanIDs = []
2535 networks = self.get_network_list()
sousaedu80135b92021-02-17 15:05:18 +01002536
kate721d79b2017-06-24 04:21:38 -07002537 for net in networks:
sousaedu80135b92021-02-17 15:05:18 +01002538 if net.get("provider:segmentation_id"):
2539 usedVlanIDs.append(net.get("provider:segmentation_id"))
2540
kate721d79b2017-06-24 04:21:38 -07002541 used_vlanIDs = set(usedVlanIDs)
2542
tierno1ec592d2020-06-16 15:29:47 +00002543 # find unused VLAN ID
sousaedu80135b92021-02-17 15:05:18 +01002544 for vlanID_range in self.config.get("dataplane_net_vlan_range"):
kate721d79b2017-06-24 04:21:38 -07002545 try:
sousaedu80135b92021-02-17 15:05:18 +01002546 start_vlanid, end_vlanid = map(
2547 int, vlanID_range.replace(" ", "").split("-")
2548 )
2549
tierno7d782ef2019-10-04 12:56:31 +00002550 for vlanID in range(start_vlanid, end_vlanid + 1):
kate721d79b2017-06-24 04:21:38 -07002551 if vlanID not in used_vlanIDs:
2552 return vlanID
2553 except Exception as exp:
sousaedu80135b92021-02-17 15:05:18 +01002554 raise vimconn.VimConnException(
2555 "Exception {} occurred while generating VLAN ID.".format(exp)
2556 )
kate721d79b2017-06-24 04:21:38 -07002557 else:
tierno1ec592d2020-06-16 15:29:47 +00002558 raise vimconn.VimConnConflictException(
2559 "Unable to create the SRIOV VLAN network. All given Vlan IDs {} are in use.".format(
sousaedu80135b92021-02-17 15:05:18 +01002560 self.config.get("dataplane_net_vlan_range")
2561 )
2562 )
kate721d79b2017-06-24 04:21:38 -07002563
garciadeblasebd66722019-01-31 16:01:31 +00002564 def _generate_multisegment_vlanID(self):
2565 """
sousaedu80135b92021-02-17 15:05:18 +01002566 Method to get unused vlanID
2567 Args:
2568 None
2569 Returns:
2570 vlanID
garciadeblasebd66722019-01-31 16:01:31 +00002571 """
tierno6869ae72020-01-09 17:37:34 +00002572 # Get used VLAN IDs
garciadeblasebd66722019-01-31 16:01:31 +00002573 usedVlanIDs = []
2574 networks = self.get_network_list()
2575 for net in networks:
sousaedu80135b92021-02-17 15:05:18 +01002576 if net.get("provider:network_type") == "vlan" and net.get(
2577 "provider:segmentation_id"
2578 ):
2579 usedVlanIDs.append(net.get("provider:segmentation_id"))
2580 elif net.get("segments"):
2581 for segment in net.get("segments"):
2582 if segment.get("provider:network_type") == "vlan" and segment.get(
2583 "provider:segmentation_id"
2584 ):
2585 usedVlanIDs.append(segment.get("provider:segmentation_id"))
2586
garciadeblasebd66722019-01-31 16:01:31 +00002587 used_vlanIDs = set(usedVlanIDs)
2588
tierno6869ae72020-01-09 17:37:34 +00002589 # find unused VLAN ID
sousaedu80135b92021-02-17 15:05:18 +01002590 for vlanID_range in self.config.get("multisegment_vlan_range"):
garciadeblasebd66722019-01-31 16:01:31 +00002591 try:
sousaedu80135b92021-02-17 15:05:18 +01002592 start_vlanid, end_vlanid = map(
2593 int, vlanID_range.replace(" ", "").split("-")
2594 )
2595
tierno7d782ef2019-10-04 12:56:31 +00002596 for vlanID in range(start_vlanid, end_vlanid + 1):
garciadeblasebd66722019-01-31 16:01:31 +00002597 if vlanID not in used_vlanIDs:
2598 return vlanID
2599 except Exception as exp:
sousaedu80135b92021-02-17 15:05:18 +01002600 raise vimconn.VimConnException(
2601 "Exception {} occurred while generating VLAN ID.".format(exp)
2602 )
garciadeblasebd66722019-01-31 16:01:31 +00002603 else:
tierno1ec592d2020-06-16 15:29:47 +00002604 raise vimconn.VimConnConflictException(
2605 "Unable to create the VLAN segment. All VLAN IDs {} are in use.".format(
sousaedu80135b92021-02-17 15:05:18 +01002606 self.config.get("multisegment_vlan_range")
2607 )
2608 )
garciadeblasebd66722019-01-31 16:01:31 +00002609
2610 def _validate_vlan_ranges(self, input_vlan_range, text_vlan_range):
kate721d79b2017-06-24 04:21:38 -07002611 """
2612 Method to validate user given vlanID ranges
2613 Args: None
2614 Returns: None
2615 """
garciadeblasebd66722019-01-31 16:01:31 +00002616 for vlanID_range in input_vlan_range:
kate721d79b2017-06-24 04:21:38 -07002617 vlan_range = vlanID_range.replace(" ", "")
tierno1ec592d2020-06-16 15:29:47 +00002618 # validate format
sousaedu80135b92021-02-17 15:05:18 +01002619 vlanID_pattern = r"(\d)*-(\d)*$"
kate721d79b2017-06-24 04:21:38 -07002620 match_obj = re.match(vlanID_pattern, vlan_range)
2621 if not match_obj:
tierno1ec592d2020-06-16 15:29:47 +00002622 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01002623 "Invalid VLAN range for {}: {}.You must provide "
2624 "'{}' in format [start_ID - end_ID].".format(
2625 text_vlan_range, vlanID_range, text_vlan_range
2626 )
2627 )
kate721d79b2017-06-24 04:21:38 -07002628
tierno1ec592d2020-06-16 15:29:47 +00002629 start_vlanid, end_vlanid = map(int, vlan_range.split("-"))
2630 if start_vlanid <= 0:
2631 raise vimconn.VimConnConflictException(
2632 "Invalid VLAN range for {}: {}. Start ID can not be zero. For VLAN "
sousaedu80135b92021-02-17 15:05:18 +01002633 "networks valid IDs are 1 to 4094 ".format(
2634 text_vlan_range, vlanID_range
2635 )
2636 )
2637
tierno1ec592d2020-06-16 15:29:47 +00002638 if end_vlanid > 4094:
2639 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01002640 "Invalid VLAN range for {}: {}. End VLAN ID can not be "
2641 "greater than 4094. For VLAN networks valid IDs are 1 to 4094 ".format(
2642 text_vlan_range, vlanID_range
2643 )
2644 )
kate721d79b2017-06-24 04:21:38 -07002645
2646 if start_vlanid > end_vlanid:
tierno1ec592d2020-06-16 15:29:47 +00002647 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01002648 "Invalid VLAN range for {}: {}. You must provide '{}'"
2649 " in format start_ID - end_ID and start_ID < end_ID ".format(
2650 text_vlan_range, vlanID_range, text_vlan_range
2651 )
2652 )
kate721d79b2017-06-24 04:21:38 -07002653
tierno1ec592d2020-06-16 15:29:47 +00002654 # NOT USED FUNCTIONS
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002655
tiernoae4a8d12016-07-08 12:30:39 +02002656 def new_external_port(self, port_data):
tierno1ec592d2020-06-16 15:29:47 +00002657 """Adds a external port to VIM
sousaedu80135b92021-02-17 15:05:18 +01002658 Returns the port identifier"""
tierno1ec592d2020-06-16 15:29:47 +00002659 # TODO openstack if needed
sousaedu80135b92021-02-17 15:05:18 +01002660 return (
2661 -vimconn.HTTP_Internal_Server_Error,
2662 "osconnector.new_external_port() not implemented",
2663 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002664
tiernoae4a8d12016-07-08 12:30:39 +02002665 def connect_port_network(self, port_id, network_id, admin=False):
tierno1ec592d2020-06-16 15:29:47 +00002666 """Connects a external port to a network
sousaedu80135b92021-02-17 15:05:18 +01002667 Returns status code of the VIM response"""
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.connect_port_network() not implemented",
2672 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002673
tiernoae4a8d12016-07-08 12:30:39 +02002674 def new_user(self, user_name, user_passwd, tenant_id=None):
tierno1ec592d2020-06-16 15:29:47 +00002675 """Adds a new user to openstack VIM
sousaedu80135b92021-02-17 15:05:18 +01002676 Returns the user identifier"""
tiernoae4a8d12016-07-08 12:30:39 +02002677 self.logger.debug("osconnector: Adding a new user to VIM")
sousaedu80135b92021-02-17 15:05:18 +01002678
tiernoae4a8d12016-07-08 12:30:39 +02002679 try:
2680 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +01002681 user = self.keystone.users.create(
2682 user_name, password=user_passwd, default_project=tenant_id
2683 )
tierno1ec592d2020-06-16 15:29:47 +00002684 # self.keystone.tenants.add_user(self.k_creds["username"], #role)
sousaedu80135b92021-02-17 15:05:18 +01002685
tiernoae4a8d12016-07-08 12:30:39 +02002686 return user.id
2687 except ksExceptions.ConnectionError as e:
tierno1ec592d2020-06-16 15:29:47 +00002688 error_value = -vimconn.HTTP_Bad_Request
sousaedu80135b92021-02-17 15:05:18 +01002689 error_text = (
2690 type(e).__name__
2691 + ": "
2692 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2693 )
tierno1ec592d2020-06-16 15:29:47 +00002694 except ksExceptions.ClientException as e: # TODO remove
2695 error_value = -vimconn.HTTP_Bad_Request
sousaedu80135b92021-02-17 15:05:18 +01002696 error_text = (
2697 type(e).__name__
2698 + ": "
2699 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2700 )
2701
tierno1ec592d2020-06-16 15:29:47 +00002702 # TODO insert exception vimconn.HTTP_Unauthorized
2703 # if reaching here is because an exception
tierno9c5c8322018-03-23 15:44:03 +01002704 self.logger.debug("new_user " + error_text)
sousaedu80135b92021-02-17 15:05:18 +01002705
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002706 return error_value, error_text
tiernoae4a8d12016-07-08 12:30:39 +02002707
2708 def delete_user(self, user_id):
tierno1ec592d2020-06-16 15:29:47 +00002709 """Delete a user from openstack VIM
sousaedu80135b92021-02-17 15:05:18 +01002710 Returns the user identifier"""
tiernoae4a8d12016-07-08 12:30:39 +02002711 if self.debug:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002712 print("osconnector: Deleting a user from VIM")
sousaedu80135b92021-02-17 15:05:18 +01002713
tiernoae4a8d12016-07-08 12:30:39 +02002714 try:
2715 self._reload_connection()
2716 self.keystone.users.delete(user_id)
sousaedu80135b92021-02-17 15:05:18 +01002717
tiernoae4a8d12016-07-08 12:30:39 +02002718 return 1, user_id
2719 except ksExceptions.ConnectionError as e:
tierno1ec592d2020-06-16 15:29:47 +00002720 error_value = -vimconn.HTTP_Bad_Request
sousaedu80135b92021-02-17 15:05:18 +01002721 error_text = (
2722 type(e).__name__
2723 + ": "
2724 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2725 )
tiernoae4a8d12016-07-08 12:30:39 +02002726 except ksExceptions.NotFound as e:
tierno1ec592d2020-06-16 15:29:47 +00002727 error_value = -vimconn.HTTP_Not_Found
sousaedu80135b92021-02-17 15:05:18 +01002728 error_text = (
2729 type(e).__name__
2730 + ": "
2731 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2732 )
tierno1ec592d2020-06-16 15:29:47 +00002733 except ksExceptions.ClientException as e: # TODO remove
2734 error_value = -vimconn.HTTP_Bad_Request
sousaedu80135b92021-02-17 15:05:18 +01002735 error_text = (
2736 type(e).__name__
2737 + ": "
2738 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2739 )
2740
tierno1ec592d2020-06-16 15:29:47 +00002741 # TODO insert exception vimconn.HTTP_Unauthorized
2742 # if reaching here is because an exception
2743 self.logger.debug("delete_tenant " + error_text)
sousaedu80135b92021-02-17 15:05:18 +01002744
tiernoae4a8d12016-07-08 12:30:39 +02002745 return error_value, error_text
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002746
tierno7edb6752016-03-21 17:37:52 +01002747 def get_hosts_info(self):
tierno1ec592d2020-06-16 15:29:47 +00002748 """Get the information of deployed hosts
2749 Returns the hosts content"""
tierno7edb6752016-03-21 17:37:52 +01002750 if self.debug:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002751 print("osconnector: Getting Host info from VIM")
sousaedu80135b92021-02-17 15:05:18 +01002752
tierno7edb6752016-03-21 17:37:52 +01002753 try:
tierno1ec592d2020-06-16 15:29:47 +00002754 h_list = []
tierno7edb6752016-03-21 17:37:52 +01002755 self._reload_connection()
2756 hypervisors = self.nova.hypervisors.list()
sousaedu80135b92021-02-17 15:05:18 +01002757
tierno7edb6752016-03-21 17:37:52 +01002758 for hype in hypervisors:
tierno1ec592d2020-06-16 15:29:47 +00002759 h_list.append(hype.to_dict())
sousaedu80135b92021-02-17 15:05:18 +01002760
tierno1ec592d2020-06-16 15:29:47 +00002761 return 1, {"hosts": h_list}
tierno7edb6752016-03-21 17:37:52 +01002762 except nvExceptions.NotFound as e:
tierno1ec592d2020-06-16 15:29:47 +00002763 error_value = -vimconn.HTTP_Not_Found
sousaedu80135b92021-02-17 15:05:18 +01002764 error_text = str(e) if len(e.args) == 0 else str(e.args[0])
tierno7edb6752016-03-21 17:37:52 +01002765 except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
tierno1ec592d2020-06-16 15:29:47 +00002766 error_value = -vimconn.HTTP_Bad_Request
sousaedu80135b92021-02-17 15:05:18 +01002767 error_text = (
2768 type(e).__name__
2769 + ": "
2770 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2771 )
2772
tierno1ec592d2020-06-16 15:29:47 +00002773 # TODO insert exception vimconn.HTTP_Unauthorized
2774 # if reaching here is because an exception
tierno9c5c8322018-03-23 15:44:03 +01002775 self.logger.debug("get_hosts_info " + error_text)
sousaedu80135b92021-02-17 15:05:18 +01002776
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002777 return error_value, error_text
tierno7edb6752016-03-21 17:37:52 +01002778
2779 def get_hosts(self, vim_tenant):
tierno1ec592d2020-06-16 15:29:47 +00002780 """Get the hosts and deployed instances
2781 Returns the hosts content"""
tierno7edb6752016-03-21 17:37:52 +01002782 r, hype_dict = self.get_hosts_info()
sousaedu80135b92021-02-17 15:05:18 +01002783
tierno1ec592d2020-06-16 15:29:47 +00002784 if r < 0:
tierno7edb6752016-03-21 17:37:52 +01002785 return r, hype_dict
sousaedu80135b92021-02-17 15:05:18 +01002786
tierno7edb6752016-03-21 17:37:52 +01002787 hypervisors = hype_dict["hosts"]
sousaedu80135b92021-02-17 15:05:18 +01002788
tierno7edb6752016-03-21 17:37:52 +01002789 try:
2790 servers = self.nova.servers.list()
2791 for hype in hypervisors:
2792 for server in servers:
sousaedu80135b92021-02-17 15:05:18 +01002793 if (
2794 server.to_dict()["OS-EXT-SRV-ATTR:hypervisor_hostname"]
2795 == hype["hypervisor_hostname"]
2796 ):
2797 if "vm" in hype:
2798 hype["vm"].append(server.id)
tierno7edb6752016-03-21 17:37:52 +01002799 else:
sousaedu80135b92021-02-17 15:05:18 +01002800 hype["vm"] = [server.id]
2801
tierno7edb6752016-03-21 17:37:52 +01002802 return 1, hype_dict
2803 except nvExceptions.NotFound as e:
tierno1ec592d2020-06-16 15:29:47 +00002804 error_value = -vimconn.HTTP_Not_Found
sousaedu80135b92021-02-17 15:05:18 +01002805 error_text = str(e) if len(e.args) == 0 else str(e.args[0])
tierno7edb6752016-03-21 17:37:52 +01002806 except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
tierno1ec592d2020-06-16 15:29:47 +00002807 error_value = -vimconn.HTTP_Bad_Request
sousaedu80135b92021-02-17 15:05:18 +01002808 error_text = (
2809 type(e).__name__
2810 + ": "
2811 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2812 )
2813
tierno1ec592d2020-06-16 15:29:47 +00002814 # TODO insert exception vimconn.HTTP_Unauthorized
2815 # if reaching here is because an exception
tierno9c5c8322018-03-23 15:44:03 +01002816 self.logger.debug("get_hosts " + error_text)
sousaedu80135b92021-02-17 15:05:18 +01002817
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002818 return error_value, error_text
tierno7edb6752016-03-21 17:37:52 +01002819
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002820 def new_classification(self, name, ctype, definition):
sousaedu80135b92021-02-17 15:05:18 +01002821 self.logger.debug(
2822 "Adding a new (Traffic) Classification to VIM, named %s", name
2823 )
2824
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002825 try:
2826 new_class = None
2827 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +01002828
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002829 if ctype not in supportedClassificationTypes:
tierno72774862020-05-04 11:44:15 +00002830 raise vimconn.VimConnNotSupportedException(
sousaedu80135b92021-02-17 15:05:18 +01002831 "OpenStack VIM connector does not support provided "
2832 "Classification Type {}, supported ones are: {}".format(
2833 ctype, supportedClassificationTypes
2834 )
2835 )
2836
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002837 if not self._validate_classification(ctype, definition):
tierno72774862020-05-04 11:44:15 +00002838 raise vimconn.VimConnException(
sousaedu80135b92021-02-17 15:05:18 +01002839 "Incorrect Classification definition for the type specified."
2840 )
tierno7edb6752016-03-21 17:37:52 +01002841
sousaedu80135b92021-02-17 15:05:18 +01002842 classification_dict = definition
2843 classification_dict["name"] = name
Igor D.Ccaadc442017-11-06 12:48:48 +00002844 new_class = self.neutron.create_sfc_flow_classifier(
sousaedu80135b92021-02-17 15:05:18 +01002845 {"flow_classifier": classification_dict}
2846 )
2847
2848 return new_class["flow_classifier"]["id"]
2849 except (
2850 neExceptions.ConnectionFailed,
2851 ksExceptions.ClientException,
2852 neExceptions.NeutronException,
2853 ConnectionError,
2854 ) as e:
2855 self.logger.error("Creation of Classification failed.")
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002856 self._format_exception(e)
2857
2858 def get_classification(self, class_id):
2859 self.logger.debug(" Getting Classification %s from VIM", class_id)
2860 filter_dict = {"id": class_id}
2861 class_list = self.get_classification_list(filter_dict)
sousaedu80135b92021-02-17 15:05:18 +01002862
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002863 if len(class_list) == 0:
tierno72774862020-05-04 11:44:15 +00002864 raise vimconn.VimConnNotFoundException(
sousaedu80135b92021-02-17 15:05:18 +01002865 "Classification '{}' not found".format(class_id)
2866 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002867 elif len(class_list) > 1:
tierno72774862020-05-04 11:44:15 +00002868 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01002869 "Found more than one Classification with this criteria"
2870 )
2871
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002872 classification = class_list[0]
sousaedu80135b92021-02-17 15:05:18 +01002873
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002874 return classification
2875
2876 def get_classification_list(self, filter_dict={}):
sousaedu80135b92021-02-17 15:05:18 +01002877 self.logger.debug(
2878 "Getting Classifications from VIM filter: '%s'", str(filter_dict)
2879 )
2880
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002881 try:
tierno69b590e2018-03-13 18:52:23 +01002882 filter_dict_os = filter_dict.copy()
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002883 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +01002884
tierno69b590e2018-03-13 18:52:23 +01002885 if self.api_version3 and "tenant_id" in filter_dict_os:
sousaedu80135b92021-02-17 15:05:18 +01002886 filter_dict_os["project_id"] = filter_dict_os.pop("tenant_id")
2887
Igor D.Ccaadc442017-11-06 12:48:48 +00002888 classification_dict = self.neutron.list_sfc_flow_classifiers(
sousaedu80135b92021-02-17 15:05:18 +01002889 **filter_dict_os
2890 )
tierno69b590e2018-03-13 18:52:23 +01002891 classification_list = classification_dict["flow_classifiers"]
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002892 self.__classification_os2mano(classification_list)
sousaedu80135b92021-02-17 15:05:18 +01002893
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002894 return classification_list
sousaedu80135b92021-02-17 15:05:18 +01002895 except (
2896 neExceptions.ConnectionFailed,
2897 ksExceptions.ClientException,
2898 neExceptions.NeutronException,
2899 ConnectionError,
2900 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002901 self._format_exception(e)
2902
2903 def delete_classification(self, class_id):
2904 self.logger.debug("Deleting Classification '%s' from VIM", class_id)
sousaedu80135b92021-02-17 15:05:18 +01002905
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002906 try:
2907 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00002908 self.neutron.delete_sfc_flow_classifier(class_id)
sousaedu80135b92021-02-17 15:05:18 +01002909
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002910 return class_id
sousaedu80135b92021-02-17 15:05:18 +01002911 except (
2912 neExceptions.ConnectionFailed,
2913 neExceptions.NeutronException,
2914 ksExceptions.ClientException,
2915 neExceptions.NeutronException,
2916 ConnectionError,
2917 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002918 self._format_exception(e)
2919
2920 def new_sfi(self, name, ingress_ports, egress_ports, sfc_encap=True):
sousaedu80135b92021-02-17 15:05:18 +01002921 self.logger.debug(
2922 "Adding a new Service Function Instance to VIM, named '%s'", name
2923 )
2924
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002925 try:
2926 new_sfi = None
2927 self._reload_connection()
2928 correlation = None
sousaedu80135b92021-02-17 15:05:18 +01002929
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002930 if sfc_encap:
sousaedu80135b92021-02-17 15:05:18 +01002931 correlation = "nsh"
2932
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002933 if len(ingress_ports) != 1:
tierno72774862020-05-04 11:44:15 +00002934 raise vimconn.VimConnNotSupportedException(
sousaedu80135b92021-02-17 15:05:18 +01002935 "OpenStack VIM connector can only have 1 ingress port per SFI"
2936 )
2937
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002938 if len(egress_ports) != 1:
tierno72774862020-05-04 11:44:15 +00002939 raise vimconn.VimConnNotSupportedException(
sousaedu80135b92021-02-17 15:05:18 +01002940 "OpenStack VIM connector can only have 1 egress port per SFI"
2941 )
2942
2943 sfi_dict = {
2944 "name": name,
2945 "ingress": ingress_ports[0],
2946 "egress": egress_ports[0],
2947 "service_function_parameters": {"correlation": correlation},
2948 }
2949 new_sfi = self.neutron.create_sfc_port_pair({"port_pair": sfi_dict})
2950
2951 return new_sfi["port_pair"]["id"]
2952 except (
2953 neExceptions.ConnectionFailed,
2954 ksExceptions.ClientException,
2955 neExceptions.NeutronException,
2956 ConnectionError,
2957 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002958 if new_sfi:
2959 try:
sousaedu80135b92021-02-17 15:05:18 +01002960 self.neutron.delete_sfc_port_pair(new_sfi["port_pair"]["id"])
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002961 except Exception:
2962 self.logger.error(
sousaedu80135b92021-02-17 15:05:18 +01002963 "Creation of Service Function Instance failed, with "
2964 "subsequent deletion failure as well."
2965 )
2966
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002967 self._format_exception(e)
2968
2969 def get_sfi(self, sfi_id):
sousaedu80135b92021-02-17 15:05:18 +01002970 self.logger.debug("Getting Service Function Instance %s from VIM", sfi_id)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002971 filter_dict = {"id": sfi_id}
2972 sfi_list = self.get_sfi_list(filter_dict)
sousaedu80135b92021-02-17 15:05:18 +01002973
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002974 if len(sfi_list) == 0:
sousaedu80135b92021-02-17 15:05:18 +01002975 raise vimconn.VimConnNotFoundException(
2976 "Service Function Instance '{}' not found".format(sfi_id)
2977 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002978 elif len(sfi_list) > 1:
tierno72774862020-05-04 11:44:15 +00002979 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01002980 "Found more than one Service Function Instance with this criteria"
2981 )
2982
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002983 sfi = sfi_list[0]
sousaedu80135b92021-02-17 15:05:18 +01002984
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002985 return sfi
2986
2987 def get_sfi_list(self, filter_dict={}):
sousaedu80135b92021-02-17 15:05:18 +01002988 self.logger.debug(
2989 "Getting Service Function Instances from VIM filter: '%s'", str(filter_dict)
2990 )
2991
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002992 try:
2993 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +01002994 filter_dict_os = filter_dict.copy()
sousaedu80135b92021-02-17 15:05:18 +01002995
tierno69b590e2018-03-13 18:52:23 +01002996 if self.api_version3 and "tenant_id" in filter_dict_os:
sousaedu80135b92021-02-17 15:05:18 +01002997 filter_dict_os["project_id"] = filter_dict_os.pop("tenant_id")
2998
tierno69b590e2018-03-13 18:52:23 +01002999 sfi_dict = self.neutron.list_sfc_port_pairs(**filter_dict_os)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003000 sfi_list = sfi_dict["port_pairs"]
3001 self.__sfi_os2mano(sfi_list)
sousaedu80135b92021-02-17 15:05:18 +01003002
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003003 return sfi_list
sousaedu80135b92021-02-17 15:05:18 +01003004 except (
3005 neExceptions.ConnectionFailed,
3006 ksExceptions.ClientException,
3007 neExceptions.NeutronException,
3008 ConnectionError,
3009 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003010 self._format_exception(e)
3011
3012 def delete_sfi(self, sfi_id):
sousaedu80135b92021-02-17 15:05:18 +01003013 self.logger.debug("Deleting Service Function Instance '%s' from VIM", sfi_id)
3014
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003015 try:
3016 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00003017 self.neutron.delete_sfc_port_pair(sfi_id)
sousaedu80135b92021-02-17 15:05:18 +01003018
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003019 return sfi_id
sousaedu80135b92021-02-17 15:05:18 +01003020 except (
3021 neExceptions.ConnectionFailed,
3022 neExceptions.NeutronException,
3023 ksExceptions.ClientException,
3024 neExceptions.NeutronException,
3025 ConnectionError,
3026 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003027 self._format_exception(e)
3028
3029 def new_sf(self, name, sfis, sfc_encap=True):
tierno7d782ef2019-10-04 12:56:31 +00003030 self.logger.debug("Adding a new Service Function to VIM, named '%s'", name)
sousaedu80135b92021-02-17 15:05:18 +01003031
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003032 try:
3033 new_sf = None
3034 self._reload_connection()
tierno9c5c8322018-03-23 15:44:03 +01003035 # correlation = None
3036 # if sfc_encap:
sousaedu80135b92021-02-17 15:05:18 +01003037 # correlation = "nsh"
3038
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003039 for instance in sfis:
3040 sfi = self.get_sfi(instance)
sousaedu80135b92021-02-17 15:05:18 +01003041
3042 if sfi.get("sfc_encap") != sfc_encap:
tierno72774862020-05-04 11:44:15 +00003043 raise vimconn.VimConnNotSupportedException(
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003044 "OpenStack VIM connector requires all SFIs of the "
sousaedu80135b92021-02-17 15:05:18 +01003045 "same SF to share the same SFC Encapsulation"
3046 )
3047
3048 sf_dict = {"name": name, "port_pairs": sfis}
3049 new_sf = self.neutron.create_sfc_port_pair_group(
3050 {"port_pair_group": sf_dict}
3051 )
3052
3053 return new_sf["port_pair_group"]["id"]
3054 except (
3055 neExceptions.ConnectionFailed,
3056 ksExceptions.ClientException,
3057 neExceptions.NeutronException,
3058 ConnectionError,
3059 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003060 if new_sf:
3061 try:
Igor D.Ccaadc442017-11-06 12:48:48 +00003062 self.neutron.delete_sfc_port_pair_group(
sousaedu80135b92021-02-17 15:05:18 +01003063 new_sf["port_pair_group"]["id"]
3064 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003065 except Exception:
3066 self.logger.error(
sousaedu80135b92021-02-17 15:05:18 +01003067 "Creation of Service Function failed, with "
3068 "subsequent deletion failure as well."
3069 )
3070
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003071 self._format_exception(e)
3072
3073 def get_sf(self, sf_id):
3074 self.logger.debug("Getting Service Function %s from VIM", sf_id)
3075 filter_dict = {"id": sf_id}
3076 sf_list = self.get_sf_list(filter_dict)
sousaedu80135b92021-02-17 15:05:18 +01003077
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003078 if len(sf_list) == 0:
tierno72774862020-05-04 11:44:15 +00003079 raise vimconn.VimConnNotFoundException(
sousaedu80135b92021-02-17 15:05:18 +01003080 "Service Function '{}' not found".format(sf_id)
3081 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003082 elif len(sf_list) > 1:
tierno72774862020-05-04 11:44:15 +00003083 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01003084 "Found more than one Service Function with this criteria"
3085 )
3086
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003087 sf = sf_list[0]
sousaedu80135b92021-02-17 15:05:18 +01003088
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003089 return sf
3090
3091 def get_sf_list(self, filter_dict={}):
sousaedu80135b92021-02-17 15:05:18 +01003092 self.logger.debug(
3093 "Getting Service Function from VIM filter: '%s'", str(filter_dict)
3094 )
3095
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003096 try:
3097 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +01003098 filter_dict_os = filter_dict.copy()
sousaedu80135b92021-02-17 15:05:18 +01003099
tierno69b590e2018-03-13 18:52:23 +01003100 if self.api_version3 and "tenant_id" in filter_dict_os:
sousaedu80135b92021-02-17 15:05:18 +01003101 filter_dict_os["project_id"] = filter_dict_os.pop("tenant_id")
3102
tierno69b590e2018-03-13 18:52:23 +01003103 sf_dict = self.neutron.list_sfc_port_pair_groups(**filter_dict_os)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003104 sf_list = sf_dict["port_pair_groups"]
3105 self.__sf_os2mano(sf_list)
sousaedu80135b92021-02-17 15:05:18 +01003106
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003107 return sf_list
sousaedu80135b92021-02-17 15:05:18 +01003108 except (
3109 neExceptions.ConnectionFailed,
3110 ksExceptions.ClientException,
3111 neExceptions.NeutronException,
3112 ConnectionError,
3113 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003114 self._format_exception(e)
3115
3116 def delete_sf(self, sf_id):
3117 self.logger.debug("Deleting Service Function '%s' from VIM", sf_id)
sousaedu80135b92021-02-17 15:05:18 +01003118
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003119 try:
3120 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00003121 self.neutron.delete_sfc_port_pair_group(sf_id)
sousaedu80135b92021-02-17 15:05:18 +01003122
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003123 return sf_id
sousaedu80135b92021-02-17 15:05:18 +01003124 except (
3125 neExceptions.ConnectionFailed,
3126 neExceptions.NeutronException,
3127 ksExceptions.ClientException,
3128 neExceptions.NeutronException,
3129 ConnectionError,
3130 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003131 self._format_exception(e)
3132
3133 def new_sfp(self, name, classifications, sfs, sfc_encap=True, spi=None):
tierno7d782ef2019-10-04 12:56:31 +00003134 self.logger.debug("Adding a new Service Function Path to VIM, named '%s'", name)
sousaedu80135b92021-02-17 15:05:18 +01003135
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003136 try:
3137 new_sfp = None
3138 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00003139 # In networking-sfc the MPLS encapsulation is legacy
3140 # should be used when no full SFC Encapsulation is intended
sousaedu80135b92021-02-17 15:05:18 +01003141 correlation = "mpls"
3142
Igor D.Ccaadc442017-11-06 12:48:48 +00003143 if sfc_encap:
sousaedu80135b92021-02-17 15:05:18 +01003144 correlation = "nsh"
3145
3146 sfp_dict = {
3147 "name": name,
3148 "flow_classifiers": classifications,
3149 "port_pair_groups": sfs,
3150 "chain_parameters": {"correlation": correlation},
3151 }
3152
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003153 if spi:
sousaedu80135b92021-02-17 15:05:18 +01003154 sfp_dict["chain_id"] = spi
3155
3156 new_sfp = self.neutron.create_sfc_port_chain({"port_chain": sfp_dict})
3157
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003158 return new_sfp["port_chain"]["id"]
sousaedu80135b92021-02-17 15:05:18 +01003159 except (
3160 neExceptions.ConnectionFailed,
3161 ksExceptions.ClientException,
3162 neExceptions.NeutronException,
3163 ConnectionError,
3164 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003165 if new_sfp:
3166 try:
sousaedu80135b92021-02-17 15:05:18 +01003167 self.neutron.delete_sfc_port_chain(new_sfp["port_chain"]["id"])
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003168 except Exception:
3169 self.logger.error(
sousaedu80135b92021-02-17 15:05:18 +01003170 "Creation of Service Function Path failed, with "
3171 "subsequent deletion failure as well."
3172 )
3173
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003174 self._format_exception(e)
3175
3176 def get_sfp(self, sfp_id):
3177 self.logger.debug(" Getting Service Function Path %s from VIM", sfp_id)
sousaedu80135b92021-02-17 15:05:18 +01003178
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003179 filter_dict = {"id": sfp_id}
3180 sfp_list = self.get_sfp_list(filter_dict)
sousaedu80135b92021-02-17 15:05:18 +01003181
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003182 if len(sfp_list) == 0:
tierno72774862020-05-04 11:44:15 +00003183 raise vimconn.VimConnNotFoundException(
sousaedu80135b92021-02-17 15:05:18 +01003184 "Service Function Path '{}' not found".format(sfp_id)
3185 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003186 elif len(sfp_list) > 1:
tierno72774862020-05-04 11:44:15 +00003187 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01003188 "Found more than one Service Function Path with this criteria"
3189 )
3190
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003191 sfp = sfp_list[0]
sousaedu80135b92021-02-17 15:05:18 +01003192
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003193 return sfp
3194
3195 def get_sfp_list(self, filter_dict={}):
sousaedu80135b92021-02-17 15:05:18 +01003196 self.logger.debug(
3197 "Getting Service Function Paths from VIM filter: '%s'", str(filter_dict)
3198 )
3199
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003200 try:
3201 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +01003202 filter_dict_os = filter_dict.copy()
sousaedu80135b92021-02-17 15:05:18 +01003203
tierno69b590e2018-03-13 18:52:23 +01003204 if self.api_version3 and "tenant_id" in filter_dict_os:
sousaedu80135b92021-02-17 15:05:18 +01003205 filter_dict_os["project_id"] = filter_dict_os.pop("tenant_id")
3206
tierno69b590e2018-03-13 18:52:23 +01003207 sfp_dict = self.neutron.list_sfc_port_chains(**filter_dict_os)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003208 sfp_list = sfp_dict["port_chains"]
3209 self.__sfp_os2mano(sfp_list)
sousaedu80135b92021-02-17 15:05:18 +01003210
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003211 return sfp_list
sousaedu80135b92021-02-17 15:05:18 +01003212 except (
3213 neExceptions.ConnectionFailed,
3214 ksExceptions.ClientException,
3215 neExceptions.NeutronException,
3216 ConnectionError,
3217 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003218 self._format_exception(e)
3219
3220 def delete_sfp(self, sfp_id):
tierno7d782ef2019-10-04 12:56:31 +00003221 self.logger.debug("Deleting Service Function Path '%s' from VIM", sfp_id)
sousaedu80135b92021-02-17 15:05:18 +01003222
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003223 try:
3224 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00003225 self.neutron.delete_sfc_port_chain(sfp_id)
sousaedu80135b92021-02-17 15:05:18 +01003226
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003227 return sfp_id
sousaedu80135b92021-02-17 15:05:18 +01003228 except (
3229 neExceptions.ConnectionFailed,
3230 neExceptions.NeutronException,
3231 ksExceptions.ClientException,
3232 neExceptions.NeutronException,
3233 ConnectionError,
3234 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003235 self._format_exception(e)
borsatti8a2dda32019-12-18 15:08:57 +00003236
borsatti8a2dda32019-12-18 15:08:57 +00003237 def refresh_sfps_status(self, sfp_list):
tierno1ec592d2020-06-16 15:29:47 +00003238 """Get the status of the service function path
sousaedu80135b92021-02-17 15:05:18 +01003239 Params: the list of sfp identifiers
3240 Returns a dictionary with:
3241 vm_id: #VIM id of this service function path
3242 status: #Mandatory. Text with one of:
3243 # DELETED (not found at vim)
3244 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
3245 # OTHER (Vim reported other status not understood)
3246 # ERROR (VIM indicates an ERROR status)
3247 # ACTIVE,
3248 # CREATING (on building process)
3249 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
3250 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)F
tierno1ec592d2020-06-16 15:29:47 +00003251 """
3252 sfp_dict = {}
sousaedu80135b92021-02-17 15:05:18 +01003253 self.logger.debug(
3254 "refresh_sfps status: Getting tenant SFP information from VIM"
3255 )
3256
borsatti8a2dda32019-12-18 15:08:57 +00003257 for sfp_id in sfp_list:
tierno1ec592d2020-06-16 15:29:47 +00003258 sfp = {}
sousaedu80135b92021-02-17 15:05:18 +01003259
borsatti8a2dda32019-12-18 15:08:57 +00003260 try:
3261 sfp_vim = self.get_sfp(sfp_id)
sousaedu80135b92021-02-17 15:05:18 +01003262
3263 if sfp_vim["spi"]:
3264 sfp["status"] = vmStatus2manoFormat["ACTIVE"]
borsatti8a2dda32019-12-18 15:08:57 +00003265 else:
sousaedu80135b92021-02-17 15:05:18 +01003266 sfp["status"] = "OTHER"
3267 sfp["error_msg"] = "VIM status reported " + sfp["status"]
borsatti8a2dda32019-12-18 15:08:57 +00003268
sousaedu80135b92021-02-17 15:05:18 +01003269 sfp["vim_info"] = self.serialize(sfp_vim)
borsatti8a2dda32019-12-18 15:08:57 +00003270
sousaedu80135b92021-02-17 15:05:18 +01003271 if sfp_vim.get("fault"):
3272 sfp["error_msg"] = str(sfp_vim["fault"])
tierno72774862020-05-04 11:44:15 +00003273 except vimconn.VimConnNotFoundException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003274 self.logger.error("Exception getting sfp status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003275 sfp["status"] = "DELETED"
3276 sfp["error_msg"] = str(e)
tierno72774862020-05-04 11:44:15 +00003277 except vimconn.VimConnException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003278 self.logger.error("Exception getting sfp status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003279 sfp["status"] = "VIM_ERROR"
3280 sfp["error_msg"] = str(e)
3281
borsatti8a2dda32019-12-18 15:08:57 +00003282 sfp_dict[sfp_id] = sfp
sousaedu80135b92021-02-17 15:05:18 +01003283
borsatti8a2dda32019-12-18 15:08:57 +00003284 return sfp_dict
3285
borsatti8a2dda32019-12-18 15:08:57 +00003286 def refresh_sfis_status(self, sfi_list):
tierno1ec592d2020-06-16 15:29:47 +00003287 """Get the status of the service function instances
sousaedu80135b92021-02-17 15:05:18 +01003288 Params: the list of sfi identifiers
3289 Returns a dictionary with:
3290 vm_id: #VIM id of this service function instance
3291 status: #Mandatory. Text with one of:
3292 # DELETED (not found at vim)
3293 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
3294 # OTHER (Vim reported other status not understood)
3295 # ERROR (VIM indicates an ERROR status)
3296 # ACTIVE,
3297 # CREATING (on building process)
3298 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
3299 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
tierno1ec592d2020-06-16 15:29:47 +00003300 """
3301 sfi_dict = {}
sousaedu80135b92021-02-17 15:05:18 +01003302 self.logger.debug(
3303 "refresh_sfis status: Getting tenant sfi information from VIM"
3304 )
3305
borsatti8a2dda32019-12-18 15:08:57 +00003306 for sfi_id in sfi_list:
tierno1ec592d2020-06-16 15:29:47 +00003307 sfi = {}
sousaedu80135b92021-02-17 15:05:18 +01003308
borsatti8a2dda32019-12-18 15:08:57 +00003309 try:
3310 sfi_vim = self.get_sfi(sfi_id)
sousaedu80135b92021-02-17 15:05:18 +01003311
borsatti8a2dda32019-12-18 15:08:57 +00003312 if sfi_vim:
sousaedu80135b92021-02-17 15:05:18 +01003313 sfi["status"] = vmStatus2manoFormat["ACTIVE"]
borsatti8a2dda32019-12-18 15:08:57 +00003314 else:
sousaedu80135b92021-02-17 15:05:18 +01003315 sfi["status"] = "OTHER"
3316 sfi["error_msg"] = "VIM status reported " + sfi["status"]
borsatti8a2dda32019-12-18 15:08:57 +00003317
sousaedu80135b92021-02-17 15:05:18 +01003318 sfi["vim_info"] = self.serialize(sfi_vim)
borsatti8a2dda32019-12-18 15:08:57 +00003319
sousaedu80135b92021-02-17 15:05:18 +01003320 if sfi_vim.get("fault"):
3321 sfi["error_msg"] = str(sfi_vim["fault"])
tierno72774862020-05-04 11:44:15 +00003322 except vimconn.VimConnNotFoundException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003323 self.logger.error("Exception getting sfi status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003324 sfi["status"] = "DELETED"
3325 sfi["error_msg"] = str(e)
tierno72774862020-05-04 11:44:15 +00003326 except vimconn.VimConnException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003327 self.logger.error("Exception getting sfi status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003328 sfi["status"] = "VIM_ERROR"
3329 sfi["error_msg"] = str(e)
3330
borsatti8a2dda32019-12-18 15:08:57 +00003331 sfi_dict[sfi_id] = sfi
sousaedu80135b92021-02-17 15:05:18 +01003332
borsatti8a2dda32019-12-18 15:08:57 +00003333 return sfi_dict
3334
borsatti8a2dda32019-12-18 15:08:57 +00003335 def refresh_sfs_status(self, sf_list):
tierno1ec592d2020-06-16 15:29:47 +00003336 """Get the status of the service functions
sousaedu80135b92021-02-17 15:05:18 +01003337 Params: the list of sf identifiers
3338 Returns a dictionary with:
3339 vm_id: #VIM id of this service function
3340 status: #Mandatory. Text with one of:
3341 # DELETED (not found at vim)
3342 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
3343 # OTHER (Vim reported other status not understood)
3344 # ERROR (VIM indicates an ERROR status)
3345 # ACTIVE,
3346 # CREATING (on building process)
3347 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
3348 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
tierno1ec592d2020-06-16 15:29:47 +00003349 """
3350 sf_dict = {}
borsatti8a2dda32019-12-18 15:08:57 +00003351 self.logger.debug("refresh_sfs status: Getting tenant sf information from VIM")
sousaedu80135b92021-02-17 15:05:18 +01003352
borsatti8a2dda32019-12-18 15:08:57 +00003353 for sf_id in sf_list:
tierno1ec592d2020-06-16 15:29:47 +00003354 sf = {}
sousaedu80135b92021-02-17 15:05:18 +01003355
borsatti8a2dda32019-12-18 15:08:57 +00003356 try:
3357 sf_vim = self.get_sf(sf_id)
sousaedu80135b92021-02-17 15:05:18 +01003358
borsatti8a2dda32019-12-18 15:08:57 +00003359 if sf_vim:
sousaedu80135b92021-02-17 15:05:18 +01003360 sf["status"] = vmStatus2manoFormat["ACTIVE"]
borsatti8a2dda32019-12-18 15:08:57 +00003361 else:
sousaedu80135b92021-02-17 15:05:18 +01003362 sf["status"] = "OTHER"
3363 sf["error_msg"] = "VIM status reported " + sf_vim["status"]
borsatti8a2dda32019-12-18 15:08:57 +00003364
sousaedu80135b92021-02-17 15:05:18 +01003365 sf["vim_info"] = self.serialize(sf_vim)
borsatti8a2dda32019-12-18 15:08:57 +00003366
sousaedu80135b92021-02-17 15:05:18 +01003367 if sf_vim.get("fault"):
3368 sf["error_msg"] = str(sf_vim["fault"])
tierno72774862020-05-04 11:44:15 +00003369 except vimconn.VimConnNotFoundException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003370 self.logger.error("Exception getting sf status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003371 sf["status"] = "DELETED"
3372 sf["error_msg"] = str(e)
tierno72774862020-05-04 11:44:15 +00003373 except vimconn.VimConnException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003374 self.logger.error("Exception getting sf status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003375 sf["status"] = "VIM_ERROR"
3376 sf["error_msg"] = str(e)
3377
borsatti8a2dda32019-12-18 15:08:57 +00003378 sf_dict[sf_id] = sf
sousaedu80135b92021-02-17 15:05:18 +01003379
borsatti8a2dda32019-12-18 15:08:57 +00003380 return sf_dict
3381
borsatti8a2dda32019-12-18 15:08:57 +00003382 def refresh_classifications_status(self, classification_list):
tierno1ec592d2020-06-16 15:29:47 +00003383 """Get the status of the classifications
sousaedu80135b92021-02-17 15:05:18 +01003384 Params: the list of classification identifiers
3385 Returns a dictionary with:
3386 vm_id: #VIM id of this classifier
3387 status: #Mandatory. Text with one of:
3388 # DELETED (not found at vim)
3389 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
3390 # OTHER (Vim reported other status not understood)
3391 # ERROR (VIM indicates an ERROR status)
3392 # ACTIVE,
3393 # CREATING (on building process)
3394 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
3395 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
tierno1ec592d2020-06-16 15:29:47 +00003396 """
3397 classification_dict = {}
sousaedu80135b92021-02-17 15:05:18 +01003398 self.logger.debug(
3399 "refresh_classifications status: Getting tenant classification information from VIM"
3400 )
3401
borsatti8a2dda32019-12-18 15:08:57 +00003402 for classification_id in classification_list:
tierno1ec592d2020-06-16 15:29:47 +00003403 classification = {}
sousaedu80135b92021-02-17 15:05:18 +01003404
borsatti8a2dda32019-12-18 15:08:57 +00003405 try:
3406 classification_vim = self.get_classification(classification_id)
sousaedu80135b92021-02-17 15:05:18 +01003407
borsatti8a2dda32019-12-18 15:08:57 +00003408 if classification_vim:
sousaedu80135b92021-02-17 15:05:18 +01003409 classification["status"] = vmStatus2manoFormat["ACTIVE"]
borsatti8a2dda32019-12-18 15:08:57 +00003410 else:
sousaedu80135b92021-02-17 15:05:18 +01003411 classification["status"] = "OTHER"
3412 classification["error_msg"] = (
3413 "VIM status reported " + classification["status"]
3414 )
borsatti8a2dda32019-12-18 15:08:57 +00003415
sousaedu80135b92021-02-17 15:05:18 +01003416 classification["vim_info"] = self.serialize(classification_vim)
borsatti8a2dda32019-12-18 15:08:57 +00003417
sousaedu80135b92021-02-17 15:05:18 +01003418 if classification_vim.get("fault"):
3419 classification["error_msg"] = str(classification_vim["fault"])
tierno72774862020-05-04 11:44:15 +00003420 except vimconn.VimConnNotFoundException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003421 self.logger.error("Exception getting classification status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003422 classification["status"] = "DELETED"
3423 classification["error_msg"] = str(e)
tierno72774862020-05-04 11:44:15 +00003424 except vimconn.VimConnException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003425 self.logger.error("Exception getting classification status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003426 classification["status"] = "VIM_ERROR"
3427 classification["error_msg"] = str(e)
3428
borsatti8a2dda32019-12-18 15:08:57 +00003429 classification_dict[classification_id] = classification
sousaedu80135b92021-02-17 15:05:18 +01003430
borsatti8a2dda32019-12-18 15:08:57 +00003431 return classification_dict