blob: 4869da163bb1868d6a4eb158cf878d1d9df5fbad [file] [log] [blame]
tierno7edb6752016-03-21 17:37:52 +01001# -*- coding: utf-8 -*-
2
3##
tierno92021022018-09-12 16:29:23 +02004# Copyright 2015 Telefonica Investigacion y Desarrollo, S.A.U.
tierno7edb6752016-03-21 17:37:52 +01005# This file is part of openmano
6# All Rights Reserved.
7#
8# Licensed under the Apache License, Version 2.0 (the "License"); you may
9# not use this file except in compliance with the License. You may obtain
10# a copy of the License at
11#
12# http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17# License for the specific language governing permissions and limitations
18# under the License.
tierno7edb6752016-03-21 17:37:52 +010019##
20
tierno1ec592d2020-06-16 15:29:47 +000021"""
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +000022osconnector implements all the methods to interact with openstack using the python-neutronclient.
23
24For the VNF forwarding graph, The OpenStack VIM connector calls the
25networking-sfc Neutron extension methods, whose resources are mapped
26to the VIM connector's SFC resources as follows:
27- Classification (OSM) -> Flow Classifier (Neutron)
28- Service Function Instance (OSM) -> Port Pair (Neutron)
29- Service Function (OSM) -> Port Pair Group (Neutron)
30- Service Function Path (OSM) -> Port Chain (Neutron)
tierno1ec592d2020-06-16 15:29:47 +000031"""
tierno7edb6752016-03-21 17:37:52 +010032
tierno72774862020-05-04 11:44:15 +000033from osm_ro_plugin import vimconn
sousaedu80135b92021-02-17 15:05:18 +010034
tierno69b590e2018-03-13 18:52:23 +010035# import json
tiernoae4a8d12016-07-08 12:30:39 +020036import logging
garciadeblas9f8456e2016-09-05 05:02:59 +020037import netaddr
montesmoreno0c8def02016-12-22 12:16:23 +000038import time
tierno36c0b172017-01-12 18:32:28 +010039import yaml
garciadeblas2299e3b2017-01-26 14:35:55 +000040import random
kate721d79b2017-06-24 04:21:38 -070041import re
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +000042import copy
Anderson Bravalheri0446cd52018-08-17 15:26:19 +010043from pprint import pformat
tiernob5cef372017-06-19 15:52:22 +020044from novaclient import client as nClient, exceptions as nvExceptions
45from keystoneauth1.identity import v2, v3
46from keystoneauth1 import session
tierno7edb6752016-03-21 17:37:52 +010047import keystoneclient.exceptions as ksExceptions
tiernof716aea2017-06-21 18:01:40 +020048import keystoneclient.v3.client as ksClient_v3
49import keystoneclient.v2_0.client as ksClient_v2
tiernob5cef372017-06-19 15:52:22 +020050from glanceclient import client as glClient
tierno7edb6752016-03-21 17:37:52 +010051import glanceclient.exc as gl1Exceptions
tierno1ec592d2020-06-16 15:29:47 +000052from cinderclient import client as cClient
sousaedu80135b92021-02-17 15:05:18 +010053
54# TODO py3 check that this base exception matches python2 httplib.HTTPException
55from http.client import HTTPException
tiernob5cef372017-06-19 15:52:22 +020056from neutronclient.neutron import client as neClient
tierno7edb6752016-03-21 17:37:52 +010057from neutronclient.common import exceptions as neExceptions
58from requests.exceptions import ConnectionError
59
tierno1ec592d2020-06-16 15:29:47 +000060__author__ = "Alfonso Tierno, Gerardo Garcia, Pablo Montes, xFlow Research, Igor D.C., Eduardo Sousa"
61__date__ = "$22-sep-2017 23:59:59$"
tierno40e1bce2017-08-09 09:12:04 +020062
63"""contain the openstack virtual machine status to openmano status"""
sousaedu80135b92021-02-17 15:05:18 +010064vmStatus2manoFormat = {
65 "ACTIVE": "ACTIVE",
66 "PAUSED": "PAUSED",
67 "SUSPENDED": "SUSPENDED",
68 "SHUTOFF": "INACTIVE",
69 "BUILD": "BUILD",
70 "ERROR": "ERROR",
71 "DELETED": "DELETED",
72}
73netStatus2manoFormat = {
74 "ACTIVE": "ACTIVE",
75 "PAUSED": "PAUSED",
76 "INACTIVE": "INACTIVE",
77 "BUILD": "BUILD",
78 "ERROR": "ERROR",
79 "DELETED": "DELETED",
80}
tierno7edb6752016-03-21 17:37:52 +010081
sousaedu80135b92021-02-17 15:05:18 +010082supportedClassificationTypes = ["legacy_flow_classifier"]
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +000083
tierno1ec592d2020-06-16 15:29:47 +000084# global var to have a timeout creating and deleting volumes
garciadeblas64b39c52020-05-21 08:07:25 +000085volume_timeout = 1800
86server_timeout = 1800
montesmoreno0c8def02016-12-22 12:16:23 +000087
Anderson Bravalheri0446cd52018-08-17 15:26:19 +010088
89class SafeDumper(yaml.SafeDumper):
90 def represent_data(self, data):
91 # Openstack APIs use custom subclasses of dict and YAML safe dumper
92 # is designed to not handle that (reference issue 142 of pyyaml)
93 if isinstance(data, dict) and data.__class__ != dict:
94 # A simple solution is to convert those items back to dicts
95 data = dict(data.items())
96
97 return super(SafeDumper, self).represent_data(data)
98
99
tierno72774862020-05-04 11:44:15 +0000100class vimconnector(vimconn.VimConnector):
sousaedu80135b92021-02-17 15:05:18 +0100101 def __init__(
102 self,
103 uuid,
104 name,
105 tenant_id,
106 tenant_name,
107 url,
108 url_admin=None,
109 user=None,
110 passwd=None,
111 log_level=None,
112 config={},
113 persistent_info={},
114 ):
tierno1ec592d2020-06-16 15:29:47 +0000115 """using common constructor parameters. In this case
tierno7edb6752016-03-21 17:37:52 +0100116 'url' is the keystone authorization url,
117 'url_admin' is not use
tierno1ec592d2020-06-16 15:29:47 +0000118 """
sousaedu80135b92021-02-17 15:05:18 +0100119 api_version = config.get("APIversion")
kate721d79b2017-06-24 04:21:38 -0700120
sousaedu80135b92021-02-17 15:05:18 +0100121 if api_version and api_version not in ("v3.3", "v2.0", "2", "3"):
122 raise vimconn.VimConnException(
123 "Invalid value '{}' for config:APIversion. "
124 "Allowed values are 'v3.3', 'v2.0', '2' or '3'".format(api_version)
125 )
126
127 vim_type = config.get("vim_type")
128
129 if vim_type and vim_type not in ("vio", "VIO"):
130 raise vimconn.VimConnException(
131 "Invalid value '{}' for config:vim_type."
132 "Allowed values are 'vio' or 'VIO'".format(vim_type)
133 )
134
135 if config.get("dataplane_net_vlan_range") is not None:
tierno1ec592d2020-06-16 15:29:47 +0000136 # validate vlan ranges provided by user
sousaedu80135b92021-02-17 15:05:18 +0100137 self._validate_vlan_ranges(
138 config.get("dataplane_net_vlan_range"), "dataplane_net_vlan_range"
139 )
garciadeblasebd66722019-01-31 16:01:31 +0000140
sousaedu80135b92021-02-17 15:05:18 +0100141 if config.get("multisegment_vlan_range") is not None:
tierno1ec592d2020-06-16 15:29:47 +0000142 # validate vlan ranges provided by user
sousaedu80135b92021-02-17 15:05:18 +0100143 self._validate_vlan_ranges(
144 config.get("multisegment_vlan_range"), "multisegment_vlan_range"
145 )
kate721d79b2017-06-24 04:21:38 -0700146
sousaedu80135b92021-02-17 15:05:18 +0100147 vimconn.VimConnector.__init__(
148 self,
149 uuid,
150 name,
151 tenant_id,
152 tenant_name,
153 url,
154 url_admin,
155 user,
156 passwd,
157 log_level,
158 config,
159 )
tiernob3d36742017-03-03 23:51:05 +0100160
tierno4d1ce222018-04-06 10:41:06 +0200161 if self.config.get("insecure") and self.config.get("ca_cert"):
sousaedu80135b92021-02-17 15:05:18 +0100162 raise vimconn.VimConnException(
163 "options insecure and ca_cert are mutually exclusive"
164 )
165
tierno4d1ce222018-04-06 10:41:06 +0200166 self.verify = True
sousaedu80135b92021-02-17 15:05:18 +0100167
tierno4d1ce222018-04-06 10:41:06 +0200168 if self.config.get("insecure"):
169 self.verify = False
sousaedu80135b92021-02-17 15:05:18 +0100170
tierno4d1ce222018-04-06 10:41:06 +0200171 if self.config.get("ca_cert"):
172 self.verify = self.config.get("ca_cert")
tierno4d1ce222018-04-06 10:41:06 +0200173
tierno7edb6752016-03-21 17:37:52 +0100174 if not url:
sousaedu80135b92021-02-17 15:05:18 +0100175 raise TypeError("url param can not be NoneType")
176
tiernob5cef372017-06-19 15:52:22 +0200177 self.persistent_info = persistent_info
sousaedu80135b92021-02-17 15:05:18 +0100178 self.availability_zone = persistent_info.get("availability_zone", None)
179 self.session = persistent_info.get("session", {"reload_client": True})
180 self.my_tenant_id = self.session.get("my_tenant_id")
181 self.nova = self.session.get("nova")
182 self.neutron = self.session.get("neutron")
183 self.cinder = self.session.get("cinder")
184 self.glance = self.session.get("glance")
185 # self.glancev1 = self.session.get("glancev1")
186 self.keystone = self.session.get("keystone")
187 self.api_version3 = self.session.get("api_version3")
kate721d79b2017-06-24 04:21:38 -0700188 self.vim_type = self.config.get("vim_type")
sousaedu80135b92021-02-17 15:05:18 +0100189
kate721d79b2017-06-24 04:21:38 -0700190 if self.vim_type:
191 self.vim_type = self.vim_type.upper()
sousaedu80135b92021-02-17 15:05:18 +0100192
kate721d79b2017-06-24 04:21:38 -0700193 if self.config.get("use_internal_endpoint"):
194 self.endpoint_type = "internalURL"
195 else:
196 self.endpoint_type = None
montesmoreno0c8def02016-12-22 12:16:23 +0000197
sousaedu80135b92021-02-17 15:05:18 +0100198 logging.getLogger("urllib3").setLevel(logging.WARNING)
199 logging.getLogger("keystoneauth").setLevel(logging.WARNING)
200 logging.getLogger("novaclient").setLevel(logging.WARNING)
201 self.logger = logging.getLogger("ro.vim.openstack")
kate721d79b2017-06-24 04:21:38 -0700202
tiernoa05b65a2019-02-01 12:30:27 +0000203 # allow security_groups to be a list or a single string
sousaedu80135b92021-02-17 15:05:18 +0100204 if isinstance(self.config.get("security_groups"), str):
205 self.config["security_groups"] = [self.config["security_groups"]]
206
tiernoa05b65a2019-02-01 12:30:27 +0000207 self.security_groups_id = None
208
tierno1ec592d2020-06-16 15:29:47 +0000209 # ###### VIO Specific Changes #########
kate721d79b2017-06-24 04:21:38 -0700210 if self.vim_type == "VIO":
sousaedu80135b92021-02-17 15:05:18 +0100211 self.logger = logging.getLogger("ro.vim.vio")
kate721d79b2017-06-24 04:21:38 -0700212
tiernofe789902016-09-29 14:20:44 +0000213 if log_level:
tierno1ec592d2020-06-16 15:29:47 +0000214 self.logger.setLevel(getattr(logging, log_level))
tiernof716aea2017-06-21 18:01:40 +0200215
216 def __getitem__(self, index):
217 """Get individuals parameters.
218 Throw KeyError"""
sousaedu80135b92021-02-17 15:05:18 +0100219 if index == "project_domain_id":
tiernof716aea2017-06-21 18:01:40 +0200220 return self.config.get("project_domain_id")
sousaedu80135b92021-02-17 15:05:18 +0100221 elif index == "user_domain_id":
tiernof716aea2017-06-21 18:01:40 +0200222 return self.config.get("user_domain_id")
223 else:
tierno72774862020-05-04 11:44:15 +0000224 return vimconn.VimConnector.__getitem__(self, index)
tiernof716aea2017-06-21 18:01:40 +0200225
226 def __setitem__(self, index, value):
227 """Set individuals parameters and it is marked as dirty so to force connection reload.
228 Throw KeyError"""
sousaedu80135b92021-02-17 15:05:18 +0100229 if index == "project_domain_id":
tiernof716aea2017-06-21 18:01:40 +0200230 self.config["project_domain_id"] = value
sousaedu80135b92021-02-17 15:05:18 +0100231 elif index == "user_domain_id":
tierno1ec592d2020-06-16 15:29:47 +0000232 self.config["user_domain_id"] = value
tiernof716aea2017-06-21 18:01:40 +0200233 else:
tierno72774862020-05-04 11:44:15 +0000234 vimconn.VimConnector.__setitem__(self, index, value)
sousaedu80135b92021-02-17 15:05:18 +0100235
236 self.session["reload_client"] = True
tiernof716aea2017-06-21 18:01:40 +0200237
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100238 def serialize(self, value):
239 """Serialization of python basic types.
240
241 In the case value is not serializable a message will be logged and a
242 simple representation of the data that cannot be converted back to
243 python is returned.
244 """
tierno7d782ef2019-10-04 12:56:31 +0000245 if isinstance(value, str):
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100246 return value
247
248 try:
sousaedu80135b92021-02-17 15:05:18 +0100249 return yaml.dump(
250 value, Dumper=SafeDumper, default_flow_style=True, width=256
251 )
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100252 except yaml.representer.RepresenterError:
sousaedu80135b92021-02-17 15:05:18 +0100253 self.logger.debug(
254 "The following entity cannot be serialized in YAML:\n\n%s\n\n",
255 pformat(value),
256 exc_info=True,
257 )
258
tierno1ec592d2020-06-16 15:29:47 +0000259 return str(value)
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100260
tierno7edb6752016-03-21 17:37:52 +0100261 def _reload_connection(self):
tierno1ec592d2020-06-16 15:29:47 +0000262 """Called before any operation, it check if credentials has changed
tierno7edb6752016-03-21 17:37:52 +0100263 Throw keystoneclient.apiclient.exceptions.AuthorizationFailure
tierno1ec592d2020-06-16 15:29:47 +0000264 """
265 # TODO control the timing and possible token timeout, but it seams that python client does this task for us :-)
sousaedu80135b92021-02-17 15:05:18 +0100266 if self.session["reload_client"]:
267 if self.config.get("APIversion"):
268 self.api_version3 = (
269 self.config["APIversion"] == "v3.3"
270 or self.config["APIversion"] == "3"
271 )
tiernof716aea2017-06-21 18:01:40 +0200272 else: # get from ending auth_url that end with v3 or with v2.0
sousaedu80135b92021-02-17 15:05:18 +0100273 self.api_version3 = self.url.endswith("/v3") or self.url.endswith(
274 "/v3/"
275 )
276
277 self.session["api_version3"] = self.api_version3
278
tiernof716aea2017-06-21 18:01:40 +0200279 if self.api_version3:
sousaedu80135b92021-02-17 15:05:18 +0100280 if self.config.get("project_domain_id") or self.config.get(
281 "project_domain_name"
282 ):
tierno3cb8dc32017-10-24 18:13:19 +0200283 project_domain_id_default = None
284 else:
sousaedu80135b92021-02-17 15:05:18 +0100285 project_domain_id_default = "default"
286
287 if self.config.get("user_domain_id") or self.config.get(
288 "user_domain_name"
289 ):
tierno3cb8dc32017-10-24 18:13:19 +0200290 user_domain_id_default = None
291 else:
sousaedu80135b92021-02-17 15:05:18 +0100292 user_domain_id_default = "default"
293 auth = v3.Password(
294 auth_url=self.url,
295 username=self.user,
296 password=self.passwd,
297 project_name=self.tenant_name,
298 project_id=self.tenant_id,
299 project_domain_id=self.config.get(
300 "project_domain_id", project_domain_id_default
301 ),
302 user_domain_id=self.config.get(
303 "user_domain_id", user_domain_id_default
304 ),
305 project_domain_name=self.config.get("project_domain_name"),
306 user_domain_name=self.config.get("user_domain_name"),
307 )
ahmadsa95baa272016-11-30 09:14:11 +0500308 else:
sousaedu80135b92021-02-17 15:05:18 +0100309 auth = v2.Password(
310 auth_url=self.url,
311 username=self.user,
312 password=self.passwd,
313 tenant_name=self.tenant_name,
314 tenant_id=self.tenant_id,
315 )
316
tierno4d1ce222018-04-06 10:41:06 +0200317 sess = session.Session(auth=auth, verify=self.verify)
tierno1ec592d2020-06-16 15:29:47 +0000318 # addedd region_name to keystone, nova, neutron and cinder to support distributed cloud for Wind River
319 # Titanium cloud and StarlingX
sousaedu80135b92021-02-17 15:05:18 +0100320 region_name = self.config.get("region_name")
321
tiernof716aea2017-06-21 18:01:40 +0200322 if self.api_version3:
sousaedu80135b92021-02-17 15:05:18 +0100323 self.keystone = ksClient_v3.Client(
324 session=sess,
325 endpoint_type=self.endpoint_type,
326 region_name=region_name,
327 )
tiernof716aea2017-06-21 18:01:40 +0200328 else:
sousaedu80135b92021-02-17 15:05:18 +0100329 self.keystone = ksClient_v2.Client(
330 session=sess, endpoint_type=self.endpoint_type
331 )
332
333 self.session["keystone"] = self.keystone
334 # In order to enable microversion functionality an explicit microversion must be specified in "config".
montesmoreno9317d302017-08-16 12:48:23 +0200335 # This implementation approach is due to the warning message in
336 # https://developer.openstack.org/api-guide/compute/microversions.html
337 # where it is stated that microversion backwards compatibility is not guaranteed and clients should
338 # always require an specific microversion.
sousaedu80135b92021-02-17 15:05:18 +0100339 # To be able to use "device role tagging" functionality define "microversion: 2.32" in datacenter config
montesmoreno9317d302017-08-16 12:48:23 +0200340 version = self.config.get("microversion")
sousaedu80135b92021-02-17 15:05:18 +0100341
montesmoreno9317d302017-08-16 12:48:23 +0200342 if not version:
343 version = "2.1"
sousaedu80135b92021-02-17 15:05:18 +0100344
tierno1ec592d2020-06-16 15:29:47 +0000345 # addedd region_name to keystone, nova, neutron and cinder to support distributed cloud for Wind River
346 # Titanium cloud and StarlingX
sousaedu80135b92021-02-17 15:05:18 +0100347 self.nova = self.session["nova"] = nClient.Client(
348 str(version),
349 session=sess,
350 endpoint_type=self.endpoint_type,
351 region_name=region_name,
352 )
353 self.neutron = self.session["neutron"] = neClient.Client(
354 "2.0",
355 session=sess,
356 endpoint_type=self.endpoint_type,
357 region_name=region_name,
358 )
359 self.cinder = self.session["cinder"] = cClient.Client(
360 2,
361 session=sess,
362 endpoint_type=self.endpoint_type,
363 region_name=region_name,
364 )
365
tiernoa05b65a2019-02-01 12:30:27 +0000366 try:
sousaedu80135b92021-02-17 15:05:18 +0100367 self.my_tenant_id = self.session["my_tenant_id"] = sess.get_project_id()
tierno1ec592d2020-06-16 15:29:47 +0000368 except Exception:
tiernoa05b65a2019-02-01 12:30:27 +0000369 self.logger.error("Cannot get project_id from session", exc_info=True)
sousaedu80135b92021-02-17 15:05:18 +0100370
kate721d79b2017-06-24 04:21:38 -0700371 if self.endpoint_type == "internalURL":
372 glance_service_id = self.keystone.services.list(name="glance")[0].id
sousaedu80135b92021-02-17 15:05:18 +0100373 glance_endpoint = self.keystone.endpoints.list(
374 glance_service_id, interface="internal"
375 )[0].url
kate721d79b2017-06-24 04:21:38 -0700376 else:
377 glance_endpoint = None
sousaedu80135b92021-02-17 15:05:18 +0100378
379 self.glance = self.session["glance"] = glClient.Client(
380 2, session=sess, endpoint=glance_endpoint
381 )
tiernoa05b65a2019-02-01 12:30:27 +0000382 # using version 1 of glance client in new_image()
sousaedu80135b92021-02-17 15:05:18 +0100383 # self.glancev1 = self.session["glancev1"] = glClient.Client("1", session=sess,
tierno1beea862018-07-11 15:47:37 +0200384 # endpoint=glance_endpoint)
sousaedu80135b92021-02-17 15:05:18 +0100385 self.session["reload_client"] = False
386 self.persistent_info["session"] = self.session
mirabal29356312017-07-27 12:21:22 +0200387 # add availablity zone info inside self.persistent_info
388 self._set_availablity_zones()
sousaedu80135b92021-02-17 15:05:18 +0100389 self.persistent_info["availability_zone"] = self.availability_zone
390 # force to get again security_groups_ids next time they are needed
391 self.security_groups_id = None
ahmadsa95baa272016-11-30 09:14:11 +0500392
tierno7edb6752016-03-21 17:37:52 +0100393 def __net_os2mano(self, net_list_dict):
tierno1ec592d2020-06-16 15:29:47 +0000394 """Transform the net openstack format to mano format
395 net_list_dict can be a list of dict or a single dict"""
tierno7edb6752016-03-21 17:37:52 +0100396 if type(net_list_dict) is dict:
tierno1ec592d2020-06-16 15:29:47 +0000397 net_list_ = (net_list_dict,)
tierno7edb6752016-03-21 17:37:52 +0100398 elif type(net_list_dict) is list:
tierno1ec592d2020-06-16 15:29:47 +0000399 net_list_ = net_list_dict
tierno7edb6752016-03-21 17:37:52 +0100400 else:
401 raise TypeError("param net_list_dict must be a list or a dictionary")
402 for net in net_list_:
sousaedu80135b92021-02-17 15:05:18 +0100403 if net.get("provider:network_type") == "vlan":
404 net["type"] = "data"
tierno7edb6752016-03-21 17:37:52 +0100405 else:
sousaedu80135b92021-02-17 15:05:18 +0100406 net["type"] = "bridge"
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +0200407
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000408 def __classification_os2mano(self, class_list_dict):
409 """Transform the openstack format (Flow Classifier) to mano format
410 (Classification) class_list_dict can be a list of dict or a single dict
411 """
412 if isinstance(class_list_dict, dict):
413 class_list_ = [class_list_dict]
414 elif isinstance(class_list_dict, list):
415 class_list_ = class_list_dict
416 else:
tierno1ec592d2020-06-16 15:29:47 +0000417 raise TypeError("param class_list_dict must be a list or a dictionary")
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000418 for classification in class_list_:
sousaedu80135b92021-02-17 15:05:18 +0100419 id = classification.pop("id")
420 name = classification.pop("name")
421 description = classification.pop("description")
422 project_id = classification.pop("project_id")
423 tenant_id = classification.pop("tenant_id")
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000424 original_classification = copy.deepcopy(classification)
425 classification.clear()
sousaedu80135b92021-02-17 15:05:18 +0100426 classification["ctype"] = "legacy_flow_classifier"
427 classification["definition"] = original_classification
428 classification["id"] = id
429 classification["name"] = name
430 classification["description"] = description
431 classification["project_id"] = project_id
432 classification["tenant_id"] = tenant_id
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000433
434 def __sfi_os2mano(self, sfi_list_dict):
435 """Transform the openstack format (Port Pair) to mano format (SFI)
436 sfi_list_dict can be a list of dict or a single dict
437 """
438 if isinstance(sfi_list_dict, dict):
439 sfi_list_ = [sfi_list_dict]
440 elif isinstance(sfi_list_dict, list):
441 sfi_list_ = sfi_list_dict
442 else:
sousaedu80135b92021-02-17 15:05:18 +0100443 raise TypeError("param sfi_list_dict must be a list or a dictionary")
444
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000445 for sfi in sfi_list_:
sousaedu80135b92021-02-17 15:05:18 +0100446 sfi["ingress_ports"] = []
447 sfi["egress_ports"] = []
448
449 if sfi.get("ingress"):
450 sfi["ingress_ports"].append(sfi["ingress"])
451
452 if sfi.get("egress"):
453 sfi["egress_ports"].append(sfi["egress"])
454
455 del sfi["ingress"]
456 del sfi["egress"]
457 params = sfi.get("service_function_parameters")
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000458 sfc_encap = False
sousaedu80135b92021-02-17 15:05:18 +0100459
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000460 if params:
sousaedu80135b92021-02-17 15:05:18 +0100461 correlation = params.get("correlation")
462
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000463 if correlation:
464 sfc_encap = True
sousaedu80135b92021-02-17 15:05:18 +0100465
466 sfi["sfc_encap"] = sfc_encap
467 del sfi["service_function_parameters"]
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000468
469 def __sf_os2mano(self, sf_list_dict):
470 """Transform the openstack format (Port Pair Group) to mano format (SF)
471 sf_list_dict can be a list of dict or a single dict
472 """
473 if isinstance(sf_list_dict, dict):
474 sf_list_ = [sf_list_dict]
475 elif isinstance(sf_list_dict, list):
476 sf_list_ = sf_list_dict
477 else:
sousaedu80135b92021-02-17 15:05:18 +0100478 raise TypeError("param sf_list_dict must be a list or a dictionary")
479
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000480 for sf in sf_list_:
sousaedu80135b92021-02-17 15:05:18 +0100481 del sf["port_pair_group_parameters"]
482 sf["sfis"] = sf["port_pairs"]
483 del sf["port_pairs"]
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000484
485 def __sfp_os2mano(self, sfp_list_dict):
486 """Transform the openstack format (Port Chain) to mano format (SFP)
487 sfp_list_dict can be a list of dict or a single dict
488 """
489 if isinstance(sfp_list_dict, dict):
490 sfp_list_ = [sfp_list_dict]
491 elif isinstance(sfp_list_dict, list):
492 sfp_list_ = sfp_list_dict
493 else:
sousaedu80135b92021-02-17 15:05:18 +0100494 raise TypeError("param sfp_list_dict must be a list or a dictionary")
495
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000496 for sfp in sfp_list_:
sousaedu80135b92021-02-17 15:05:18 +0100497 params = sfp.pop("chain_parameters")
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000498 sfc_encap = False
sousaedu80135b92021-02-17 15:05:18 +0100499
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000500 if params:
sousaedu80135b92021-02-17 15:05:18 +0100501 correlation = params.get("correlation")
502
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000503 if correlation:
504 sfc_encap = True
sousaedu80135b92021-02-17 15:05:18 +0100505
506 sfp["sfc_encap"] = sfc_encap
507 sfp["spi"] = sfp.pop("chain_id")
508 sfp["classifications"] = sfp.pop("flow_classifiers")
509 sfp["service_functions"] = sfp.pop("port_pair_groups")
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000510
511 # placeholder for now; read TODO note below
512 def _validate_classification(self, type, definition):
513 # only legacy_flow_classifier Type is supported at this point
514 return True
515 # TODO(igordcard): this method should be an abstract method of an
516 # abstract Classification class to be implemented by the specific
517 # Types. Also, abstract vimconnector should call the validation
518 # method before the implemented VIM connectors are called.
519
tiernoae4a8d12016-07-08 12:30:39 +0200520 def _format_exception(self, exception):
tierno69647792020-03-05 16:45:48 +0000521 """Transform a keystone, nova, neutron exception into a vimconn exception discovering the cause"""
tierno69647792020-03-05 16:45:48 +0000522 message_error = str(exception)
tierno5ad826a2020-08-11 11:19:44 +0000523 tip = ""
tiernode12f782019-04-05 12:46:42 +0000524
sousaedu80135b92021-02-17 15:05:18 +0100525 if isinstance(
526 exception,
527 (
528 neExceptions.NetworkNotFoundClient,
529 nvExceptions.NotFound,
530 ksExceptions.NotFound,
531 gl1Exceptions.HTTPNotFound,
532 ),
533 ):
534 raise vimconn.VimConnNotFoundException(
535 type(exception).__name__ + ": " + message_error
536 )
537 elif isinstance(
538 exception,
539 (
540 HTTPException,
541 gl1Exceptions.HTTPException,
542 gl1Exceptions.CommunicationError,
543 ConnectionError,
544 ksExceptions.ConnectionError,
545 neExceptions.ConnectionFailed,
546 ),
547 ):
tierno5ad826a2020-08-11 11:19:44 +0000548 if type(exception).__name__ == "SSLError":
549 tip = " (maybe option 'insecure' must be added to the VIM)"
sousaedu80135b92021-02-17 15:05:18 +0100550
551 raise vimconn.VimConnConnectionException(
552 "Invalid URL or credentials{}: {}".format(tip, message_error)
553 )
554 elif isinstance(
555 exception,
556 (
557 KeyError,
558 nvExceptions.BadRequest,
559 ksExceptions.BadRequest,
560 ),
561 ):
562 raise vimconn.VimConnException(
563 type(exception).__name__ + ": " + message_error
564 )
565 elif isinstance(
566 exception,
567 (
568 nvExceptions.ClientException,
569 ksExceptions.ClientException,
570 neExceptions.NeutronException,
571 ),
572 ):
573 raise vimconn.VimConnUnexpectedResponse(
574 type(exception).__name__ + ": " + message_error
575 )
tiernoae4a8d12016-07-08 12:30:39 +0200576 elif isinstance(exception, nvExceptions.Conflict):
sousaedu80135b92021-02-17 15:05:18 +0100577 raise vimconn.VimConnConflictException(
578 type(exception).__name__ + ": " + message_error
579 )
tierno72774862020-05-04 11:44:15 +0000580 elif isinstance(exception, vimconn.VimConnException):
tierno41a69812018-02-16 14:34:33 +0100581 raise exception
tiernof716aea2017-06-21 18:01:40 +0200582 else: # ()
tiernode12f782019-04-05 12:46:42 +0000583 self.logger.error("General Exception " + message_error, exc_info=True)
sousaedu80135b92021-02-17 15:05:18 +0100584
585 raise vimconn.VimConnConnectionException(
586 type(exception).__name__ + ": " + message_error
587 )
tiernoae4a8d12016-07-08 12:30:39 +0200588
tiernoa05b65a2019-02-01 12:30:27 +0000589 def _get_ids_from_name(self):
590 """
591 Obtain ids from name of tenant and security_groups. Store at self .security_groups_id"
592 :return: None
593 """
594 # get tenant_id if only tenant_name is supplied
595 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +0100596
tiernoa05b65a2019-02-01 12:30:27 +0000597 if not self.my_tenant_id:
sousaedu80135b92021-02-17 15:05:18 +0100598 raise vimconn.VimConnConnectionException(
599 "Error getting tenant information from name={} id={}".format(
600 self.tenant_name, self.tenant_id
601 )
602 )
603
604 if self.config.get("security_groups") and not self.security_groups_id:
tiernoa05b65a2019-02-01 12:30:27 +0000605 # convert from name to id
sousaedu80135b92021-02-17 15:05:18 +0100606 neutron_sg_list = self.neutron.list_security_groups(
607 tenant_id=self.my_tenant_id
608 )["security_groups"]
tiernoa05b65a2019-02-01 12:30:27 +0000609
610 self.security_groups_id = []
sousaedu80135b92021-02-17 15:05:18 +0100611 for sg in self.config.get("security_groups"):
tiernoa05b65a2019-02-01 12:30:27 +0000612 for neutron_sg in neutron_sg_list:
613 if sg in (neutron_sg["id"], neutron_sg["name"]):
614 self.security_groups_id.append(neutron_sg["id"])
615 break
616 else:
617 self.security_groups_id = None
sousaedu80135b92021-02-17 15:05:18 +0100618
619 raise vimconn.VimConnConnectionException(
620 "Not found security group {} for this tenant".format(sg)
621 )
tiernoa05b65a2019-02-01 12:30:27 +0000622
tierno5509c2e2019-07-04 16:23:20 +0000623 def check_vim_connectivity(self):
624 # just get network list to check connectivity and credentials
625 self.get_network_list(filter_dict={})
626
tiernoae4a8d12016-07-08 12:30:39 +0200627 def get_tenant_list(self, filter_dict={}):
tierno1ec592d2020-06-16 15:29:47 +0000628 """Obtain tenants of VIM
tiernoae4a8d12016-07-08 12:30:39 +0200629 filter_dict can contain the following keys:
630 name: filter by tenant name
631 id: filter by tenant uuid/id
632 <other VIM specific>
633 Returns the tenant list of dictionaries: [{'name':'<name>, 'id':'<id>, ...}, ...]
tierno1ec592d2020-06-16 15:29:47 +0000634 """
ahmadsa95baa272016-11-30 09:14:11 +0500635 self.logger.debug("Getting tenants from VIM filter: '%s'", str(filter_dict))
sousaedu80135b92021-02-17 15:05:18 +0100636
tiernoae4a8d12016-07-08 12:30:39 +0200637 try:
638 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +0100639
tiernof716aea2017-06-21 18:01:40 +0200640 if self.api_version3:
sousaedu80135b92021-02-17 15:05:18 +0100641 project_class_list = self.keystone.projects.list(
642 name=filter_dict.get("name")
643 )
ahmadsa95baa272016-11-30 09:14:11 +0500644 else:
tiernof716aea2017-06-21 18:01:40 +0200645 project_class_list = self.keystone.tenants.findall(**filter_dict)
sousaedu80135b92021-02-17 15:05:18 +0100646
tierno1ec592d2020-06-16 15:29:47 +0000647 project_list = []
sousaedu80135b92021-02-17 15:05:18 +0100648
ahmadsa95baa272016-11-30 09:14:11 +0500649 for project in project_class_list:
sousaedu80135b92021-02-17 15:05:18 +0100650 if filter_dict.get("id") and filter_dict["id"] != project.id:
tiernof716aea2017-06-21 18:01:40 +0200651 continue
sousaedu80135b92021-02-17 15:05:18 +0100652
ahmadsa95baa272016-11-30 09:14:11 +0500653 project_list.append(project.to_dict())
sousaedu80135b92021-02-17 15:05:18 +0100654
ahmadsa95baa272016-11-30 09:14:11 +0500655 return project_list
sousaedu80135b92021-02-17 15:05:18 +0100656 except (
657 ksExceptions.ConnectionError,
658 ksExceptions.ClientException,
659 ConnectionError,
660 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200661 self._format_exception(e)
662
663 def new_tenant(self, tenant_name, tenant_description):
tierno1ec592d2020-06-16 15:29:47 +0000664 """Adds a new tenant to openstack VIM. Returns the tenant identifier"""
tiernoae4a8d12016-07-08 12:30:39 +0200665 self.logger.debug("Adding a new tenant name: %s", tenant_name)
sousaedu80135b92021-02-17 15:05:18 +0100666
tiernoae4a8d12016-07-08 12:30:39 +0200667 try:
668 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +0100669
tiernof716aea2017-06-21 18:01:40 +0200670 if self.api_version3:
sousaedu80135b92021-02-17 15:05:18 +0100671 project = self.keystone.projects.create(
672 tenant_name,
673 self.config.get("project_domain_id", "default"),
674 description=tenant_description,
675 is_domain=False,
676 )
ahmadsa95baa272016-11-30 09:14:11 +0500677 else:
tiernof716aea2017-06-21 18:01:40 +0200678 project = self.keystone.tenants.create(tenant_name, tenant_description)
sousaedu80135b92021-02-17 15:05:18 +0100679
ahmadsa95baa272016-11-30 09:14:11 +0500680 return project.id
sousaedu80135b92021-02-17 15:05:18 +0100681 except (
682 ksExceptions.ConnectionError,
683 ksExceptions.ClientException,
684 ksExceptions.BadRequest,
685 ConnectionError,
686 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200687 self._format_exception(e)
688
689 def delete_tenant(self, tenant_id):
tierno1ec592d2020-06-16 15:29:47 +0000690 """Delete a tenant from openstack VIM. Returns the old tenant identifier"""
tiernoae4a8d12016-07-08 12:30:39 +0200691 self.logger.debug("Deleting tenant %s from VIM", tenant_id)
sousaedu80135b92021-02-17 15:05:18 +0100692
tiernoae4a8d12016-07-08 12:30:39 +0200693 try:
694 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +0100695
tiernof716aea2017-06-21 18:01:40 +0200696 if self.api_version3:
ahmadsa95baa272016-11-30 09:14:11 +0500697 self.keystone.projects.delete(tenant_id)
698 else:
699 self.keystone.tenants.delete(tenant_id)
sousaedu80135b92021-02-17 15:05:18 +0100700
tiernoae4a8d12016-07-08 12:30:39 +0200701 return tenant_id
sousaedu80135b92021-02-17 15:05:18 +0100702 except (
703 ksExceptions.ConnectionError,
704 ksExceptions.ClientException,
705 ksExceptions.NotFound,
706 ConnectionError,
707 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200708 self._format_exception(e)
ahmadsa95baa272016-11-30 09:14:11 +0500709
sousaedu80135b92021-02-17 15:05:18 +0100710 def new_network(
711 self,
712 net_name,
713 net_type,
714 ip_profile=None,
715 shared=False,
716 provider_network_profile=None,
717 ):
garciadeblasebd66722019-01-31 16:01:31 +0000718 """Adds a tenant network to VIM
719 Params:
720 'net_name': name of the network
721 'net_type': one of:
722 'bridge': overlay isolated network
723 'data': underlay E-LAN network for Passthrough and SRIOV interfaces
724 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces.
725 'ip_profile': is a dict containing the IP parameters of the network
726 'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented)
727 'subnet_address': ip_prefix_schema, that is X.X.X.X/Y
728 'gateway_address': (Optional) ip_schema, that is X.X.X.X
729 'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X]
730 'dhcp_enabled': True or False
731 'dhcp_start_address': ip_schema, first IP to grant
732 'dhcp_count': number of IPs to grant.
733 'shared': if this network can be seen/use by other tenants/organization
garciadeblas4af0d542020-02-18 16:01:13 +0100734 'provider_network_profile': (optional) contains {segmentation-id: vlan, network-type: vlan|vxlan,
735 physical-network: physnet-label}
garciadeblasebd66722019-01-31 16:01:31 +0000736 Returns a tuple with the network identifier and created_items, or raises an exception on error
737 created_items can be None or a dictionary where this method can include key-values that will be passed to
738 the method delete_network. Can be used to store created segments, created l2gw connections, etc.
739 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
740 as not present.
741 """
sousaedu80135b92021-02-17 15:05:18 +0100742 self.logger.debug(
743 "Adding a new network to VIM name '%s', type '%s'", net_name, net_type
744 )
garciadeblasebd66722019-01-31 16:01:31 +0000745 # self.logger.debug(">>>>>>>>>>>>>>>>>> IP profile %s", str(ip_profile))
kbsuba85c54d2019-10-17 16:30:32 +0000746
tierno7edb6752016-03-21 17:37:52 +0100747 try:
kbsuba85c54d2019-10-17 16:30:32 +0000748 vlan = None
sousaedu80135b92021-02-17 15:05:18 +0100749
kbsuba85c54d2019-10-17 16:30:32 +0000750 if provider_network_profile:
751 vlan = provider_network_profile.get("segmentation-id")
sousaedu80135b92021-02-17 15:05:18 +0100752
garciadeblasedca7b32016-09-29 14:01:52 +0000753 new_net = None
garciadeblasebd66722019-01-31 16:01:31 +0000754 created_items = {}
tierno7edb6752016-03-21 17:37:52 +0100755 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +0100756 network_dict = {"name": net_name, "admin_state_up": True}
757
tierno6869ae72020-01-09 17:37:34 +0000758 if net_type in ("data", "ptp"):
759 provider_physical_network = None
sousaedu80135b92021-02-17 15:05:18 +0100760
761 if provider_network_profile and provider_network_profile.get(
762 "physical-network"
763 ):
764 provider_physical_network = provider_network_profile.get(
765 "physical-network"
766 )
767
tierno6869ae72020-01-09 17:37:34 +0000768 # provider-network must be one of the dataplane_physcial_netowrk if this is a list. If it is string
769 # or not declared, just ignore the checking
sousaedu80135b92021-02-17 15:05:18 +0100770 if (
771 isinstance(
772 self.config.get("dataplane_physical_net"), (tuple, list)
773 )
774 and provider_physical_network
775 not in self.config["dataplane_physical_net"]
776 ):
tierno72774862020-05-04 11:44:15 +0000777 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +0100778 "Invalid parameter 'provider-network:physical-network' "
779 "for network creation. '{}' is not one of the declared "
780 "list at VIM_config:dataplane_physical_net".format(
781 provider_physical_network
782 )
783 )
784
785 # use the default dataplane_physical_net
786 if not provider_physical_network:
787 provider_physical_network = self.config.get(
788 "dataplane_physical_net"
789 )
790
tierno6869ae72020-01-09 17:37:34 +0000791 # if it is non empty list, use the first value. If it is a string use the value directly
sousaedu80135b92021-02-17 15:05:18 +0100792 if (
793 isinstance(provider_physical_network, (tuple, list))
794 and provider_physical_network
795 ):
tierno6869ae72020-01-09 17:37:34 +0000796 provider_physical_network = provider_physical_network[0]
797
798 if not provider_physical_network:
tierno5ad826a2020-08-11 11:19:44 +0000799 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +0100800 "missing information needed for underlay networks. Provide "
801 "'dataplane_physical_net' configuration at VIM or use the NS "
802 "instantiation parameter 'provider-network.physical-network'"
803 " for the VLD"
804 )
tierno6869ae72020-01-09 17:37:34 +0000805
sousaedu80135b92021-02-17 15:05:18 +0100806 if not self.config.get("multisegment_support"):
807 network_dict[
808 "provider:physical_network"
809 ] = provider_physical_network
810
811 if (
812 provider_network_profile
813 and "network-type" in provider_network_profile
814 ):
815 network_dict[
816 "provider:network_type"
817 ] = provider_network_profile["network-type"]
garciadeblas4af0d542020-02-18 16:01:13 +0100818 else:
sousaedu80135b92021-02-17 15:05:18 +0100819 network_dict["provider:network_type"] = self.config.get(
820 "dataplane_network_type", "vlan"
821 )
822
tierno6869ae72020-01-09 17:37:34 +0000823 if vlan:
824 network_dict["provider:segmentation_id"] = vlan
garciadeblasebd66722019-01-31 16:01:31 +0000825 else:
tierno6869ae72020-01-09 17:37:34 +0000826 # Multi-segment case
garciadeblasebd66722019-01-31 16:01:31 +0000827 segment_list = []
tierno6869ae72020-01-09 17:37:34 +0000828 segment1_dict = {
sousaedu80135b92021-02-17 15:05:18 +0100829 "provider:physical_network": "",
830 "provider:network_type": "vxlan",
tierno6869ae72020-01-09 17:37:34 +0000831 }
garciadeblasebd66722019-01-31 16:01:31 +0000832 segment_list.append(segment1_dict)
tierno6869ae72020-01-09 17:37:34 +0000833 segment2_dict = {
834 "provider:physical_network": provider_physical_network,
sousaedu80135b92021-02-17 15:05:18 +0100835 "provider:network_type": "vlan",
tierno6869ae72020-01-09 17:37:34 +0000836 }
sousaedu80135b92021-02-17 15:05:18 +0100837
tierno6869ae72020-01-09 17:37:34 +0000838 if vlan:
839 segment2_dict["provider:segmentation_id"] = vlan
sousaedu80135b92021-02-17 15:05:18 +0100840 elif self.config.get("multisegment_vlan_range"):
garciadeblasebd66722019-01-31 16:01:31 +0000841 vlanID = self._generate_multisegment_vlanID()
842 segment2_dict["provider:segmentation_id"] = vlanID
sousaedu80135b92021-02-17 15:05:18 +0100843
garciadeblasebd66722019-01-31 16:01:31 +0000844 # else
tierno72774862020-05-04 11:44:15 +0000845 # raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +0100846 # "You must provide "multisegment_vlan_range" at config dict before creating a multisegment
tierno1ec592d2020-06-16 15:29:47 +0000847 # network")
garciadeblasebd66722019-01-31 16:01:31 +0000848 segment_list.append(segment2_dict)
849 network_dict["segments"] = segment_list
kate721d79b2017-06-24 04:21:38 -0700850
tierno6869ae72020-01-09 17:37:34 +0000851 # VIO Specific Changes. It needs a concrete VLAN
852 if self.vim_type == "VIO" and vlan is None:
sousaedu80135b92021-02-17 15:05:18 +0100853 if self.config.get("dataplane_net_vlan_range") is None:
tierno72774862020-05-04 11:44:15 +0000854 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +0100855 "You must provide 'dataplane_net_vlan_range' in format "
856 "[start_ID - end_ID] at VIM_config for creating underlay "
857 "networks"
858 )
859
tierno6869ae72020-01-09 17:37:34 +0000860 network_dict["provider:segmentation_id"] = self._generate_vlanID()
kate721d79b2017-06-24 04:21:38 -0700861
garciadeblasebd66722019-01-31 16:01:31 +0000862 network_dict["shared"] = shared
sousaedu80135b92021-02-17 15:05:18 +0100863
anwarsff168192019-05-06 11:23:07 +0530864 if self.config.get("disable_network_port_security"):
865 network_dict["port_security_enabled"] = False
sousaedu80135b92021-02-17 15:05:18 +0100866
sousaedu2aa5f802021-06-17 15:39:29 +0100867 if self.config.get("neutron_availability_zone_hints"):
868 hints = self.config.get("neutron_availability_zone_hints")
869
870 if isinstance(hints, str):
871 hints = [hints]
872
873 network_dict["availability_zone_hints"] = hints
874
sousaedu80135b92021-02-17 15:05:18 +0100875 new_net = self.neutron.create_network({"network": network_dict})
garciadeblasebd66722019-01-31 16:01:31 +0000876 # print new_net
877 # create subnetwork, even if there is no profile
sousaedu80135b92021-02-17 15:05:18 +0100878
garciadeblas9f8456e2016-09-05 05:02:59 +0200879 if not ip_profile:
880 ip_profile = {}
sousaedu80135b92021-02-17 15:05:18 +0100881
882 if not ip_profile.get("subnet_address"):
tierno1ec592d2020-06-16 15:29:47 +0000883 # Fake subnet is required
garciadeblas2299e3b2017-01-26 14:35:55 +0000884 subnet_rand = random.randint(0, 255)
sousaedu80135b92021-02-17 15:05:18 +0100885 ip_profile["subnet_address"] = "192.168.{}.0/24".format(subnet_rand)
886
887 if "ip_version" not in ip_profile:
888 ip_profile["ip_version"] = "IPv4"
889
890 subnet = {
891 "name": net_name + "-subnet",
892 "network_id": new_net["network"]["id"],
893 "ip_version": 4 if ip_profile["ip_version"] == "IPv4" else 6,
894 "cidr": ip_profile["subnet_address"],
895 }
896
tiernoa1fb4462017-06-30 12:25:50 +0200897 # Gateway should be set to None if not needed. Otherwise openstack assigns one by default
sousaedu80135b92021-02-17 15:05:18 +0100898 if ip_profile.get("gateway_address"):
899 subnet["gateway_ip"] = ip_profile["gateway_address"]
tierno55d234c2018-07-04 18:29:21 +0200900 else:
sousaedu80135b92021-02-17 15:05:18 +0100901 subnet["gateway_ip"] = None
902
903 if ip_profile.get("dns_address"):
904 subnet["dns_nameservers"] = ip_profile["dns_address"].split(";")
905
906 if "dhcp_enabled" in ip_profile:
907 subnet["enable_dhcp"] = (
908 False
909 if ip_profile["dhcp_enabled"] == "false"
910 or ip_profile["dhcp_enabled"] is False
911 else True
912 )
913
914 if ip_profile.get("dhcp_start_address"):
915 subnet["allocation_pools"] = []
916 subnet["allocation_pools"].append(dict())
917 subnet["allocation_pools"][0]["start"] = ip_profile[
918 "dhcp_start_address"
919 ]
920
921 if ip_profile.get("dhcp_count"):
922 # parts = ip_profile["dhcp_start_address"].split(".")
tierno1ec592d2020-06-16 15:29:47 +0000923 # ip_int = (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3])
sousaedu80135b92021-02-17 15:05:18 +0100924 ip_int = int(netaddr.IPAddress(ip_profile["dhcp_start_address"]))
925 ip_int += ip_profile["dhcp_count"] - 1
garciadeblas9f8456e2016-09-05 05:02:59 +0200926 ip_str = str(netaddr.IPAddress(ip_int))
sousaedu80135b92021-02-17 15:05:18 +0100927 subnet["allocation_pools"][0]["end"] = ip_str
928
tierno1ec592d2020-06-16 15:29:47 +0000929 # self.logger.debug(">>>>>>>>>>>>>>>>>> Subnet: %s", str(subnet))
930 self.neutron.create_subnet({"subnet": subnet})
garciadeblasebd66722019-01-31 16:01:31 +0000931
sousaedu80135b92021-02-17 15:05:18 +0100932 if net_type == "data" and self.config.get("multisegment_support"):
933 if self.config.get("l2gw_support"):
garciadeblasebd66722019-01-31 16:01:31 +0000934 l2gw_list = self.neutron.list_l2_gateways().get("l2_gateways", ())
935 for l2gw in l2gw_list:
tierno1ec592d2020-06-16 15:29:47 +0000936 l2gw_conn = {
937 "l2_gateway_id": l2gw["id"],
938 "network_id": new_net["network"]["id"],
939 "segmentation_id": str(vlanID),
940 }
sousaedu80135b92021-02-17 15:05:18 +0100941 new_l2gw_conn = self.neutron.create_l2_gateway_connection(
942 {"l2_gateway_connection": l2gw_conn}
943 )
944 created_items[
945 "l2gwconn:"
946 + str(new_l2gw_conn["l2_gateway_connection"]["id"])
947 ] = True
948
garciadeblasebd66722019-01-31 16:01:31 +0000949 return new_net["network"]["id"], created_items
tierno41a69812018-02-16 14:34:33 +0100950 except Exception as e:
tierno1ec592d2020-06-16 15:29:47 +0000951 # delete l2gw connections (if any) before deleting the network
garciadeblasebd66722019-01-31 16:01:31 +0000952 for k, v in created_items.items():
953 if not v: # skip already deleted
954 continue
sousaedu80135b92021-02-17 15:05:18 +0100955
garciadeblasebd66722019-01-31 16:01:31 +0000956 try:
957 k_item, _, k_id = k.partition(":")
sousaedu80135b92021-02-17 15:05:18 +0100958
garciadeblasebd66722019-01-31 16:01:31 +0000959 if k_item == "l2gwconn":
960 self.neutron.delete_l2_gateway_connection(k_id)
961 except Exception as e2:
sousaedu80135b92021-02-17 15:05:18 +0100962 self.logger.error(
963 "Error deleting l2 gateway connection: {}: {}".format(
964 type(e2).__name__, e2
965 )
966 )
967
garciadeblasedca7b32016-09-29 14:01:52 +0000968 if new_net:
sousaedu80135b92021-02-17 15:05:18 +0100969 self.neutron.delete_network(new_net["network"]["id"])
970
tiernoae4a8d12016-07-08 12:30:39 +0200971 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100972
973 def get_network_list(self, filter_dict={}):
tierno1ec592d2020-06-16 15:29:47 +0000974 """Obtain tenant networks of VIM
tierno7edb6752016-03-21 17:37:52 +0100975 Filter_dict can be:
976 name: network name
977 id: network uuid
978 shared: boolean
979 tenant_id: tenant
980 admin_state_up: boolean
981 status: 'ACTIVE'
982 Returns the network list of dictionaries
tierno1ec592d2020-06-16 15:29:47 +0000983 """
tiernoae4a8d12016-07-08 12:30:39 +0200984 self.logger.debug("Getting network from VIM filter: '%s'", str(filter_dict))
sousaedu80135b92021-02-17 15:05:18 +0100985
tierno7edb6752016-03-21 17:37:52 +0100986 try:
987 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +0100988 filter_dict_os = filter_dict.copy()
sousaedu80135b92021-02-17 15:05:18 +0100989
tierno69b590e2018-03-13 18:52:23 +0100990 if self.api_version3 and "tenant_id" in filter_dict_os:
sousaedu80135b92021-02-17 15:05:18 +0100991 # TODO check
992 filter_dict_os["project_id"] = filter_dict_os.pop("tenant_id")
993
tierno69b590e2018-03-13 18:52:23 +0100994 net_dict = self.neutron.list_networks(**filter_dict_os)
tierno00e3df72017-11-29 17:20:13 +0100995 net_list = net_dict["networks"]
tierno7edb6752016-03-21 17:37:52 +0100996 self.__net_os2mano(net_list)
sousaedu80135b92021-02-17 15:05:18 +0100997
tiernoae4a8d12016-07-08 12:30:39 +0200998 return net_list
sousaedu80135b92021-02-17 15:05:18 +0100999 except (
1000 neExceptions.ConnectionFailed,
1001 ksExceptions.ClientException,
1002 neExceptions.NeutronException,
1003 ConnectionError,
1004 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001005 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001006
tiernoae4a8d12016-07-08 12:30:39 +02001007 def get_network(self, net_id):
tierno1ec592d2020-06-16 15:29:47 +00001008 """Obtain details of network from VIM
1009 Returns the network information from a network id"""
tiernoae4a8d12016-07-08 12:30:39 +02001010 self.logger.debug(" Getting tenant network %s from VIM", net_id)
tierno1ec592d2020-06-16 15:29:47 +00001011 filter_dict = {"id": net_id}
tiernoae4a8d12016-07-08 12:30:39 +02001012 net_list = self.get_network_list(filter_dict)
sousaedu80135b92021-02-17 15:05:18 +01001013
tierno1ec592d2020-06-16 15:29:47 +00001014 if len(net_list) == 0:
sousaedu80135b92021-02-17 15:05:18 +01001015 raise vimconn.VimConnNotFoundException(
1016 "Network '{}' not found".format(net_id)
1017 )
tierno1ec592d2020-06-16 15:29:47 +00001018 elif len(net_list) > 1:
sousaedu80135b92021-02-17 15:05:18 +01001019 raise vimconn.VimConnConflictException(
1020 "Found more than one network with this criteria"
1021 )
1022
tierno7edb6752016-03-21 17:37:52 +01001023 net = net_list[0]
tierno1ec592d2020-06-16 15:29:47 +00001024 subnets = []
1025 for subnet_id in net.get("subnets", ()):
tierno7edb6752016-03-21 17:37:52 +01001026 try:
1027 subnet = self.neutron.show_subnet(subnet_id)
1028 except Exception as e:
sousaedu80135b92021-02-17 15:05:18 +01001029 self.logger.error(
1030 "osconnector.get_network(): Error getting subnet %s %s"
1031 % (net_id, str(e))
1032 )
tiernoae4a8d12016-07-08 12:30:39 +02001033 subnet = {"id": subnet_id, "fault": str(e)}
sousaedu80135b92021-02-17 15:05:18 +01001034
tierno7edb6752016-03-21 17:37:52 +01001035 subnets.append(subnet)
sousaedu80135b92021-02-17 15:05:18 +01001036
tierno7edb6752016-03-21 17:37:52 +01001037 net["subnets"] = subnets
sousaedu80135b92021-02-17 15:05:18 +01001038 net["encapsulation"] = net.get("provider:network_type")
1039 net["encapsulation_type"] = net.get("provider:network_type")
1040 net["segmentation_id"] = net.get("provider:segmentation_id")
1041 net["encapsulation_id"] = net.get("provider:segmentation_id")
1042
tiernoae4a8d12016-07-08 12:30:39 +02001043 return net
tierno7edb6752016-03-21 17:37:52 +01001044
garciadeblasebd66722019-01-31 16:01:31 +00001045 def delete_network(self, net_id, created_items=None):
1046 """
1047 Removes a tenant network from VIM and its associated elements
1048 :param net_id: VIM identifier of the network, provided by method new_network
1049 :param created_items: dictionary with extra items to be deleted. provided by method new_network
1050 Returns the network identifier or raises an exception upon error or when network is not found
1051 """
tiernoae4a8d12016-07-08 12:30:39 +02001052 self.logger.debug("Deleting network '%s' from VIM", net_id)
sousaedu80135b92021-02-17 15:05:18 +01001053
tierno1ec592d2020-06-16 15:29:47 +00001054 if created_items is None:
garciadeblasebd66722019-01-31 16:01:31 +00001055 created_items = {}
sousaedu80135b92021-02-17 15:05:18 +01001056
tierno7edb6752016-03-21 17:37:52 +01001057 try:
1058 self._reload_connection()
tierno1ec592d2020-06-16 15:29:47 +00001059 # delete l2gw connections (if any) before deleting the network
garciadeblasebd66722019-01-31 16:01:31 +00001060 for k, v in created_items.items():
1061 if not v: # skip already deleted
1062 continue
sousaedu80135b92021-02-17 15:05:18 +01001063
garciadeblasebd66722019-01-31 16:01:31 +00001064 try:
1065 k_item, _, k_id = k.partition(":")
1066 if k_item == "l2gwconn":
1067 self.neutron.delete_l2_gateway_connection(k_id)
1068 except Exception as e:
sousaedu80135b92021-02-17 15:05:18 +01001069 self.logger.error(
1070 "Error deleting l2 gateway connection: {}: {}".format(
1071 type(e).__name__, e
1072 )
1073 )
1074
tierno1ec592d2020-06-16 15:29:47 +00001075 # delete VM ports attached to this networks before the network
tierno7edb6752016-03-21 17:37:52 +01001076 ports = self.neutron.list_ports(network_id=net_id)
sousaedu80135b92021-02-17 15:05:18 +01001077 for p in ports["ports"]:
tierno7edb6752016-03-21 17:37:52 +01001078 try:
1079 self.neutron.delete_port(p["id"])
1080 except Exception as e:
tiernoae4a8d12016-07-08 12:30:39 +02001081 self.logger.error("Error deleting port %s: %s", p["id"], str(e))
sousaedu80135b92021-02-17 15:05:18 +01001082
tierno7edb6752016-03-21 17:37:52 +01001083 self.neutron.delete_network(net_id)
sousaedu80135b92021-02-17 15:05:18 +01001084
tiernoae4a8d12016-07-08 12:30:39 +02001085 return net_id
sousaedu80135b92021-02-17 15:05:18 +01001086 except (
1087 neExceptions.ConnectionFailed,
1088 neExceptions.NetworkNotFoundClient,
1089 neExceptions.NeutronException,
1090 ksExceptions.ClientException,
1091 neExceptions.NeutronException,
1092 ConnectionError,
1093 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001094 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001095
tiernoae4a8d12016-07-08 12:30:39 +02001096 def refresh_nets_status(self, net_list):
tierno1ec592d2020-06-16 15:29:47 +00001097 """Get the status of the networks
sousaedu80135b92021-02-17 15:05:18 +01001098 Params: the list of network identifiers
1099 Returns a dictionary with:
1100 net_id: #VIM id of this network
1101 status: #Mandatory. Text with one of:
1102 # DELETED (not found at vim)
1103 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1104 # OTHER (Vim reported other status not understood)
1105 # ERROR (VIM indicates an ERROR status)
1106 # ACTIVE, INACTIVE, DOWN (admin down),
1107 # BUILD (on building process)
1108 #
1109 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1110 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
tierno1ec592d2020-06-16 15:29:47 +00001111 """
1112 net_dict = {}
sousaedu80135b92021-02-17 15:05:18 +01001113
tiernoae4a8d12016-07-08 12:30:39 +02001114 for net_id in net_list:
1115 net = {}
sousaedu80135b92021-02-17 15:05:18 +01001116
tiernoae4a8d12016-07-08 12:30:39 +02001117 try:
1118 net_vim = self.get_network(net_id)
sousaedu80135b92021-02-17 15:05:18 +01001119
1120 if net_vim["status"] in netStatus2manoFormat:
1121 net["status"] = netStatus2manoFormat[net_vim["status"]]
tiernoae4a8d12016-07-08 12:30:39 +02001122 else:
1123 net["status"] = "OTHER"
sousaedu80135b92021-02-17 15:05:18 +01001124 net["error_msg"] = "VIM status reported " + net_vim["status"]
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001125
sousaedu80135b92021-02-17 15:05:18 +01001126 if net["status"] == "ACTIVE" and not net_vim["admin_state_up"]:
1127 net["status"] = "DOWN"
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001128
sousaedu80135b92021-02-17 15:05:18 +01001129 net["vim_info"] = self.serialize(net_vim)
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001130
sousaedu80135b92021-02-17 15:05:18 +01001131 if net_vim.get("fault"): # TODO
1132 net["error_msg"] = str(net_vim["fault"])
tierno72774862020-05-04 11:44:15 +00001133 except vimconn.VimConnNotFoundException as e:
tiernoae4a8d12016-07-08 12:30:39 +02001134 self.logger.error("Exception getting net status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01001135 net["status"] = "DELETED"
1136 net["error_msg"] = str(e)
tierno72774862020-05-04 11:44:15 +00001137 except vimconn.VimConnException as e:
tiernoae4a8d12016-07-08 12:30:39 +02001138 self.logger.error("Exception getting net status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01001139 net["status"] = "VIM_ERROR"
1140 net["error_msg"] = str(e)
tiernoae4a8d12016-07-08 12:30:39 +02001141 net_dict[net_id] = net
1142 return net_dict
1143
1144 def get_flavor(self, flavor_id):
tierno1ec592d2020-06-16 15:29:47 +00001145 """Obtain flavor details from the VIM. Returns the flavor dict details"""
tiernoae4a8d12016-07-08 12:30:39 +02001146 self.logger.debug("Getting flavor '%s'", flavor_id)
sousaedu80135b92021-02-17 15:05:18 +01001147
tierno7edb6752016-03-21 17:37:52 +01001148 try:
1149 self._reload_connection()
1150 flavor = self.nova.flavors.find(id=flavor_id)
tierno1ec592d2020-06-16 15:29:47 +00001151 # TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
sousaedu80135b92021-02-17 15:05:18 +01001152
tiernoae4a8d12016-07-08 12:30:39 +02001153 return flavor.to_dict()
sousaedu80135b92021-02-17 15:05:18 +01001154 except (
1155 nvExceptions.NotFound,
1156 nvExceptions.ClientException,
1157 ksExceptions.ClientException,
1158 ConnectionError,
1159 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001160 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001161
tiernocf157a82017-01-30 14:07:06 +01001162 def get_flavor_id_from_data(self, flavor_dict):
1163 """Obtain flavor id that match the flavor description
sousaedu80135b92021-02-17 15:05:18 +01001164 Returns the flavor_id or raises a vimconnNotFoundException
1165 flavor_dict: contains the required ram, vcpus, disk
1166 If 'use_existing_flavors' is set to True at config, the closer flavor that provides same or more ram, vcpus
1167 and disk is returned. Otherwise a flavor with exactly same ram, vcpus and disk is returned or a
1168 vimconnNotFoundException is raised
tiernocf157a82017-01-30 14:07:06 +01001169 """
sousaedu80135b92021-02-17 15:05:18 +01001170 exact_match = False if self.config.get("use_existing_flavors") else True
1171
tiernocf157a82017-01-30 14:07:06 +01001172 try:
1173 self._reload_connection()
tiernoe26fc7a2017-05-30 14:43:03 +02001174 flavor_candidate_id = None
1175 flavor_candidate_data = (10000, 10000, 10000)
sousaedu80135b92021-02-17 15:05:18 +01001176 flavor_target = (
1177 flavor_dict["ram"],
1178 flavor_dict["vcpus"],
1179 flavor_dict["disk"],
sousaedu648ee3d2021-11-22 14:09:15 +00001180 flavor_dict.get("ephemeral", 0),
1181 flavor_dict.get("swap", 0),
sousaedu80135b92021-02-17 15:05:18 +01001182 )
tiernoe26fc7a2017-05-30 14:43:03 +02001183 # numa=None
anwarsae5f52c2019-04-22 10:35:27 +05301184 extended = flavor_dict.get("extended", {})
1185 if extended:
tierno1ec592d2020-06-16 15:29:47 +00001186 # TODO
sousaedu80135b92021-02-17 15:05:18 +01001187 raise vimconn.VimConnNotFoundException(
1188 "Flavor with EPA still not implemented"
1189 )
tiernocf157a82017-01-30 14:07:06 +01001190 # if len(numas) > 1:
tierno72774862020-05-04 11:44:15 +00001191 # raise vimconn.VimConnNotFoundException("Cannot find any flavor with more than one numa")
tiernocf157a82017-01-30 14:07:06 +01001192 # numa=numas[0]
1193 # numas = extended.get("numas")
1194 for flavor in self.nova.flavors.list():
1195 epa = flavor.get_keys()
sousaedu80135b92021-02-17 15:05:18 +01001196
tiernocf157a82017-01-30 14:07:06 +01001197 if epa:
1198 continue
tiernoe26fc7a2017-05-30 14:43:03 +02001199 # TODO
sousaedu80135b92021-02-17 15:05:18 +01001200
sousaedu648ee3d2021-11-22 14:09:15 +00001201 flavor_data = (
1202 flavor.ram,
1203 flavor.vcpus,
1204 flavor.disk,
1205 flavor.ephemeral,
1206 flavor.swap,
1207 )
tiernoe26fc7a2017-05-30 14:43:03 +02001208 if flavor_data == flavor_target:
1209 return flavor.id
sousaedu80135b92021-02-17 15:05:18 +01001210 elif (
1211 not exact_match
1212 and flavor_target < flavor_data < flavor_candidate_data
1213 ):
tiernoe26fc7a2017-05-30 14:43:03 +02001214 flavor_candidate_id = flavor.id
1215 flavor_candidate_data = flavor_data
sousaedu80135b92021-02-17 15:05:18 +01001216
tiernoe26fc7a2017-05-30 14:43:03 +02001217 if not exact_match and flavor_candidate_id:
1218 return flavor_candidate_id
sousaedu80135b92021-02-17 15:05:18 +01001219
1220 raise vimconn.VimConnNotFoundException(
1221 "Cannot find any flavor matching '{}'".format(flavor_dict)
1222 )
1223 except (
1224 nvExceptions.NotFound,
1225 nvExceptions.ClientException,
1226 ksExceptions.ClientException,
1227 ConnectionError,
1228 ) as e:
tiernocf157a82017-01-30 14:07:06 +01001229 self._format_exception(e)
1230
anwarsae5f52c2019-04-22 10:35:27 +05301231 def process_resource_quota(self, quota, prefix, extra_specs):
1232 """
1233 :param prefix:
borsatti8a2dda32019-12-18 15:08:57 +00001234 :param extra_specs:
anwarsae5f52c2019-04-22 10:35:27 +05301235 :return:
1236 """
sousaedu80135b92021-02-17 15:05:18 +01001237 if "limit" in quota:
1238 extra_specs["quota:" + prefix + "_limit"] = quota["limit"]
1239
1240 if "reserve" in quota:
1241 extra_specs["quota:" + prefix + "_reservation"] = quota["reserve"]
1242
1243 if "shares" in quota:
anwarsae5f52c2019-04-22 10:35:27 +05301244 extra_specs["quota:" + prefix + "_shares_level"] = "custom"
sousaedu80135b92021-02-17 15:05:18 +01001245 extra_specs["quota:" + prefix + "_shares_share"] = quota["shares"]
anwarsae5f52c2019-04-22 10:35:27 +05301246
tiernoae4a8d12016-07-08 12:30:39 +02001247 def new_flavor(self, flavor_data, change_name_if_used=True):
tierno1ec592d2020-06-16 15:29:47 +00001248 """Adds a tenant flavor to openstack VIM
1249 if change_name_if_used is True, it will change name in case of conflict, because it is not supported name
1250 repetition
tierno7edb6752016-03-21 17:37:52 +01001251 Returns the flavor identifier
tierno1ec592d2020-06-16 15:29:47 +00001252 """
tiernoae4a8d12016-07-08 12:30:39 +02001253 self.logger.debug("Adding flavor '%s'", str(flavor_data))
tierno1ec592d2020-06-16 15:29:47 +00001254 retry = 0
1255 max_retries = 3
tierno7edb6752016-03-21 17:37:52 +01001256 name_suffix = 0
sousaedu80135b92021-02-17 15:05:18 +01001257
anwarsc76a3ee2018-10-04 14:05:32 +05301258 try:
sousaedu80135b92021-02-17 15:05:18 +01001259 name = flavor_data["name"]
tierno1ec592d2020-06-16 15:29:47 +00001260 while retry < max_retries:
1261 retry += 1
anwarsc76a3ee2018-10-04 14:05:32 +05301262 try:
1263 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +01001264
anwarsc76a3ee2018-10-04 14:05:32 +05301265 if change_name_if_used:
tierno1ec592d2020-06-16 15:29:47 +00001266 # get used names
1267 fl_names = []
1268 fl = self.nova.flavors.list()
sousaedu80135b92021-02-17 15:05:18 +01001269
anwarsc76a3ee2018-10-04 14:05:32 +05301270 for f in fl:
1271 fl_names.append(f.name)
sousaedu80135b92021-02-17 15:05:18 +01001272
anwarsc76a3ee2018-10-04 14:05:32 +05301273 while name in fl_names:
1274 name_suffix += 1
sousaedu80135b92021-02-17 15:05:18 +01001275 name = flavor_data["name"] + "-" + str(name_suffix)
kate721d79b2017-06-24 04:21:38 -07001276
sousaedu80135b92021-02-17 15:05:18 +01001277 ram = flavor_data.get("ram", 64)
1278 vcpus = flavor_data.get("vcpus", 1)
tierno1ec592d2020-06-16 15:29:47 +00001279 extra_specs = {}
tierno7edb6752016-03-21 17:37:52 +01001280
anwarsc76a3ee2018-10-04 14:05:32 +05301281 extended = flavor_data.get("extended")
1282 if extended:
tierno1ec592d2020-06-16 15:29:47 +00001283 numas = extended.get("numas")
sousaedu80135b92021-02-17 15:05:18 +01001284
anwarsc76a3ee2018-10-04 14:05:32 +05301285 if numas:
1286 numa_nodes = len(numas)
sousaedu80135b92021-02-17 15:05:18 +01001287
anwarsc76a3ee2018-10-04 14:05:32 +05301288 if numa_nodes > 1:
1289 return -1, "Can not add flavor with more than one numa"
sousaedu80135b92021-02-17 15:05:18 +01001290
anwarsae5f52c2019-04-22 10:35:27 +05301291 extra_specs["hw:numa_nodes"] = str(numa_nodes)
1292 extra_specs["hw:mem_page_size"] = "large"
1293 extra_specs["hw:cpu_policy"] = "dedicated"
1294 extra_specs["hw:numa_mempolicy"] = "strict"
sousaedu80135b92021-02-17 15:05:18 +01001295
anwarsc76a3ee2018-10-04 14:05:32 +05301296 if self.vim_type == "VIO":
sousaedu80135b92021-02-17 15:05:18 +01001297 extra_specs[
1298 "vmware:extra_config"
1299 ] = '{"numa.nodeAffinity":"0"}'
anwarsae5f52c2019-04-22 10:35:27 +05301300 extra_specs["vmware:latency_sensitivity_level"] = "high"
sousaedu80135b92021-02-17 15:05:18 +01001301
anwarsc76a3ee2018-10-04 14:05:32 +05301302 for numa in numas:
tierno1ec592d2020-06-16 15:29:47 +00001303 # overwrite ram and vcpus
sousaedu80135b92021-02-17 15:05:18 +01001304 # check if key "memory" is present in numa else use ram value at flavor
1305 if "memory" in numa:
1306 ram = numa["memory"] * 1024
tierno1ec592d2020-06-16 15:29:47 +00001307 # See for reference: https://specs.openstack.org/openstack/nova-specs/specs/mitaka/
1308 # implemented/virt-driver-cpu-thread-pinning.html
garciadeblasfa35a722019-04-11 19:15:49 +02001309 extra_specs["hw:cpu_sockets"] = 1
sousaedu80135b92021-02-17 15:05:18 +01001310
1311 if "paired-threads" in numa:
1312 vcpus = numa["paired-threads"] * 2
1313 # cpu_thread_policy "require" implies that the compute node must have an
tierno1ec592d2020-06-16 15:29:47 +00001314 # STM architecture
anwarsae5f52c2019-04-22 10:35:27 +05301315 extra_specs["hw:cpu_thread_policy"] = "require"
1316 extra_specs["hw:cpu_policy"] = "dedicated"
sousaedu80135b92021-02-17 15:05:18 +01001317 elif "cores" in numa:
1318 vcpus = numa["cores"]
1319 # cpu_thread_policy "prefer" implies that the host must not have an SMT
tierno1ec592d2020-06-16 15:29:47 +00001320 # architecture, or a non-SMT architecture will be emulated
anwarsae5f52c2019-04-22 10:35:27 +05301321 extra_specs["hw:cpu_thread_policy"] = "isolate"
1322 extra_specs["hw:cpu_policy"] = "dedicated"
sousaedu80135b92021-02-17 15:05:18 +01001323 elif "threads" in numa:
1324 vcpus = numa["threads"]
tierno1ec592d2020-06-16 15:29:47 +00001325 # cpu_thread_policy "prefer" implies that the host may or may not have an SMT
1326 # architecture
anwarsae5f52c2019-04-22 10:35:27 +05301327 extra_specs["hw:cpu_thread_policy"] = "prefer"
1328 extra_specs["hw:cpu_policy"] = "dedicated"
anwarsc76a3ee2018-10-04 14:05:32 +05301329 # for interface in numa.get("interfaces",() ):
1330 # if interface["dedicated"]=="yes":
tierno1ec592d2020-06-16 15:29:47 +00001331 # raise vimconn.VimConnException("Passthrough interfaces are not supported
1332 # for the openstack connector", http_code=vimconn.HTTP_Service_Unavailable)
sousaedu80135b92021-02-17 15:05:18 +01001333 # #TODO, add the key 'pci_passthrough:alias"="<label at config>:<number ifaces>"'
tierno1ec592d2020-06-16 15:29:47 +00001334 # when a way to connect it is available
anwarsae5f52c2019-04-22 10:35:27 +05301335 elif extended.get("cpu-quota"):
sousaedu80135b92021-02-17 15:05:18 +01001336 self.process_resource_quota(
1337 extended.get("cpu-quota"), "cpu", extra_specs
1338 )
1339
anwarsae5f52c2019-04-22 10:35:27 +05301340 if extended.get("mem-quota"):
sousaedu80135b92021-02-17 15:05:18 +01001341 self.process_resource_quota(
1342 extended.get("mem-quota"), "memory", extra_specs
1343 )
1344
anwarsae5f52c2019-04-22 10:35:27 +05301345 if extended.get("vif-quota"):
sousaedu80135b92021-02-17 15:05:18 +01001346 self.process_resource_quota(
1347 extended.get("vif-quota"), "vif", extra_specs
1348 )
1349
anwarsae5f52c2019-04-22 10:35:27 +05301350 if extended.get("disk-io-quota"):
sousaedu80135b92021-02-17 15:05:18 +01001351 self.process_resource_quota(
1352 extended.get("disk-io-quota"), "disk_io", extra_specs
1353 )
1354
tierno1ec592d2020-06-16 15:29:47 +00001355 # create flavor
sousaedu80135b92021-02-17 15:05:18 +01001356 new_flavor = self.nova.flavors.create(
sousaeduf524da82021-11-22 14:02:17 +00001357 name=name,
1358 ram=ram,
1359 vcpus=vcpus,
1360 disk=flavor_data.get("disk", 0),
1361 ephemeral=flavor_data.get("ephemeral", 0),
sousaedu648ee3d2021-11-22 14:09:15 +00001362 swap=flavor_data.get("swap", 0),
sousaedu80135b92021-02-17 15:05:18 +01001363 is_public=flavor_data.get("is_public", True),
1364 )
tierno1ec592d2020-06-16 15:29:47 +00001365 # add metadata
anwarsae5f52c2019-04-22 10:35:27 +05301366 if extra_specs:
1367 new_flavor.set_keys(extra_specs)
sousaedu80135b92021-02-17 15:05:18 +01001368
anwarsc76a3ee2018-10-04 14:05:32 +05301369 return new_flavor.id
1370 except nvExceptions.Conflict as e:
1371 if change_name_if_used and retry < max_retries:
1372 continue
sousaedu80135b92021-02-17 15:05:18 +01001373
anwarsc76a3ee2018-10-04 14:05:32 +05301374 self._format_exception(e)
tierno1ec592d2020-06-16 15:29:47 +00001375 # except nvExceptions.BadRequest as e:
sousaedu80135b92021-02-17 15:05:18 +01001376 except (
1377 ksExceptions.ClientException,
1378 nvExceptions.ClientException,
1379 ConnectionError,
1380 KeyError,
1381 ) as e:
anwarsc76a3ee2018-10-04 14:05:32 +05301382 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001383
tierno1ec592d2020-06-16 15:29:47 +00001384 def delete_flavor(self, flavor_id):
sousaedu80135b92021-02-17 15:05:18 +01001385 """Deletes a tenant flavor from openstack VIM. Returns the old flavor_id"""
tiernoae4a8d12016-07-08 12:30:39 +02001386 try:
1387 self._reload_connection()
1388 self.nova.flavors.delete(flavor_id)
sousaedu80135b92021-02-17 15:05:18 +01001389
tiernoae4a8d12016-07-08 12:30:39 +02001390 return flavor_id
tierno1ec592d2020-06-16 15:29:47 +00001391 # except nvExceptions.BadRequest as e:
sousaedu80135b92021-02-17 15:05:18 +01001392 except (
1393 nvExceptions.NotFound,
1394 ksExceptions.ClientException,
1395 nvExceptions.ClientException,
1396 ConnectionError,
1397 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001398 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001399
tierno1ec592d2020-06-16 15:29:47 +00001400 def new_image(self, image_dict):
1401 """
tiernoae4a8d12016-07-08 12:30:39 +02001402 Adds a tenant image to VIM. imge_dict is a dictionary with:
1403 name: name
1404 disk_format: qcow2, vhd, vmdk, raw (by default), ...
1405 location: path or URI
1406 public: "yes" or "no"
1407 metadata: metadata of the image
1408 Returns the image_id
tierno1ec592d2020-06-16 15:29:47 +00001409 """
1410 retry = 0
1411 max_retries = 3
sousaedu80135b92021-02-17 15:05:18 +01001412
tierno1ec592d2020-06-16 15:29:47 +00001413 while retry < max_retries:
1414 retry += 1
tierno7edb6752016-03-21 17:37:52 +01001415 try:
1416 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +01001417
tierno1ec592d2020-06-16 15:29:47 +00001418 # determine format http://docs.openstack.org/developer/glance/formats.html
tierno7edb6752016-03-21 17:37:52 +01001419 if "disk_format" in image_dict:
tierno1ec592d2020-06-16 15:29:47 +00001420 disk_format = image_dict["disk_format"]
1421 else: # autodiscover based on extension
sousaedu80135b92021-02-17 15:05:18 +01001422 if image_dict["location"].endswith(".qcow2"):
tierno1ec592d2020-06-16 15:29:47 +00001423 disk_format = "qcow2"
sousaedu80135b92021-02-17 15:05:18 +01001424 elif image_dict["location"].endswith(".vhd"):
tierno1ec592d2020-06-16 15:29:47 +00001425 disk_format = "vhd"
sousaedu80135b92021-02-17 15:05:18 +01001426 elif image_dict["location"].endswith(".vmdk"):
tierno1ec592d2020-06-16 15:29:47 +00001427 disk_format = "vmdk"
sousaedu80135b92021-02-17 15:05:18 +01001428 elif image_dict["location"].endswith(".vdi"):
tierno1ec592d2020-06-16 15:29:47 +00001429 disk_format = "vdi"
sousaedu80135b92021-02-17 15:05:18 +01001430 elif image_dict["location"].endswith(".iso"):
tierno1ec592d2020-06-16 15:29:47 +00001431 disk_format = "iso"
sousaedu80135b92021-02-17 15:05:18 +01001432 elif image_dict["location"].endswith(".aki"):
tierno1ec592d2020-06-16 15:29:47 +00001433 disk_format = "aki"
sousaedu80135b92021-02-17 15:05:18 +01001434 elif image_dict["location"].endswith(".ari"):
tierno1ec592d2020-06-16 15:29:47 +00001435 disk_format = "ari"
sousaedu80135b92021-02-17 15:05:18 +01001436 elif image_dict["location"].endswith(".ami"):
tierno1ec592d2020-06-16 15:29:47 +00001437 disk_format = "ami"
tierno7edb6752016-03-21 17:37:52 +01001438 else:
tierno1ec592d2020-06-16 15:29:47 +00001439 disk_format = "raw"
sousaedu80135b92021-02-17 15:05:18 +01001440
1441 self.logger.debug(
1442 "new_image: '%s' loading from '%s'",
1443 image_dict["name"],
1444 image_dict["location"],
1445 )
shashankjain3c83a212018-10-04 13:05:46 +05301446 if self.vim_type == "VIO":
1447 container_format = "bare"
sousaedu80135b92021-02-17 15:05:18 +01001448 if "container_format" in image_dict:
1449 container_format = image_dict["container_format"]
1450
1451 new_image = self.glance.images.create(
1452 name=image_dict["name"],
1453 container_format=container_format,
1454 disk_format=disk_format,
1455 )
shashankjain3c83a212018-10-04 13:05:46 +05301456 else:
sousaedu80135b92021-02-17 15:05:18 +01001457 new_image = self.glance.images.create(name=image_dict["name"])
1458
1459 if image_dict["location"].startswith("http"):
tierno1beea862018-07-11 15:47:37 +02001460 # TODO there is not a method to direct download. It must be downloaded locally with requests
tierno72774862020-05-04 11:44:15 +00001461 raise vimconn.VimConnNotImplemented("Cannot create image from URL")
tierno1ec592d2020-06-16 15:29:47 +00001462 else: # local path
sousaedu80135b92021-02-17 15:05:18 +01001463 with open(image_dict["location"]) as fimage:
tierno1beea862018-07-11 15:47:37 +02001464 self.glance.images.upload(new_image.id, fimage)
sousaedu80135b92021-02-17 15:05:18 +01001465 # new_image = self.glancev1.images.create(name=image_dict["name"], is_public=
1466 # image_dict.get("public","yes")=="yes",
tierno1beea862018-07-11 15:47:37 +02001467 # container_format="bare", data=fimage, disk_format=disk_format)
sousaedu80135b92021-02-17 15:05:18 +01001468
1469 metadata_to_load = image_dict.get("metadata")
1470
1471 # TODO location is a reserved word for current openstack versions. fixed for VIO please check
tierno1ec592d2020-06-16 15:29:47 +00001472 # for openstack
shashankjain3c83a212018-10-04 13:05:46 +05301473 if self.vim_type == "VIO":
sousaedu80135b92021-02-17 15:05:18 +01001474 metadata_to_load["upload_location"] = image_dict["location"]
shashankjain3c83a212018-10-04 13:05:46 +05301475 else:
sousaedu80135b92021-02-17 15:05:18 +01001476 metadata_to_load["location"] = image_dict["location"]
1477
tierno1beea862018-07-11 15:47:37 +02001478 self.glance.images.update(new_image.id, **metadata_to_load)
sousaedu80135b92021-02-17 15:05:18 +01001479
tiernoae4a8d12016-07-08 12:30:39 +02001480 return new_image.id
sousaedu80135b92021-02-17 15:05:18 +01001481 except (
1482 nvExceptions.Conflict,
1483 ksExceptions.ClientException,
1484 nvExceptions.ClientException,
1485 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001486 self._format_exception(e)
sousaedu80135b92021-02-17 15:05:18 +01001487 except (
1488 HTTPException,
1489 gl1Exceptions.HTTPException,
1490 gl1Exceptions.CommunicationError,
1491 ConnectionError,
1492 ) as e:
tierno1ec592d2020-06-16 15:29:47 +00001493 if retry == max_retries:
tiernoae4a8d12016-07-08 12:30:39 +02001494 continue
sousaedu80135b92021-02-17 15:05:18 +01001495
tiernoae4a8d12016-07-08 12:30:39 +02001496 self._format_exception(e)
tierno1ec592d2020-06-16 15:29:47 +00001497 except IOError as e: # can not open the file
sousaedu80135b92021-02-17 15:05:18 +01001498 raise vimconn.VimConnConnectionException(
1499 "{}: {} for {}".format(type(e).__name__, e, image_dict["location"]),
1500 http_code=vimconn.HTTP_Bad_Request,
1501 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001502
tiernoae4a8d12016-07-08 12:30:39 +02001503 def delete_image(self, image_id):
sousaedu80135b92021-02-17 15:05:18 +01001504 """Deletes a tenant image from openstack VIM. Returns the old id"""
tiernoae4a8d12016-07-08 12:30:39 +02001505 try:
1506 self._reload_connection()
tierno1beea862018-07-11 15:47:37 +02001507 self.glance.images.delete(image_id)
sousaedu80135b92021-02-17 15:05:18 +01001508
tiernoae4a8d12016-07-08 12:30:39 +02001509 return image_id
sousaedu80135b92021-02-17 15:05:18 +01001510 except (
1511 nvExceptions.NotFound,
1512 ksExceptions.ClientException,
1513 nvExceptions.ClientException,
1514 gl1Exceptions.CommunicationError,
1515 gl1Exceptions.HTTPNotFound,
1516 ConnectionError,
1517 ) as e: # TODO remove
tiernoae4a8d12016-07-08 12:30:39 +02001518 self._format_exception(e)
1519
1520 def get_image_id_from_path(self, path):
tierno1ec592d2020-06-16 15:29:47 +00001521 """Get the image id from image path in the VIM database. Returns the image_id"""
tiernoae4a8d12016-07-08 12:30:39 +02001522 try:
1523 self._reload_connection()
tierno1beea862018-07-11 15:47:37 +02001524 images = self.glance.images.list()
sousaedu80135b92021-02-17 15:05:18 +01001525
tiernoae4a8d12016-07-08 12:30:39 +02001526 for image in images:
tierno1ec592d2020-06-16 15:29:47 +00001527 if image.metadata.get("location") == path:
tiernoae4a8d12016-07-08 12:30:39 +02001528 return image.id
sousaedu80135b92021-02-17 15:05:18 +01001529
1530 raise vimconn.VimConnNotFoundException(
1531 "image with location '{}' not found".format(path)
1532 )
1533 except (
1534 ksExceptions.ClientException,
1535 nvExceptions.ClientException,
1536 gl1Exceptions.CommunicationError,
1537 ConnectionError,
1538 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001539 self._format_exception(e)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001540
garciadeblasb69fa9f2016-09-28 12:04:10 +02001541 def get_image_list(self, filter_dict={}):
tierno1ec592d2020-06-16 15:29:47 +00001542 """Obtain tenant images from VIM
garciadeblasb69fa9f2016-09-28 12:04:10 +02001543 Filter_dict can be:
1544 id: image id
1545 name: image name
1546 checksum: image checksum
1547 Returns the image list of dictionaries:
1548 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1549 List can be empty
tierno1ec592d2020-06-16 15:29:47 +00001550 """
garciadeblasb69fa9f2016-09-28 12:04:10 +02001551 self.logger.debug("Getting image list from VIM filter: '%s'", str(filter_dict))
sousaedu80135b92021-02-17 15:05:18 +01001552
garciadeblasb69fa9f2016-09-28 12:04:10 +02001553 try:
1554 self._reload_connection()
tierno1ec592d2020-06-16 15:29:47 +00001555 # filter_dict_os = filter_dict.copy()
1556 # First we filter by the available filter fields: name, id. The others are removed.
tierno1beea862018-07-11 15:47:37 +02001557 image_list = self.glance.images.list()
garciadeblasb69fa9f2016-09-28 12:04:10 +02001558 filtered_list = []
sousaedu80135b92021-02-17 15:05:18 +01001559
garciadeblasb69fa9f2016-09-28 12:04:10 +02001560 for image in image_list:
tierno3cb8dc32017-10-24 18:13:19 +02001561 try:
tierno1beea862018-07-11 15:47:37 +02001562 if filter_dict.get("name") and image["name"] != filter_dict["name"]:
1563 continue
sousaedu80135b92021-02-17 15:05:18 +01001564
tierno1beea862018-07-11 15:47:37 +02001565 if filter_dict.get("id") and image["id"] != filter_dict["id"]:
1566 continue
sousaedu80135b92021-02-17 15:05:18 +01001567
1568 if (
1569 filter_dict.get("checksum")
1570 and image["checksum"] != filter_dict["checksum"]
1571 ):
tierno1beea862018-07-11 15:47:37 +02001572 continue
1573
1574 filtered_list.append(image.copy())
tierno3cb8dc32017-10-24 18:13:19 +02001575 except gl1Exceptions.HTTPNotFound:
1576 pass
sousaedu80135b92021-02-17 15:05:18 +01001577
garciadeblasb69fa9f2016-09-28 12:04:10 +02001578 return filtered_list
sousaedu80135b92021-02-17 15:05:18 +01001579 except (
1580 ksExceptions.ClientException,
1581 nvExceptions.ClientException,
1582 gl1Exceptions.CommunicationError,
1583 ConnectionError,
1584 ) as e:
garciadeblasb69fa9f2016-09-28 12:04:10 +02001585 self._format_exception(e)
1586
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001587 def __wait_for_vm(self, vm_id, status):
1588 """wait until vm is in the desired status and return True.
1589 If the VM gets in ERROR status, return false.
1590 If the timeout is reached generate an exception"""
1591 elapsed_time = 0
1592 while elapsed_time < server_timeout:
1593 vm_status = self.nova.servers.get(vm_id).status
sousaedu80135b92021-02-17 15:05:18 +01001594
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001595 if vm_status == status:
1596 return True
sousaedu80135b92021-02-17 15:05:18 +01001597
1598 if vm_status == "ERROR":
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001599 return False
sousaedu80135b92021-02-17 15:05:18 +01001600
tierno1df468d2018-07-06 14:25:16 +02001601 time.sleep(5)
1602 elapsed_time += 5
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001603
1604 # if we exceeded the timeout rollback
1605 if elapsed_time >= server_timeout:
sousaedu80135b92021-02-17 15:05:18 +01001606 raise vimconn.VimConnException(
1607 "Timeout waiting for instance " + vm_id + " to get " + status,
1608 http_code=vimconn.HTTP_Request_Timeout,
1609 )
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001610
mirabal29356312017-07-27 12:21:22 +02001611 def _get_openstack_availablity_zones(self):
1612 """
1613 Get from openstack availability zones available
1614 :return:
1615 """
1616 try:
1617 openstack_availability_zone = self.nova.availability_zones.list()
sousaedu80135b92021-02-17 15:05:18 +01001618 openstack_availability_zone = [
1619 str(zone.zoneName)
1620 for zone in openstack_availability_zone
1621 if zone.zoneName != "internal"
1622 ]
1623
mirabal29356312017-07-27 12:21:22 +02001624 return openstack_availability_zone
tierno1ec592d2020-06-16 15:29:47 +00001625 except Exception:
mirabal29356312017-07-27 12:21:22 +02001626 return None
1627
1628 def _set_availablity_zones(self):
1629 """
1630 Set vim availablity zone
1631 :return:
1632 """
sousaedu80135b92021-02-17 15:05:18 +01001633 if "availability_zone" in self.config:
1634 vim_availability_zones = self.config.get("availability_zone")
mirabal29356312017-07-27 12:21:22 +02001635
mirabal29356312017-07-27 12:21:22 +02001636 if isinstance(vim_availability_zones, str):
1637 self.availability_zone = [vim_availability_zones]
1638 elif isinstance(vim_availability_zones, list):
1639 self.availability_zone = vim_availability_zones
1640 else:
1641 self.availability_zone = self._get_openstack_availablity_zones()
1642
sousaedu80135b92021-02-17 15:05:18 +01001643 def _get_vm_availability_zone(
1644 self, availability_zone_index, availability_zone_list
1645 ):
mirabal29356312017-07-27 12:21:22 +02001646 """
tierno5a3273c2017-08-29 11:43:46 +02001647 Return thge availability zone to be used by the created VM.
1648 :return: The VIM availability zone to be used or None
mirabal29356312017-07-27 12:21:22 +02001649 """
tierno5a3273c2017-08-29 11:43:46 +02001650 if availability_zone_index is None:
sousaedu80135b92021-02-17 15:05:18 +01001651 if not self.config.get("availability_zone"):
tierno5a3273c2017-08-29 11:43:46 +02001652 return None
sousaedu80135b92021-02-17 15:05:18 +01001653 elif isinstance(self.config.get("availability_zone"), str):
1654 return self.config["availability_zone"]
tierno5a3273c2017-08-29 11:43:46 +02001655 else:
1656 # TODO consider using a different parameter at config for default AV and AV list match
sousaedu80135b92021-02-17 15:05:18 +01001657 return self.config["availability_zone"][0]
mirabal29356312017-07-27 12:21:22 +02001658
tierno5a3273c2017-08-29 11:43:46 +02001659 vim_availability_zones = self.availability_zone
1660 # check if VIM offer enough availability zones describe in the VNFD
sousaedu80135b92021-02-17 15:05:18 +01001661 if vim_availability_zones and len(availability_zone_list) <= len(
1662 vim_availability_zones
1663 ):
tierno5a3273c2017-08-29 11:43:46 +02001664 # check if all the names of NFV AV match VIM AV names
1665 match_by_index = False
1666 for av in availability_zone_list:
1667 if av not in vim_availability_zones:
1668 match_by_index = True
1669 break
sousaedu80135b92021-02-17 15:05:18 +01001670
tierno5a3273c2017-08-29 11:43:46 +02001671 if match_by_index:
1672 return vim_availability_zones[availability_zone_index]
1673 else:
1674 return availability_zone_list[availability_zone_index]
mirabal29356312017-07-27 12:21:22 +02001675 else:
sousaedu80135b92021-02-17 15:05:18 +01001676 raise vimconn.VimConnConflictException(
1677 "No enough availability zones at VIM for this deployment"
1678 )
mirabal29356312017-07-27 12:21:22 +02001679
sousaedu80135b92021-02-17 15:05:18 +01001680 def new_vminstance(
1681 self,
1682 name,
1683 description,
1684 start,
1685 image_id,
1686 flavor_id,
1687 net_list,
1688 cloud_config=None,
1689 disk_list=None,
1690 availability_zone_index=None,
1691 availability_zone_list=None,
1692 ):
tierno98e909c2017-10-14 13:27:03 +02001693 """Adds a VM instance to VIM
tierno7edb6752016-03-21 17:37:52 +01001694 Params:
1695 start: indicates if VM must start or boot in pause mode. Ignored
1696 image_id,flavor_id: iamge and flavor uuid
1697 net_list: list of interfaces, each one is a dictionary with:
1698 name:
1699 net_id: network uuid to connect
1700 vpci: virtual vcpi to assign, ignored because openstack lack #TODO
1701 model: interface model, ignored #TODO
1702 mac_address: used for SR-IOV ifaces #TODO for other types
1703 use: 'data', 'bridge', 'mgmt'
tierno66eba6e2017-11-10 17:09:18 +01001704 type: 'virtual', 'PCI-PASSTHROUGH'('PF'), 'SR-IOV'('VF'), 'VFnotShared'
tierno7edb6752016-03-21 17:37:52 +01001705 vim_id: filled/added by this function
ahmadsaf853d452016-12-22 11:33:47 +05001706 floating_ip: True/False (or it can be None)
tierno70eeb182020-10-19 16:38:00 +00001707 port_security: True/False
tierno41a69812018-02-16 14:34:33 +01001708 'cloud_config': (optional) dictionary with:
tierno1d213f42020-04-24 14:02:51 +00001709 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
1710 'users': (optional) list of users to be inserted, each item is a dict with:
1711 'name': (mandatory) user name,
1712 'key-pairs': (optional) list of strings with the public key to be inserted to the user
1713 'user-data': (optional) string is a text script to be passed directly to cloud-init
1714 'config-files': (optional). List of files to be transferred. Each item is a dict with:
1715 'dest': (mandatory) string with the destination absolute path
1716 'encoding': (optional, by default text). Can be one of:
1717 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
1718 'content' (mandatory): string with the content of the file
1719 'permissions': (optional) string with file permissions, typically octal notation '0644'
1720 'owner': (optional) file owner, string with the format 'owner:group'
1721 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk)
mirabal29356312017-07-27 12:21:22 +02001722 'disk_list': (optional) list with additional disks to the VM. Each item is a dict with:
1723 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted
1724 'size': (mandatory) string with the size of the disk in GB
tierno1df468d2018-07-06 14:25:16 +02001725 'vim_id' (optional) should use this existing volume id
tierno5a3273c2017-08-29 11:43:46 +02001726 availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV required
1727 availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if
1728 availability_zone_index is None
tierno7edb6752016-03-21 17:37:52 +01001729 #TODO ip, security groups
tierno98e909c2017-10-14 13:27:03 +02001730 Returns a tuple with the instance identifier and created_items or raises an exception on error
1731 created_items can be None or a dictionary where this method can include key-values that will be passed to
1732 the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc.
1733 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
1734 as not present.
1735 """
sousaedu80135b92021-02-17 15:05:18 +01001736 self.logger.debug(
1737 "new_vminstance input: image='%s' flavor='%s' nics='%s'",
1738 image_id,
1739 flavor_id,
1740 str(net_list),
1741 )
1742
tierno7edb6752016-03-21 17:37:52 +01001743 try:
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001744 server = None
tierno98e909c2017-10-14 13:27:03 +02001745 created_items = {}
tiernob0b9dab2017-10-14 14:25:20 +02001746 # metadata = {}
tierno98e909c2017-10-14 13:27:03 +02001747 net_list_vim = []
tierno1ec592d2020-06-16 15:29:47 +00001748 external_network = []
1749 # ^list of external networks to be connected to instance, later on used to create floating_ip
sousaedu80135b92021-02-17 15:05:18 +01001750 no_secured_ports = [] # List of port-is with port-security disabled
tierno7edb6752016-03-21 17:37:52 +01001751 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +01001752 # metadata_vpci = {} # For a specific neutron plugin
tiernob84cbdc2017-07-07 14:30:30 +02001753 block_device_mapping = None
tiernoa05b65a2019-02-01 12:30:27 +00001754
tierno7edb6752016-03-21 17:37:52 +01001755 for net in net_list:
sousaedu80135b92021-02-17 15:05:18 +01001756 if not net.get("net_id"): # skip non connected iface
tierno7edb6752016-03-21 17:37:52 +01001757 continue
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001758
tiernoa05b65a2019-02-01 12:30:27 +00001759 port_dict = {
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001760 "network_id": net["net_id"],
1761 "name": net.get("name"),
sousaedu80135b92021-02-17 15:05:18 +01001762 "admin_state_up": True,
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001763 }
sousaedu80135b92021-02-17 15:05:18 +01001764
1765 if (
1766 self.config.get("security_groups")
1767 and net.get("port_security") is not False
1768 and not self.config.get("no_port_security_extension")
1769 ):
tiernoa05b65a2019-02-01 12:30:27 +00001770 if not self.security_groups_id:
1771 self._get_ids_from_name()
sousaedu80135b92021-02-17 15:05:18 +01001772
tiernoa05b65a2019-02-01 12:30:27 +00001773 port_dict["security_groups"] = self.security_groups_id
1774
tierno1ec592d2020-06-16 15:29:47 +00001775 if net["type"] == "virtual":
tiernob0b9dab2017-10-14 14:25:20 +02001776 pass
1777 # if "vpci" in net:
1778 # metadata_vpci[ net["net_id"] ] = [[ net["vpci"], "" ]]
tierno66eba6e2017-11-10 17:09:18 +01001779 elif net["type"] == "VF" or net["type"] == "SR-IOV": # for VF
tiernob0b9dab2017-10-14 14:25:20 +02001780 # if "vpci" in net:
1781 # if "VF" not in metadata_vpci:
1782 # metadata_vpci["VF"]=[]
1783 # metadata_vpci["VF"].append([ net["vpci"], "" ])
tierno1ec592d2020-06-16 15:29:47 +00001784 port_dict["binding:vnic_type"] = "direct"
sousaedu80135b92021-02-17 15:05:18 +01001785
tiernob0b9dab2017-10-14 14:25:20 +02001786 # VIO specific Changes
kate721d79b2017-06-24 04:21:38 -07001787 if self.vim_type == "VIO":
tiernob0b9dab2017-10-14 14:25:20 +02001788 # Need to create port with port_security_enabled = False and no-security-groups
tierno1ec592d2020-06-16 15:29:47 +00001789 port_dict["port_security_enabled"] = False
1790 port_dict["provider_security_groups"] = []
1791 port_dict["security_groups"] = []
sousaedu80135b92021-02-17 15:05:18 +01001792 else: # For PT PCI-PASSTHROUGH
tiernob0b9dab2017-10-14 14:25:20 +02001793 # if "vpci" in net:
1794 # if "PF" not in metadata_vpci:
1795 # metadata_vpci["PF"]=[]
1796 # metadata_vpci["PF"].append([ net["vpci"], "" ])
tierno1ec592d2020-06-16 15:29:47 +00001797 port_dict["binding:vnic_type"] = "direct-physical"
sousaedu80135b92021-02-17 15:05:18 +01001798
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001799 if not port_dict["name"]:
tierno1ec592d2020-06-16 15:29:47 +00001800 port_dict["name"] = name
sousaedu80135b92021-02-17 15:05:18 +01001801
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001802 if net.get("mac_address"):
tierno1ec592d2020-06-16 15:29:47 +00001803 port_dict["mac_address"] = net["mac_address"]
sousaedu80135b92021-02-17 15:05:18 +01001804
tierno41a69812018-02-16 14:34:33 +01001805 if net.get("ip_address"):
sousaedu80135b92021-02-17 15:05:18 +01001806 port_dict["fixed_ips"] = [{"ip_address": net["ip_address"]}]
1807 # TODO add "subnet_id": <subnet_id>
1808
tierno1ec592d2020-06-16 15:29:47 +00001809 new_port = self.neutron.create_port({"port": port_dict})
tierno00e3df72017-11-29 17:20:13 +01001810 created_items["port:" + str(new_port["port"]["id"])] = True
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001811 net["mac_adress"] = new_port["port"]["mac_address"]
1812 net["vim_id"] = new_port["port"]["id"]
tiernob84cbdc2017-07-07 14:30:30 +02001813 # if try to use a network without subnetwork, it will return a emtpy list
1814 fixed_ips = new_port["port"].get("fixed_ips")
sousaedu80135b92021-02-17 15:05:18 +01001815
tiernob84cbdc2017-07-07 14:30:30 +02001816 if fixed_ips:
1817 net["ip"] = fixed_ips[0].get("ip_address")
1818 else:
1819 net["ip"] = None
montesmoreno994a29d2017-08-22 11:23:06 +02001820
1821 port = {"port-id": new_port["port"]["id"]}
1822 if float(self.nova.api_version.get_string()) >= 2.32:
1823 port["tag"] = new_port["port"]["name"]
sousaedu80135b92021-02-17 15:05:18 +01001824
montesmoreno994a29d2017-08-22 11:23:06 +02001825 net_list_vim.append(port)
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001826
sousaedu80135b92021-02-17 15:05:18 +01001827 if net.get("floating_ip", False):
1828 net["exit_on_floating_ip_error"] = True
ahmadsaf853d452016-12-22 11:33:47 +05001829 external_network.append(net)
sousaedu80135b92021-02-17 15:05:18 +01001830 elif net["use"] == "mgmt" and self.config.get("use_floating_ip"):
1831 net["exit_on_floating_ip_error"] = False
tiernof8383b82017-01-18 15:49:48 +01001832 external_network.append(net)
sousaedu80135b92021-02-17 15:05:18 +01001833 net["floating_ip"] = self.config.get("use_floating_ip")
tiernof8383b82017-01-18 15:49:48 +01001834
tierno1ec592d2020-06-16 15:29:47 +00001835 # If port security is disabled when the port has not yet been attached to the VM, then all vm traffic
1836 # is dropped.
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001837 # As a workaround we wait until the VM is active and then disable the port-security
sousaedu80135b92021-02-17 15:05:18 +01001838 if net.get("port_security") is False and not self.config.get(
1839 "no_port_security_extension"
1840 ):
1841 no_secured_ports.append(
1842 (
1843 new_port["port"]["id"],
1844 net.get("port_security_disable_strategy"),
1845 )
1846 )
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001847
tiernob0b9dab2017-10-14 14:25:20 +02001848 # if metadata_vpci:
1849 # metadata = {"pci_assignement": json.dumps(metadata_vpci)}
1850 # if len(metadata["pci_assignement"]) >255:
1851 # #limit the metadata size
1852 # #metadata["pci_assignement"] = metadata["pci_assignement"][0:255]
1853 # self.logger.warn("Metadata deleted since it exceeds the expected length (255) ")
1854 # metadata = {}
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001855
sousaedu80135b92021-02-17 15:05:18 +01001856 self.logger.debug(
1857 "name '%s' image_id '%s'flavor_id '%s' net_list_vim '%s' description '%s'",
1858 name,
1859 image_id,
1860 flavor_id,
1861 str(net_list_vim),
1862 description,
1863 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001864
tierno98e909c2017-10-14 13:27:03 +02001865 # cloud config
tierno0a1437e2017-10-02 00:17:43 +02001866 config_drive, userdata = self._create_user_data(cloud_config)
montesmoreno0c8def02016-12-22 12:16:23 +00001867
tierno98e909c2017-10-14 13:27:03 +02001868 # Create additional volumes in case these are present in disk_list
sousaedu80135b92021-02-17 15:05:18 +01001869 base_disk_index = ord("b")
tierno1df468d2018-07-06 14:25:16 +02001870 if disk_list:
tiernob84cbdc2017-07-07 14:30:30 +02001871 block_device_mapping = {}
montesmoreno0c8def02016-12-22 12:16:23 +00001872 for disk in disk_list:
sousaedu80135b92021-02-17 15:05:18 +01001873 if disk.get("vim_id"):
1874 block_device_mapping["_vd" + chr(base_disk_index)] = disk[
1875 "vim_id"
1876 ]
montesmoreno0c8def02016-12-22 12:16:23 +00001877 else:
sousaedu80135b92021-02-17 15:05:18 +01001878 if "image_id" in disk:
1879 volume = self.cinder.volumes.create(
1880 size=disk["size"],
1881 name=name + "_vd" + chr(base_disk_index),
1882 imageRef=disk["image_id"],
1883 )
tierno1df468d2018-07-06 14:25:16 +02001884 else:
sousaedu80135b92021-02-17 15:05:18 +01001885 volume = self.cinder.volumes.create(
1886 size=disk["size"],
1887 name=name + "_vd" + chr(base_disk_index),
1888 )
1889
tierno1df468d2018-07-06 14:25:16 +02001890 created_items["volume:" + str(volume.id)] = True
sousaedu80135b92021-02-17 15:05:18 +01001891 block_device_mapping["_vd" + chr(base_disk_index)] = volume.id
1892
montesmoreno0c8def02016-12-22 12:16:23 +00001893 base_disk_index += 1
1894
tierno1df468d2018-07-06 14:25:16 +02001895 # Wait until created volumes are with status available
montesmoreno0c8def02016-12-22 12:16:23 +00001896 elapsed_time = 0
tierno1df468d2018-07-06 14:25:16 +02001897 while elapsed_time < volume_timeout:
1898 for created_item in created_items:
1899 v, _, volume_id = created_item.partition(":")
sousaedu80135b92021-02-17 15:05:18 +01001900 if v == "volume":
1901 if self.cinder.volumes.get(volume_id).status != "available":
tierno1df468d2018-07-06 14:25:16 +02001902 break
1903 else: # all ready: break from while
1904 break
sousaedu80135b92021-02-17 15:05:18 +01001905
tierno1df468d2018-07-06 14:25:16 +02001906 time.sleep(5)
1907 elapsed_time += 5
sousaedu80135b92021-02-17 15:05:18 +01001908
tiernob0b9dab2017-10-14 14:25:20 +02001909 # If we exceeded the timeout rollback
montesmoreno0c8def02016-12-22 12:16:23 +00001910 if elapsed_time >= volume_timeout:
sousaedu80135b92021-02-17 15:05:18 +01001911 raise vimconn.VimConnException(
1912 "Timeout creating volumes for instance " + name,
1913 http_code=vimconn.HTTP_Request_Timeout,
1914 )
montesmoreno0c8def02016-12-22 12:16:23 +00001915
sousaedu80135b92021-02-17 15:05:18 +01001916 # get availability Zone
1917 vm_av_zone = self._get_vm_availability_zone(
1918 availability_zone_index, availability_zone_list
1919 )
1920
1921 self.logger.debug(
1922 "nova.servers.create({}, {}, {}, nics={}, security_groups={}, "
1923 "availability_zone={}, key_name={}, userdata={}, config_drive={}, "
1924 "block_device_mapping={})".format(
1925 name,
1926 image_id,
1927 flavor_id,
1928 net_list_vim,
1929 self.config.get("security_groups"),
1930 vm_av_zone,
1931 self.config.get("keypair"),
1932 userdata,
1933 config_drive,
1934 block_device_mapping,
1935 )
1936 )
1937 server = self.nova.servers.create(
1938 name,
1939 image_id,
1940 flavor_id,
1941 nics=net_list_vim,
1942 security_groups=self.config.get("security_groups"),
1943 # TODO remove security_groups in future versions. Already at neutron port
1944 availability_zone=vm_av_zone,
1945 key_name=self.config.get("keypair"),
1946 userdata=userdata,
1947 config_drive=config_drive,
1948 block_device_mapping=block_device_mapping,
1949 ) # , description=description)
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001950
tierno326fd5e2018-02-22 11:58:59 +01001951 vm_start_time = time.time()
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001952 # Previously mentioned workaround to wait until the VM is active and then disable the port-security
1953 if no_secured_ports:
sousaedu80135b92021-02-17 15:05:18 +01001954 self.__wait_for_vm(server.id, "ACTIVE")
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001955
bravof7a1f5252020-10-20 10:27:42 -03001956 for port in no_secured_ports:
1957 port_update = {
sousaedu80135b92021-02-17 15:05:18 +01001958 "port": {"port_security_enabled": False, "security_groups": None}
bravof7a1f5252020-10-20 10:27:42 -03001959 }
1960
1961 if port[1] == "allow-address-pairs":
1962 port_update = {
sousaedu80135b92021-02-17 15:05:18 +01001963 "port": {"allowed_address_pairs": [{"ip_address": "0.0.0.0/0"}]}
bravof7a1f5252020-10-20 10:27:42 -03001964 }
1965
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001966 try:
bravof7a1f5252020-10-20 10:27:42 -03001967 self.neutron.update_port(port[0], port_update)
tierno1ec592d2020-06-16 15:29:47 +00001968 except Exception:
bravof7a1f5252020-10-20 10:27:42 -03001969 raise vimconn.VimConnException(
sousaedu80135b92021-02-17 15:05:18 +01001970 "It was not possible to disable port security for port {}".format(
1971 port[0]
1972 )
bravof7a1f5252020-10-20 10:27:42 -03001973 )
1974
tierno98e909c2017-10-14 13:27:03 +02001975 # print "DONE :-)", server
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001976
tierno4d1ce222018-04-06 10:41:06 +02001977 # pool_id = None
ahmadsaf853d452016-12-22 11:33:47 +05001978 for floating_network in external_network:
tiernof8383b82017-01-18 15:49:48 +01001979 try:
tiernof8383b82017-01-18 15:49:48 +01001980 assigned = False
tiernocb66c7e2020-07-22 10:42:58 +00001981 floating_ip_retries = 3
1982 # In case of RO in HA there can be conflicts, two RO trying to assign same floating IP, so retry
1983 # several times
tierno98e909c2017-10-14 13:27:03 +02001984 while not assigned:
sousaedu80135b92021-02-17 15:05:18 +01001985 floating_ips = self.neutron.list_floatingips().get(
1986 "floatingips", ()
1987 )
1988 random.shuffle(floating_ips) # randomize
tiernocb66c7e2020-07-22 10:42:58 +00001989 for fip in floating_ips:
sousaedu80135b92021-02-17 15:05:18 +01001990 if (
1991 fip.get("port_id")
1992 or fip.get("tenant_id") != server.tenant_id
1993 ):
tierno326fd5e2018-02-22 11:58:59 +01001994 continue
sousaedu80135b92021-02-17 15:05:18 +01001995
1996 if isinstance(floating_network["floating_ip"], str):
1997 if (
1998 fip.get("floating_network_id")
1999 != floating_network["floating_ip"]
2000 ):
tierno326fd5e2018-02-22 11:58:59 +01002001 continue
sousaedu80135b92021-02-17 15:05:18 +01002002
tiernocb66c7e2020-07-22 10:42:58 +00002003 free_floating_ip = fip["id"]
2004 break
tiernof8383b82017-01-18 15:49:48 +01002005 else:
sousaedu80135b92021-02-17 15:05:18 +01002006 if (
2007 isinstance(floating_network["floating_ip"], str)
2008 and floating_network["floating_ip"].lower() != "true"
2009 ):
2010 pool_id = floating_network["floating_ip"]
tierno326fd5e2018-02-22 11:58:59 +01002011 else:
tierno4d1ce222018-04-06 10:41:06 +02002012 # Find the external network
tierno326fd5e2018-02-22 11:58:59 +01002013 external_nets = list()
sousaedu80135b92021-02-17 15:05:18 +01002014
2015 for net in self.neutron.list_networks()["networks"]:
2016 if net["router:external"]:
tierno1ec592d2020-06-16 15:29:47 +00002017 external_nets.append(net)
tiernof8383b82017-01-18 15:49:48 +01002018
tierno326fd5e2018-02-22 11:58:59 +01002019 if len(external_nets) == 0:
tierno1ec592d2020-06-16 15:29:47 +00002020 raise vimconn.VimConnException(
sousaedu80135b92021-02-17 15:05:18 +01002021 "Cannot create floating_ip automatically since "
2022 "no external network is present",
2023 http_code=vimconn.HTTP_Conflict,
2024 )
2025
tierno326fd5e2018-02-22 11:58:59 +01002026 if len(external_nets) > 1:
tierno1ec592d2020-06-16 15:29:47 +00002027 raise vimconn.VimConnException(
sousaedu80135b92021-02-17 15:05:18 +01002028 "Cannot create floating_ip automatically since "
2029 "multiple external networks are present",
2030 http_code=vimconn.HTTP_Conflict,
2031 )
tiernof8383b82017-01-18 15:49:48 +01002032
sousaedu80135b92021-02-17 15:05:18 +01002033 pool_id = external_nets[0].get("id")
2034
2035 param = {
2036 "floatingip": {
2037 "floating_network_id": pool_id,
2038 "tenant_id": server.tenant_id,
2039 }
2040 }
2041
ahmadsaf853d452016-12-22 11:33:47 +05002042 try:
tierno4d1ce222018-04-06 10:41:06 +02002043 # self.logger.debug("Creating floating IP")
tiernof8383b82017-01-18 15:49:48 +01002044 new_floating_ip = self.neutron.create_floatingip(param)
sousaedu80135b92021-02-17 15:05:18 +01002045 free_floating_ip = new_floating_ip["floatingip"]["id"]
2046 created_items[
2047 "floating_ip:" + str(free_floating_ip)
2048 ] = True
ahmadsaf853d452016-12-22 11:33:47 +05002049 except Exception as e:
sousaedu80135b92021-02-17 15:05:18 +01002050 raise vimconn.VimConnException(
2051 type(e).__name__
2052 + ": Cannot create new floating_ip "
2053 + str(e),
2054 http_code=vimconn.HTTP_Conflict,
2055 )
tierno326fd5e2018-02-22 11:58:59 +01002056
tiernocb66c7e2020-07-22 10:42:58 +00002057 try:
2058 # for race condition ensure not already assigned
2059 fip = self.neutron.show_floatingip(free_floating_ip)
sousaedu80135b92021-02-17 15:05:18 +01002060
2061 if fip["floatingip"]["port_id"]:
tiernocb66c7e2020-07-22 10:42:58 +00002062 continue
sousaedu80135b92021-02-17 15:05:18 +01002063
tiernocb66c7e2020-07-22 10:42:58 +00002064 # the vim_id key contains the neutron.port_id
sousaedu80135b92021-02-17 15:05:18 +01002065 self.neutron.update_floatingip(
2066 free_floating_ip,
2067 {"floatingip": {"port_id": floating_network["vim_id"]}},
2068 )
tiernocb66c7e2020-07-22 10:42:58 +00002069 # for race condition ensure not re-assigned to other VM after 5 seconds
2070 time.sleep(5)
2071 fip = self.neutron.show_floatingip(free_floating_ip)
sousaedu80135b92021-02-17 15:05:18 +01002072
2073 if (
2074 fip["floatingip"]["port_id"]
2075 != floating_network["vim_id"]
2076 ):
2077 self.logger.error(
2078 "floating_ip {} re-assigned to other port".format(
2079 free_floating_ip
2080 )
2081 )
tiernocb66c7e2020-07-22 10:42:58 +00002082 continue
sousaedu80135b92021-02-17 15:05:18 +01002083
2084 self.logger.debug(
2085 "Assigned floating_ip {} to VM {}".format(
2086 free_floating_ip, server.id
2087 )
2088 )
tiernocb66c7e2020-07-22 10:42:58 +00002089 assigned = True
2090 except Exception as e:
2091 # openstack need some time after VM creation to assign an IP. So retry if fails
2092 vm_status = self.nova.servers.get(server.id).status
sousaedu80135b92021-02-17 15:05:18 +01002093
2094 if vm_status not in ("ACTIVE", "ERROR"):
tiernocb66c7e2020-07-22 10:42:58 +00002095 if time.time() - vm_start_time < server_timeout:
2096 time.sleep(5)
2097 continue
2098 elif floating_ip_retries > 0:
2099 floating_ip_retries -= 1
2100 continue
sousaedu80135b92021-02-17 15:05:18 +01002101
tiernocb66c7e2020-07-22 10:42:58 +00002102 raise vimconn.VimConnException(
sousaedu80135b92021-02-17 15:05:18 +01002103 "Cannot create floating_ip: {} {}".format(
2104 type(e).__name__, e
2105 ),
2106 http_code=vimconn.HTTP_Conflict,
2107 )
tierno326fd5e2018-02-22 11:58:59 +01002108
tiernof8383b82017-01-18 15:49:48 +01002109 except Exception as e:
sousaedu80135b92021-02-17 15:05:18 +01002110 if not floating_network["exit_on_floating_ip_error"]:
tiernocb66c7e2020-07-22 10:42:58 +00002111 self.logger.error("Cannot create floating_ip. %s", str(e))
tiernof8383b82017-01-18 15:49:48 +01002112 continue
sousaedu80135b92021-02-17 15:05:18 +01002113
tiernof8383b82017-01-18 15:49:48 +01002114 raise
montesmoreno2a1fc4e2017-01-09 16:46:04 +00002115
tierno98e909c2017-10-14 13:27:03 +02002116 return server.id, created_items
tierno1ec592d2020-06-16 15:29:47 +00002117 # except nvExceptions.NotFound as e:
2118 # error_value=-vimconn.HTTP_Not_Found
2119 # error_text= "vm instance %s not found" % vm_id
2120 # except TypeError as e:
2121 # raise vimconn.VimConnException(type(e).__name__ + ": "+ str(e), http_code=vimconn.HTTP_Bad_Request)
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02002122
2123 except Exception as e:
tierno98e909c2017-10-14 13:27:03 +02002124 server_id = None
2125 if server:
2126 server_id = server.id
sousaedu80135b92021-02-17 15:05:18 +01002127
tierno98e909c2017-10-14 13:27:03 +02002128 try:
2129 self.delete_vminstance(server_id, created_items)
2130 except Exception as e2:
2131 self.logger.error("new_vminstance rollback fail {}".format(e2))
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02002132
tiernoae4a8d12016-07-08 12:30:39 +02002133 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01002134
tierno1ec592d2020-06-16 15:29:47 +00002135 def get_vminstance(self, vm_id):
2136 """Returns the VM instance information from VIM"""
2137 # self.logger.debug("Getting VM from VIM")
tierno7edb6752016-03-21 17:37:52 +01002138 try:
2139 self._reload_connection()
2140 server = self.nova.servers.find(id=vm_id)
tierno1ec592d2020-06-16 15:29:47 +00002141 # TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
sousaedu80135b92021-02-17 15:05:18 +01002142
tiernoae4a8d12016-07-08 12:30:39 +02002143 return server.to_dict()
sousaedu80135b92021-02-17 15:05:18 +01002144 except (
2145 ksExceptions.ClientException,
2146 nvExceptions.ClientException,
2147 nvExceptions.NotFound,
2148 ConnectionError,
2149 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02002150 self._format_exception(e)
2151
tierno1ec592d2020-06-16 15:29:47 +00002152 def get_vminstance_console(self, vm_id, console_type="vnc"):
2153 """
tierno7edb6752016-03-21 17:37:52 +01002154 Get a console for the virtual machine
2155 Params:
2156 vm_id: uuid of the VM
2157 console_type, can be:
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01002158 "novnc" (by default), "xvpvnc" for VNC types,
tierno7edb6752016-03-21 17:37:52 +01002159 "rdp-html5" for RDP types, "spice-html5" for SPICE types
tiernoae4a8d12016-07-08 12:30:39 +02002160 Returns dict with the console parameters:
2161 protocol: ssh, ftp, http, https, ...
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01002162 server: usually ip address
2163 port: the http, ssh, ... port
2164 suffix: extra text, e.g. the http path and query string
tierno1ec592d2020-06-16 15:29:47 +00002165 """
tiernoae4a8d12016-07-08 12:30:39 +02002166 self.logger.debug("Getting VM CONSOLE from VIM")
sousaedu80135b92021-02-17 15:05:18 +01002167
tierno7edb6752016-03-21 17:37:52 +01002168 try:
2169 self._reload_connection()
2170 server = self.nova.servers.find(id=vm_id)
sousaedu80135b92021-02-17 15:05:18 +01002171
tierno1ec592d2020-06-16 15:29:47 +00002172 if console_type is None or console_type == "novnc":
tierno7edb6752016-03-21 17:37:52 +01002173 console_dict = server.get_vnc_console("novnc")
2174 elif console_type == "xvpvnc":
2175 console_dict = server.get_vnc_console(console_type)
2176 elif console_type == "rdp-html5":
2177 console_dict = server.get_rdp_console(console_type)
2178 elif console_type == "spice-html5":
2179 console_dict = server.get_spice_console(console_type)
2180 else:
sousaedu80135b92021-02-17 15:05:18 +01002181 raise vimconn.VimConnException(
2182 "console type '{}' not allowed".format(console_type),
2183 http_code=vimconn.HTTP_Bad_Request,
2184 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002185
tierno7edb6752016-03-21 17:37:52 +01002186 console_dict1 = console_dict.get("console")
sousaedu80135b92021-02-17 15:05:18 +01002187
tierno7edb6752016-03-21 17:37:52 +01002188 if console_dict1:
2189 console_url = console_dict1.get("url")
sousaedu80135b92021-02-17 15:05:18 +01002190
tierno7edb6752016-03-21 17:37:52 +01002191 if console_url:
tierno1ec592d2020-06-16 15:29:47 +00002192 # parse console_url
tierno7edb6752016-03-21 17:37:52 +01002193 protocol_index = console_url.find("//")
sousaedu80135b92021-02-17 15:05:18 +01002194 suffix_index = (
2195 console_url[protocol_index + 2 :].find("/") + protocol_index + 2
2196 )
2197 port_index = (
2198 console_url[protocol_index + 2 : suffix_index].find(":")
2199 + protocol_index
2200 + 2
2201 )
2202
tierno1ec592d2020-06-16 15:29:47 +00002203 if protocol_index < 0 or port_index < 0 or suffix_index < 0:
sousaedu80135b92021-02-17 15:05:18 +01002204 return (
2205 -vimconn.HTTP_Internal_Server_Error,
2206 "Unexpected response from VIM",
2207 )
2208
2209 console_dict = {
2210 "protocol": console_url[0:protocol_index],
2211 "server": console_url[protocol_index + 2 : port_index],
2212 "port": console_url[port_index:suffix_index],
2213 "suffix": console_url[suffix_index + 1 :],
2214 }
tierno7edb6752016-03-21 17:37:52 +01002215 protocol_index += 2
sousaedu80135b92021-02-17 15:05:18 +01002216
tiernoae4a8d12016-07-08 12:30:39 +02002217 return console_dict
tierno72774862020-05-04 11:44:15 +00002218 raise vimconn.VimConnUnexpectedResponse("Unexpected response from VIM")
sousaedu80135b92021-02-17 15:05:18 +01002219 except (
2220 nvExceptions.NotFound,
2221 ksExceptions.ClientException,
2222 nvExceptions.ClientException,
2223 nvExceptions.BadRequest,
2224 ConnectionError,
2225 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02002226 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01002227
tierno98e909c2017-10-14 13:27:03 +02002228 def delete_vminstance(self, vm_id, created_items=None):
sousaedu80135b92021-02-17 15:05:18 +01002229 """Removes a VM instance from VIM. Returns the old identifier"""
tierno1ec592d2020-06-16 15:29:47 +00002230 # print "osconnector: Getting VM from VIM"
2231 if created_items is None:
tierno98e909c2017-10-14 13:27:03 +02002232 created_items = {}
sousaedu80135b92021-02-17 15:05:18 +01002233
tierno7edb6752016-03-21 17:37:52 +01002234 try:
2235 self._reload_connection()
tierno98e909c2017-10-14 13:27:03 +02002236 # delete VM ports attached to this networks before the virtual machine
2237 for k, v in created_items.items():
2238 if not v: # skip already deleted
2239 continue
sousaedu80135b92021-02-17 15:05:18 +01002240
tierno7edb6752016-03-21 17:37:52 +01002241 try:
tiernoad6bdd42018-01-10 10:43:46 +01002242 k_item, _, k_id = k.partition(":")
2243 if k_item == "port":
2244 self.neutron.delete_port(k_id)
tierno7edb6752016-03-21 17:37:52 +01002245 except Exception as e:
sousaedu80135b92021-02-17 15:05:18 +01002246 self.logger.error(
2247 "Error deleting port: {}: {}".format(type(e).__name__, e)
2248 )
montesmoreno0c8def02016-12-22 12:16:23 +00002249
tierno98e909c2017-10-14 13:27:03 +02002250 # #commented because detaching the volumes makes the servers.delete not work properly ?!?
2251 # #dettach volumes attached
2252 # server = self.nova.servers.get(vm_id)
sousaedu80135b92021-02-17 15:05:18 +01002253 # volumes_attached_dict = server._info["os-extended-volumes:volumes_attached"] #volume["id"]
tierno98e909c2017-10-14 13:27:03 +02002254 # #for volume in volumes_attached_dict:
sousaedu80135b92021-02-17 15:05:18 +01002255 # # self.cinder.volumes.detach(volume["id"])
montesmoreno0c8def02016-12-22 12:16:23 +00002256
tierno98e909c2017-10-14 13:27:03 +02002257 if vm_id:
2258 self.nova.servers.delete(vm_id)
montesmoreno0c8def02016-12-22 12:16:23 +00002259
tierno98e909c2017-10-14 13:27:03 +02002260 # delete volumes. Although having detached, they should have in active status before deleting
2261 # we ensure in this loop
montesmoreno0c8def02016-12-22 12:16:23 +00002262 keep_waiting = True
2263 elapsed_time = 0
sousaedu80135b92021-02-17 15:05:18 +01002264
montesmoreno0c8def02016-12-22 12:16:23 +00002265 while keep_waiting and elapsed_time < volume_timeout:
2266 keep_waiting = False
sousaedu80135b92021-02-17 15:05:18 +01002267
tierno98e909c2017-10-14 13:27:03 +02002268 for k, v in created_items.items():
2269 if not v: # skip already deleted
2270 continue
sousaedu80135b92021-02-17 15:05:18 +01002271
tierno98e909c2017-10-14 13:27:03 +02002272 try:
tiernoad6bdd42018-01-10 10:43:46 +01002273 k_item, _, k_id = k.partition(":")
2274 if k_item == "volume":
sousaedu80135b92021-02-17 15:05:18 +01002275 if self.cinder.volumes.get(k_id).status != "available":
tierno98e909c2017-10-14 13:27:03 +02002276 keep_waiting = True
2277 else:
tiernoad6bdd42018-01-10 10:43:46 +01002278 self.cinder.volumes.delete(k_id)
tiernocb66c7e2020-07-22 10:42:58 +00002279 created_items[k] = None
2280 elif k_item == "floating_ip": # floating ip
2281 self.neutron.delete_floatingip(k_id)
2282 created_items[k] = None
2283
tierno98e909c2017-10-14 13:27:03 +02002284 except Exception as e:
tiernocb66c7e2020-07-22 10:42:58 +00002285 self.logger.error("Error deleting {}: {}".format(k, e))
sousaedu80135b92021-02-17 15:05:18 +01002286
montesmoreno0c8def02016-12-22 12:16:23 +00002287 if keep_waiting:
2288 time.sleep(1)
2289 elapsed_time += 1
sousaedu80135b92021-02-17 15:05:18 +01002290
tierno98e909c2017-10-14 13:27:03 +02002291 return None
sousaedu80135b92021-02-17 15:05:18 +01002292 except (
2293 nvExceptions.NotFound,
2294 ksExceptions.ClientException,
2295 nvExceptions.ClientException,
2296 ConnectionError,
2297 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02002298 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01002299
tiernoae4a8d12016-07-08 12:30:39 +02002300 def refresh_vms_status(self, vm_list):
tierno1ec592d2020-06-16 15:29:47 +00002301 """Get the status of the virtual machines and their interfaces/ports
sousaedu80135b92021-02-17 15:05:18 +01002302 Params: the list of VM identifiers
2303 Returns a dictionary with:
2304 vm_id: #VIM id of this Virtual Machine
2305 status: #Mandatory. Text with one of:
2306 # DELETED (not found at vim)
2307 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
2308 # OTHER (Vim reported other status not understood)
2309 # ERROR (VIM indicates an ERROR status)
2310 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
2311 # CREATING (on building process), ERROR
2312 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
2313 #
2314 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
2315 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
2316 interfaces:
2317 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
2318 mac_address: #Text format XX:XX:XX:XX:XX:XX
2319 vim_net_id: #network id where this interface is connected
2320 vim_interface_id: #interface/port VIM id
2321 ip_address: #null, or text with IPv4, IPv6 address
2322 compute_node: #identification of compute node where PF,VF interface is allocated
2323 pci: #PCI address of the NIC that hosts the PF,VF
2324 vlan: #physical VLAN used for VF
tierno1ec592d2020-06-16 15:29:47 +00002325 """
2326 vm_dict = {}
sousaedu80135b92021-02-17 15:05:18 +01002327 self.logger.debug(
2328 "refresh_vms status: Getting tenant VM instance information from VIM"
2329 )
2330
tiernoae4a8d12016-07-08 12:30:39 +02002331 for vm_id in vm_list:
tierno1ec592d2020-06-16 15:29:47 +00002332 vm = {}
sousaedu80135b92021-02-17 15:05:18 +01002333
tiernoae4a8d12016-07-08 12:30:39 +02002334 try:
2335 vm_vim = self.get_vminstance(vm_id)
sousaedu80135b92021-02-17 15:05:18 +01002336
2337 if vm_vim["status"] in vmStatus2manoFormat:
2338 vm["status"] = vmStatus2manoFormat[vm_vim["status"]]
tierno7edb6752016-03-21 17:37:52 +01002339 else:
sousaedu80135b92021-02-17 15:05:18 +01002340 vm["status"] = "OTHER"
2341 vm["error_msg"] = "VIM status reported " + vm_vim["status"]
2342
tierno70eeb182020-10-19 16:38:00 +00002343 vm_vim.pop("OS-EXT-SRV-ATTR:user_data", None)
2344 vm_vim.pop("user_data", None)
sousaedu80135b92021-02-17 15:05:18 +01002345 vm["vim_info"] = self.serialize(vm_vim)
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01002346
tiernoae4a8d12016-07-08 12:30:39 +02002347 vm["interfaces"] = []
sousaedu80135b92021-02-17 15:05:18 +01002348 if vm_vim.get("fault"):
2349 vm["error_msg"] = str(vm_vim["fault"])
2350
tierno1ec592d2020-06-16 15:29:47 +00002351 # get interfaces
tierno7edb6752016-03-21 17:37:52 +01002352 try:
tiernoae4a8d12016-07-08 12:30:39 +02002353 self._reload_connection()
tiernob42fd9b2018-06-20 10:44:32 +02002354 port_dict = self.neutron.list_ports(device_id=vm_id)
sousaedu80135b92021-02-17 15:05:18 +01002355
tiernoae4a8d12016-07-08 12:30:39 +02002356 for port in port_dict["ports"]:
tierno1ec592d2020-06-16 15:29:47 +00002357 interface = {}
sousaedu80135b92021-02-17 15:05:18 +01002358 interface["vim_info"] = self.serialize(port)
tiernoae4a8d12016-07-08 12:30:39 +02002359 interface["mac_address"] = port.get("mac_address")
2360 interface["vim_net_id"] = port["network_id"]
2361 interface["vim_interface_id"] = port["id"]
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01002362 # check if OS-EXT-SRV-ATTR:host is there,
Mike Marchetti5b9da422017-05-02 15:35:47 -04002363 # in case of non-admin credentials, it will be missing
sousaedu80135b92021-02-17 15:05:18 +01002364
2365 if vm_vim.get("OS-EXT-SRV-ATTR:host"):
2366 interface["compute_node"] = vm_vim["OS-EXT-SRV-ATTR:host"]
2367
tierno867ffe92017-03-27 12:50:34 +02002368 interface["pci"] = None
Mike Marchetti5b9da422017-05-02 15:35:47 -04002369
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01002370 # check if binding:profile is there,
Mike Marchetti5b9da422017-05-02 15:35:47 -04002371 # in case of non-admin credentials, it will be missing
sousaedu80135b92021-02-17 15:05:18 +01002372 if port.get("binding:profile"):
2373 if port["binding:profile"].get("pci_slot"):
tierno1ec592d2020-06-16 15:29:47 +00002374 # TODO: At the moment sr-iov pci addresses are converted to PF pci addresses by setting
2375 # the slot to 0x00
Mike Marchetti5b9da422017-05-02 15:35:47 -04002376 # TODO: This is just a workaround valid for niantinc. Find a better way to do so
2377 # CHANGE DDDD:BB:SS.F to DDDD:BB:00.(F%2) assuming there are 2 ports per nic
sousaedu80135b92021-02-17 15:05:18 +01002378 pci = port["binding:profile"]["pci_slot"]
Mike Marchetti5b9da422017-05-02 15:35:47 -04002379 # interface["pci"] = pci[:-4] + "00." + str(int(pci[-1]) % 2)
2380 interface["pci"] = pci
sousaedu80135b92021-02-17 15:05:18 +01002381
tierno867ffe92017-03-27 12:50:34 +02002382 interface["vlan"] = None
sousaedu80135b92021-02-17 15:05:18 +01002383
2384 if port.get("binding:vif_details"):
2385 interface["vlan"] = port["binding:vif_details"].get("vlan")
2386
tierno1dfe9932020-06-18 08:50:10 +00002387 # Get vlan from network in case not present in port for those old openstacks and cases where
2388 # it is needed vlan at PT
2389 if not interface["vlan"]:
2390 # if network is of type vlan and port is of type direct (sr-iov) then set vlan id
2391 network = self.neutron.show_network(port["network_id"])
sousaedu80135b92021-02-17 15:05:18 +01002392
2393 if (
2394 network["network"].get("provider:network_type")
2395 == "vlan"
2396 ):
tierno1dfe9932020-06-18 08:50:10 +00002397 # and port.get("binding:vnic_type") in ("direct", "direct-physical"):
sousaedu80135b92021-02-17 15:05:18 +01002398 interface["vlan"] = network["network"].get(
2399 "provider:segmentation_id"
2400 )
2401
tierno1ec592d2020-06-16 15:29:47 +00002402 ips = []
2403 # look for floating ip address
tiernob42fd9b2018-06-20 10:44:32 +02002404 try:
sousaedu80135b92021-02-17 15:05:18 +01002405 floating_ip_dict = self.neutron.list_floatingips(
2406 port_id=port["id"]
2407 )
2408
tiernob42fd9b2018-06-20 10:44:32 +02002409 if floating_ip_dict.get("floatingips"):
sousaedu80135b92021-02-17 15:05:18 +01002410 ips.append(
2411 floating_ip_dict["floatingips"][0].get(
2412 "floating_ip_address"
2413 )
2414 )
tiernob42fd9b2018-06-20 10:44:32 +02002415 except Exception:
2416 pass
tierno7edb6752016-03-21 17:37:52 +01002417
tiernoae4a8d12016-07-08 12:30:39 +02002418 for subnet in port["fixed_ips"]:
2419 ips.append(subnet["ip_address"])
sousaedu80135b92021-02-17 15:05:18 +01002420
tiernoae4a8d12016-07-08 12:30:39 +02002421 interface["ip_address"] = ";".join(ips)
2422 vm["interfaces"].append(interface)
2423 except Exception as e:
sousaedu80135b92021-02-17 15:05:18 +01002424 self.logger.error(
2425 "Error getting vm interface information {}: {}".format(
2426 type(e).__name__, e
2427 ),
2428 exc_info=True,
2429 )
tierno72774862020-05-04 11:44:15 +00002430 except vimconn.VimConnNotFoundException as e:
tiernoae4a8d12016-07-08 12:30:39 +02002431 self.logger.error("Exception getting vm status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01002432 vm["status"] = "DELETED"
2433 vm["error_msg"] = str(e)
tierno72774862020-05-04 11:44:15 +00002434 except vimconn.VimConnException as e:
tiernoae4a8d12016-07-08 12:30:39 +02002435 self.logger.error("Exception getting vm status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01002436 vm["status"] = "VIM_ERROR"
2437 vm["error_msg"] = str(e)
2438
tiernoae4a8d12016-07-08 12:30:39 +02002439 vm_dict[vm_id] = vm
sousaedu80135b92021-02-17 15:05:18 +01002440
tiernoae4a8d12016-07-08 12:30:39 +02002441 return vm_dict
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002442
tierno98e909c2017-10-14 13:27:03 +02002443 def action_vminstance(self, vm_id, action_dict, created_items={}):
tierno1ec592d2020-06-16 15:29:47 +00002444 """Send and action over a VM instance from VIM
2445 Returns None or the console dict if the action was successfully sent to the VIM"""
tiernoae4a8d12016-07-08 12:30:39 +02002446 self.logger.debug("Action over VM '%s': %s", vm_id, str(action_dict))
sousaedu80135b92021-02-17 15:05:18 +01002447
tierno7edb6752016-03-21 17:37:52 +01002448 try:
2449 self._reload_connection()
2450 server = self.nova.servers.find(id=vm_id)
sousaedu80135b92021-02-17 15:05:18 +01002451
tierno7edb6752016-03-21 17:37:52 +01002452 if "start" in action_dict:
tierno1ec592d2020-06-16 15:29:47 +00002453 if action_dict["start"] == "rebuild":
tierno7edb6752016-03-21 17:37:52 +01002454 server.rebuild()
2455 else:
tierno1ec592d2020-06-16 15:29:47 +00002456 if server.status == "PAUSED":
tierno7edb6752016-03-21 17:37:52 +01002457 server.unpause()
tierno1ec592d2020-06-16 15:29:47 +00002458 elif server.status == "SUSPENDED":
tierno7edb6752016-03-21 17:37:52 +01002459 server.resume()
tierno1ec592d2020-06-16 15:29:47 +00002460 elif server.status == "SHUTOFF":
tierno7edb6752016-03-21 17:37:52 +01002461 server.start()
2462 elif "pause" in action_dict:
2463 server.pause()
2464 elif "resume" in action_dict:
2465 server.resume()
2466 elif "shutoff" in action_dict or "shutdown" in action_dict:
2467 server.stop()
2468 elif "forceOff" in action_dict:
tierno1ec592d2020-06-16 15:29:47 +00002469 server.stop() # TODO
tierno7edb6752016-03-21 17:37:52 +01002470 elif "terminate" in action_dict:
2471 server.delete()
2472 elif "createImage" in action_dict:
2473 server.create_image()
tierno1ec592d2020-06-16 15:29:47 +00002474 # "path":path_schema,
2475 # "description":description_schema,
2476 # "name":name_schema,
2477 # "metadata":metadata_schema,
2478 # "imageRef": id_schema,
2479 # "disk": {"oneOf":[{"type": "null"}, {"type":"string"}] },
tierno7edb6752016-03-21 17:37:52 +01002480 elif "rebuild" in action_dict:
sousaedu80135b92021-02-17 15:05:18 +01002481 server.rebuild(server.image["id"])
tierno7edb6752016-03-21 17:37:52 +01002482 elif "reboot" in action_dict:
sousaedu80135b92021-02-17 15:05:18 +01002483 server.reboot() # reboot_type="SOFT"
tierno7edb6752016-03-21 17:37:52 +01002484 elif "console" in action_dict:
2485 console_type = action_dict["console"]
sousaedu80135b92021-02-17 15:05:18 +01002486
tierno1ec592d2020-06-16 15:29:47 +00002487 if console_type is None or console_type == "novnc":
tierno7edb6752016-03-21 17:37:52 +01002488 console_dict = server.get_vnc_console("novnc")
2489 elif console_type == "xvpvnc":
2490 console_dict = server.get_vnc_console(console_type)
2491 elif console_type == "rdp-html5":
2492 console_dict = server.get_rdp_console(console_type)
2493 elif console_type == "spice-html5":
2494 console_dict = server.get_spice_console(console_type)
2495 else:
sousaedu80135b92021-02-17 15:05:18 +01002496 raise vimconn.VimConnException(
2497 "console type '{}' not allowed".format(console_type),
2498 http_code=vimconn.HTTP_Bad_Request,
2499 )
2500
tierno7edb6752016-03-21 17:37:52 +01002501 try:
2502 console_url = console_dict["console"]["url"]
tierno1ec592d2020-06-16 15:29:47 +00002503 # parse console_url
tierno7edb6752016-03-21 17:37:52 +01002504 protocol_index = console_url.find("//")
sousaedu80135b92021-02-17 15:05:18 +01002505 suffix_index = (
2506 console_url[protocol_index + 2 :].find("/") + protocol_index + 2
2507 )
2508 port_index = (
2509 console_url[protocol_index + 2 : suffix_index].find(":")
2510 + protocol_index
2511 + 2
2512 )
2513
tierno1ec592d2020-06-16 15:29:47 +00002514 if protocol_index < 0 or port_index < 0 or suffix_index < 0:
sousaedu80135b92021-02-17 15:05:18 +01002515 raise vimconn.VimConnException(
2516 "Unexpected response from VIM " + str(console_dict)
2517 )
2518
2519 console_dict2 = {
2520 "protocol": console_url[0:protocol_index],
2521 "server": console_url[protocol_index + 2 : port_index],
2522 "port": int(console_url[port_index + 1 : suffix_index]),
2523 "suffix": console_url[suffix_index + 1 :],
2524 }
2525
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002526 return console_dict2
tierno1ec592d2020-06-16 15:29:47 +00002527 except Exception:
sousaedu80135b92021-02-17 15:05:18 +01002528 raise vimconn.VimConnException(
2529 "Unexpected response from VIM " + str(console_dict)
2530 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002531
tierno98e909c2017-10-14 13:27:03 +02002532 return None
sousaedu80135b92021-02-17 15:05:18 +01002533 except (
2534 ksExceptions.ClientException,
2535 nvExceptions.ClientException,
2536 nvExceptions.NotFound,
2537 ConnectionError,
2538 ) as e:
tiernoae4a8d12016-07-08 12:30:39 +02002539 self._format_exception(e)
tierno1ec592d2020-06-16 15:29:47 +00002540 # TODO insert exception vimconn.HTTP_Unauthorized
tiernoae4a8d12016-07-08 12:30:39 +02002541
tierno1ec592d2020-06-16 15:29:47 +00002542 # ###### VIO Specific Changes #########
garciadeblasebd66722019-01-31 16:01:31 +00002543 def _generate_vlanID(self):
kate721d79b2017-06-24 04:21:38 -07002544 """
sousaedu80135b92021-02-17 15:05:18 +01002545 Method to get unused vlanID
kate721d79b2017-06-24 04:21:38 -07002546 Args:
2547 None
2548 Returns:
2549 vlanID
2550 """
tierno1ec592d2020-06-16 15:29:47 +00002551 # Get used VLAN IDs
kate721d79b2017-06-24 04:21:38 -07002552 usedVlanIDs = []
2553 networks = self.get_network_list()
sousaedu80135b92021-02-17 15:05:18 +01002554
kate721d79b2017-06-24 04:21:38 -07002555 for net in networks:
sousaedu80135b92021-02-17 15:05:18 +01002556 if net.get("provider:segmentation_id"):
2557 usedVlanIDs.append(net.get("provider:segmentation_id"))
2558
kate721d79b2017-06-24 04:21:38 -07002559 used_vlanIDs = set(usedVlanIDs)
2560
tierno1ec592d2020-06-16 15:29:47 +00002561 # find unused VLAN ID
sousaedu80135b92021-02-17 15:05:18 +01002562 for vlanID_range in self.config.get("dataplane_net_vlan_range"):
kate721d79b2017-06-24 04:21:38 -07002563 try:
sousaedu80135b92021-02-17 15:05:18 +01002564 start_vlanid, end_vlanid = map(
2565 int, vlanID_range.replace(" ", "").split("-")
2566 )
2567
tierno7d782ef2019-10-04 12:56:31 +00002568 for vlanID in range(start_vlanid, end_vlanid + 1):
kate721d79b2017-06-24 04:21:38 -07002569 if vlanID not in used_vlanIDs:
2570 return vlanID
2571 except Exception as exp:
sousaedu80135b92021-02-17 15:05:18 +01002572 raise vimconn.VimConnException(
2573 "Exception {} occurred while generating VLAN ID.".format(exp)
2574 )
kate721d79b2017-06-24 04:21:38 -07002575 else:
tierno1ec592d2020-06-16 15:29:47 +00002576 raise vimconn.VimConnConflictException(
2577 "Unable to create the SRIOV VLAN network. All given Vlan IDs {} are in use.".format(
sousaedu80135b92021-02-17 15:05:18 +01002578 self.config.get("dataplane_net_vlan_range")
2579 )
2580 )
kate721d79b2017-06-24 04:21:38 -07002581
garciadeblasebd66722019-01-31 16:01:31 +00002582 def _generate_multisegment_vlanID(self):
2583 """
sousaedu80135b92021-02-17 15:05:18 +01002584 Method to get unused vlanID
2585 Args:
2586 None
2587 Returns:
2588 vlanID
garciadeblasebd66722019-01-31 16:01:31 +00002589 """
tierno6869ae72020-01-09 17:37:34 +00002590 # Get used VLAN IDs
garciadeblasebd66722019-01-31 16:01:31 +00002591 usedVlanIDs = []
2592 networks = self.get_network_list()
2593 for net in networks:
sousaedu80135b92021-02-17 15:05:18 +01002594 if net.get("provider:network_type") == "vlan" and net.get(
2595 "provider:segmentation_id"
2596 ):
2597 usedVlanIDs.append(net.get("provider:segmentation_id"))
2598 elif net.get("segments"):
2599 for segment in net.get("segments"):
2600 if segment.get("provider:network_type") == "vlan" and segment.get(
2601 "provider:segmentation_id"
2602 ):
2603 usedVlanIDs.append(segment.get("provider:segmentation_id"))
2604
garciadeblasebd66722019-01-31 16:01:31 +00002605 used_vlanIDs = set(usedVlanIDs)
2606
tierno6869ae72020-01-09 17:37:34 +00002607 # find unused VLAN ID
sousaedu80135b92021-02-17 15:05:18 +01002608 for vlanID_range in self.config.get("multisegment_vlan_range"):
garciadeblasebd66722019-01-31 16:01:31 +00002609 try:
sousaedu80135b92021-02-17 15:05:18 +01002610 start_vlanid, end_vlanid = map(
2611 int, vlanID_range.replace(" ", "").split("-")
2612 )
2613
tierno7d782ef2019-10-04 12:56:31 +00002614 for vlanID in range(start_vlanid, end_vlanid + 1):
garciadeblasebd66722019-01-31 16:01:31 +00002615 if vlanID not in used_vlanIDs:
2616 return vlanID
2617 except Exception as exp:
sousaedu80135b92021-02-17 15:05:18 +01002618 raise vimconn.VimConnException(
2619 "Exception {} occurred while generating VLAN ID.".format(exp)
2620 )
garciadeblasebd66722019-01-31 16:01:31 +00002621 else:
tierno1ec592d2020-06-16 15:29:47 +00002622 raise vimconn.VimConnConflictException(
2623 "Unable to create the VLAN segment. All VLAN IDs {} are in use.".format(
sousaedu80135b92021-02-17 15:05:18 +01002624 self.config.get("multisegment_vlan_range")
2625 )
2626 )
garciadeblasebd66722019-01-31 16:01:31 +00002627
2628 def _validate_vlan_ranges(self, input_vlan_range, text_vlan_range):
kate721d79b2017-06-24 04:21:38 -07002629 """
2630 Method to validate user given vlanID ranges
2631 Args: None
2632 Returns: None
2633 """
garciadeblasebd66722019-01-31 16:01:31 +00002634 for vlanID_range in input_vlan_range:
kate721d79b2017-06-24 04:21:38 -07002635 vlan_range = vlanID_range.replace(" ", "")
tierno1ec592d2020-06-16 15:29:47 +00002636 # validate format
sousaedu80135b92021-02-17 15:05:18 +01002637 vlanID_pattern = r"(\d)*-(\d)*$"
kate721d79b2017-06-24 04:21:38 -07002638 match_obj = re.match(vlanID_pattern, vlan_range)
2639 if not match_obj:
tierno1ec592d2020-06-16 15:29:47 +00002640 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01002641 "Invalid VLAN range for {}: {}.You must provide "
2642 "'{}' in format [start_ID - end_ID].".format(
2643 text_vlan_range, vlanID_range, text_vlan_range
2644 )
2645 )
kate721d79b2017-06-24 04:21:38 -07002646
tierno1ec592d2020-06-16 15:29:47 +00002647 start_vlanid, end_vlanid = map(int, vlan_range.split("-"))
2648 if start_vlanid <= 0:
2649 raise vimconn.VimConnConflictException(
2650 "Invalid VLAN range for {}: {}. Start ID can not be zero. For VLAN "
sousaedu80135b92021-02-17 15:05:18 +01002651 "networks valid IDs are 1 to 4094 ".format(
2652 text_vlan_range, vlanID_range
2653 )
2654 )
2655
tierno1ec592d2020-06-16 15:29:47 +00002656 if end_vlanid > 4094:
2657 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01002658 "Invalid VLAN range for {}: {}. End VLAN ID can not be "
2659 "greater than 4094. For VLAN networks valid IDs are 1 to 4094 ".format(
2660 text_vlan_range, vlanID_range
2661 )
2662 )
kate721d79b2017-06-24 04:21:38 -07002663
2664 if start_vlanid > end_vlanid:
tierno1ec592d2020-06-16 15:29:47 +00002665 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01002666 "Invalid VLAN range for {}: {}. You must provide '{}'"
2667 " in format start_ID - end_ID and start_ID < end_ID ".format(
2668 text_vlan_range, vlanID_range, text_vlan_range
2669 )
2670 )
kate721d79b2017-06-24 04:21:38 -07002671
tierno1ec592d2020-06-16 15:29:47 +00002672 # NOT USED FUNCTIONS
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002673
tiernoae4a8d12016-07-08 12:30:39 +02002674 def new_external_port(self, port_data):
tierno1ec592d2020-06-16 15:29:47 +00002675 """Adds a external port to VIM
sousaedu80135b92021-02-17 15:05:18 +01002676 Returns the port identifier"""
tierno1ec592d2020-06-16 15:29:47 +00002677 # TODO openstack if needed
sousaedu80135b92021-02-17 15:05:18 +01002678 return (
2679 -vimconn.HTTP_Internal_Server_Error,
2680 "osconnector.new_external_port() not implemented",
2681 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002682
tiernoae4a8d12016-07-08 12:30:39 +02002683 def connect_port_network(self, port_id, network_id, admin=False):
tierno1ec592d2020-06-16 15:29:47 +00002684 """Connects a external port to a network
sousaedu80135b92021-02-17 15:05:18 +01002685 Returns status code of the VIM response"""
tierno1ec592d2020-06-16 15:29:47 +00002686 # TODO openstack if needed
sousaedu80135b92021-02-17 15:05:18 +01002687 return (
2688 -vimconn.HTTP_Internal_Server_Error,
2689 "osconnector.connect_port_network() not implemented",
2690 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002691
tiernoae4a8d12016-07-08 12:30:39 +02002692 def new_user(self, user_name, user_passwd, tenant_id=None):
tierno1ec592d2020-06-16 15:29:47 +00002693 """Adds a new user to openstack VIM
sousaedu80135b92021-02-17 15:05:18 +01002694 Returns the user identifier"""
tiernoae4a8d12016-07-08 12:30:39 +02002695 self.logger.debug("osconnector: Adding a new user to VIM")
sousaedu80135b92021-02-17 15:05:18 +01002696
tiernoae4a8d12016-07-08 12:30:39 +02002697 try:
2698 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +01002699 user = self.keystone.users.create(
2700 user_name, password=user_passwd, default_project=tenant_id
2701 )
tierno1ec592d2020-06-16 15:29:47 +00002702 # self.keystone.tenants.add_user(self.k_creds["username"], #role)
sousaedu80135b92021-02-17 15:05:18 +01002703
tiernoae4a8d12016-07-08 12:30:39 +02002704 return user.id
2705 except ksExceptions.ConnectionError as e:
tierno1ec592d2020-06-16 15:29:47 +00002706 error_value = -vimconn.HTTP_Bad_Request
sousaedu80135b92021-02-17 15:05:18 +01002707 error_text = (
2708 type(e).__name__
2709 + ": "
2710 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2711 )
tierno1ec592d2020-06-16 15:29:47 +00002712 except ksExceptions.ClientException as e: # TODO remove
2713 error_value = -vimconn.HTTP_Bad_Request
sousaedu80135b92021-02-17 15:05:18 +01002714 error_text = (
2715 type(e).__name__
2716 + ": "
2717 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2718 )
2719
tierno1ec592d2020-06-16 15:29:47 +00002720 # TODO insert exception vimconn.HTTP_Unauthorized
2721 # if reaching here is because an exception
tierno9c5c8322018-03-23 15:44:03 +01002722 self.logger.debug("new_user " + error_text)
sousaedu80135b92021-02-17 15:05:18 +01002723
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002724 return error_value, error_text
tiernoae4a8d12016-07-08 12:30:39 +02002725
2726 def delete_user(self, user_id):
tierno1ec592d2020-06-16 15:29:47 +00002727 """Delete a user from openstack VIM
sousaedu80135b92021-02-17 15:05:18 +01002728 Returns the user identifier"""
tiernoae4a8d12016-07-08 12:30:39 +02002729 if self.debug:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002730 print("osconnector: Deleting a user from VIM")
sousaedu80135b92021-02-17 15:05:18 +01002731
tiernoae4a8d12016-07-08 12:30:39 +02002732 try:
2733 self._reload_connection()
2734 self.keystone.users.delete(user_id)
sousaedu80135b92021-02-17 15:05:18 +01002735
tiernoae4a8d12016-07-08 12:30:39 +02002736 return 1, user_id
2737 except ksExceptions.ConnectionError as e:
tierno1ec592d2020-06-16 15:29:47 +00002738 error_value = -vimconn.HTTP_Bad_Request
sousaedu80135b92021-02-17 15:05:18 +01002739 error_text = (
2740 type(e).__name__
2741 + ": "
2742 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2743 )
tiernoae4a8d12016-07-08 12:30:39 +02002744 except ksExceptions.NotFound as e:
tierno1ec592d2020-06-16 15:29:47 +00002745 error_value = -vimconn.HTTP_Not_Found
sousaedu80135b92021-02-17 15:05:18 +01002746 error_text = (
2747 type(e).__name__
2748 + ": "
2749 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2750 )
tierno1ec592d2020-06-16 15:29:47 +00002751 except ksExceptions.ClientException as e: # TODO remove
2752 error_value = -vimconn.HTTP_Bad_Request
sousaedu80135b92021-02-17 15:05:18 +01002753 error_text = (
2754 type(e).__name__
2755 + ": "
2756 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2757 )
2758
tierno1ec592d2020-06-16 15:29:47 +00002759 # TODO insert exception vimconn.HTTP_Unauthorized
2760 # if reaching here is because an exception
2761 self.logger.debug("delete_tenant " + error_text)
sousaedu80135b92021-02-17 15:05:18 +01002762
tiernoae4a8d12016-07-08 12:30:39 +02002763 return error_value, error_text
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002764
tierno7edb6752016-03-21 17:37:52 +01002765 def get_hosts_info(self):
tierno1ec592d2020-06-16 15:29:47 +00002766 """Get the information of deployed hosts
2767 Returns the hosts content"""
tierno7edb6752016-03-21 17:37:52 +01002768 if self.debug:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002769 print("osconnector: Getting Host info from VIM")
sousaedu80135b92021-02-17 15:05:18 +01002770
tierno7edb6752016-03-21 17:37:52 +01002771 try:
tierno1ec592d2020-06-16 15:29:47 +00002772 h_list = []
tierno7edb6752016-03-21 17:37:52 +01002773 self._reload_connection()
2774 hypervisors = self.nova.hypervisors.list()
sousaedu80135b92021-02-17 15:05:18 +01002775
tierno7edb6752016-03-21 17:37:52 +01002776 for hype in hypervisors:
tierno1ec592d2020-06-16 15:29:47 +00002777 h_list.append(hype.to_dict())
sousaedu80135b92021-02-17 15:05:18 +01002778
tierno1ec592d2020-06-16 15:29:47 +00002779 return 1, {"hosts": h_list}
tierno7edb6752016-03-21 17:37:52 +01002780 except nvExceptions.NotFound as e:
tierno1ec592d2020-06-16 15:29:47 +00002781 error_value = -vimconn.HTTP_Not_Found
sousaedu80135b92021-02-17 15:05:18 +01002782 error_text = str(e) if len(e.args) == 0 else str(e.args[0])
tierno7edb6752016-03-21 17:37:52 +01002783 except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
tierno1ec592d2020-06-16 15:29:47 +00002784 error_value = -vimconn.HTTP_Bad_Request
sousaedu80135b92021-02-17 15:05:18 +01002785 error_text = (
2786 type(e).__name__
2787 + ": "
2788 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2789 )
2790
tierno1ec592d2020-06-16 15:29:47 +00002791 # TODO insert exception vimconn.HTTP_Unauthorized
2792 # if reaching here is because an exception
tierno9c5c8322018-03-23 15:44:03 +01002793 self.logger.debug("get_hosts_info " + error_text)
sousaedu80135b92021-02-17 15:05:18 +01002794
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002795 return error_value, error_text
tierno7edb6752016-03-21 17:37:52 +01002796
2797 def get_hosts(self, vim_tenant):
tierno1ec592d2020-06-16 15:29:47 +00002798 """Get the hosts and deployed instances
2799 Returns the hosts content"""
tierno7edb6752016-03-21 17:37:52 +01002800 r, hype_dict = self.get_hosts_info()
sousaedu80135b92021-02-17 15:05:18 +01002801
tierno1ec592d2020-06-16 15:29:47 +00002802 if r < 0:
tierno7edb6752016-03-21 17:37:52 +01002803 return r, hype_dict
sousaedu80135b92021-02-17 15:05:18 +01002804
tierno7edb6752016-03-21 17:37:52 +01002805 hypervisors = hype_dict["hosts"]
sousaedu80135b92021-02-17 15:05:18 +01002806
tierno7edb6752016-03-21 17:37:52 +01002807 try:
2808 servers = self.nova.servers.list()
2809 for hype in hypervisors:
2810 for server in servers:
sousaedu80135b92021-02-17 15:05:18 +01002811 if (
2812 server.to_dict()["OS-EXT-SRV-ATTR:hypervisor_hostname"]
2813 == hype["hypervisor_hostname"]
2814 ):
2815 if "vm" in hype:
2816 hype["vm"].append(server.id)
tierno7edb6752016-03-21 17:37:52 +01002817 else:
sousaedu80135b92021-02-17 15:05:18 +01002818 hype["vm"] = [server.id]
2819
tierno7edb6752016-03-21 17:37:52 +01002820 return 1, hype_dict
2821 except nvExceptions.NotFound as e:
tierno1ec592d2020-06-16 15:29:47 +00002822 error_value = -vimconn.HTTP_Not_Found
sousaedu80135b92021-02-17 15:05:18 +01002823 error_text = str(e) if len(e.args) == 0 else str(e.args[0])
tierno7edb6752016-03-21 17:37:52 +01002824 except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
tierno1ec592d2020-06-16 15:29:47 +00002825 error_value = -vimconn.HTTP_Bad_Request
sousaedu80135b92021-02-17 15:05:18 +01002826 error_text = (
2827 type(e).__name__
2828 + ": "
2829 + (str(e) if len(e.args) == 0 else str(e.args[0]))
2830 )
2831
tierno1ec592d2020-06-16 15:29:47 +00002832 # TODO insert exception vimconn.HTTP_Unauthorized
2833 # if reaching here is because an exception
tierno9c5c8322018-03-23 15:44:03 +01002834 self.logger.debug("get_hosts " + error_text)
sousaedu80135b92021-02-17 15:05:18 +01002835
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002836 return error_value, error_text
tierno7edb6752016-03-21 17:37:52 +01002837
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002838 def new_classification(self, name, ctype, definition):
sousaedu80135b92021-02-17 15:05:18 +01002839 self.logger.debug(
2840 "Adding a new (Traffic) Classification to VIM, named %s", name
2841 )
2842
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002843 try:
2844 new_class = None
2845 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +01002846
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002847 if ctype not in supportedClassificationTypes:
tierno72774862020-05-04 11:44:15 +00002848 raise vimconn.VimConnNotSupportedException(
sousaedu80135b92021-02-17 15:05:18 +01002849 "OpenStack VIM connector does not support provided "
2850 "Classification Type {}, supported ones are: {}".format(
2851 ctype, supportedClassificationTypes
2852 )
2853 )
2854
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002855 if not self._validate_classification(ctype, definition):
tierno72774862020-05-04 11:44:15 +00002856 raise vimconn.VimConnException(
sousaedu80135b92021-02-17 15:05:18 +01002857 "Incorrect Classification definition for the type specified."
2858 )
tierno7edb6752016-03-21 17:37:52 +01002859
sousaedu80135b92021-02-17 15:05:18 +01002860 classification_dict = definition
2861 classification_dict["name"] = name
Igor D.Ccaadc442017-11-06 12:48:48 +00002862 new_class = self.neutron.create_sfc_flow_classifier(
sousaedu80135b92021-02-17 15:05:18 +01002863 {"flow_classifier": classification_dict}
2864 )
2865
2866 return new_class["flow_classifier"]["id"]
2867 except (
2868 neExceptions.ConnectionFailed,
2869 ksExceptions.ClientException,
2870 neExceptions.NeutronException,
2871 ConnectionError,
2872 ) as e:
2873 self.logger.error("Creation of Classification failed.")
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002874 self._format_exception(e)
2875
2876 def get_classification(self, class_id):
2877 self.logger.debug(" Getting Classification %s from VIM", class_id)
2878 filter_dict = {"id": class_id}
2879 class_list = self.get_classification_list(filter_dict)
sousaedu80135b92021-02-17 15:05:18 +01002880
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002881 if len(class_list) == 0:
tierno72774862020-05-04 11:44:15 +00002882 raise vimconn.VimConnNotFoundException(
sousaedu80135b92021-02-17 15:05:18 +01002883 "Classification '{}' not found".format(class_id)
2884 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002885 elif len(class_list) > 1:
tierno72774862020-05-04 11:44:15 +00002886 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01002887 "Found more than one Classification with this criteria"
2888 )
2889
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002890 classification = class_list[0]
sousaedu80135b92021-02-17 15:05:18 +01002891
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002892 return classification
2893
2894 def get_classification_list(self, filter_dict={}):
sousaedu80135b92021-02-17 15:05:18 +01002895 self.logger.debug(
2896 "Getting Classifications from VIM filter: '%s'", str(filter_dict)
2897 )
2898
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002899 try:
tierno69b590e2018-03-13 18:52:23 +01002900 filter_dict_os = filter_dict.copy()
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002901 self._reload_connection()
sousaedu80135b92021-02-17 15:05:18 +01002902
tierno69b590e2018-03-13 18:52:23 +01002903 if self.api_version3 and "tenant_id" in filter_dict_os:
sousaedu80135b92021-02-17 15:05:18 +01002904 filter_dict_os["project_id"] = filter_dict_os.pop("tenant_id")
2905
Igor D.Ccaadc442017-11-06 12:48:48 +00002906 classification_dict = self.neutron.list_sfc_flow_classifiers(
sousaedu80135b92021-02-17 15:05:18 +01002907 **filter_dict_os
2908 )
tierno69b590e2018-03-13 18:52:23 +01002909 classification_list = classification_dict["flow_classifiers"]
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002910 self.__classification_os2mano(classification_list)
sousaedu80135b92021-02-17 15:05:18 +01002911
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002912 return classification_list
sousaedu80135b92021-02-17 15:05:18 +01002913 except (
2914 neExceptions.ConnectionFailed,
2915 ksExceptions.ClientException,
2916 neExceptions.NeutronException,
2917 ConnectionError,
2918 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002919 self._format_exception(e)
2920
2921 def delete_classification(self, class_id):
2922 self.logger.debug("Deleting Classification '%s' from VIM", class_id)
sousaedu80135b92021-02-17 15:05:18 +01002923
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002924 try:
2925 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00002926 self.neutron.delete_sfc_flow_classifier(class_id)
sousaedu80135b92021-02-17 15:05:18 +01002927
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002928 return class_id
sousaedu80135b92021-02-17 15:05:18 +01002929 except (
2930 neExceptions.ConnectionFailed,
2931 neExceptions.NeutronException,
2932 ksExceptions.ClientException,
2933 neExceptions.NeutronException,
2934 ConnectionError,
2935 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002936 self._format_exception(e)
2937
2938 def new_sfi(self, name, ingress_ports, egress_ports, sfc_encap=True):
sousaedu80135b92021-02-17 15:05:18 +01002939 self.logger.debug(
2940 "Adding a new Service Function Instance to VIM, named '%s'", name
2941 )
2942
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002943 try:
2944 new_sfi = None
2945 self._reload_connection()
2946 correlation = None
sousaedu80135b92021-02-17 15:05:18 +01002947
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002948 if sfc_encap:
sousaedu80135b92021-02-17 15:05:18 +01002949 correlation = "nsh"
2950
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002951 if len(ingress_ports) != 1:
tierno72774862020-05-04 11:44:15 +00002952 raise vimconn.VimConnNotSupportedException(
sousaedu80135b92021-02-17 15:05:18 +01002953 "OpenStack VIM connector can only have 1 ingress port per SFI"
2954 )
2955
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002956 if len(egress_ports) != 1:
tierno72774862020-05-04 11:44:15 +00002957 raise vimconn.VimConnNotSupportedException(
sousaedu80135b92021-02-17 15:05:18 +01002958 "OpenStack VIM connector can only have 1 egress port per SFI"
2959 )
2960
2961 sfi_dict = {
2962 "name": name,
2963 "ingress": ingress_ports[0],
2964 "egress": egress_ports[0],
2965 "service_function_parameters": {"correlation": correlation},
2966 }
2967 new_sfi = self.neutron.create_sfc_port_pair({"port_pair": sfi_dict})
2968
2969 return new_sfi["port_pair"]["id"]
2970 except (
2971 neExceptions.ConnectionFailed,
2972 ksExceptions.ClientException,
2973 neExceptions.NeutronException,
2974 ConnectionError,
2975 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002976 if new_sfi:
2977 try:
sousaedu80135b92021-02-17 15:05:18 +01002978 self.neutron.delete_sfc_port_pair(new_sfi["port_pair"]["id"])
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002979 except Exception:
2980 self.logger.error(
sousaedu80135b92021-02-17 15:05:18 +01002981 "Creation of Service Function Instance failed, with "
2982 "subsequent deletion failure as well."
2983 )
2984
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002985 self._format_exception(e)
2986
2987 def get_sfi(self, sfi_id):
sousaedu80135b92021-02-17 15:05:18 +01002988 self.logger.debug("Getting Service Function Instance %s from VIM", sfi_id)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002989 filter_dict = {"id": sfi_id}
2990 sfi_list = self.get_sfi_list(filter_dict)
sousaedu80135b92021-02-17 15:05:18 +01002991
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002992 if len(sfi_list) == 0:
sousaedu80135b92021-02-17 15:05:18 +01002993 raise vimconn.VimConnNotFoundException(
2994 "Service Function Instance '{}' not found".format(sfi_id)
2995 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002996 elif len(sfi_list) > 1:
tierno72774862020-05-04 11:44:15 +00002997 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01002998 "Found more than one Service Function Instance with this criteria"
2999 )
3000
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003001 sfi = sfi_list[0]
sousaedu80135b92021-02-17 15:05:18 +01003002
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003003 return sfi
3004
3005 def get_sfi_list(self, filter_dict={}):
sousaedu80135b92021-02-17 15:05:18 +01003006 self.logger.debug(
3007 "Getting Service Function Instances from VIM filter: '%s'", str(filter_dict)
3008 )
3009
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003010 try:
3011 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +01003012 filter_dict_os = filter_dict.copy()
sousaedu80135b92021-02-17 15:05:18 +01003013
tierno69b590e2018-03-13 18:52:23 +01003014 if self.api_version3 and "tenant_id" in filter_dict_os:
sousaedu80135b92021-02-17 15:05:18 +01003015 filter_dict_os["project_id"] = filter_dict_os.pop("tenant_id")
3016
tierno69b590e2018-03-13 18:52:23 +01003017 sfi_dict = self.neutron.list_sfc_port_pairs(**filter_dict_os)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003018 sfi_list = sfi_dict["port_pairs"]
3019 self.__sfi_os2mano(sfi_list)
sousaedu80135b92021-02-17 15:05:18 +01003020
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003021 return sfi_list
sousaedu80135b92021-02-17 15:05:18 +01003022 except (
3023 neExceptions.ConnectionFailed,
3024 ksExceptions.ClientException,
3025 neExceptions.NeutronException,
3026 ConnectionError,
3027 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003028 self._format_exception(e)
3029
3030 def delete_sfi(self, sfi_id):
sousaedu80135b92021-02-17 15:05:18 +01003031 self.logger.debug("Deleting Service Function Instance '%s' from VIM", sfi_id)
3032
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003033 try:
3034 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00003035 self.neutron.delete_sfc_port_pair(sfi_id)
sousaedu80135b92021-02-17 15:05:18 +01003036
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003037 return sfi_id
sousaedu80135b92021-02-17 15:05:18 +01003038 except (
3039 neExceptions.ConnectionFailed,
3040 neExceptions.NeutronException,
3041 ksExceptions.ClientException,
3042 neExceptions.NeutronException,
3043 ConnectionError,
3044 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003045 self._format_exception(e)
3046
3047 def new_sf(self, name, sfis, sfc_encap=True):
tierno7d782ef2019-10-04 12:56:31 +00003048 self.logger.debug("Adding a new Service Function to VIM, named '%s'", name)
sousaedu80135b92021-02-17 15:05:18 +01003049
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003050 try:
3051 new_sf = None
3052 self._reload_connection()
tierno9c5c8322018-03-23 15:44:03 +01003053 # correlation = None
3054 # if sfc_encap:
sousaedu80135b92021-02-17 15:05:18 +01003055 # correlation = "nsh"
3056
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003057 for instance in sfis:
3058 sfi = self.get_sfi(instance)
sousaedu80135b92021-02-17 15:05:18 +01003059
3060 if sfi.get("sfc_encap") != sfc_encap:
tierno72774862020-05-04 11:44:15 +00003061 raise vimconn.VimConnNotSupportedException(
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003062 "OpenStack VIM connector requires all SFIs of the "
sousaedu80135b92021-02-17 15:05:18 +01003063 "same SF to share the same SFC Encapsulation"
3064 )
3065
3066 sf_dict = {"name": name, "port_pairs": sfis}
3067 new_sf = self.neutron.create_sfc_port_pair_group(
3068 {"port_pair_group": sf_dict}
3069 )
3070
3071 return new_sf["port_pair_group"]["id"]
3072 except (
3073 neExceptions.ConnectionFailed,
3074 ksExceptions.ClientException,
3075 neExceptions.NeutronException,
3076 ConnectionError,
3077 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003078 if new_sf:
3079 try:
Igor D.Ccaadc442017-11-06 12:48:48 +00003080 self.neutron.delete_sfc_port_pair_group(
sousaedu80135b92021-02-17 15:05:18 +01003081 new_sf["port_pair_group"]["id"]
3082 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003083 except Exception:
3084 self.logger.error(
sousaedu80135b92021-02-17 15:05:18 +01003085 "Creation of Service Function failed, with "
3086 "subsequent deletion failure as well."
3087 )
3088
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003089 self._format_exception(e)
3090
3091 def get_sf(self, sf_id):
3092 self.logger.debug("Getting Service Function %s from VIM", sf_id)
3093 filter_dict = {"id": sf_id}
3094 sf_list = self.get_sf_list(filter_dict)
sousaedu80135b92021-02-17 15:05:18 +01003095
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003096 if len(sf_list) == 0:
tierno72774862020-05-04 11:44:15 +00003097 raise vimconn.VimConnNotFoundException(
sousaedu80135b92021-02-17 15:05:18 +01003098 "Service Function '{}' not found".format(sf_id)
3099 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003100 elif len(sf_list) > 1:
tierno72774862020-05-04 11:44:15 +00003101 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01003102 "Found more than one Service Function with this criteria"
3103 )
3104
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003105 sf = sf_list[0]
sousaedu80135b92021-02-17 15:05:18 +01003106
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003107 return sf
3108
3109 def get_sf_list(self, filter_dict={}):
sousaedu80135b92021-02-17 15:05:18 +01003110 self.logger.debug(
3111 "Getting Service Function from VIM filter: '%s'", str(filter_dict)
3112 )
3113
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003114 try:
3115 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +01003116 filter_dict_os = filter_dict.copy()
sousaedu80135b92021-02-17 15:05:18 +01003117
tierno69b590e2018-03-13 18:52:23 +01003118 if self.api_version3 and "tenant_id" in filter_dict_os:
sousaedu80135b92021-02-17 15:05:18 +01003119 filter_dict_os["project_id"] = filter_dict_os.pop("tenant_id")
3120
tierno69b590e2018-03-13 18:52:23 +01003121 sf_dict = self.neutron.list_sfc_port_pair_groups(**filter_dict_os)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003122 sf_list = sf_dict["port_pair_groups"]
3123 self.__sf_os2mano(sf_list)
sousaedu80135b92021-02-17 15:05:18 +01003124
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003125 return sf_list
sousaedu80135b92021-02-17 15:05:18 +01003126 except (
3127 neExceptions.ConnectionFailed,
3128 ksExceptions.ClientException,
3129 neExceptions.NeutronException,
3130 ConnectionError,
3131 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003132 self._format_exception(e)
3133
3134 def delete_sf(self, sf_id):
3135 self.logger.debug("Deleting Service Function '%s' from VIM", sf_id)
sousaedu80135b92021-02-17 15:05:18 +01003136
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003137 try:
3138 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00003139 self.neutron.delete_sfc_port_pair_group(sf_id)
sousaedu80135b92021-02-17 15:05:18 +01003140
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003141 return sf_id
sousaedu80135b92021-02-17 15:05:18 +01003142 except (
3143 neExceptions.ConnectionFailed,
3144 neExceptions.NeutronException,
3145 ksExceptions.ClientException,
3146 neExceptions.NeutronException,
3147 ConnectionError,
3148 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003149 self._format_exception(e)
3150
3151 def new_sfp(self, name, classifications, sfs, sfc_encap=True, spi=None):
tierno7d782ef2019-10-04 12:56:31 +00003152 self.logger.debug("Adding a new Service Function Path to VIM, named '%s'", name)
sousaedu80135b92021-02-17 15:05:18 +01003153
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003154 try:
3155 new_sfp = None
3156 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00003157 # In networking-sfc the MPLS encapsulation is legacy
3158 # should be used when no full SFC Encapsulation is intended
sousaedu80135b92021-02-17 15:05:18 +01003159 correlation = "mpls"
3160
Igor D.Ccaadc442017-11-06 12:48:48 +00003161 if sfc_encap:
sousaedu80135b92021-02-17 15:05:18 +01003162 correlation = "nsh"
3163
3164 sfp_dict = {
3165 "name": name,
3166 "flow_classifiers": classifications,
3167 "port_pair_groups": sfs,
3168 "chain_parameters": {"correlation": correlation},
3169 }
3170
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003171 if spi:
sousaedu80135b92021-02-17 15:05:18 +01003172 sfp_dict["chain_id"] = spi
3173
3174 new_sfp = self.neutron.create_sfc_port_chain({"port_chain": sfp_dict})
3175
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003176 return new_sfp["port_chain"]["id"]
sousaedu80135b92021-02-17 15:05:18 +01003177 except (
3178 neExceptions.ConnectionFailed,
3179 ksExceptions.ClientException,
3180 neExceptions.NeutronException,
3181 ConnectionError,
3182 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003183 if new_sfp:
3184 try:
sousaedu80135b92021-02-17 15:05:18 +01003185 self.neutron.delete_sfc_port_chain(new_sfp["port_chain"]["id"])
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003186 except Exception:
3187 self.logger.error(
sousaedu80135b92021-02-17 15:05:18 +01003188 "Creation of Service Function Path failed, with "
3189 "subsequent deletion failure as well."
3190 )
3191
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003192 self._format_exception(e)
3193
3194 def get_sfp(self, sfp_id):
3195 self.logger.debug(" Getting Service Function Path %s from VIM", sfp_id)
sousaedu80135b92021-02-17 15:05:18 +01003196
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003197 filter_dict = {"id": sfp_id}
3198 sfp_list = self.get_sfp_list(filter_dict)
sousaedu80135b92021-02-17 15:05:18 +01003199
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003200 if len(sfp_list) == 0:
tierno72774862020-05-04 11:44:15 +00003201 raise vimconn.VimConnNotFoundException(
sousaedu80135b92021-02-17 15:05:18 +01003202 "Service Function Path '{}' not found".format(sfp_id)
3203 )
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003204 elif len(sfp_list) > 1:
tierno72774862020-05-04 11:44:15 +00003205 raise vimconn.VimConnConflictException(
sousaedu80135b92021-02-17 15:05:18 +01003206 "Found more than one Service Function Path with this criteria"
3207 )
3208
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003209 sfp = sfp_list[0]
sousaedu80135b92021-02-17 15:05:18 +01003210
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003211 return sfp
3212
3213 def get_sfp_list(self, filter_dict={}):
sousaedu80135b92021-02-17 15:05:18 +01003214 self.logger.debug(
3215 "Getting Service Function Paths from VIM filter: '%s'", str(filter_dict)
3216 )
3217
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003218 try:
3219 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +01003220 filter_dict_os = filter_dict.copy()
sousaedu80135b92021-02-17 15:05:18 +01003221
tierno69b590e2018-03-13 18:52:23 +01003222 if self.api_version3 and "tenant_id" in filter_dict_os:
sousaedu80135b92021-02-17 15:05:18 +01003223 filter_dict_os["project_id"] = filter_dict_os.pop("tenant_id")
3224
tierno69b590e2018-03-13 18:52:23 +01003225 sfp_dict = self.neutron.list_sfc_port_chains(**filter_dict_os)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003226 sfp_list = sfp_dict["port_chains"]
3227 self.__sfp_os2mano(sfp_list)
sousaedu80135b92021-02-17 15:05:18 +01003228
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003229 return sfp_list
sousaedu80135b92021-02-17 15:05:18 +01003230 except (
3231 neExceptions.ConnectionFailed,
3232 ksExceptions.ClientException,
3233 neExceptions.NeutronException,
3234 ConnectionError,
3235 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003236 self._format_exception(e)
3237
3238 def delete_sfp(self, sfp_id):
tierno7d782ef2019-10-04 12:56:31 +00003239 self.logger.debug("Deleting Service Function Path '%s' from VIM", sfp_id)
sousaedu80135b92021-02-17 15:05:18 +01003240
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003241 try:
3242 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00003243 self.neutron.delete_sfc_port_chain(sfp_id)
sousaedu80135b92021-02-17 15:05:18 +01003244
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003245 return sfp_id
sousaedu80135b92021-02-17 15:05:18 +01003246 except (
3247 neExceptions.ConnectionFailed,
3248 neExceptions.NeutronException,
3249 ksExceptions.ClientException,
3250 neExceptions.NeutronException,
3251 ConnectionError,
3252 ) as e:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00003253 self._format_exception(e)
borsatti8a2dda32019-12-18 15:08:57 +00003254
borsatti8a2dda32019-12-18 15:08:57 +00003255 def refresh_sfps_status(self, sfp_list):
tierno1ec592d2020-06-16 15:29:47 +00003256 """Get the status of the service function path
sousaedu80135b92021-02-17 15:05:18 +01003257 Params: the list of sfp identifiers
3258 Returns a dictionary with:
3259 vm_id: #VIM id of this service function path
3260 status: #Mandatory. Text with one of:
3261 # DELETED (not found at vim)
3262 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
3263 # OTHER (Vim reported other status not understood)
3264 # ERROR (VIM indicates an ERROR status)
3265 # ACTIVE,
3266 # CREATING (on building process)
3267 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
3268 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)F
tierno1ec592d2020-06-16 15:29:47 +00003269 """
3270 sfp_dict = {}
sousaedu80135b92021-02-17 15:05:18 +01003271 self.logger.debug(
3272 "refresh_sfps status: Getting tenant SFP information from VIM"
3273 )
3274
borsatti8a2dda32019-12-18 15:08:57 +00003275 for sfp_id in sfp_list:
tierno1ec592d2020-06-16 15:29:47 +00003276 sfp = {}
sousaedu80135b92021-02-17 15:05:18 +01003277
borsatti8a2dda32019-12-18 15:08:57 +00003278 try:
3279 sfp_vim = self.get_sfp(sfp_id)
sousaedu80135b92021-02-17 15:05:18 +01003280
3281 if sfp_vim["spi"]:
3282 sfp["status"] = vmStatus2manoFormat["ACTIVE"]
borsatti8a2dda32019-12-18 15:08:57 +00003283 else:
sousaedu80135b92021-02-17 15:05:18 +01003284 sfp["status"] = "OTHER"
3285 sfp["error_msg"] = "VIM status reported " + sfp["status"]
borsatti8a2dda32019-12-18 15:08:57 +00003286
sousaedu80135b92021-02-17 15:05:18 +01003287 sfp["vim_info"] = self.serialize(sfp_vim)
borsatti8a2dda32019-12-18 15:08:57 +00003288
sousaedu80135b92021-02-17 15:05:18 +01003289 if sfp_vim.get("fault"):
3290 sfp["error_msg"] = str(sfp_vim["fault"])
tierno72774862020-05-04 11:44:15 +00003291 except vimconn.VimConnNotFoundException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003292 self.logger.error("Exception getting sfp status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003293 sfp["status"] = "DELETED"
3294 sfp["error_msg"] = str(e)
tierno72774862020-05-04 11:44:15 +00003295 except vimconn.VimConnException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003296 self.logger.error("Exception getting sfp status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003297 sfp["status"] = "VIM_ERROR"
3298 sfp["error_msg"] = str(e)
3299
borsatti8a2dda32019-12-18 15:08:57 +00003300 sfp_dict[sfp_id] = sfp
sousaedu80135b92021-02-17 15:05:18 +01003301
borsatti8a2dda32019-12-18 15:08:57 +00003302 return sfp_dict
3303
borsatti8a2dda32019-12-18 15:08:57 +00003304 def refresh_sfis_status(self, sfi_list):
tierno1ec592d2020-06-16 15:29:47 +00003305 """Get the status of the service function instances
sousaedu80135b92021-02-17 15:05:18 +01003306 Params: the list of sfi identifiers
3307 Returns a dictionary with:
3308 vm_id: #VIM id of this service function instance
3309 status: #Mandatory. Text with one of:
3310 # DELETED (not found at vim)
3311 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
3312 # OTHER (Vim reported other status not understood)
3313 # ERROR (VIM indicates an ERROR status)
3314 # ACTIVE,
3315 # CREATING (on building process)
3316 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
3317 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
tierno1ec592d2020-06-16 15:29:47 +00003318 """
3319 sfi_dict = {}
sousaedu80135b92021-02-17 15:05:18 +01003320 self.logger.debug(
3321 "refresh_sfis status: Getting tenant sfi information from VIM"
3322 )
3323
borsatti8a2dda32019-12-18 15:08:57 +00003324 for sfi_id in sfi_list:
tierno1ec592d2020-06-16 15:29:47 +00003325 sfi = {}
sousaedu80135b92021-02-17 15:05:18 +01003326
borsatti8a2dda32019-12-18 15:08:57 +00003327 try:
3328 sfi_vim = self.get_sfi(sfi_id)
sousaedu80135b92021-02-17 15:05:18 +01003329
borsatti8a2dda32019-12-18 15:08:57 +00003330 if sfi_vim:
sousaedu80135b92021-02-17 15:05:18 +01003331 sfi["status"] = vmStatus2manoFormat["ACTIVE"]
borsatti8a2dda32019-12-18 15:08:57 +00003332 else:
sousaedu80135b92021-02-17 15:05:18 +01003333 sfi["status"] = "OTHER"
3334 sfi["error_msg"] = "VIM status reported " + sfi["status"]
borsatti8a2dda32019-12-18 15:08:57 +00003335
sousaedu80135b92021-02-17 15:05:18 +01003336 sfi["vim_info"] = self.serialize(sfi_vim)
borsatti8a2dda32019-12-18 15:08:57 +00003337
sousaedu80135b92021-02-17 15:05:18 +01003338 if sfi_vim.get("fault"):
3339 sfi["error_msg"] = str(sfi_vim["fault"])
tierno72774862020-05-04 11:44:15 +00003340 except vimconn.VimConnNotFoundException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003341 self.logger.error("Exception getting sfi status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003342 sfi["status"] = "DELETED"
3343 sfi["error_msg"] = str(e)
tierno72774862020-05-04 11:44:15 +00003344 except vimconn.VimConnException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003345 self.logger.error("Exception getting sfi status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003346 sfi["status"] = "VIM_ERROR"
3347 sfi["error_msg"] = str(e)
3348
borsatti8a2dda32019-12-18 15:08:57 +00003349 sfi_dict[sfi_id] = sfi
sousaedu80135b92021-02-17 15:05:18 +01003350
borsatti8a2dda32019-12-18 15:08:57 +00003351 return sfi_dict
3352
borsatti8a2dda32019-12-18 15:08:57 +00003353 def refresh_sfs_status(self, sf_list):
tierno1ec592d2020-06-16 15:29:47 +00003354 """Get the status of the service functions
sousaedu80135b92021-02-17 15:05:18 +01003355 Params: the list of sf identifiers
3356 Returns a dictionary with:
3357 vm_id: #VIM id of this service function
3358 status: #Mandatory. Text with one of:
3359 # DELETED (not found at vim)
3360 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
3361 # OTHER (Vim reported other status not understood)
3362 # ERROR (VIM indicates an ERROR status)
3363 # ACTIVE,
3364 # CREATING (on building process)
3365 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
3366 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
tierno1ec592d2020-06-16 15:29:47 +00003367 """
3368 sf_dict = {}
borsatti8a2dda32019-12-18 15:08:57 +00003369 self.logger.debug("refresh_sfs status: Getting tenant sf information from VIM")
sousaedu80135b92021-02-17 15:05:18 +01003370
borsatti8a2dda32019-12-18 15:08:57 +00003371 for sf_id in sf_list:
tierno1ec592d2020-06-16 15:29:47 +00003372 sf = {}
sousaedu80135b92021-02-17 15:05:18 +01003373
borsatti8a2dda32019-12-18 15:08:57 +00003374 try:
3375 sf_vim = self.get_sf(sf_id)
sousaedu80135b92021-02-17 15:05:18 +01003376
borsatti8a2dda32019-12-18 15:08:57 +00003377 if sf_vim:
sousaedu80135b92021-02-17 15:05:18 +01003378 sf["status"] = vmStatus2manoFormat["ACTIVE"]
borsatti8a2dda32019-12-18 15:08:57 +00003379 else:
sousaedu80135b92021-02-17 15:05:18 +01003380 sf["status"] = "OTHER"
3381 sf["error_msg"] = "VIM status reported " + sf_vim["status"]
borsatti8a2dda32019-12-18 15:08:57 +00003382
sousaedu80135b92021-02-17 15:05:18 +01003383 sf["vim_info"] = self.serialize(sf_vim)
borsatti8a2dda32019-12-18 15:08:57 +00003384
sousaedu80135b92021-02-17 15:05:18 +01003385 if sf_vim.get("fault"):
3386 sf["error_msg"] = str(sf_vim["fault"])
tierno72774862020-05-04 11:44:15 +00003387 except vimconn.VimConnNotFoundException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003388 self.logger.error("Exception getting sf status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003389 sf["status"] = "DELETED"
3390 sf["error_msg"] = str(e)
tierno72774862020-05-04 11:44:15 +00003391 except vimconn.VimConnException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003392 self.logger.error("Exception getting sf status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003393 sf["status"] = "VIM_ERROR"
3394 sf["error_msg"] = str(e)
3395
borsatti8a2dda32019-12-18 15:08:57 +00003396 sf_dict[sf_id] = sf
sousaedu80135b92021-02-17 15:05:18 +01003397
borsatti8a2dda32019-12-18 15:08:57 +00003398 return sf_dict
3399
borsatti8a2dda32019-12-18 15:08:57 +00003400 def refresh_classifications_status(self, classification_list):
tierno1ec592d2020-06-16 15:29:47 +00003401 """Get the status of the classifications
sousaedu80135b92021-02-17 15:05:18 +01003402 Params: the list of classification identifiers
3403 Returns a dictionary with:
3404 vm_id: #VIM id of this classifier
3405 status: #Mandatory. Text with one of:
3406 # DELETED (not found at vim)
3407 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
3408 # OTHER (Vim reported other status not understood)
3409 # ERROR (VIM indicates an ERROR status)
3410 # ACTIVE,
3411 # CREATING (on building process)
3412 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
3413 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
tierno1ec592d2020-06-16 15:29:47 +00003414 """
3415 classification_dict = {}
sousaedu80135b92021-02-17 15:05:18 +01003416 self.logger.debug(
3417 "refresh_classifications status: Getting tenant classification information from VIM"
3418 )
3419
borsatti8a2dda32019-12-18 15:08:57 +00003420 for classification_id in classification_list:
tierno1ec592d2020-06-16 15:29:47 +00003421 classification = {}
sousaedu80135b92021-02-17 15:05:18 +01003422
borsatti8a2dda32019-12-18 15:08:57 +00003423 try:
3424 classification_vim = self.get_classification(classification_id)
sousaedu80135b92021-02-17 15:05:18 +01003425
borsatti8a2dda32019-12-18 15:08:57 +00003426 if classification_vim:
sousaedu80135b92021-02-17 15:05:18 +01003427 classification["status"] = vmStatus2manoFormat["ACTIVE"]
borsatti8a2dda32019-12-18 15:08:57 +00003428 else:
sousaedu80135b92021-02-17 15:05:18 +01003429 classification["status"] = "OTHER"
3430 classification["error_msg"] = (
3431 "VIM status reported " + classification["status"]
3432 )
borsatti8a2dda32019-12-18 15:08:57 +00003433
sousaedu80135b92021-02-17 15:05:18 +01003434 classification["vim_info"] = self.serialize(classification_vim)
borsatti8a2dda32019-12-18 15:08:57 +00003435
sousaedu80135b92021-02-17 15:05:18 +01003436 if classification_vim.get("fault"):
3437 classification["error_msg"] = str(classification_vim["fault"])
tierno72774862020-05-04 11:44:15 +00003438 except vimconn.VimConnNotFoundException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003439 self.logger.error("Exception getting classification status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003440 classification["status"] = "DELETED"
3441 classification["error_msg"] = str(e)
tierno72774862020-05-04 11:44:15 +00003442 except vimconn.VimConnException as e:
borsatti8a2dda32019-12-18 15:08:57 +00003443 self.logger.error("Exception getting classification status: %s", str(e))
sousaedu80135b92021-02-17 15:05:18 +01003444 classification["status"] = "VIM_ERROR"
3445 classification["error_msg"] = str(e)
3446
borsatti8a2dda32019-12-18 15:08:57 +00003447 classification_dict[classification_id] = classification
sousaedu80135b92021-02-17 15:05:18 +01003448
borsatti8a2dda32019-12-18 15:08:57 +00003449 return classification_dict