Support flow of Getting VNF package
Supported the following operations to get the VNF package information from NFVO when starting LCM operation. - VNF packages (GET) - VNF package content (GET) - VNFD in an individual VNF package (GET) - Individual VNF package artifact (GET) Implements: blueprint support-vnfm-operations Spec: https://specs.openstack.org/openstack/tacker-specs/specs/victoria/support-sol003-vnfm-operations.html Change-Id: Ibdafdda815f8e130226b9d8eef4f18639f01292c
This commit is contained in:
parent
a7dc3ab6b5
commit
9d95f91504
@ -206,7 +206,8 @@ terminate = {
|
||||
'properties': {
|
||||
'terminationType': {'type': 'string',
|
||||
'enum': ['FORCEFUL', 'GRACEFUL']},
|
||||
'gracefulTerminationTimeout': {'type': 'integer', 'minimum': 0}
|
||||
'gracefulTerminationTimeout': {'type': 'integer', 'minimum': 0},
|
||||
'additionalParams': parameter_types.keyvalue_pairs,
|
||||
},
|
||||
'required': ['terminationType'],
|
||||
'additionalProperties': False,
|
||||
|
@ -116,18 +116,11 @@ class ViewBuilder(base.BaseViewBuilder):
|
||||
|
||||
return {"_links": _links}
|
||||
|
||||
def _get_vnf_instance_info(self,
|
||||
vnf_instance, api_version=None):
|
||||
def _get_vnf_instance_info(self, vnf_instance):
|
||||
vnf_instance_dict = vnf_instance.to_dict()
|
||||
if vnf_instance_dict.get('vim_connection_info'):
|
||||
vnf_instance_dict['vim_connection_info'] = \
|
||||
self._get_vim_conn_info(vnf_instance_dict.get(
|
||||
'vim_connection_info', []))
|
||||
|
||||
if 'vnf_metadata' in vnf_instance_dict:
|
||||
metadata_val = vnf_instance_dict.pop('vnf_metadata')
|
||||
vnf_instance_dict['metadata'] = metadata_val
|
||||
|
||||
vnf_metadata = vnf_instance_dict.pop("vnf_metadata")
|
||||
if vnf_metadata:
|
||||
vnf_instance_dict.update({"metadata": vnf_metadata})
|
||||
vnf_instance_dict = utils.convert_snakecase_to_camelcase(
|
||||
vnf_instance_dict)
|
||||
|
||||
|
@ -13,11 +13,20 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
import requests
|
||||
import six
|
||||
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
|
||||
|
||||
import ast
|
||||
import functools
|
||||
@ -25,20 +34,18 @@ import json
|
||||
import re
|
||||
import traceback
|
||||
|
||||
import six
|
||||
from six.moves import http_client
|
||||
from six.moves.urllib import parse
|
||||
import webob
|
||||
|
||||
|
||||
from tacker._i18n import _
|
||||
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.vnflcm.v1 import sync_resource
|
||||
from tacker.common import exceptions
|
||||
from tacker.common import utils
|
||||
from tacker.conductor.conductorrpc import vnf_lcm_rpc
|
||||
import tacker.conf
|
||||
from tacker.db.vnfm import vnfm_db
|
||||
from tacker.extensions import nfvo
|
||||
from tacker.extensions import vnfm
|
||||
from tacker import manager
|
||||
@ -47,9 +54,11 @@ from tacker.objects import fields
|
||||
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
|
||||
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__)
|
||||
@ -307,73 +316,187 @@ class VnfLcmController(wsgi.Controller):
|
||||
|
||||
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.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')
|
||||
|
||||
req_body = utils.convert_camelcase_to_snakecase(body)
|
||||
vnfd_id = req_body.get('vnfd_id')
|
||||
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 as exc:
|
||||
raise webob.exc.HTTPBadRequest(explanation=six.text_type(exc))
|
||||
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')
|
||||
|
||||
# get default vim information
|
||||
vim_client_obj = vim_client.VimClient()
|
||||
default_vim = vim_client_obj.get_vim(context)
|
||||
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)
|
||||
|
||||
# set vim_connection_info
|
||||
access_info = {
|
||||
'username': default_vim.get('vim_auth', {}).get('username'),
|
||||
'password': default_vim.get('vim_auth', {}).get('password'),
|
||||
'region': default_vim.get('placement_attr', {}).get('region'),
|
||||
'tenant': default_vim.get('tenant')
|
||||
}
|
||||
vim_con_info = objects.VimConnectionInfo(id=default_vim.get('vim_id'),
|
||||
vim_id=default_vim.get('vim_id'),
|
||||
vim_type=default_vim.get('vim_type'),
|
||||
access_info=access_info)
|
||||
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,
|
||||
tenant_id=request.context.project_id,
|
||||
vnf_metadata=req_body.get('metadata'))
|
||||
|
||||
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()
|
||||
|
||||
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)
|
||||
self._update_package_usage_state(context, vnf_package)
|
||||
|
||||
# add default vim to vim_connection_info
|
||||
setattr(vnf_instance, 'vim_connection_info', [vim_con_info])
|
||||
|
||||
# create notification data
|
||||
notification = {
|
||||
'notificationType':
|
||||
# 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)}}}
|
||||
'vnfInstanceId': vnf_instance.id,
|
||||
'links': {
|
||||
'vnfInstance': {
|
||||
'href': self._get_vnf_instance_href(vnf_instance)}}}
|
||||
|
||||
# call send nootification
|
||||
self.rpc_api.send_notification(context, notification)
|
||||
vnf_instance.save()
|
||||
# call sendNotification
|
||||
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)
|
||||
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=six.text_type(exc))
|
||||
except(sqlexc.SQLAlchemyError, Exception)\
|
||||
as exc:
|
||||
raise webob.exc.HTTPInternalServerError(
|
||||
explanation=six.text_type(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))
|
||||
@ -401,8 +524,16 @@ class VnfLcmController(wsgi.Controller):
|
||||
@check_vnf_state(action="delete",
|
||||
instantiation_state=[fields.VnfInstanceState.NOT_INSTANTIATED],
|
||||
task_state=[None])
|
||||
def _delete(self, context, vnf_instance):
|
||||
@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,
|
||||
@ -411,7 +542,8 @@ class VnfLcmController(wsgi.Controller):
|
||||
context = request.environ['tacker.context']
|
||||
|
||||
vnf_instance = self._get_vnf_instance(context, id)
|
||||
self._delete(context, vnf_instance)
|
||||
vnf = self._get_vnf(context, id)
|
||||
self._delete(context, vnf_instance, vnf)
|
||||
|
||||
notification = {
|
||||
"notificationType": "VnfIdentifierDeletionNotification",
|
||||
|
138
tacker/api/vnflcm/v1/sync_resource.py
Normal file
138
tacker/api/vnflcm/v1/sync_resource.py
Normal file
@ -0,0 +1,138 @@
|
||||
#
|
||||
# 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.
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from tacker.common import csar_utils
|
||||
from tacker.common import exceptions
|
||||
from tacker.common import utils
|
||||
from tacker.conductor.conductorrpc import vnf_pkgm_rpc
|
||||
from tacker.glance_store import store as glance_store
|
||||
from tacker import objects
|
||||
from tacker.objects import fields
|
||||
import tacker.vnfm.nfvo_client as nfvo_client
|
||||
import time
|
||||
import webob
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SyncVnfPackage:
|
||||
|
||||
vnf_package_rpc_api = vnf_pkgm_rpc.VNFPackageRPCAPI()
|
||||
|
||||
@classmethod
|
||||
def create_package(cls, context, vnf_package_info):
|
||||
"""vnf_package, create a vnf_package_vnfd table."""
|
||||
|
||||
vnf_package_info = utils.convert_camelcase_to_snakecase(
|
||||
vnf_package_info)
|
||||
|
||||
try:
|
||||
vnf_package = cls.__create_vnf_package(context, vnf_package_info)
|
||||
except Exception as exc:
|
||||
raise webob.exc.HTTPInternalServerError(
|
||||
explanation=exc)
|
||||
|
||||
try:
|
||||
artifact_paths = cls._get_artifact_paths(vnf_package_info)
|
||||
vnf_package_binary = \
|
||||
nfvo_client.VnfPackageRequest.download_vnf_packages(
|
||||
vnf_package.id, artifact_paths)
|
||||
except nfvo_client.UndefinedExternalSettingException as exc:
|
||||
raise webob.exc.HTTPNotFound(explanation=exc)
|
||||
except (nfvo_client.FaliedDownloadContentException, Exception) as exc:
|
||||
raise webob.exc.HTTPInternalServerError(
|
||||
explanation=exc)
|
||||
|
||||
try:
|
||||
(location, size, _, multihash, _) = glance_store.store_csar(
|
||||
context, vnf_package.id, vnf_package_binary)
|
||||
|
||||
cls.__update_vnf_package(vnf_package, location, size, multihash)
|
||||
cls.vnf_package_rpc_api.upload_vnf_package_content(
|
||||
context, vnf_package)
|
||||
vnf_package_vnfd = cls._get_vnf_package_vnfd(
|
||||
context, vnf_package_info.get('vnfd_id'))
|
||||
except Exception as exc:
|
||||
raise webob.exc.HTTPInternalServerError(
|
||||
explanation=exc)
|
||||
|
||||
return vnf_package_vnfd
|
||||
|
||||
@classmethod
|
||||
def _get_artifact_paths(cls, vnf_package_info):
|
||||
additional_artifacts = vnf_package_info.get('additional_artifacts')
|
||||
if additional_artifacts is None:
|
||||
return None
|
||||
|
||||
return [artifact.get('artifact_path')
|
||||
for artifact in additional_artifacts
|
||||
if 'artifact_path' in artifact]
|
||||
|
||||
@classmethod
|
||||
def __store_csar(cls, context, id, body):
|
||||
(location, size, checksum, multihash,
|
||||
loc_meta) = glance_store.store_csar(context, id, body)
|
||||
return location, size, checksum, multihash, loc_meta
|
||||
|
||||
@classmethod
|
||||
def __load_csar(cls, context, vnf_package):
|
||||
location = vnf_package.location_glance_store
|
||||
zip_path = glance_store.load_csar(vnf_package.id, location)
|
||||
vnf_data, flavours = csar_utils.load_csar_data(
|
||||
context.elevated(), vnf_package.id, zip_path)
|
||||
return vnf_data, flavours
|
||||
|
||||
@classmethod
|
||||
def __create_vnf_package(cls, context, vnf_package_info):
|
||||
"""VNF Package Table Registration."""
|
||||
vnf_package = objects.VnfPackage(
|
||||
context=context,
|
||||
id=vnf_package_info.get('id'),
|
||||
onboarding_state=fields.PackageOnboardingStateType.CREATED,
|
||||
operational_state=fields.PackageOperationalStateType.DISABLED,
|
||||
usage_state=fields.PackageUsageStateType.NOT_IN_USE,
|
||||
tenant_id=context.project_id
|
||||
)
|
||||
vnf_package.create()
|
||||
return vnf_package
|
||||
|
||||
@classmethod
|
||||
def __update_vnf_package(cls, vnf_package, location, size, multihash):
|
||||
"""VNF Package Table Update."""
|
||||
vnf_package.algorithm = CONF.vnf_package.hashing_algorithm
|
||||
vnf_package.location_glance_store = location
|
||||
vnf_package.hash = multihash
|
||||
vnf_package.size = size
|
||||
vnf_package.save()
|
||||
|
||||
@classmethod
|
||||
def _get_vnf_package_vnfd(cls, context, vnfd_id):
|
||||
"""Get VNF Package VNFD."""
|
||||
for num in range(CONF.vnf_lcm.retry_num):
|
||||
try:
|
||||
vnfd = objects.VnfPackageVnfd.get_by_id(
|
||||
context,
|
||||
vnfd_id)
|
||||
return vnfd
|
||||
except exceptions.VnfPackageVnfdNotFound:
|
||||
LOG.debug("retry_wait %s" %
|
||||
CONF.vnf_lcm.retry_wait)
|
||||
time.sleep(CONF.vnf_lcm.retry_wait)
|
||||
|
||||
return None
|
@ -13,8 +13,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from io import BytesIO
|
||||
import json
|
||||
import mimetypes
|
||||
import os
|
||||
|
||||
@ -302,19 +302,37 @@ class VnfPkgmController(wsgi.Controller):
|
||||
context = request.environ['tacker.context']
|
||||
context.can(vnf_package_policies.VNFPKGM % 'upload_package_content')
|
||||
|
||||
vnf_package = self._get_vnf_package(id, request)
|
||||
# check if id is of type uuid format
|
||||
if not uuidutils.is_uuid_like(id):
|
||||
msg = _("Can not find requested vnf package: %s") % id
|
||||
return self._make_problem_detail('Not Found', msg, 404)
|
||||
|
||||
try:
|
||||
vnf_package = vnf_package_obj.VnfPackage.get_by_id(
|
||||
request.context, id)
|
||||
except exceptions.VnfPackageNotFound:
|
||||
msg = _("Can not find requested vnf package: %s") % id
|
||||
return self._make_problem_detail('Not Found', msg, 404)
|
||||
except Exception as e:
|
||||
return self._make_problem_detail(
|
||||
'Internal Server Error', str(e), 500)
|
||||
|
||||
if vnf_package.onboarding_state != \
|
||||
fields.PackageOnboardingStateType.CREATED:
|
||||
msg = _("VNF Package %(id)s onboarding state "
|
||||
"is not %(onboarding)s")
|
||||
raise webob.exc.HTTPConflict(explanation=msg % {"id": id,
|
||||
"onboarding": fields.PackageOnboardingStateType.CREATED})
|
||||
return self._make_problem_detail('Conflict', msg % {"id": id,
|
||||
"onboarding": fields.PackageOnboardingStateType.CREATED},
|
||||
409)
|
||||
|
||||
vnf_package.onboarding_state = (
|
||||
fields.PackageOnboardingStateType.UPLOADING)
|
||||
|
||||
vnf_package.save()
|
||||
try:
|
||||
vnf_package.save()
|
||||
except Exception as e:
|
||||
return self._make_problem_detail(
|
||||
'Internal Server Error', str(e), 500)
|
||||
|
||||
try:
|
||||
(location, size, checksum, multihash,
|
||||
@ -323,16 +341,29 @@ class VnfPkgmController(wsgi.Controller):
|
||||
with excutils.save_and_reraise_exception():
|
||||
vnf_package.onboarding_state = (
|
||||
fields.PackageOnboardingStateType.CREATED)
|
||||
vnf_package.save()
|
||||
|
||||
vnf_package.onboarding_state = (
|
||||
fields.PackageOnboardingStateType.PROCESSING)
|
||||
try:
|
||||
vnf_package.save()
|
||||
except Exception as e:
|
||||
return self._make_problem_detail(
|
||||
'Internal Server Error', str(e), 500)
|
||||
|
||||
vnf_package.algorithm = CONF.vnf_package.hashing_algorithm
|
||||
vnf_package.hash = multihash
|
||||
vnf_package.location_glance_store = location
|
||||
vnf_package.size = size
|
||||
vnf_package.save()
|
||||
try:
|
||||
vnf_package.save()
|
||||
except Exception as e:
|
||||
vnf_package.onboarding_state = (
|
||||
fields.PackageOnboardingStateType.CREATED)
|
||||
try:
|
||||
vnf_package.save()
|
||||
except Exception as e:
|
||||
return self._make_problem_detail(
|
||||
'Internal Server Error', str(e), 500)
|
||||
|
||||
return self._make_problem_detail(
|
||||
'Internal Server Error', str(e), 500)
|
||||
|
||||
# process vnf_package
|
||||
self.rpc_api.upload_vnf_package_content(context, vnf_package)
|
||||
@ -618,6 +649,16 @@ class VnfPkgmController(wsgi.Controller):
|
||||
|
||||
return buff.getvalue()
|
||||
|
||||
def _make_problem_detail(self, title, detail, status):
|
||||
res = webob.Response(content_type='application/problem+json')
|
||||
problemDetails = {}
|
||||
problemDetails['title'] = title
|
||||
problemDetails['detail'] = detail
|
||||
problemDetails['status'] = status
|
||||
res.text = json.dumps(problemDetails)
|
||||
res.status_int = status
|
||||
return res
|
||||
|
||||
|
||||
def create_resource():
|
||||
body_deserializers = {
|
||||
|
@ -12,6 +12,7 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from copy import deepcopy
|
||||
import hashlib
|
||||
import os
|
||||
import re
|
||||
@ -29,6 +30,7 @@ import zipfile
|
||||
|
||||
from tacker.common import exceptions
|
||||
import tacker.conf
|
||||
from tacker.extensions import vnfm
|
||||
import urllib.request as urllib2
|
||||
|
||||
|
||||
@ -124,8 +126,10 @@ def _get_software_image(custom_defs, nodetemplate_name, node_tpl):
|
||||
properties = node_tpl['properties']
|
||||
sw_image_artifact = _get_sw_image_artifact(node_tpl.get('artifacts'))
|
||||
if sw_image_artifact:
|
||||
image_path = sw_image_artifact['file'].lstrip("./")
|
||||
properties['sw_image_data'].update(
|
||||
{'software_image_id': nodetemplate_name})
|
||||
{'software_image_id': nodetemplate_name,
|
||||
'image_path': image_path})
|
||||
sw_image_data = properties['sw_image_data']
|
||||
if 'metadata' in sw_image_artifact:
|
||||
sw_image_data.update({'metadata':
|
||||
@ -137,7 +141,79 @@ def _populate_flavour_data(tosca):
|
||||
flavours = []
|
||||
if tosca.nested_tosca_templates_with_topology:
|
||||
for tp in tosca.nested_tosca_templates_with_topology:
|
||||
_get_flavour_data(tp, flavours)
|
||||
sw_image_list = []
|
||||
|
||||
# Setting up flavour data
|
||||
flavour_id = tp.substitution_mappings.properties.get('flavour_id')
|
||||
if flavour_id:
|
||||
flavour = {'flavour_id': flavour_id}
|
||||
tpl_dict = dict()
|
||||
|
||||
# get from top-vnfd data
|
||||
for key, value in tosca.tpl.items():
|
||||
if key in CONF.vnf_package.get_top_list:
|
||||
tpl_dict[key] = value
|
||||
|
||||
# get from lower-vnfd data
|
||||
tpl_dict['topology_template'] = dict()
|
||||
tpl_dict['topology_template']['policies'] = \
|
||||
tp.tpl.get('policies')
|
||||
tpl_dict['topology_template']['node_templates'] = \
|
||||
deepcopy(tp.tpl.get('node_templates'))
|
||||
for e_node in CONF.vnf_package.exclude_node:
|
||||
if tpl_dict['topology_template']['node_templates'].\
|
||||
get(e_node):
|
||||
del (tpl_dict['topology_template']
|
||||
['node_templates'][e_node])
|
||||
tpl_dict['topology_template']['inputs'] = \
|
||||
deepcopy(tp.tpl.get('inputs'))
|
||||
for del_input in CONF.vnf_package.del_input_list:
|
||||
if tpl_dict['topology_template']['inputs'].get(del_input):
|
||||
del tpl_dict['topology_template']['inputs'][del_input]
|
||||
if len(tpl_dict['topology_template']['inputs']) < 1:
|
||||
del tpl_dict['topology_template']['inputs']
|
||||
|
||||
flavour.update({'tpl_dict': tpl_dict})
|
||||
|
||||
instantiation_levels = _get_instantiation_levels(tp.policies)
|
||||
if instantiation_levels:
|
||||
flavour.update(
|
||||
{'instantiation_levels': instantiation_levels})
|
||||
|
||||
mgmt_driver = None
|
||||
for template_name, node_tpl in \
|
||||
tp.tpl.get('node_templates').items():
|
||||
# check the flavour property in vnf data
|
||||
_update_flavour_data_from_vnf(
|
||||
tp.custom_defs, node_tpl, flavour)
|
||||
if node_tpl['type'] in CONF.vnf_package.get_lower_list:
|
||||
if node_tpl['type'] == "tosca.nodes.nfv.VDU.Tacker":
|
||||
# get mgmt_driver
|
||||
mgmt_driver_flavour = \
|
||||
node_tpl['properties'].get('mgmt_driver')
|
||||
if mgmt_driver_flavour:
|
||||
if mgmt_driver and \
|
||||
mgmt_driver_flavour != mgmt_driver:
|
||||
raise vnfm.MultipleMGMTDriversSpecified()
|
||||
mgmt_driver = mgmt_driver_flavour
|
||||
flavour.update({'mgmt_driver': mgmt_driver})
|
||||
|
||||
for template_name, node_tpl in \
|
||||
tp.tpl.get('node_templates').items():
|
||||
# Update the software image data
|
||||
sw_image = _get_software_image(tp.custom_defs,
|
||||
template_name,
|
||||
node_tpl)
|
||||
if sw_image:
|
||||
sw_image_list.append(sw_image)
|
||||
|
||||
# Add software images for flavour
|
||||
if sw_image_list:
|
||||
flavour.update({'sw_images': sw_image_list})
|
||||
|
||||
if flavour:
|
||||
flavours.append(flavour)
|
||||
|
||||
else:
|
||||
_get_flavour_data(tosca.topology_template, flavours)
|
||||
|
||||
|
@ -356,5 +356,9 @@ class UserDataUpdateCreateFailed(TackerException):
|
||||
"or created after %(retries)d retries.")
|
||||
|
||||
|
||||
class DBAccessError(TackerException):
|
||||
message = _("DB Access Error")
|
||||
|
||||
|
||||
class SeeOther(TackerException):
|
||||
code = 303
|
||||
|
@ -18,6 +18,7 @@ import functools
|
||||
import inspect
|
||||
import json
|
||||
import os
|
||||
import oslo_messaging
|
||||
import shutil
|
||||
import sys
|
||||
import time
|
||||
@ -28,7 +29,6 @@ import yaml
|
||||
from glance_store import exceptions as store_exceptions
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import oslo_messaging
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_service import periodic_task
|
||||
from oslo_service import service
|
||||
@ -42,20 +42,25 @@ from sqlalchemy.orm import exc as orm_exc
|
||||
from tacker import auth
|
||||
from tacker.common import coordination
|
||||
from tacker.common import csar_utils
|
||||
from tacker.common import driver_manager
|
||||
from tacker.common import exceptions
|
||||
from tacker.common import log
|
||||
from tacker.common import safe_utils
|
||||
from tacker.common import topics
|
||||
from tacker.common import utils
|
||||
import tacker.conf
|
||||
from tacker import context as t_context
|
||||
from tacker.db.common_services import common_services_db
|
||||
from tacker.db.nfvo import nfvo_db
|
||||
from tacker.db.vnfm import vnfm_db
|
||||
from tacker.extensions import nfvo
|
||||
from tacker.glance_store import store as glance_store
|
||||
from tacker import manager
|
||||
from tacker import objects
|
||||
from tacker.objects import fields
|
||||
from tacker.objects.vnf_package import VnfPackagesList
|
||||
from tacker.objects import vnfd as vnfd_db
|
||||
from tacker.objects import vnfd_attribute as vnfd_attribute_db
|
||||
from tacker.plugins.common import constants
|
||||
from tacker import service as tacker_service
|
||||
from tacker import version
|
||||
@ -63,7 +68,7 @@ from tacker.vnflcm import utils as vnflcm_utils
|
||||
from tacker.vnflcm import vnflcm_driver
|
||||
from tacker.vnfm import plugin
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF = tacker.conf.CONF
|
||||
|
||||
# NOTE(tpatil): keystone_authtoken opts registered explicitly as conductor
|
||||
# service doesn't use the keystonemiddleware.authtoken middleware as it's
|
||||
@ -97,6 +102,13 @@ cfg.CONF.register_opts(OPTS, 'keystone_authtoken')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
_INACTIVE_STATUS = ('INACTIVE')
|
||||
_ACTIVE_STATUS = ('ACTIVE')
|
||||
_PENDING_STATUS = ('PENDING_CREATE',
|
||||
'PENDING_TERMINATE',
|
||||
'PENDING_DELETE',
|
||||
'PENDING_HEAL')
|
||||
|
||||
|
||||
def _delete_csar(context, vnf_package):
|
||||
# Delete from glance store
|
||||
@ -154,6 +166,9 @@ class Conductor(manager.Manager):
|
||||
super(Conductor, self).__init__(host=self.conf.host)
|
||||
self.vnfm_plugin = plugin.VNFMPlugin()
|
||||
self.vnflcm_driver = vnflcm_driver.VnfLcmDriver()
|
||||
self.vnf_manager = driver_manager.DriverManager(
|
||||
'tacker.tacker.vnfm.drivers',
|
||||
cfg.CONF.tacker.infra_driver)
|
||||
|
||||
def start(self):
|
||||
coordination.COORDINATOR.start()
|
||||
@ -223,7 +238,7 @@ class Conductor(manager.Manager):
|
||||
vnf_sw_image.min_ram = 0
|
||||
vnf_sw_image.min_disk = int(sw_image.get('min_disk').split()[0])
|
||||
vnf_sw_image.size = int(sw_image.get('size').split()[0])
|
||||
vnf_sw_image.image_path = ''
|
||||
vnf_sw_image.image_path = sw_image['image_path']
|
||||
vnf_sw_image.software_image_id = sw_image['software_image_id']
|
||||
vnf_sw_image.metadata = sw_image.get('metadata', dict())
|
||||
vnf_sw_image.create()
|
||||
@ -233,8 +248,9 @@ class Conductor(manager.Manager):
|
||||
deploy_flavour.package_uuid = package_uuid
|
||||
deploy_flavour.flavour_id = flavour['flavour_id']
|
||||
deploy_flavour.flavour_description = flavour['flavour_description']
|
||||
deploy_flavour.instantiation_levels = \
|
||||
flavour.get('instantiation_levels')
|
||||
if flavour.get('instantiation_levels'):
|
||||
deploy_flavour.instantiation_levels = \
|
||||
flavour.get('instantiation_levels')
|
||||
deploy_flavour.create()
|
||||
|
||||
sw_images = flavour.get('sw_images')
|
||||
@ -273,27 +289,55 @@ class Conductor(manager.Manager):
|
||||
package_vnfd.vnfd_version = vnf_data.get('descriptor_version')
|
||||
package_vnfd.create()
|
||||
|
||||
self._onboard_vnfd(context, vnf_package, vnf_data, flavours)
|
||||
|
||||
for flavour in flavours:
|
||||
self._create_flavour(context, vnf_package.id, flavour)
|
||||
|
||||
def _onboard_vnfd(self, context, vnf_package, vnf_data, flavours):
|
||||
vnfd = vnfd_db.Vnfd(context=context)
|
||||
vnfd.id = vnf_data.get('descriptor_id')
|
||||
vnfd.tenant_id = context.tenant_id
|
||||
vnfd.name = vnf_data.get('product_name') + '-' + \
|
||||
vnf_data.get('descriptor_version')
|
||||
vnfd.discription = vnf_data.get('discription')
|
||||
for flavour in flavours:
|
||||
if flavour.get('mgmt_driver'):
|
||||
vnfd.mgmt_driver = flavour.get('mgmt_driver')
|
||||
break
|
||||
vnfd.create()
|
||||
|
||||
for flavour in flavours:
|
||||
vnfd_attribute = vnfd_attribute_db.VnfdAttribute(context=context)
|
||||
vnfd_attribute.id = uuidutils.generate_uuid()
|
||||
vnfd_attribute.vnfd_id = vnf_data.get('descriptor_id')
|
||||
vnfd_attribute.key = 'vnfd_' + flavour['flavour_id']
|
||||
vnfd_attribute.value = \
|
||||
yaml.dump(flavour.get('tpl_dict'), default_flow_style=False)
|
||||
vnfd_attribute.create()
|
||||
|
||||
break
|
||||
|
||||
@revert_upload_vnf_package
|
||||
def upload_vnf_package_content(self, context, vnf_package):
|
||||
location = vnf_package.location_glance_store
|
||||
zip_path = glance_store.load_csar(vnf_package.id, location)
|
||||
vnf_data, flavours, vnf_artifacts = csar_utils.load_csar_data(
|
||||
context.elevated(), vnf_package.id, zip_path)
|
||||
self._onboard_vnf_package(
|
||||
context,
|
||||
vnf_package,
|
||||
vnf_data,
|
||||
flavours,
|
||||
vnf_artifacts)
|
||||
vnf_package.onboarding_state = (
|
||||
fields.PackageOnboardingStateType.ONBOARDED)
|
||||
vnf_package.operational_state = (
|
||||
fields.PackageOperationalStateType.ENABLED)
|
||||
fields.PackageOnboardingStateType.PROCESSING)
|
||||
try:
|
||||
vnf_package.save()
|
||||
|
||||
vnf_package.save()
|
||||
location = vnf_package.location_glance_store
|
||||
zip_path = glance_store.load_csar(vnf_package.id, location)
|
||||
vnf_data, flavours, vnf_artifacts = csar_utils.load_csar_data(
|
||||
context.elevated(), vnf_package.id, zip_path)
|
||||
self._onboard_vnf_package(context, vnf_package, vnf_data, flavours)
|
||||
vnf_package.onboarding_state = (
|
||||
fields.PackageOnboardingStateType.ONBOARDED)
|
||||
vnf_package.operational_state = (
|
||||
fields.PackageOperationalStateType.ENABLED)
|
||||
vnf_package.save()
|
||||
|
||||
except Exception as msg:
|
||||
raise Exception(msg)
|
||||
|
||||
@revert_upload_vnf_package
|
||||
def upload_vnf_package_from_uri(self, context, vnf_package,
|
||||
@ -433,6 +477,180 @@ class Conductor(manager.Manager):
|
||||
|
||||
vnf_package.save()
|
||||
|
||||
@log.log
|
||||
def _change_vnf_status(self, context, vnf_id,
|
||||
current_statuses, new_status, error_reason=None):
|
||||
'''Change vnf status and add error reason if error'''
|
||||
LOG.debug("Change status of vnf %s from %s to %s", vnf_id,
|
||||
current_statuses, new_status)
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
updated_values = {
|
||||
'status': new_status, 'updated_at': timeutils.utcnow()}
|
||||
vnf_model = (context.session.query(
|
||||
vnfm_db.VNF).filter_by(id=vnf_id).first())
|
||||
if not vnf_model:
|
||||
raise exceptions.VnfInstanceNotFound(
|
||||
message="VNF {} not found".format(vnf_id))
|
||||
if vnf_model.status not in current_statuses:
|
||||
raise exceptions.VnfConflictState(
|
||||
message='Cannot change status to {} \
|
||||
while in {}'.format(
|
||||
updated_values['status'], vnf_model.status))
|
||||
vnf_model.update(updated_values)
|
||||
|
||||
def _update_vnf_attributes(self, context, vnf_dict, current_statuses,
|
||||
new_status):
|
||||
with context.session.begin(subtransactions=True):
|
||||
try:
|
||||
modified_attributes = {}
|
||||
added_attributes = {}
|
||||
updated_values = {
|
||||
'mgmt_ip_address': vnf_dict['mgmt_ip_address'],
|
||||
'status': new_status,
|
||||
'updated_at': timeutils.utcnow()}
|
||||
vnf_model = (context.session.query(vnfm_db.VNF).filter_by(
|
||||
id=vnf_dict['id']).first())
|
||||
if not vnf_model:
|
||||
raise exceptions.VnfInstanceNotFound(
|
||||
message="VNF {} not found".format(vnf_dict['id']))
|
||||
if vnf_model.status not in current_statuses:
|
||||
raise exceptions.VnfConflictState(
|
||||
message='Cannot change status to {} while \
|
||||
in {}'.format(updated_values['status'],
|
||||
vnf_model.status))
|
||||
vnf_model.update(updated_values)
|
||||
|
||||
for key, val in vnf_dict['attributes'].items():
|
||||
vnf_attr_model = (context.session.query(
|
||||
vnfm_db.VNFAttribute).
|
||||
filter_by(vnf_id=vnf_dict['id']).
|
||||
filter_by(key=key).first())
|
||||
if vnf_attr_model:
|
||||
modified_attributes.update(
|
||||
{vnf_attr_model.key: vnf_attr_model.value})
|
||||
vnf_attr_model.update({'value': val})
|
||||
else:
|
||||
added_attributes.update({key: val})
|
||||
vnf_attr_model = vnfm_db.VNFAttribute(
|
||||
id=uuidutils.generate_uuid(),
|
||||
vnf_id=vnf_dict['id'],
|
||||
key=key, value=val)
|
||||
context.session.add(vnf_attr_model)
|
||||
|
||||
except Exception as exc:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Error in updating tables {}".format(str(exc)))
|
||||
# Roll back modified/added vnf attributes
|
||||
for key, val in modified_attributes.items():
|
||||
vnf_attr_model = (context.session.query(
|
||||
vnfm_db.VNFAttribute).
|
||||
filter_by(vnf_id=vnf_dict['id']).
|
||||
filter_by(key=key).first())
|
||||
if vnf_attr_model:
|
||||
vnf_attr_model.update({'value': val})
|
||||
|
||||
for key, val in added_attributes.items():
|
||||
vnf_attr_model = (context.session.query(
|
||||
vnfm_db.VNFAttribute).
|
||||
filter_by(vnf_id=vnf_dict['id']).
|
||||
filter_by(key=key).first())
|
||||
if vnf_attr_model:
|
||||
vnf_attr_model.delete()
|
||||
|
||||
@log.log
|
||||
def _build_instantiated_vnf_info(self, context, vnf_instance,
|
||||
instantiate_vnf_req=None):
|
||||
try:
|
||||
# if instantiate_vnf_req is not present, create from vnf_instance
|
||||
if not instantiate_vnf_req:
|
||||
instantiate_vnf_req = objects.InstantiateVnfRequest.\
|
||||
from_vnf_instance(vnf_instance)
|
||||
|
||||
# update instantiated vnf info based on created stack resources
|
||||
if hasattr(vnf_instance.instantiated_vnf_info, 'instance_id'):
|
||||
|
||||
# get final vnfd_dict
|
||||
vnfd_dict = vnflcm_utils._get_vnfd_dict(context,
|
||||
vnf_instance.vnfd_id,
|
||||
instantiate_vnf_req.flavour_id)
|
||||
|
||||
# get vim_connection info from request
|
||||
vim_info = vnflcm_utils._get_vim(context,
|
||||
instantiate_vnf_req.vim_connection_info)
|
||||
|
||||
vim_connection_info = objects.VimConnectionInfo.\
|
||||
obj_from_primitive(vim_info, context)
|
||||
|
||||
vnflcm_utils._build_instantiated_vnf_info(vnfd_dict,
|
||||
instantiate_vnf_req, vnf_instance,
|
||||
vim_id=vim_connection_info.vim_id)
|
||||
|
||||
if vnf_instance.instantiated_vnf_info.instance_id:
|
||||
self.vnf_manager.invoke(vim_connection_info.vim_type,
|
||||
'post_vnf_instantiation', context=context,
|
||||
vnf_instance=vnf_instance,
|
||||
vim_connection_info=vim_connection_info)
|
||||
|
||||
except Exception as ex:
|
||||
try:
|
||||
vnf_instance.instantiated_vnf_info.reinitialize()
|
||||
vnf_instance.instantiated_vnf_info.save()
|
||||
finally:
|
||||
error_msg = "Failed to build instantiation information \
|
||||
for vnf {} because {}".\
|
||||
format(vnf_instance.id, encodeutils.
|
||||
exception_to_unicode(ex))
|
||||
LOG.error("_build_instantiated_vnf_info error {}".
|
||||
format(error_msg))
|
||||
raise exceptions.TackerException(message=error_msg)
|
||||
|
||||
@log.log
|
||||
def _update_instantiated_vnf_info(
|
||||
self, context, vnf_instance, heal_vnf_request):
|
||||
try:
|
||||
vim_info = vnflcm_utils._get_vim(context,
|
||||
vnf_instance.vim_connection_info)
|
||||
vim_connection_info = \
|
||||
objects.VimConnectionInfo.obj_from_primitive(
|
||||
vim_info, context)
|
||||
|
||||
self.vnf_manager.invoke(
|
||||
vim_connection_info.vim_type, 'post_heal_vnf',
|
||||
context=context, vnf_instance=vnf_instance,
|
||||
vim_connection_info=vim_connection_info,
|
||||
heal_vnf_request=heal_vnf_request)
|
||||
|
||||
except Exception as exp:
|
||||
error_msg = \
|
||||
"Failed to update instantiation information for vnf {}: {}".\
|
||||
format(vnf_instance.id, encodeutils.exception_to_unicode(exp))
|
||||
LOG.error("_update_instantiated_vnf_info error {}".
|
||||
format(error_msg))
|
||||
raise exceptions.TackerException(message=error_msg)
|
||||
|
||||
@log.log
|
||||
def _add_additional_vnf_info(self, context, vnf_instance):
|
||||
'''this method adds misc info to 'vnf' table'''
|
||||
try:
|
||||
if hasattr(vnf_instance.instantiated_vnf_info, 'instance_id'):
|
||||
if vnf_instance.instantiated_vnf_info.instance_id:
|
||||
# add instance_id info
|
||||
instance_id = vnf_instance.instantiated_vnf_info.\
|
||||
instance_id
|
||||
with context.session.begin(subtransactions=True):
|
||||
updated_values = {'instance_id': instance_id}
|
||||
context.session.query(vnfm_db.VNF).filter_by(
|
||||
id=vnf_instance.id).update(updated_values)
|
||||
|
||||
except Exception as ex:
|
||||
# with excutils.save_and_reraise_exception():
|
||||
error_msg = "Failed to add additional vnf info to vnf {}. Details -\
|
||||
{}".format(
|
||||
vnf_instance.id, str(ex))
|
||||
LOG.error("_add_additional_vnf_info error {}".format(error_msg))
|
||||
raise exceptions.TackerException(message=error_msg)
|
||||
|
||||
@periodic_task.periodic_task(spacing=CONF.vnf_package_delete_interval)
|
||||
def _run_cleanup_vnf_packages(self, context):
|
||||
"""Delete orphan extracted csar zip and files from extracted path
|
||||
@ -490,7 +708,6 @@ class Conductor(manager.Manager):
|
||||
fields.LcmOccsOperationType.INSTANTIATE)
|
||||
operation_state = kwargs.get('operation_state',
|
||||
fields.LcmOccsOperationState.PROCESSING)
|
||||
evacuate_end_list = kwargs.get('evacuate_end_list', None)
|
||||
is_automatic_invocation = \
|
||||
kwargs.get('is_automatic_invocation', False)
|
||||
error = kwargs.get('error', None)
|
||||
@ -546,8 +763,7 @@ class Conductor(manager.Manager):
|
||||
operation_state == fields.LcmOccsOperationState.FAILED_TEMP):
|
||||
affected_resources = vnflcm_utils._get_affected_resources(
|
||||
old_vnf_instance=old_vnf_instance,
|
||||
new_vnf_instance=vnf_instance,
|
||||
extra_list=evacuate_end_list)
|
||||
new_vnf_instance=vnf_instance)
|
||||
affected_resources_snake_case = \
|
||||
utils.convert_camelcase_to_snakecase(affected_resources)
|
||||
resource_change_obj = \
|
||||
@ -666,6 +882,7 @@ class Conductor(manager.Manager):
|
||||
self,
|
||||
context,
|
||||
vnf_instance,
|
||||
vnf_dict,
|
||||
instantiate_vnf,
|
||||
vnf_lcm_op_occs_id):
|
||||
|
||||
@ -679,30 +896,23 @@ class Conductor(manager.Manager):
|
||||
request_obj=instantiate_vnf
|
||||
)
|
||||
|
||||
# Check if vnf is already instantiated.
|
||||
vnf_instance = objects.VnfInstance.get_by_id(context,
|
||||
vnf_instance.id)
|
||||
if vnf_instance.instantiation_state == \
|
||||
fields.VnfInstanceState.INSTANTIATED:
|
||||
LOG.error("Vnf instance %(id)s is already in %(state)s state.",
|
||||
{"id": vnf_instance.id,
|
||||
"state": vnf_instance.instantiation_state})
|
||||
return
|
||||
# change vnf_status
|
||||
if vnf_dict['status'] == 'INACTIVE':
|
||||
vnf_dict['status'] = 'PENDING_CREATE'
|
||||
self._change_vnf_status(context, vnf_instance.id,
|
||||
_INACTIVE_STATUS, 'PENDING_CREATE')
|
||||
|
||||
self.vnflcm_driver.instantiate_vnf(context, vnf_instance,
|
||||
instantiate_vnf)
|
||||
vnf_dict, instantiate_vnf)
|
||||
self._build_instantiated_vnf_info(context,
|
||||
vnf_instance,
|
||||
instantiate_vnf_req=instantiate_vnf)
|
||||
|
||||
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,
|
||||
expected_attrs=['vnfd'])
|
||||
try:
|
||||
self._update_package_usage_state(context, vnf_package)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Failed to update usage_state of vnf package %s",
|
||||
vnf_package.id)
|
||||
self._update_vnf_attributes(context, vnf_dict,
|
||||
_PENDING_STATUS, _ACTIVE_STATUS)
|
||||
self.vnflcm_driver._vnf_instance_update(context, vnf_instance,
|
||||
instantiation_state=fields.VnfInstanceState.
|
||||
INSTANTIATED, task_state=None)
|
||||
|
||||
# Update vnf_lcm_op_occs table and send notification "COMPLETED"
|
||||
self._send_lcm_op_occ_notification(
|
||||
@ -715,6 +925,12 @@ class Conductor(manager.Manager):
|
||||
)
|
||||
|
||||
except Exception as ex:
|
||||
self._change_vnf_status(context, vnf_instance.id,
|
||||
_PENDING_STATUS, 'ERROR')
|
||||
|
||||
self._build_instantiated_vnf_info(context, vnf_instance,
|
||||
instantiate_vnf)
|
||||
|
||||
# Update vnf_lcm_op_occs table and send notification "FAILED_TEMP"
|
||||
self._send_lcm_op_occ_notification(
|
||||
context=context,
|
||||
@ -730,17 +946,6 @@ class Conductor(manager.Manager):
|
||||
def terminate(self, context, vnf_lcm_op_occs_id,
|
||||
vnf_instance, terminate_vnf_req, vnf_dict):
|
||||
try:
|
||||
# Check if vnf is in instantiated state.
|
||||
vnf_instance = objects.VnfInstance.get_by_id(context,
|
||||
vnf_instance.id)
|
||||
if vnf_instance.instantiation_state == \
|
||||
fields.VnfInstanceState.NOT_INSTANTIATED:
|
||||
LOG.error("Terminate action cannot be performed on vnf %(id)s "
|
||||
"which is in %(state)s state.",
|
||||
{"id": vnf_instance.id,
|
||||
"state": vnf_instance.instantiation_state})
|
||||
return
|
||||
|
||||
old_vnf_instance = copy.deepcopy(vnf_instance)
|
||||
|
||||
# Update vnf_lcm_op_occs table and send notification "PROCESSING"
|
||||
@ -753,22 +958,18 @@ class Conductor(manager.Manager):
|
||||
operation=fields.LcmOccsOperationType.TERMINATE
|
||||
)
|
||||
|
||||
self.vnflcm_driver.terminate_vnf(context, vnf_instance,
|
||||
terminate_vnf_req,
|
||||
vnf_lcm_op_occs_id)
|
||||
self._change_vnf_status(context, vnf_instance.id,
|
||||
_ACTIVE_STATUS, 'PENDING_TERMINATE')
|
||||
|
||||
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,
|
||||
expected_attrs=['vnfd'])
|
||||
try:
|
||||
self._update_package_usage_state(context, vnf_package)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Failed to update usage_state of vnf package %s",
|
||||
vnf_package.id)
|
||||
self.vnflcm_driver.terminate_vnf(context, vnf_instance,
|
||||
terminate_vnf_req)
|
||||
self._change_vnf_status(context, vnf_instance.id,
|
||||
_PENDING_STATUS, 'INACTIVE')
|
||||
|
||||
self.vnflcm_driver._vnf_instance_update(context, vnf_instance,
|
||||
vim_connection_info=[], task_state=None,
|
||||
instantiation_state=fields.VnfInstanceState.NOT_INSTANTIATED)
|
||||
vnf_instance.instantiated_vnf_info.reinitialize()
|
||||
|
||||
# Update vnf_lcm_op_occs table and send notification "COMPLETED"
|
||||
self._send_lcm_op_occ_notification(
|
||||
@ -782,6 +983,10 @@ class Conductor(manager.Manager):
|
||||
)
|
||||
|
||||
except Exception as exc:
|
||||
# set vnf_status to error
|
||||
self._change_vnf_status(context, vnf_instance.id,
|
||||
_PENDING_STATUS, 'ERROR')
|
||||
|
||||
# Update vnf_lcm_op_occs table and send notification "FAILED_TEMP"
|
||||
self._send_lcm_op_occ_notification(
|
||||
context=context,
|
||||
@ -803,19 +1008,6 @@ class Conductor(manager.Manager):
|
||||
vnf_lcm_op_occs_id):
|
||||
|
||||
try:
|
||||
evacuate_end_list = []
|
||||
|
||||
# Check if vnf is in instantiated state.
|
||||
vnf_instance = objects.VnfInstance.get_by_id(context,
|
||||
vnf_instance.id)
|
||||
if vnf_instance.instantiation_state == \
|
||||
fields.VnfInstanceState.NOT_INSTANTIATED:
|
||||
LOG.error("Heal action cannot be performed on vnf %(id)s "
|
||||
"which is in %(state)s state.",
|
||||
{"id": vnf_instance.id,
|
||||
"state": vnf_instance.instantiation_state})
|
||||
return
|
||||
|
||||
old_vnf_instance = copy.deepcopy(vnf_instance)
|
||||
|
||||
# Update vnf_lcm_op_occs table and send notification "PROCESSING"
|
||||
@ -828,15 +1020,25 @@ class Conductor(manager.Manager):
|
||||
operation=fields.LcmOccsOperationType.HEAL
|
||||
)
|
||||
|
||||
heal_result = \
|
||||
self.vnflcm_driver.heal_vnf(context, vnf_instance, vnf_dict,
|
||||
heal_vnf_request,
|
||||
vnf_lcm_op_occs_id)
|
||||
# update vnf status to PENDING_HEAL
|
||||
self._change_vnf_status(context, vnf_instance.id,
|
||||
_ACTIVE_STATUS, constants.PENDING_HEAL)
|
||||
self.vnflcm_driver.heal_vnf(context, vnf_instance,
|
||||
vnf_dict, heal_vnf_request)
|
||||
self._update_instantiated_vnf_info(context, vnf_instance,
|
||||
heal_vnf_request)
|
||||
|
||||
# update instance_in in vnf_table
|
||||
self._add_additional_vnf_info(context, vnf_instance)
|
||||
# update vnf status to ACTIVE
|
||||
self._change_vnf_status(context, vnf_instance.id,
|
||||
_PENDING_STATUS, constants.ACTIVE)
|
||||
|
||||
# during .save() ,instantiated_vnf_info is also saved to DB
|
||||
self.vnflcm_driver._vnf_instance_update(context, vnf_instance,
|
||||
task_state=None)
|
||||
|
||||
# update vnf_lcm_op_occs and send notification "COMPLETED"
|
||||
if heal_result:
|
||||
evacuate_end_list = heal_result.get('evacuate_end_list')
|
||||
|
||||
self._send_lcm_op_occ_notification(
|
||||
context=context,
|
||||
vnf_lcm_op_occs_id=vnf_lcm_op_occs_id,
|
||||
@ -844,10 +1046,16 @@ class Conductor(manager.Manager):
|
||||
vnf_instance=vnf_instance,
|
||||
request_obj=heal_vnf_request,
|
||||
operation=fields.LcmOccsOperationType.HEAL,
|
||||
operation_state=fields.LcmOccsOperationState.COMPLETED,
|
||||
evacuate_end_list=evacuate_end_list
|
||||
operation_state=fields.LcmOccsOperationState.COMPLETED
|
||||
)
|
||||
except Exception as ex:
|
||||
# update vnf_status to 'ERROR' and create event with 'ERROR' status
|
||||
self._change_vnf_status(context, vnf_instance,
|
||||
_PENDING_STATUS, constants.ERROR, str(ex))
|
||||
|
||||
# call _update_instantiated_vnf_info for notification
|
||||
self._update_instantiated_vnf_info(context, vnf_instance,
|
||||
heal_vnf_request)
|
||||
|
||||
# update vnf_lcm_op_occs and send notification "FAILED_TEMP"
|
||||
self._send_lcm_op_occ_notification(
|
||||
@ -858,7 +1066,6 @@ class Conductor(manager.Manager):
|
||||
request_obj=heal_vnf_request,
|
||||
operation=fields.LcmOccsOperationType.HEAL,
|
||||
operation_state=fields.LcmOccsOperationState.FAILED_TEMP,
|
||||
evacuate_end_list=evacuate_end_list,
|
||||
error=str(ex)
|
||||
)
|
||||
|
||||
|
@ -56,7 +56,28 @@ Possible values:
|
||||
|
||||
Related options:
|
||||
* None
|
||||
"""))]
|
||||
""")),
|
||||
|
||||
cfg.ListOpt('get_top_list',
|
||||
default=['tosca_definitions_version',
|
||||
'description', 'metadata'],
|
||||
help=_("List of items to get from top-vnfd")),
|
||||
|
||||
cfg.ListOpt('exclude_node',
|
||||
default=['VNF'],
|
||||
help=_("Exclude node from node_template")),
|
||||
|
||||
cfg.ListOpt('get_lower_list',
|
||||
default=['tosca.nodes.nfv.VNF', 'tosca.nodes.nfv.VDU.Tacker'],
|
||||
help=_("List of types to get from lower-vnfd")),
|
||||
|
||||
cfg.ListOpt('del_input_list',
|
||||
default=['descriptor_id', 'descriptor_version'
|
||||
'provider', 'product_name', 'software_version',
|
||||
'vnfm_info', 'flavour_id', 'flavour_description'],
|
||||
help=_("List of del inputs from lower-vnfd")),
|
||||
|
||||
]
|
||||
|
||||
vnf_package_group = cfg.OptGroup('vnf_package',
|
||||
title='vnf_package options',
|
||||
|
@ -201,8 +201,8 @@ class VnfInstance(model_base.BASE, models.SoftDeleteMixin,
|
||||
task_state = sa.Column(sa.String(255), nullable=True)
|
||||
vim_connection_info = sa.Column(sa.JSON(), nullable=True)
|
||||
tenant_id = sa.Column('tenant_id', sa.String(length=64), nullable=False)
|
||||
vnf_metadata = sa.Column(sa.JSON(), nullable=True)
|
||||
vnf_pkg_id = sa.Column(types.Uuid, nullable=False)
|
||||
vnf_metadata |