blob: f6bba2f475bfc84ddd046da0efbb26d027a48d5d [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
21'''
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)
tierno7edb6752016-03-21 17:37:52 +010031'''
Eduardo Sousae3c0dbc2018-09-03 11:56:07 +010032__author__ = "Alfonso Tierno, Gerardo Garcia, Pablo Montes, xFlow Research, Igor D.C., Eduardo Sousa"
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +000033__date__ = "$22-sep-2017 23:59:59$"
tierno7edb6752016-03-21 17:37:52 +010034
tierno7d782ef2019-10-04 12:56:31 +000035from osm_ro import vimconn
tierno69b590e2018-03-13 18:52:23 +010036# import json
tiernoae4a8d12016-07-08 12:30:39 +020037import logging
garciadeblas9f8456e2016-09-05 05:02:59 +020038import netaddr
montesmoreno0c8def02016-12-22 12:16:23 +000039import time
tierno36c0b172017-01-12 18:32:28 +010040import yaml
garciadeblas2299e3b2017-01-26 14:35:55 +000041import random
kate721d79b2017-06-24 04:21:38 -070042import re
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +000043import copy
Anderson Bravalheri0446cd52018-08-17 15:26:19 +010044from pprint import pformat
tierno7edb6752016-03-21 17:37:52 +010045
tiernob5cef372017-06-19 15:52:22 +020046from novaclient import client as nClient, exceptions as nvExceptions
47from keystoneauth1.identity import v2, v3
48from keystoneauth1 import session
tierno7edb6752016-03-21 17:37:52 +010049import keystoneclient.exceptions as ksExceptions
tiernof716aea2017-06-21 18:01:40 +020050import keystoneclient.v3.client as ksClient_v3
51import keystoneclient.v2_0.client as ksClient_v2
tiernob5cef372017-06-19 15:52:22 +020052from glanceclient import client as glClient
tierno7edb6752016-03-21 17:37:52 +010053import glanceclient.exc as gl1Exceptions
tiernob5cef372017-06-19 15:52:22 +020054from cinderclient import client as cClient
tierno7d782ef2019-10-04 12:56:31 +000055from http.client import HTTPException # TODO py3 check that this base exception matches python2 httplib.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
tierno40e1bce2017-08-09 09:12:04 +020060
61"""contain the openstack virtual machine status to openmano status"""
tierno7edb6752016-03-21 17:37:52 +010062vmStatus2manoFormat={'ACTIVE':'ACTIVE',
63 'PAUSED':'PAUSED',
64 'SUSPENDED': 'SUSPENDED',
65 'SHUTOFF':'INACTIVE',
66 'BUILD':'BUILD',
67 'ERROR':'ERROR','DELETED':'DELETED'
68 }
69netStatus2manoFormat={'ACTIVE':'ACTIVE','PAUSED':'PAUSED','INACTIVE':'INACTIVE','BUILD':'BUILD','ERROR':'ERROR','DELETED':'DELETED'
70 }
71
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +000072supportedClassificationTypes = ['legacy_flow_classifier']
73
montesmoreno0c8def02016-12-22 12:16:23 +000074#global var to have a timeout creating and deleting volumes
tierno00e3df72017-11-29 17:20:13 +010075volume_timeout = 600
76server_timeout = 600
montesmoreno0c8def02016-12-22 12:16:23 +000077
Anderson Bravalheri0446cd52018-08-17 15:26:19 +010078
79class SafeDumper(yaml.SafeDumper):
80 def represent_data(self, data):
81 # Openstack APIs use custom subclasses of dict and YAML safe dumper
82 # is designed to not handle that (reference issue 142 of pyyaml)
83 if isinstance(data, dict) and data.__class__ != dict:
84 # A simple solution is to convert those items back to dicts
85 data = dict(data.items())
86
87 return super(SafeDumper, self).represent_data(data)
88
89
tierno7edb6752016-03-21 17:37:52 +010090class vimconnector(vimconn.vimconnector):
tiernob3d36742017-03-03 23:51:05 +010091 def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None,
92 log_level=None, config={}, persistent_info={}):
ahmadsa96af9f42017-01-31 16:17:14 +050093 '''using common constructor parameters. In this case
tierno7edb6752016-03-21 17:37:52 +010094 'url' is the keystone authorization url,
95 'url_admin' is not use
96 '''
tiernof716aea2017-06-21 18:01:40 +020097 api_version = config.get('APIversion')
98 if api_version and api_version not in ('v3.3', 'v2.0', '2', '3'):
tiernob5cef372017-06-19 15:52:22 +020099 raise vimconn.vimconnException("Invalid value '{}' for config:APIversion. "
tiernof716aea2017-06-21 18:01:40 +0200100 "Allowed values are 'v3.3', 'v2.0', '2' or '3'".format(api_version))
kate721d79b2017-06-24 04:21:38 -0700101 vim_type = config.get('vim_type')
102 if vim_type and vim_type not in ('vio', 'VIO'):
103 raise vimconn.vimconnException("Invalid value '{}' for config:vim_type."
104 "Allowed values are 'vio' or 'VIO'".format(vim_type))
105
106 if config.get('dataplane_net_vlan_range') is not None:
107 #validate vlan ranges provided by user
garciadeblasebd66722019-01-31 16:01:31 +0000108 self._validate_vlan_ranges(config.get('dataplane_net_vlan_range'), 'dataplane_net_vlan_range')
109
110 if config.get('multisegment_vlan_range') is not None:
111 #validate vlan ranges provided by user
112 self._validate_vlan_ranges(config.get('multisegment_vlan_range'), 'multisegment_vlan_range')
kate721d79b2017-06-24 04:21:38 -0700113
tiernob5cef372017-06-19 15:52:22 +0200114 vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level,
115 config)
tiernob3d36742017-03-03 23:51:05 +0100116
tierno4d1ce222018-04-06 10:41:06 +0200117 if self.config.get("insecure") and self.config.get("ca_cert"):
118 raise vimconn.vimconnException("options insecure and ca_cert are mutually exclusive")
119 self.verify = True
120 if self.config.get("insecure"):
121 self.verify = False
122 if self.config.get("ca_cert"):
123 self.verify = self.config.get("ca_cert")
tierno4d1ce222018-04-06 10:41:06 +0200124
tierno7edb6752016-03-21 17:37:52 +0100125 if not url:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000126 raise TypeError('url param can not be NoneType')
tiernob5cef372017-06-19 15:52:22 +0200127 self.persistent_info = persistent_info
mirabal29356312017-07-27 12:21:22 +0200128 self.availability_zone = persistent_info.get('availability_zone', None)
tiernob5cef372017-06-19 15:52:22 +0200129 self.session = persistent_info.get('session', {'reload_client': True})
tiernoa05b65a2019-02-01 12:30:27 +0000130 self.my_tenant_id = self.session.get('my_tenant_id')
tiernob5cef372017-06-19 15:52:22 +0200131 self.nova = self.session.get('nova')
132 self.neutron = self.session.get('neutron')
133 self.cinder = self.session.get('cinder')
134 self.glance = self.session.get('glance')
tierno1beea862018-07-11 15:47:37 +0200135 # self.glancev1 = self.session.get('glancev1')
tiernof716aea2017-06-21 18:01:40 +0200136 self.keystone = self.session.get('keystone')
137 self.api_version3 = self.session.get('api_version3')
kate721d79b2017-06-24 04:21:38 -0700138 self.vim_type = self.config.get("vim_type")
139 if self.vim_type:
140 self.vim_type = self.vim_type.upper()
141 if self.config.get("use_internal_endpoint"):
142 self.endpoint_type = "internalURL"
143 else:
144 self.endpoint_type = None
montesmoreno0c8def02016-12-22 12:16:23 +0000145
garciadeblasa0a356e2019-12-18 09:32:09 +0100146 logging.getLogger('urllib3').setLevel(logging.WARNING)
147 logging.getLogger('keystoneauth').setLevel(logging.WARNING)
148 logging.getLogger('novaclient').setLevel(logging.WARNING)
tierno73ad9e42016-09-12 18:11:11 +0200149 self.logger = logging.getLogger('openmano.vim.openstack')
kate721d79b2017-06-24 04:21:38 -0700150
tiernoa05b65a2019-02-01 12:30:27 +0000151 # allow security_groups to be a list or a single string
152 if isinstance(self.config.get('security_groups'), str):
153 self.config['security_groups'] = [self.config['security_groups']]
154 self.security_groups_id = None
155
kate721d79b2017-06-24 04:21:38 -0700156 ####### VIO Specific Changes #########
157 if self.vim_type == "VIO":
158 self.logger = logging.getLogger('openmano.vim.vio')
159
tiernofe789902016-09-29 14:20:44 +0000160 if log_level:
kate54616752017-09-05 23:26:28 -0700161 self.logger.setLevel( getattr(logging, log_level))
tiernof716aea2017-06-21 18:01:40 +0200162
163 def __getitem__(self, index):
164 """Get individuals parameters.
165 Throw KeyError"""
166 if index == 'project_domain_id':
167 return self.config.get("project_domain_id")
168 elif index == 'user_domain_id':
169 return self.config.get("user_domain_id")
170 else:
tierno76a3c312017-06-29 16:42:15 +0200171 return vimconn.vimconnector.__getitem__(self, index)
tiernof716aea2017-06-21 18:01:40 +0200172
173 def __setitem__(self, index, value):
174 """Set individuals parameters and it is marked as dirty so to force connection reload.
175 Throw KeyError"""
176 if index == 'project_domain_id':
177 self.config["project_domain_id"] = value
178 elif index == 'user_domain_id':
179 self.config["user_domain_id"] = value
180 else:
181 vimconn.vimconnector.__setitem__(self, index, value)
tiernob5cef372017-06-19 15:52:22 +0200182 self.session['reload_client'] = True
tiernof716aea2017-06-21 18:01:40 +0200183
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100184 def serialize(self, value):
185 """Serialization of python basic types.
186
187 In the case value is not serializable a message will be logged and a
188 simple representation of the data that cannot be converted back to
189 python is returned.
190 """
tierno7d782ef2019-10-04 12:56:31 +0000191 if isinstance(value, str):
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100192 return value
193
194 try:
195 return yaml.dump(value, Dumper=SafeDumper,
196 default_flow_style=True, width=256)
197 except yaml.representer.RepresenterError:
tierno7d782ef2019-10-04 12:56:31 +0000198 self.logger.debug('The following entity cannot be serialized in YAML:\n\n%s\n\n', pformat(value),
199 exc_info=True)
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100200 return str(value)
201
tierno7edb6752016-03-21 17:37:52 +0100202 def _reload_connection(self):
203 '''Called before any operation, it check if credentials has changed
204 Throw keystoneclient.apiclient.exceptions.AuthorizationFailure
205 '''
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100206 #TODO control the timing and possible token timeout, but it seams that python client does this task for us :-)
tiernob5cef372017-06-19 15:52:22 +0200207 if self.session['reload_client']:
tiernof716aea2017-06-21 18:01:40 +0200208 if self.config.get('APIversion'):
209 self.api_version3 = self.config['APIversion'] == 'v3.3' or self.config['APIversion'] == '3'
210 else: # get from ending auth_url that end with v3 or with v2.0
tierno3cb8dc32017-10-24 18:13:19 +0200211 self.api_version3 = self.url.endswith("/v3") or self.url.endswith("/v3/")
tiernof716aea2017-06-21 18:01:40 +0200212 self.session['api_version3'] = self.api_version3
213 if self.api_version3:
tierno3cb8dc32017-10-24 18:13:19 +0200214 if self.config.get('project_domain_id') or self.config.get('project_domain_name'):
215 project_domain_id_default = None
216 else:
217 project_domain_id_default = 'default'
218 if self.config.get('user_domain_id') or self.config.get('user_domain_name'):
219 user_domain_id_default = None
220 else:
221 user_domain_id_default = 'default'
tiernof716aea2017-06-21 18:01:40 +0200222 auth = v3.Password(auth_url=self.url,
tiernob5cef372017-06-19 15:52:22 +0200223 username=self.user,
224 password=self.passwd,
225 project_name=self.tenant_name,
226 project_id=self.tenant_id,
tierno3cb8dc32017-10-24 18:13:19 +0200227 project_domain_id=self.config.get('project_domain_id', project_domain_id_default),
228 user_domain_id=self.config.get('user_domain_id', user_domain_id_default),
229 project_domain_name=self.config.get('project_domain_name'),
230 user_domain_name=self.config.get('user_domain_name'))
ahmadsa95baa272016-11-30 09:14:11 +0500231 else:
tiernof716aea2017-06-21 18:01:40 +0200232 auth = v2.Password(auth_url=self.url,
tiernob5cef372017-06-19 15:52:22 +0200233 username=self.user,
234 password=self.passwd,
235 tenant_name=self.tenant_name,
236 tenant_id=self.tenant_id)
tierno4d1ce222018-04-06 10:41:06 +0200237 sess = session.Session(auth=auth, verify=self.verify)
fatollahy40c6a3f2019-02-19 12:53:40 +0000238 # addedd region_name to keystone, nova, neutron and cinder to support distributed cloud for Wind River Titanium cloud and StarlingX
239 region_name = self.config.get('region_name')
tiernof716aea2017-06-21 18:01:40 +0200240 if self.api_version3:
fatollahy40c6a3f2019-02-19 12:53:40 +0000241 self.keystone = ksClient_v3.Client(session=sess, endpoint_type=self.endpoint_type, region_name=region_name)
tiernof716aea2017-06-21 18:01:40 +0200242 else:
kate721d79b2017-06-24 04:21:38 -0700243 self.keystone = ksClient_v2.Client(session=sess, endpoint_type=self.endpoint_type)
tiernof716aea2017-06-21 18:01:40 +0200244 self.session['keystone'] = self.keystone
montesmoreno9317d302017-08-16 12:48:23 +0200245 # In order to enable microversion functionality an explicit microversion must be specified in 'config'.
246 # This implementation approach is due to the warning message in
247 # https://developer.openstack.org/api-guide/compute/microversions.html
248 # where it is stated that microversion backwards compatibility is not guaranteed and clients should
249 # always require an specific microversion.
250 # To be able to use 'device role tagging' functionality define 'microversion: 2.32' in datacenter config
251 version = self.config.get("microversion")
252 if not version:
253 version = "2.1"
fatollahy40c6a3f2019-02-19 12:53:40 +0000254 # addedd region_name to keystone, nova, neutron and cinder to support distributed cloud for Wind River Titanium cloud and StarlingX
255 self.nova = self.session['nova'] = nClient.Client(str(version), session=sess, endpoint_type=self.endpoint_type, region_name=region_name)
256 self.neutron = self.session['neutron'] = neClient.Client('2.0', session=sess, endpoint_type=self.endpoint_type, region_name=region_name)
257 self.cinder = self.session['cinder'] = cClient.Client(2, session=sess, endpoint_type=self.endpoint_type, region_name=region_name)
tiernoa05b65a2019-02-01 12:30:27 +0000258 try:
259 self.my_tenant_id = self.session['my_tenant_id'] = sess.get_project_id()
260 except Exception as e:
261 self.logger.error("Cannot get project_id from session", exc_info=True)
kate721d79b2017-06-24 04:21:38 -0700262 if self.endpoint_type == "internalURL":
263 glance_service_id = self.keystone.services.list(name="glance")[0].id
264 glance_endpoint = self.keystone.endpoints.list(glance_service_id, interface="internal")[0].url
265 else:
266 glance_endpoint = None
267 self.glance = self.session['glance'] = glClient.Client(2, session=sess, endpoint=glance_endpoint)
tiernoa05b65a2019-02-01 12:30:27 +0000268 # using version 1 of glance client in new_image()
tierno1beea862018-07-11 15:47:37 +0200269 # self.glancev1 = self.session['glancev1'] = glClient.Client('1', session=sess,
270 # endpoint=glance_endpoint)
tiernob5cef372017-06-19 15:52:22 +0200271 self.session['reload_client'] = False
272 self.persistent_info['session'] = self.session
mirabal29356312017-07-27 12:21:22 +0200273 # add availablity zone info inside self.persistent_info
274 self._set_availablity_zones()
275 self.persistent_info['availability_zone'] = self.availability_zone
tiernoa05b65a2019-02-01 12:30:27 +0000276 self.security_groups_id = None # force to get again security_groups_ids next time they are needed
ahmadsa95baa272016-11-30 09:14:11 +0500277
tierno7edb6752016-03-21 17:37:52 +0100278 def __net_os2mano(self, net_list_dict):
279 '''Transform the net openstack format to mano format
280 net_list_dict can be a list of dict or a single dict'''
281 if type(net_list_dict) is dict:
282 net_list_=(net_list_dict,)
283 elif type(net_list_dict) is list:
284 net_list_=net_list_dict
285 else:
286 raise TypeError("param net_list_dict must be a list or a dictionary")
287 for net in net_list_:
288 if net.get('provider:network_type') == "vlan":
289 net['type']='data'
290 else:
291 net['type']='bridge'
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +0200292
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000293 def __classification_os2mano(self, class_list_dict):
294 """Transform the openstack format (Flow Classifier) to mano format
295 (Classification) class_list_dict can be a list of dict or a single dict
296 """
297 if isinstance(class_list_dict, dict):
298 class_list_ = [class_list_dict]
299 elif isinstance(class_list_dict, list):
300 class_list_ = class_list_dict
301 else:
302 raise TypeError(
303 "param class_list_dict must be a list or a dictionary")
304 for classification in class_list_:
305 id = classification.pop('id')
306 name = classification.pop('name')
307 description = classification.pop('description')
308 project_id = classification.pop('project_id')
309 tenant_id = classification.pop('tenant_id')
310 original_classification = copy.deepcopy(classification)
311 classification.clear()
312 classification['ctype'] = 'legacy_flow_classifier'
313 classification['definition'] = original_classification
314 classification['id'] = id
315 classification['name'] = name
316 classification['description'] = description
317 classification['project_id'] = project_id
318 classification['tenant_id'] = tenant_id
319
320 def __sfi_os2mano(self, sfi_list_dict):
321 """Transform the openstack format (Port Pair) to mano format (SFI)
322 sfi_list_dict can be a list of dict or a single dict
323 """
324 if isinstance(sfi_list_dict, dict):
325 sfi_list_ = [sfi_list_dict]
326 elif isinstance(sfi_list_dict, list):
327 sfi_list_ = sfi_list_dict
328 else:
329 raise TypeError(
330 "param sfi_list_dict must be a list or a dictionary")
331 for sfi in sfi_list_:
332 sfi['ingress_ports'] = []
333 sfi['egress_ports'] = []
334 if sfi.get('ingress'):
335 sfi['ingress_ports'].append(sfi['ingress'])
336 if sfi.get('egress'):
337 sfi['egress_ports'].append(sfi['egress'])
338 del sfi['ingress']
339 del sfi['egress']
340 params = sfi.get('service_function_parameters')
341 sfc_encap = False
342 if params:
343 correlation = params.get('correlation')
344 if correlation:
345 sfc_encap = True
346 sfi['sfc_encap'] = sfc_encap
347 del sfi['service_function_parameters']
348
349 def __sf_os2mano(self, sf_list_dict):
350 """Transform the openstack format (Port Pair Group) to mano format (SF)
351 sf_list_dict can be a list of dict or a single dict
352 """
353 if isinstance(sf_list_dict, dict):
354 sf_list_ = [sf_list_dict]
355 elif isinstance(sf_list_dict, list):
356 sf_list_ = sf_list_dict
357 else:
358 raise TypeError(
359 "param sf_list_dict must be a list or a dictionary")
360 for sf in sf_list_:
361 del sf['port_pair_group_parameters']
362 sf['sfis'] = sf['port_pairs']
363 del sf['port_pairs']
364
365 def __sfp_os2mano(self, sfp_list_dict):
366 """Transform the openstack format (Port Chain) to mano format (SFP)
367 sfp_list_dict can be a list of dict or a single dict
368 """
369 if isinstance(sfp_list_dict, dict):
370 sfp_list_ = [sfp_list_dict]
371 elif isinstance(sfp_list_dict, list):
372 sfp_list_ = sfp_list_dict
373 else:
374 raise TypeError(
375 "param sfp_list_dict must be a list or a dictionary")
376 for sfp in sfp_list_:
377 params = sfp.pop('chain_parameters')
378 sfc_encap = False
379 if params:
380 correlation = params.get('correlation')
381 if correlation:
382 sfc_encap = True
383 sfp['sfc_encap'] = sfc_encap
384 sfp['spi'] = sfp.pop('chain_id')
385 sfp['classifications'] = sfp.pop('flow_classifiers')
386 sfp['service_functions'] = sfp.pop('port_pair_groups')
387
388 # placeholder for now; read TODO note below
389 def _validate_classification(self, type, definition):
390 # only legacy_flow_classifier Type is supported at this point
391 return True
392 # TODO(igordcard): this method should be an abstract method of an
393 # abstract Classification class to be implemented by the specific
394 # Types. Also, abstract vimconnector should call the validation
395 # method before the implemented VIM connectors are called.
396
tiernoae4a8d12016-07-08 12:30:39 +0200397 def _format_exception(self, exception):
398 '''Transform a keystone, nova, neutron exception into a vimconn exception'''
tiernode12f782019-04-05 12:46:42 +0000399
tiernode12f782019-04-05 12:46:42 +0000400 message_error = exception.message
tiernode12f782019-04-05 12:46:42 +0000401
402 if isinstance(exception, (neExceptions.NetworkNotFoundClient, nvExceptions.NotFound, ksExceptions.NotFound,
403 gl1Exceptions.HTTPNotFound)):
404 raise vimconn.vimconnNotFoundException(type(exception).__name__ + ": " + message_error)
shashankjain3c83a212018-10-04 13:05:46 +0530405 elif isinstance(exception, (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError,
406 ConnectionError, ksExceptions.ConnectionError, neExceptions.ConnectionFailed)):
tiernode12f782019-04-05 12:46:42 +0000407 raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + message_error)
shashankjain3c83a212018-10-04 13:05:46 +0530408 elif isinstance(exception, (KeyError, nvExceptions.BadRequest, ksExceptions.BadRequest)):
tiernode12f782019-04-05 12:46:42 +0000409 raise vimconn.vimconnException(type(exception).__name__ + ": " + message_error)
anwarsc76a3ee2018-10-04 14:05:32 +0530410 elif isinstance(exception, (nvExceptions.ClientException, ksExceptions.ClientException,
411 neExceptions.NeutronException)):
tiernode12f782019-04-05 12:46:42 +0000412 raise vimconn.vimconnUnexpectedResponse(type(exception).__name__ + ": " + message_error)
tiernoae4a8d12016-07-08 12:30:39 +0200413 elif isinstance(exception, nvExceptions.Conflict):
tiernode12f782019-04-05 12:46:42 +0000414 raise vimconn.vimconnConflictException(type(exception).__name__ + ": " + message_error)
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +0200415 elif isinstance(exception, vimconn.vimconnException):
tierno41a69812018-02-16 14:34:33 +0100416 raise exception
tiernof716aea2017-06-21 18:01:40 +0200417 else: # ()
tiernode12f782019-04-05 12:46:42 +0000418 self.logger.error("General Exception " + message_error, exc_info=True)
419 raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + message_error)
tiernoae4a8d12016-07-08 12:30:39 +0200420
tiernoa05b65a2019-02-01 12:30:27 +0000421 def _get_ids_from_name(self):
422 """
423 Obtain ids from name of tenant and security_groups. Store at self .security_groups_id"
424 :return: None
425 """
426 # get tenant_id if only tenant_name is supplied
427 self._reload_connection()
428 if not self.my_tenant_id:
429 raise vimconn.vimconnConnectionException("Error getting tenant information from name={} id={}".
430 format(self.tenant_name, self.tenant_id))
431 if self.config.get('security_groups') and not self.security_groups_id:
432 # convert from name to id
433 neutron_sg_list = self.neutron.list_security_groups(tenant_id=self.my_tenant_id)["security_groups"]
434
435 self.security_groups_id = []
436 for sg in self.config.get('security_groups'):
437 for neutron_sg in neutron_sg_list:
438 if sg in (neutron_sg["id"], neutron_sg["name"]):
439 self.security_groups_id.append(neutron_sg["id"])
440 break
441 else:
442 self.security_groups_id = None
443 raise vimconn.vimconnConnectionException("Not found security group {} for this tenant".format(sg))
444
tierno5509c2e2019-07-04 16:23:20 +0000445 def check_vim_connectivity(self):
446 # just get network list to check connectivity and credentials
447 self.get_network_list(filter_dict={})
448
tiernoae4a8d12016-07-08 12:30:39 +0200449 def get_tenant_list(self, filter_dict={}):
450 '''Obtain tenants of VIM
451 filter_dict can contain the following keys:
452 name: filter by tenant name
453 id: filter by tenant uuid/id
454 <other VIM specific>
455 Returns the tenant list of dictionaries: [{'name':'<name>, 'id':'<id>, ...}, ...]
456 '''
ahmadsa95baa272016-11-30 09:14:11 +0500457 self.logger.debug("Getting tenants from VIM filter: '%s'", str(filter_dict))
tiernoae4a8d12016-07-08 12:30:39 +0200458 try:
459 self._reload_connection()
tiernof716aea2017-06-21 18:01:40 +0200460 if self.api_version3:
461 project_class_list = self.keystone.projects.list(name=filter_dict.get("name"))
ahmadsa95baa272016-11-30 09:14:11 +0500462 else:
tiernof716aea2017-06-21 18:01:40 +0200463 project_class_list = self.keystone.tenants.findall(**filter_dict)
ahmadsa95baa272016-11-30 09:14:11 +0500464 project_list=[]
465 for project in project_class_list:
tiernof716aea2017-06-21 18:01:40 +0200466 if filter_dict.get('id') and filter_dict["id"] != project.id:
467 continue
ahmadsa95baa272016-11-30 09:14:11 +0500468 project_list.append(project.to_dict())
469 return project_list
tiernof716aea2017-06-21 18:01:40 +0200470 except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200471 self._format_exception(e)
472
473 def new_tenant(self, tenant_name, tenant_description):
474 '''Adds a new tenant to openstack VIM. Returns the tenant identifier'''
475 self.logger.debug("Adding a new tenant name: %s", tenant_name)
476 try:
477 self._reload_connection()
tiernof716aea2017-06-21 18:01:40 +0200478 if self.api_version3:
479 project = self.keystone.projects.create(tenant_name, self.config.get("project_domain_id", "default"),
480 description=tenant_description, is_domain=False)
ahmadsa95baa272016-11-30 09:14:11 +0500481 else:
tiernof716aea2017-06-21 18:01:40 +0200482 project = self.keystone.tenants.create(tenant_name, tenant_description)
ahmadsa95baa272016-11-30 09:14:11 +0500483 return project.id
shashankjain3c83a212018-10-04 13:05:46 +0530484 except (ksExceptions.ConnectionError, ksExceptions.ClientException, ksExceptions.BadRequest, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200485 self._format_exception(e)
486
487 def delete_tenant(self, tenant_id):
488 '''Delete a tenant from openstack VIM. Returns the old tenant identifier'''
489 self.logger.debug("Deleting tenant %s from VIM", tenant_id)
490 try:
491 self._reload_connection()
tiernof716aea2017-06-21 18:01:40 +0200492 if self.api_version3:
ahmadsa95baa272016-11-30 09:14:11 +0500493 self.keystone.projects.delete(tenant_id)
494 else:
495 self.keystone.tenants.delete(tenant_id)
tiernoae4a8d12016-07-08 12:30:39 +0200496 return tenant_id
tierno6869ae72020-01-09 17:37:34 +0000497 except (ksExceptions.ConnectionError, ksExceptions.ClientException, ksExceptions.NotFound, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200498 self._format_exception(e)
ahmadsa95baa272016-11-30 09:14:11 +0500499
tierno6869ae72020-01-09 17:37:34 +0000500 def new_network(self, net_name, net_type, ip_profile=None, shared=False, provider_network_profile=None):
garciadeblasebd66722019-01-31 16:01:31 +0000501 """Adds a tenant network to VIM
502 Params:
503 'net_name': name of the network
504 'net_type': one of:
505 'bridge': overlay isolated network
506 'data': underlay E-LAN network for Passthrough and SRIOV interfaces
507 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces.
508 'ip_profile': is a dict containing the IP parameters of the network
509 'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented)
510 'subnet_address': ip_prefix_schema, that is X.X.X.X/Y
511 'gateway_address': (Optional) ip_schema, that is X.X.X.X
512 'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X]
513 'dhcp_enabled': True or False
514 'dhcp_start_address': ip_schema, first IP to grant
515 'dhcp_count': number of IPs to grant.
516 'shared': if this network can be seen/use by other tenants/organization
kbsuba85c54d2019-10-17 16:30:32 +0000517 'provider_network_profile': (optional) contains {segmentation-id: vlan, provider-network: vim_netowrk}
garciadeblasebd66722019-01-31 16:01:31 +0000518 Returns a tuple with the network identifier and created_items, or raises an exception on error
519 created_items can be None or a dictionary where this method can include key-values that will be passed to
520 the method delete_network. Can be used to store created segments, created l2gw connections, etc.
521 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
522 as not present.
523 """
tiernoae4a8d12016-07-08 12:30:39 +0200524 self.logger.debug("Adding a new network to VIM name '%s', type '%s'", net_name, net_type)
garciadeblasebd66722019-01-31 16:01:31 +0000525 # self.logger.debug(">>>>>>>>>>>>>>>>>> IP profile %s", str(ip_profile))
kbsuba85c54d2019-10-17 16:30:32 +0000526
tierno7edb6752016-03-21 17:37:52 +0100527 try:
kbsuba85c54d2019-10-17 16:30:32 +0000528 vlan = None
529 if provider_network_profile:
530 vlan = provider_network_profile.get("segmentation-id")
garciadeblasedca7b32016-09-29 14:01:52 +0000531 new_net = None
garciadeblasebd66722019-01-31 16:01:31 +0000532 created_items = {}
tierno7edb6752016-03-21 17:37:52 +0100533 self._reload_connection()
534 network_dict = {'name': net_name, 'admin_state_up': True}
tierno6869ae72020-01-09 17:37:34 +0000535 if net_type in ("data", "ptp"):
536 provider_physical_network = None
537 if provider_network_profile and provider_network_profile.get("physical-network"):
538 provider_physical_network = provider_network_profile.get("physical-network")
539 # provider-network must be one of the dataplane_physcial_netowrk if this is a list. If it is string
540 # or not declared, just ignore the checking
541 if isinstance(self.config.get('dataplane_physical_net'), (tuple, list)) and \
542 provider_physical_network not in self.config['dataplane_physical_net']:
543 raise vimconn.vimconnConflictException(
544 "Invalid parameter 'provider-network:physical-network' for network creation. '{}' is not "
545 "one of the declared list at VIM_config:dataplane_physical_net".format(
546 provider_physical_network))
547 if not provider_physical_network: # use the default dataplane_physical_net
548 provider_physical_network = self.config.get('dataplane_physical_net')
549 # if it is non empty list, use the first value. If it is a string use the value directly
550 if isinstance(provider_physical_network, (tuple, list)) and provider_physical_network:
551 provider_physical_network = provider_physical_network[0]
552
553 if not provider_physical_network:
554 raise vimconn.vimconnConflictException("You must provide a 'dataplane_physical_net' at VIM_config "
555 "for creating underlay networks. or use the NS instantiation"
556 " parameter provider-network:physical-network for the VLD")
557
garciadeblasebd66722019-01-31 16:01:31 +0000558 if not self.config.get('multisegment_support'):
tierno6869ae72020-01-09 17:37:34 +0000559 network_dict["provider:physical_network"] = provider_physical_network
garciadeblasebd66722019-01-31 16:01:31 +0000560 network_dict["provider:network_type"] = "vlan"
tierno6869ae72020-01-09 17:37:34 +0000561 if vlan:
562 network_dict["provider:segmentation_id"] = vlan
garciadeblasebd66722019-01-31 16:01:31 +0000563 else:
tierno6869ae72020-01-09 17:37:34 +0000564 # Multi-segment case
garciadeblasebd66722019-01-31 16:01:31 +0000565 segment_list = []
tierno6869ae72020-01-09 17:37:34 +0000566 segment1_dict = {
567 "provider:physical_network": '',
568 "provider:network_type": 'vxlan'
569 }
garciadeblasebd66722019-01-31 16:01:31 +0000570 segment_list.append(segment1_dict)
tierno6869ae72020-01-09 17:37:34 +0000571 segment2_dict = {
572 "provider:physical_network": provider_physical_network,
573 "provider:network_type": "vlan"
574 }
575 if vlan:
576 segment2_dict["provider:segmentation_id"] = vlan
577 elif self.config.get('multisegment_vlan_range'):
garciadeblasebd66722019-01-31 16:01:31 +0000578 vlanID = self._generate_multisegment_vlanID()
579 segment2_dict["provider:segmentation_id"] = vlanID
580 # else
581 # raise vimconn.vimconnConflictException(
582 # "You must provide 'multisegment_vlan_range' at config dict before creating a multisegment network")
583 segment_list.append(segment2_dict)
584 network_dict["segments"] = segment_list
kate721d79b2017-06-24 04:21:38 -0700585
tierno6869ae72020-01-09 17:37:34 +0000586 # VIO Specific Changes. It needs a concrete VLAN
587 if self.vim_type == "VIO" and vlan is None:
588 if self.config.get('dataplane_net_vlan_range') is None:
589 raise vimconn.vimconnConflictException(
590 "You must provide 'dataplane_net_vlan_range' in format [start_ID - end_ID] at VIM_config "
591 "for creating underlay networks")
592 network_dict["provider:segmentation_id"] = self._generate_vlanID()
kate721d79b2017-06-24 04:21:38 -0700593
garciadeblasebd66722019-01-31 16:01:31 +0000594 network_dict["shared"] = shared
anwarsff168192019-05-06 11:23:07 +0530595 if self.config.get("disable_network_port_security"):
596 network_dict["port_security_enabled"] = False
garciadeblasebd66722019-01-31 16:01:31 +0000597 new_net = self.neutron.create_network({'network':network_dict})
598 # print new_net
599 # create subnetwork, even if there is no profile
garciadeblas9f8456e2016-09-05 05:02:59 +0200600 if not ip_profile:
601 ip_profile = {}
tierno41a69812018-02-16 14:34:33 +0100602 if not ip_profile.get('subnet_address'):
garciadeblas2299e3b2017-01-26 14:35:55 +0000603 #Fake subnet is required
604 subnet_rand = random.randint(0, 255)
605 ip_profile['subnet_address'] = "192.168.{}.0/24".format(subnet_rand)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000606 if 'ip_version' not in ip_profile:
garciadeblas9f8456e2016-09-05 05:02:59 +0200607 ip_profile['ip_version'] = "IPv4"
garciadeblasebd66722019-01-31 16:01:31 +0000608 subnet = {"name": net_name+"-subnet",
tierno7edb6752016-03-21 17:37:52 +0100609 "network_id": new_net["network"]["id"],
garciadeblas9f8456e2016-09-05 05:02:59 +0200610 "ip_version": 4 if ip_profile['ip_version']=="IPv4" else 6,
611 "cidr": ip_profile['subnet_address']
tierno7edb6752016-03-21 17:37:52 +0100612 }
tiernoa1fb4462017-06-30 12:25:50 +0200613 # Gateway should be set to None if not needed. Otherwise openstack assigns one by default
tierno41a69812018-02-16 14:34:33 +0100614 if ip_profile.get('gateway_address'):
tierno55d234c2018-07-04 18:29:21 +0200615 subnet['gateway_ip'] = ip_profile['gateway_address']
616 else:
617 subnet['gateway_ip'] = None
garciadeblasedca7b32016-09-29 14:01:52 +0000618 if ip_profile.get('dns_address'):
tierno455612d2017-05-30 16:40:10 +0200619 subnet['dns_nameservers'] = ip_profile['dns_address'].split(";")
garciadeblas9f8456e2016-09-05 05:02:59 +0200620 if 'dhcp_enabled' in ip_profile:
tierno41a69812018-02-16 14:34:33 +0100621 subnet['enable_dhcp'] = False if \
622 ip_profile['dhcp_enabled']=="false" or ip_profile['dhcp_enabled']==False else True
623 if ip_profile.get('dhcp_start_address'):
tiernoa1fb4462017-06-30 12:25:50 +0200624 subnet['allocation_pools'] = []
garciadeblas9f8456e2016-09-05 05:02:59 +0200625 subnet['allocation_pools'].append(dict())
626 subnet['allocation_pools'][0]['start'] = ip_profile['dhcp_start_address']
tierno41a69812018-02-16 14:34:33 +0100627 if ip_profile.get('dhcp_count'):
garciadeblas9f8456e2016-09-05 05:02:59 +0200628 #parts = ip_profile['dhcp_start_address'].split('.')
629 #ip_int = (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3])
630 ip_int = int(netaddr.IPAddress(ip_profile['dhcp_start_address']))
garciadeblas21d795b2016-09-29 17:31:46 +0200631 ip_int += ip_profile['dhcp_count'] - 1
garciadeblas9f8456e2016-09-05 05:02:59 +0200632 ip_str = str(netaddr.IPAddress(ip_int))
633 subnet['allocation_pools'][0]['end'] = ip_str
garciadeblasedca7b32016-09-29 14:01:52 +0000634 #self.logger.debug(">>>>>>>>>>>>>>>>>> Subnet: %s", str(subnet))
tierno7edb6752016-03-21 17:37:52 +0100635 self.neutron.create_subnet({"subnet": subnet} )
garciadeblasebd66722019-01-31 16:01:31 +0000636
637 if net_type == "data" and self.config.get('multisegment_support'):
638 if self.config.get('l2gw_support'):
639 l2gw_list = self.neutron.list_l2_gateways().get("l2_gateways", ())
640 for l2gw in l2gw_list:
641 l2gw_conn = {}
642 l2gw_conn["l2_gateway_id"] = l2gw["id"]
643 l2gw_conn["network_id"] = new_net["network"]["id"]
644 l2gw_conn["segmentation_id"] = str(vlanID)
645 new_l2gw_conn = self.neutron.create_l2_gateway_connection({"l2_gateway_connection": l2gw_conn})
646 created_items["l2gwconn:" + str(new_l2gw_conn["l2_gateway_connection"]["id"])] = True
647 return new_net["network"]["id"], created_items
tierno41a69812018-02-16 14:34:33 +0100648 except Exception as e:
garciadeblasebd66722019-01-31 16:01:31 +0000649 #delete l2gw connections (if any) before deleting the network
650 for k, v in created_items.items():
651 if not v: # skip already deleted
652 continue
653 try:
654 k_item, _, k_id = k.partition(":")
655 if k_item == "l2gwconn":
656 self.neutron.delete_l2_gateway_connection(k_id)
657 except Exception as e2:
658 self.logger.error("Error deleting l2 gateway connection: {}: {}".format(type(e2).__name__, e2))
garciadeblasedca7b32016-09-29 14:01:52 +0000659 if new_net:
660 self.neutron.delete_network(new_net['network']['id'])
tiernoae4a8d12016-07-08 12:30:39 +0200661 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100662
663 def get_network_list(self, filter_dict={}):
664 '''Obtain tenant networks of VIM
665 Filter_dict can be:
666 name: network name
667 id: network uuid
668 shared: boolean
669 tenant_id: tenant
670 admin_state_up: boolean
671 status: 'ACTIVE'
672 Returns the network list of dictionaries
673 '''
tiernoae4a8d12016-07-08 12:30:39 +0200674 self.logger.debug("Getting network from VIM filter: '%s'", str(filter_dict))
tierno7edb6752016-03-21 17:37:52 +0100675 try:
676 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +0100677 filter_dict_os = filter_dict.copy()
678 if self.api_version3 and "tenant_id" in filter_dict_os:
679 filter_dict_os['project_id'] = filter_dict_os.pop('tenant_id') #T ODO check
680 net_dict = self.neutron.list_networks(**filter_dict_os)
tierno00e3df72017-11-29 17:20:13 +0100681 net_list = net_dict["networks"]
tierno7edb6752016-03-21 17:37:52 +0100682 self.__net_os2mano(net_list)
tiernoae4a8d12016-07-08 12:30:39 +0200683 return net_list
tierno8e995ce2016-09-22 08:13:00 +0000684 except (neExceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200685 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100686
tiernoae4a8d12016-07-08 12:30:39 +0200687 def get_network(self, net_id):
688 '''Obtain details of network from VIM
689 Returns the network information from a network id'''
690 self.logger.debug(" Getting tenant network %s from VIM", net_id)
tierno7edb6752016-03-21 17:37:52 +0100691 filter_dict={"id": net_id}
tiernoae4a8d12016-07-08 12:30:39 +0200692 net_list = self.get_network_list(filter_dict)
tierno7edb6752016-03-21 17:37:52 +0100693 if len(net_list)==0:
tiernoae4a8d12016-07-08 12:30:39 +0200694 raise vimconn.vimconnNotFoundException("Network '{}' not found".format(net_id))
tierno7edb6752016-03-21 17:37:52 +0100695 elif len(net_list)>1:
tiernoae4a8d12016-07-08 12:30:39 +0200696 raise vimconn.vimconnConflictException("Found more than one network with this criteria")
tierno7edb6752016-03-21 17:37:52 +0100697 net = net_list[0]
698 subnets=[]
699 for subnet_id in net.get("subnets", () ):
700 try:
701 subnet = self.neutron.show_subnet(subnet_id)
702 except Exception as e:
tiernoae4a8d12016-07-08 12:30:39 +0200703 self.logger.error("osconnector.get_network(): Error getting subnet %s %s" % (net_id, str(e)))
704 subnet = {"id": subnet_id, "fault": str(e)}
tierno7edb6752016-03-21 17:37:52 +0100705 subnets.append(subnet)
706 net["subnets"] = subnets
Pablo Montes Moreno51e553b2017-03-23 16:39:12 +0100707 net["encapsulation"] = net.get('provider:network_type')
Anderson Bravalheri0fb70282018-12-16 19:28:37 +0000708 net["encapsulation_type"] = net.get('provider:network_type')
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +0100709 net["segmentation_id"] = net.get('provider:segmentation_id')
Anderson Bravalheri0fb70282018-12-16 19:28:37 +0000710 net["encapsulation_id"] = net.get('provider:segmentation_id')
tiernoae4a8d12016-07-08 12:30:39 +0200711 return net
tierno7edb6752016-03-21 17:37:52 +0100712
garciadeblasebd66722019-01-31 16:01:31 +0000713 def delete_network(self, net_id, created_items=None):
714 """
715 Removes a tenant network from VIM and its associated elements
716 :param net_id: VIM identifier of the network, provided by method new_network
717 :param created_items: dictionary with extra items to be deleted. provided by method new_network
718 Returns the network identifier or raises an exception upon error or when network is not found
719 """
tiernoae4a8d12016-07-08 12:30:39 +0200720 self.logger.debug("Deleting network '%s' from VIM", net_id)
garciadeblasebd66722019-01-31 16:01:31 +0000721 if created_items == None:
722 created_items = {}
tierno7edb6752016-03-21 17:37:52 +0100723 try:
724 self._reload_connection()
garciadeblasebd66722019-01-31 16:01:31 +0000725 #delete l2gw connections (if any) before deleting the network
726 for k, v in created_items.items():
727 if not v: # skip already deleted
728 continue
729 try:
730 k_item, _, k_id = k.partition(":")
731 if k_item == "l2gwconn":
732 self.neutron.delete_l2_gateway_connection(k_id)
733 except Exception as e:
734 self.logger.error("Error deleting l2 gateway connection: {}: {}".format(type(e).__name__, e))
tierno7edb6752016-03-21 17:37:52 +0100735 #delete VM ports attached to this networks before the network
736 ports = self.neutron.list_ports(network_id=net_id)
737 for p in ports['ports']:
738 try:
739 self.neutron.delete_port(p["id"])
740 except Exception as e:
tiernoae4a8d12016-07-08 12:30:39 +0200741 self.logger.error("Error deleting port %s: %s", p["id"], str(e))
tierno7edb6752016-03-21 17:37:52 +0100742 self.neutron.delete_network(net_id)
tiernoae4a8d12016-07-08 12:30:39 +0200743 return net_id
744 except (neExceptions.ConnectionFailed, neExceptions.NetworkNotFoundClient, neExceptions.NeutronException,
tierno8e995ce2016-09-22 08:13:00 +0000745 ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200746 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100747
tiernoae4a8d12016-07-08 12:30:39 +0200748 def refresh_nets_status(self, net_list):
749 '''Get the status of the networks
750 Params: the list of network identifiers
751 Returns a dictionary with:
752 net_id: #VIM id of this network
753 status: #Mandatory. Text with one of:
754 # DELETED (not found at vim)
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100755 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
tiernoae4a8d12016-07-08 12:30:39 +0200756 # OTHER (Vim reported other status not understood)
757 # ERROR (VIM indicates an ERROR status)
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100758 # ACTIVE, INACTIVE, DOWN (admin down),
tiernoae4a8d12016-07-08 12:30:39 +0200759 # BUILD (on building process)
760 #
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100761 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
tiernoae4a8d12016-07-08 12:30:39 +0200762 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
763
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000764 '''
tiernoae4a8d12016-07-08 12:30:39 +0200765 net_dict={}
766 for net_id in net_list:
767 net = {}
768 try:
769 net_vim = self.get_network(net_id)
770 if net_vim['status'] in netStatus2manoFormat:
771 net["status"] = netStatus2manoFormat[ net_vim['status'] ]
772 else:
773 net["status"] = "OTHER"
774 net["error_msg"] = "VIM status reported " + net_vim['status']
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000775
tierno8e995ce2016-09-22 08:13:00 +0000776 if net['status'] == "ACTIVE" and not net_vim['admin_state_up']:
tiernoae4a8d12016-07-08 12:30:39 +0200777 net['status'] = 'DOWN'
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100778
779 net['vim_info'] = self.serialize(net_vim)
780
tiernoae4a8d12016-07-08 12:30:39 +0200781 if net_vim.get('fault'): #TODO
782 net['error_msg'] = str(net_vim['fault'])
783 except vimconn.vimconnNotFoundException as e:
784 self.logger.error("Exception getting net status: %s", str(e))
785 net['status'] = "DELETED"
786 net['error_msg'] = str(e)
787 except vimconn.vimconnException as e:
788 self.logger.error("Exception getting net status: %s", str(e))
789 net['status'] = "VIM_ERROR"
790 net['error_msg'] = str(e)
791 net_dict[net_id] = net
792 return net_dict
793
794 def get_flavor(self, flavor_id):
795 '''Obtain flavor details from the VIM. Returns the flavor dict details'''
796 self.logger.debug("Getting flavor '%s'", flavor_id)
tierno7edb6752016-03-21 17:37:52 +0100797 try:
798 self._reload_connection()
799 flavor = self.nova.flavors.find(id=flavor_id)
800 #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
tiernoae4a8d12016-07-08 12:30:39 +0200801 return flavor.to_dict()
tierno8e995ce2016-09-22 08:13:00 +0000802 except (nvExceptions.NotFound, nvExceptions.ClientException, ksExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200803 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100804
tiernocf157a82017-01-30 14:07:06 +0100805 def get_flavor_id_from_data(self, flavor_dict):
806 """Obtain flavor id that match the flavor description
807 Returns the flavor_id or raises a vimconnNotFoundException
tiernoe26fc7a2017-05-30 14:43:03 +0200808 flavor_dict: contains the required ram, vcpus, disk
809 If 'use_existing_flavors' is set to True at config, the closer flavor that provides same or more ram, vcpus
810 and disk is returned. Otherwise a flavor with exactly same ram, vcpus and disk is returned or a
811 vimconnNotFoundException is raised
tiernocf157a82017-01-30 14:07:06 +0100812 """
tiernoe26fc7a2017-05-30 14:43:03 +0200813 exact_match = False if self.config.get('use_existing_flavors') else True
tiernocf157a82017-01-30 14:07:06 +0100814 try:
815 self._reload_connection()
tiernoe26fc7a2017-05-30 14:43:03 +0200816 flavor_candidate_id = None
817 flavor_candidate_data = (10000, 10000, 10000)
818 flavor_target = (flavor_dict["ram"], flavor_dict["vcpus"], flavor_dict["disk"])
819 # numa=None
anwarsae5f52c2019-04-22 10:35:27 +0530820 extended = flavor_dict.get("extended", {})
821 if extended:
tiernocf157a82017-01-30 14:07:06 +0100822 #TODO
tiernob7aa1bb2019-07-24 15:47:16 +0000823 raise vimconn.vimconnNotFoundException("Flavor with EPA still not implemented")
tiernocf157a82017-01-30 14:07:06 +0100824 # if len(numas) > 1:
825 # raise vimconn.vimconnNotFoundException("Cannot find any flavor with more than one numa")
826 # numa=numas[0]
827 # numas = extended.get("numas")
828 for flavor in self.nova.flavors.list():
829 epa = flavor.get_keys()
830 if epa:
831 continue
tiernoe26fc7a2017-05-30 14:43:03 +0200832 # TODO
833 flavor_data = (flavor.ram, flavor.vcpus, flavor.disk)
834 if flavor_data == flavor_target:
835 return flavor.id
836 elif not exact_match and flavor_target < flavor_data < flavor_candidate_data:
837 flavor_candidate_id = flavor.id
838 flavor_candidate_data = flavor_data
839 if not exact_match and flavor_candidate_id:
840 return flavor_candidate_id
tiernocf157a82017-01-30 14:07:06 +0100841 raise vimconn.vimconnNotFoundException("Cannot find any flavor matching '{}'".format(str(flavor_dict)))
842 except (nvExceptions.NotFound, nvExceptions.ClientException, ksExceptions.ClientException, ConnectionError) as e:
843 self._format_exception(e)
844
anwarsae5f52c2019-04-22 10:35:27 +0530845 def process_resource_quota(self, quota, prefix, extra_specs):
846 """
847 :param prefix:
borsatti8a2dda32019-12-18 15:08:57 +0000848 :param extra_specs:
anwarsae5f52c2019-04-22 10:35:27 +0530849 :return:
850 """
851 if 'limit' in quota:
852 extra_specs["quota:" + prefix + "_limit"] = quota['limit']
853 if 'reserve' in quota:
854 extra_specs["quota:" + prefix + "_reservation"] = quota['reserve']
855 if 'shares' in quota:
856 extra_specs["quota:" + prefix + "_shares_level"] = "custom"
857 extra_specs["quota:" + prefix + "_shares_share"] = quota['shares']
858
tiernoae4a8d12016-07-08 12:30:39 +0200859 def new_flavor(self, flavor_data, change_name_if_used=True):
tierno7edb6752016-03-21 17:37:52 +0100860 '''Adds a tenant flavor to openstack VIM
tiernoae4a8d12016-07-08 12:30:39 +0200861 if change_name_if_used is True, it will change name in case of conflict, because it is not supported name repetition
tierno7edb6752016-03-21 17:37:52 +0100862 Returns the flavor identifier
863 '''
tiernoae4a8d12016-07-08 12:30:39 +0200864 self.logger.debug("Adding flavor '%s'", str(flavor_data))
tierno7edb6752016-03-21 17:37:52 +0100865 retry=0
tiernoae4a8d12016-07-08 12:30:39 +0200866 max_retries=3
tierno7edb6752016-03-21 17:37:52 +0100867 name_suffix = 0
anwarsc76a3ee2018-10-04 14:05:32 +0530868 try:
869 name=flavor_data['name']
870 while retry<max_retries:
871 retry+=1
872 try:
873 self._reload_connection()
874 if change_name_if_used:
875 #get used names
876 fl_names=[]
877 fl=self.nova.flavors.list()
878 for f in fl:
879 fl_names.append(f.name)
880 while name in fl_names:
881 name_suffix += 1
882 name = flavor_data['name']+"-" + str(name_suffix)
kate721d79b2017-06-24 04:21:38 -0700883
anwarsc76a3ee2018-10-04 14:05:32 +0530884 ram = flavor_data.get('ram',64)
885 vcpus = flavor_data.get('vcpus',1)
anwarsae5f52c2019-04-22 10:35:27 +0530886 extra_specs={}
tierno7edb6752016-03-21 17:37:52 +0100887
anwarsc76a3ee2018-10-04 14:05:32 +0530888 extended = flavor_data.get("extended")
889 if extended:
890 numas=extended.get("numas")
891 if numas:
892 numa_nodes = len(numas)
893 if numa_nodes > 1:
894 return -1, "Can not add flavor with more than one numa"
anwarsae5f52c2019-04-22 10:35:27 +0530895 extra_specs["hw:numa_nodes"] = str(numa_nodes)
896 extra_specs["hw:mem_page_size"] = "large"
897 extra_specs["hw:cpu_policy"] = "dedicated"
898 extra_specs["hw:numa_mempolicy"] = "strict"
anwarsc76a3ee2018-10-04 14:05:32 +0530899 if self.vim_type == "VIO":
anwarsae5f52c2019-04-22 10:35:27 +0530900 extra_specs["vmware:extra_config"] = '{"numa.nodeAffinity":"0"}'
901 extra_specs["vmware:latency_sensitivity_level"] = "high"
anwarsc76a3ee2018-10-04 14:05:32 +0530902 for numa in numas:
903 #overwrite ram and vcpus
904 #check if key 'memory' is present in numa else use ram value at flavor
905 if 'memory' in numa:
906 ram = numa['memory']*1024
907 #See for reference: https://specs.openstack.org/openstack/nova-specs/specs/mitaka/implemented/virt-driver-cpu-thread-pinning.html
garciadeblasfa35a722019-04-11 19:15:49 +0200908 extra_specs["hw:cpu_sockets"] = 1
anwarsc76a3ee2018-10-04 14:05:32 +0530909 if 'paired-threads' in numa:
910 vcpus = numa['paired-threads']*2
911 #cpu_thread_policy "require" implies that the compute node must have an STM architecture
anwarsae5f52c2019-04-22 10:35:27 +0530912 extra_specs["hw:cpu_thread_policy"] = "require"
913 extra_specs["hw:cpu_policy"] = "dedicated"
anwarsc76a3ee2018-10-04 14:05:32 +0530914 elif 'cores' in numa:
915 vcpus = numa['cores']
916 # cpu_thread_policy "prefer" implies that the host must not have an SMT architecture, or a non-SMT architecture will be emulated
anwarsae5f52c2019-04-22 10:35:27 +0530917 extra_specs["hw:cpu_thread_policy"] = "isolate"
918 extra_specs["hw:cpu_policy"] = "dedicated"
anwarsc76a3ee2018-10-04 14:05:32 +0530919 elif 'threads' in numa:
920 vcpus = numa['threads']
921 # cpu_thread_policy "prefer" implies that the host may or may not have an SMT architecture
anwarsae5f52c2019-04-22 10:35:27 +0530922 extra_specs["hw:cpu_thread_policy"] = "prefer"
923 extra_specs["hw:cpu_policy"] = "dedicated"
anwarsc76a3ee2018-10-04 14:05:32 +0530924 # for interface in numa.get("interfaces",() ):
925 # if interface["dedicated"]=="yes":
926 # raise vimconn.vimconnException("Passthrough interfaces are not supported for the openstack connector", http_code=vimconn.HTTP_Service_Unavailable)
927 # #TODO, add the key 'pci_passthrough:alias"="<label at config>:<number ifaces>"' when a way to connect it is available
anwarsae5f52c2019-04-22 10:35:27 +0530928 elif extended.get("cpu-quota"):
929 self.process_resource_quota(extended.get("cpu-quota"), "cpu", extra_specs)
930 if extended.get("mem-quota"):
931 self.process_resource_quota(extended.get("mem-quota"), "memory", extra_specs)
932 if extended.get("vif-quota"):
933 self.process_resource_quota(extended.get("vif-quota"), "vif", extra_specs)
934 if extended.get("disk-io-quota"):
935 self.process_resource_quota(extended.get("disk-io-quota"), "disk_io", extra_specs)
anwarsc76a3ee2018-10-04 14:05:32 +0530936 #create flavor
937 new_flavor=self.nova.flavors.create(name,
938 ram,
939 vcpus,
940 flavor_data.get('disk',0),
941 is_public=flavor_data.get('is_public', True)
942 )
943 #add metadata
anwarsae5f52c2019-04-22 10:35:27 +0530944 if extra_specs:
945 new_flavor.set_keys(extra_specs)
anwarsc76a3ee2018-10-04 14:05:32 +0530946 return new_flavor.id
947 except nvExceptions.Conflict as e:
948 if change_name_if_used and retry < max_retries:
949 continue
950 self._format_exception(e)
951 #except nvExceptions.BadRequest as e:
952 except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError, KeyError) as e:
953 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100954
tiernoae4a8d12016-07-08 12:30:39 +0200955 def delete_flavor(self,flavor_id):
956 '''Deletes a tenant flavor from openstack VIM. Returns the old flavor_id
tierno7edb6752016-03-21 17:37:52 +0100957 '''
tiernoae4a8d12016-07-08 12:30:39 +0200958 try:
959 self._reload_connection()
960 self.nova.flavors.delete(flavor_id)
961 return flavor_id
962 #except nvExceptions.BadRequest as e:
tierno8e995ce2016-09-22 08:13:00 +0000963 except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200964 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100965
tiernoae4a8d12016-07-08 12:30:39 +0200966 def new_image(self,image_dict):
tierno7edb6752016-03-21 17:37:52 +0100967 '''
tiernoae4a8d12016-07-08 12:30:39 +0200968 Adds a tenant image to VIM. imge_dict is a dictionary with:
969 name: name
970 disk_format: qcow2, vhd, vmdk, raw (by default), ...
971 location: path or URI
972 public: "yes" or "no"
973 metadata: metadata of the image
974 Returns the image_id
tierno7edb6752016-03-21 17:37:52 +0100975 '''
tiernoae4a8d12016-07-08 12:30:39 +0200976 retry=0
977 max_retries=3
978 while retry<max_retries:
tierno7edb6752016-03-21 17:37:52 +0100979 retry+=1
980 try:
981 self._reload_connection()
982 #determine format http://docs.openstack.org/developer/glance/formats.html
983 if "disk_format" in image_dict:
984 disk_format=image_dict["disk_format"]
garciadeblas14480452017-01-10 13:08:07 +0100985 else: #autodiscover based on extension
tierno1beea862018-07-11 15:47:37 +0200986 if image_dict['location'].endswith(".qcow2"):
tierno7edb6752016-03-21 17:37:52 +0100987 disk_format="qcow2"
tierno1beea862018-07-11 15:47:37 +0200988 elif image_dict['location'].endswith(".vhd"):
tierno7edb6752016-03-21 17:37:52 +0100989 disk_format="vhd"
tierno1beea862018-07-11 15:47:37 +0200990 elif image_dict['location'].endswith(".vmdk"):
tierno7edb6752016-03-21 17:37:52 +0100991 disk_format="vmdk"
tierno1beea862018-07-11 15:47:37 +0200992 elif image_dict['location'].endswith(".vdi"):
tierno7edb6752016-03-21 17:37:52 +0100993 disk_format="vdi"
tierno1beea862018-07-11 15:47:37 +0200994 elif image_dict['location'].endswith(".iso"):
tierno7edb6752016-03-21 17:37:52 +0100995 disk_format="iso"
tierno1beea862018-07-11 15:47:37 +0200996 elif image_dict['location'].endswith(".aki"):
tierno7edb6752016-03-21 17:37:52 +0100997 disk_format="aki"
tierno1beea862018-07-11 15:47:37 +0200998 elif image_dict['location'].endswith(".ari"):
tierno7edb6752016-03-21 17:37:52 +0100999 disk_format="ari"
tierno1beea862018-07-11 15:47:37 +02001000 elif image_dict['location'].endswith(".ami"):
tierno7edb6752016-03-21 17:37:52 +01001001 disk_format="ami"
1002 else:
1003 disk_format="raw"
tiernoae4a8d12016-07-08 12:30:39 +02001004 self.logger.debug("new_image: '%s' loading from '%s'", image_dict['name'], image_dict['location'])
shashankjain3c83a212018-10-04 13:05:46 +05301005 if self.vim_type == "VIO":
1006 container_format = "bare"
1007 if 'container_format' in image_dict:
1008 container_format = image_dict['container_format']
1009 new_image = self.glance.images.create(name=image_dict['name'], container_format=container_format,
1010 disk_format=disk_format)
1011 else:
1012 new_image = self.glance.images.create(name=image_dict['name'])
tierno1beea862018-07-11 15:47:37 +02001013 if image_dict['location'].startswith("http"):
1014 # TODO there is not a method to direct download. It must be downloaded locally with requests
1015 raise vimconn.vimconnNotImplemented("Cannot create image from URL")
tierno7edb6752016-03-21 17:37:52 +01001016 else: #local path
1017 with open(image_dict['location']) as fimage:
tierno1beea862018-07-11 15:47:37 +02001018 self.glance.images.upload(new_image.id, fimage)
1019 #new_image = self.glancev1.images.create(name=image_dict['name'], is_public=image_dict.get('public',"yes")=="yes",
1020 # container_format="bare", data=fimage, disk_format=disk_format)
tierno7edb6752016-03-21 17:37:52 +01001021 metadata_to_load = image_dict.get('metadata')
shashankjain3c83a212018-10-04 13:05:46 +05301022 # TODO location is a reserved word for current openstack versions. fixed for VIO please check for openstack
1023 if self.vim_type == "VIO":
1024 metadata_to_load['upload_location'] = image_dict['location']
1025 else:
1026 metadata_to_load['location'] = image_dict['location']
tierno1beea862018-07-11 15:47:37 +02001027 self.glance.images.update(new_image.id, **metadata_to_load)
tiernoae4a8d12016-07-08 12:30:39 +02001028 return new_image.id
1029 except (nvExceptions.Conflict, ksExceptions.ClientException, nvExceptions.ClientException) as e:
1030 self._format_exception(e)
tierno8e995ce2016-09-22 08:13:00 +00001031 except (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001032 if retry==max_retries:
1033 continue
1034 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001035 except IOError as e: #can not open the file
tiernoae4a8d12016-07-08 12:30:39 +02001036 raise vimconn.vimconnConnectionException(type(e).__name__ + ": " + str(e)+ " for " + image_dict['location'],
1037 http_code=vimconn.HTTP_Bad_Request)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001038
tiernoae4a8d12016-07-08 12:30:39 +02001039 def delete_image(self, image_id):
1040 '''Deletes a tenant image from openstack VIM. Returns the old id
tierno7edb6752016-03-21 17:37:52 +01001041 '''
tiernoae4a8d12016-07-08 12:30:39 +02001042 try:
1043 self._reload_connection()
tierno1beea862018-07-11 15:47:37 +02001044 self.glance.images.delete(image_id)
tiernoae4a8d12016-07-08 12:30:39 +02001045 return image_id
shashankjain3c83a212018-10-04 13:05:46 +05301046 except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, gl1Exceptions.HTTPNotFound, ConnectionError) as e: #TODO remove
tiernoae4a8d12016-07-08 12:30:39 +02001047 self._format_exception(e)
1048
1049 def get_image_id_from_path(self, path):
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001050 '''Get the image id from image path in the VIM database. Returns the image_id'''
tiernoae4a8d12016-07-08 12:30:39 +02001051 try:
1052 self._reload_connection()
tierno1beea862018-07-11 15:47:37 +02001053 images = self.glance.images.list()
tiernoae4a8d12016-07-08 12:30:39 +02001054 for image in images:
1055 if image.metadata.get("location")==path:
1056 return image.id
1057 raise vimconn.vimconnNotFoundException("image with location '{}' not found".format( path))
tierno8e995ce2016-09-22 08:13:00 +00001058 except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001059 self._format_exception(e)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001060
garciadeblasb69fa9f2016-09-28 12:04:10 +02001061 def get_image_list(self, filter_dict={}):
1062 '''Obtain tenant images from VIM
1063 Filter_dict can be:
1064 id: image id
1065 name: image name
1066 checksum: image checksum
1067 Returns the image list of dictionaries:
1068 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1069 List can be empty
1070 '''
1071 self.logger.debug("Getting image list from VIM filter: '%s'", str(filter_dict))
1072 try:
1073 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +01001074 filter_dict_os = filter_dict.copy()
garciadeblasb69fa9f2016-09-28 12:04:10 +02001075 #First we filter by the available filter fields: name, id. The others are removed.
tierno1beea862018-07-11 15:47:37 +02001076 image_list = self.glance.images.list()
garciadeblasb69fa9f2016-09-28 12:04:10 +02001077 filtered_list = []
1078 for image in image_list:
tierno3cb8dc32017-10-24 18:13:19 +02001079 try:
tierno1beea862018-07-11 15:47:37 +02001080 if filter_dict.get("name") and image["name"] != filter_dict["name"]:
1081 continue
1082 if filter_dict.get("id") and image["id"] != filter_dict["id"]:
1083 continue
1084 if filter_dict.get("checksum") and image["checksum"] != filter_dict["checksum"]:
1085 continue
1086
1087 filtered_list.append(image.copy())
tierno3cb8dc32017-10-24 18:13:19 +02001088 except gl1Exceptions.HTTPNotFound:
1089 pass
garciadeblasb69fa9f2016-09-28 12:04:10 +02001090 return filtered_list
1091 except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e:
1092 self._format_exception(e)
1093
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001094 def __wait_for_vm(self, vm_id, status):
1095 """wait until vm is in the desired status and return True.
1096 If the VM gets in ERROR status, return false.
1097 If the timeout is reached generate an exception"""
1098 elapsed_time = 0
1099 while elapsed_time < server_timeout:
1100 vm_status = self.nova.servers.get(vm_id).status
1101 if vm_status == status:
1102 return True
1103 if vm_status == 'ERROR':
1104 return False
tierno1df468d2018-07-06 14:25:16 +02001105 time.sleep(5)
1106 elapsed_time += 5
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001107
1108 # if we exceeded the timeout rollback
1109 if elapsed_time >= server_timeout:
1110 raise vimconn.vimconnException('Timeout waiting for instance ' + vm_id + ' to get ' + status,
1111 http_code=vimconn.HTTP_Request_Timeout)
1112
mirabal29356312017-07-27 12:21:22 +02001113 def _get_openstack_availablity_zones(self):
1114 """
1115 Get from openstack availability zones available
1116 :return:
1117 """
1118 try:
1119 openstack_availability_zone = self.nova.availability_zones.list()
1120 openstack_availability_zone = [str(zone.zoneName) for zone in openstack_availability_zone
1121 if zone.zoneName != 'internal']
1122 return openstack_availability_zone
1123 except Exception as e:
1124 return None
1125
1126 def _set_availablity_zones(self):
1127 """
1128 Set vim availablity zone
1129 :return:
1130 """
1131
1132 if 'availability_zone' in self.config:
1133 vim_availability_zones = self.config.get('availability_zone')
1134 if isinstance(vim_availability_zones, str):
1135 self.availability_zone = [vim_availability_zones]
1136 elif isinstance(vim_availability_zones, list):
1137 self.availability_zone = vim_availability_zones
1138 else:
1139 self.availability_zone = self._get_openstack_availablity_zones()
1140
tierno5a3273c2017-08-29 11:43:46 +02001141 def _get_vm_availability_zone(self, availability_zone_index, availability_zone_list):
mirabal29356312017-07-27 12:21:22 +02001142 """
tierno5a3273c2017-08-29 11:43:46 +02001143 Return thge availability zone to be used by the created VM.
1144 :return: The VIM availability zone to be used or None
mirabal29356312017-07-27 12:21:22 +02001145 """
tierno5a3273c2017-08-29 11:43:46 +02001146 if availability_zone_index is None:
1147 if not self.config.get('availability_zone'):
1148 return None
1149 elif isinstance(self.config.get('availability_zone'), str):
1150 return self.config['availability_zone']
1151 else:
1152 # TODO consider using a different parameter at config for default AV and AV list match
1153 return self.config['availability_zone'][0]
mirabal29356312017-07-27 12:21:22 +02001154
tierno5a3273c2017-08-29 11:43:46 +02001155 vim_availability_zones = self.availability_zone
1156 # check if VIM offer enough availability zones describe in the VNFD
1157 if vim_availability_zones and len(availability_zone_list) <= len(vim_availability_zones):
1158 # check if all the names of NFV AV match VIM AV names
1159 match_by_index = False
1160 for av in availability_zone_list:
1161 if av not in vim_availability_zones:
1162 match_by_index = True
1163 break
1164 if match_by_index:
1165 return vim_availability_zones[availability_zone_index]
1166 else:
1167 return availability_zone_list[availability_zone_index]
mirabal29356312017-07-27 12:21:22 +02001168 else:
tierno5a3273c2017-08-29 11:43:46 +02001169 raise vimconn.vimconnConflictException("No enough availability zones at VIM for this deployment")
mirabal29356312017-07-27 12:21:22 +02001170
tierno5a3273c2017-08-29 11:43:46 +02001171 def new_vminstance(self, name, description, start, image_id, flavor_id, net_list, cloud_config=None, disk_list=None,
1172 availability_zone_index=None, availability_zone_list=None):
tierno98e909c2017-10-14 13:27:03 +02001173 """Adds a VM instance to VIM
tierno7edb6752016-03-21 17:37:52 +01001174 Params:
1175 start: indicates if VM must start or boot in pause mode. Ignored
1176 image_id,flavor_id: iamge and flavor uuid
1177 net_list: list of interfaces, each one is a dictionary with:
1178 name:
1179 net_id: network uuid to connect
1180 vpci: virtual vcpi to assign, ignored because openstack lack #TODO
1181 model: interface model, ignored #TODO
1182 mac_address: used for SR-IOV ifaces #TODO for other types
1183 use: 'data', 'bridge', 'mgmt'
tierno66eba6e2017-11-10 17:09:18 +01001184 type: 'virtual', 'PCI-PASSTHROUGH'('PF'), 'SR-IOV'('VF'), 'VFnotShared'
tierno7edb6752016-03-21 17:37:52 +01001185 vim_id: filled/added by this function
ahmadsaf853d452016-12-22 11:33:47 +05001186 floating_ip: True/False (or it can be None)
tierno41a69812018-02-16 14:34:33 +01001187 'cloud_config': (optional) dictionary with:
1188 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
1189 'users': (optional) list of users to be inserted, each item is a dict with:
1190 'name': (mandatory) user name,
1191 'key-pairs': (optional) list of strings with the public key to be inserted to the user
1192 'user-data': (optional) string is a text script to be passed directly to cloud-init
1193 'config-files': (optional). List of files to be transferred. Each item is a dict with:
1194 'dest': (mandatory) string with the destination absolute path
1195 'encoding': (optional, by default text). Can be one of:
1196 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
1197 'content' (mandatory): string with the content of the file
1198 'permissions': (optional) string with file permissions, typically octal notation '0644'
1199 'owner': (optional) file owner, string with the format 'owner:group'
1200 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk)
mirabal29356312017-07-27 12:21:22 +02001201 'disk_list': (optional) list with additional disks to the VM. Each item is a dict with:
1202 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted
1203 'size': (mandatory) string with the size of the disk in GB
tierno1df468d2018-07-06 14:25:16 +02001204 'vim_id' (optional) should use this existing volume id
tierno5a3273c2017-08-29 11:43:46 +02001205 availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV required
1206 availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if
1207 availability_zone_index is None
tierno7edb6752016-03-21 17:37:52 +01001208 #TODO ip, security groups
tierno98e909c2017-10-14 13:27:03 +02001209 Returns a tuple with the instance identifier and created_items or raises an exception on error
1210 created_items can be None or a dictionary where this method can include key-values that will be passed to
1211 the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc.
1212 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
1213 as not present.
1214 """
tiernofa51c202017-01-27 14:58:17 +01001215 self.logger.debug("new_vminstance input: image='%s' flavor='%s' nics='%s'",image_id, flavor_id,str(net_list))
tierno7edb6752016-03-21 17:37:52 +01001216 try:
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001217 server = None
tierno98e909c2017-10-14 13:27:03 +02001218 created_items = {}
tiernob0b9dab2017-10-14 14:25:20 +02001219 # metadata = {}
tierno98e909c2017-10-14 13:27:03 +02001220 net_list_vim = []
1221 external_network = [] # list of external networks to be connected to instance, later on used to create floating_ip
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001222 no_secured_ports = [] # List of port-is with port-security disabled
tierno7edb6752016-03-21 17:37:52 +01001223 self._reload_connection()
tiernob0b9dab2017-10-14 14:25:20 +02001224 # metadata_vpci = {} # For a specific neutron plugin
tiernob84cbdc2017-07-07 14:30:30 +02001225 block_device_mapping = None
tiernoa05b65a2019-02-01 12:30:27 +00001226
tierno7edb6752016-03-21 17:37:52 +01001227 for net in net_list:
tierno98e909c2017-10-14 13:27:03 +02001228 if not net.get("net_id"): # skip non connected iface
tierno7edb6752016-03-21 17:37:52 +01001229 continue
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001230
tiernoa05b65a2019-02-01 12:30:27 +00001231 port_dict = {
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001232 "network_id": net["net_id"],
1233 "name": net.get("name"),
1234 "admin_state_up": True
1235 }
tiernoa05b65a2019-02-01 12:30:27 +00001236 if self.config.get("security_groups") and net.get("port_security") is not False and \
1237 not self.config.get("no_port_security_extension"):
1238 if not self.security_groups_id:
1239 self._get_ids_from_name()
1240 port_dict["security_groups"] = self.security_groups_id
1241
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001242 if net["type"]=="virtual":
tiernob0b9dab2017-10-14 14:25:20 +02001243 pass
1244 # if "vpci" in net:
1245 # metadata_vpci[ net["net_id"] ] = [[ net["vpci"], "" ]]
tierno66eba6e2017-11-10 17:09:18 +01001246 elif net["type"] == "VF" or net["type"] == "SR-IOV": # for VF
tiernob0b9dab2017-10-14 14:25:20 +02001247 # if "vpci" in net:
1248 # if "VF" not in metadata_vpci:
1249 # metadata_vpci["VF"]=[]
1250 # metadata_vpci["VF"].append([ net["vpci"], "" ])
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001251 port_dict["binding:vnic_type"]="direct"
tiernob0b9dab2017-10-14 14:25:20 +02001252 # VIO specific Changes
kate721d79b2017-06-24 04:21:38 -07001253 if self.vim_type == "VIO":
tiernob0b9dab2017-10-14 14:25:20 +02001254 # Need to create port with port_security_enabled = False and no-security-groups
kate721d79b2017-06-24 04:21:38 -07001255 port_dict["port_security_enabled"]=False
1256 port_dict["provider_security_groups"]=[]
1257 port_dict["security_groups"]=[]
tierno66eba6e2017-11-10 17:09:18 +01001258 else: # For PT PCI-PASSTHROUGH
tiernob0b9dab2017-10-14 14:25:20 +02001259 # VIO specific Changes
1260 # Current VIO release does not support port with type 'direct-physical'
1261 # So no need to create virtual port in case of PCI-device.
1262 # Will update port_dict code when support gets added in next VIO release
kate721d79b2017-06-24 04:21:38 -07001263 if self.vim_type == "VIO":
tiernob0b9dab2017-10-14 14:25:20 +02001264 raise vimconn.vimconnNotSupportedException(
1265 "Current VIO release does not support full passthrough (PT)")
1266 # if "vpci" in net:
1267 # if "PF" not in metadata_vpci:
1268 # metadata_vpci["PF"]=[]
1269 # metadata_vpci["PF"].append([ net["vpci"], "" ])
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001270 port_dict["binding:vnic_type"]="direct-physical"
1271 if not port_dict["name"]:
1272 port_dict["name"]=name
1273 if net.get("mac_address"):
1274 port_dict["mac_address"]=net["mac_address"]
tierno41a69812018-02-16 14:34:33 +01001275 if net.get("ip_address"):
1276 port_dict["fixed_ips"] = [{'ip_address': net["ip_address"]}]
1277 # TODO add 'subnet_id': <subnet_id>
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001278 new_port = self.neutron.create_port({"port": port_dict })
tierno00e3df72017-11-29 17:20:13 +01001279 created_items["port:" + str(new_port["port"]["id"])] = True
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001280 net["mac_adress"] = new_port["port"]["mac_address"]
1281 net["vim_id"] = new_port["port"]["id"]
tiernob84cbdc2017-07-07 14:30:30 +02001282 # if try to use a network without subnetwork, it will return a emtpy list
1283 fixed_ips = new_port["port"].get("fixed_ips")
1284 if fixed_ips:
1285 net["ip"] = fixed_ips[0].get("ip_address")
1286 else:
1287 net["ip"] = None
montesmoreno994a29d2017-08-22 11:23:06 +02001288
1289 port = {"port-id": new_port["port"]["id"]}
1290 if float(self.nova.api_version.get_string()) >= 2.32:
1291 port["tag"] = new_port["port"]["name"]
1292 net_list_vim.append(port)
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001293
ahmadsaf853d452016-12-22 11:33:47 +05001294 if net.get('floating_ip', False):
tiernof8383b82017-01-18 15:49:48 +01001295 net['exit_on_floating_ip_error'] = True
ahmadsaf853d452016-12-22 11:33:47 +05001296 external_network.append(net)
tiernof8383b82017-01-18 15:49:48 +01001297 elif net['use'] == 'mgmt' and self.config.get('use_floating_ip'):
1298 net['exit_on_floating_ip_error'] = False
1299 external_network.append(net)
tierno326fd5e2018-02-22 11:58:59 +01001300 net['floating_ip'] = self.config.get('use_floating_ip')
tiernof8383b82017-01-18 15:49:48 +01001301
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001302 # If port security is disabled when the port has not yet been attached to the VM, then all vm traffic is dropped.
1303 # As a workaround we wait until the VM is active and then disable the port-security
tierno4d1ce222018-04-06 10:41:06 +02001304 if net.get("port_security") == False and not self.config.get("no_port_security_extension"):
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001305 no_secured_ports.append(new_port["port"]["id"])
1306
tiernob0b9dab2017-10-14 14:25:20 +02001307 # if metadata_vpci:
1308 # metadata = {"pci_assignement": json.dumps(metadata_vpci)}
1309 # if len(metadata["pci_assignement"]) >255:
1310 # #limit the metadata size
1311 # #metadata["pci_assignement"] = metadata["pci_assignement"][0:255]
1312 # self.logger.warn("Metadata deleted since it exceeds the expected length (255) ")
1313 # metadata = {}
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001314
tiernob0b9dab2017-10-14 14:25:20 +02001315 self.logger.debug("name '%s' image_id '%s'flavor_id '%s' net_list_vim '%s' description '%s'",
1316 name, image_id, flavor_id, str(net_list_vim), description)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001317
tierno98e909c2017-10-14 13:27:03 +02001318 # cloud config
tierno0a1437e2017-10-02 00:17:43 +02001319 config_drive, userdata = self._create_user_data(cloud_config)
montesmoreno0c8def02016-12-22 12:16:23 +00001320
tierno98e909c2017-10-14 13:27:03 +02001321 # Create additional volumes in case these are present in disk_list
montesmoreno0c8def02016-12-22 12:16:23 +00001322 base_disk_index = ord('b')
tierno1df468d2018-07-06 14:25:16 +02001323 if disk_list:
tiernob84cbdc2017-07-07 14:30:30 +02001324 block_device_mapping = {}
montesmoreno0c8def02016-12-22 12:16:23 +00001325 for disk in disk_list:
tierno1df468d2018-07-06 14:25:16 +02001326 if disk.get('vim_id'):
1327 block_device_mapping['_vd' + chr(base_disk_index)] = disk['vim_id']
montesmoreno0c8def02016-12-22 12:16:23 +00001328 else:
tierno1df468d2018-07-06 14:25:16 +02001329 if 'image_id' in disk:
1330 volume = self.cinder.volumes.create(size=disk['size'], name=name + '_vd' +
1331 chr(base_disk_index), imageRef=disk['image_id'])
1332 else:
1333 volume = self.cinder.volumes.create(size=disk['size'], name=name + '_vd' +
1334 chr(base_disk_index))
1335 created_items["volume:" + str(volume.id)] = True
1336 block_device_mapping['_vd' + chr(base_disk_index)] = volume.id
montesmoreno0c8def02016-12-22 12:16:23 +00001337 base_disk_index += 1
1338
tierno1df468d2018-07-06 14:25:16 +02001339 # Wait until created volumes are with status available
montesmoreno0c8def02016-12-22 12:16:23 +00001340 elapsed_time = 0
tierno1df468d2018-07-06 14:25:16 +02001341 while elapsed_time < volume_timeout:
1342 for created_item in created_items:
1343 v, _, volume_id = created_item.partition(":")
1344 if v == 'volume':
1345 if self.cinder.volumes.get(volume_id).status != 'available':
1346 break
1347 else: # all ready: break from while
1348 break
1349 time.sleep(5)
1350 elapsed_time += 5
tiernob0b9dab2017-10-14 14:25:20 +02001351 # If we exceeded the timeout rollback
montesmoreno0c8def02016-12-22 12:16:23 +00001352 if elapsed_time >= volume_timeout:
montesmoreno0c8def02016-12-22 12:16:23 +00001353 raise vimconn.vimconnException('Timeout creating volumes for instance ' + name,
1354 http_code=vimconn.HTTP_Request_Timeout)
mirabal29356312017-07-27 12:21:22 +02001355 # get availability Zone
tierno5a3273c2017-08-29 11:43:46 +02001356 vm_av_zone = self._get_vm_availability_zone(availability_zone_index, availability_zone_list)
montesmoreno0c8def02016-12-22 12:16:23 +00001357
tiernob0b9dab2017-10-14 14:25:20 +02001358 self.logger.debug("nova.servers.create({}, {}, {}, nics={}, security_groups={}, "
mirabal29356312017-07-27 12:21:22 +02001359 "availability_zone={}, key_name={}, userdata={}, config_drive={}, "
tiernob0b9dab2017-10-14 14:25:20 +02001360 "block_device_mapping={})".format(name, image_id, flavor_id, net_list_vim,
tiernoa05b65a2019-02-01 12:30:27 +00001361 self.config.get("security_groups"), vm_av_zone,
1362 self.config.get('keypair'), userdata, config_drive,
1363 block_device_mapping))
tiernob0b9dab2017-10-14 14:25:20 +02001364 server = self.nova.servers.create(name, image_id, flavor_id, nics=net_list_vim,
tiernoa05b65a2019-02-01 12:30:27 +00001365 security_groups=self.config.get("security_groups"),
1366 # TODO remove security_groups in future versions. Already at neutron port
mirabal29356312017-07-27 12:21:22 +02001367 availability_zone=vm_av_zone,
montesmoreno0c8def02016-12-22 12:16:23 +00001368 key_name=self.config.get('keypair'),
1369 userdata=userdata,
tiernob84cbdc2017-07-07 14:30:30 +02001370 config_drive=config_drive,
1371 block_device_mapping=block_device_mapping
montesmoreno0c8def02016-12-22 12:16:23 +00001372 ) # , description=description)
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001373
tierno326fd5e2018-02-22 11:58:59 +01001374 vm_start_time = time.time()
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001375 # Previously mentioned workaround to wait until the VM is active and then disable the port-security
1376 if no_secured_ports:
1377 self.__wait_for_vm(server.id, 'ACTIVE')
1378
1379 for port_id in no_secured_ports:
1380 try:
tierno4d1ce222018-04-06 10:41:06 +02001381 self.neutron.update_port(port_id,
1382 {"port": {"port_security_enabled": False, "security_groups": None}})
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001383 except Exception as e:
tierno4d1ce222018-04-06 10:41:06 +02001384 raise vimconn.vimconnException("It was not possible to disable port security for port {}".format(
1385 port_id))
tierno98e909c2017-10-14 13:27:03 +02001386 # print "DONE :-)", server
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001387
tierno4d1ce222018-04-06 10:41:06 +02001388 # pool_id = None
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001389 if external_network:
tierno98e909c2017-10-14 13:27:03 +02001390 floating_ips = self.neutron.list_floatingips().get("floatingips", ())
ahmadsaf853d452016-12-22 11:33:47 +05001391 for floating_network in external_network:
tiernof8383b82017-01-18 15:49:48 +01001392 try:
tiernof8383b82017-01-18 15:49:48 +01001393 assigned = False
tierno98e909c2017-10-14 13:27:03 +02001394 while not assigned:
tiernof8383b82017-01-18 15:49:48 +01001395 if floating_ips:
1396 ip = floating_ips.pop(0)
tierno326fd5e2018-02-22 11:58:59 +01001397 if ip.get("port_id", False) or ip.get('tenant_id') != server.tenant_id:
1398 continue
1399 if isinstance(floating_network['floating_ip'], str):
1400 if ip.get("floating_network_id") != floating_network['floating_ip']:
1401 continue
tierno7d782ef2019-10-04 12:56:31 +00001402 free_floating_ip = ip["id"]
tiernof8383b82017-01-18 15:49:48 +01001403 else:
tiernocb3cca22018-05-31 15:08:52 +02001404 if isinstance(floating_network['floating_ip'], str) and \
1405 floating_network['floating_ip'].lower() != "true":
tierno326fd5e2018-02-22 11:58:59 +01001406 pool_id = floating_network['floating_ip']
1407 else:
tierno4d1ce222018-04-06 10:41:06 +02001408 # Find the external network
tierno326fd5e2018-02-22 11:58:59 +01001409 external_nets = list()
1410 for net in self.neutron.list_networks()['networks']:
1411 if net['router:external']:
1412 external_nets.append(net)
tiernof8383b82017-01-18 15:49:48 +01001413
tierno326fd5e2018-02-22 11:58:59 +01001414 if len(external_nets) == 0:
1415 raise vimconn.vimconnException("Cannot create floating_ip automatically since no external "
1416 "network is present",
1417 http_code=vimconn.HTTP_Conflict)
1418 if len(external_nets) > 1:
1419 raise vimconn.vimconnException("Cannot create floating_ip automatically since multiple "
1420 "external networks are present",
1421 http_code=vimconn.HTTP_Conflict)
tiernof8383b82017-01-18 15:49:48 +01001422
tierno326fd5e2018-02-22 11:58:59 +01001423 pool_id = external_nets[0].get('id')
tiernof8383b82017-01-18 15:49:48 +01001424 param = {'floatingip': {'floating_network_id': pool_id, 'tenant_id': server.tenant_id}}
ahmadsaf853d452016-12-22 11:33:47 +05001425 try:
tierno4d1ce222018-04-06 10:41:06 +02001426 # self.logger.debug("Creating floating IP")
tiernof8383b82017-01-18 15:49:48 +01001427 new_floating_ip = self.neutron.create_floatingip(param)
tierno7d782ef2019-10-04 12:56:31 +00001428 free_floating_ip = new_floating_ip['floatingip']['id']
ahmadsaf853d452016-12-22 11:33:47 +05001429 except Exception as e:
tierno326fd5e2018-02-22 11:58:59 +01001430 raise vimconn.vimconnException(type(e).__name__ + ": Cannot create new floating_ip " +
1431 str(e), http_code=vimconn.HTTP_Conflict)
1432
tierno326fd5e2018-02-22 11:58:59 +01001433 while not assigned:
1434 try:
tierno7d782ef2019-10-04 12:56:31 +00001435 # the vim_id key contains the neutron.port_id
1436 self.neutron.update_floatingip(free_floating_ip,
1437 {"floatingip": {"port_id": floating_network["vim_id"]}})
1438 # Using nove is deprecated on nova client 10.0
tierno326fd5e2018-02-22 11:58:59 +01001439 assigned = True
1440 except Exception as e:
tierno4d1ce222018-04-06 10:41:06 +02001441 # openstack need some time after VM creation to asign an IP. So retry if fails
tierno326fd5e2018-02-22 11:58:59 +01001442 vm_status = self.nova.servers.get(server.id).status
1443 if vm_status != 'ACTIVE' and vm_status != 'ERROR':
1444 if time.time() - vm_start_time < server_timeout:
1445 time.sleep(5)
1446 continue
tierno4d1ce222018-04-06 10:41:06 +02001447 raise vimconn.vimconnException(
1448 "Cannot create floating_ip: {} {}".format(type(e).__name__, e),
1449 http_code=vimconn.HTTP_Conflict)
tierno326fd5e2018-02-22 11:58:59 +01001450
tiernof8383b82017-01-18 15:49:48 +01001451 except Exception as e:
1452 if not floating_network['exit_on_floating_ip_error']:
tierno7d782ef2019-10-04 12:56:31 +00001453 self.logger.warning("Cannot create floating_ip. %s", str(e))
tiernof8383b82017-01-18 15:49:48 +01001454 continue
tiernof8383b82017-01-18 15:49:48 +01001455 raise
montesmoreno2a1fc4e2017-01-09 16:46:04 +00001456
tierno98e909c2017-10-14 13:27:03 +02001457 return server.id, created_items
tierno7edb6752016-03-21 17:37:52 +01001458# except nvExceptions.NotFound as e:
1459# error_value=-vimconn.HTTP_Not_Found
1460# error_text= "vm instance %s not found" % vm_id
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001461# except TypeError as e:
1462# raise vimconn.vimconnException(type(e).__name__ + ": "+ str(e), http_code=vimconn.HTTP_Bad_Request)
1463
1464 except Exception as e:
tierno98e909c2017-10-14 13:27:03 +02001465 server_id = None
1466 if server:
1467 server_id = server.id
1468 try:
1469 self.delete_vminstance(server_id, created_items)
1470 except Exception as e2:
1471 self.logger.error("new_vminstance rollback fail {}".format(e2))
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001472
tiernoae4a8d12016-07-08 12:30:39 +02001473 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001474
tiernoae4a8d12016-07-08 12:30:39 +02001475 def get_vminstance(self,vm_id):
tierno7edb6752016-03-21 17:37:52 +01001476 '''Returns the VM instance information from VIM'''
tiernoae4a8d12016-07-08 12:30:39 +02001477 #self.logger.debug("Getting VM from VIM")
tierno7edb6752016-03-21 17:37:52 +01001478 try:
1479 self._reload_connection()
1480 server = self.nova.servers.find(id=vm_id)
1481 #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
tiernoae4a8d12016-07-08 12:30:39 +02001482 return server.to_dict()
tierno8e995ce2016-09-22 08:13:00 +00001483 except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001484 self._format_exception(e)
1485
1486 def get_vminstance_console(self,vm_id, console_type="vnc"):
tierno7edb6752016-03-21 17:37:52 +01001487 '''
1488 Get a console for the virtual machine
1489 Params:
1490 vm_id: uuid of the VM
1491 console_type, can be:
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001492 "novnc" (by default), "xvpvnc" for VNC types,
tierno7edb6752016-03-21 17:37:52 +01001493 "rdp-html5" for RDP types, "spice-html5" for SPICE types
tiernoae4a8d12016-07-08 12:30:39 +02001494 Returns dict with the console parameters:
1495 protocol: ssh, ftp, http, https, ...
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001496 server: usually ip address
1497 port: the http, ssh, ... port
1498 suffix: extra text, e.g. the http path and query string
tierno7edb6752016-03-21 17:37:52 +01001499 '''
tiernoae4a8d12016-07-08 12:30:39 +02001500 self.logger.debug("Getting VM CONSOLE from VIM")
tierno7edb6752016-03-21 17:37:52 +01001501 try:
1502 self._reload_connection()
1503 server = self.nova.servers.find(id=vm_id)
1504 if console_type == None or console_type == "novnc":
1505 console_dict = server.get_vnc_console("novnc")
1506 elif console_type == "xvpvnc":
1507 console_dict = server.get_vnc_console(console_type)
1508 elif console_type == "rdp-html5":
1509 console_dict = server.get_rdp_console(console_type)
1510 elif console_type == "spice-html5":
1511 console_dict = server.get_spice_console(console_type)
1512 else:
tiernoae4a8d12016-07-08 12:30:39 +02001513 raise vimconn.vimconnException("console type '{}' not allowed".format(console_type), http_code=vimconn.HTTP_Bad_Request)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001514
tierno7edb6752016-03-21 17:37:52 +01001515 console_dict1 = console_dict.get("console")
1516 if console_dict1:
1517 console_url = console_dict1.get("url")
1518 if console_url:
1519 #parse console_url
1520 protocol_index = console_url.find("//")
1521 suffix_index = console_url[protocol_index+2:].find("/") + protocol_index+2
1522 port_index = console_url[protocol_index+2:suffix_index].find(":") + protocol_index+2
1523 if protocol_index < 0 or port_index<0 or suffix_index<0:
1524 return -vimconn.HTTP_Internal_Server_Error, "Unexpected response from VIM"
1525 console_dict={"protocol": console_url[0:protocol_index],
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001526 "server": console_url[protocol_index+2:port_index],
1527 "port": console_url[port_index:suffix_index],
1528 "suffix": console_url[suffix_index+1:]
tierno7edb6752016-03-21 17:37:52 +01001529 }
1530 protocol_index += 2
tiernoae4a8d12016-07-08 12:30:39 +02001531 return console_dict
1532 raise vimconn.vimconnUnexpectedResponse("Unexpected response from VIM")
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001533
tierno8e995ce2016-09-22 08:13:00 +00001534 except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.BadRequest, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001535 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001536
tierno98e909c2017-10-14 13:27:03 +02001537 def delete_vminstance(self, vm_id, created_items=None):
tiernoae4a8d12016-07-08 12:30:39 +02001538 '''Removes a VM instance from VIM. Returns the old identifier
tierno7edb6752016-03-21 17:37:52 +01001539 '''
tiernoae4a8d12016-07-08 12:30:39 +02001540 #print "osconnector: Getting VM from VIM"
tierno98e909c2017-10-14 13:27:03 +02001541 if created_items == None:
1542 created_items = {}
tierno7edb6752016-03-21 17:37:52 +01001543 try:
1544 self._reload_connection()
tierno98e909c2017-10-14 13:27:03 +02001545 # delete VM ports attached to this networks before the virtual machine
1546 for k, v in created_items.items():
1547 if not v: # skip already deleted
1548 continue
tierno7edb6752016-03-21 17:37:52 +01001549 try:
tiernoad6bdd42018-01-10 10:43:46 +01001550 k_item, _, k_id = k.partition(":")
1551 if k_item == "port":
1552 self.neutron.delete_port(k_id)
tierno7edb6752016-03-21 17:37:52 +01001553 except Exception as e:
tierno00e3df72017-11-29 17:20:13 +01001554 self.logger.error("Error deleting port: {}: {}".format(type(e).__name__, e))
montesmoreno0c8def02016-12-22 12:16:23 +00001555
tierno98e909c2017-10-14 13:27:03 +02001556 # #commented because detaching the volumes makes the servers.delete not work properly ?!?
1557 # #dettach volumes attached
1558 # server = self.nova.servers.get(vm_id)
1559 # volumes_attached_dict = server._info['os-extended-volumes:volumes_attached'] #volume['id']
1560 # #for volume in volumes_attached_dict:
1561 # # self.cinder.volumes.detach(volume['id'])
montesmoreno0c8def02016-12-22 12:16:23 +00001562
tierno98e909c2017-10-14 13:27:03 +02001563 if vm_id:
1564 self.nova.servers.delete(vm_id)
montesmoreno0c8def02016-12-22 12:16:23 +00001565
tierno98e909c2017-10-14 13:27:03 +02001566 # delete volumes. Although having detached, they should have in active status before deleting
1567 # we ensure in this loop
montesmoreno0c8def02016-12-22 12:16:23 +00001568 keep_waiting = True
1569 elapsed_time = 0
1570 while keep_waiting and elapsed_time < volume_timeout:
1571 keep_waiting = False
tierno98e909c2017-10-14 13:27:03 +02001572 for k, v in created_items.items():
1573 if not v: # skip already deleted
1574 continue
1575 try:
tiernoad6bdd42018-01-10 10:43:46 +01001576 k_item, _, k_id = k.partition(":")
1577 if k_item == "volume":
1578 if self.cinder.volumes.get(k_id).status != 'available':
tierno98e909c2017-10-14 13:27:03 +02001579 keep_waiting = True
1580 else:
tiernoad6bdd42018-01-10 10:43:46 +01001581 self.cinder.volumes.delete(k_id)
tierno98e909c2017-10-14 13:27:03 +02001582 except Exception as e:
tierno00e3df72017-11-29 17:20:13 +01001583 self.logger.error("Error deleting volume: {}: {}".format(type(e).__name__, e))
montesmoreno0c8def02016-12-22 12:16:23 +00001584 if keep_waiting:
1585 time.sleep(1)
1586 elapsed_time += 1
tierno98e909c2017-10-14 13:27:03 +02001587 return None
tierno8e995ce2016-09-22 08:13:00 +00001588 except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001589 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001590
tiernoae4a8d12016-07-08 12:30:39 +02001591 def refresh_vms_status(self, vm_list):
1592 '''Get the status of the virtual machines and their interfaces/ports
1593 Params: the list of VM identifiers
1594 Returns a dictionary with:
1595 vm_id: #VIM id of this Virtual Machine
1596 status: #Mandatory. Text with one of:
1597 # DELETED (not found at vim)
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001598 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
tiernoae4a8d12016-07-08 12:30:39 +02001599 # OTHER (Vim reported other status not understood)
1600 # ERROR (VIM indicates an ERROR status)
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001601 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
tiernoae4a8d12016-07-08 12:30:39 +02001602 # CREATING (on building process), ERROR
1603 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1604 #
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001605 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
tiernoae4a8d12016-07-08 12:30:39 +02001606 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1607 interfaces:
1608 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1609 mac_address: #Text format XX:XX:XX:XX:XX:XX
1610 vim_net_id: #network id where this interface is connected
1611 vim_interface_id: #interface/port VIM id
1612 ip_address: #null, or text with IPv4, IPv6 address
tierno867ffe92017-03-27 12:50:34 +02001613 compute_node: #identification of compute node where PF,VF interface is allocated
1614 pci: #PCI address of the NIC that hosts the PF,VF
1615 vlan: #physical VLAN used for VF
tierno7edb6752016-03-21 17:37:52 +01001616 '''
tiernoae4a8d12016-07-08 12:30:39 +02001617 vm_dict={}
1618 self.logger.debug("refresh_vms status: Getting tenant VM instance information from VIM")
1619 for vm_id in vm_list:
1620 vm={}
1621 try:
1622 vm_vim = self.get_vminstance(vm_id)
1623 if vm_vim['status'] in vmStatus2manoFormat:
1624 vm['status'] = vmStatus2manoFormat[ vm_vim['status'] ]
tierno7edb6752016-03-21 17:37:52 +01001625 else:
tiernoae4a8d12016-07-08 12:30:39 +02001626 vm['status'] = "OTHER"
1627 vm['error_msg'] = "VIM status reported " + vm_vim['status']
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001628
1629 vm['vim_info'] = self.serialize(vm_vim)
1630
tiernoae4a8d12016-07-08 12:30:39 +02001631 vm["interfaces"] = []
1632 if vm_vim.get('fault'):
1633 vm['error_msg'] = str(vm_vim['fault'])
1634 #get interfaces
tierno7edb6752016-03-21 17:37:52 +01001635 try:
tiernoae4a8d12016-07-08 12:30:39 +02001636 self._reload_connection()
tiernob42fd9b2018-06-20 10:44:32 +02001637 port_dict = self.neutron.list_ports(device_id=vm_id)
tiernoae4a8d12016-07-08 12:30:39 +02001638 for port in port_dict["ports"]:
1639 interface={}
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001640 interface['vim_info'] = self.serialize(port)
tiernoae4a8d12016-07-08 12:30:39 +02001641 interface["mac_address"] = port.get("mac_address")
1642 interface["vim_net_id"] = port["network_id"]
1643 interface["vim_interface_id"] = port["id"]
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001644 # check if OS-EXT-SRV-ATTR:host is there,
Mike Marchetti5b9da422017-05-02 15:35:47 -04001645 # in case of non-admin credentials, it will be missing
1646 if vm_vim.get('OS-EXT-SRV-ATTR:host'):
1647 interface["compute_node"] = vm_vim['OS-EXT-SRV-ATTR:host']
tierno867ffe92017-03-27 12:50:34 +02001648 interface["pci"] = None
Mike Marchetti5b9da422017-05-02 15:35:47 -04001649
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001650 # check if binding:profile is there,
Mike Marchetti5b9da422017-05-02 15:35:47 -04001651 # in case of non-admin credentials, it will be missing
1652 if port.get('binding:profile'):
1653 if port['binding:profile'].get('pci_slot'):
1654 # TODO: At the moment sr-iov pci addresses are converted to PF pci addresses by setting the slot to 0x00
1655 # TODO: This is just a workaround valid for niantinc. Find a better way to do so
1656 # CHANGE DDDD:BB:SS.F to DDDD:BB:00.(F%2) assuming there are 2 ports per nic
1657 pci = port['binding:profile']['pci_slot']
1658 # interface["pci"] = pci[:-4] + "00." + str(int(pci[-1]) % 2)
1659 interface["pci"] = pci
tierno867ffe92017-03-27 12:50:34 +02001660 interface["vlan"] = None
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001661 #if network is of type vlan and port is of type direct (sr-iov) then set vlan id
Pablo Montes Moreno51e553b2017-03-23 16:39:12 +01001662 network = self.neutron.show_network(port["network_id"])
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001663 if network['network'].get('provider:network_type') == 'vlan' and \
1664 port.get("binding:vnic_type") == "direct":
tierno867ffe92017-03-27 12:50:34 +02001665 interface["vlan"] = network['network'].get('provider:segmentation_id')
tiernoae4a8d12016-07-08 12:30:39 +02001666 ips=[]
1667 #look for floating ip address
tiernob42fd9b2018-06-20 10:44:32 +02001668 try:
1669 floating_ip_dict = self.neutron.list_floatingips(port_id=port["id"])
1670 if floating_ip_dict.get("floatingips"):
1671 ips.append(floating_ip_dict["floatingips"][0].get("floating_ip_address") )
1672 except Exception:
1673 pass
tierno7edb6752016-03-21 17:37:52 +01001674
tiernoae4a8d12016-07-08 12:30:39 +02001675 for subnet in port["fixed_ips"]:
1676 ips.append(subnet["ip_address"])
1677 interface["ip_address"] = ";".join(ips)
1678 vm["interfaces"].append(interface)
1679 except Exception as e:
tiernob42fd9b2018-06-20 10:44:32 +02001680 self.logger.error("Error getting vm interface information {}: {}".format(type(e).__name__, e),
1681 exc_info=True)
tiernoae4a8d12016-07-08 12:30:39 +02001682 except vimconn.vimconnNotFoundException as e:
1683 self.logger.error("Exception getting vm status: %s", str(e))
1684 vm['status'] = "DELETED"
1685 vm['error_msg'] = str(e)
1686 except vimconn.vimconnException as e:
1687 self.logger.error("Exception getting vm status: %s", str(e))
1688 vm['status'] = "VIM_ERROR"
1689 vm['error_msg'] = str(e)
1690 vm_dict[vm_id] = vm
1691 return vm_dict
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001692
tierno98e909c2017-10-14 13:27:03 +02001693 def action_vminstance(self, vm_id, action_dict, created_items={}):
tierno7edb6752016-03-21 17:37:52 +01001694 '''Send and action over a VM instance from VIM
tierno98e909c2017-10-14 13:27:03 +02001695 Returns None or the console dict if the action was successfully sent to the VIM'''
tiernoae4a8d12016-07-08 12:30:39 +02001696 self.logger.debug("Action over VM '%s': %s", vm_id, str(action_dict))
tierno7edb6752016-03-21 17:37:52 +01001697 try:
1698 self._reload_connection()
1699 server = self.nova.servers.find(id=vm_id)
1700 if "start" in action_dict:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001701 if action_dict["start"]=="rebuild":
tierno7edb6752016-03-21 17:37:52 +01001702 server.rebuild()
1703 else:
1704 if server.status=="PAUSED":
1705 server.unpause()
1706 elif server.status=="SUSPENDED":
1707 server.resume()
1708 elif server.status=="SHUTOFF":
1709 server.start()
1710 elif "pause" in action_dict:
1711 server.pause()
1712 elif "resume" in action_dict:
1713 server.resume()
1714 elif "shutoff" in action_dict or "shutdown" in action_dict:
1715 server.stop()
1716 elif "forceOff" in action_dict:
1717 server.stop() #TODO
1718 elif "terminate" in action_dict:
1719 server.delete()
1720 elif "createImage" in action_dict:
1721 server.create_image()
1722 #"path":path_schema,
1723 #"description":description_schema,
1724 #"name":name_schema,
1725 #"metadata":metadata_schema,
1726 #"imageRef": id_schema,
1727 #"disk": {"oneOf":[{"type": "null"}, {"type":"string"}] },
1728 elif "rebuild" in action_dict:
1729 server.rebuild(server.image['id'])
1730 elif "reboot" in action_dict:
1731 server.reboot() #reboot_type='SOFT'
1732 elif "console" in action_dict:
1733 console_type = action_dict["console"]
1734 if console_type == None or console_type == "novnc":
1735 console_dict = server.get_vnc_console("novnc")
1736 elif console_type == "xvpvnc":
1737 console_dict = server.get_vnc_console(console_type)
1738 elif console_type == "rdp-html5":
1739 console_dict = server.get_rdp_console(console_type)
1740 elif console_type == "spice-html5":
1741 console_dict = server.get_spice_console(console_type)
1742 else:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001743 raise vimconn.vimconnException("console type '{}' not allowed".format(console_type),
tiernoae4a8d12016-07-08 12:30:39 +02001744 http_code=vimconn.HTTP_Bad_Request)
tierno7edb6752016-03-21 17:37:52 +01001745 try:
1746 console_url = console_dict["console"]["url"]
1747 #parse console_url
1748 protocol_index = console_url.find("//")
1749 suffix_index = console_url[protocol_index+2:].find("/") + protocol_index+2
1750 port_index = console_url[protocol_index+2:suffix_index].find(":") + protocol_index+2
1751 if protocol_index < 0 or port_index<0 or suffix_index<0:
tiernoae4a8d12016-07-08 12:30:39 +02001752 raise vimconn.vimconnException("Unexpected response from VIM " + str(console_dict))
tierno7edb6752016-03-21 17:37:52 +01001753 console_dict2={"protocol": console_url[0:protocol_index],
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001754 "server": console_url[protocol_index+2 : port_index],
1755 "port": int(console_url[port_index+1 : suffix_index]),
1756 "suffix": console_url[suffix_index+1:]
tierno7edb6752016-03-21 17:37:52 +01001757 }
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001758 return console_dict2
tiernoae4a8d12016-07-08 12:30:39 +02001759 except Exception as e:
1760 raise vimconn.vimconnException("Unexpected response from VIM " + str(console_dict))
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001761
tierno98e909c2017-10-14 13:27:03 +02001762 return None
tierno8e995ce2016-09-22 08:13:00 +00001763 except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001764 self._format_exception(e)
1765 #TODO insert exception vimconn.HTTP_Unauthorized
1766
kate721d79b2017-06-24 04:21:38 -07001767 ####### VIO Specific Changes #########
garciadeblasebd66722019-01-31 16:01:31 +00001768 def _generate_vlanID(self):
kate721d79b2017-06-24 04:21:38 -07001769 """
1770 Method to get unused vlanID
1771 Args:
1772 None
1773 Returns:
1774 vlanID
1775 """
1776 #Get used VLAN IDs
1777 usedVlanIDs = []
1778 networks = self.get_network_list()
1779 for net in networks:
1780 if net.get('provider:segmentation_id'):
1781 usedVlanIDs.append(net.get('provider:segmentation_id'))
1782 used_vlanIDs = set(usedVlanIDs)
1783
1784 #find unused VLAN ID
1785 for vlanID_range in self.config.get('dataplane_net_vlan_range'):
1786 try:
1787 start_vlanid , end_vlanid = map(int, vlanID_range.replace(" ", "").split("-"))
tierno7d782ef2019-10-04 12:56:31 +00001788 for vlanID in range(start_vlanid, end_vlanid + 1):
kate721d79b2017-06-24 04:21:38 -07001789 if vlanID not in used_vlanIDs:
1790 return vlanID
1791 except Exception as exp:
1792 raise vimconn.vimconnException("Exception {} occurred while generating VLAN ID.".format(exp))
1793 else:
1794 raise vimconn.vimconnConflictException("Unable to create the SRIOV VLAN network."\
1795 " All given Vlan IDs {} are in use.".format(self.config.get('dataplane_net_vlan_range')))
1796
1797
garciadeblasebd66722019-01-31 16:01:31 +00001798 def _generate_multisegment_vlanID(self):
1799 """
1800 Method to get unused vlanID
1801 Args:
1802 None
1803 Returns:
1804 vlanID
1805 """
tierno6869ae72020-01-09 17:37:34 +00001806 # Get used VLAN IDs
garciadeblasebd66722019-01-31 16:01:31 +00001807 usedVlanIDs = []
1808 networks = self.get_network_list()
1809 for net in networks:
1810 if net.get('provider:network_type') == "vlan" and net.get('provider:segmentation_id'):
1811 usedVlanIDs.append(net.get('provider:segmentation_id'))
1812 elif net.get('segments'):
1813 for segment in net.get('segments'):
1814 if segment.get('provider:network_type') == "vlan" and segment.get('provider:segmentation_id'):
1815 usedVlanIDs.append(segment.get('provider:segmentation_id'))
1816 used_vlanIDs = set(usedVlanIDs)
1817
tierno6869ae72020-01-09 17:37:34 +00001818 # find unused VLAN ID
garciadeblasebd66722019-01-31 16:01:31 +00001819 for vlanID_range in self.config.get('multisegment_vlan_range'):
1820 try:
tierno6869ae72020-01-09 17:37:34 +00001821 start_vlanid, end_vlanid = map(int, vlanID_range.replace(" ", "").split("-"))
tierno7d782ef2019-10-04 12:56:31 +00001822 for vlanID in range(start_vlanid, end_vlanid + 1):
garciadeblasebd66722019-01-31 16:01:31 +00001823 if vlanID not in used_vlanIDs:
1824 return vlanID
1825 except Exception as exp:
1826 raise vimconn.vimconnException("Exception {} occurred while generating VLAN ID.".format(exp))
1827 else:
tierno6869ae72020-01-09 17:37:34 +00001828 raise vimconn.vimconnConflictException("Unable to create the VLAN segment."
garciadeblasebd66722019-01-31 16:01:31 +00001829 " All VLAN IDs {} are in use.".format(self.config.get('multisegment_vlan_range')))
1830
1831
1832 def _validate_vlan_ranges(self, input_vlan_range, text_vlan_range):
kate721d79b2017-06-24 04:21:38 -07001833 """
1834 Method to validate user given vlanID ranges
1835 Args: None
1836 Returns: None
1837 """
garciadeblasebd66722019-01-31 16:01:31 +00001838 for vlanID_range in input_vlan_range:
kate721d79b2017-06-24 04:21:38 -07001839 vlan_range = vlanID_range.replace(" ", "")
1840 #validate format
1841 vlanID_pattern = r'(\d)*-(\d)*$'
1842 match_obj = re.match(vlanID_pattern, vlan_range)
1843 if not match_obj:
garciadeblasebd66722019-01-31 16:01:31 +00001844 raise vimconn.vimconnConflictException("Invalid VLAN range for {}: {}.You must provide "\
1845 "'{}' in format [start_ID - end_ID].".format(text_vlan_range, vlanID_range, text_vlan_range))
kate721d79b2017-06-24 04:21:38 -07001846
1847 start_vlanid , end_vlanid = map(int,vlan_range.split("-"))
1848 if start_vlanid <= 0 :
garciadeblasebd66722019-01-31 16:01:31 +00001849 raise vimconn.vimconnConflictException("Invalid VLAN range for {}: {}."\
kate721d79b2017-06-24 04:21:38 -07001850 "Start ID can not be zero. For VLAN "\
garciadeblasebd66722019-01-31 16:01:31 +00001851 "networks valid IDs are 1 to 4094 ".format(text_vlan_range, vlanID_range))
kate721d79b2017-06-24 04:21:38 -07001852 if end_vlanid > 4094 :
garciadeblasebd66722019-01-31 16:01:31 +00001853 raise vimconn.vimconnConflictException("Invalid VLAN range for {}: {}."\
kate721d79b2017-06-24 04:21:38 -07001854 "End VLAN ID can not be greater than 4094. For VLAN "\
garciadeblasebd66722019-01-31 16:01:31 +00001855 "networks valid IDs are 1 to 4094 ".format(text_vlan_range, vlanID_range))
kate721d79b2017-06-24 04:21:38 -07001856
1857 if start_vlanid > end_vlanid:
garciadeblasebd66722019-01-31 16:01:31 +00001858 raise vimconn.vimconnConflictException("Invalid VLAN range for {}: {}."\
1859 "You must provide '{}' in format start_ID - end_ID and "\
1860 "start_ID < end_ID ".format(text_vlan_range, vlanID_range, text_vlan_range))
kate721d79b2017-06-24 04:21:38 -07001861
tiernoae4a8d12016-07-08 12:30:39 +02001862#NOT USED FUNCTIONS
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001863
tiernoae4a8d12016-07-08 12:30:39 +02001864 def new_external_port(self, port_data):
1865 #TODO openstack if needed
1866 '''Adds a external port to VIM'''
1867 '''Returns the port identifier'''
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001868 return -vimconn.HTTP_Internal_Server_Error, "osconnector.new_external_port() not implemented"
1869
tiernoae4a8d12016-07-08 12:30:39 +02001870 def connect_port_network(self, port_id, network_id, admin=False):
1871 #TODO openstack if needed
1872 '''Connects a external port to a network'''
1873 '''Returns status code of the VIM response'''
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001874 return -vimconn.HTTP_Internal_Server_Error, "osconnector.connect_port_network() not implemented"
1875
tiernoae4a8d12016-07-08 12:30:39 +02001876 def new_user(self, user_name, user_passwd, tenant_id=None):
1877 '''Adds a new user to openstack VIM'''
1878 '''Returns the user identifier'''
1879 self.logger.debug("osconnector: Adding a new user to VIM")
1880 try:
1881 self._reload_connection()
Eduardo Sousae3c0dbc2018-09-03 11:56:07 +01001882 user=self.keystone.users.create(user_name, password=user_passwd, default_project=tenant_id)
tiernoae4a8d12016-07-08 12:30:39 +02001883 #self.keystone.tenants.add_user(self.k_creds["username"], #role)
1884 return user.id
1885 except ksExceptions.ConnectionError as e:
1886 error_value=-vimconn.HTTP_Bad_Request
1887 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1888 except ksExceptions.ClientException as e: #TODO remove
tierno7edb6752016-03-21 17:37:52 +01001889 error_value=-vimconn.HTTP_Bad_Request
1890 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1891 #TODO insert exception vimconn.HTTP_Unauthorized
1892 #if reaching here is because an exception
tierno9c5c8322018-03-23 15:44:03 +01001893 self.logger.debug("new_user " + error_text)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001894 return error_value, error_text
tiernoae4a8d12016-07-08 12:30:39 +02001895
1896 def delete_user(self, user_id):
1897 '''Delete a user from openstack VIM'''
1898 '''Returns the user identifier'''
1899 if self.debug:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001900 print("osconnector: Deleting a user from VIM")
tiernoae4a8d12016-07-08 12:30:39 +02001901 try:
1902 self._reload_connection()
1903 self.keystone.users.delete(user_id)
1904 return 1, user_id
1905 except ksExceptions.ConnectionError as e:
1906 error_value=-vimconn.HTTP_Bad_Request
1907 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1908 except ksExceptions.NotFound as e:
1909 error_value=-vimconn.HTTP_Not_Found
1910 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1911 except ksExceptions.ClientException as e: #TODO remove
1912 error_value=-vimconn.HTTP_Bad_Request
1913 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1914 #TODO insert exception vimconn.HTTP_Unauthorized
1915 #if reaching here is because an exception
tierno9c5c8322018-03-23 15:44:03 +01001916 self.logger.debug("delete_tenant " + error_text)
tiernoae4a8d12016-07-08 12:30:39 +02001917 return error_value, error_text
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001918
tierno7edb6752016-03-21 17:37:52 +01001919 def get_hosts_info(self):
1920 '''Get the information of deployed hosts
1921 Returns the hosts content'''
1922 if self.debug:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001923 print("osconnector: Getting Host info from VIM")
tierno7edb6752016-03-21 17:37:52 +01001924 try:
1925 h_list=[]
1926 self._reload_connection()
1927 hypervisors = self.nova.hypervisors.list()
1928 for hype in hypervisors:
1929 h_list.append( hype.to_dict() )
1930 return 1, {"hosts":h_list}
1931 except nvExceptions.NotFound as e:
1932 error_value=-vimconn.HTTP_Not_Found
1933 error_text= (str(e) if len(e.args)==0 else str(e.args[0]))
1934 except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
1935 error_value=-vimconn.HTTP_Bad_Request
1936 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1937 #TODO insert exception vimconn.HTTP_Unauthorized
1938 #if reaching here is because an exception
tierno9c5c8322018-03-23 15:44:03 +01001939 self.logger.debug("get_hosts_info " + error_text)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001940 return error_value, error_text
tierno7edb6752016-03-21 17:37:52 +01001941
1942 def get_hosts(self, vim_tenant):
1943 '''Get the hosts and deployed instances
1944 Returns the hosts content'''
1945 r, hype_dict = self.get_hosts_info()
1946 if r<0:
1947 return r, hype_dict
1948 hypervisors = hype_dict["hosts"]
1949 try:
1950 servers = self.nova.servers.list()
1951 for hype in hypervisors:
1952 for server in servers:
1953 if server.to_dict()['OS-EXT-SRV-ATTR:hypervisor_hostname']==hype['hypervisor_hostname']:
1954 if 'vm' in hype:
1955 hype['vm'].append(server.id)
1956 else:
1957 hype['vm'] = [server.id]
1958 return 1, hype_dict
1959 except nvExceptions.NotFound as e:
1960 error_value=-vimconn.HTTP_Not_Found
1961 error_text= (str(e) if len(e.args)==0 else str(e.args[0]))
1962 except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
1963 error_value=-vimconn.HTTP_Bad_Request
1964 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1965 #TODO insert exception vimconn.HTTP_Unauthorized
1966 #if reaching here is because an exception
tierno9c5c8322018-03-23 15:44:03 +01001967 self.logger.debug("get_hosts " + error_text)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001968 return error_value, error_text
tierno7edb6752016-03-21 17:37:52 +01001969
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001970 def new_classification(self, name, ctype, definition):
tierno7d782ef2019-10-04 12:56:31 +00001971 self.logger.debug('Adding a new (Traffic) Classification to VIM, named %s', name)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001972 try:
1973 new_class = None
1974 self._reload_connection()
1975 if ctype not in supportedClassificationTypes:
1976 raise vimconn.vimconnNotSupportedException(
1977 'OpenStack VIM connector doesn\'t support provided '
1978 'Classification Type {}, supported ones are: '
1979 '{}'.format(ctype, supportedClassificationTypes))
1980 if not self._validate_classification(ctype, definition):
1981 raise vimconn.vimconnException(
1982 'Incorrect Classification definition '
1983 'for the type specified.')
1984 classification_dict = definition
1985 classification_dict['name'] = name
tierno7edb6752016-03-21 17:37:52 +01001986
Igor D.Ccaadc442017-11-06 12:48:48 +00001987 new_class = self.neutron.create_sfc_flow_classifier(
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001988 {'flow_classifier': classification_dict})
1989 return new_class['flow_classifier']['id']
1990 except (neExceptions.ConnectionFailed, ksExceptions.ClientException,
1991 neExceptions.NeutronException, ConnectionError) as e:
1992 self.logger.error(
1993 'Creation of Classification failed.')
1994 self._format_exception(e)
1995
1996 def get_classification(self, class_id):
1997 self.logger.debug(" Getting Classification %s from VIM", class_id)
1998 filter_dict = {"id": class_id}
1999 class_list = self.get_classification_list(filter_dict)
2000 if len(class_list) == 0:
2001 raise vimconn.vimconnNotFoundException(
2002 "Classification '{}' not found".format(class_id))
2003 elif len(class_list) > 1:
2004 raise vimconn.vimconnConflictException(
2005 "Found more than one Classification with this criteria")
2006 classification = class_list[0]
2007 return classification
2008
2009 def get_classification_list(self, filter_dict={}):
2010 self.logger.debug("Getting Classifications from VIM filter: '%s'",
2011 str(filter_dict))
2012 try:
tierno69b590e2018-03-13 18:52:23 +01002013 filter_dict_os = filter_dict.copy()
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002014 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +01002015 if self.api_version3 and "tenant_id" in filter_dict_os:
2016 filter_dict_os['project_id'] = filter_dict_os.pop('tenant_id')
Igor D.Ccaadc442017-11-06 12:48:48 +00002017 classification_dict = self.neutron.list_sfc_flow_classifiers(
tierno69b590e2018-03-13 18:52:23 +01002018 **filter_dict_os)
2019 classification_list = classification_dict["flow_classifiers"]
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002020 self.__classification_os2mano(classification_list)
2021 return classification_list
2022 except (neExceptions.ConnectionFailed, ksExceptions.ClientException,
2023 neExceptions.NeutronException, ConnectionError) as e:
2024 self._format_exception(e)
2025
2026 def delete_classification(self, class_id):
2027 self.logger.debug("Deleting Classification '%s' from VIM", class_id)
2028 try:
2029 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00002030 self.neutron.delete_sfc_flow_classifier(class_id)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002031 return class_id
2032 except (neExceptions.ConnectionFailed, neExceptions.NeutronException,
2033 ksExceptions.ClientException, neExceptions.NeutronException,
2034 ConnectionError) as e:
2035 self._format_exception(e)
2036
2037 def new_sfi(self, name, ingress_ports, egress_ports, sfc_encap=True):
tierno7d782ef2019-10-04 12:56:31 +00002038 self.logger.debug("Adding a new Service Function Instance to VIM, named '%s'", name)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002039 try:
2040 new_sfi = None
2041 self._reload_connection()
2042 correlation = None
2043 if sfc_encap:
Igor D.Ccaadc442017-11-06 12:48:48 +00002044 correlation = 'nsh'
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002045 if len(ingress_ports) != 1:
2046 raise vimconn.vimconnNotSupportedException(
2047 "OpenStack VIM connector can only have "
2048 "1 ingress port per SFI")
2049 if len(egress_ports) != 1:
2050 raise vimconn.vimconnNotSupportedException(
2051 "OpenStack VIM connector can only have "
2052 "1 egress port per SFI")
2053 sfi_dict = {'name': name,
2054 'ingress': ingress_ports[0],
2055 'egress': egress_ports[0],
2056 'service_function_parameters': {
2057 'correlation': correlation}}
Igor D.Ccaadc442017-11-06 12:48:48 +00002058 new_sfi = self.neutron.create_sfc_port_pair({'port_pair': sfi_dict})
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002059 return new_sfi['port_pair']['id']
2060 except (neExceptions.ConnectionFailed, ksExceptions.ClientException,
2061 neExceptions.NeutronException, ConnectionError) as e:
2062 if new_sfi:
2063 try:
Igor D.Ccaadc442017-11-06 12:48:48 +00002064 self.neutron.delete_sfc_port_pair(
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002065 new_sfi['port_pair']['id'])
2066 except Exception:
2067 self.logger.error(
2068 'Creation of Service Function Instance failed, with '
2069 'subsequent deletion failure as well.')
2070 self._format_exception(e)
2071
2072 def get_sfi(self, sfi_id):
tierno7d782ef2019-10-04 12:56:31 +00002073 self.logger.debug('Getting Service Function Instance %s from VIM', sfi_id)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002074 filter_dict = {"id": sfi_id}
2075 sfi_list = self.get_sfi_list(filter_dict)
2076 if len(sfi_list) == 0:
tierno7d782ef2019-10-04 12:56:31 +00002077 raise vimconn.vimconnNotFoundException("Service Function Instance '{}' not found".format(sfi_id))
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002078 elif len(sfi_list) > 1:
2079 raise vimconn.vimconnConflictException(
2080 'Found more than one Service Function Instance '
2081 'with this criteria')
2082 sfi = sfi_list[0]
2083 return sfi
2084
2085 def get_sfi_list(self, filter_dict={}):
tierno7d782ef2019-10-04 12:56:31 +00002086 self.logger.debug("Getting Service Function Instances from VIM filter: '%s'", str(filter_dict))
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002087 try:
2088 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +01002089 filter_dict_os = filter_dict.copy()
2090 if self.api_version3 and "tenant_id" in filter_dict_os:
2091 filter_dict_os['project_id'] = filter_dict_os.pop('tenant_id')
2092 sfi_dict = self.neutron.list_sfc_port_pairs(**filter_dict_os)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002093 sfi_list = sfi_dict["port_pairs"]
2094 self.__sfi_os2mano(sfi_list)
2095 return sfi_list
2096 except (neExceptions.ConnectionFailed, ksExceptions.ClientException,
2097 neExceptions.NeutronException, ConnectionError) as e:
2098 self._format_exception(e)
2099
2100 def delete_sfi(self, sfi_id):
2101 self.logger.debug("Deleting Service Function Instance '%s' "
2102 "from VIM", sfi_id)
2103 try:
2104 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00002105 self.neutron.delete_sfc_port_pair(sfi_id)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002106 return sfi_id
2107 except (neExceptions.ConnectionFailed, neExceptions.NeutronException,
2108 ksExceptions.ClientException, neExceptions.NeutronException,
2109 ConnectionError) as e:
2110 self._format_exception(e)
2111
2112 def new_sf(self, name, sfis, sfc_encap=True):
tierno7d782ef2019-10-04 12:56:31 +00002113 self.logger.debug("Adding a new Service Function to VIM, named '%s'", name)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002114 try:
2115 new_sf = None
2116 self._reload_connection()
tierno9c5c8322018-03-23 15:44:03 +01002117 # correlation = None
2118 # if sfc_encap:
2119 # correlation = 'nsh'
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002120 for instance in sfis:
2121 sfi = self.get_sfi(instance)
Igor D.Ccaadc442017-11-06 12:48:48 +00002122 if sfi.get('sfc_encap') != sfc_encap:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002123 raise vimconn.vimconnNotSupportedException(
2124 "OpenStack VIM connector requires all SFIs of the "
2125 "same SF to share the same SFC Encapsulation")
2126 sf_dict = {'name': name,
2127 'port_pairs': sfis}
Igor D.Ccaadc442017-11-06 12:48:48 +00002128 new_sf = self.neutron.create_sfc_port_pair_group({
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002129 'port_pair_group': sf_dict})
2130 return new_sf['port_pair_group']['id']
2131 except (neExceptions.ConnectionFailed, ksExceptions.ClientException,
2132 neExceptions.NeutronException, ConnectionError) as e:
2133 if new_sf:
2134 try:
Igor D.Ccaadc442017-11-06 12:48:48 +00002135 self.neutron.delete_sfc_port_pair_group(
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002136 new_sf['port_pair_group']['id'])
2137 except Exception:
2138 self.logger.error(
2139 'Creation of Service Function failed, with '
2140 'subsequent deletion failure as well.')
2141 self._format_exception(e)
2142
2143 def get_sf(self, sf_id):
2144 self.logger.debug("Getting Service Function %s from VIM", sf_id)
2145 filter_dict = {"id": sf_id}
2146 sf_list = self.get_sf_list(filter_dict)
2147 if len(sf_list) == 0:
2148 raise vimconn.vimconnNotFoundException(
2149 "Service Function '{}' not found".format(sf_id))
2150 elif len(sf_list) > 1:
2151 raise vimconn.vimconnConflictException(
2152 "Found more than one Service Function with this criteria")
2153 sf = sf_list[0]
2154 return sf
2155
2156 def get_sf_list(self, filter_dict={}):
2157 self.logger.debug("Getting Service Function from VIM filter: '%s'",
2158 str(filter_dict))
2159 try:
2160 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +01002161 filter_dict_os = filter_dict.copy()
2162 if self.api_version3 and "tenant_id" in filter_dict_os:
2163 filter_dict_os['project_id'] = filter_dict_os.pop('tenant_id')
2164 sf_dict = self.neutron.list_sfc_port_pair_groups(**filter_dict_os)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002165 sf_list = sf_dict["port_pair_groups"]
2166 self.__sf_os2mano(sf_list)
2167 return sf_list
2168 except (neExceptions.ConnectionFailed, ksExceptions.ClientException,
2169 neExceptions.NeutronException, ConnectionError) as e:
2170 self._format_exception(e)
2171
2172 def delete_sf(self, sf_id):
2173 self.logger.debug("Deleting Service Function '%s' from VIM", sf_id)
2174 try:
2175 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00002176 self.neutron.delete_sfc_port_pair_group(sf_id)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002177 return sf_id
2178 except (neExceptions.ConnectionFailed, neExceptions.NeutronException,
2179 ksExceptions.ClientException, neExceptions.NeutronException,
2180 ConnectionError) as e:
2181 self._format_exception(e)
2182
2183 def new_sfp(self, name, classifications, sfs, sfc_encap=True, spi=None):
tierno7d782ef2019-10-04 12:56:31 +00002184 self.logger.debug("Adding a new Service Function Path to VIM, named '%s'", name)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002185 try:
2186 new_sfp = None
2187 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00002188 # In networking-sfc the MPLS encapsulation is legacy
2189 # should be used when no full SFC Encapsulation is intended
schillinge981df9a2019-01-24 09:25:11 +01002190 correlation = 'mpls'
Igor D.Ccaadc442017-11-06 12:48:48 +00002191 if sfc_encap:
2192 correlation = 'nsh'
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002193 sfp_dict = {'name': name,
2194 'flow_classifiers': classifications,
2195 'port_pair_groups': sfs,
2196 'chain_parameters': {'correlation': correlation}}
2197 if spi:
2198 sfp_dict['chain_id'] = spi
Igor D.Ccaadc442017-11-06 12:48:48 +00002199 new_sfp = self.neutron.create_sfc_port_chain({'port_chain': sfp_dict})
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002200 return new_sfp["port_chain"]["id"]
2201 except (neExceptions.ConnectionFailed, ksExceptions.ClientException,
2202 neExceptions.NeutronException, ConnectionError) as e:
2203 if new_sfp:
2204 try:
Igor D.Ccaadc442017-11-06 12:48:48 +00002205 self.neutron.delete_sfc_port_chain(new_sfp['port_chain']['id'])
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002206 except Exception:
2207 self.logger.error(
2208 'Creation of Service Function Path failed, with '
2209 'subsequent deletion failure as well.')
2210 self._format_exception(e)
2211
2212 def get_sfp(self, sfp_id):
2213 self.logger.debug(" Getting Service Function Path %s from VIM", sfp_id)
2214 filter_dict = {"id": sfp_id}
2215 sfp_list = self.get_sfp_list(filter_dict)
2216 if len(sfp_list) == 0:
2217 raise vimconn.vimconnNotFoundException(
2218 "Service Function Path '{}' not found".format(sfp_id))
2219 elif len(sfp_list) > 1:
2220 raise vimconn.vimconnConflictException(
2221 "Found more than one Service Function Path with this criteria")
2222 sfp = sfp_list[0]
2223 return sfp
2224
2225 def get_sfp_list(self, filter_dict={}):
tierno7d782ef2019-10-04 12:56:31 +00002226 self.logger.debug("Getting Service Function Paths from VIM filter: '%s'", str(filter_dict))
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002227 try:
2228 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +01002229 filter_dict_os = filter_dict.copy()
2230 if self.api_version3 and "tenant_id" in filter_dict_os:
2231 filter_dict_os['project_id'] = filter_dict_os.pop('tenant_id')
2232 sfp_dict = self.neutron.list_sfc_port_chains(**filter_dict_os)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002233 sfp_list = sfp_dict["port_chains"]
2234 self.__sfp_os2mano(sfp_list)
2235 return sfp_list
2236 except (neExceptions.ConnectionFailed, ksExceptions.ClientException,
2237 neExceptions.NeutronException, ConnectionError) as e:
2238 self._format_exception(e)
2239
2240 def delete_sfp(self, sfp_id):
tierno7d782ef2019-10-04 12:56:31 +00002241 self.logger.debug("Deleting Service Function Path '%s' from VIM", sfp_id)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002242 try:
2243 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00002244 self.neutron.delete_sfc_port_chain(sfp_id)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002245 return sfp_id
2246 except (neExceptions.ConnectionFailed, neExceptions.NeutronException,
2247 ksExceptions.ClientException, neExceptions.NeutronException,
2248 ConnectionError) as e:
2249 self._format_exception(e)
borsatti8a2dda32019-12-18 15:08:57 +00002250
2251
2252 def refresh_sfps_status(self, sfp_list):
2253 '''Get the status of the service function path
2254 Params: the list of sfp identifiers
2255 Returns a dictionary with:
2256 vm_id: #VIM id of this service function path
2257 status: #Mandatory. Text with one of:
2258 # DELETED (not found at vim)
2259 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
2260 # OTHER (Vim reported other status not understood)
2261 # ERROR (VIM indicates an ERROR status)
2262 # ACTIVE,
2263 # CREATING (on building process)
2264 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
2265 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)F
2266 '''
2267 sfp_dict={}
2268 self.logger.debug("refresh_sfps status: Getting tenant SFP information from VIM")
2269 for sfp_id in sfp_list:
2270 sfp={}
2271 try:
2272 sfp_vim = self.get_sfp(sfp_id)
2273 if sfp_vim['spi']:
2274 sfp['status'] = vmStatus2manoFormat[ 'ACTIVE' ]
2275 else:
2276 sfp['status'] = "OTHER"
2277 sfp['error_msg'] = "VIM status reported " + vm_vim['status']
2278
2279 sfp['vim_info'] = self.serialize(sfp_vim)
2280
2281 if sfp_vim.get('fault'):
2282 sfp['error_msg'] = str(sfp_vim['fault'])
2283
2284 except vimconn.vimconnNotFoundException as e:
2285 self.logger.error("Exception getting sfp status: %s", str(e))
2286 sfp['status'] = "DELETED"
2287 sfp['error_msg'] = str(e)
2288 except vimconn.vimconnException as e:
2289 self.logger.error("Exception getting sfp status: %s", str(e))
2290 sfp['status'] = "VIM_ERROR"
2291 sfp['error_msg'] = str(e)
2292 sfp_dict[sfp_id] = sfp
2293 return sfp_dict
2294
2295
2296 def refresh_sfis_status(self, sfi_list):
2297 '''Get the status of the service function instances
2298 Params: the list of sfi identifiers
2299 Returns a dictionary with:
2300 vm_id: #VIM id of this service function instance
2301 status: #Mandatory. Text with one of:
2302 # DELETED (not found at vim)
2303 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
2304 # OTHER (Vim reported other status not understood)
2305 # ERROR (VIM indicates an ERROR status)
2306 # ACTIVE,
2307 # CREATING (on building process)
2308 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
2309 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
2310 '''
2311 sfi_dict={}
2312 self.logger.debug("refresh_sfis status: Getting tenant sfi information from VIM")
2313 for sfi_id in sfi_list:
2314 sfi={}
2315 try:
2316 sfi_vim = self.get_sfi(sfi_id)
2317 if sfi_vim:
2318 sfi['status'] = vmStatus2manoFormat[ 'ACTIVE' ]
2319 else:
2320 sfi['status'] = "OTHER"
2321 sfi['error_msg'] = "VIM status reported " + vm_vim['status']
2322
2323 sfi['vim_info'] = self.serialize(sfi_vim)
2324
2325 if sfi_vim.get('fault'):
2326 sfi['error_msg'] = str(sfi_vim['fault'])
2327
2328 except vimconn.vimconnNotFoundException as e:
2329 self.logger.error("Exception getting sfi status: %s", str(e))
2330 sfi['status'] = "DELETED"
2331 sfi['error_msg'] = str(e)
2332 except vimconn.vimconnException as e:
2333 self.logger.error("Exception getting sfi status: %s", str(e))
2334 sfi['status'] = "VIM_ERROR"
2335 sfi['error_msg'] = str(e)
2336 sfi_dict[sfi_id] = sfi
2337 return sfi_dict
2338
2339
2340 def refresh_sfs_status(self, sf_list):
2341 '''Get the status of the service functions
2342 Params: the list of sf identifiers
2343 Returns a dictionary with:
2344 vm_id: #VIM id of this service function
2345 status: #Mandatory. Text with one of:
2346 # DELETED (not found at vim)
2347 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
2348 # OTHER (Vim reported other status not understood)
2349 # ERROR (VIM indicates an ERROR status)
2350 # ACTIVE,
2351 # CREATING (on building process)
2352 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
2353 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
2354 '''
2355 sf_dict={}
2356 self.logger.debug("refresh_sfs status: Getting tenant sf information from VIM")
2357 for sf_id in sf_list:
2358 sf={}
2359 try:
2360 sf_vim = self.get_sf(sf_id)
2361 if sf_vim:
2362 sf['status'] = vmStatus2manoFormat[ 'ACTIVE' ]
2363 else:
2364 sf['status'] = "OTHER"
2365 sf['error_msg'] = "VIM status reported " + vm_vim['status']
2366
2367 sf['vim_info'] = self.serialize(sf_vim)
2368
2369 if sf_vim.get('fault'):
2370 sf['error_msg'] = str(sf_vim['fault'])
2371
2372 except vimconn.vimconnNotFoundException as e:
2373 self.logger.error("Exception getting sf status: %s", str(e))
2374 sf['status'] = "DELETED"
2375 sf['error_msg'] = str(e)
2376 except vimconn.vimconnException as e:
2377 self.logger.error("Exception getting sf status: %s", str(e))
2378 sf['status'] = "VIM_ERROR"
2379 sf['error_msg'] = str(e)
2380 sf_dict[sf_id] = sf
2381 return sf_dict
2382
2383
2384
2385 def refresh_classifications_status(self, classification_list):
2386 '''Get the status of the classifications
2387 Params: the list of classification identifiers
2388 Returns a dictionary with:
2389 vm_id: #VIM id of this classifier
2390 status: #Mandatory. Text with one of:
2391 # DELETED (not found at vim)
2392 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
2393 # OTHER (Vim reported other status not understood)
2394 # ERROR (VIM indicates an ERROR status)
2395 # ACTIVE,
2396 # CREATING (on building process)
2397 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
2398 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
2399 '''
2400 classification_dict={}
2401 self.logger.debug("refresh_classifications status: Getting tenant classification information from VIM")
2402 for classification_id in classification_list:
2403 classification={}
2404 try:
2405 classification_vim = self.get_classification(classification_id)
2406 if classification_vim:
2407 classification['status'] = vmStatus2manoFormat[ 'ACTIVE' ]
2408 else:
2409 classification['status'] = "OTHER"
2410 classification['error_msg'] = "VIM status reported " + vm_vim['status']
2411
2412 classification['vim_info'] = self.serialize(classification_vim)
2413
2414 if classification_vim.get('fault'):
2415 classification['error_msg'] = str(classification_vim['fault'])
2416
2417 except vimconn.vimconnNotFoundException as e:
2418 self.logger.error("Exception getting classification status: %s", str(e))
2419 classification['status'] = "DELETED"
2420 classification['error_msg'] = str(e)
2421 except vimconn.vimconnException as e:
2422 self.logger.error("Exception getting classification status: %s", str(e))
2423 classification['status'] = "VIM_ERROR"
2424 classification['error_msg'] = str(e)
2425 classification_dict[classification_id] = classification
2426 return classification_dict