Support multiple conductors onboarding

In current implementation,
onboarding process doesn't work when using Tacker as N-Act cluster.

This patch enables all conductors in the N-Act cluster
to download or delete VNF Package.

The functional test for multi-conductors environment will be added
in another patch.

Co-Authored-By: Hitomi Koba <hi-koba@kddi.com>
Co-Authored-By: Yukihiro Kinjo <yu-kinjou@kddi.com>
Co-Authored-By: Xu Hongjin <ho-xu@kddi.com>
Implements: blueprint support-multi-conductors-onboarding
Change-Id: Iabb100872d0d78bebc29378592dffba8e83710fc
This commit is contained in:
ma-ooyama.kddi.com 2023-02-24 04:03:56 +00:00
parent f75827612f
commit 3fe7831d63
12 changed files with 327 additions and 145 deletions

View File

@ -0,0 +1,6 @@
---
features:
- |
VNF package management APIs in multiple conductors environment
are supported that includes Upload VNF Package from content,
Upload VNF Package from uri and Delete VNF Package.

View File

@ -223,8 +223,20 @@ class VnfPkgmController(wsgi.Controller):
"operational_state": vnf_package.operational_state,
"usage_state": vnf_package.usage_state})
# Delete vnf_package
self.rpc_api.delete_vnf_package(context, vnf_package)
# Controller deletes vnf_package just from DB here.
# Related objects in Legacy DB are also deleted.
# Actual vnf_packages that is in backend storage
# or each conductors' local filesystems are deleted after.
if vnf_package.vnfd is not None:
objects.VnfdAttribute(context).delete(vnf_package.vnfd.vnfd_id)
objects.Vnfd(context).delete(vnf_package.vnfd.vnfd_id)
vnf_package.destroy(context)
if vnf_package.onboarding_state == (
fields.PackageOnboardingStateType.ONBOARDED):
# send rpc message for deleting actual vnf_package
# to conductors
self.rpc_api.delete_vnf_package(context, vnf_package)
@wsgi.response(http_client.ACCEPTED)
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND,

View File

@ -567,14 +567,9 @@ def delete_csar_data(package_uuid):
csar_path = os.path.join(CONF.vnf_package.vnf_package_csar_path,
package_uuid + ".zip")
try:
shutil.rmtree(csar_zip_temp_path)
shutil.rmtree(csar_zip_temp_path, ignore_errors=True)
if os.path.isfile(csar_path):
os.remove(csar_path)
except OSError as exc:
exc_message = encodeutils.exception_to_unicode(exc)
msg = _('Failed to delete csar folder: '
'%(csar_path)s, Error: %(exc)s')
LOG.error(msg, {'csar_path': csar_path, 'exc': exc_message})
class PreserveZipFilePermissions(zipfile.ZipFile):

View File

