tacker/tacker/nfvo/nfvo_plugin.py

722 lines
32 KiB
Python

# Copyright 2016 Brocade Communications System, Inc.
# All Rights Reserved.
#
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import threading
import time
import uuid
import yaml
from cryptography import fernet
import eventlet
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import strutils
from six import iteritems
from tempfile import mkstemp
from toscaparser.tosca_template import ToscaTemplate
from tacker._i18n import _
from tacker.common import driver_manager
from tacker.common import exceptions
from tacker.common import log
from tacker.common import utils
from tacker import context as t_context
from tacker.db.nfvo import nfvo_db
from tacker.db.nfvo import ns_db
from tacker.db.nfvo import vnffg_db
from tacker.extensions import common_services as cs
from tacker.extensions import nfvo
from tacker import manager
from tacker.plugins.common import constants
from tacker.vnfm.tosca import utils as toscautils
from tacker.vnfm import vim_client
from toscaparser import tosca_template
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
MISTRAL_RETRIES = 30
MISTRAL_RETRY_WAIT = 6
def config_opts():
return [('nfvo_vim', NfvoPlugin.OPTS)]
class NfvoPlugin(nfvo_db.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
ns_db.NSPluginDb):
"""NFVO reference plugin for NFVO extension
Implements the NFVO extension and defines public facing APIs for VIM
operations. NFVO internally invokes the appropriate VIM driver in
backend based on configured VIM types. Plugin also interacts with VNFM
extension for providing the specified VIM information
"""
supported_extension_aliases = ['nfvo']
_lock = threading.RLock()
OPTS = [
cfg.ListOpt(
'vim_drivers', default=['openstack'],
help=_('VIM driver for launching VNFs')),
cfg.IntOpt(
'monitor_interval', default=30,
help=_('Interval to check for VIM health')),
]
cfg.CONF.register_opts(OPTS, 'nfvo_vim')
def __init__(self):
super(NfvoPlugin, self).__init__()
self._pool = eventlet.GreenPool()
self._vim_drivers = driver_manager.DriverManager(
'tacker.nfvo.vim.drivers',
cfg.CONF.nfvo_vim.vim_drivers)
self._created_vims = dict()
self.vim_client = vim_client.VimClient()
context = t_context.get_admin_context()
vims = self.get_vims(context)
for vim in vims:
self._created_vims[vim["id"]] = vim
self._monitor_interval = cfg.CONF.nfvo_vim.monitor_interval
threading.Thread(target=self.__run__).start()
def __run__(self):
while(1):
time.sleep(self._monitor_interval)
for created_vim in self._created_vims.values():
self.monitor_vim(created_vim)
def spawn_n(self, function, *args, **kwargs):
self._pool.spawn_n(function, *args, **kwargs)
@log.log
def create_vim(self, context, vim):
LOG.debug(_('Create vim called with parameters %s'),
strutils.mask_password(vim))
vim_obj = vim['vim']
name = vim_obj['name']
if self._get_by_name(context, nfvo_db.Vim, name):
raise exceptions.DuplicateResourceName(resource='VIM', name=name)
vim_type = vim_obj['type']
vim_obj['id'] = str(uuid.uuid4())
vim_obj['status'] = 'PENDING'
try:
self._vim_drivers.invoke(vim_type, 'register_vim', vim_obj=vim_obj)
res = super(NfvoPlugin, self).create_vim(context, vim_obj)
vim_obj["status"] = "REGISTERING"
with self._lock:
self._created_vims[res["id"]] = res
self.monitor_vim(vim_obj)
return res
except Exception:
with excutils.save_and_reraise_exception():
self._vim_drivers.invoke(vim_type, 'delete_vim_auth',
vim_id=vim_obj['id'])
def _get_vim(self, context, vim_id):
if not self.is_vim_still_in_use(context, vim_id):
return self.get_vim(context, vim_id)
@log.log
def update_vim(self, context, vim_id, vim):
vim_obj = self._get_vim(context, vim_id)
utils.deep_update(vim_obj, vim['vim'])
vim_type = vim_obj['type']
try:
self._vim_drivers.invoke(vim_type, 'register_vim', vim_obj=vim_obj)
return super(NfvoPlugin, self).update_vim(context, vim_id, vim_obj)
except Exception:
with excutils.save_and_reraise_exception():
self._vim_drivers.invoke(vim_type, 'delete_vim_auth',
vim_id=vim_obj['id'])
@log.log
def delete_vim(self, context, vim_id):
vim_obj = self._get_vim(context, vim_id)
self._vim_drivers.invoke(vim_obj['type'], 'deregister_vim',
vim_id=vim_id)
with self._lock:
self._created_vims.pop(vim_id, None)
super(NfvoPlugin, self).delete_vim(context, vim_id)
@log.log
def monitor_vim(self, vim_obj):
vim_id = vim_obj["id"]
auth_url = vim_obj["auth_url"]
vim_status = self._vim_drivers.invoke(vim_obj['type'],
'vim_status',
auth_url=auth_url)
current_status = "REACHABLE" if vim_status else "UNREACHABLE"
if current_status != vim_obj["status"]:
status = current_status
with self._lock:
context = t_context.get_admin_context()
res = super(NfvoPlugin, self).update_vim_status(context,
vim_id, status)
self._created_vims[vim_id]["status"] = status
self._cos_db_plg.create_event(
context, res_id=res['id'],
res_type=constants.RES_TYPE_VIM,
res_state=res['status'],
evt_type=constants.RES_EVT_MONITOR,
tstamp=res[constants.RES_EVT_UPDATED_FLD])
@log.log
def validate_tosca(self, template):
if "tosca_definitions_version" not in template:
raise nfvo.ToscaParserFailed(
error_msg_details='tosca_definitions_version missing in '
'template'
)
LOG.debug(_('template yaml: %s'), template)
toscautils.updateimports(template)
try:
tosca_template.ToscaTemplate(
a_file=False, yaml_dict_tpl=template)
except Exception as e:
LOG.exception(_("tosca-parser error: %s"), str(e))
raise nfvo.ToscaParserFailed(error_msg_details=str(e))
@log.log
def create_vnffgd(self, context, vnffgd):
template = vnffgd['vnffgd']
if 'vnffgd' not in template.get('template'):
raise nfvo.VnffgdInvalidTemplate(template=template.get('template'))
else:
self.validate_tosca(template['template']['vnffgd'])
temp = template['template']['vnffgd']['topology_template']
vnffg_name = list(temp['groups'].keys())[0]
nfp_name = temp['groups'][vnffg_name]['members'][0]
path = self._get_nfp_attribute(template['template'], nfp_name,
'path')
prev_element = None
known_forwarders = set()
for element in path:
if element.get('forwarder') in known_forwarders:
if prev_element is not None and element.get('forwarder')\
!= prev_element['forwarder']:
raise nfvo.VnffgdDuplicateForwarderException(
forwarder=element.get('forwarder')
)
elif prev_element is not None and element.get(
'capability') == prev_element['capability']:
raise nfvo.VnffgdDuplicateCPException(
cp=element.get('capability')
)
else:
known_forwarders.add(element.get('forwarder'))
prev_element = element
return super(NfvoPlugin, self).create_vnffgd(context, vnffgd)
@log.log
def create_vnffg(self, context, vnffg):
vnffg_dict = super(NfvoPlugin, self)._create_vnffg_pre(context, vnffg)
nfp = super(NfvoPlugin, self).get_nfp(context,
vnffg_dict['forwarding_paths'])
sfc = super(NfvoPlugin, self).get_sfc(context, nfp['chain_id'])
match = super(NfvoPlugin, self).get_classifier(context,
nfp['classifier_id'],
fields='match')['match']
# grab the first VNF to check it's VIM type
# we have already checked that all VNFs are in the same VIM
vim_obj = self._get_vim_from_vnf(context,
list(vnffg_dict[
'vnf_mapping'].values())[0])
# TODO(trozet): figure out what auth info we actually need to pass
# to the driver. Is it a session, or is full vim obj good enough?
driver_type = vim_obj['type']
try:
fc_id = self._vim_drivers.invoke(driver_type,
'create_flow_classifier',
name=vnffg_dict['name'],
fc=match,
auth_attr=vim_obj['auth_cred'],
symmetrical=sfc['symmetrical'])
sfc_id = self._vim_drivers.invoke(driver_type,
'create_chain',
name=vnffg_dict['name'],
vnfs=sfc['chain'], fc_id=fc_id,
symmetrical=sfc['symmetrical'],
auth_attr=vim_obj['auth_cred'])
except Exception:
with excutils.save_and_reraise_exception():
self.delete_vnffg(context, vnffg_id=vnffg_dict['id'])
super(NfvoPlugin, self)._create_vnffg_post(context, sfc_id, fc_id,
vnffg_dict)
super(NfvoPlugin, self)._create_vnffg_status(context, vnffg_dict)
return vnffg_dict
@log.log
def update_vnffg(self, context, vnffg_id, vnffg):
vnffg_dict = super(NfvoPlugin, self)._update_vnffg_pre(context,
vnffg_id)
new_vnffg = vnffg['vnffg']
LOG.debug(_('vnffg update: %s'), vnffg)
nfp = super(NfvoPlugin, self).get_nfp(context,
vnffg_dict['forwarding_paths'])
sfc = super(NfvoPlugin, self).get_sfc(context, nfp['chain_id'])
fc = super(NfvoPlugin, self).get_classifier(context,
nfp['classifier_id'])
template_db = self._get_resource(context, vnffg_db.VnffgTemplate,
vnffg_dict['vnffgd_id'])
vnf_members = self._get_vnffg_property(template_db,
'constituent_vnfs')
new_vnffg['vnf_mapping'] = super(NfvoPlugin, self)._get_vnf_mapping(
context, new_vnffg.get('vnf_mapping'), vnf_members)
template_id = vnffg_dict['vnffgd_id']
template_db = self._get_resource(context, vnffg_db.VnffgTemplate,
template_id)
# functional attributes that allow update are vnf_mapping,
# and symmetrical. Therefore we need to figure out the new chain if
# it was updated by new vnf_mapping. Symmetrical is handled by driver.
chain = super(NfvoPlugin, self)._create_port_chain(context,
new_vnffg[
'vnf_mapping'],
template_db,
nfp['name'])
LOG.debug(_('chain update: %s'), chain)
sfc['chain'] = chain
sfc['symmetrical'] = new_vnffg['symmetrical']
vim_obj = self._get_vim_from_vnf(context,
list(vnffg_dict[
'vnf_mapping'].values())[0])
driver_type = vim_obj['type']
try:
# we don't support updating the match criteria in first iteration
# so this is essentially a noop. Good to keep for future use
# though.
self._vim_drivers.invoke(driver_type, 'update_flow_classifier',
fc_id=fc['instance_id'], fc=fc['match'],
auth_attr=vim_obj['auth_cred'],
symmetrical=new_vnffg['symmetrical'])
self._vim_drivers.invoke(driver_type, 'update_chain',
vnfs=sfc['chain'],
fc_ids=[fc['instance_id']],
chain_id=sfc['instance_id'],
auth_attr=vim_obj['auth_cred'],
symmetrical=new_vnffg['symmetrical'])
except Exception:
with excutils.save_and_reraise_exception():
vnffg_dict['status'] = constants.ERROR
super(NfvoPlugin, self)._update_vnffg_post(context, vnffg_id,
constants.ERROR)
super(NfvoPlugin, self)._update_vnffg_post(context, vnffg_id,
constants.ACTIVE, new_vnffg)
# update chain
super(NfvoPlugin, self)._update_sfc_post(context, sfc['id'],
constants.ACTIVE, sfc)
# update classifier - this is just updating status until functional
# updates are supported to classifier
super(NfvoPlugin, self)._update_classifier_post(context, fc['id'],
constants.ACTIVE)
return vnffg_dict
@log.log
def delete_vnffg(self, context, vnffg_id):
vnffg_dict = super(NfvoPlugin, self)._delete_vnffg_pre(context,
vnffg_id)
nfp = super(NfvoPlugin, self).get_nfp(context,
vnffg_dict['forwarding_paths'])
sfc = super(NfvoPlugin, self).get_sfc(context, nfp['chain_id'])
fc = super(NfvoPlugin, self).get_classifier(context,
nfp['classifier_id'])
vim_obj = self._get_vim_from_vnf(context,
list(vnffg_dict[
'vnf_mapping'].values())[0])
driver_type = vim_obj['type']
try:
if sfc['instance_id'] is not None:
self._vim_drivers.invoke(driver_type, 'delete_chain',
chain_id=sfc['instance_id'],
auth_attr=vim_obj['auth_cred'])
if fc['instance_id'] is not None:
self._vim_drivers.invoke(driver_type,
'delete_flow_classifier',
fc_id=fc['instance_id'],
auth_attr=vim_obj['auth_cred'])
except Exception:
with excutils.save_and_reraise_exception():
vnffg_dict['status'] = constants.ERROR
super(NfvoPlugin, self)._delete_vnffg_post(context, vnffg_id,
True)
super(NfvoPlugin, self)._delete_vnffg_post(context, vnffg_id, False)
return vnffg_dict
def _get_vim_from_vnf(self, context, vnf_id):
"""Figures out VIM based on a VNF
:param context: SQL Session Context
:param vnf_id: VNF ID
:return: VIM or VIM properties if fields are provided
"""
vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
vim_id = vnfm_plugin.get_vnf(context, vnf_id, fields=['vim_id'])
vim_obj = self.get_vim(context, vim_id['vim_id'], mask_password=False)
vim_auth = vim_obj['auth_cred']
vim_auth['password'] = self._decode_vim_auth(vim_obj['id'],
vim_auth['password'].
encode('utf-8'))
vim_auth['auth_url'] = vim_obj['auth_url']
if vim_obj is None:
raise nfvo.VimFromVnfNotFoundException(vnf_id=vnf_id)
return vim_obj
def _decode_vim_auth(self, vim_id, cred):
"""Decode Vim credentials
Decrypt VIM cred. using Fernet Key
"""
vim_key = self._find_vim_key(vim_id)
f = fernet.Fernet(vim_key)
if not f:
LOG.warning(_('Unable to decode VIM auth'))
raise nfvo.VimNotFoundException('Unable to decode VIM auth key')
return f.decrypt(cred)
@staticmethod
def _find_vim_key(vim_id):
key_file = os.path.join(CONF.vim_keys.openstack, vim_id)
LOG.debug(_('Attempting to open key file for vim id %s'), vim_id)
with open(key_file, 'r') as f:
return f.read()
LOG.warning(_('VIM id invalid or key not found for %s'), vim_id)
def _vim_resource_name_to_id(self, context, resource, name, vnf_id):
"""Converts a VIM resource name to its ID
:param resource: resource type to find (network, subnet, etc)
:param name: name of the resource to find its ID
:param vnf_id: A VNF instance ID that is part of the chain to which
the classifier will apply to
:return: ID of the resource name
"""
vim_obj = self._get_vim_from_vnf(context, vnf_id)
driver_type = vim_obj['type']
return self._vim_drivers.invoke(driver_type,
'get_vim_resource_id',
vim_auth=vim_obj['auth_cred'],
resource_type=resource,
resource_name=name)
@log.log
def create_nsd(self, context, nsd):
nsd_data = nsd['nsd']
template = nsd_data['attributes'].get('nsd')
if isinstance(template, dict):
nsd_data['attributes']['nsd'] = yaml.safe_dump(
template)
LOG.debug(_('nsd %s'), nsd_data)
name = nsd_data['name']
if self._get_by_name(context, ns_db.NSD, name):
raise exceptions.DuplicateResourceName(resource='NSD', name=name)
self._parse_template_input(context, nsd)
return super(NfvoPlugin, self).create_nsd(
context, nsd)
def _parse_template_input(self, context, nsd):
nsd_dict = nsd['nsd']
nsd_yaml = nsd_dict['attributes'].get('nsd')
inner_nsd_dict = yaml.load(nsd_yaml)
nsd['vnfds'] = dict()
LOG.debug(_('nsd_dict: %s'), inner_nsd_dict)
vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
vnfd_imports = inner_nsd_dict['imports']
inner_nsd_dict['imports'] = []
new_files = []
for vnfd_name in vnfd_imports:
vnfd = vnfm_plugin.get_vnfd(context, vnfd_name)
# Copy VNF types and VNF names
sm_dict = yaml.load(vnfd['attributes']['vnfd'])[
'topology_template'][
'substitution_mappings']
nsd['vnfds'][sm_dict['node_type']] = vnfd['name']
# Ugly Hack to validate the child templates
# TODO(tbh): add support in tosca-parser to pass child
# templates as dict
fd, temp_path = mkstemp()
with open(temp_path, 'w') as fp:
fp.write(vnfd['attributes']['vnfd'])
os.close(fd)
new_files.append(temp_path)
inner_nsd_dict['imports'].append(temp_path)
# Prepend the tacker_defs.yaml import file with the full
# path to the file
toscautils.updateimports(inner_nsd_dict)
try:
ToscaTemplate(a_file=False,
yaml_dict_tpl=inner_nsd_dict)
except Exception as e:
LOG.exception(_("tosca-parser error: %s"), str(e))
raise nfvo.ToscaParserFailed(error_msg_details=str(e))
finally:
for file_path in new_files:
os.remove(file_path)
inner_nsd_dict['imports'] = vnfd_imports
if ('description' not in nsd_dict or
nsd_dict['description'] == ''):
nsd_dict['description'] = inner_nsd_dict.get(
'description', '')
if (('name' not in nsd_dict or
not len(nsd_dict['name'])) and
'metadata' in inner_nsd_dict):
nsd_dict['name'] = inner_nsd_dict['metadata'].get(
'template_name', '')
LOG.debug(_('nsd %s'), nsd)
def _get_vnfd_id(self, vnfd_name, onboarded_vnfds):
for vnfd in onboarded_vnfds:
if vnfd_name == vnfd['name']:
return vnfd['id']
@log.log
def create_ns(self, context, ns):
"""Create NS and corresponding VNFs.
:param ns ns dict which contains nsd_id and attributes
This method has 3 steps:
step-1: substitute all get_input params to its corresponding values
step-2: Build params dict for substitution mappings case through which
VNFs will actually substitute their requirements.
step-3: Create mistral workflow and execute the workflow
"""
nsd = self.get_nsd(context, ns['ns']['nsd_id'])
nsd_dict = yaml.load(nsd['attributes']['nsd'])
vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
onboarded_vnfds = vnfm_plugin.get_vnfds(context, [])
region_name = ns.setdefault('placement_attr', {}).get(
'region_name', None)
vim_res = self.vim_client.get_vim(context, ns['ns']['vim_id'],
region_name)
driver_type = vim_res['vim_type']
if not ns['ns']['vim_id']:
ns['ns']['vim_id'] = vim_res['vim_id']
if self._get_by_name(context, ns_db.NS, ns['ns']['name']):
raise exceptions.DuplicateResourceName(resource='NS',
name=ns['ns']['name'])
# Step-1
param_values = ns['ns']['attributes'].get('param_values', {})
if 'get_input' in str(nsd_dict):
self._process_parameterized_input(ns['ns']['attributes'],
nsd_dict)
# Step-2
vnfds = nsd['vnfds']
# vnfd_dict is used while generating workflow
vnfd_dict = dict()
for node_name, node_val in \
iteritems(nsd_dict['topology_template']['node_templates']):
if node_val.get('type') not in vnfds.keys():
continue
vnfd_name = vnfds[node_val.get('type')]
if not vnfd_dict.get(vnfd_name):
vnfd_dict[vnfd_name] = {
'id': self._get_vnfd_id(vnfd_name, onboarded_vnfds),
'instances': [node_name]
}
else:
vnfd_dict[vnfd_name]['instances'].append(node_name)
if not node_val.get('requirements'):
continue
if not param_values.get(vnfd_name):
param_values[vnfd_name] = {}
param_values[vnfd_name]['substitution_mappings'] = dict()
req_dict = dict()
requirements = node_val.get('requirements')
for requirement in requirements:
req_name = list(requirement.keys())[0]
req_val = list(requirement.values())[0]
res_name = req_val + ns['ns']['nsd_id'][:11]
req_dict[req_name] = res_name
if req_val in nsd_dict['topology_template']['node_templates']:
param_values[vnfd_name]['substitution_mappings'][
res_name] = nsd_dict['topology_template'][
'node_templates'][req_val]
param_values[vnfd_name]['substitution_mappings'][
'requirements'] = req_dict
ns['vnfd_details'] = vnfd_dict
# Step-3
kwargs = {'ns': ns, 'params': param_values}
workflow = self._vim_drivers.invoke(driver_type,
'prepare_and_create_workflow',
resource='vnf',
action='create',
vim_auth=vim_res['vim_auth'],
auth_token=context.auth_token,
kwargs=kwargs)
try:
mistral_execution = self._vim_drivers.invoke(
driver_type,
'execute_workflow',
workflow=workflow,
vim_auth=vim_res['vim_auth'],
auth_token=context.auth_token)
except Exception as ex:
raise ex
ns_dict = super(NfvoPlugin, self).create_ns(context, ns)
def _create_ns_wait(self_obj, ns_id, execution_id):
exec_state = "RUNNING"
mistral_retries = MISTRAL_RETRIES
while exec_state == "RUNNING" and mistral_retries > 0:
time.sleep(MISTRAL_RETRY_WAIT)
exec_state = self._vim_drivers.invoke(
driver_type,
'get_execution',
execution_id=execution_id,
vim_auth=vim_res['vim_auth'],
auth_token=context.auth_token).state
LOG.debug(_('status: %s'), exec_state)
if exec_state == 'SUCCESS' or exec_state == 'ERROR':
break
mistral_retries = mistral_retries - 1
error_reason = None
if mistral_retries == 0 and exec_state == 'RUNNING':
error_reason = _("NS creation is not completed within"
" {wait} seconds as creation of mistral"
" exection {mistral} is not completed").format(
wait=MISTRAL_RETRIES * MISTRAL_RETRY_WAIT,
mistral=execution_id)
exec_obj = self._vim_drivers.invoke(driver_type,
'get_execution',
execution_id=execution_id,
vim_auth=vim_res['vim_auth'],
auth_token=context.auth_token)
self._vim_drivers.invoke(driver_type,
'delete_execution',
execution_id=execution_id,
vim_auth=vim_res['vim_auth'],
auth_token=context.auth_token)
self._vim_drivers.invoke(driver_type,
'delete_workflow',
workflow_id=workflow['id'],
vim_auth=vim_res['vim_auth'],
auth_token=context.auth_token)
super(NfvoPlugin, self).create_ns_post(context, ns_id, exec_obj,
vnfd_dict, error_reason)
self.spawn_n(_create_ns_wait, self, ns_dict['id'],
mistral_execution.id)
return ns_dict
@log.log
def _update_params(self, original, paramvalues):
for key, value in iteritems(original):
if not isinstance(value, dict) or 'get_input' not in str(value):
pass
elif isinstance(value, dict):
if 'get_input' in value:
if value['get_input'] in paramvalues:
original[key] = paramvalues[value['get_input']]
else:
LOG.debug('Key missing Value: %s', key)
raise cs.InputValuesMissing(key=key)
else:
self._update_params(value, paramvalues)
@log.log
def _process_parameterized_input(self, attrs, nsd_dict):
param_vattrs_dict = attrs.pop('param_values', None)
if param_vattrs_dict:
for node in \
nsd_dict['topology_template']['node_templates'].values():
if 'get_input' in str(node):
self._update_params(node, param_vattrs_dict['nsd'])
else:
raise cs.ParamYAMLInputMissing()
@log.log
def delete_ns(self, context, ns_id):
ns = super(NfvoPlugin, self).get_ns(context, ns_id)
vim_res = self.vim_client.get_vim(context, ns['vim_id'])
driver_type = vim_res['vim_type']
workflow = self._vim_drivers.invoke(driver_type,
'prepare_and_create_workflow',
resource='vnf',
action='delete',
vim_auth=vim_res['vim_auth'],
auth_token=context.auth_token,
kwargs={'ns': ns})
try:
mistral_execution = self._vim_drivers.invoke(
driver_type,
'execute_workflow',
workflow=workflow,
vim_auth=vim_res['vim_auth'],
auth_token=context.auth_token)
except Exception as ex:
raise ex
super(NfvoPlugin, self).delete_ns(context, ns_id)
def _delete_ns_wait(ns_id, execution_id):
exec_state = "RUNNING"
mistral_retries = MISTRAL_RETRIES
while exec_state == "RUNNING" and mistral_retries > 0:
time.sleep(MISTRAL_RETRY_WAIT)
exec_state = self._vim_drivers.invoke(
driver_type,
'get_execution',
execution_id=execution_id,
vim_auth=vim_res['vim_auth'],
auth_token=context.auth_token).state
LOG.debug(_('status: %s'), exec_state)
if exec_state == 'SUCCESS' or exec_state == 'ERROR':
break
mistral_retries -= 1
error_reason = None
if mistral_retries == 0 and exec_state == 'RUNNING':
error_reason = _("NS deletion is not completed within"
" {wait} seconds as deletion of mistral"
" exection {mistral} is not completed").format(
wait=MISTRAL_RETRIES * MISTRAL_RETRY_WAIT,
mistral=execution_id)
exec_obj = self._vim_drivers.invoke(driver_type,
'get_execution',
execution_id=execution_id,
vim_auth=vim_res['vim_auth'],
auth_token=context.auth_token)
self._vim_drivers.invoke(driver_type,
'delete_execution',
execution_id=execution_id,
vim_auth=vim_res['vim_auth'],
auth_token=context.auth_token)
self._vim_drivers.invoke(driver_type,
'delete_workflow',
workflow_id=workflow['id'],
vim_auth=vim_res['vim_auth'],
auth_token=context.auth_token)
super(NfvoPlugin, self).delete_ns_post(context, ns_id, exec_obj,
error_reason)
self.spawn_n(_delete_ns_wait, ns['id'], mistral_execution.id)
return ns['id']