blob: b3203189b7d6a26dd9d0f87695fcb4197ebc18b6 [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
shashankjain3c83a212018-10-04 13:05:46 +0530497 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
kbsuba85c54d2019-10-17 16:30:32 +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}
535 if net_type=="data" or net_type=="ptp":
536 if self.config.get('dataplane_physical_net') == None:
tiernoae4a8d12016-07-08 12:30:39 +0200537 raise vimconn.vimconnConflictException("You must provide a 'dataplane_physical_net' at config value before creating sriov network")
garciadeblasebd66722019-01-31 16:01:31 +0000538 if not self.config.get('multisegment_support'):
539 network_dict["provider:physical_network"] = self.config[
540 'dataplane_physical_net'] # "physnet_sriov" #TODO physical
541 network_dict["provider:network_type"] = "vlan"
542 if vlan!=None:
543 network_dict["provider:network_type"] = vlan
544 else:
545 ###### Multi-segment case ######
546 segment_list = []
547 segment1_dict = {}
548 segment1_dict["provider:physical_network"] = ''
549 segment1_dict["provider:network_type"] = 'vxlan'
550 segment_list.append(segment1_dict)
551 segment2_dict = {}
552 segment2_dict["provider:physical_network"] = self.config['dataplane_physical_net']
553 segment2_dict["provider:network_type"] = "vlan"
554 if self.config.get('multisegment_vlan_range'):
555 vlanID = self._generate_multisegment_vlanID()
556 segment2_dict["provider:segmentation_id"] = vlanID
557 # else
558 # raise vimconn.vimconnConflictException(
559 # "You must provide 'multisegment_vlan_range' at config dict before creating a multisegment network")
560 segment_list.append(segment2_dict)
561 network_dict["segments"] = segment_list
kate721d79b2017-06-24 04:21:38 -0700562
563 ####### VIO Specific Changes #########
564 if self.vim_type == "VIO":
565 if vlan is not None:
566 network_dict["provider:segmentation_id"] = vlan
567 else:
568 if self.config.get('dataplane_net_vlan_range') is None:
569 raise vimconn.vimconnConflictException("You must provide "\
570 "'dataplane_net_vlan_range' in format [start_ID - end_ID]"\
571 "at config value before creating sriov network with vlan tag")
572
garciadeblasebd66722019-01-31 16:01:31 +0000573 network_dict["provider:segmentation_id"] = self._generate_vlanID()
kate721d79b2017-06-24 04:21:38 -0700574
garciadeblasebd66722019-01-31 16:01:31 +0000575 network_dict["shared"] = shared
anwarsff168192019-05-06 11:23:07 +0530576 if self.config.get("disable_network_port_security"):
577 network_dict["port_security_enabled"] = False
garciadeblasebd66722019-01-31 16:01:31 +0000578 new_net = self.neutron.create_network({'network':network_dict})
579 # print new_net
580 # create subnetwork, even if there is no profile
garciadeblas9f8456e2016-09-05 05:02:59 +0200581 if not ip_profile:
582 ip_profile = {}
tierno41a69812018-02-16 14:34:33 +0100583 if not ip_profile.get('subnet_address'):
garciadeblas2299e3b2017-01-26 14:35:55 +0000584 #Fake subnet is required
585 subnet_rand = random.randint(0, 255)
586 ip_profile['subnet_address'] = "192.168.{}.0/24".format(subnet_rand)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000587 if 'ip_version' not in ip_profile:
garciadeblas9f8456e2016-09-05 05:02:59 +0200588 ip_profile['ip_version'] = "IPv4"
garciadeblasebd66722019-01-31 16:01:31 +0000589 subnet = {"name": net_name+"-subnet",
tierno7edb6752016-03-21 17:37:52 +0100590 "network_id": new_net["network"]["id"],
garciadeblas9f8456e2016-09-05 05:02:59 +0200591 "ip_version": 4 if ip_profile['ip_version']=="IPv4" else 6,
592 "cidr": ip_profile['subnet_address']
tierno7edb6752016-03-21 17:37:52 +0100593 }
tiernoa1fb4462017-06-30 12:25:50 +0200594 # Gateway should be set to None if not needed. Otherwise openstack assigns one by default
tierno41a69812018-02-16 14:34:33 +0100595 if ip_profile.get('gateway_address'):
tierno55d234c2018-07-04 18:29:21 +0200596 subnet['gateway_ip'] = ip_profile['gateway_address']
597 else:
598 subnet['gateway_ip'] = None
garciadeblasedca7b32016-09-29 14:01:52 +0000599 if ip_profile.get('dns_address'):
tierno455612d2017-05-30 16:40:10 +0200600 subnet['dns_nameservers'] = ip_profile['dns_address'].split(";")
garciadeblas9f8456e2016-09-05 05:02:59 +0200601 if 'dhcp_enabled' in ip_profile:
tierno41a69812018-02-16 14:34:33 +0100602 subnet['enable_dhcp'] = False if \
603 ip_profile['dhcp_enabled']=="false" or ip_profile['dhcp_enabled']==False else True
604 if ip_profile.get('dhcp_start_address'):
tiernoa1fb4462017-06-30 12:25:50 +0200605 subnet['allocation_pools'] = []
garciadeblas9f8456e2016-09-05 05:02:59 +0200606 subnet['allocation_pools'].append(dict())
607 subnet['allocation_pools'][0]['start'] = ip_profile['dhcp_start_address']
tierno41a69812018-02-16 14:34:33 +0100608 if ip_profile.get('dhcp_count'):
garciadeblas9f8456e2016-09-05 05:02:59 +0200609 #parts = ip_profile['dhcp_start_address'].split('.')
610 #ip_int = (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3])
611 ip_int = int(netaddr.IPAddress(ip_profile['dhcp_start_address']))
garciadeblas21d795b2016-09-29 17:31:46 +0200612 ip_int += ip_profile['dhcp_count'] - 1
garciadeblas9f8456e2016-09-05 05:02:59 +0200613 ip_str = str(netaddr.IPAddress(ip_int))
614 subnet['allocation_pools'][0]['end'] = ip_str
garciadeblasedca7b32016-09-29 14:01:52 +0000615 #self.logger.debug(">>>>>>>>>>>>>>>>>> Subnet: %s", str(subnet))
tierno7edb6752016-03-21 17:37:52 +0100616 self.neutron.create_subnet({"subnet": subnet} )
garciadeblasebd66722019-01-31 16:01:31 +0000617
618 if net_type == "data" and self.config.get('multisegment_support'):
619 if self.config.get('l2gw_support'):
620 l2gw_list = self.neutron.list_l2_gateways().get("l2_gateways", ())
621 for l2gw in l2gw_list:
622 l2gw_conn = {}
623 l2gw_conn["l2_gateway_id"] = l2gw["id"]
624 l2gw_conn["network_id"] = new_net["network"]["id"]
625 l2gw_conn["segmentation_id"] = str(vlanID)
626 new_l2gw_conn = self.neutron.create_l2_gateway_connection({"l2_gateway_connection": l2gw_conn})
627 created_items["l2gwconn:" + str(new_l2gw_conn["l2_gateway_connection"]["id"])] = True
628 return new_net["network"]["id"], created_items
tierno41a69812018-02-16 14:34:33 +0100629 except Exception as e:
garciadeblasebd66722019-01-31 16:01:31 +0000630 #delete l2gw connections (if any) before deleting the network
631 for k, v in created_items.items():
632 if not v: # skip already deleted
633 continue
634 try:
635 k_item, _, k_id = k.partition(":")
636 if k_item == "l2gwconn":
637 self.neutron.delete_l2_gateway_connection(k_id)
638 except Exception as e2:
639 self.logger.error("Error deleting l2 gateway connection: {}: {}".format(type(e2).__name__, e2))
garciadeblasedca7b32016-09-29 14:01:52 +0000640 if new_net:
641 self.neutron.delete_network(new_net['network']['id'])
tiernoae4a8d12016-07-08 12:30:39 +0200642 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100643
644 def get_network_list(self, filter_dict={}):
645 '''Obtain tenant networks of VIM
646 Filter_dict can be:
647 name: network name
648 id: network uuid
649 shared: boolean
650 tenant_id: tenant
651 admin_state_up: boolean
652 status: 'ACTIVE'
653 Returns the network list of dictionaries
654 '''
tiernoae4a8d12016-07-08 12:30:39 +0200655 self.logger.debug("Getting network from VIM filter: '%s'", str(filter_dict))
tierno7edb6752016-03-21 17:37:52 +0100656 try:
657 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +0100658 filter_dict_os = filter_dict.copy()
659 if self.api_version3 and "tenant_id" in filter_dict_os:
660 filter_dict_os['project_id'] = filter_dict_os.pop('tenant_id') #T ODO check
661 net_dict = self.neutron.list_networks(**filter_dict_os)
tierno00e3df72017-11-29 17:20:13 +0100662 net_list = net_dict["networks"]
tierno7edb6752016-03-21 17:37:52 +0100663 self.__net_os2mano(net_list)
tiernoae4a8d12016-07-08 12:30:39 +0200664 return net_list
tierno8e995ce2016-09-22 08:13:00 +0000665 except (neExceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200666 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100667
tiernoae4a8d12016-07-08 12:30:39 +0200668 def get_network(self, net_id):
669 '''Obtain details of network from VIM
670 Returns the network information from a network id'''
671 self.logger.debug(" Getting tenant network %s from VIM", net_id)
tierno7edb6752016-03-21 17:37:52 +0100672 filter_dict={"id": net_id}
tiernoae4a8d12016-07-08 12:30:39 +0200673 net_list = self.get_network_list(filter_dict)
tierno7edb6752016-03-21 17:37:52 +0100674 if len(net_list)==0:
tiernoae4a8d12016-07-08 12:30:39 +0200675 raise vimconn.vimconnNotFoundException("Network '{}' not found".format(net_id))
tierno7edb6752016-03-21 17:37:52 +0100676 elif len(net_list)>1:
tiernoae4a8d12016-07-08 12:30:39 +0200677 raise vimconn.vimconnConflictException("Found more than one network with this criteria")
tierno7edb6752016-03-21 17:37:52 +0100678 net = net_list[0]
679 subnets=[]
680 for subnet_id in net.get("subnets", () ):
681 try:
682 subnet = self.neutron.show_subnet(subnet_id)
683 except Exception as e:
tiernoae4a8d12016-07-08 12:30:39 +0200684 self.logger.error("osconnector.get_network(): Error getting subnet %s %s" % (net_id, str(e)))
685 subnet = {"id": subnet_id, "fault": str(e)}
tierno7edb6752016-03-21 17:37:52 +0100686 subnets.append(subnet)
687 net["subnets"] = subnets
Pablo Montes Moreno51e553b2017-03-23 16:39:12 +0100688 net["encapsulation"] = net.get('provider:network_type')
Anderson Bravalheri0fb70282018-12-16 19:28:37 +0000689 net["encapsulation_type"] = net.get('provider:network_type')
Pablo Montes Moreno3fbff9b2017-03-08 11:28:15 +0100690 net["segmentation_id"] = net.get('provider:segmentation_id')
Anderson Bravalheri0fb70282018-12-16 19:28:37 +0000691 net["encapsulation_id"] = net.get('provider:segmentation_id')
tiernoae4a8d12016-07-08 12:30:39 +0200692 return net
tierno7edb6752016-03-21 17:37:52 +0100693
garciadeblasebd66722019-01-31 16:01:31 +0000694 def delete_network(self, net_id, created_items=None):
695 """
696 Removes a tenant network from VIM and its associated elements
697 :param net_id: VIM identifier of the network, provided by method new_network
698 :param created_items: dictionary with extra items to be deleted. provided by method new_network
699 Returns the network identifier or raises an exception upon error or when network is not found
700 """
tiernoae4a8d12016-07-08 12:30:39 +0200701 self.logger.debug("Deleting network '%s' from VIM", net_id)
garciadeblasebd66722019-01-31 16:01:31 +0000702 if created_items == None:
703 created_items = {}
tierno7edb6752016-03-21 17:37:52 +0100704 try:
705 self._reload_connection()
garciadeblasebd66722019-01-31 16:01:31 +0000706 #delete l2gw connections (if any) before deleting the network
707 for k, v in created_items.items():
708 if not v: # skip already deleted
709 continue
710 try:
711 k_item, _, k_id = k.partition(":")
712 if k_item == "l2gwconn":
713 self.neutron.delete_l2_gateway_connection(k_id)
714 except Exception as e:
715 self.logger.error("Error deleting l2 gateway connection: {}: {}".format(type(e).__name__, e))
tierno7edb6752016-03-21 17:37:52 +0100716 #delete VM ports attached to this networks before the network
717 ports = self.neutron.list_ports(network_id=net_id)
718 for p in ports['ports']:
719 try:
720 self.neutron.delete_port(p["id"])
721 except Exception as e:
tiernoae4a8d12016-07-08 12:30:39 +0200722 self.logger.error("Error deleting port %s: %s", p["id"], str(e))
tierno7edb6752016-03-21 17:37:52 +0100723 self.neutron.delete_network(net_id)
tiernoae4a8d12016-07-08 12:30:39 +0200724 return net_id
725 except (neExceptions.ConnectionFailed, neExceptions.NetworkNotFoundClient, neExceptions.NeutronException,
tierno8e995ce2016-09-22 08:13:00 +0000726 ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200727 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100728
tiernoae4a8d12016-07-08 12:30:39 +0200729 def refresh_nets_status(self, net_list):
730 '''Get the status of the networks
731 Params: the list of network identifiers
732 Returns a dictionary with:
733 net_id: #VIM id of this network
734 status: #Mandatory. Text with one of:
735 # DELETED (not found at vim)
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100736 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
tiernoae4a8d12016-07-08 12:30:39 +0200737 # OTHER (Vim reported other status not understood)
738 # ERROR (VIM indicates an ERROR status)
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100739 # ACTIVE, INACTIVE, DOWN (admin down),
tiernoae4a8d12016-07-08 12:30:39 +0200740 # BUILD (on building process)
741 #
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100742 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
tiernoae4a8d12016-07-08 12:30:39 +0200743 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
744
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000745 '''
tiernoae4a8d12016-07-08 12:30:39 +0200746 net_dict={}
747 for net_id in net_list:
748 net = {}
749 try:
750 net_vim = self.get_network(net_id)
751 if net_vim['status'] in netStatus2manoFormat:
752 net["status"] = netStatus2manoFormat[ net_vim['status'] ]
753 else:
754 net["status"] = "OTHER"
755 net["error_msg"] = "VIM status reported " + net_vim['status']
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +0000756
tierno8e995ce2016-09-22 08:13:00 +0000757 if net['status'] == "ACTIVE" and not net_vim['admin_state_up']:
tiernoae4a8d12016-07-08 12:30:39 +0200758 net['status'] = 'DOWN'
Anderson Bravalheri0446cd52018-08-17 15:26:19 +0100759
760 net['vim_info'] = self.serialize(net_vim)
761
tiernoae4a8d12016-07-08 12:30:39 +0200762 if net_vim.get('fault'): #TODO
763 net['error_msg'] = str(net_vim['fault'])
764 except vimconn.vimconnNotFoundException as e:
765 self.logger.error("Exception getting net status: %s", str(e))
766 net['status'] = "DELETED"
767 net['error_msg'] = str(e)
768 except vimconn.vimconnException as e:
769 self.logger.error("Exception getting net status: %s", str(e))
770 net['status'] = "VIM_ERROR"
771 net['error_msg'] = str(e)
772 net_dict[net_id] = net
773 return net_dict
774
775 def get_flavor(self, flavor_id):
776 '''Obtain flavor details from the VIM. Returns the flavor dict details'''
777 self.logger.debug("Getting flavor '%s'", flavor_id)
tierno7edb6752016-03-21 17:37:52 +0100778 try:
779 self._reload_connection()
780 flavor = self.nova.flavors.find(id=flavor_id)
781 #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
tiernoae4a8d12016-07-08 12:30:39 +0200782 return flavor.to_dict()
tierno8e995ce2016-09-22 08:13:00 +0000783 except (nvExceptions.NotFound, nvExceptions.ClientException, ksExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200784 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100785
tiernocf157a82017-01-30 14:07:06 +0100786 def get_flavor_id_from_data(self, flavor_dict):
787 """Obtain flavor id that match the flavor description
788 Returns the flavor_id or raises a vimconnNotFoundException
tiernoe26fc7a2017-05-30 14:43:03 +0200789 flavor_dict: contains the required ram, vcpus, disk
790 If 'use_existing_flavors' is set to True at config, the closer flavor that provides same or more ram, vcpus
791 and disk is returned. Otherwise a flavor with exactly same ram, vcpus and disk is returned or a
792 vimconnNotFoundException is raised
tiernocf157a82017-01-30 14:07:06 +0100793 """
tiernoe26fc7a2017-05-30 14:43:03 +0200794 exact_match = False if self.config.get('use_existing_flavors') else True
tiernocf157a82017-01-30 14:07:06 +0100795 try:
796 self._reload_connection()
tiernoe26fc7a2017-05-30 14:43:03 +0200797 flavor_candidate_id = None
798 flavor_candidate_data = (10000, 10000, 10000)
799 flavor_target = (flavor_dict["ram"], flavor_dict["vcpus"], flavor_dict["disk"])
800 # numa=None
anwarsae5f52c2019-04-22 10:35:27 +0530801 extended = flavor_dict.get("extended", {})
802 if extended:
tiernocf157a82017-01-30 14:07:06 +0100803 #TODO
tiernob7aa1bb2019-07-24 15:47:16 +0000804 raise vimconn.vimconnNotFoundException("Flavor with EPA still not implemented")
tiernocf157a82017-01-30 14:07:06 +0100805 # if len(numas) > 1:
806 # raise vimconn.vimconnNotFoundException("Cannot find any flavor with more than one numa")
807 # numa=numas[0]
808 # numas = extended.get("numas")
809 for flavor in self.nova.flavors.list():
810 epa = flavor.get_keys()
811 if epa:
812 continue
tiernoe26fc7a2017-05-30 14:43:03 +0200813 # TODO
814 flavor_data = (flavor.ram, flavor.vcpus, flavor.disk)
815 if flavor_data == flavor_target:
816 return flavor.id
817 elif not exact_match and flavor_target < flavor_data < flavor_candidate_data:
818 flavor_candidate_id = flavor.id
819 flavor_candidate_data = flavor_data
820 if not exact_match and flavor_candidate_id:
821 return flavor_candidate_id
tiernocf157a82017-01-30 14:07:06 +0100822 raise vimconn.vimconnNotFoundException("Cannot find any flavor matching '{}'".format(str(flavor_dict)))
823 except (nvExceptions.NotFound, nvExceptions.ClientException, ksExceptions.ClientException, ConnectionError) as e:
824 self._format_exception(e)
825
anwarsae5f52c2019-04-22 10:35:27 +0530826 def process_resource_quota(self, quota, prefix, extra_specs):
827 """
828 :param prefix:
borsatti8a2dda32019-12-18 15:08:57 +0000829 :param extra_specs:
anwarsae5f52c2019-04-22 10:35:27 +0530830 :return:
831 """
832 if 'limit' in quota:
833 extra_specs["quota:" + prefix + "_limit"] = quota['limit']
834 if 'reserve' in quota:
835 extra_specs["quota:" + prefix + "_reservation"] = quota['reserve']
836 if 'shares' in quota:
837 extra_specs["quota:" + prefix + "_shares_level"] = "custom"
838 extra_specs["quota:" + prefix + "_shares_share"] = quota['shares']
839
tiernoae4a8d12016-07-08 12:30:39 +0200840 def new_flavor(self, flavor_data, change_name_if_used=True):
tierno7edb6752016-03-21 17:37:52 +0100841 '''Adds a tenant flavor to openstack VIM
tiernoae4a8d12016-07-08 12:30:39 +0200842 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 +0100843 Returns the flavor identifier
844 '''
tiernoae4a8d12016-07-08 12:30:39 +0200845 self.logger.debug("Adding flavor '%s'", str(flavor_data))
tierno7edb6752016-03-21 17:37:52 +0100846 retry=0
tiernoae4a8d12016-07-08 12:30:39 +0200847 max_retries=3
tierno7edb6752016-03-21 17:37:52 +0100848 name_suffix = 0
anwarsc76a3ee2018-10-04 14:05:32 +0530849 try:
850 name=flavor_data['name']
851 while retry<max_retries:
852 retry+=1
853 try:
854 self._reload_connection()
855 if change_name_if_used:
856 #get used names
857 fl_names=[]
858 fl=self.nova.flavors.list()
859 for f in fl:
860 fl_names.append(f.name)
861 while name in fl_names:
862 name_suffix += 1
863 name = flavor_data['name']+"-" + str(name_suffix)
kate721d79b2017-06-24 04:21:38 -0700864
anwarsc76a3ee2018-10-04 14:05:32 +0530865 ram = flavor_data.get('ram',64)
866 vcpus = flavor_data.get('vcpus',1)
anwarsae5f52c2019-04-22 10:35:27 +0530867 extra_specs={}
tierno7edb6752016-03-21 17:37:52 +0100868
anwarsc76a3ee2018-10-04 14:05:32 +0530869 extended = flavor_data.get("extended")
870 if extended:
871 numas=extended.get("numas")
872 if numas:
873 numa_nodes = len(numas)
874 if numa_nodes > 1:
875 return -1, "Can not add flavor with more than one numa"
anwarsae5f52c2019-04-22 10:35:27 +0530876 extra_specs["hw:numa_nodes"] = str(numa_nodes)
877 extra_specs["hw:mem_page_size"] = "large"
878 extra_specs["hw:cpu_policy"] = "dedicated"
879 extra_specs["hw:numa_mempolicy"] = "strict"
anwarsc76a3ee2018-10-04 14:05:32 +0530880 if self.vim_type == "VIO":
anwarsae5f52c2019-04-22 10:35:27 +0530881 extra_specs["vmware:extra_config"] = '{"numa.nodeAffinity":"0"}'
882 extra_specs["vmware:latency_sensitivity_level"] = "high"
anwarsc76a3ee2018-10-04 14:05:32 +0530883 for numa in numas:
884 #overwrite ram and vcpus
885 #check if key 'memory' is present in numa else use ram value at flavor
886 if 'memory' in numa:
887 ram = numa['memory']*1024
888 #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 +0200889 extra_specs["hw:cpu_sockets"] = 1
anwarsc76a3ee2018-10-04 14:05:32 +0530890 if 'paired-threads' in numa:
891 vcpus = numa['paired-threads']*2
892 #cpu_thread_policy "require" implies that the compute node must have an STM architecture
anwarsae5f52c2019-04-22 10:35:27 +0530893 extra_specs["hw:cpu_thread_policy"] = "require"
894 extra_specs["hw:cpu_policy"] = "dedicated"
anwarsc76a3ee2018-10-04 14:05:32 +0530895 elif 'cores' in numa:
896 vcpus = numa['cores']
897 # 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 +0530898 extra_specs["hw:cpu_thread_policy"] = "isolate"
899 extra_specs["hw:cpu_policy"] = "dedicated"
anwarsc76a3ee2018-10-04 14:05:32 +0530900 elif 'threads' in numa:
901 vcpus = numa['threads']
902 # cpu_thread_policy "prefer" implies that the host may or may not have an SMT architecture
anwarsae5f52c2019-04-22 10:35:27 +0530903 extra_specs["hw:cpu_thread_policy"] = "prefer"
904 extra_specs["hw:cpu_policy"] = "dedicated"
anwarsc76a3ee2018-10-04 14:05:32 +0530905 # for interface in numa.get("interfaces",() ):
906 # if interface["dedicated"]=="yes":
907 # raise vimconn.vimconnException("Passthrough interfaces are not supported for the openstack connector", http_code=vimconn.HTTP_Service_Unavailable)
908 # #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 +0530909 elif extended.get("cpu-quota"):
910 self.process_resource_quota(extended.get("cpu-quota"), "cpu", extra_specs)
911 if extended.get("mem-quota"):
912 self.process_resource_quota(extended.get("mem-quota"), "memory", extra_specs)
913 if extended.get("vif-quota"):
914 self.process_resource_quota(extended.get("vif-quota"), "vif", extra_specs)
915 if extended.get("disk-io-quota"):
916 self.process_resource_quota(extended.get("disk-io-quota"), "disk_io", extra_specs)
anwarsc76a3ee2018-10-04 14:05:32 +0530917 #create flavor
918 new_flavor=self.nova.flavors.create(name,
919 ram,
920 vcpus,
921 flavor_data.get('disk',0),
922 is_public=flavor_data.get('is_public', True)
923 )
924 #add metadata
anwarsae5f52c2019-04-22 10:35:27 +0530925 if extra_specs:
926 new_flavor.set_keys(extra_specs)
anwarsc76a3ee2018-10-04 14:05:32 +0530927 return new_flavor.id
928 except nvExceptions.Conflict as e:
929 if change_name_if_used and retry < max_retries:
930 continue
931 self._format_exception(e)
932 #except nvExceptions.BadRequest as e:
933 except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError, KeyError) as e:
934 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100935
tiernoae4a8d12016-07-08 12:30:39 +0200936 def delete_flavor(self,flavor_id):
937 '''Deletes a tenant flavor from openstack VIM. Returns the old flavor_id
tierno7edb6752016-03-21 17:37:52 +0100938 '''
tiernoae4a8d12016-07-08 12:30:39 +0200939 try:
940 self._reload_connection()
941 self.nova.flavors.delete(flavor_id)
942 return flavor_id
943 #except nvExceptions.BadRequest as e:
tierno8e995ce2016-09-22 08:13:00 +0000944 except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +0200945 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +0100946
tiernoae4a8d12016-07-08 12:30:39 +0200947 def new_image(self,image_dict):
tierno7edb6752016-03-21 17:37:52 +0100948 '''
tiernoae4a8d12016-07-08 12:30:39 +0200949 Adds a tenant image to VIM. imge_dict is a dictionary with:
950 name: name
951 disk_format: qcow2, vhd, vmdk, raw (by default), ...
952 location: path or URI
953 public: "yes" or "no"
954 metadata: metadata of the image
955 Returns the image_id
tierno7edb6752016-03-21 17:37:52 +0100956 '''
tiernoae4a8d12016-07-08 12:30:39 +0200957 retry=0
958 max_retries=3
959 while retry<max_retries:
tierno7edb6752016-03-21 17:37:52 +0100960 retry+=1
961 try:
962 self._reload_connection()
963 #determine format http://docs.openstack.org/developer/glance/formats.html
964 if "disk_format" in image_dict:
965 disk_format=image_dict["disk_format"]
garciadeblas14480452017-01-10 13:08:07 +0100966 else: #autodiscover based on extension
tierno1beea862018-07-11 15:47:37 +0200967 if image_dict['location'].endswith(".qcow2"):
tierno7edb6752016-03-21 17:37:52 +0100968 disk_format="qcow2"
tierno1beea862018-07-11 15:47:37 +0200969 elif image_dict['location'].endswith(".vhd"):
tierno7edb6752016-03-21 17:37:52 +0100970 disk_format="vhd"
tierno1beea862018-07-11 15:47:37 +0200971 elif image_dict['location'].endswith(".vmdk"):
tierno7edb6752016-03-21 17:37:52 +0100972 disk_format="vmdk"
tierno1beea862018-07-11 15:47:37 +0200973 elif image_dict['location'].endswith(".vdi"):
tierno7edb6752016-03-21 17:37:52 +0100974 disk_format="vdi"
tierno1beea862018-07-11 15:47:37 +0200975 elif image_dict['location'].endswith(".iso"):
tierno7edb6752016-03-21 17:37:52 +0100976 disk_format="iso"
tierno1beea862018-07-11 15:47:37 +0200977 elif image_dict['location'].endswith(".aki"):
tierno7edb6752016-03-21 17:37:52 +0100978 disk_format="aki"
tierno1beea862018-07-11 15:47:37 +0200979 elif image_dict['location'].endswith(".ari"):
tierno7edb6752016-03-21 17:37:52 +0100980 disk_format="ari"
tierno1beea862018-07-11 15:47:37 +0200981 elif image_dict['location'].endswith(".ami"):
tierno7edb6752016-03-21 17:37:52 +0100982 disk_format="ami"
983 else:
984 disk_format="raw"
tiernoae4a8d12016-07-08 12:30:39 +0200985 self.logger.debug("new_image: '%s' loading from '%s'", image_dict['name'], image_dict['location'])
shashankjain3c83a212018-10-04 13:05:46 +0530986 if self.vim_type == "VIO":
987 container_format = "bare"
988 if 'container_format' in image_dict:
989 container_format = image_dict['container_format']
990 new_image = self.glance.images.create(name=image_dict['name'], container_format=container_format,
991 disk_format=disk_format)
992 else:
993 new_image = self.glance.images.create(name=image_dict['name'])
tierno1beea862018-07-11 15:47:37 +0200994 if image_dict['location'].startswith("http"):
995 # TODO there is not a method to direct download. It must be downloaded locally with requests
996 raise vimconn.vimconnNotImplemented("Cannot create image from URL")
tierno7edb6752016-03-21 17:37:52 +0100997 else: #local path
998 with open(image_dict['location']) as fimage:
tierno1beea862018-07-11 15:47:37 +0200999 self.glance.images.upload(new_image.id, fimage)
1000 #new_image = self.glancev1.images.create(name=image_dict['name'], is_public=image_dict.get('public',"yes")=="yes",
1001 # container_format="bare", data=fimage, disk_format=disk_format)
tierno7edb6752016-03-21 17:37:52 +01001002 metadata_to_load = image_dict.get('metadata')
shashankjain3c83a212018-10-04 13:05:46 +05301003 # TODO location is a reserved word for current openstack versions. fixed for VIO please check for openstack
1004 if self.vim_type == "VIO":
1005 metadata_to_load['upload_location'] = image_dict['location']
1006 else:
1007 metadata_to_load['location'] = image_dict['location']
tierno1beea862018-07-11 15:47:37 +02001008 self.glance.images.update(new_image.id, **metadata_to_load)
tiernoae4a8d12016-07-08 12:30:39 +02001009 return new_image.id
1010 except (nvExceptions.Conflict, ksExceptions.ClientException, nvExceptions.ClientException) as e:
1011 self._format_exception(e)
tierno8e995ce2016-09-22 08:13:00 +00001012 except (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001013 if retry==max_retries:
1014 continue
1015 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001016 except IOError as e: #can not open the file
tiernoae4a8d12016-07-08 12:30:39 +02001017 raise vimconn.vimconnConnectionException(type(e).__name__ + ": " + str(e)+ " for " + image_dict['location'],
1018 http_code=vimconn.HTTP_Bad_Request)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001019
tiernoae4a8d12016-07-08 12:30:39 +02001020 def delete_image(self, image_id):
1021 '''Deletes a tenant image from openstack VIM. Returns the old id
tierno7edb6752016-03-21 17:37:52 +01001022 '''
tiernoae4a8d12016-07-08 12:30:39 +02001023 try:
1024 self._reload_connection()
tierno1beea862018-07-11 15:47:37 +02001025 self.glance.images.delete(image_id)
tiernoae4a8d12016-07-08 12:30:39 +02001026 return image_id
shashankjain3c83a212018-10-04 13:05:46 +05301027 except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, gl1Exceptions.HTTPNotFound, ConnectionError) as e: #TODO remove
tiernoae4a8d12016-07-08 12:30:39 +02001028 self._format_exception(e)
1029
1030 def get_image_id_from_path(self, path):
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001031 '''Get the image id from image path in the VIM database. Returns the image_id'''
tiernoae4a8d12016-07-08 12:30:39 +02001032 try:
1033 self._reload_connection()
tierno1beea862018-07-11 15:47:37 +02001034 images = self.glance.images.list()
tiernoae4a8d12016-07-08 12:30:39 +02001035 for image in images:
1036 if image.metadata.get("location")==path:
1037 return image.id
1038 raise vimconn.vimconnNotFoundException("image with location '{}' not found".format( path))
tierno8e995ce2016-09-22 08:13:00 +00001039 except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001040 self._format_exception(e)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001041
garciadeblasb69fa9f2016-09-28 12:04:10 +02001042 def get_image_list(self, filter_dict={}):
1043 '''Obtain tenant images from VIM
1044 Filter_dict can be:
1045 id: image id
1046 name: image name
1047 checksum: image checksum
1048 Returns the image list of dictionaries:
1049 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1050 List can be empty
1051 '''
1052 self.logger.debug("Getting image list from VIM filter: '%s'", str(filter_dict))
1053 try:
1054 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +01001055 filter_dict_os = filter_dict.copy()
garciadeblasb69fa9f2016-09-28 12:04:10 +02001056 #First we filter by the available filter fields: name, id. The others are removed.
tierno1beea862018-07-11 15:47:37 +02001057 image_list = self.glance.images.list()
garciadeblasb69fa9f2016-09-28 12:04:10 +02001058 filtered_list = []
1059 for image in image_list:
tierno3cb8dc32017-10-24 18:13:19 +02001060 try:
tierno1beea862018-07-11 15:47:37 +02001061 if filter_dict.get("name") and image["name"] != filter_dict["name"]:
1062 continue
1063 if filter_dict.get("id") and image["id"] != filter_dict["id"]:
1064 continue
1065 if filter_dict.get("checksum") and image["checksum"] != filter_dict["checksum"]:
1066 continue
1067
1068 filtered_list.append(image.copy())
tierno3cb8dc32017-10-24 18:13:19 +02001069 except gl1Exceptions.HTTPNotFound:
1070 pass
garciadeblasb69fa9f2016-09-28 12:04:10 +02001071 return filtered_list
1072 except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e:
1073 self._format_exception(e)
1074
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001075 def __wait_for_vm(self, vm_id, status):
1076 """wait until vm is in the desired status and return True.
1077 If the VM gets in ERROR status, return false.
1078 If the timeout is reached generate an exception"""
1079 elapsed_time = 0
1080 while elapsed_time < server_timeout:
1081 vm_status = self.nova.servers.get(vm_id).status
1082 if vm_status == status:
1083 return True
1084 if vm_status == 'ERROR':
1085 return False
tierno1df468d2018-07-06 14:25:16 +02001086 time.sleep(5)
1087 elapsed_time += 5
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001088
1089 # if we exceeded the timeout rollback
1090 if elapsed_time >= server_timeout:
1091 raise vimconn.vimconnException('Timeout waiting for instance ' + vm_id + ' to get ' + status,
1092 http_code=vimconn.HTTP_Request_Timeout)
1093
mirabal29356312017-07-27 12:21:22 +02001094 def _get_openstack_availablity_zones(self):
1095 """
1096 Get from openstack availability zones available
1097 :return:
1098 """
1099 try:
1100 openstack_availability_zone = self.nova.availability_zones.list()
1101 openstack_availability_zone = [str(zone.zoneName) for zone in openstack_availability_zone
1102 if zone.zoneName != 'internal']
1103 return openstack_availability_zone
1104 except Exception as e:
1105 return None
1106
1107 def _set_availablity_zones(self):
1108 """
1109 Set vim availablity zone
1110 :return:
1111 """
1112
1113 if 'availability_zone' in self.config:
1114 vim_availability_zones = self.config.get('availability_zone')
1115 if isinstance(vim_availability_zones, str):
1116 self.availability_zone = [vim_availability_zones]
1117 elif isinstance(vim_availability_zones, list):
1118 self.availability_zone = vim_availability_zones
1119 else:
1120 self.availability_zone = self._get_openstack_availablity_zones()
1121
tierno5a3273c2017-08-29 11:43:46 +02001122 def _get_vm_availability_zone(self, availability_zone_index, availability_zone_list):
mirabal29356312017-07-27 12:21:22 +02001123 """
tierno5a3273c2017-08-29 11:43:46 +02001124 Return thge availability zone to be used by the created VM.
1125 :return: The VIM availability zone to be used or None
mirabal29356312017-07-27 12:21:22 +02001126 """
tierno5a3273c2017-08-29 11:43:46 +02001127 if availability_zone_index is None:
1128 if not self.config.get('availability_zone'):
1129 return None
1130 elif isinstance(self.config.get('availability_zone'), str):
1131 return self.config['availability_zone']
1132 else:
1133 # TODO consider using a different parameter at config for default AV and AV list match
1134 return self.config['availability_zone'][0]
mirabal29356312017-07-27 12:21:22 +02001135
tierno5a3273c2017-08-29 11:43:46 +02001136 vim_availability_zones = self.availability_zone
1137 # check if VIM offer enough availability zones describe in the VNFD
1138 if vim_availability_zones and len(availability_zone_list) <= len(vim_availability_zones):
1139 # check if all the names of NFV AV match VIM AV names
1140 match_by_index = False
1141 for av in availability_zone_list:
1142 if av not in vim_availability_zones:
1143 match_by_index = True
1144 break
1145 if match_by_index:
1146 return vim_availability_zones[availability_zone_index]
1147 else:
1148 return availability_zone_list[availability_zone_index]
mirabal29356312017-07-27 12:21:22 +02001149 else:
tierno5a3273c2017-08-29 11:43:46 +02001150 raise vimconn.vimconnConflictException("No enough availability zones at VIM for this deployment")
mirabal29356312017-07-27 12:21:22 +02001151
tierno5a3273c2017-08-29 11:43:46 +02001152 def new_vminstance(self, name, description, start, image_id, flavor_id, net_list, cloud_config=None, disk_list=None,
1153 availability_zone_index=None, availability_zone_list=None):
tierno98e909c2017-10-14 13:27:03 +02001154 """Adds a VM instance to VIM
tierno7edb6752016-03-21 17:37:52 +01001155 Params:
1156 start: indicates if VM must start or boot in pause mode. Ignored
1157 image_id,flavor_id: iamge and flavor uuid
1158 net_list: list of interfaces, each one is a dictionary with:
1159 name:
1160 net_id: network uuid to connect
1161 vpci: virtual vcpi to assign, ignored because openstack lack #TODO
1162 model: interface model, ignored #TODO
1163 mac_address: used for SR-IOV ifaces #TODO for other types
1164 use: 'data', 'bridge', 'mgmt'
tierno66eba6e2017-11-10 17:09:18 +01001165 type: 'virtual', 'PCI-PASSTHROUGH'('PF'), 'SR-IOV'('VF'), 'VFnotShared'
tierno7edb6752016-03-21 17:37:52 +01001166 vim_id: filled/added by this function
ahmadsaf853d452016-12-22 11:33:47 +05001167 floating_ip: True/False (or it can be None)
tierno41a69812018-02-16 14:34:33 +01001168 'cloud_config': (optional) dictionary with:
1169 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
1170 'users': (optional) list of users to be inserted, each item is a dict with:
1171 'name': (mandatory) user name,
1172 'key-pairs': (optional) list of strings with the public key to be inserted to the user
1173 'user-data': (optional) string is a text script to be passed directly to cloud-init
1174 'config-files': (optional). List of files to be transferred. Each item is a dict with:
1175 'dest': (mandatory) string with the destination absolute path
1176 'encoding': (optional, by default text). Can be one of:
1177 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
1178 'content' (mandatory): string with the content of the file
1179 'permissions': (optional) string with file permissions, typically octal notation '0644'
1180 'owner': (optional) file owner, string with the format 'owner:group'
1181 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk)
mirabal29356312017-07-27 12:21:22 +02001182 'disk_list': (optional) list with additional disks to the VM. Each item is a dict with:
1183 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted
1184 'size': (mandatory) string with the size of the disk in GB
tierno1df468d2018-07-06 14:25:16 +02001185 'vim_id' (optional) should use this existing volume id
tierno5a3273c2017-08-29 11:43:46 +02001186 availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV required
1187 availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if
1188 availability_zone_index is None
tierno7edb6752016-03-21 17:37:52 +01001189 #TODO ip, security groups
tierno98e909c2017-10-14 13:27:03 +02001190 Returns a tuple with the instance identifier and created_items or raises an exception on error
1191 created_items can be None or a dictionary where this method can include key-values that will be passed to
1192 the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc.
1193 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
1194 as not present.
1195 """
tiernofa51c202017-01-27 14:58:17 +01001196 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 +01001197 try:
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001198 server = None
tierno98e909c2017-10-14 13:27:03 +02001199 created_items = {}
tiernob0b9dab2017-10-14 14:25:20 +02001200 # metadata = {}
tierno98e909c2017-10-14 13:27:03 +02001201 net_list_vim = []
1202 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 +02001203 no_secured_ports = [] # List of port-is with port-security disabled
tierno7edb6752016-03-21 17:37:52 +01001204 self._reload_connection()
tiernob0b9dab2017-10-14 14:25:20 +02001205 # metadata_vpci = {} # For a specific neutron plugin
tiernob84cbdc2017-07-07 14:30:30 +02001206 block_device_mapping = None
tiernoa05b65a2019-02-01 12:30:27 +00001207
tierno7edb6752016-03-21 17:37:52 +01001208 for net in net_list:
tierno98e909c2017-10-14 13:27:03 +02001209 if not net.get("net_id"): # skip non connected iface
tierno7edb6752016-03-21 17:37:52 +01001210 continue
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001211
tiernoa05b65a2019-02-01 12:30:27 +00001212 port_dict = {
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001213 "network_id": net["net_id"],
1214 "name": net.get("name"),
1215 "admin_state_up": True
1216 }
tiernoa05b65a2019-02-01 12:30:27 +00001217 if self.config.get("security_groups") and net.get("port_security") is not False and \
1218 not self.config.get("no_port_security_extension"):
1219 if not self.security_groups_id:
1220 self._get_ids_from_name()
1221 port_dict["security_groups"] = self.security_groups_id
1222
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001223 if net["type"]=="virtual":
tiernob0b9dab2017-10-14 14:25:20 +02001224 pass
1225 # if "vpci" in net:
1226 # metadata_vpci[ net["net_id"] ] = [[ net["vpci"], "" ]]
tierno66eba6e2017-11-10 17:09:18 +01001227 elif net["type"] == "VF" or net["type"] == "SR-IOV": # for VF
tiernob0b9dab2017-10-14 14:25:20 +02001228 # if "vpci" in net:
1229 # if "VF" not in metadata_vpci:
1230 # metadata_vpci["VF"]=[]
1231 # metadata_vpci["VF"].append([ net["vpci"], "" ])
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001232 port_dict["binding:vnic_type"]="direct"
tiernob0b9dab2017-10-14 14:25:20 +02001233 # VIO specific Changes
kate721d79b2017-06-24 04:21:38 -07001234 if self.vim_type == "VIO":
tiernob0b9dab2017-10-14 14:25:20 +02001235 # Need to create port with port_security_enabled = False and no-security-groups
kate721d79b2017-06-24 04:21:38 -07001236 port_dict["port_security_enabled"]=False
1237 port_dict["provider_security_groups"]=[]
1238 port_dict["security_groups"]=[]
tierno66eba6e2017-11-10 17:09:18 +01001239 else: # For PT PCI-PASSTHROUGH
tiernob0b9dab2017-10-14 14:25:20 +02001240 # VIO specific Changes
1241 # Current VIO release does not support port with type 'direct-physical'
1242 # So no need to create virtual port in case of PCI-device.
1243 # Will update port_dict code when support gets added in next VIO release
kate721d79b2017-06-24 04:21:38 -07001244 if self.vim_type == "VIO":
tiernob0b9dab2017-10-14 14:25:20 +02001245 raise vimconn.vimconnNotSupportedException(
1246 "Current VIO release does not support full passthrough (PT)")
1247 # if "vpci" in net:
1248 # if "PF" not in metadata_vpci:
1249 # metadata_vpci["PF"]=[]
1250 # metadata_vpci["PF"].append([ net["vpci"], "" ])
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001251 port_dict["binding:vnic_type"]="direct-physical"
1252 if not port_dict["name"]:
1253 port_dict["name"]=name
1254 if net.get("mac_address"):
1255 port_dict["mac_address"]=net["mac_address"]
tierno41a69812018-02-16 14:34:33 +01001256 if net.get("ip_address"):
1257 port_dict["fixed_ips"] = [{'ip_address': net["ip_address"]}]
1258 # TODO add 'subnet_id': <subnet_id>
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001259 new_port = self.neutron.create_port({"port": port_dict })
tierno00e3df72017-11-29 17:20:13 +01001260 created_items["port:" + str(new_port["port"]["id"])] = True
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001261 net["mac_adress"] = new_port["port"]["mac_address"]
1262 net["vim_id"] = new_port["port"]["id"]
tiernob84cbdc2017-07-07 14:30:30 +02001263 # if try to use a network without subnetwork, it will return a emtpy list
1264 fixed_ips = new_port["port"].get("fixed_ips")
1265 if fixed_ips:
1266 net["ip"] = fixed_ips[0].get("ip_address")
1267 else:
1268 net["ip"] = None
montesmoreno994a29d2017-08-22 11:23:06 +02001269
1270 port = {"port-id": new_port["port"]["id"]}
1271 if float(self.nova.api_version.get_string()) >= 2.32:
1272 port["tag"] = new_port["port"]["name"]
1273 net_list_vim.append(port)
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001274
ahmadsaf853d452016-12-22 11:33:47 +05001275 if net.get('floating_ip', False):
tiernof8383b82017-01-18 15:49:48 +01001276 net['exit_on_floating_ip_error'] = True
ahmadsaf853d452016-12-22 11:33:47 +05001277 external_network.append(net)
tiernof8383b82017-01-18 15:49:48 +01001278 elif net['use'] == 'mgmt' and self.config.get('use_floating_ip'):
1279 net['exit_on_floating_ip_error'] = False
1280 external_network.append(net)
tierno326fd5e2018-02-22 11:58:59 +01001281 net['floating_ip'] = self.config.get('use_floating_ip')
tiernof8383b82017-01-18 15:49:48 +01001282
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001283 # If port security is disabled when the port has not yet been attached to the VM, then all vm traffic is dropped.
1284 # As a workaround we wait until the VM is active and then disable the port-security
tierno4d1ce222018-04-06 10:41:06 +02001285 if net.get("port_security") == False and not self.config.get("no_port_security_extension"):
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001286 no_secured_ports.append(new_port["port"]["id"])
1287
tiernob0b9dab2017-10-14 14:25:20 +02001288 # if metadata_vpci:
1289 # metadata = {"pci_assignement": json.dumps(metadata_vpci)}
1290 # if len(metadata["pci_assignement"]) >255:
1291 # #limit the metadata size
1292 # #metadata["pci_assignement"] = metadata["pci_assignement"][0:255]
1293 # self.logger.warn("Metadata deleted since it exceeds the expected length (255) ")
1294 # metadata = {}
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001295
tiernob0b9dab2017-10-14 14:25:20 +02001296 self.logger.debug("name '%s' image_id '%s'flavor_id '%s' net_list_vim '%s' description '%s'",
1297 name, image_id, flavor_id, str(net_list_vim), description)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001298
tierno98e909c2017-10-14 13:27:03 +02001299 # cloud config
tierno0a1437e2017-10-02 00:17:43 +02001300 config_drive, userdata = self._create_user_data(cloud_config)
montesmoreno0c8def02016-12-22 12:16:23 +00001301
tierno98e909c2017-10-14 13:27:03 +02001302 # Create additional volumes in case these are present in disk_list
montesmoreno0c8def02016-12-22 12:16:23 +00001303 base_disk_index = ord('b')
tierno1df468d2018-07-06 14:25:16 +02001304 if disk_list:
tiernob84cbdc2017-07-07 14:30:30 +02001305 block_device_mapping = {}
montesmoreno0c8def02016-12-22 12:16:23 +00001306 for disk in disk_list:
tierno1df468d2018-07-06 14:25:16 +02001307 if disk.get('vim_id'):
1308 block_device_mapping['_vd' + chr(base_disk_index)] = disk['vim_id']
montesmoreno0c8def02016-12-22 12:16:23 +00001309 else:
tierno1df468d2018-07-06 14:25:16 +02001310 if 'image_id' in disk:
1311 volume = self.cinder.volumes.create(size=disk['size'], name=name + '_vd' +
1312 chr(base_disk_index), imageRef=disk['image_id'])
1313 else:
1314 volume = self.cinder.volumes.create(size=disk['size'], name=name + '_vd' +
1315 chr(base_disk_index))
1316 created_items["volume:" + str(volume.id)] = True
1317 block_device_mapping['_vd' + chr(base_disk_index)] = volume.id
montesmoreno0c8def02016-12-22 12:16:23 +00001318 base_disk_index += 1
1319
tierno1df468d2018-07-06 14:25:16 +02001320 # Wait until created volumes are with status available
montesmoreno0c8def02016-12-22 12:16:23 +00001321 elapsed_time = 0
tierno1df468d2018-07-06 14:25:16 +02001322 while elapsed_time < volume_timeout:
1323 for created_item in created_items:
1324 v, _, volume_id = created_item.partition(":")
1325 if v == 'volume':
1326 if self.cinder.volumes.get(volume_id).status != 'available':
1327 break
1328 else: # all ready: break from while
1329 break
1330 time.sleep(5)
1331 elapsed_time += 5
tiernob0b9dab2017-10-14 14:25:20 +02001332 # If we exceeded the timeout rollback
montesmoreno0c8def02016-12-22 12:16:23 +00001333 if elapsed_time >= volume_timeout:
montesmoreno0c8def02016-12-22 12:16:23 +00001334 raise vimconn.vimconnException('Timeout creating volumes for instance ' + name,
1335 http_code=vimconn.HTTP_Request_Timeout)
mirabal29356312017-07-27 12:21:22 +02001336 # get availability Zone
tierno5a3273c2017-08-29 11:43:46 +02001337 vm_av_zone = self._get_vm_availability_zone(availability_zone_index, availability_zone_list)
montesmoreno0c8def02016-12-22 12:16:23 +00001338
tiernob0b9dab2017-10-14 14:25:20 +02001339 self.logger.debug("nova.servers.create({}, {}, {}, nics={}, security_groups={}, "
mirabal29356312017-07-27 12:21:22 +02001340 "availability_zone={}, key_name={}, userdata={}, config_drive={}, "
tiernob0b9dab2017-10-14 14:25:20 +02001341 "block_device_mapping={})".format(name, image_id, flavor_id, net_list_vim,
tiernoa05b65a2019-02-01 12:30:27 +00001342 self.config.get("security_groups"), vm_av_zone,
1343 self.config.get('keypair'), userdata, config_drive,
1344 block_device_mapping))
tiernob0b9dab2017-10-14 14:25:20 +02001345 server = self.nova.servers.create(name, image_id, flavor_id, nics=net_list_vim,
tiernoa05b65a2019-02-01 12:30:27 +00001346 security_groups=self.config.get("security_groups"),
1347 # TODO remove security_groups in future versions. Already at neutron port
mirabal29356312017-07-27 12:21:22 +02001348 availability_zone=vm_av_zone,
montesmoreno0c8def02016-12-22 12:16:23 +00001349 key_name=self.config.get('keypair'),
1350 userdata=userdata,
tiernob84cbdc2017-07-07 14:30:30 +02001351 config_drive=config_drive,
1352 block_device_mapping=block_device_mapping
montesmoreno0c8def02016-12-22 12:16:23 +00001353 ) # , description=description)
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001354
tierno326fd5e2018-02-22 11:58:59 +01001355 vm_start_time = time.time()
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001356 # Previously mentioned workaround to wait until the VM is active and then disable the port-security
1357 if no_secured_ports:
1358 self.__wait_for_vm(server.id, 'ACTIVE')
1359
1360 for port_id in no_secured_ports:
1361 try:
tierno4d1ce222018-04-06 10:41:06 +02001362 self.neutron.update_port(port_id,
1363 {"port": {"port_security_enabled": False, "security_groups": None}})
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001364 except Exception as e:
tierno4d1ce222018-04-06 10:41:06 +02001365 raise vimconn.vimconnException("It was not possible to disable port security for port {}".format(
1366 port_id))
tierno98e909c2017-10-14 13:27:03 +02001367 # print "DONE :-)", server
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001368
tierno4d1ce222018-04-06 10:41:06 +02001369 # pool_id = None
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001370 if external_network:
tierno98e909c2017-10-14 13:27:03 +02001371 floating_ips = self.neutron.list_floatingips().get("floatingips", ())
ahmadsaf853d452016-12-22 11:33:47 +05001372 for floating_network in external_network:
tiernof8383b82017-01-18 15:49:48 +01001373 try:
tiernof8383b82017-01-18 15:49:48 +01001374 assigned = False
tierno98e909c2017-10-14 13:27:03 +02001375 while not assigned:
tiernof8383b82017-01-18 15:49:48 +01001376 if floating_ips:
1377 ip = floating_ips.pop(0)
tierno326fd5e2018-02-22 11:58:59 +01001378 if ip.get("port_id", False) or ip.get('tenant_id') != server.tenant_id:
1379 continue
1380 if isinstance(floating_network['floating_ip'], str):
1381 if ip.get("floating_network_id") != floating_network['floating_ip']:
1382 continue
tierno7d782ef2019-10-04 12:56:31 +00001383 free_floating_ip = ip["id"]
tiernof8383b82017-01-18 15:49:48 +01001384 else:
tiernocb3cca22018-05-31 15:08:52 +02001385 if isinstance(floating_network['floating_ip'], str) and \
1386 floating_network['floating_ip'].lower() != "true":
tierno326fd5e2018-02-22 11:58:59 +01001387 pool_id = floating_network['floating_ip']
1388 else:
tierno4d1ce222018-04-06 10:41:06 +02001389 # Find the external network
tierno326fd5e2018-02-22 11:58:59 +01001390 external_nets = list()
1391 for net in self.neutron.list_networks()['networks']:
1392 if net['router:external']:
1393 external_nets.append(net)
tiernof8383b82017-01-18 15:49:48 +01001394
tierno326fd5e2018-02-22 11:58:59 +01001395 if len(external_nets) == 0:
1396 raise vimconn.vimconnException("Cannot create floating_ip automatically since no external "
1397 "network is present",
1398 http_code=vimconn.HTTP_Conflict)
1399 if len(external_nets) > 1:
1400 raise vimconn.vimconnException("Cannot create floating_ip automatically since multiple "
1401 "external networks are present",
1402 http_code=vimconn.HTTP_Conflict)
tiernof8383b82017-01-18 15:49:48 +01001403
tierno326fd5e2018-02-22 11:58:59 +01001404 pool_id = external_nets[0].get('id')
tiernof8383b82017-01-18 15:49:48 +01001405 param = {'floatingip': {'floating_network_id': pool_id, 'tenant_id': server.tenant_id}}
ahmadsaf853d452016-12-22 11:33:47 +05001406 try:
tierno4d1ce222018-04-06 10:41:06 +02001407 # self.logger.debug("Creating floating IP")
tiernof8383b82017-01-18 15:49:48 +01001408 new_floating_ip = self.neutron.create_floatingip(param)
tierno7d782ef2019-10-04 12:56:31 +00001409 free_floating_ip = new_floating_ip['floatingip']['id']
ahmadsaf853d452016-12-22 11:33:47 +05001410 except Exception as e:
tierno326fd5e2018-02-22 11:58:59 +01001411 raise vimconn.vimconnException(type(e).__name__ + ": Cannot create new floating_ip " +
1412 str(e), http_code=vimconn.HTTP_Conflict)
1413
tierno326fd5e2018-02-22 11:58:59 +01001414 while not assigned:
1415 try:
tierno7d782ef2019-10-04 12:56:31 +00001416 # the vim_id key contains the neutron.port_id
1417 self.neutron.update_floatingip(free_floating_ip,
1418 {"floatingip": {"port_id": floating_network["vim_id"]}})
1419 # Using nove is deprecated on nova client 10.0
tierno326fd5e2018-02-22 11:58:59 +01001420 assigned = True
1421 except Exception as e:
tierno4d1ce222018-04-06 10:41:06 +02001422 # openstack need some time after VM creation to asign an IP. So retry if fails
tierno326fd5e2018-02-22 11:58:59 +01001423 vm_status = self.nova.servers.get(server.id).status
1424 if vm_status != 'ACTIVE' and vm_status != 'ERROR':
1425 if time.time() - vm_start_time < server_timeout:
1426 time.sleep(5)
1427 continue
tierno4d1ce222018-04-06 10:41:06 +02001428 raise vimconn.vimconnException(
1429 "Cannot create floating_ip: {} {}".format(type(e).__name__, e),
1430 http_code=vimconn.HTTP_Conflict)
tierno326fd5e2018-02-22 11:58:59 +01001431
tiernof8383b82017-01-18 15:49:48 +01001432 except Exception as e:
1433 if not floating_network['exit_on_floating_ip_error']:
tierno7d782ef2019-10-04 12:56:31 +00001434 self.logger.warning("Cannot create floating_ip. %s", str(e))
tiernof8383b82017-01-18 15:49:48 +01001435 continue
tiernof8383b82017-01-18 15:49:48 +01001436 raise
montesmoreno2a1fc4e2017-01-09 16:46:04 +00001437
tierno98e909c2017-10-14 13:27:03 +02001438 return server.id, created_items
tierno7edb6752016-03-21 17:37:52 +01001439# except nvExceptions.NotFound as e:
1440# error_value=-vimconn.HTTP_Not_Found
1441# error_text= "vm instance %s not found" % vm_id
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001442# except TypeError as e:
1443# raise vimconn.vimconnException(type(e).__name__ + ": "+ str(e), http_code=vimconn.HTTP_Bad_Request)
1444
1445 except Exception as e:
tierno98e909c2017-10-14 13:27:03 +02001446 server_id = None
1447 if server:
1448 server_id = server.id
1449 try:
1450 self.delete_vminstance(server_id, created_items)
1451 except Exception as e2:
1452 self.logger.error("new_vminstance rollback fail {}".format(e2))
Pablo Montes Moreno6a7785b2017-07-03 10:44:30 +02001453
tiernoae4a8d12016-07-08 12:30:39 +02001454 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001455
tiernoae4a8d12016-07-08 12:30:39 +02001456 def get_vminstance(self,vm_id):
tierno7edb6752016-03-21 17:37:52 +01001457 '''Returns the VM instance information from VIM'''
tiernoae4a8d12016-07-08 12:30:39 +02001458 #self.logger.debug("Getting VM from VIM")
tierno7edb6752016-03-21 17:37:52 +01001459 try:
1460 self._reload_connection()
1461 server = self.nova.servers.find(id=vm_id)
1462 #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
tiernoae4a8d12016-07-08 12:30:39 +02001463 return server.to_dict()
tierno8e995ce2016-09-22 08:13:00 +00001464 except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001465 self._format_exception(e)
1466
1467 def get_vminstance_console(self,vm_id, console_type="vnc"):
tierno7edb6752016-03-21 17:37:52 +01001468 '''
1469 Get a console for the virtual machine
1470 Params:
1471 vm_id: uuid of the VM
1472 console_type, can be:
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001473 "novnc" (by default), "xvpvnc" for VNC types,
tierno7edb6752016-03-21 17:37:52 +01001474 "rdp-html5" for RDP types, "spice-html5" for SPICE types
tiernoae4a8d12016-07-08 12:30:39 +02001475 Returns dict with the console parameters:
1476 protocol: ssh, ftp, http, https, ...
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001477 server: usually ip address
1478 port: the http, ssh, ... port
1479 suffix: extra text, e.g. the http path and query string
tierno7edb6752016-03-21 17:37:52 +01001480 '''
tiernoae4a8d12016-07-08 12:30:39 +02001481 self.logger.debug("Getting VM CONSOLE from VIM")
tierno7edb6752016-03-21 17:37:52 +01001482 try:
1483 self._reload_connection()
1484 server = self.nova.servers.find(id=vm_id)
1485 if console_type == None or console_type == "novnc":
1486 console_dict = server.get_vnc_console("novnc")
1487 elif console_type == "xvpvnc":
1488 console_dict = server.get_vnc_console(console_type)
1489 elif console_type == "rdp-html5":
1490 console_dict = server.get_rdp_console(console_type)
1491 elif console_type == "spice-html5":
1492 console_dict = server.get_spice_console(console_type)
1493 else:
tiernoae4a8d12016-07-08 12:30:39 +02001494 raise vimconn.vimconnException("console type '{}' not allowed".format(console_type), http_code=vimconn.HTTP_Bad_Request)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001495
tierno7edb6752016-03-21 17:37:52 +01001496 console_dict1 = console_dict.get("console")
1497 if console_dict1:
1498 console_url = console_dict1.get("url")
1499 if console_url:
1500 #parse console_url
1501 protocol_index = console_url.find("//")
1502 suffix_index = console_url[protocol_index+2:].find("/") + protocol_index+2
1503 port_index = console_url[protocol_index+2:suffix_index].find(":") + protocol_index+2
1504 if protocol_index < 0 or port_index<0 or suffix_index<0:
1505 return -vimconn.HTTP_Internal_Server_Error, "Unexpected response from VIM"
1506 console_dict={"protocol": console_url[0:protocol_index],
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001507 "server": console_url[protocol_index+2:port_index],
1508 "port": console_url[port_index:suffix_index],
1509 "suffix": console_url[suffix_index+1:]
tierno7edb6752016-03-21 17:37:52 +01001510 }
1511 protocol_index += 2
tiernoae4a8d12016-07-08 12:30:39 +02001512 return console_dict
1513 raise vimconn.vimconnUnexpectedResponse("Unexpected response from VIM")
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001514
tierno8e995ce2016-09-22 08:13:00 +00001515 except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.BadRequest, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001516 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001517
tierno98e909c2017-10-14 13:27:03 +02001518 def delete_vminstance(self, vm_id, created_items=None):
tiernoae4a8d12016-07-08 12:30:39 +02001519 '''Removes a VM instance from VIM. Returns the old identifier
tierno7edb6752016-03-21 17:37:52 +01001520 '''
tiernoae4a8d12016-07-08 12:30:39 +02001521 #print "osconnector: Getting VM from VIM"
tierno98e909c2017-10-14 13:27:03 +02001522 if created_items == None:
1523 created_items = {}
tierno7edb6752016-03-21 17:37:52 +01001524 try:
1525 self._reload_connection()
tierno98e909c2017-10-14 13:27:03 +02001526 # delete VM ports attached to this networks before the virtual machine
1527 for k, v in created_items.items():
1528 if not v: # skip already deleted
1529 continue
tierno7edb6752016-03-21 17:37:52 +01001530 try:
tiernoad6bdd42018-01-10 10:43:46 +01001531 k_item, _, k_id = k.partition(":")
1532 if k_item == "port":
1533 self.neutron.delete_port(k_id)
tierno7edb6752016-03-21 17:37:52 +01001534 except Exception as e:
tierno00e3df72017-11-29 17:20:13 +01001535 self.logger.error("Error deleting port: {}: {}".format(type(e).__name__, e))
montesmoreno0c8def02016-12-22 12:16:23 +00001536
tierno98e909c2017-10-14 13:27:03 +02001537 # #commented because detaching the volumes makes the servers.delete not work properly ?!?
1538 # #dettach volumes attached
1539 # server = self.nova.servers.get(vm_id)
1540 # volumes_attached_dict = server._info['os-extended-volumes:volumes_attached'] #volume['id']
1541 # #for volume in volumes_attached_dict:
1542 # # self.cinder.volumes.detach(volume['id'])
montesmoreno0c8def02016-12-22 12:16:23 +00001543
tierno98e909c2017-10-14 13:27:03 +02001544 if vm_id:
1545 self.nova.servers.delete(vm_id)
montesmoreno0c8def02016-12-22 12:16:23 +00001546
tierno98e909c2017-10-14 13:27:03 +02001547 # delete volumes. Although having detached, they should have in active status before deleting
1548 # we ensure in this loop
montesmoreno0c8def02016-12-22 12:16:23 +00001549 keep_waiting = True
1550 elapsed_time = 0
1551 while keep_waiting and elapsed_time < volume_timeout:
1552 keep_waiting = False
tierno98e909c2017-10-14 13:27:03 +02001553 for k, v in created_items.items():
1554 if not v: # skip already deleted
1555 continue
1556 try:
tiernoad6bdd42018-01-10 10:43:46 +01001557 k_item, _, k_id = k.partition(":")
1558 if k_item == "volume":
1559 if self.cinder.volumes.get(k_id).status != 'available':
tierno98e909c2017-10-14 13:27:03 +02001560 keep_waiting = True
1561 else:
tiernoad6bdd42018-01-10 10:43:46 +01001562 self.cinder.volumes.delete(k_id)
tierno98e909c2017-10-14 13:27:03 +02001563 except Exception as e:
tierno00e3df72017-11-29 17:20:13 +01001564 self.logger.error("Error deleting volume: {}: {}".format(type(e).__name__, e))
montesmoreno0c8def02016-12-22 12:16:23 +00001565 if keep_waiting:
1566 time.sleep(1)
1567 elapsed_time += 1
tierno98e909c2017-10-14 13:27:03 +02001568 return None
tierno8e995ce2016-09-22 08:13:00 +00001569 except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001570 self._format_exception(e)
tierno7edb6752016-03-21 17:37:52 +01001571
tiernoae4a8d12016-07-08 12:30:39 +02001572 def refresh_vms_status(self, vm_list):
1573 '''Get the status of the virtual machines and their interfaces/ports
1574 Params: the list of VM identifiers
1575 Returns a dictionary with:
1576 vm_id: #VIM id of this Virtual Machine
1577 status: #Mandatory. Text with one of:
1578 # DELETED (not found at vim)
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001579 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
tiernoae4a8d12016-07-08 12:30:39 +02001580 # OTHER (Vim reported other status not understood)
1581 # ERROR (VIM indicates an ERROR status)
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001582 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
tiernoae4a8d12016-07-08 12:30:39 +02001583 # CREATING (on building process), ERROR
1584 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1585 #
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001586 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
tiernoae4a8d12016-07-08 12:30:39 +02001587 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1588 interfaces:
1589 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1590 mac_address: #Text format XX:XX:XX:XX:XX:XX
1591 vim_net_id: #network id where this interface is connected
1592 vim_interface_id: #interface/port VIM id
1593 ip_address: #null, or text with IPv4, IPv6 address
tierno867ffe92017-03-27 12:50:34 +02001594 compute_node: #identification of compute node where PF,VF interface is allocated
1595 pci: #PCI address of the NIC that hosts the PF,VF
1596 vlan: #physical VLAN used for VF
tierno7edb6752016-03-21 17:37:52 +01001597 '''
tiernoae4a8d12016-07-08 12:30:39 +02001598 vm_dict={}
1599 self.logger.debug("refresh_vms status: Getting tenant VM instance information from VIM")
1600 for vm_id in vm_list:
1601 vm={}
1602 try:
1603 vm_vim = self.get_vminstance(vm_id)
1604 if vm_vim['status'] in vmStatus2manoFormat:
1605 vm['status'] = vmStatus2manoFormat[ vm_vim['status'] ]
tierno7edb6752016-03-21 17:37:52 +01001606 else:
tiernoae4a8d12016-07-08 12:30:39 +02001607 vm['status'] = "OTHER"
1608 vm['error_msg'] = "VIM status reported " + vm_vim['status']
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001609
1610 vm['vim_info'] = self.serialize(vm_vim)
1611
tiernoae4a8d12016-07-08 12:30:39 +02001612 vm["interfaces"] = []
1613 if vm_vim.get('fault'):
1614 vm['error_msg'] = str(vm_vim['fault'])
1615 #get interfaces
tierno7edb6752016-03-21 17:37:52 +01001616 try:
tiernoae4a8d12016-07-08 12:30:39 +02001617 self._reload_connection()
tiernob42fd9b2018-06-20 10:44:32 +02001618 port_dict = self.neutron.list_ports(device_id=vm_id)
tiernoae4a8d12016-07-08 12:30:39 +02001619 for port in port_dict["ports"]:
1620 interface={}
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001621 interface['vim_info'] = self.serialize(port)
tiernoae4a8d12016-07-08 12:30:39 +02001622 interface["mac_address"] = port.get("mac_address")
1623 interface["vim_net_id"] = port["network_id"]
1624 interface["vim_interface_id"] = port["id"]
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001625 # check if OS-EXT-SRV-ATTR:host is there,
Mike Marchetti5b9da422017-05-02 15:35:47 -04001626 # in case of non-admin credentials, it will be missing
1627 if vm_vim.get('OS-EXT-SRV-ATTR:host'):
1628 interface["compute_node"] = vm_vim['OS-EXT-SRV-ATTR:host']
tierno867ffe92017-03-27 12:50:34 +02001629 interface["pci"] = None
Mike Marchetti5b9da422017-05-02 15:35:47 -04001630
Anderson Bravalheri0446cd52018-08-17 15:26:19 +01001631 # check if binding:profile is there,
Mike Marchetti5b9da422017-05-02 15:35:47 -04001632 # in case of non-admin credentials, it will be missing
1633 if port.get('binding:profile'):
1634 if port['binding:profile'].get('pci_slot'):
1635 # TODO: At the moment sr-iov pci addresses are converted to PF pci addresses by setting the slot to 0x00
1636 # TODO: This is just a workaround valid for niantinc. Find a better way to do so
1637 # CHANGE DDDD:BB:SS.F to DDDD:BB:00.(F%2) assuming there are 2 ports per nic
1638 pci = port['binding:profile']['pci_slot']
1639 # interface["pci"] = pci[:-4] + "00." + str(int(pci[-1]) % 2)
1640 interface["pci"] = pci
tierno867ffe92017-03-27 12:50:34 +02001641 interface["vlan"] = None
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001642 #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 +01001643 network = self.neutron.show_network(port["network_id"])
Pablo Montes Moreno3be0b2a2017-03-30 13:22:15 +02001644 if network['network'].get('provider:network_type') == 'vlan' and \
1645 port.get("binding:vnic_type") == "direct":
tierno867ffe92017-03-27 12:50:34 +02001646 interface["vlan"] = network['network'].get('provider:segmentation_id')
tiernoae4a8d12016-07-08 12:30:39 +02001647 ips=[]
1648 #look for floating ip address
tiernob42fd9b2018-06-20 10:44:32 +02001649 try:
1650 floating_ip_dict = self.neutron.list_floatingips(port_id=port["id"])
1651 if floating_ip_dict.get("floatingips"):
1652 ips.append(floating_ip_dict["floatingips"][0].get("floating_ip_address") )
1653 except Exception:
1654 pass
tierno7edb6752016-03-21 17:37:52 +01001655
tiernoae4a8d12016-07-08 12:30:39 +02001656 for subnet in port["fixed_ips"]:
1657 ips.append(subnet["ip_address"])
1658 interface["ip_address"] = ";".join(ips)
1659 vm["interfaces"].append(interface)
1660 except Exception as e:
tiernob42fd9b2018-06-20 10:44:32 +02001661 self.logger.error("Error getting vm interface information {}: {}".format(type(e).__name__, e),
1662 exc_info=True)
tiernoae4a8d12016-07-08 12:30:39 +02001663 except vimconn.vimconnNotFoundException as e:
1664 self.logger.error("Exception getting vm status: %s", str(e))
1665 vm['status'] = "DELETED"
1666 vm['error_msg'] = str(e)
1667 except vimconn.vimconnException as e:
1668 self.logger.error("Exception getting vm status: %s", str(e))
1669 vm['status'] = "VIM_ERROR"
1670 vm['error_msg'] = str(e)
1671 vm_dict[vm_id] = vm
1672 return vm_dict
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001673
tierno98e909c2017-10-14 13:27:03 +02001674 def action_vminstance(self, vm_id, action_dict, created_items={}):
tierno7edb6752016-03-21 17:37:52 +01001675 '''Send and action over a VM instance from VIM
tierno98e909c2017-10-14 13:27:03 +02001676 Returns None or the console dict if the action was successfully sent to the VIM'''
tiernoae4a8d12016-07-08 12:30:39 +02001677 self.logger.debug("Action over VM '%s': %s", vm_id, str(action_dict))
tierno7edb6752016-03-21 17:37:52 +01001678 try:
1679 self._reload_connection()
1680 server = self.nova.servers.find(id=vm_id)
1681 if "start" in action_dict:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001682 if action_dict["start"]=="rebuild":
tierno7edb6752016-03-21 17:37:52 +01001683 server.rebuild()
1684 else:
1685 if server.status=="PAUSED":
1686 server.unpause()
1687 elif server.status=="SUSPENDED":
1688 server.resume()
1689 elif server.status=="SHUTOFF":
1690 server.start()
1691 elif "pause" in action_dict:
1692 server.pause()
1693 elif "resume" in action_dict:
1694 server.resume()
1695 elif "shutoff" in action_dict or "shutdown" in action_dict:
1696 server.stop()
1697 elif "forceOff" in action_dict:
1698 server.stop() #TODO
1699 elif "terminate" in action_dict:
1700 server.delete()
1701 elif "createImage" in action_dict:
1702 server.create_image()
1703 #"path":path_schema,
1704 #"description":description_schema,
1705 #"name":name_schema,
1706 #"metadata":metadata_schema,
1707 #"imageRef": id_schema,
1708 #"disk": {"oneOf":[{"type": "null"}, {"type":"string"}] },
1709 elif "rebuild" in action_dict:
1710 server.rebuild(server.image['id'])
1711 elif "reboot" in action_dict:
1712 server.reboot() #reboot_type='SOFT'
1713 elif "console" in action_dict:
1714 console_type = action_dict["console"]
1715 if console_type == None or console_type == "novnc":
1716 console_dict = server.get_vnc_console("novnc")
1717 elif console_type == "xvpvnc":
1718 console_dict = server.get_vnc_console(console_type)
1719 elif console_type == "rdp-html5":
1720 console_dict = server.get_rdp_console(console_type)
1721 elif console_type == "spice-html5":
1722 console_dict = server.get_spice_console(console_type)
1723 else:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001724 raise vimconn.vimconnException("console type '{}' not allowed".format(console_type),
tiernoae4a8d12016-07-08 12:30:39 +02001725 http_code=vimconn.HTTP_Bad_Request)
tierno7edb6752016-03-21 17:37:52 +01001726 try:
1727 console_url = console_dict["console"]["url"]
1728 #parse console_url
1729 protocol_index = console_url.find("//")
1730 suffix_index = console_url[protocol_index+2:].find("/") + protocol_index+2
1731 port_index = console_url[protocol_index+2:suffix_index].find(":") + protocol_index+2
1732 if protocol_index < 0 or port_index<0 or suffix_index<0:
tiernoae4a8d12016-07-08 12:30:39 +02001733 raise vimconn.vimconnException("Unexpected response from VIM " + str(console_dict))
tierno7edb6752016-03-21 17:37:52 +01001734 console_dict2={"protocol": console_url[0:protocol_index],
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001735 "server": console_url[protocol_index+2 : port_index],
1736 "port": int(console_url[port_index+1 : suffix_index]),
1737 "suffix": console_url[suffix_index+1:]
tierno7edb6752016-03-21 17:37:52 +01001738 }
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001739 return console_dict2
tiernoae4a8d12016-07-08 12:30:39 +02001740 except Exception as e:
1741 raise vimconn.vimconnException("Unexpected response from VIM " + str(console_dict))
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001742
tierno98e909c2017-10-14 13:27:03 +02001743 return None
tierno8e995ce2016-09-22 08:13:00 +00001744 except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound, ConnectionError) as e:
tiernoae4a8d12016-07-08 12:30:39 +02001745 self._format_exception(e)
1746 #TODO insert exception vimconn.HTTP_Unauthorized
1747
kate721d79b2017-06-24 04:21:38 -07001748 ####### VIO Specific Changes #########
garciadeblasebd66722019-01-31 16:01:31 +00001749 def _generate_vlanID(self):
kate721d79b2017-06-24 04:21:38 -07001750 """
1751 Method to get unused vlanID
1752 Args:
1753 None
1754 Returns:
1755 vlanID
1756 """
1757 #Get used VLAN IDs
1758 usedVlanIDs = []
1759 networks = self.get_network_list()
1760 for net in networks:
1761 if net.get('provider:segmentation_id'):
1762 usedVlanIDs.append(net.get('provider:segmentation_id'))
1763 used_vlanIDs = set(usedVlanIDs)
1764
1765 #find unused VLAN ID
1766 for vlanID_range in self.config.get('dataplane_net_vlan_range'):
1767 try:
1768 start_vlanid , end_vlanid = map(int, vlanID_range.replace(" ", "").split("-"))
tierno7d782ef2019-10-04 12:56:31 +00001769 for vlanID in range(start_vlanid, end_vlanid + 1):
kate721d79b2017-06-24 04:21:38 -07001770 if vlanID not in used_vlanIDs:
1771 return vlanID
1772 except Exception as exp:
1773 raise vimconn.vimconnException("Exception {} occurred while generating VLAN ID.".format(exp))
1774 else:
1775 raise vimconn.vimconnConflictException("Unable to create the SRIOV VLAN network."\
1776 " All given Vlan IDs {} are in use.".format(self.config.get('dataplane_net_vlan_range')))
1777
1778
garciadeblasebd66722019-01-31 16:01:31 +00001779 def _generate_multisegment_vlanID(self):
1780 """
1781 Method to get unused vlanID
1782 Args:
1783 None
1784 Returns:
1785 vlanID
1786 """
1787 #Get used VLAN IDs
1788 usedVlanIDs = []
1789 networks = self.get_network_list()
1790 for net in networks:
1791 if net.get('provider:network_type') == "vlan" and net.get('provider:segmentation_id'):
1792 usedVlanIDs.append(net.get('provider:segmentation_id'))
1793 elif net.get('segments'):
1794 for segment in net.get('segments'):
1795 if segment.get('provider:network_type') == "vlan" and segment.get('provider:segmentation_id'):
1796 usedVlanIDs.append(segment.get('provider:segmentation_id'))
1797 used_vlanIDs = set(usedVlanIDs)
1798
1799 #find unused VLAN ID
1800 for vlanID_range in self.config.get('multisegment_vlan_range'):
1801 try:
1802 start_vlanid , end_vlanid = map(int, vlanID_range.replace(" ", "").split("-"))
tierno7d782ef2019-10-04 12:56:31 +00001803 for vlanID in range(start_vlanid, end_vlanid + 1):
garciadeblasebd66722019-01-31 16:01:31 +00001804 if vlanID not in used_vlanIDs:
1805 return vlanID
1806 except Exception as exp:
1807 raise vimconn.vimconnException("Exception {} occurred while generating VLAN ID.".format(exp))
1808 else:
1809 raise vimconn.vimconnConflictException("Unable to create the VLAN segment."\
1810 " All VLAN IDs {} are in use.".format(self.config.get('multisegment_vlan_range')))
1811
1812
1813 def _validate_vlan_ranges(self, input_vlan_range, text_vlan_range):
kate721d79b2017-06-24 04:21:38 -07001814 """
1815 Method to validate user given vlanID ranges
1816 Args: None
1817 Returns: None
1818 """
garciadeblasebd66722019-01-31 16:01:31 +00001819 for vlanID_range in input_vlan_range:
kate721d79b2017-06-24 04:21:38 -07001820 vlan_range = vlanID_range.replace(" ", "")
1821 #validate format
1822 vlanID_pattern = r'(\d)*-(\d)*$'
1823 match_obj = re.match(vlanID_pattern, vlan_range)
1824 if not match_obj:
garciadeblasebd66722019-01-31 16:01:31 +00001825 raise vimconn.vimconnConflictException("Invalid VLAN range for {}: {}.You must provide "\
1826 "'{}' in format [start_ID - end_ID].".format(text_vlan_range, vlanID_range, text_vlan_range))
kate721d79b2017-06-24 04:21:38 -07001827
1828 start_vlanid , end_vlanid = map(int,vlan_range.split("-"))
1829 if start_vlanid <= 0 :
garciadeblasebd66722019-01-31 16:01:31 +00001830 raise vimconn.vimconnConflictException("Invalid VLAN range for {}: {}."\
kate721d79b2017-06-24 04:21:38 -07001831 "Start ID can not be zero. For VLAN "\
garciadeblasebd66722019-01-31 16:01:31 +00001832 "networks valid IDs are 1 to 4094 ".format(text_vlan_range, vlanID_range))
kate721d79b2017-06-24 04:21:38 -07001833 if end_vlanid > 4094 :
garciadeblasebd66722019-01-31 16:01:31 +00001834 raise vimconn.vimconnConflictException("Invalid VLAN range for {}: {}."\
kate721d79b2017-06-24 04:21:38 -07001835 "End VLAN ID can not be greater than 4094. For VLAN "\
garciadeblasebd66722019-01-31 16:01:31 +00001836 "networks valid IDs are 1 to 4094 ".format(text_vlan_range, vlanID_range))
kate721d79b2017-06-24 04:21:38 -07001837
1838 if start_vlanid > end_vlanid:
garciadeblasebd66722019-01-31 16:01:31 +00001839 raise vimconn.vimconnConflictException("Invalid VLAN range for {}: {}."\
1840 "You must provide '{}' in format start_ID - end_ID and "\
1841 "start_ID < end_ID ".format(text_vlan_range, vlanID_range, text_vlan_range))
kate721d79b2017-06-24 04:21:38 -07001842
tiernoae4a8d12016-07-08 12:30:39 +02001843#NOT USED FUNCTIONS
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001844
tiernoae4a8d12016-07-08 12:30:39 +02001845 def new_external_port(self, port_data):
1846 #TODO openstack if needed
1847 '''Adds a external port to VIM'''
1848 '''Returns the port identifier'''
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001849 return -vimconn.HTTP_Internal_Server_Error, "osconnector.new_external_port() not implemented"
1850
tiernoae4a8d12016-07-08 12:30:39 +02001851 def connect_port_network(self, port_id, network_id, admin=False):
1852 #TODO openstack if needed
1853 '''Connects a external port to a network'''
1854 '''Returns status code of the VIM response'''
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001855 return -vimconn.HTTP_Internal_Server_Error, "osconnector.connect_port_network() not implemented"
1856
tiernoae4a8d12016-07-08 12:30:39 +02001857 def new_user(self, user_name, user_passwd, tenant_id=None):
1858 '''Adds a new user to openstack VIM'''
1859 '''Returns the user identifier'''
1860 self.logger.debug("osconnector: Adding a new user to VIM")
1861 try:
1862 self._reload_connection()
Eduardo Sousae3c0dbc2018-09-03 11:56:07 +01001863 user=self.keystone.users.create(user_name, password=user_passwd, default_project=tenant_id)
tiernoae4a8d12016-07-08 12:30:39 +02001864 #self.keystone.tenants.add_user(self.k_creds["username"], #role)
1865 return user.id
1866 except ksExceptions.ConnectionError as e:
1867 error_value=-vimconn.HTTP_Bad_Request
1868 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1869 except ksExceptions.ClientException as e: #TODO remove
tierno7edb6752016-03-21 17:37:52 +01001870 error_value=-vimconn.HTTP_Bad_Request
1871 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1872 #TODO insert exception vimconn.HTTP_Unauthorized
1873 #if reaching here is because an exception
tierno9c5c8322018-03-23 15:44:03 +01001874 self.logger.debug("new_user " + error_text)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001875 return error_value, error_text
tiernoae4a8d12016-07-08 12:30:39 +02001876
1877 def delete_user(self, user_id):
1878 '''Delete a user from openstack VIM'''
1879 '''Returns the user identifier'''
1880 if self.debug:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001881 print("osconnector: Deleting a user from VIM")
tiernoae4a8d12016-07-08 12:30:39 +02001882 try:
1883 self._reload_connection()
1884 self.keystone.users.delete(user_id)
1885 return 1, user_id
1886 except ksExceptions.ConnectionError as e:
1887 error_value=-vimconn.HTTP_Bad_Request
1888 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1889 except ksExceptions.NotFound as e:
1890 error_value=-vimconn.HTTP_Not_Found
1891 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1892 except ksExceptions.ClientException as e: #TODO remove
1893 error_value=-vimconn.HTTP_Bad_Request
1894 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1895 #TODO insert exception vimconn.HTTP_Unauthorized
1896 #if reaching here is because an exception
tierno9c5c8322018-03-23 15:44:03 +01001897 self.logger.debug("delete_tenant " + error_text)
tiernoae4a8d12016-07-08 12:30:39 +02001898 return error_value, error_text
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001899
tierno7edb6752016-03-21 17:37:52 +01001900 def get_hosts_info(self):
1901 '''Get the information of deployed hosts
1902 Returns the hosts content'''
1903 if self.debug:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001904 print("osconnector: Getting Host info from VIM")
tierno7edb6752016-03-21 17:37:52 +01001905 try:
1906 h_list=[]
1907 self._reload_connection()
1908 hypervisors = self.nova.hypervisors.list()
1909 for hype in hypervisors:
1910 h_list.append( hype.to_dict() )
1911 return 1, {"hosts":h_list}
1912 except nvExceptions.NotFound as e:
1913 error_value=-vimconn.HTTP_Not_Found
1914 error_text= (str(e) if len(e.args)==0 else str(e.args[0]))
1915 except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
1916 error_value=-vimconn.HTTP_Bad_Request
1917 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1918 #TODO insert exception vimconn.HTTP_Unauthorized
1919 #if reaching here is because an exception
tierno9c5c8322018-03-23 15:44:03 +01001920 self.logger.debug("get_hosts_info " + error_text)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001921 return error_value, error_text
tierno7edb6752016-03-21 17:37:52 +01001922
1923 def get_hosts(self, vim_tenant):
1924 '''Get the hosts and deployed instances
1925 Returns the hosts content'''
1926 r, hype_dict = self.get_hosts_info()
1927 if r<0:
1928 return r, hype_dict
1929 hypervisors = hype_dict["hosts"]
1930 try:
1931 servers = self.nova.servers.list()
1932 for hype in hypervisors:
1933 for server in servers:
1934 if server.to_dict()['OS-EXT-SRV-ATTR:hypervisor_hostname']==hype['hypervisor_hostname']:
1935 if 'vm' in hype:
1936 hype['vm'].append(server.id)
1937 else:
1938 hype['vm'] = [server.id]
1939 return 1, hype_dict
1940 except nvExceptions.NotFound as e:
1941 error_value=-vimconn.HTTP_Not_Found
1942 error_text= (str(e) if len(e.args)==0 else str(e.args[0]))
1943 except (ksExceptions.ClientException, nvExceptions.ClientException) as e:
1944 error_value=-vimconn.HTTP_Bad_Request
1945 error_text= type(e).__name__ + ": "+ (str(e) if len(e.args)==0 else str(e.args[0]))
1946 #TODO insert exception vimconn.HTTP_Unauthorized
1947 #if reaching here is because an exception
tierno9c5c8322018-03-23 15:44:03 +01001948 self.logger.debug("get_hosts " + error_text)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001949 return error_value, error_text
tierno7edb6752016-03-21 17:37:52 +01001950
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001951 def new_classification(self, name, ctype, definition):
tierno7d782ef2019-10-04 12:56:31 +00001952 self.logger.debug('Adding a new (Traffic) Classification to VIM, named %s', name)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001953 try:
1954 new_class = None
1955 self._reload_connection()
1956 if ctype not in supportedClassificationTypes:
1957 raise vimconn.vimconnNotSupportedException(
1958 'OpenStack VIM connector doesn\'t support provided '
1959 'Classification Type {}, supported ones are: '
1960 '{}'.format(ctype, supportedClassificationTypes))
1961 if not self._validate_classification(ctype, definition):
1962 raise vimconn.vimconnException(
1963 'Incorrect Classification definition '
1964 'for the type specified.')
1965 classification_dict = definition
1966 classification_dict['name'] = name
tierno7edb6752016-03-21 17:37:52 +01001967
Igor D.Ccaadc442017-11-06 12:48:48 +00001968 new_class = self.neutron.create_sfc_flow_classifier(
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001969 {'flow_classifier': classification_dict})
1970 return new_class['flow_classifier']['id']
1971 except (neExceptions.ConnectionFailed, ksExceptions.ClientException,
1972 neExceptions.NeutronException, ConnectionError) as e:
1973 self.logger.error(
1974 'Creation of Classification failed.')
1975 self._format_exception(e)
1976
1977 def get_classification(self, class_id):
1978 self.logger.debug(" Getting Classification %s from VIM", class_id)
1979 filter_dict = {"id": class_id}
1980 class_list = self.get_classification_list(filter_dict)
1981 if len(class_list) == 0:
1982 raise vimconn.vimconnNotFoundException(
1983 "Classification '{}' not found".format(class_id))
1984 elif len(class_list) > 1:
1985 raise vimconn.vimconnConflictException(
1986 "Found more than one Classification with this criteria")
1987 classification = class_list[0]
1988 return classification
1989
1990 def get_classification_list(self, filter_dict={}):
1991 self.logger.debug("Getting Classifications from VIM filter: '%s'",
1992 str(filter_dict))
1993 try:
tierno69b590e2018-03-13 18:52:23 +01001994 filter_dict_os = filter_dict.copy()
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00001995 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +01001996 if self.api_version3 and "tenant_id" in filter_dict_os:
1997 filter_dict_os['project_id'] = filter_dict_os.pop('tenant_id')
Igor D.Ccaadc442017-11-06 12:48:48 +00001998 classification_dict = self.neutron.list_sfc_flow_classifiers(
tierno69b590e2018-03-13 18:52:23 +01001999 **filter_dict_os)
2000 classification_list = classification_dict["flow_classifiers"]
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002001 self.__classification_os2mano(classification_list)
2002 return classification_list
2003 except (neExceptions.ConnectionFailed, ksExceptions.ClientException,
2004 neExceptions.NeutronException, ConnectionError) as e:
2005 self._format_exception(e)
2006
2007 def delete_classification(self, class_id):
2008 self.logger.debug("Deleting Classification '%s' from VIM", class_id)
2009 try:
2010 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00002011 self.neutron.delete_sfc_flow_classifier(class_id)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002012 return class_id
2013 except (neExceptions.ConnectionFailed, neExceptions.NeutronException,
2014 ksExceptions.ClientException, neExceptions.NeutronException,
2015 ConnectionError) as e:
2016 self._format_exception(e)
2017
2018 def new_sfi(self, name, ingress_ports, egress_ports, sfc_encap=True):
tierno7d782ef2019-10-04 12:56:31 +00002019 self.logger.debug("Adding a new Service Function Instance to VIM, named '%s'", name)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002020 try:
2021 new_sfi = None
2022 self._reload_connection()
2023 correlation = None
2024 if sfc_encap:
Igor D.Ccaadc442017-11-06 12:48:48 +00002025 correlation = 'nsh'
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002026 if len(ingress_ports) != 1:
2027 raise vimconn.vimconnNotSupportedException(
2028 "OpenStack VIM connector can only have "
2029 "1 ingress port per SFI")
2030 if len(egress_ports) != 1:
2031 raise vimconn.vimconnNotSupportedException(
2032 "OpenStack VIM connector can only have "
2033 "1 egress port per SFI")
2034 sfi_dict = {'name': name,
2035 'ingress': ingress_ports[0],
2036 'egress': egress_ports[0],
2037 'service_function_parameters': {
2038 'correlation': correlation}}
Igor D.Ccaadc442017-11-06 12:48:48 +00002039 new_sfi = self.neutron.create_sfc_port_pair({'port_pair': sfi_dict})
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002040 return new_sfi['port_pair']['id']
2041 except (neExceptions.ConnectionFailed, ksExceptions.ClientException,
2042 neExceptions.NeutronException, ConnectionError) as e:
2043 if new_sfi:
2044 try:
Igor D.Ccaadc442017-11-06 12:48:48 +00002045 self.neutron.delete_sfc_port_pair(
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002046 new_sfi['port_pair']['id'])
2047 except Exception:
2048 self.logger.error(
2049 'Creation of Service Function Instance failed, with '
2050 'subsequent deletion failure as well.')
2051 self._format_exception(e)
2052
2053 def get_sfi(self, sfi_id):
tierno7d782ef2019-10-04 12:56:31 +00002054 self.logger.debug('Getting Service Function Instance %s from VIM', sfi_id)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002055 filter_dict = {"id": sfi_id}
2056 sfi_list = self.get_sfi_list(filter_dict)
2057 if len(sfi_list) == 0:
tierno7d782ef2019-10-04 12:56:31 +00002058 raise vimconn.vimconnNotFoundException("Service Function Instance '{}' not found".format(sfi_id))
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002059 elif len(sfi_list) > 1:
2060 raise vimconn.vimconnConflictException(
2061 'Found more than one Service Function Instance '
2062 'with this criteria')
2063 sfi = sfi_list[0]
2064 return sfi
2065
2066 def get_sfi_list(self, filter_dict={}):
tierno7d782ef2019-10-04 12:56:31 +00002067 self.logger.debug("Getting Service Function Instances from VIM filter: '%s'", str(filter_dict))
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002068 try:
2069 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +01002070 filter_dict_os = filter_dict.copy()
2071 if self.api_version3 and "tenant_id" in filter_dict_os:
2072 filter_dict_os['project_id'] = filter_dict_os.pop('tenant_id')
2073 sfi_dict = self.neutron.list_sfc_port_pairs(**filter_dict_os)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002074 sfi_list = sfi_dict["port_pairs"]
2075 self.__sfi_os2mano(sfi_list)
2076 return sfi_list
2077 except (neExceptions.ConnectionFailed, ksExceptions.ClientException,
2078 neExceptions.NeutronException, ConnectionError) as e:
2079 self._format_exception(e)
2080
2081 def delete_sfi(self, sfi_id):
2082 self.logger.debug("Deleting Service Function Instance '%s' "
2083 "from VIM", sfi_id)
2084 try:
2085 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00002086 self.neutron.delete_sfc_port_pair(sfi_id)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002087 return sfi_id
2088 except (neExceptions.ConnectionFailed, neExceptions.NeutronException,
2089 ksExceptions.ClientException, neExceptions.NeutronException,
2090 ConnectionError) as e:
2091 self._format_exception(e)
2092
2093 def new_sf(self, name, sfis, sfc_encap=True):
tierno7d782ef2019-10-04 12:56:31 +00002094 self.logger.debug("Adding a new Service Function to VIM, named '%s'", name)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002095 try:
2096 new_sf = None
2097 self._reload_connection()
tierno9c5c8322018-03-23 15:44:03 +01002098 # correlation = None
2099 # if sfc_encap:
2100 # correlation = 'nsh'
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002101 for instance in sfis:
2102 sfi = self.get_sfi(instance)
Igor D.Ccaadc442017-11-06 12:48:48 +00002103 if sfi.get('sfc_encap') != sfc_encap:
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002104 raise vimconn.vimconnNotSupportedException(
2105 "OpenStack VIM connector requires all SFIs of the "
2106 "same SF to share the same SFC Encapsulation")
2107 sf_dict = {'name': name,
2108 'port_pairs': sfis}
Igor D.Ccaadc442017-11-06 12:48:48 +00002109 new_sf = self.neutron.create_sfc_port_pair_group({
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002110 'port_pair_group': sf_dict})
2111 return new_sf['port_pair_group']['id']
2112 except (neExceptions.ConnectionFailed, ksExceptions.ClientException,
2113 neExceptions.NeutronException, ConnectionError) as e:
2114 if new_sf:
2115 try:
Igor D.Ccaadc442017-11-06 12:48:48 +00002116 self.neutron.delete_sfc_port_pair_group(
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002117 new_sf['port_pair_group']['id'])
2118 except Exception:
2119 self.logger.error(
2120 'Creation of Service Function failed, with '
2121 'subsequent deletion failure as well.')
2122 self._format_exception(e)
2123
2124 def get_sf(self, sf_id):
2125 self.logger.debug("Getting Service Function %s from VIM", sf_id)
2126 filter_dict = {"id": sf_id}
2127 sf_list = self.get_sf_list(filter_dict)
2128 if len(sf_list) == 0:
2129 raise vimconn.vimconnNotFoundException(
2130 "Service Function '{}' not found".format(sf_id))
2131 elif len(sf_list) > 1:
2132 raise vimconn.vimconnConflictException(
2133 "Found more than one Service Function with this criteria")
2134 sf = sf_list[0]
2135 return sf
2136
2137 def get_sf_list(self, filter_dict={}):
2138 self.logger.debug("Getting Service Function from VIM filter: '%s'",
2139 str(filter_dict))
2140 try:
2141 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +01002142 filter_dict_os = filter_dict.copy()
2143 if self.api_version3 and "tenant_id" in filter_dict_os:
2144 filter_dict_os['project_id'] = filter_dict_os.pop('tenant_id')
2145 sf_dict = self.neutron.list_sfc_port_pair_groups(**filter_dict_os)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002146 sf_list = sf_dict["port_pair_groups"]
2147 self.__sf_os2mano(sf_list)
2148 return sf_list
2149 except (neExceptions.ConnectionFailed, ksExceptions.ClientException,
2150 neExceptions.NeutronException, ConnectionError) as e:
2151 self._format_exception(e)
2152
2153 def delete_sf(self, sf_id):
2154 self.logger.debug("Deleting Service Function '%s' from VIM", sf_id)
2155 try:
2156 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00002157 self.neutron.delete_sfc_port_pair_group(sf_id)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002158 return sf_id
2159 except (neExceptions.ConnectionFailed, neExceptions.NeutronException,
2160 ksExceptions.ClientException, neExceptions.NeutronException,
2161 ConnectionError) as e:
2162 self._format_exception(e)
2163
2164 def new_sfp(self, name, classifications, sfs, sfc_encap=True, spi=None):
tierno7d782ef2019-10-04 12:56:31 +00002165 self.logger.debug("Adding a new Service Function Path to VIM, named '%s'", name)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002166 try:
2167 new_sfp = None
2168 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00002169 # In networking-sfc the MPLS encapsulation is legacy
2170 # should be used when no full SFC Encapsulation is intended
schillinge981df9a2019-01-24 09:25:11 +01002171 correlation = 'mpls'
Igor D.Ccaadc442017-11-06 12:48:48 +00002172 if sfc_encap:
2173 correlation = 'nsh'
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002174 sfp_dict = {'name': name,
2175 'flow_classifiers': classifications,
2176 'port_pair_groups': sfs,
2177 'chain_parameters': {'correlation': correlation}}
2178 if spi:
2179 sfp_dict['chain_id'] = spi
Igor D.Ccaadc442017-11-06 12:48:48 +00002180 new_sfp = self.neutron.create_sfc_port_chain({'port_chain': sfp_dict})
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002181 return new_sfp["port_chain"]["id"]
2182 except (neExceptions.ConnectionFailed, ksExceptions.ClientException,
2183 neExceptions.NeutronException, ConnectionError) as e:
2184 if new_sfp:
2185 try:
Igor D.Ccaadc442017-11-06 12:48:48 +00002186 self.neutron.delete_sfc_port_chain(new_sfp['port_chain']['id'])
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002187 except Exception:
2188 self.logger.error(
2189 'Creation of Service Function Path failed, with '
2190 'subsequent deletion failure as well.')
2191 self._format_exception(e)
2192
2193 def get_sfp(self, sfp_id):
2194 self.logger.debug(" Getting Service Function Path %s from VIM", sfp_id)
2195 filter_dict = {"id": sfp_id}
2196 sfp_list = self.get_sfp_list(filter_dict)
2197 if len(sfp_list) == 0:
2198 raise vimconn.vimconnNotFoundException(
2199 "Service Function Path '{}' not found".format(sfp_id))
2200 elif len(sfp_list) > 1:
2201 raise vimconn.vimconnConflictException(
2202 "Found more than one Service Function Path with this criteria")
2203 sfp = sfp_list[0]
2204 return sfp
2205
2206 def get_sfp_list(self, filter_dict={}):
tierno7d782ef2019-10-04 12:56:31 +00002207 self.logger.debug("Getting Service Function Paths from VIM filter: '%s'", str(filter_dict))
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002208 try:
2209 self._reload_connection()
tierno69b590e2018-03-13 18:52:23 +01002210 filter_dict_os = filter_dict.copy()
2211 if self.api_version3 and "tenant_id" in filter_dict_os:
2212 filter_dict_os['project_id'] = filter_dict_os.pop('tenant_id')
2213 sfp_dict = self.neutron.list_sfc_port_chains(**filter_dict_os)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002214 sfp_list = sfp_dict["port_chains"]
2215 self.__sfp_os2mano(sfp_list)
2216 return sfp_list
2217 except (neExceptions.ConnectionFailed, ksExceptions.ClientException,
2218 neExceptions.NeutronException, ConnectionError) as e:
2219 self._format_exception(e)
2220
2221 def delete_sfp(self, sfp_id):
tierno7d782ef2019-10-04 12:56:31 +00002222 self.logger.debug("Deleting Service Function Path '%s' from VIM", sfp_id)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002223 try:
2224 self._reload_connection()
Igor D.Ccaadc442017-11-06 12:48:48 +00002225 self.neutron.delete_sfc_port_chain(sfp_id)
Igor Duarte Cardoso3cf9bcd2017-08-14 16:39:34 +00002226 return sfp_id
2227 except (neExceptions.ConnectionFailed, neExceptions.NeutronException,
2228 ksExceptions.ClientException, neExceptions.NeutronException,
2229 ConnectionError) as e:
2230 self._format_exception(e)
borsatti8a2dda32019-12-18 15:08:57 +00002231
2232
2233 def refresh_sfps_status(self, sfp_list):
2234 '''Get the status of the service function path
2235 Params: the list of sfp identifiers
2236 Returns a dictionary with:
2237 vm_id: #VIM id of this service function path
2238 status: #Mandatory. Text with one of:
2239 # DELETED (not found at vim)
2240 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
2241 # OTHER (Vim reported other status not understood)
2242 # ERROR (VIM indicates an ERROR status)
2243 # ACTIVE,
2244 # CREATING (on building process)
2245 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
2246 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)F
2247 '''
2248 sfp_dict={}
2249 self.logger.debug("refresh_sfps status: Getting tenant SFP information from VIM")
2250 for sfp_id in sfp_list:
2251 sfp={}
2252 try:
2253 sfp_vim = self.get_sfp(sfp_id)
2254 if sfp_vim['spi']:
2255 sfp['status'] = vmStatus2manoFormat[ 'ACTIVE' ]
2256 else:
2257 sfp['status'] = "OTHER"
2258 sfp['error_msg'] = "VIM status reported " + vm_vim['status']
2259
2260 sfp['vim_info'] = self.serialize(sfp_vim)
2261
2262 if sfp_vim.get('fault'):
2263 sfp['error_msg'] = str(sfp_vim['fault'])
2264
2265 except vimconn.vimconnNotFoundException as e:
2266 self.logger.error("Exception getting sfp status: %s", str(e))
2267 sfp['status'] = "DELETED"
2268 sfp['error_msg'] = str(e)
2269 except vimconn.vimconnException as e:
2270 self.logger.error("Exception getting sfp status: %s", str(e))
2271 sfp['status'] = "VIM_ERROR"
2272 sfp['error_msg'] = str(e)
2273 sfp_dict[sfp_id] = sfp
2274 return sfp_dict
2275
2276
2277 def refresh_sfis_status(self, sfi_list):
2278 '''Get the status of the service function instances
2279 Params: the list of sfi identifiers
2280 Returns a dictionary with:
2281 vm_id: #VIM id of this service function instance
2282 status: #Mandatory. Text with one of:
2283 # DELETED (not found at vim)
2284 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
2285 # OTHER (Vim reported other status not understood)
2286 # ERROR (VIM indicates an ERROR status)
2287 # ACTIVE,
2288 # CREATING (on building process)
2289 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
2290 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
2291 '''
2292 sfi_dict={}
2293 self.logger.debug("refresh_sfis status: Getting tenant sfi information from VIM")
2294 for sfi_id in sfi_list:
2295 sfi={}
2296 try:
2297 sfi_vim = self.get_sfi(sfi_id)
2298 if sfi_vim:
2299 sfi['status'] = vmStatus2manoFormat[ 'ACTIVE' ]
2300 else:
2301 sfi['status'] = "OTHER"
2302 sfi['error_msg'] = "VIM status reported " + vm_vim['status']
2303
2304 sfi['vim_info'] = self.serialize(sfi_vim)
2305
2306 if sfi_vim.get('fault'):
2307 sfi['error_msg'] = str(sfi_vim['fault'])
2308
2309 except vimconn.vimconnNotFoundException as e:
2310 self.logger.error("Exception getting sfi status: %s", str(e))
2311 sfi['status'] = "DELETED"
2312 sfi['error_msg'] = str(e)
2313 except vimconn.vimconnException as e:
2314 self.logger.error("Exception getting sfi status: %s", str(e))
2315 sfi['status'] = "VIM_ERROR"
2316 sfi['error_msg'] = str(e)
2317 sfi_dict[sfi_id] = sfi
2318 return sfi_dict
2319
2320
2321 def refresh_sfs_status(self, sf_list):
2322 '''Get the status of the service functions
2323 Params: the list of sf identifiers
2324 Returns a dictionary with:
2325 vm_id: #VIM id of this service function
2326 status: #Mandatory. Text with one of:
2327 # DELETED (not found at vim)
2328 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
2329 # OTHER (Vim reported other status not understood)
2330 # ERROR (VIM indicates an ERROR status)
2331 # ACTIVE,
2332 # CREATING (on building process)
2333 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
2334 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
2335 '''
2336 sf_dict={}
2337 self.logger.debug("refresh_sfs status: Getting tenant sf information from VIM")
2338 for sf_id in sf_list:
2339 sf={}
2340 try:
2341 sf_vim = self.get_sf(sf_id)
2342 if sf_vim:
2343 sf['status'] = vmStatus2manoFormat[ 'ACTIVE' ]
2344 else:
2345 sf['status'] = "OTHER"
2346 sf['error_msg'] = "VIM status reported " + vm_vim['status']
2347
2348 sf['vim_info'] = self.serialize(sf_vim)
2349
2350 if sf_vim.get('fault'):
2351 sf['error_msg'] = str(sf_vim['fault'])
2352
2353 except vimconn.vimconnNotFoundException as e:
2354 self.logger.error("Exception getting sf status: %s", str(e))
2355 sf['status'] = "DELETED"
2356 sf['error_msg'] = str(e)
2357 except vimconn.vimconnException as e:
2358 self.logger.error("Exception getting sf status: %s", str(e))
2359 sf['status'] = "VIM_ERROR"
2360 sf['error_msg'] = str(e)
2361 sf_dict[sf_id] = sf
2362 return sf_dict
2363
2364
2365
2366 def refresh_classifications_status(self, classification_list):
2367 '''Get the status of the classifications
2368 Params: the list of classification identifiers
2369 Returns a dictionary with:
2370 vm_id: #VIM id of this classifier
2371 status: #Mandatory. Text with one of:
2372 # DELETED (not found at vim)
2373 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
2374 # OTHER (Vim reported other status not understood)
2375 # ERROR (VIM indicates an ERROR status)
2376 # ACTIVE,
2377 # CREATING (on building process)
2378 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
2379 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
2380 '''
2381 classification_dict={}
2382 self.logger.debug("refresh_classifications status: Getting tenant classification information from VIM")
2383 for classification_id in classification_list:
2384 classification={}
2385 try:
2386 classification_vim = self.get_classification(classification_id)
2387 if classification_vim:
2388 classification['status'] = vmStatus2manoFormat[ 'ACTIVE' ]
2389 else:
2390 classification['status'] = "OTHER"
2391 classification['error_msg'] = "VIM status reported " + vm_vim['status']
2392
2393 classification['vim_info'] = self.serialize(classification_vim)
2394
2395 if classification_vim.get('fault'):
2396 classification['error_msg'] = str(classification_vim['fault'])
2397
2398 except vimconn.vimconnNotFoundException as e:
2399 self.logger.error("Exception getting classification status: %s", str(e))
2400 classification['status'] = "DELETED"
2401 classification['error_msg'] = str(e)
2402 except vimconn.vimconnException as e:
2403 self.logger.error("Exception getting classification status: %s", str(e))
2404 classification['status'] = "VIM_ERROR"
2405 classification['error_msg'] = str(e)
2406 classification_dict[classification_id] = classification
2407 return classification_dict