
When creating a duplicated subscription, according to spec SOL003, the api should return 303 See Other instead of 201 Created. To fix this error, the following things is done in this patch: 1.Fix the error when the database virtual field extracts the value from json in table `vnf_lcm_filters`. 2.Fix the error of sql query statement when querying subscription. 3.The attribute VnfProductsFromProviders should be an array, not an object. Fix this error in UT, FT and schemas. 4.Fix typos when writing "object" as "objects" in dict "_versions" in vnf_lcm schemas. 5.Rewrite the throwing and catching of SeeOther exception of this api to fix the following errors: SeeOther should be thrown instead of Exception; the return body should be empty instead of ProblemDetail; the return header should contain location instead of link. Closes-Bug: #1933169 Change-Id: I51c198d60001dba94dd369f495cecef526e79800
1672 lines
69 KiB
Python
1672 lines
69 KiB
Python
# Copyright (C) 2020 NTT DATA
|
|
# 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 copy
|
|
import datetime
|
|
import requests
|
|
import tacker.conf
|
|
import webob
|
|
|
|
from oslo_db.exception import DBDuplicateEntry
|
|
from oslo_log import log as logging
|
|
from oslo_serialization import jsonutils
|
|
from oslo_utils import encodeutils
|
|
from oslo_utils import excutils
|
|
from oslo_utils import timeutils
|
|
from oslo_utils import uuidutils
|
|
from sqlalchemy import exc as sqlexc
|
|
from toscaparser import tosca_template
|
|
|
|
import ast
|
|
import functools
|
|
import json
|
|
import re
|
|
import traceback
|
|
|
|
from http import client as http_client
|
|
from urllib import parse
|
|
|
|
from tacker._i18n import _
|
|
from tacker.api import api_common
|
|
from tacker.api.schemas import vnf_lcm
|
|
from tacker.api import validation
|
|
from tacker.api.views import vnf_lcm as vnf_lcm_view
|
|
from tacker.api.views import vnf_lcm_op_occs as vnf_op_occs_view
|
|
from tacker.api.views import vnf_subscriptions as vnf_subscription_view
|
|
from tacker.api.vnflcm.v1 import sync_resource
|
|
from tacker.common import exceptions
|
|
from tacker.common import utils
|
|
from tacker.conductor.conductorrpc import vnf_lcm_rpc
|
|
from tacker.db.vnfm import vnfm_db
|
|
from tacker.extensions import nfvo
|
|
from tacker.extensions import vnfm
|
|
from tacker import manager
|
|
from tacker import objects
|
|
from tacker.objects import fields
|
|
from tacker.objects.fields import ErrorPoint as EP
|
|
from tacker.objects import vnf_lcm_op_occs as vnf_lcm_op_occs_obj
|
|
from tacker.objects import vnf_lcm_subscriptions as subscription_obj
|
|
from tacker.plugins.common import constants
|
|
from tacker.policies import vnf_lcm as vnf_lcm_policies
|
|
from tacker.tosca import utils as toscautils
|
|
from tacker.vnflcm import utils as vnflcm_utils
|
|
import tacker.vnfm.nfvo_client as nfvo_client
|
|
from tacker.vnfm import vim_client
|
|
from tacker import wsgi
|
|
|
|
|
|
CONF = tacker.conf.CONF
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
def check_vnf_state(action, instantiation_state=None, task_state=(None,)):
|
|
"""Decorator to check vnf states are valid for particular action.
|
|
|
|
If the vnf is in the wrong state, it will raise conflict exception.
|
|
"""
|
|
|
|
if instantiation_state is not None and not \
|
|
isinstance(instantiation_state, set):
|
|
instantiation_state = set(instantiation_state)
|
|
if task_state is not None and not isinstance(task_state, set):
|
|
task_state = set(task_state)
|
|
|
|
def outer(f):
|
|
@functools.wraps(f)
|
|
def inner(self, context, vnf_instance, *args, **kw):
|
|
if instantiation_state is not None and \
|
|
vnf_instance.instantiation_state not in \
|
|
instantiation_state:
|
|
raise exceptions.VnfInstanceConflictState(
|
|
attr='instantiation_state',
|
|
uuid=vnf_instance.id,
|
|
state=vnf_instance.instantiation_state,
|
|
action=action)
|
|
if (task_state is not None and
|
|
vnf_instance.task_state not in task_state):
|
|
raise exceptions.VnfInstanceConflictState(
|
|
attr='task_state',
|
|
uuid=vnf_instance.id,
|
|
state=vnf_instance.task_state,
|
|
action=action)
|
|
return f(self, context, vnf_instance, *args, **kw)
|
|
return inner
|
|
return outer
|
|
|
|
|
|
def check_vnf_status(action, status=None):
|
|
"""Decorator to check vnf status are valid for particular action.
|
|
|
|
If the vnf is in the wrong state, it will raise conflict exception.
|
|
"""
|
|
|
|
if status is not None and not isinstance(status, set):
|
|
status = set(status)
|
|
|
|
def outer(f):
|
|
@functools.wraps(f)
|
|
def inner(self, context, vnf_instance, vnf, *args, **kw):
|
|
if status is not None and \
|
|
vnf['status'] not in \
|
|
status:
|
|
raise exceptions.VnfConflictState(
|
|
attr='status',
|
|
uuid=vnf['id'],
|
|
state=vnf['status'],
|
|
action=action)
|
|
return f(self, context, vnf_instance, vnf, *args, **kw)
|
|
return inner
|
|
return outer
|
|
|
|
|
|
def check_vnf_status_and_error_point(action, status=None):
|
|
"""Decorator to check vnf status are valid for particular action.
|
|
|
|
If the vnf has the wrong status with the wrong error point,
|
|
it will raise conflict exception.
|
|
"""
|
|
|
|
if status is not None and not isinstance(status, set):
|
|
status = set(status)
|
|
|
|
def outer(f):
|
|
@functools.wraps(f)
|
|
def inner(self, context, vnf_instance, vnf, *args, **kw):
|
|
vnf['current_error_point'] = fields.ErrorPoint.INITIAL
|
|
|
|
if 'before_error_point' not in vnf:
|
|
vnf['before_error_point'] = fields.ErrorPoint.INITIAL
|
|
|
|
if status is not None and vnf['status'] not in status and \
|
|
vnf['before_error_point'] == fields.ErrorPoint.INITIAL:
|
|
raise exceptions.VnfConflictStateWithErrorPoint(
|
|
uuid=vnf['id'],
|
|
state=vnf['status'],
|
|
action=action,
|
|
error_point=vnf['before_error_point'])
|
|
return f(self, context, vnf_instance, vnf, *args, **kw)
|
|
return inner
|
|
return outer
|
|
|
|
|
|
class VnfLcmController(wsgi.Controller):
|
|
|
|
notification_type_list = ['VnfLcmOperationOccurrenceNotification',
|
|
'VnfIdentifierCreationNotification',
|
|
'VnfIdentifierDeletionNotification']
|
|
operation_type_list = ['INSTANTIATE',
|
|
'SCALE',
|
|
'SCALE_TO_LEVEL',
|
|
'CHANGE_FLAVOUR',
|
|
'TERMINATE',
|
|
'HEAL',
|
|
'OPERATE',
|
|
'CHANGE_EXT_CONN',
|
|
'MODIFY_INFO']
|
|
operation_state_list = ['STARTING',
|
|
'PROCESSING',
|
|
'COMPLETED',
|
|
'FAILED_TEMP',
|
|
'FAILED',
|
|
'ROLLING_BACK',
|
|
'ROLLED_BACK']
|
|
|
|
_view_builder_class = vnf_lcm_view.ViewBuilder
|
|
|
|
def __init__(self):
|
|
super(VnfLcmController, self).__init__()
|
|
self.rpc_api = vnf_lcm_rpc.VNFLcmRPCAPI()
|
|
self._vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
|
|
self._view_builder_op_occ = vnf_op_occs_view.ViewBuilder()
|
|
self._view_builder_subscription = vnf_subscription_view.ViewBuilder()
|
|
|
|
def _get_vnf_instance_href(self, vnf_instance):
|
|
return '{}vnflcm/v1/vnf_instances/{}'.format(
|
|
CONF.vnf_lcm.endpoint_url, vnf_instance.id)
|
|
|
|
def _get_vnf_lcm_op_occs_href(self, vnf_lcm_op_occs_id):
|
|
return '{}vnflcm/v1/vnf_lcm_op_occs/{}'.format(
|
|
CONF.vnf_lcm.endpoint_url, vnf_lcm_op_occs_id)
|
|
|
|
def _get_vnf_instance(self, context, id):
|
|
# check if id is of type uuid format
|
|
if not uuidutils.is_uuid_like(id):
|
|
msg = _("Can not find requested vnf instance: %s") % id
|
|
raise webob.exc.HTTPNotFound(explanation=msg)
|
|
|
|
try:
|
|
vnf_instance = objects.VnfInstance.get_by_id(context, id)
|
|
except exceptions.VnfInstanceNotFound:
|
|
msg = _("Can not find requested vnf instance: %s") % id
|
|
raise webob.exc.HTTPNotFound(explanation=msg)
|
|
|
|
return vnf_instance
|
|
|
|
def _get_vnf(self, context, id):
|
|
# check if id is of type uuid format
|
|
if not uuidutils.is_uuid_like(id):
|
|
msg = _("Can not find requested vnf: %s") % id
|
|
raise webob.exc.HTTPNotFound(explanation=msg)
|
|
|
|
try:
|
|
vnf = self._vnfm_plugin.get_vnf(context, id)
|
|
except vnfm.VNFNotFound:
|
|
msg = _("Can not find requested vnf: %s") % id
|
|
raise webob.exc.HTTPNotFound(explanation=msg)
|
|
except Exception as exc:
|
|
msg = _("Encountered error while fetching vnf: %s") % id
|
|
LOG.debug("{}: {}".format(msg, str(exc)))
|
|
raise webob.exc.HTTPInternalServerError(explanation=str(exc))
|
|
return vnf
|
|
|
|
def _validate_flavour_and_inst_level(self, context, req_body,
|
|
vnf_instance):
|
|
inst_levels = {}
|
|
flavour_list = []
|
|
vnf_package_vnfd = objects.VnfPackageVnfd.get_by_id(
|
|
context, vnf_instance.vnfd_id)
|
|
vnf_package = objects.VnfPackage.get_by_id(
|
|
context, vnf_package_vnfd.package_uuid)
|
|
deployment_flavour = vnf_package.vnf_deployment_flavours
|
|
for dep_flavour in deployment_flavour.objects:
|
|
flavour_list.append(dep_flavour.flavour_id)
|
|
if dep_flavour.instantiation_levels:
|
|
inst_levels.update({
|
|
dep_flavour.flavour_id: dep_flavour.instantiation_levels})
|
|
|
|
if req_body['flavour_id'] not in flavour_list:
|
|
raise exceptions.FlavourNotFound(flavour_id=req_body['flavour_id'])
|
|
|
|
req_inst_level_id = req_body.get('instantiation_level_id')
|
|
if req_inst_level_id is None:
|
|
return
|
|
|
|
if not inst_levels:
|
|
raise exceptions.InstantiationLevelNotFound(
|
|
inst_level_id=req_inst_level_id)
|
|
|
|
for flavour, inst_level in inst_levels.items():
|
|
if flavour != req_body['flavour_id']:
|
|
continue
|
|
|
|
if req_inst_level_id in inst_level.get('levels').keys():
|
|
# Found instantiation level
|
|
return
|
|
|
|
raise exceptions.InstantiationLevelNotFound(
|
|
inst_level_id=req_body['instantiation_level_id'])
|
|
|
|
def _validate_vim_connection(self, context, instantiate_vnf_request):
|
|
if instantiate_vnf_request.vim_connection_info:
|
|
vim_id = instantiate_vnf_request.vim_connection_info[0].vim_id
|
|
access_info = \
|
|
instantiate_vnf_request.vim_connection_info[0].access_info
|
|
if access_info:
|
|
region_name = access_info.get('region')
|
|
else:
|
|
region_name = None
|
|
else:
|
|
vim_id = None
|
|
region_name = None
|
|
|
|
vim_client_obj = vim_client.VimClient()
|
|
|
|
try:
|
|
vim_client_obj.get_vim(
|
|
context, vim_id, region_name=region_name)
|
|
except nfvo.VimDefaultNotDefined as exp:
|
|
raise webob.exc.HTTPBadRequest(explanation=exp.message)
|
|
except nfvo.VimNotFoundException:
|
|
msg = _("VimConnection id is not found: %s")\
|
|
% vim_id
|
|
raise webob.exc.HTTPBadRequest(explanation=msg)
|
|
except nfvo.VimRegionNotFoundException:
|
|
msg = _("Region not found for the VimConnection: %s")\
|
|
% vim_id
|
|
raise webob.exc.HTTPBadRequest(explanation=msg)
|
|
|
|
def _notification_process(
|
|
self, context, vnf_instance, lcm_operation, request, body,
|
|
vnf_lcm_op_occs=None,
|
|
operation_state=fields.LcmOccsOperationState.STARTING,
|
|
notification_status=fields.LcmOccsNotificationStatus.START,
|
|
affected_resources=None, is_auto=False):
|
|
LOG.debug('START NOTIFICATION PROCESS')
|
|
vnf_url = self._get_vnf_instance_href(vnf_instance)
|
|
|
|
notification = {
|
|
'notificationType':
|
|
fields.LcmOccsNotificationType.VNF_OP_OCC_NOTIFICATION,
|
|
'notificationStatus': notification_status,
|
|
'operationState': operation_state,
|
|
'vnfInstanceId': vnf_instance.id,
|
|
'operation': lcm_operation,
|
|
'isAutomaticInvocation': is_auto,
|
|
'_links': {
|
|
'vnfInstance': {
|
|
'href': vnf_url},
|
|
'vnfLcmOpOcc': {}}}
|
|
|
|
if operation_state is fields.LcmOccsOperationState.FAILED:
|
|
vnf_lcm_op_occs_id = vnf_lcm_op_occs.id
|
|
|
|
notification['affectedVnfcs'] = affected_resources.get(
|
|
'affectedVnfcs', [])
|
|
notification['affectedVirtualLinks'] = affected_resources.get(
|
|
'affectedVirtualLinks', [])
|
|
notification['affectedVirtualStorages'] = affected_resources.get(
|
|
'affectedVirtualStorages', [])
|
|
notification['error'] = str(vnf_lcm_op_occs.error)
|
|
|
|
else:
|
|
vnf_lcm_op_occs_id = uuidutils.generate_uuid()
|
|
error_point = 0
|
|
operation_params = jsonutils.dumps(body)
|
|
try:
|
|
# call create lcm op occs here
|
|
LOG.debug('Create LCM OP OCCS')
|
|
vnf_lcm_op_occs = objects.VnfLcmOpOcc(
|
|
context=context,
|
|
id=vnf_lcm_op_occs_id,
|
|
operation_state=operation_state,
|
|
start_time=timeutils.utcnow(),
|
|
state_entered_time=timeutils.utcnow(),
|
|
vnf_instance_id=vnf_instance.id,
|
|
is_cancel_pending=is_auto,
|
|
operation=lcm_operation,
|
|
is_automatic_invocation=is_auto,
|
|
operation_params=operation_params,
|
|
error_point=error_point)
|
|
vnf_lcm_op_occs.create()
|
|
except Exception:
|
|
msg = _("Failed to create LCM occurrence")
|
|
raise webob.exc.HTTPInternalServerError(explanation=msg)
|
|
|
|
vnf_lcm_url = self._get_vnf_lcm_op_occs_href(vnf_lcm_op_occs_id)
|
|
notification['vnfLcmOpOccId'] = vnf_lcm_op_occs_id
|
|
notification['_links']['vnfLcmOpOcc']['href'] = vnf_lcm_url
|
|
# call send notification
|
|
try:
|
|
self.rpc_api.send_notification(context, notification)
|
|
except Exception as ex:
|
|
LOG.error(
|
|
"Encoutered problem sending notification {}".format(
|
|
encodeutils.exception_to_unicode(ex)))
|
|
|
|
return vnf_lcm_op_occs_id
|
|
|
|
def _create_vnf(self, context, vnf_instance, default_vim, attributes=None):
|
|
tenant_id = vnf_instance.tenant_id
|
|
vnfd_id = vnf_instance.vnfd_id
|
|
name = vnf_instance.vnf_instance_name
|
|
description = vnf_instance.vnf_instance_description
|
|
vnf_id = vnf_instance.id
|
|
vim_id = default_vim.get('vim_id')
|
|
placement_attr = default_vim.get('placement_attr', {})
|
|
|
|
try:
|
|
with context.session.begin(subtransactions=True):
|
|
vnf_db = vnfm_db.VNF(id=vnf_id,
|
|
tenant_id=tenant_id,
|
|
name=name,
|
|
description=description,
|
|
instance_id=None,
|
|
vnfd_id=vnfd_id,
|
|
vim_id=vim_id,
|
|
placement_attr=placement_attr,
|
|
status=constants.INACTIVE,
|
|
error_reason=None,
|
|
deleted_at=datetime.datetime.min)
|
|
context.session.add(vnf_db)
|
|
for key, value in attributes.items():
|
|
arg = vnfm_db.VNFAttribute(
|
|
id=uuidutils.generate_uuid(), vnf_id=vnf_id,
|
|
key=key, value=str(value))
|
|
context.session.add(arg)
|
|
except DBDuplicateEntry as e:
|
|
raise exceptions.DuplicateEntity(
|
|
_type="vnf",
|
|
entry=e.columns)
|
|
|
|
def _destroy_vnf(self, context, vnf_instance):
|
|
with context.session.begin(subtransactions=True):
|
|
if vnf_instance.id:
|
|
now = timeutils.utcnow()
|
|
updated_values = {'deleted_at': now, 'status':
|
|
'PENDING_DELETE'}
|
|
context.session.query(vnfm_db.VNFAttribute).filter_by(
|
|
vnf_id=vnf_instance.id).delete()
|
|
context.session.query(vnfm_db.VNF).filter_by(
|
|
id=vnf_instance.id).update(updated_values)
|
|
|
|
def _update_package_usage_state(self, context, vnf_package):
|
|
"""Update vnf package usage state to IN_USE/NOT_IN_USE
|
|
|
|
If vnf package is not used by any of the vnf instances, it's usage
|
|
state should be set to NOT_IN_USE otherwise it should be set to
|
|
IN_USE.
|
|
"""
|
|
result = vnf_package.is_package_in_use(context)
|
|
if result:
|
|
vnf_package.usage_state = fields.PackageUsageStateType.IN_USE
|
|
else:
|
|
vnf_package.usage_state = fields.PackageUsageStateType.NOT_IN_USE
|
|
|
|
vnf_package.save()
|
|
|
|
@wsgi.response(http_client.CREATED)
|
|
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN))
|
|
@validation.schema(vnf_lcm.create)
|
|
def create(self, request, body):
|
|
context = request.environ['tacker.context']
|
|
context.can(vnf_lcm_policies.VNFLCM % 'create')
|
|
try:
|
|
req_body = utils.convert_camelcase_to_snakecase(body)
|
|
vnfd_id = req_body.get('vnfd_id')
|
|
vnfd = objects.VnfPackageVnfd.get_by_id(request.context,
|
|
vnfd_id)
|
|
except exceptions.VnfPackageVnfdNotFound:
|
|
vnf_package_info = self._find_vnf_package_info('vnfdId',
|
|
vnfd_id)
|
|
if not vnf_package_info:
|
|
msg = (
|
|
_("Can not find requested to NFVO, \
|
|
vnf package info: vnfdId=[%s]") %
|
|
vnfd_id)
|
|
return self._make_problem_detail(
|
|
msg, 404, title='Not Found')
|
|
|
|
vnfd = sync_resource.SyncVnfPackage.create_package(
|
|
context,
|
|
vnf_package_info)
|
|
if not vnfd:
|
|
msg = (
|
|
_("Can not find requested to NFVO, \
|
|
vnf package vnfd: %s") %
|
|
vnfd_id)
|
|
return self._make_problem_detail(
|
|
msg, 500, 'Internal Server Error')
|
|
try:
|
|
# get default vim information
|
|
vim_client_obj = vim_client.VimClient()
|
|
default_vim = vim_client_obj.get_vim(context)
|
|
|
|
vnf_instance = objects.VnfInstance(
|
|
context=request.context,
|
|
vnf_instance_name=req_body.get('vnf_instance_name'),
|
|
vnf_instance_description=req_body.get(
|
|
'vnf_instance_description'),
|
|
vnfd_id=vnfd_id,
|
|
instantiation_state=fields.VnfInstanceState.NOT_INSTANTIATED,
|
|
vnf_provider=vnfd.vnf_provider,
|
|
vnf_product_name=vnfd.vnf_product_name,
|
|
vnf_software_version=vnfd.vnf_software_version,
|
|
vnfd_version=vnfd.vnfd_version,
|
|
vnf_pkg_id=vnfd.package_uuid,
|
|
tenant_id=request.context.project_id,
|
|
vnf_metadata=req_body.get('metadata'))
|
|
|
|
try:
|
|
vnf_instance.create()
|
|
|
|
# create entry to 'vnf' table and 'vnf_attribute' table
|
|
attributes = {'placement_attr': default_vim.
|
|
get('placement_attr', {})}
|
|
self._create_vnf(context, vnf_instance,
|
|
default_vim, attributes)
|
|
# get vnf package
|
|
vnf_package = objects.VnfPackage.get_by_id(context,
|
|
vnfd.package_uuid, expected_attrs=['vnfd'])
|
|
# Update VNF Package to IN_USE
|
|
self._update_package_usage_state(context, vnf_package)
|
|
except Exception:
|
|
with excutils.save_and_reraise_exception():
|
|
# roll back db changes
|
|
self._destroy_vnf(context, vnf_instance)
|
|
vnf_instance.destroy(context)
|
|
if 'vnf_package' not in locals():
|
|
LOG.error("vnf_package is not assigned")
|
|
else:
|
|
self._update_package_usage_state(context, vnf_package)
|
|
|
|
# create notification data
|
|
notification = {
|
|
'notificationType':
|
|
fields.LcmOccsNotificationType.VNF_ID_CREATION_NOTIFICATION,
|
|
'vnfInstanceId': vnf_instance.id,
|
|
'links': {
|
|
'vnfInstance': {
|
|
'href': self._get_vnf_instance_href(vnf_instance)}}}
|
|
|
|
# call send_notification
|
|
self.rpc_api.send_notification(context, notification)
|
|
|
|
result = self._view_builder.create(vnf_instance)
|
|
headers = {"location": self._get_vnf_instance_href(vnf_instance)}
|
|
return wsgi.ResponseObject(result, headers=headers)
|
|
|
|
except nfvo.VimDefaultNotDefined as exc:
|
|
raise webob.exc.HTTPBadRequest(explanation=str(exc))
|
|
except(sqlexc.SQLAlchemyError, Exception) as exc:
|
|
raise webob.exc.HTTPInternalServerError(
|
|
explanation=str(exc))
|
|
except webob.exc.HTTPNotFound as e:
|
|
return self._make_problem_detail(str(e), 404,
|
|
'Not Found')
|
|
except webob.exc.HTTPInternalServerError as e:
|
|
return self._make_problem_detail(str(e), 500,
|
|
'Internal Server Error')
|
|
except Exception as e:
|
|
return self._make_problem_detail(str(e), 500,
|
|
'Internal Server Error')
|
|
|
|
def _find_vnf_package_info(self, filter_key, filter_val):
|
|
try:
|
|
vnf_package_info_res = \
|
|
nfvo_client.VnfPackageRequest.index(params={
|
|
"filter":
|
|
"(eq,{},{})".format(filter_key, filter_val)
|
|
})
|
|
except requests.exceptions.RequestException as e:
|
|
LOG.exception(e)
|
|
return None
|
|
|
|
if not vnf_package_info_res.ok:
|
|
return None
|
|
|
|
vnf_package_info = vnf_package_info_res.json()
|
|
if (not vnf_package_info or len(vnf_package_info) == 0):
|
|
return None
|
|
|
|
return vnf_package_info[0]
|
|
|
|
@wsgi.response(http_client.OK)
|
|
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND))
|
|
def show(self, request, id):
|
|
context = request.environ['tacker.context']
|
|
context.can(vnf_lcm_policies.VNFLCM % 'show')
|
|
vnf_instance = self._get_vnf_instance(context, id)
|
|
|
|
return self._view_builder.show(vnf_instance)
|
|
|
|
@wsgi.response(http_client.OK)
|
|
def api_versions(self, request):
|
|
return {'uriPrefix': '/vnflcm/v1',
|
|
'apiVersions': [{'version': '1.3.0', 'isDeprecated': False}]}
|
|
|
|
@wsgi.response(http_client.OK)
|
|
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.BAD_REQUEST))
|
|
@api_common.validate_supported_params({'filter'})
|
|
def index(self, request):
|
|
context = request.environ['tacker.context']
|
|
context.can(vnf_lcm_policies.VNFLCM % 'index')
|
|
|
|
filters = request.GET.get('filter')
|
|
filters = self._view_builder.validate_filter(filters)
|
|
|
|
vnf_instances = objects.VnfInstanceList.get_by_filters(
|
|
request.context, filters=filters)
|
|
|
|
return self._view_builder.index(vnf_instances)
|
|
|
|
@check_vnf_state(action="delete",
|
|
instantiation_state=[fields.VnfInstanceState.NOT_INSTANTIATED],
|
|
task_state=[None])
|
|
@check_vnf_status(action="delete",
|
|
status=[constants.INACTIVE])
|
|
def _delete(self, context, vnf_instance, vnf):
|
|
vnf_package_vnfd = objects.VnfPackageVnfd.get_by_id(context,
|
|
vnf_instance.vnfd_id)
|
|
vnf_instance.destroy(context)
|
|
self._destroy_vnf(context, vnf_instance)
|
|
vnf_package = objects.VnfPackage.get_by_id(context,
|
|
vnf_package_vnfd.package_uuid, expected_attrs=['vnfd'])
|
|
self._update_package_usage_state(context, vnf_package)
|
|
|
|
@wsgi.response(http_client.NO_CONTENT)
|
|
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND,
|
|
http_client.CONFLICT))
|
|
def delete(self, request, id):
|
|
context = request.environ['tacker.context']
|
|
|
|
vnf_instance = self._get_vnf_instance(context, id)
|
|
vnf = self._get_vnf(context, id)
|
|
self._delete(context, vnf_instance, vnf)
|
|
|
|
notification = {
|
|
"notificationType": "VnfIdentifierDeletionNotification",
|
|
"vnfInstanceId": vnf_instance.id,
|
|
"links": {
|
|
"vnfInstance":
|
|
"href:{apiRoot}/vnflcm/v1/vnf_instances/{vnfInstanceId}"}}
|
|
# send notification
|
|
self.rpc_api.send_notification(context, notification)
|
|
|
|
@check_vnf_state(action="instantiate",
|
|
instantiation_state=[fields.VnfInstanceState.NOT_INSTANTIATED],
|
|
task_state=[None])
|
|
@check_vnf_status_and_error_point(action="instantiate",
|
|
status=[constants.INACTIVE])
|
|
def _instantiate(self, context, vnf_instance, vnf, request_body):
|
|
req_body = utils.convert_camelcase_to_snakecase(request_body)
|
|
|
|
vnf_lcm_op_occs_id = vnf.get('vnf_lcm_op_occs_id')
|
|
|
|
try:
|
|
self._validate_flavour_and_inst_level(context, req_body,
|
|
vnf_instance)
|
|
except exceptions.NotFound as ex:
|
|
raise webob.exc.HTTPBadRequest(explanation=str(ex))
|
|
|
|
instantiate_vnf_request = \
|
|
objects.InstantiateVnfRequest.obj_from_primitive(
|
|
req_body, context=context)
|
|
|
|
# validate the vim connection id passed through request is exist or not
|
|
self._validate_vim_connection(context, instantiate_vnf_request)
|
|
|
|
vnf_instance.task_state = fields.VnfInstanceTaskState.INSTANTIATING
|
|
vnf_instance.save()
|
|
|
|
# lcm op process
|
|
if vnf['before_error_point'] == fields.ErrorPoint.INITIAL:
|
|
vnf_lcm_op_occs_id = \
|
|
self._notification_process(context, vnf_instance,
|
|
fields.LcmOccsOperationType.INSTANTIATE,
|
|
instantiate_vnf_request, request_body)
|
|
|
|
if vnf_lcm_op_occs_id:
|
|
self.rpc_api.instantiate(context, vnf_instance, vnf,
|
|
instantiate_vnf_request,
|
|
vnf_lcm_op_occs_id)
|
|
# set response header
|
|
res = webob.Response()
|
|
res.status_int = 202
|
|
location = ('Location',
|
|
self._get_vnf_lcm_op_occs_href(vnf_lcm_op_occs_id))
|
|
res.headerlist.append(location)
|
|
return res
|
|
|
|
@wsgi.response(http_client.ACCEPTED)
|
|
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND,
|
|
http_client.CONFLICT, http_client.BAD_REQUEST))
|
|
@validation.schema(vnf_lcm.instantiate)
|
|
def instantiate(self, request, id, body):
|
|
context = request.environ['tacker.context']
|
|
context.can(vnf_lcm_policies.VNFLCM % 'instantiate')
|
|
|
|
vnf = self._get_vnf(context, id)
|
|
vnf_instance = self._get_vnf_instance(context, id)
|
|
|
|
return self._instantiate(context, vnf_instance, vnf, body)
|
|
|
|
@check_vnf_state(action="terminate",
|
|
instantiation_state=[
|
|
fields.VnfInstanceState.INSTANTIATED,
|
|
fields.VnfInstanceState.NOT_INSTANTIATED],
|
|
task_state=[None])
|
|
@check_vnf_status_and_error_point(action="terminate",
|
|
status=[constants.ACTIVE])
|
|
def _terminate(self, context, vnf_instance, vnf, request_body):
|
|
req_body = utils.convert_camelcase_to_snakecase(request_body)
|
|
terminate_vnf_req = \
|
|
objects.TerminateVnfRequest.obj_from_primitive(
|
|
req_body, context=context)
|
|
|
|
vnf_instance.task_state = fields.VnfInstanceTaskState.TERMINATING
|
|
vnf_instance.save()
|
|
|
|
vnf_lcm_op_occs_id = vnf.get('vnf_lcm_op_occs_id')
|
|
|
|
# lcm op process
|
|
if vnf['before_error_point'] == fields.ErrorPoint.INITIAL:
|
|
vnf_lcm_op_occs_id = \
|
|
self._notification_process(context, vnf_instance,
|
|
fields.LcmOccsOperationType.TERMINATE,
|
|
terminate_vnf_req, request_body)
|
|
|
|
if vnf_lcm_op_occs_id:
|
|
self.rpc_api.terminate(context, vnf_instance, vnf,
|
|
terminate_vnf_req, vnf_lcm_op_occs_id)
|
|
# set response header
|
|
res = webob.Response()
|
|
res.status_int = 202
|
|
location = ('Location',
|
|
self._get_vnf_lcm_op_occs_href(vnf_lcm_op_occs_id))
|
|
res.headerlist.append(location)
|
|
return res
|
|
|
|
@wsgi.response(http_client.ACCEPTED)
|
|
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN,
|
|
http_client.NOT_FOUND, http_client.CONFLICT))
|
|
@validation.schema(vnf_lcm.terminate)
|
|
def terminate(self, request, id, body):
|
|
context = request.environ['tacker.context']
|
|
context.can(vnf_lcm_policies.VNFLCM % 'terminate')
|
|
|
|
vnf = self._get_vnf(context, id)
|
|
vnf_instance = self._get_vnf_instance(context, id)
|
|
return self._terminate(context, vnf_instance, vnf, body)
|
|
|
|
@check_vnf_status_and_error_point(action="heal",
|
|
status=[constants.ACTIVE])
|
|
def _heal(self, context, vnf_instance, vnf_dict, request_body):
|
|
req_body = utils.convert_camelcase_to_snakecase(request_body)
|
|
heal_vnf_request = objects.HealVnfRequest(context=context, **req_body)
|
|
inst_vnf_info = vnf_instance.instantiated_vnf_info
|
|
vnfc_resource_info_ids = [
|
|
vnfc_resource_info.id for vnfc_resource_info in
|
|
inst_vnf_info.vnfc_resource_info]
|
|
|
|
vnf_lcm_op_occs_id = vnf_dict.get('vnf_lcm_op_occs_id')
|
|
|
|
for vnfc_id in heal_vnf_request.vnfc_instance_id:
|
|
# check if vnfc_id exists in vnfc_resource_info
|
|
if vnfc_id not in vnfc_resource_info_ids:
|
|
msg = _("Vnfc id %(vnfc_id)s not present in vnf instance "
|
|
"%(id)s")
|
|
raise webob.exc.HTTPBadRequest(
|
|
explanation=msg % {"vnfc_id": vnfc_id,
|
|
"id": vnf_instance.id})
|
|
|
|
vnf_instance.task_state = fields.VnfInstanceTaskState.HEALING
|
|
vnf_instance.save()
|
|
|
|
# call notification process
|
|
if vnf_dict['before_error_point'] == fields.ErrorPoint.INITIAL:
|
|
vnf_lcm_op_occs_id = \
|
|
self._notification_process(context, vnf_instance,
|
|
fields.LcmOccsOperationType.HEAL,
|
|
heal_vnf_request, request_body)
|
|
|
|
if vnf_lcm_op_occs_id:
|
|
self.rpc_api.heal(context, vnf_instance, vnf_dict,
|
|
heal_vnf_request, vnf_lcm_op_occs_id)
|
|
|
|
# set response header
|
|
res = webob.Response()
|
|
res.status_int = 202
|
|
location = ('Location',
|
|
self._get_vnf_lcm_op_occs_href(vnf_lcm_op_occs_id))
|
|
res.headerlist.append(location)
|
|
return res
|
|
|
|
@wsgi.response(http_client.ACCEPTED)
|
|
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN,
|
|
http_client.NOT_FOUND, http_client.CONFLICT))
|
|
@validation.schema(vnf_lcm.heal)
|
|
def heal(self, request, id, body):
|
|
context = request.environ['tacker.context']
|
|
context.can(vnf_lcm_policies.VNFLCM % 'heal')
|
|
|
|
vnf = self._get_vnf(context, id)
|
|
vnf_instance = self._get_vnf_instance(context, id)
|
|
|
|
if vnf_instance.instantiation_state not in \
|
|
[fields.VnfInstanceState.INSTANTIATED]:
|
|
raise exceptions.VnfInstanceConflictState(
|
|
attr='instantiation_state',
|
|
uuid=vnf_instance.id,
|
|
state=vnf_instance.instantiation_state,
|
|
action='heal')
|
|
if vnf_instance.task_state not in [None]:
|
|
raise exceptions.VnfInstanceConflictState(
|
|
attr='task_state',
|
|
uuid=vnf_instance.id,
|
|
state=vnf_instance.task_state,
|
|
action='heal')
|
|
|
|
return self._heal(context, vnf_instance, vnf, body)
|
|
|
|
@wsgi.response(http_client.OK)
|
|
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND))
|
|
def show_lcm_op_occs(self, request, id):
|
|
context = request.environ['tacker.context']
|
|
context.can(vnf_lcm_policies.VNFLCM % 'show_lcm_op_occs')
|
|
|
|
try:
|
|
vnf_lcm_op_occs = objects.VnfLcmOpOcc.get_by_id(context, id)
|
|
except exceptions.NotFound as occ_e:
|
|
return self._make_problem_detail(str(occ_e),
|
|
404, title='VnfLcmOpOcc NOT FOUND')
|
|
except Exception as e:
|
|
LOG.error(traceback.format_exc())
|
|
return self._make_problem_detail(str(e),
|
|
500, title='Internal Server Error')
|
|
|
|
return self._view_builder.show_lcm_op_occs(vnf_lcm_op_occs)
|
|
|
|
@wsgi.response(http_client.ACCEPTED)
|
|
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND))
|
|
def update(self, request, id, body):
|
|
context = request.environ['tacker.context']
|
|
context.can(vnf_lcm_policies.VNFLCM % 'update_vnf')
|
|
|
|
# get body
|
|
body_data = {}
|
|
body_data['vnf_instance_name'] = body.get('vnfInstanceName')
|
|
body_data['vnf_instance_description'] = body.get(
|
|
'vnfInstanceDescription')
|
|
body_data['vnfd_id'] = body.get('vnfdId')
|
|
if (body.get('vnfdId') is None and body.get('vnfPkgId')):
|
|
body_data['vnf_pkg_id'] = body.get('vnfPkgId')
|
|
|
|
# According to the ETSI NFV SOL document,
|
|
# there is no API request/response
|
|
# specification for Etag yet,
|
|
# and transactions using Etag are not defined
|
|
# by standardization. Therefore, the Victoria release does not support
|
|
# `Error Code: 412 Precondition Failed`. Once a standard specification
|
|
# for this is established, it will be installed on the tacker.
|
|
|
|
# Confirmation of update target
|
|
try:
|
|
vnf_data = objects.VNF.vnf_index_list(id, context)
|
|
if not vnf_data:
|
|
msg = _("Can not find requested vnf data: %s") % id
|
|
return self._make_problem_detail(msg, 404, title='Not Found')
|
|
except Exception as e:
|
|
return self._make_problem_detail(
|
|
str(e), 500, 'Internal Server Error')
|
|
|
|
if (vnf_data.get("status") != fields.VnfStatus.ACTIVE and
|
|
vnf_data.get("status") != fields.VnfStatus.INACTIVE):
|
|
msg = _("VNF %(id)s status is %(state)s")
|
|
return self._make_problem_detail(
|
|
msg % {
|
|
"id": id,
|
|
"state": vnf_data.get("status")},
|
|
409,
|
|
'Conflict')
|
|
|
|
try:
|
|
vnf_instance_data = objects.VnfInstanceList.vnf_instance_list(
|
|
vnf_data.get('vnfd_id'), context)
|
|
if not vnf_instance_data:
|
|
msg = _("Can not find requested vnf instance data: %s") \
|
|
% vnf_data.get('vnfd_id')
|
|
return self._make_problem_detail(msg, 404, title='Not Found')
|
|
except Exception as e:
|
|
return self._make_problem_detail(
|
|
str(e), 500, 'Internal Server Error')
|
|
|
|
vnfd_pkg_data = {}
|
|
if (body_data.get('vnfd_id') or body_data.get('vnf_pkg_id')):
|
|
pkg_obj = objects.VnfPackageVnfd(context=context)
|
|
try:
|
|
if (body.get('vnfdId')):
|
|
input_id = 'vnfd_id'
|
|
filter_id = 'vnfdId'
|
|
vnfd_pkg = pkg_obj.get_vnf_package_vnfd(
|
|
body_data[input_id])
|
|
elif (body_data.get('vnf_pkg_id')):
|
|
input_id = 'vnf_pkg_id'
|
|
filter_id = 'id'
|
|
vnfd_pkg = pkg_obj.get_vnf_package_vnfd(
|
|
body_data[input_id], package_uuid=True)
|
|
except exceptions.VnfPackageVnfdNotFound:
|
|
vnf_package_info = self._find_vnf_package_info(filter_id,
|
|
body_data[input_id])
|
|
if not vnf_package_info:
|
|
msg = _(
|
|
"Can not find requested vnf package vnfd: %s") %\
|
|
body_data[input_id]
|
|
return self._make_problem_detail(msg, 400,
|
|
'Bad Request')
|
|
|
|
vnfd_pkg = sync_resource.SyncVnfPackage.create_package(
|
|
context, vnf_package_info)
|
|
|
|
if not vnfd_pkg:
|
|
msg = (
|
|
_("Can not find requested to NFVO,\
|
|
vnf package vnfd: %s") %
|
|
body_data[input_id])
|
|
return self._make_problem_detail(
|
|
msg, 500, 'Internal Server Error')
|
|
vnfd_pkg_data['vnf_provider'] = vnfd_pkg.get('vnf_provider')
|
|
vnfd_pkg_data['vnf_product_name'] = vnfd_pkg.get(
|
|
'vnf_product_name')
|
|
vnfd_pkg_data['vnf_software_version'] = vnfd_pkg.get(
|
|
'vnf_software_version')
|
|
vnfd_pkg_data['vnfd_version'] = vnfd_pkg.get('vnfd_version')
|
|
vnfd_pkg_data['package_uuid'] = vnfd_pkg.get('package_uuid')
|
|
vnfd_pkg_data['vnfd_id'] = vnfd_pkg.get('vnfd_id')
|
|
|
|
# make op_occs_uuid
|
|
op_occs_uuid = uuidutils.generate_uuid()
|
|
|
|
# process vnf
|
|
vnf_lcm_opoccs = {
|
|
'vnf_instance_id': id,
|
|
'id': op_occs_uuid,
|
|
'state_entered_time': timeutils.utcnow(),
|
|
'operationParams': str(body)}
|
|
|
|
self.rpc_api.update(
|
|
context,
|
|
vnf_lcm_opoccs,
|
|
body_data,
|
|
vnfd_pkg_data,
|
|
vnf_data.get('vnfd_id'))
|
|
|
|
# make response
|
|
res = webob.Response(content_type='application/json')
|
|
res.status_int = 202
|
|
loc_url = CONF.vnf_lcm.endpoint_url.rstrip("/") + \
|
|
'/vnflcm/v1/vnf_lcm_op_occs/' + op_occs_uuid
|
|
location = ('Location', loc_url)
|
|
res.headerlist.append(location)
|
|
|
|
return res
|
|
|
|
@wsgi.response(http_client.CREATED)
|
|
@validation.schema(vnf_lcm.register_subscription)
|
|
def register_subscription(self, request, body):
|
|
subscription_request_data = body
|
|
|
|
subscription_id = uuidutils.generate_uuid()
|
|
|
|
vnf_lcm_subscription = subscription_obj.LccnSubscriptionRequest(
|
|
context=request.context)
|
|
vnf_lcm_subscription.id = subscription_id
|
|
vnf_lcm_subscription.callback_uri = subscription_request_data.get(
|
|
'callbackUri')
|
|
vnf_lcm_subscription.subscription_authentication = \
|
|
subscription_request_data.get('subscriptionAuthentication')
|
|
LOG.debug("filter %s " % subscription_request_data.get('filter'))
|
|
LOG.debug(
|
|
"filter type %s " %
|
|
type(
|
|
subscription_request_data.get('filter')))
|
|
filter_uni = subscription_request_data.get('filter')
|
|
filter = ast.literal_eval(str(filter_uni).replace("'", "'"))
|
|
|
|
if CONF.vnf_lcm.test_callback_uri:
|
|
resp = self._test_notification(request.context,
|
|
vnf_lcm_subscription)
|
|
if resp == -1:
|
|
LOG.exception(traceback.format_exc())
|
|
return self._make_problem_detail(
|
|
'Failed to Test Notification', 400,
|
|
title='Bad Request')
|
|
|
|
try:
|
|
vnf_lcm_subscription = vnf_lcm_subscription.create(filter)
|
|
LOG.debug("vnf_lcm_subscription %s" % vnf_lcm_subscription)
|
|
except exceptions.SeeOther as e:
|
|
res = webob.Response(content_type='application/json')
|
|
res.status_int = http_client.SEE_OTHER.value
|
|
location = (
|
|
'Location',
|
|
CONF.vnf_lcm.endpoint_url.rstrip("/") +
|
|
"/vnflcm/v1/subscriptions/" + str(e))
|
|
res.headerlist.append(location)
|
|
return res
|
|
|
|
result = self._view_builder.subscription_create(vnf_lcm_subscription,
|
|
filter)
|
|
location = result.get('_links', {}).get('self', {}).get('href')
|
|
headers = {"location": location}
|
|
return wsgi.ResponseObject(result, headers=headers)
|
|
|
|
@wsgi.response(http_client.OK)
|
|
def subscription_show(self, request, subscriptionId):
|
|
try:
|
|
vnf_lcm_subscriptions = (
|
|
subscription_obj.LccnSubscriptionRequest.
|
|
vnf_lcm_subscriptions_show(request.context, subscriptionId))
|
|
if not vnf_lcm_subscriptions:
|
|
msg = (
|
|
_("Can not find requested vnf lcm subscriptions: %s") %
|
|
subscriptionId)
|
|
return self._make_problem_detail(msg, 404, title='Not Found')
|
|
except Exception as e:
|
|
return self._make_problem_detail(
|
|
str(e), 500, title='Internal Server Error')
|
|
|
|
return self._view_builder.subscription_show(vnf_lcm_subscriptions)
|
|
|
|
@wsgi.response(http_client.OK)
|
|
def subscription_list(self, request):
|
|
nextpage_opaque_marker = ""
|
|
paging = 1
|
|
filter_string = ""
|
|
|
|
re_url = request.path_url
|
|
query_params = request.query_string
|
|
|
|
if query_params:
|
|
query_params = parse.unquote(query_params)
|
|
query_param_list = query_params.split('&')
|
|
for query_param in query_param_list:
|
|
query_param_key_value = query_param.split('=')
|
|
if len(query_param_key_value) != 2:
|
|
msg = _("Request query parameter error")
|
|
return self._make_problem_detail(
|
|
msg, 400, title='Bad Request')
|
|
if query_param_key_value[0] == 'filter':
|
|
filter_string = query_param_key_value[1]
|
|
if query_param_key_value[0] == 'nextpage_opaque_marker':
|
|
nextpage_opaque_marker = query_param_key_value[1]
|
|
if query_param_key_value[0] == 'page':
|
|
paging = int(query_param_key_value[1])
|
|
|
|
if filter_string:
|
|
# check enumerations columns
|
|
for filter_segment in filter_string.split(';'):
|
|
filter_segment = re.sub(
|
|
r'\(|\)|\'', '', filter_segment)
|
|
filter_name = filter_segment.split(',')[1]
|
|
filter_value = filter_segment.split(',')[2]
|
|
if filter_name == 'notificationTypes':
|
|
if filter_value not in self.notification_type_list:
|
|
msg = (_("notificationTypes value mismatch: %s")
|
|
% filter_value)
|
|
return self._make_problem_detail(msg, 400,
|
|
title='Bad Request')
|
|
elif filter_name == 'operationTypes':
|
|
if filter_value not in self.operation_type_list:
|
|
msg = (_("operationTypes value mismatch: %s")
|
|
% filter_value)
|
|
return self._make_problem_detail(msg, 400,
|
|
title='Bad Request')
|
|
elif filter_name == 'operationStates':
|
|
if filter_value not in self.operation_state_list:
|
|
msg = (_("operationStates value mismatch: %s")
|
|
% filter_value)
|
|
return self._make_problem_detail(msg, 400,
|
|
title='Bad Request')
|
|
|
|
try:
|
|
filter_string_parsed = self._view_builder_subscription. \
|
|
validate_filter(filter_string)
|
|
if nextpage_opaque_marker:
|
|
start_index = paging - 1
|
|
else:
|
|
start_index = None
|
|
|
|
vnf_lcm_subscriptions, last = (
|
|
subscription_obj.LccnSubscriptionList.
|
|
get_by_filters(request.context,
|
|
read_deleted='no',
|
|
filters=filter_string_parsed,
|
|
nextpage_opaque_marker=start_index))
|
|
|
|
LOG.debug("vnf_lcm_subscriptions %s" % vnf_lcm_subscriptions)
|
|
subscription_data = self._view_builder_subscription. \
|
|
subscription_list(vnf_lcm_subscriptions)
|
|
LOG.debug("last %s" % last)
|
|
except Exception as e:
|
|
LOG.error(traceback.format_exc())
|
|
return self._make_problem_detail(
|
|
str(e), 500, title='Internal Server Error')
|
|
|
|
if subscription_data == 400:
|
|
msg = _("Number of records exceeds nextpage_opaque_marker")
|
|
return self._make_problem_detail(msg, 400, title='Bad Request')
|
|
|
|
# make response
|
|
res = webob.Response(content_type='application/json')
|
|
res.body = jsonutils.dump_as_bytes(subscription_data)
|
|
res.status_int = 200
|
|
if nextpage_opaque_marker:
|
|
if not last:
|
|
ln = '<%s?page=%s>;rel="next"; title*="next chapter"' % (
|
|
re_url, paging + 1)
|
|
# Regarding the setting in http header related to
|
|
# nextpage control, RFC8288 and NFV-SOL013
|
|
# specifications have not been confirmed.
|
|
# Therefore, it is implemented by setting "page",
|
|
# which is a general control method of WebAPI,
|
|
# as "URI-Reference" of Link header.
|
|
|
|
links = ('Link', ln)
|
|
res.headerlist.append(links)
|
|
LOG.debug("subscription_list res %s" % res)
|
|
|
|
return res
|
|
|
|
@wsgi.response(http_client.NO_CONTENT)
|
|
def delete_subscription(self, request, subscriptionId):
|
|
try:
|
|
vnf_lcm_subscription = \
|
|
subscription_obj.LccnSubscriptionRequest.destroy(
|
|
request.context, subscriptionId)
|
|
if vnf_lcm_subscription == 404:
|
|
msg = (
|
|
_("Can not find requested vnf lcm subscription: %s") %
|
|
subscriptionId)
|
|
return self._make_problem_detail(msg, 404, title='Not Found')
|
|
except Exception as e:
|
|
return self._make_problem_detail(
|
|
str(e), 500, title='Internal Server Error')
|
|
|
|
def _get_scale_max_level_from_vnfd(self, context, vnf_instance, aspect_id):
|
|
vnfd_dict = vnflcm_utils._get_vnfd_dict(context,
|
|
vnf_instance.vnfd_id,
|
|
vnf_instance.instantiated_vnf_info.flavour_id)
|
|
tosca = tosca_template.ToscaTemplate(parsed_params={}, a_file=False,
|
|
yaml_dict_tpl=vnfd_dict)
|
|
tosca_policies = tosca.topology_template.policies
|
|
|
|
aspect_max_level_dict = {}
|
|
toscautils._extract_policy_info(
|
|
tosca_policies, {}, {}, {}, {}, {}, aspect_max_level_dict)
|
|
|
|
return aspect_max_level_dict.get(aspect_id)
|
|
|
|
@check_vnf_state(action="scale",
|
|
instantiation_state=[fields.VnfInstanceState.INSTANTIATED],
|
|
task_state=[None])
|
|
@check_vnf_status_and_error_point(action="scale",
|
|
status=[constants.ACTIVE])
|
|
def _scale(self, context, vnf_instance, vnf_info, request_body):
|
|
req_body = utils.convert_camelcase_to_snakecase(request_body)
|
|
scale_vnf_request = objects.ScaleVnfRequest.obj_from_primitive(
|
|
req_body, context=context)
|
|
inst_vnf_info = vnf_instance.instantiated_vnf_info
|
|
|
|
if 'vnf_lcm_op_occs_id' in vnf_info:
|
|
vnf_lcm_op_occs_id = vnf_info['vnf_lcm_op_occs_id']
|
|
|
|
aspect = False
|
|
current_level = 0
|
|
for scale in inst_vnf_info.scale_status:
|
|
if scale_vnf_request.aspect_id == scale.aspect_id:
|
|
aspect = True
|
|
current_level = scale.scale_level
|
|
break
|
|
if not aspect:
|
|
return self._make_problem_detail(
|
|
'aspectId not in ScaleStatus',
|
|
400,
|
|
title='aspectId not in ScaleStatus')
|
|
if not scale_vnf_request.number_of_steps:
|
|
scale_vnf_request.number_of_steps = 1
|
|
if not scale_vnf_request.additional_params:
|
|
scale_vnf_request.additional_params = {"is_reverse": "False",
|
|
"is_auto": "False"}
|
|
if not scale_vnf_request.additional_params.get('is_reverse'):
|
|
scale_vnf_request.additional_params['is_reverse'] = "False"
|
|
if not scale_vnf_request.additional_params.get('is_auto'):
|
|
scale_vnf_request.additional_params['is_auto'] = "False"
|
|
|
|
vim_type = vnf_instance.vim_connection_info[0].vim_type
|
|
if scale_vnf_request.type == 'SCALE_IN':
|
|
if current_level == 0 or\
|
|
current_level < scale_vnf_request.number_of_steps:
|
|
return self._make_problem_detail(
|
|
'can not scale_in', 400, title='can not scale_in')
|
|
if vim_type == "kubernetes" and\
|
|
scale_vnf_request.additional_params['is_reverse'] == "True":
|
|
return self._make_problem_detail(
|
|
'is_reverse option is not supported when Kubernetes '
|
|
'scale operation',
|
|
400,
|
|
title='is_reverse option is not supported when Kubernetes '
|
|
'scale operation')
|
|
scale_level = current_level - scale_vnf_request.number_of_steps
|
|
|
|
elif scale_vnf_request.type == 'SCALE_OUT':
|
|
if vim_type == "kubernetes":
|
|
max_level = self._get_scale_max_level_from_vnfd(
|
|
context=context,
|
|
vnf_instance=vnf_instance,
|
|
aspect_id=scale_vnf_request.aspect_id)
|
|
else:
|
|
scaleGroupDict = jsonutils.loads(
|
|
vnf_info['attributes']['scale_group'])
|
|
max_level = (scaleGroupDict['scaleGroupDict']
|
|
[scale_vnf_request.aspect_id]['maxLevel'])
|
|
|
|
scale_level = current_level + scale_vnf_request.number_of_steps
|
|
if max_level < scale_level:
|
|
return self._make_problem_detail(
|
|
'can not scale_out', 400, title='can not scale_out')
|
|
if 'vnf_lcm_op_occs_id' in vnf_info:
|
|
num = (scaleGroupDict['scaleGroupDict']
|
|
[scale_vnf_request.aspect_id]['num'])
|
|
default = (scaleGroupDict['scaleGroupDict']
|
|
[scale_vnf_request.aspect_id]['default'])
|
|
vnf_info['res_num'] = (num *
|
|
scale_vnf_request.number_of_steps + default)
|
|
|
|
if vnf_info['before_error_point'] == fields.ErrorPoint.INITIAL:
|
|
vnf_lcm_op_occs_id = uuidutils.generate_uuid()
|
|
timestamp = datetime.datetime.utcnow()
|
|
operation_params = {
|
|
'type': scale_vnf_request.type,
|
|
'aspect_id': scale_vnf_request.aspect_id,
|
|
'number_of_steps': scale_vnf_request.number_of_steps,
|
|
'additional_params': scale_vnf_request.additional_params}
|
|
|
|
vnf_lcm_op_occ = objects.VnfLcmOpOcc(
|
|
context=context,
|
|
id=vnf_lcm_op_occs_id,
|
|
operation_state='STARTING',
|
|
state_entered_time=timestamp,
|
|
start_time=timestamp,
|
|
vnf_instance_id=inst_vnf_info.vnf_instance_id,
|
|
operation='SCALE',
|
|
is_automatic_invocation=scale_vnf_request.additional_params.get('\
|
|
is_auto'),
|
|
operation_params=json.dumps(operation_params),
|
|
error_point=1)
|
|
vnf_lcm_op_occ.create()
|
|
else:
|
|
try:
|
|
vnf_lcm_op_occ = objects.VnfLcmOpOcc.get_by_id(
|
|
context, vnf_lcm_op_occs_id)
|
|
except exceptions.NotFound as lcm_e:
|
|
return self._make_problem_detail(str(lcm_e),
|
|
404, title='Not Found')
|
|
except (sqlexc.SQLAlchemyError, Exception) as exc:
|
|
LOG.exception(exc)
|
|
return self._make_problem_detail(str(exc),
|
|
500, title='Internal Server Error')
|
|
|
|
vnf_instance.task_state = fields.VnfInstanceTaskState.SCALING
|
|
vnf_instance.save()
|
|
|
|
vnflcm_url = CONF.vnf_lcm.endpoint_url.rstrip("/") + \
|
|
"/vnflcm/v1/vnf_lcm_op_occs/" + vnf_lcm_op_occs_id
|
|
insta_url = CONF.vnf_lcm.endpoint_url.rstrip("/") + \
|
|
"/vnflcm/v1/vnf_instances/" + inst_vnf_info.vnf_instance_id
|
|
|
|
vnf_info['vnflcm_id'] = vnf_lcm_op_occs_id
|
|
vnf_info['vnf_lcm_op_occ'] = vnf_lcm_op_occ
|
|
vnf_info['after_scale_level'] = scale_level
|
|
vnf_info['scale_level'] = current_level
|
|
vnf_info['instance_id'] = inst_vnf_info.instance_id
|
|
|
|
notification = {}
|
|
notification['notificationType'] = \
|
|
'VnfLcmOperationOccurrenceNotification'
|
|
notification['vnfInstanceId'] = inst_vnf_info.vnf_instance_id
|
|
notification['notificationStatus'] = 'START'
|
|
notification['operation'] = 'SCALE'
|
|
notification['operationState'] = 'STARTING'
|
|
notification['isAutomaticInvocation'] = \
|
|
scale_vnf_request.additional_params.get('is_auto')
|
|
notification['vnfLcmOpOccId'] = vnf_lcm_op_occs_id
|
|
notification['_links'] = {}
|
|
notification['_links']['vnfInstance'] = {}
|
|
notification['_links']['vnfInstance']['href'] = insta_url
|
|
notification['_links']['vnfLcmOpOcc'] = {}
|
|
notification['_links']['vnfLcmOpOcc']['href'] = vnflcm_url
|
|
vnf_info['notification'] = notification
|
|
|
|
if vnf_info['before_error_point'] == fields.ErrorPoint.INITIAL:
|
|
self.rpc_api.send_notification(context, notification)
|
|
self.rpc_api.scale(context, vnf_info, vnf_instance, scale_vnf_request)
|
|
|
|
res = webob.Response()
|
|
res.status_int = 202
|
|
location = ('Location', vnflcm_url)
|
|
res.headerlist.append(location)
|
|
return res
|
|
|
|
@validation.schema(vnf_lcm.scale)
|
|
@wsgi.response(http_client.ACCEPTED)
|
|
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN,
|
|
http_client.NOT_FOUND, http_client.CONFLICT))
|
|
def scale(self, request, id, body):
|
|
context = request.environ['tacker.context']
|
|
context.can(vnf_lcm_policies.VNFLCM % 'scale')
|
|
|
|
try:
|
|
vnf_info = self._vnfm_plugin.get_vnf(context, id)
|
|
if vnf_info['status'] != constants.ACTIVE:
|
|
return self._make_problem_detail(
|
|
'VNF IS NOT ACTIVE', 409, title='VNF IS NOT ACTIVE')
|
|
vnf_instance = self._get_vnf_instance(context, id)
|
|
if not vnf_instance.instantiated_vnf_info.scale_status:
|
|
return self._make_problem_detail(
|
|
'NOT SCALE VNF', 409, title='NOT SCALE VNF')
|
|
return self._scale(context, vnf_instance, vnf_info, body)
|
|
except vnfm.VNFNotFound as vnf_e:
|
|
return self._make_problem_detail(
|
|
str(vnf_e), 404, title='VNF NOT FOUND')
|
|
except webob.exc.HTTPNotFound as inst_e:
|
|
return self._make_problem_detail(
|
|
str(inst_e), 404, title='VNF NOT FOUND')
|
|
except Exception as e:
|
|
LOG.error(traceback.format_exc())
|
|
return self._make_problem_detail(
|
|
str(e), 500, title='Internal Server Error')
|
|
|
|
def _rollback(
|
|
self,
|
|
context,
|
|
vnf_info,
|
|
vnf_instance,
|
|
vnf_lcm_op_occs,
|
|
operation_params):
|
|
|
|
self.rpc_api.rollback(
|
|
context,
|
|
vnf_info,
|
|
vnf_instance,
|
|
operation_params)
|
|
vnf_info['vnf_lcm_op_occ'] = vnf_lcm_op_occs
|
|
|
|
vnflcm_url = CONF.vnf_lcm.endpoint_url.rstrip("/") + \
|
|
"/vnflcm/v1/vnf_lcm_op_occs/" + vnf_lcm_op_occs.id
|
|
res = webob.Response()
|
|
res.status_int = 202
|
|
location = ('Location', vnflcm_url)
|
|
res.headerlist.append(location)
|
|
return res
|
|
|
|
def _get_rollback_vnf(self, context, vnf_instance_id):
|
|
return self._vnfm_plugin.get_vnf(context, vnf_instance_id)
|
|
|
|
@wsgi.response(http_client.ACCEPTED)
|
|
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN,
|
|
http_client.NOT_FOUND, http_client.CONFLICT))
|
|
def rollback(self, request, id):
|
|
context = request.environ['tacker.context']
|
|
context.can(vnf_lcm_policies.VNFLCM % 'rollback')
|
|
|
|
try:
|
|
vnf_lcm_op_occs = objects.VnfLcmOpOcc.get_by_id(context, id)
|
|
if vnf_lcm_op_occs.operation_state != 'FAILED_TEMP':
|
|
return self._make_problem_detail(
|
|
'OperationState IS NOT FAILED_TEMP',
|
|
409,
|
|
title='OperationState IS NOT FAILED_TEMP')
|
|
|
|
if vnf_lcm_op_occs.operation != 'INSTANTIATE' \
|
|
and vnf_lcm_op_occs.operation != 'SCALE':
|
|
return self._make_problem_detail(
|
|
'OPERATION IS NOT INSTANTIATE/SCALE',
|
|
409,
|
|
title='OPERATION IS NOT INSTANTIATE/SCALE')
|
|
|
|
operation_params = jsonutils.loads(
|
|
vnf_lcm_op_occs.operation_params)
|
|
|
|
if vnf_lcm_op_occs.operation == 'SCALE' \
|
|
and operation_params['type'] == 'SCALE_IN':
|
|
return self._make_problem_detail(
|
|
'SCALE_IN CAN NOT ROLLBACK', 409,
|
|
title='SCALE_IN CAN NOT ROLLBACK')
|
|
|
|
vnf_info = self._get_rollback_vnf(
|
|
context, vnf_lcm_op_occs.vnf_instance_id)
|
|
vnf_instance = self._get_vnf_instance(
|
|
context, vnf_lcm_op_occs.vnf_instance_id)
|
|
|
|
inst_vnf_info = vnf_instance.instantiated_vnf_info
|
|
if inst_vnf_info is not None:
|
|
vnf_info['instance_id'] = inst_vnf_info.instance_id
|
|
|
|
vnf_lcm_op_occs.changed_info = None
|
|
vnf_info['vnf_lcm_op_occ'] = vnf_lcm_op_occs
|
|
return self._rollback(
|
|
context,
|
|
vnf_info,
|
|
vnf_instance,
|
|
vnf_lcm_op_occs,
|
|
operation_params)
|
|
except vnfm.VNFNotFound as vnf_e:
|
|
return self._make_problem_detail(
|
|
str(vnf_e), 404, title='VNF NOT FOUND')
|
|
except exceptions.NotFound as occ_e:
|
|
return self._make_problem_detail(
|
|
str(occ_e), 404, title='VNF NOT FOUND')
|
|
except webob.exc.HTTPNotFound as inst_e:
|
|
return self._make_problem_detail(
|
|
str(inst_e), 404, title='VNF NOT FOUND')
|
|
except Exception as e:
|
|
LOG.error(traceback.format_exc())
|
|
return self._make_problem_detail(
|
|
str(e), 500, title='Internal Server Error')
|
|
|
|
# TODO(esto-aln): For adding it to make it consistent
|
|
# We change vnf status here. In near future, we plan to
|
|
# delete this method.
|
|
def _update_vnf_fail_status(self, context, vnf_instance_id,
|
|
new_status):
|
|
self._vnfm_plugin.update_vnf_fail_status(
|
|
context, vnf_instance_id, new_status)
|
|
|
|
@wsgi.response(http_client.OK)
|
|
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN,
|
|
http_client.NOT_FOUND))
|
|
def fail(self, request, id):
|
|
context = request.environ['tacker.context']
|
|
context.can(vnf_lcm_policies.VNFLCM % 'fail')
|
|
|
|
try:
|
|
vnf_lcm_op_occs = objects.VnfLcmOpOcc.get_by_id(context, id)
|
|
operation = vnf_lcm_op_occs.operation
|
|
|
|
if vnf_lcm_op_occs.operation_state != 'FAILED_TEMP':
|
|
return self._make_problem_detail(
|
|
'State is not FAILED_TEMP', 409, title='Conflict')
|
|
|
|
vnf_instance_id = vnf_lcm_op_occs.vnf_instance_id
|
|
vnf_instance = self._get_vnf_instance(context, vnf_instance_id)
|
|
except webob.exc.HTTPNotFound as e:
|
|
return self._make_problem_detail(
|
|
str(e), 404, title='VNF NOT FOUND')
|
|
except exceptions.NotFound as e:
|
|
return self._make_problem_detail(
|
|
str(e), 404, title='VNF LCM NOT FOUND')
|
|
except Exception as e:
|
|
LOG.error(traceback.format_exc())
|
|
return self._make_problem_detail(
|
|
str(e), 500, title='Internal Server Error')
|
|
|
|
try:
|
|
old_vnf_instance = copy.deepcopy(vnf_instance)
|
|
|
|
vnf_lcm_op_occs.operation_state = "FAILED"
|
|
vnf_lcm_op_occs.state_entered_time = \
|
|
datetime.datetime.utcnow().isoformat()
|
|
vnf_lcm_op_occs.updated_at = vnf_lcm_op_occs.state_entered_time
|
|
|
|
error_details = objects.ProblemDetails(
|
|
context=context,
|
|
status=500,
|
|
detail=str(vnf_lcm_op_occs.error)
|
|
)
|
|
vnf_lcm_op_occs.error = error_details
|
|
|
|
# TODO(esto-aln): For adding it to make it consistent
|
|
# We change vnf status here. In near future, we plan to
|
|
# delete this branch.
|
|
if vnf_instance.instantiation_state == \
|
|
fields.VnfInstanceState.INSTANTIATED:
|
|
new_status = constants.ACTIVE
|
|
else:
|
|
new_status = constants.INACTIVE
|
|
|
|
self._update_vnf_fail_status(context, vnf_instance.id,
|
|
new_status)
|
|
vnf_instance.task_state = None
|
|
vnf_instance.save()
|
|
|
|
affected_resources = vnflcm_utils._get_affected_resources(
|
|
old_vnf_instance=old_vnf_instance,
|
|
new_vnf_instance=vnf_instance)
|
|
resource_change_obj = jsonutils.dumps(
|
|
utils.convert_camelcase_to_snakecase(affected_resources))
|
|
changed_resource = objects.ResourceChanges.obj_from_primitive(
|
|
resource_change_obj, context)
|
|
vnf_lcm_op_occs.resource_changes = changed_resource
|
|
vnf_lcm_op_occs.save()
|
|
except Exception as ex:
|
|
error_msg = "Error in VNF Fail for vnf {} because {}".format(
|
|
vnf_instance.id, encodeutils.exception_to_unicode(ex))
|
|
LOG.error(error_msg)
|
|
raise exceptions.TackerException(message=error_msg)
|
|
|
|
return self._fail(context, vnf_instance, vnf_lcm_op_occs,
|
|
operation, affected_resources)
|
|
|
|
def _fail(self, context, vnf_instance, vnf_lcm_op_occs,
|
|
operation, affected_resources):
|
|
|
|
self._notification_process(
|
|
context, vnf_instance, operation, {}, {},
|
|
vnf_lcm_op_occs=vnf_lcm_op_occs,
|
|
operation_state=fields.LcmOccsOperationState.FAILED,
|
|
notification_status=fields.LcmOccsNotificationStatus.RESULT,
|
|
affected_resources=affected_resources)
|
|
|
|
return self._view_builder.show_lcm_op_occs(vnf_lcm_op_occs)
|
|
|
|
@wsgi.response(http_client.ACCEPTED)
|
|
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN,
|
|
http_client.NOT_FOUND, http_client.CONFLICT))
|
|
def retry(self, request, id):
|
|
context = request.environ['tacker.context']
|
|
context.can(vnf_lcm_policies.VNFLCM % 'retry')
|
|
|
|
try:
|
|
vnf_lcm_op_occs = objects.VnfLcmOpOcc.get_by_id(context, id)
|
|
except exceptions.NotFound as lcm_e:
|
|
return self._make_problem_detail(str(lcm_e),
|
|
404, title='Not Found')
|
|
except (sqlexc.SQLAlchemyError, Exception) as exc:
|
|
LOG.exception(exc)
|
|
return self._make_problem_detail(str(exc),
|
|
500, title='Internal Server Error')
|
|
|
|
# operation state checking
|
|
if vnf_lcm_op_occs.operation_state != \
|
|
fields.LcmOccsOperationState.FAILED_TEMP:
|
|
error_msg = ('Cannot proceed with operation_state %s'
|
|
% vnf_lcm_op_occs.operation_state)
|
|
return self._make_problem_detail(error_msg,
|
|
409, title='Conflict')
|
|
|
|
# get vnf
|
|
try:
|
|
vnf = self._get_vnf(context, vnf_lcm_op_occs.vnf_instance_id)
|
|
except webob.exc.HTTPNotFound as lcm_e:
|
|
return self._make_problem_detail(str(lcm_e),
|
|
404, title='Not Found')
|
|
except Exception as exc:
|
|
LOG.exception(exc)
|
|
return self._make_problem_detail(str(exc),
|
|
500, title='Internal Server Error')
|
|
|
|
# get vnf instance
|
|
try:
|
|
vnf_instance = objects.VnfInstance.get_by_id(
|
|
context, vnf_lcm_op_occs.vnf_instance_id)
|
|
except exceptions.VnfInstanceNotFound:
|
|
msg = (_("Can not find requested vnf instance: %s")
|
|
% vnf_lcm_op_occs.vnf_instance_id)
|
|
return self._make_problem_detail(msg,
|
|
404, title='Not Found')
|
|
except Exception as exc:
|
|
LOG.exception(exc)
|
|
return self._make_problem_detail(str(exc),
|
|
500, title='Internal Server Error')
|
|
|
|
operation = vnf_lcm_op_occs.operation
|
|
body = jsonutils.loads(vnf_lcm_op_occs.operation_params)
|
|
vnf['before_error_point'] = vnf_lcm_op_occs.error_point
|
|
vnf['vnf_lcm_op_occs_id'] = id
|
|
if operation == fields.LcmOccsOperationType.INSTANTIATE:
|
|
self._instantiate(context, vnf_instance, vnf, body)
|
|
elif operation == fields.LcmOccsOperationType.TERMINATE:
|
|
self._terminate(context, vnf_instance, vnf, body)
|
|
elif operation == fields.LcmOccsOperationType.HEAL:
|
|
self._heal(context, vnf_instance, vnf, body)
|
|
elif operation == fields.LcmOccsOperationType.SCALE:
|
|
self._scale(context, vnf_instance, vnf, body)
|
|
elif operation == fields.LcmOccsOperationType.CHANGE_EXT_CONN:
|
|
self._change_ext_conn(context, vnf_instance, vnf, body)
|
|
else:
|
|
error_msg = 'Operation type %s is inavalid' % operation
|
|
return self._make_problem_detail(error_msg,
|
|
500, title='Internal Server Error')
|
|
|
|
@wsgi.response(http_client.OK)
|
|
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.BAD_REQUEST))
|
|
def list_lcm_op_occs(self, request):
|
|
context = request.environ['tacker.context']
|
|
context.can(vnf_lcm_policies.VNFLCM % 'list_lcm_op_occs')
|
|
|
|
all_fields = request.GET.get('all_fields')
|
|
exclude_default = request.GET.get('exclude_default')
|
|
fields = request.GET.get('fields')
|
|
exclude_fields = request.GET.get('exclude_fields')
|
|
filters = request.GET.get('filter')
|
|
if not (all_fields or fields or exclude_fields):
|
|
exclude_default = True
|
|
|
|
self._view_builder_op_occ.validate_attribute_fields(
|
|
all_fields=all_fields, fields=fields,
|
|
exclude_fields=exclude_fields,
|
|
exclude_default=exclude_default)
|
|
|
|
filters = self._view_builder_op_occ.validate_filter(filters)
|
|
|
|
try:
|
|
vnf_lcm_op_occs = \
|
|
vnf_lcm_op_occs_obj.VnfLcmOpOccList.get_by_filters(
|
|
request.context, read_deleted='no', filters=filters)
|
|
except Exception as e:
|
|
LOG.exception(traceback.format_exc())
|
|
return self._make_problem_detail(
|
|
str(e), 500, title='Internal Server Error')
|
|
|
|
return self._view_builder_op_occ.index(request, vnf_lcm_op_occs,
|
|
all_fields=all_fields, exclude_fields=exclude_fields,
|
|
fields=fields, exclude_default=exclude_default)
|
|
|
|
def _make_problem_detail(
|
|
self,
|
|
detail,
|
|
status,
|
|
title=None,
|
|
type=None,
|
|
instance=None):
|
|
'''This process returns the problem_detail to the caller'''
|
|
LOG.error(detail)
|
|
res = webob.Response(content_type='application/problem+json')
|
|
problem_details = {}
|
|
if type:
|
|
problem_details['type'] = type
|
|
if title:
|
|
problem_details['title'] = title
|
|
problem_details['detail'] = detail
|
|
problem_details['status'] = status
|
|
if instance:
|
|
problem_details['instance'] = instance
|
|
res.text = json.dumps(problem_details)
|
|
res.status_int = status
|
|
return res
|
|
|
|
def _test_notification(self, context, vnf_lcm_subscription):
|
|
resp = self.rpc_api.test_notification(context,
|
|
vnf_lcm_subscription, cast=False)
|
|
return resp
|
|
|
|
@wsgi.response(http_client.ACCEPTED)
|
|
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN,
|
|
http_client.NOT_FOUND, http_client.CONFLICT))
|
|
@validation.schema(vnf_lcm.change_ext_conn)
|
|
def change_ext_conn(self, request, id, body):
|
|
context = request.environ['tacker.context']
|
|
context.can(vnf_lcm_policies.VNFLCM % 'change_ext_conn')
|
|
|
|
vnf = self._get_vnf(context, id)
|
|
vnf_instance = self._get_vnf_instance(context, id)
|
|
if (vnf_instance.instantiation_state !=
|
|
fields.VnfInstanceState.INSTANTIATED):
|
|
return self._make_problem_detail(
|
|
'VNF is not instantiated',
|
|
409,
|
|
title='VNF IS NOT INSTANTIATED')
|
|
vnf['before_error_point'] = EP.INITIAL
|
|
self._change_ext_conn(context, vnf_instance, vnf, body)
|
|
|
|
def _change_ext_conn(self, context, vnf_instance, vnf, request_body):
|
|
req_body = utils.convert_camelcase_to_snakecase(request_body)
|
|
change_ext_conn_req = objects.ChangeExtConnRequest.obj_from_primitive(
|
|
req_body, context)
|
|
|
|
# call notification process
|
|
if vnf['before_error_point'] == EP.INITIAL:
|
|
vnf_lcm_op_occs_id = self._notification_process(
|
|
context,
|
|
vnf_instance,
|
|
fields.LcmOccsOperationType.CHANGE_EXT_CONN,
|
|
change_ext_conn_req,
|
|
request_body)
|
|
else:
|
|
vnf_lcm_op_occs_id = vnf['vnf_lcm_op_occs_id']
|
|
|
|
# Call Conductor server.
|
|
self.rpc_api.change_ext_conn(
|
|
context,
|
|
vnf_instance,
|
|
vnf,
|
|
change_ext_conn_req,
|
|
vnf_lcm_op_occs_id)
|
|
|
|
|
|
def create_resource():
|
|
return wsgi.Resource(VnfLcmController())
|