Implement rpc conductor for vnf packages

Added new RPC APIs for uploading and deleting vnf packages.

Partial-Implements: blueprint tosca-csar-mgmt-driver

Co-Author: Neha Alhat <neha.alhat@nttdata.com>
Change-Id: Ia5051f3c1bf2ce5f45c94711a67be5f9a8ef21d0
This commit is contained in:
Niraj Singh 2019-08-08 09:19:42 +00:00 committed by nirajsingh
parent 34b661ad08
commit a95ffbc668
4 changed files with 245 additions and 0 deletions

View File

@ -0,0 +1,63 @@
# Copyright (C) 2019 NTT DATA
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import oslo_messaging
from tacker.common import rpc
from tacker.common import topics
from tacker.objects import base as objects_base
class VNFPackageRPCAPI(object):
target = oslo_messaging.Target(
exchange='tacker',
topic=topics.TOPIC_CONDUCTOR,
fanout=False,
version='1.0')
def upload_vnf_package_content(self, context, vnf_package, cast=True):
serializer = objects_base.TackerObjectSerializer()
client = rpc.get_client(self.target, version_cap=None,
serializer=serializer)
cctxt = client.prepare()
rpc_method = cctxt.cast if cast else cctxt.call
return rpc_method(context, 'upload_vnf_package_content',
vnf_package=vnf_package)
def upload_vnf_package_from_uri(self, context, vnf_package,
address_information, user_name=None,
password=None, cast=True):
serializer = objects_base.TackerObjectSerializer()
client = rpc.get_client(self.target, version_cap=None,
serializer=serializer)
cctxt = client.prepare()
rpc_method = cctxt.cast if cast else cctxt.call
return rpc_method(context, 'upload_vnf_package_from_uri',
vnf_package=vnf_package,
address_information=address_information,
user_name=user_name, password=password)
def delete_vnf_package(self, context, vnf_package, cast=True):
serializer = objects_base.TackerObjectSerializer()
client = rpc.get_client(self.target, version_cap=None,
serializer=serializer)
cctxt = client.prepare()
rpc_method = cctxt.cast if cast else cctxt.call
return rpc_method(context, 'delete_vnf_package',
vnf_package=vnf_package)

View File