@ -46,6 +46,7 @@ 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 rpc
from tacker.common import safe_utils
from tacker.common import topics
from tacker.common import utils
@ -59,6 +60,7 @@ from tacker import objects
from tacker.objects import fields
from tacker.objects.fields import ErrorPoint as EP
from tacker.objects.vnf_lcm_subscriptions import LccnSubscriptionRequest
from tacker.objects import vnf_package as vnf_package_obj
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
@ -115,14 +117,6 @@ def config_opts():
return [('keystone_authtoken', OPTS)]
def _delete_csar(context, vnf_package):
# Delete from glance store
glance_store.delete_csar(context, vnf_package.id,
vnf_package.location_glance_store)
csar_utils.delete_csar_data(vnf_package.id)
@utils.expects_func_args('vnf_package')
def revert_upload_vnf_package(function):
"""Decorator to revert upload_vnf_package on failure."""
@ -144,7 +138,17 @@ def revert_upload_vnf_package(function):
glance_store.delete_csar(context, vnf_package.id,
vnf_package.location_glance_store)
csar_utils.delete_csar_data(vnf_package.id)
target = oslo_messaging.Target(
exchange='tacker',
topic=topics.TOPIC_CONDUCTOR,
fanout=True,
version='1.0')
serializer = objects.base.TackerObjectSerializer()
client = rpc.get_client(target, version_cap=None,
serializer=serializer)
cctxt = client.prepare()
rpc_method = cctxt.cast
rpc_method(context, 'delete_csar', vnf_package=vnf_package)
# Delete the vnf_deployment_flavour if created.
if vnf_package.vnf_deployment_flavours:
@ -333,6 +337,28 @@ class Conductor(manager.Manager, v2_hook.ConductorV2Hook):
def init_host(self):
glance_store.initialize_glance_store()
self._basic_config_check()
self.init_csar_files()
def init_csar_files(self):
context = t_context.get_admin_context()
filters = {'field': 'onboarding_state', 'model': 'VnfPackage',
'value': fields.PackageOnboardingStateType.ONBOARDED,
'op': '=='}
vnf_packages = vnf_package_obj.VnfPackagesList.get_by_filters(
context, read_deleted='no', filters=filters)
for vnf_package in vnf_packages:
try:
location = vnf_package.location_glance_store
zip_path = os.path.join(CONF.vnf_package.vnf_package_csar_path,
vnf_package.id)
if not os.path.exists(zip_path):
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))
except Exception as error_msg:
raise exceptions.TackerException(message=error_msg)
def _get_vnf_instance_href(self, vnf_instance_id):
return '{endpoint}/vnflcm/v1/vnf_instances/{id}'.format(
@ -457,16 +483,27 @@ class Conductor(manager.Manager, v2_hook.ConductorV2Hook):
vnfd_attribute.create()
@revert_upload_vnf_package
def upload_vnf_package_content(self, context, vnf_package):
vnf_package.onboarding_state = (
fields.PackageOnboardingStateType.PROCESSING)
try:
vnf_package.save()
def load_csar_data(self, context, vnf_package):
with context.session.begin(subtransactions=True):
vnf_package = vnf_package_obj.VnfPackage.get_by_id_with_lock(
context, vnf_package.id)
vnf_package.downloading += 1
context.session.commit()
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)
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)
with context.session.begin(subtransactions=True):
vnf_package = vnf_package_obj.VnfPackage.get_by_id_with_lock(
context, vnf_package.id)
vnf_package.downloading -= 1
context.session.commit()
vnf_package = vnf_package_obj.VnfPackage.get_by_id(
context, vnf_package.id)
if (vnf_package.downloading == 0) and (vnf_package.onboarding_state
== fields.PackageOnboardingStateType.PROCESSING):
self._onboard_vnf_package(
context,
vnf_package,
@ -479,6 +516,27 @@ class Conductor(manager.Manager, v2_hook.ConductorV2Hook):
fields.PackageOperationalStateType.ENABLED)
vnf_package.save()
@revert_upload_vnf_package
def upload_vnf_package_content(self, context, vnf_package):
vnf_package.onboarding_state = (
fields.PackageOnboardingStateType.PROCESSING)
vnf_package.downloading = 0
target = oslo_messaging.Target(
exchange='tacker',
topic=topics.TOPIC_CONDUCTOR,
fanout=True,
version='1.0')
serializer = objects.base.TackerObjectSerializer()
client = rpc.get_client(target, version_cap=None,
serializer=serializer)
cctxt = client.prepare()
rpc_method = cctxt.cast
try:
vnf_package.save()
rpc_method(context, 'load_csar_data', vnf_package=vnf_package)
except Exception as msg:
raise Exception(msg)
@ -495,6 +553,7 @@ class Conductor(manager.Manager, v2_hook.ConductorV2Hook):
vnf_package.onboarding_state = (
fields.PackageOnboardingStateType.PROCESSING)
vnf_package.downloading = 0
vnf_package.algorithm = CONF.vnf_package.hashing_algorithm
vnf_package.hash = multihash
@ -502,36 +561,43 @@ class Conductor(manager.Manager, v2_hook.ConductorV2Hook):
vnf_package.size = size
vnf_package.save()
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)
target = oslo_messaging.Target(
exchange='tacker',
topic=topics.TOPIC_CONDUCTOR,
fanout=True,
version='1.0')
self._onboard_vnf_package(
context,
vnf_package,
vnf_data,
flavours,
vnf_artifacts)
serializer = objects.base.TackerObjectSerializer()
client = rpc.get_client(target, version_cap=None,
serializer=serializer)
cctxt = client.prepare()
rpc_method = cctxt.cast
vnf_package.onboarding_state = (
fields.PackageOnboardingStateType.ONBOARDED)
vnf_package.operational_state = (
fields.PackageOperationalStateType.ENABLED)
try:
vnf_package.save()
rpc_method(context, 'load_csar_data', vnf_package=vnf_package)
except Exception as msg:
raise Exception(msg)
vnf_package.save()
def delete_csar(self, context, vnf_package):
csar_utils.delete_csar_data(vnf_package.id)
def delete_vnf_package(self, context, vnf_package):
if (vnf_package.onboarding_state ==
fields.PackageOnboardingStateType.ONBOARDED):
# Delete from glance store
glance_store.delete_csar(context, vnf_package.id,
vnf_package.location_glance_store)
_delete_csar(context, vnf_package)
# TODO(h-asahina): stop using these Legacy DB
if vnf_package.vnfd is not None:
objects.VnfdAttribute(context).delete(vnf_package.vnfd.vnfd_id)
objects.Vnfd(context).delete(vnf_package.vnfd.vnfd_id)
vnf_package.destroy(context)
target = oslo_messaging.Target(
exchange='tacker',
topic=topics.TOPIC_CONDUCTOR,
fanout=True,
version='1.0')
serializer = objects.base.TackerObjectSerializer()
client = rpc.get_client(target, version_cap=None,
serializer=serializer)
cctxt = client.prepare()
rpc_method = cctxt.cast
rpc_method(context, 'delete_csar', vnf_package=vnf_package)
def get_vnf_package_vnfd(self, context, vnf_package):
csar_path = os.path.join(CONF.vnf_package.vnf_package_csar_path,

View File

@ -171,6 +171,7 @@ class VnfPackage(model_base.BASE, models.SoftDeleteMixin,
hash = sa.Column(sa.String(128), nullable=True)
location_glance_store = sa.Column(sa.Text(), nullable=True)
size = sa.Column(sa.BigInteger, nullable=False, default=0)
downloading = sa.Column(sa.Integer, nullable=False, default=0)
_metadata = orm.relationship(
VnfPackageUserData,

View File

@ -0,0 +1,36 @@
# Copyright 2023 OpenStack Foundation
#
# 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.
#
"""add_downloading_to_vnfPackage
Revision ID: 34cfceb25a49
Revises: 2531c976c0f1
Create Date: 2023-03-08 01:56:40.134793
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '34cfceb25a49'
down_revision = '2531c976c0f1'
def upgrade(active_plugins=None, options=None):
op.add_column('vnf_packages', sa.Column('downloading', sa.Integer(),
nullable=False,
server_default='0'))

View File

@ -1 +1 @@
2531c976c0f1
34cfceb25a49

View File

@ -380,7 +380,8 @@ class VnfPackage(base.TackerObject, base.TackerPersistentObject,
'vnfd': fields.ObjectField('VnfPackageVnfd', nullable=True),
'size': fields.IntegerField(nullable=False, default=0),
'vnf_artifacts': fields.ObjectField('VnfPackageArtifactInfoList',
nullable=True)
nullable=True),
'downloading': fields.IntegerField(nullable=False, default=0)
}
def __init__(self, context=None, **kwargs):
@ -569,6 +570,12 @@ class VnfPackage(base.TackerObject, base.TackerPersistentObject,
return cls._from_db_object(context, cls(), db_vnf_package,
expected_attrs=expected_attrs)
@base.remotable_classmethod
def get_by_id_with_lock(cls, context, id):
vnf_package = context.session.query(models.VnfPackage).filter(
models.VnfPackage.id == id).with_for_update().one()
return vnf_package
@base.remotable
def destroy(self, context):
if not self.obj_attr_is_set('id'):

View File

@ -384,9 +384,14 @@ class TestCSARUtils(testtools.TestCase):
self.assertEqual("No VNF flavours are available",
exc.format_message())
@mock.patch.object(os.path, 'isfile')
@mock.patch.object(os.path, 'exists')
@mock.patch.object(os, 'remove')
@mock.patch.object(shutil, 'rmtree')
def test_delete_csar_data(self, mock_rmtree, mock_remove):
def test_delete_csar_data(self, mock_rmtree,
mock_remove, mock_exists, mock_isfile):
mock_exists.return_value = True
mock_isfile.return_value = True
csar_utils.delete_csar_data(constants.UUID)
mock_rmtree.assert_called()
mock_remove.assert_called()

View File

@ -37,13 +37,12 @@ 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.rpc import BackingOffClient
from tacker.conductor import conductor_server
import tacker.conf
from tacker import context
from tacker import context as t_context
from tacker.db.db_sqlalchemy import api
from tacker.db.db_sqlalchemy import models
from tacker.db.vnfm import vnfm_db
from tacker.glance_store import store as glance_store
from tacker import objects
from tacker.objects import fields
@ -106,6 +105,7 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
self.vnfd_pkg_data = self._create_vnfd_pkg_data()
cfg.CONF.set_override('retry_wait', 0, group='vnf_lcm')
self.addCleanup(cfg.CONF.clear_override, 'retry_wait', group='vnf_lcm')
self.cctxt_mock = mock.MagicMock()
def _mock_vnfm_plugin(self):
self.vnfm_plugin = mock.Mock(wraps=FakeVNFMPlugin())
@ -204,35 +204,15 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
"Authorization")
self.assertEqual("Bearer " + expected_token, actual_auth)
@mock.patch.object(conductor_server.Conductor, '_onboard_vnf_package')
@mock.patch.object(conductor_server, 'revert_upload_vnf_package')
@mock.patch.object(csar_utils, 'load_csar_data')
@mock.patch.object(glance_store, 'load_csar')
def test_upload_vnf_package_content(self, mock_load_csar,
mock_load_csar_data,
mock_revert, mock_onboard):
mock_load_csar_data.return_value = (mock.ANY, mock.ANY, mock.ANY)
mock_load_csar.return_value = '/var/lib/tacker/5f5d99c6-844a-4c3' \
'1-9e6d-ab21b87dcfff.zip'
self.conductor.upload_vnf_package_content(
self.context, self.vnf_package)
mock_load_csar.assert_called()
mock_load_csar_data.assert_called()
mock_onboard.assert_called()
@mock.patch.object(conductor_server.Conductor, '_onboard_vnf_package')
@mock.patch.object(BackingOffClient, 'prepare')
@mock.patch.object(glance_store, 'store_csar')
@mock.patch.object(conductor_server, 'revert_upload_vnf_package')
@mock.patch.object(csar_utils, 'load_csar_data')
@mock.patch.object(glance_store, 'load_csar')
def test_upload_vnf_package_from_uri(self, mock_load_csar,
mock_load_csar_data,
mock_revert, mock_store,
mock_onboard):
def test_upload_vnf_package_from_uri(self,
mock_revert,
mock_store,
mock_prepare):
mock_prepare.return_value = self.cctxt_mock
address_information = "http://test.zip"
mock_load_csar_data.return_value = (mock.ANY, mock.ANY, mock.ANY)
mock_load_csar.return_value = '/var/lib/tacker/5f5d99c6-844a' \
'-4c31-9e6d-ab21b87dcfff.zip'
mock_store.return_value = 'location', 0, 'checksum',\
'multihash', 'loc_meta'
self.conductor.upload_vnf_package_from_uri(self.context,
@ -240,58 +220,54 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
address_information,
user_name=None,
password=None)
mock_load_csar.assert_called()
mock_load_csar_data.assert_called()
mock_store.assert_called()
mock_onboard.assert_called()
mock_prepare.assert_called()
self.cctxt_mock.cast.assert_called_once_with(
self.context, 'load_csar_data', vnf_package=self.vnf_package)
self.assertEqual('multihash', self.vnf_package.hash)
self.assertEqual('location', self.vnf_package.location_glance_store)
@mock.patch.object(csar_utils, 'load_csar_data')
@mock.patch.object(glance_store, 'load_csar')
@mock.patch.object(objects.vnf_package.VnfPackagesList, 'get_by_filters')
def test_init_csar_files(self, mock_pkg_list,
mock_load_csar, mock_load_csar_data):
fake_csar = os.path.join(self.temp_dir, self.vnf_package.id)
self.vnf_package.location = fake_csar
mock_pkg_list.return_value = [self.vnf_package]
mock_load_csar.return_value = True
mock_load_csar_data.return_value = (mock.ANY, mock.ANY, mock.ANY)
self.conductor.init_csar_files()
mock_load_csar.assert_called()
mock_load_csar_data.assert_called()
@mock.patch.object(csar_utils, 'load_csar_data')
@mock.patch.object(glance_store, 'load_csar')
@mock.patch.object(objects.vnf_package.VnfPackagesList, 'get_by_filters')
def test_init_csar_files_exception(self, mock_pkg_list,
mock_load_csar, mock_load_csar_data):
fake_csar = os.path.join(self.temp_dir, self.vnf_package.id)
self.vnf_package.location = fake_csar
mock_pkg_list.return_value = [self.vnf_package]
mock_load_csar.return_value = True
mock_load_csar_data.side_effect = exceptions.InvalidCSAR
self.assertRaises(exceptions.TackerException,
self.conductor.init_csar_files)
@mock.patch.object(BackingOffClient, 'prepare')
@mock.patch.object(glance_store, 'delete_csar')
def test_delete_vnf_package(self, mock_delete_csar):
def test_delete_vnf_package(self, mock_delete_csar, mock_prepare):
self.vnf_package.onboarding_state = 'ONBOARDED'
vnfd_id = uuidsentinel.vnfd_id
# create VnfPackageVnfd
vnf_pack_vnfd_data = fake_obj.get_vnf_package_vnfd_data(
self.vnf_package.id, vnfd_id)
vnf_pack_vnfd = objects.VnfPackageVnfd(
self.context, **vnf_pack_vnfd_data)
vnf_pack_vnfd.create()
self.vnf_package.vnfd = vnf_pack_vnfd
# create Legacy Vnfd
vnfd = objects.Vnfd(self.context)
vnfd.id = vnfd_id
vnfd.tenant_id = uuidsentinel.tenant_id
vnfd.name = 'dummy_name'
vnfd.create()
# create Legacy VnfdAttribute
vnfd_attr = objects.VnfdAttribute(self.context)
vnfd_attr.id = uuidsentinel.vnfd_attr_id
vnfd_attr.vnfd_id = vnfd_id
vnfd_attr.key = 'dummy_key'
vnfd_attr.create()
# check Vnfd and VnfdAttribute were created
self.assertIsNotNone(
api.model_query(self.context, vnfm_db.VNFDAttribute)
.filter_by(vnfd_id=vnfd_id).first())
self.assertIsNotNone(
api.model_query(self.context, vnfm_db.VNFD)
.filter_by(id=vnfd_id).first())
mock_prepare.return_value = self.cctxt_mock
self.conductor.delete_vnf_package(self.context, self.vnf_package)
# check Vnfd and VnfdAttribute were deleted
mock_delete_csar.assert_called()
self.assertIsNone(
api.model_query(self.context, vnfm_db.VNFDAttribute)
.filter_by(vnfd_id=vnfd_id).first())
self.assertIsNone(
api.model_query(self.context, vnfm_db.VNFD)
.filter_by(id=vnfd_id).first())
mock_prepare.assert_called()
self.cctxt_mock.cast.assert_called_once_with(
self.context, 'delete_csar', vnf_package=self.vnf_package)
def test_get_vnf_package_vnfd_with_tosca_meta_file_in_csar(self):
fake_csar = fakes.create_fake_csar_dir(self.vnf_package.id,
@ -3497,37 +3473,112 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
self.vnfd_pkg_data,
vnfd_id)
@mock.patch.object(BackingOffClient, 'prepare')
@mock.patch.object(objects.vnf_package.VnfPackage, 'save')
@mock.patch.object(glance_store, 'delete_csar')
@mock.patch.object(csar_utils, 'load_csar_data')
@mock.patch.object(glance_store, 'load_csar')
@mock.patch.object(glance_store, 'delete_csar')
def test_revert_upload_vnf_package_in_upload_vnf_package_content(self,
mock_load_csar,
mock_load_csar_data,
mock_delete_csar):
mock_load_csar_data.return_value = (mock.ANY, mock.ANY, mock.ANY)
def test_revert_upload_vnf_package_in_load_csar_data(self,
mock_load_csar,
mock_load_csar_data,
mock_delete_csar,
mock_save,
mock_prepare):
self.vnf_package.onboarding_state = "PROCESSING"
self.vnf_package.save()
mock_load_csar.return_value = '/var/lib/tacker/5f5d99c6-844a-4c3' \
'1-9e6d-ab21b87dcfff.zip'
mock_delete_csar.return_value = ""
self.assertRaisesRegex(Exception,
"not enough values to unpack",
self.conductor.upload_vnf_package_content,
self.context, self.vnf_package)
mock_load_csar_data.side_effect = exceptions.InvalidCSAR
mock_prepare.return_value = self.cctxt_mock
@mock.patch.object(conductor_server, 'revert_upload_vnf_package')
self.assertRaises(exceptions.InvalidCSAR,
self.conductor.load_csar_data,
self.context, self.vnf_package)
mock_delete_csar.assert_called()
mock_prepare.assert_called()
self.cctxt_mock.cast.assert_called_once_with(
self.context, 'delete_csar', vnf_package=self.vnf_package)
self.assertEqual(self.vnf_package.onboarding_state, "CREATED")
@mock.patch.object(conductor_server.Conductor, '_onboard_vnf_package')
@mock.patch.object(csar_utils, 'load_csar_data')
@mock.patch.object(glance_store, 'load_csar')
def test_onboard_vnf_package_through_upload_vnf_package_content(self,
mock_load_csar,
mock_load_csar_data,
mock_revert):
def test_load_csar_data(self, mock_load_csar,
mock_load_csar_data, mock_onboard):
self.vnf_package.onboarding_state = "PROCESSING"
self.vnf_package.save()
vnf_data = fakes.get_vnf_package_vnfd()
vnf_data = self._rename_vnfdata_keys(vnf_data)
mock_load_csar_data.return_value = (vnf_data, [], [])
mock_load_csar.return_value = '/var/lib/tacker/5f5d99c6-844a-4c3' \
'1-9e6d-ab21b87dcfff.zip'
fake_context = context.Context(tenant_id=uuidsentinel.tenant_id)
self.conductor.load_csar_data(self.context, self.vnf_package)
mock_load_csar.assert_called()
mock_load_csar_data.assert_called()
mock_onboard.assert_called()
@mock.patch.object(conductor_server.Conductor, '_onboard_vnf_package')
@mock.patch.object(csar_utils, 'load_csar_data')
@mock.patch.object(glance_store, 'load_csar')
def test_load_csar_data_with_onboarding_state_created(self, mock_load_csar,
mock_load_csar_data, mock_onboard):
self.vnf_package.onboarding_state = "PROCESSING"
self.vnf_package.save()
vnf_data = fakes.get_vnf_package_vnfd()
vnf_data = self._rename_vnfdata_keys(vnf_data)
mock_load_csar_data.return_value = (vnf_data, [], [])
def f():
vnf_pkg = objects.VnfPackage.get_by_id(
self.context, self.vnf_package.id)
vnf_pkg.onboarding_state = "CREATED"
vnf_pkg.save()
return (mock.ANY, mock.ANY, mock.ANY)
mock_load_csar_data.return_value = f()
mock_load_csar.return_value = '/var/lib/tacker/5f5d99c6-844a-4c3' \
'1-9e6d-ab21b87dcfff.zip'
self.conductor.load_csar_data(self.context, self.vnf_package)
mock_load_csar.assert_called()
mock_load_csar_data.assert_called()
vnf_pkg = objects.VnfPackage.get_by_id(
self.context, self.vnf_package.id)
self.assertEqual(vnf_pkg.onboarding_state, "CREATED")
mock_onboard.assert_not_called()
@mock.patch.object(conductor_server.Conductor, '_onboard_vnf_package')
@mock.patch.object(csar_utils, 'load_csar_data')
@mock.patch.object(glance_store, 'load_csar')
def test_load_csar_data_downloading_not_completed(self, mock_load_csar,
mock_load_csar_data, mock_onboard):
self.vnf_package.onboarding_state = "PROCESSING"
self.vnf_package.downloading = 1
self.vnf_package.save()
vnf_data = fakes.get_vnf_package_vnfd()
vnf_data = self._rename_vnfdata_keys(vnf_data)
mock_load_csar_data.return_value = (vnf_data, [], [])
mock_load_csar.return_value = '/var/lib/tacker/5f5d99c6-844a-4c3' \
'1-9e6d-ab21b87dcfff.zip'
self.conductor.load_csar_data(self.context, self.vnf_package)
mock_load_csar.assert_called()
mock_load_csar_data.assert_called()
mock_onboard.assert_not_called()
vnf_pkg = objects.VnfPackage.get_by_id(
self.context, self.vnf_package.id)
self.assertEqual(vnf_pkg.onboarding_state, "PROCESSING")
self.assertEqual(vnf_pkg.downloading, 1)
@mock.patch.object(BackingOffClient, 'prepare')
def test_onboard_vnf_package_through_upload_vnf_package_content(
self, mock_prepare):
mock_prepare.return_value = self.cctxt_mock
self.conductor.upload_vnf_package_content(
fake_context, self.vnf_package)
self.context, self.vnf_package)
mock_prepare.assert_called()
self.cctxt_mock.cast.assert_called_once_with(
self.context, 'load_csar_data',
vnf_package=self.vnf_package)
def _rename_vnfdata_keys(self, vnf_data):
vnf_data["descriptor_id"] = vnf_data.pop("id")

View File

@ -31,7 +31,8 @@ vnf_package_data = {'algorithm': None, 'hash': None,
'created_at': datetime.datetime(
2019, 8, 8, 0, 0, 0, tzinfo=iso8601.UTC),
'deleted': False,
'size': 0
'size': 0,
"downloading": 0
}
software_image = {

View File

@ -647,9 +647,11 @@ class TestController(base.TestCase):
if expected_result_link is not None:
self.assertEqual(expected_result_link, res_dict.headers['Link'])
@mock.patch.object(vnf_package.VnfPackage, "destroy")
@mock.patch.object(vnf_package.VnfPackage, "get_by_id")
@mock.patch.object(VNFPackageRPCAPI, "delete_vnf_package")
def test_delete_with_204_status(self, mock_delete_rpc, mock_vnf_by_id):
def test_delete_with_204_status(self, mock_delete_rpc,
mock_vnf_by_id, mock_vnf_pack_destroy):
vnfpkg_updates = {'operational_state': 'DISABLED'}
mock_vnf_by_id.return_value = fakes.return_vnfpkg_obj(
vnf_package_updates=vnfpkg_updates)