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:
parent
f75827612f
commit
3fe7831d63
@ -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.
|
@ -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,
|
||||
|
@ -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):
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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'))
|
@ -1 +1 @@
|
||||
2531c976c0f1
|
||||
34cfceb25a49
|
@ -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'):
|
||||
|
@ -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()
|
||||
|
@ -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")
|
||||
|
@ -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 = {
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user