@ -14,8 +14,10 @@
import datetime
import oslo_messaging as messaging
from oslo_utils import versionutils
from oslo_versionedobjects import base as ovoo_base
from oslo_versionedobjects import exception as ovoo_exc
from tacker import objects
from tacker.objects import fields as obj_fields
@ -117,6 +119,63 @@ class TackerObject(ovoo_base.VersionedObject):
self._changed_fields.clear()
class TackerObjectSerializer(messaging.NoOpSerializer):
"""A TackerObject-aware Serializer.
This implements the Oslo Serializer interface and provides the
ability to serialize and deserialize TackerObject entities. Any service
that needs to accept or return TackerObjects as arguments or result values
should pass this to its RPCClient and RPCServer objects.
"""
def _process_object(self, context, objprim):
try:
objinst = TackerObject.obj_from_primitive(objprim, context=context)
except ovoo_exc.IncompatibleObjectVersion:
raise
return objinst
def _process_iterable(self, context, action_fn, values):
"""Process an iterable, taking an action on each value.
:param:context: Request context
:param:action_fn: Action to take on each item in values
:param:values: Iterable container of things to take action on
:returns: A new container of the same type (except set) with
items from values having had action applied.
"""
iterable = values.__class__
if issubclass(iterable, dict):
return iterable(**{k: action_fn(context, v)
for k, v in values.items()})
else:
# NOTE(nirajsingh) A set can't have an unhashable value inside,
# such as a dict. Convert the set to list, which is fine, since we
# can't send them over RPC anyway. We convert it to list as this
# way there will be no semantic change between the fake rpc driver
# used in functional test and a normal rpc driver.
if iterable == set:
iterable = list
return iterable([action_fn(context, value) for value in values])
def serialize_entity(self, context, entity):
if isinstance(entity, (tuple, list, set, dict)):
entity = self._process_iterable(context, self.serialize_entity,
entity)
elif (hasattr(entity, 'obj_to_primitive') and
callable(entity.obj_to_primitive)):
entity = entity.obj_to_primitive()
return entity
def deserialize_entity(self, context, entity):
if isinstance(entity, dict) and 'tacker_object.name' in entity:
entity = self._process_object(context, entity)
elif isinstance(entity, (tuple, list, set, dict)):
entity = self._process_iterable(context, self.deserialize_entity,
entity)
return entity
class TackerPersistentObject(object):
"""Mixin class for Persistent objects.

View File

@ -0,0 +1,81 @@
# Copyright (C) 2019 NTT DATA
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from tacker.common.rpc import BackingOffClient
from tacker.conductor.conductorrpc import vnf_pkgm_rpc
from tacker.objects import vnf_package
from tacker.tests import base
from tacker.tests.unit.conductor import fakes
class VnfPackageRPCTestCase(base.BaseTestCase):
def setUp(self):
super(VnfPackageRPCTestCase, self).setUp()
self.context = self.fake_admin_context()
self.rpc_api = vnf_pkgm_rpc.VNFPackageRPCAPI()
self.cctxt_mock = mock.MagicMock()
def test_upload_vnf_package_content(self):
@mock.patch.object(BackingOffClient, 'prepare')
def _test(prepare_mock):
prepare_mock.return_value = self.cctxt_mock
vnf_package_obj = vnf_package.VnfPackage(
self.context, **fakes.VNF_UPLOAD_VNF_PACKAGE_CONTENT)
self.rpc_api.upload_vnf_package_content(self.context,
vnf_package_obj, cast=True)
prepare_mock.assert_called()
self.cctxt_mock.cast.assert_called_once_with(
self.context, 'upload_vnf_package_content',
vnf_package=vnf_package_obj)
_test()
def test_upload_vnf_package_from_uri(self):
fake_addressInformation = "http://test_csar.zip"
@mock.patch.object(BackingOffClient, 'prepare')
def _test(prepare_mock):
prepare_mock.return_value = self.cctxt_mock
vnf_package_obj = vnf_package.VnfPackage(self.context,
**fakes.VNF_DATA)
self.rpc_api.upload_vnf_package_from_uri(self.context,
vnf_package_obj,
fake_addressInformation,
cast=True)
prepare_mock.assert_called()
self.cctxt_mock.cast.assert_called_once_with(
self.context, 'upload_vnf_package_from_uri',
vnf_package=vnf_package_obj,
address_information=fake_addressInformation,
password=None, user_name=None)
_test()
def test_delete_vnf_package(self):
@mock.patch.object(BackingOffClient, 'prepare')
def _test(prepare_mock):
prepare_mock.return_value = self.cctxt_mock
vnf_package_obj = vnf_package.VnfPackage(self.context,
**fakes.VNF_DATA)
self.rpc_api.delete_vnf_package(self.context,
vnf_package_obj, cast=True)
prepare_mock.assert_called()
self.cctxt_mock.cast.assert_called_once_with(
self.context, 'delete_vnf_package',
vnf_package=vnf_package_obj)
_test()

View File

@ -0,0 +1,42 @@
# Copyright (C) 2019 NTT DATA
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tacker.tests import uuidsentinel
VNF_UPLOAD_VNF_PACKAGE_CONTENT = {
'algorithm': 'sha512', 'created_at': '2019-08-16T06:57:09Z',
'deleted': False, 'deleted_at': None,
'hash': 'ce48b8ba15bfb060fb70471cf955bef433e4513973b4bac42b37c36f57357dc35'
'bf788c16545d3a59781914adf19fca26d6984583b7739e55c447383d774356a',
'id': uuidsentinel.tenant_id,
'location_glance_store': 'file:///var/lib/glance/images/'
'd617ea52-b16b-417e-a68c-08dfb69aab9e',
'onboarding_state': 'PROCESSING', 'operational_state': 'DISABLED',
'tenant_id': uuidsentinel.tenant_id,
'updated_at': '2019-08-16T06:57:30Z',
'usage_state': 'NOT_IN_USE', 'user_data': {'abc': 'xyz'}}
VNF_DATA = {
'created_at': '2019-08-16T06:57:09Z',
'deleted': False, 'deleted_at': None,
'id': uuidsentinel.id,
'onboarding_state': 'UPLOADING',
'operational_state': 'DISABLED',
'tenant_id': uuidsentinel.tenant_id,
'updated_at': '2019-08-16T06:57:30Z',
'usage_state': 'NOT_IN_USE',
'user_data': {'abc': 'xyz'}
}