Browse Source

Support enhancement for Get Subscription List

- Added support for the following attributes:
       * operationStates
       * vnfInstanceSubscriptionFilter

  - The query information is enhanced by improving
    filtering expressions and operators

Implements: blueprint support-fundamental-lcm
Spec: https://specs.openstack.org/openstack/tacker-specs/specs/wallaby/support-fundamental-vnf-lcm-based-on-ETSI-NFV.html
Change-Id: I3d2abc25370b0a34793a50edffeccc0bc2a2bfe1
changes/33/774733/14
Aldinson Esto 1 year ago
parent
commit
f5f29b34fa
  1. 2
      api-ref/source/v1/vnflcm.inc
  2. 8
      tacker/api/views/vnf_lcm.py
  3. 80
      tacker/api/views/vnf_subscriptions.py
  4. 75
      tacker/api/vnflcm/v1/controller.py
  5. 10
      tacker/common/utils.py
  6. 43
      tacker/db/db_sqlalchemy/models.py
  7. 261
      tacker/objects/vnf_lcm_subscriptions.py
  8. 11
      tacker/tests/functional/sol/vnflcm/base.py
  9. 66
      tacker/tests/functional/sol/vnflcm/test_vnf_instance_with_user_data.py
  10. 51
      tacker/tests/unit/vnflcm/fakes.py
  11. 138
      tacker/tests/unit/vnflcm/test_controller.py

2
api-ref/source/v1/vnflcm.inc

@ -1297,8 +1297,10 @@ Response Parameters
- id: subscription_id_response
- filter: filter
- vnfInstanceSubscriptionFilter: vnf_instance_subscription_filter
- notificationTypes: filter_notification_types
- operationTypes: filter_operation_types
- operationStates: filter_operation_states
- callbackUri: callback_uri
- _links: vnf_instance_links

8
tacker/api/views/vnf_lcm.py

@ -203,6 +203,10 @@ class ViewBuilder(base.BaseViewBuilder):
'callbackUri': vnf_lcm_subscription.callback_uri,
}
# TODO(esto.aln): To remove all processing that are
# related to list subscription from vnf_lcm.py and
# transfer these to vnf_subscriptions.py, but this
# will be handled in a future patch.
def _subscription_filter(
self,
subscription_data,
@ -266,6 +270,10 @@ class ViewBuilder(base.BaseViewBuilder):
def subscription_create(self, vnf_lcm_subscription, filter):
return self._get_vnf_lcm_subscription(vnf_lcm_subscription, filter)
# TODO(esto.aln): To remove all processing that are
# related to list subscription from vnf_lcm.py and
# transfer these to vnf_subscriptions.py, but this
# will be handled in a future patch.
def subscription_list(
self,
vnf_lcm_subscriptions,

80
tacker/api/views/vnf_subscriptions.py

@ -0,0 +1,80 @@
# 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 json
from oslo_log import log as logging
from tacker.api import views as base
import tacker.conf
from tacker.objects import vnf_lcm_subscriptions as vnf_subscription
CONF = tacker.conf.CONF
LOG = logging.getLogger(__name__)
class ViewBuilder(base.BaseViewBuilder):
FLATTEN_ATTRIBUTES = vnf_subscription.LccnSubscription. \
FLATTEN_ATTRIBUTES
def _get_subscription_links(self, vnf_lcm_subscription):
if isinstance(vnf_lcm_subscription.id, str):
decode_id = vnf_lcm_subscription.id
else:
decode_id = vnf_lcm_subscription.id
return {
"_links": {
"self": {
"href": '%(endpoint)s/vnflcm/v1/subscriptions/%(id)s' %
{
"endpoint": CONF.vnf_lcm.endpoint_url,
"id": decode_id}}}}
def _basic_subscription_info(self, vnf_lcm_subscription, filter=None):
if filter is None:
if 'filter' in vnf_lcm_subscription:
filter_dict = {}
if 'filter' in vnf_lcm_subscription.filter:
filter_dict = json.loads(
vnf_lcm_subscription.filter.filter)
return {
'id': vnf_lcm_subscription.id,
'filter': filter_dict,
'callbackUri': vnf_lcm_subscription.callback_uri,
}
return {
'id': vnf_lcm_subscription.id,
'callbackUri': vnf_lcm_subscription.callback_uri,
}
else:
return {
'id': vnf_lcm_subscription.id,
'filter': filter,
'callbackUri': vnf_lcm_subscription.callback_uri,
}
def _get_subscription(self, subscription):
subscription_response = self._basic_subscription_info(subscription)
links = self._get_subscription_links(subscription)
subscription_response.update(links)
return subscription_response
def subscription_list(
self,
vnf_lcm_subscriptions):
return [self._get_subscription(subscription)
for subscription in vnf_lcm_subscriptions]

75
tacker/api/vnflcm/v1/controller.py

@ -43,6 +43,7 @@ from tacker.api.schemas import vnf_lcm
from tacker.api import validation
from tacker.api.views import vnf_lcm as vnf_lcm_view
from tacker.api.views import vnf_lcm_op_occs as vnf_op_occs_view
from tacker.api.views import vnf_subscriptions as vnf_subscription_view
from tacker.api.vnflcm.v1 import sync_resource
from tacker.common import exceptions
from tacker.common import utils
@ -162,6 +163,26 @@ def check_vnf_status_and_error_point(action, status=None):
class VnfLcmController(wsgi.Controller):
notification_type_list = ['VnfLcmOperationOccurrenceNotification',
'VnfIdentifierCreationNotification',
'VnfIdentifierDeletionNotification']
operation_type_list = ['INSTANTIATE',
'SCALE',
'SCALE_TO_LEVEL',
'CHANGE_FLAVOUR',
'TERMINATE',
'HEAL',
'OPERATE',
'CHANGE_EXT_CONN',
'MODIFY_INFO']
operation_state_list = ['STARTING',
'PROCESSING',
'COMPLETED',
'FAILED_TEMP',
'FAILED',
'ROLLING_BACK',
'ROLLED_BACK']
_view_builder_class = vnf_lcm_view.ViewBuilder
def __init__(self):
@ -169,6 +190,7 @@ class VnfLcmController(wsgi.Controller):
self.rpc_api = vnf_lcm_rpc.VNFLcmRPCAPI()
self._vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
self._view_builder_op_occ = vnf_op_occs_view.ViewBuilder()
self._view_builder_subscription = vnf_subscription_view.ViewBuilder()
def _get_vnf_instance_href(self, vnf_instance):
return '/vnflcm/v1/vnf_instances/%s' % vnf_instance.id
@ -958,13 +980,13 @@ class VnfLcmController(wsgi.Controller):
def subscription_list(self, request):
nextpage_opaque_marker = ""
paging = 1
filter_string = ""
re_url = request.path_url
query_params = request.query_string
if query_params:
query_params = parse.unquote(query_params)
LOG.debug("query_params %s" % query_params)
if query_params:
query_param_list = query_params.split('&')
for query_param in query_param_list:
query_param_key_value = query_param.split('=')
@ -972,18 +994,57 @@ class VnfLcmController(wsgi.Controller):
msg = _("Request query parameter error")
return self._make_problem_detail(
msg, 400, title='Bad Request')
if query_param_key_value[0] == 'filter':
filter_string = query_param_key_value[1]
if query_param_key_value[0] == 'nextpage_opaque_marker':
nextpage_opaque_marker = query_param_key_value[1]
if query_param_key_value[0] == 'page':
paging = int(query_param_key_value[1])
if filter_string:
# check enumerations columns
for filter_segment in filter_string.split(';'):
filter_segment = re.sub(
r'\(|\)|\'', '', filter_segment)
filter_name = filter_segment.split(',')[1]
filter_value = filter_segment.split(',')[2]
if filter_name == 'notificationTypes':
if filter_value not in self.notification_type_list:
msg = (_("notificationTypes value mismatch: %s")
% filter_value)
return self._make_problem_detail(msg, 400,
title='Bad Request')
elif filter_name == 'operationTypes':
if filter_value not in self.operation_type_list:
msg = (_("operationTypes value mismatch: %s")
% filter_value)
return self._make_problem_detail(msg, 400,
title='Bad Request')
elif filter_name == 'operationStates':
if filter_value not in self.operation_state_list:
msg = (_("operationStates value mismatch: %s")
% filter_value)
return self._make_problem_detail(msg, 400,
title='Bad Request')
try:
vnf_lcm_subscriptions = (
subscription_obj.LccnSubscriptionRequest.
vnf_lcm_subscriptions_list(request.context))
filter_string_parsed = self._view_builder_subscription. \
validate_filter(filter_string)
if nextpage_opaque_marker:
start_index = paging - 1
else:
start_index = None
vnf_lcm_subscriptions, last = (
subscription_obj.LccnSubscriptionList.
get_by_filters(request.context,
read_deleted='no',
filters=filter_string_parsed,
nextpage_opaque_marker=start_index))
LOG.debug("vnf_lcm_subscriptions %s" % vnf_lcm_subscriptions)
subscription_data, last = self._view_builder.subscription_list(
vnf_lcm_subscriptions, nextpage_opaque_marker, paging)
subscription_data = self._view_builder_subscription. \
subscription_list(vnf_lcm_subscriptions)
LOG.debug("last %s" % last)
except Exception as e:
LOG.error(traceback.format_exc())

10
tacker/common/utils.py

@ -670,3 +670,13 @@ def str_to_num(value):
return float(value)
except ValueError:
return None
def str_to_bytes(value):
"""Convert string to bytes"""
if isinstance(value, str):
value = bytes(value, 'utf-8')
elif value is not None:
value = bytes(value)
return value

43
tacker/db/db_sqlalchemy/models.py

@ -267,16 +267,6 @@ class VnfResource(model_base.BASE, models.SoftDeleteMixin,
resource_status = sa.Column(sa.String(255), nullable=False)
class VnfLcmSubscriptions(model_base.BASE, models.SoftDeleteMixin,
models.TimestampMixin):
"""Contains all info about vnf LCM Subscriptions."""
__tablename__ = 'vnf_lcm_subscriptions'
id = sa.Column(sa.String(36), nullable=False, primary_key=True)
callback_uri = sa.Column(sa.String(255), nullable=False)
subscription_authentication = sa.Column(sa.JSON, nullable=True)
class VnfLcmFilters(model_base.BASE):
"""Contains all info about vnf LCM filters."""
@ -288,13 +278,44 @@ class VnfLcmFilters(model_base.BASE):
nullable=False)
filter = sa.Column(sa.JSON, nullable=False)
vnf_products_from_providers = sa.Column(sa.JSON, nullable=True)
notification_types = sa.Column(sa.VARBINARY(255), nullable=True)
vnfd_ids = sa.Column(sa.Text(), nullable=True)
vnfd_ids_len = sa.Column(sa.Integer, nullable=True)
vnf_provider = sa.Column(sa.Text(), nullable=True)
vnf_product_name = sa.Column(sa.Text(), nullable=True)
vnf_software_version = sa.Column(sa.Text(), nullable=True)
vnfd_versions = sa.Column(sa.Text(), nullable=True)
vnfd_versions_len = sa.Column(sa.Integer, nullable=True)
vnf_instance_ids = sa.Column(sa.Text(), nullable=True)
vnf_instance_ids_len = sa.Column(sa.Integer, nullable=True)
vnf_instance_names = sa.Column(sa.Text(), nullable=True)
vnf_instance_names_len = sa.Column(sa.Integer, nullable=True)
notification_types = sa.Column(
sa.LargeBinary(
length=__maxsize__),
nullable=True)
notification_types_len = sa.Column(sa.Integer, nullable=True)
operation_types = sa.Column(
sa.LargeBinary(
length=__maxsize__),
nullable=True)
operation_types_len = sa.Column(sa.Integer, nullable=True)
operation_states = sa.Column(sa.Text(), nullable=True)
operation_states_len = sa.Column(sa.Integer, nullable=True)
class VnfLcmSubscriptions(model_base.BASE, models.SoftDeleteMixin,
models.TimestampMixin):
"""Contains all info about vnf LCM Subscriptions."""
__tablename__ = 'vnf_lcm_subscriptions'
id = sa.Column(sa.String(36), nullable=False, primary_key=True)
callback_uri = sa.Column(sa.String(255), nullable=False)
subscription_authentication = sa.Column(sa.JSON, nullable=True)
subscription_filter = orm.relationship(
VnfLcmFilters,
primaryjoin='and_(VnfLcmSubscriptions.id == '
'VnfLcmFilters.subscription_uuid)')
class VnfLcmOpOccs(model_base.BASE, models.SoftDeleteMixin,

261
tacker/objects/vnf_lcm_subscriptions.py

@ -11,15 +11,21 @@
# under the License.
from oslo_log import log as logging
from oslo_serialization import jsonutils as json
from oslo_utils import timeutils
from oslo_versionedobjects import base as ovoo_base
from sqlalchemy.orm import joinedload
from sqlalchemy.sql import text
from sqlalchemy_filters import apply_filters
from tacker.common import exceptions
from tacker.common import utils
from tacker.common.utils import convert_string_to_snakecase
import tacker.conf
from tacker.db import api as db_api
from tacker.db.db_sqlalchemy import api
from tacker.db.db_sqlalchemy import models
from tacker import objects
from tacker.objects import base
from tacker.objects import fields
@ -308,6 +314,45 @@ def _add_filter_data(context, subscription_id, filter):
new_entries)
@db_api.context_manager.reader
def _vnf_lcm_subscription_list_by_filters(context,
read_deleted=None, filters=None, nextpage_opaque_marker=None):
query = api.model_query(context, models.VnfLcmSubscriptions,
read_deleted=read_deleted,
project_only=True)
binary_columns = ['notification_types', 'operation_types']
if filters:
filter_data = json.dumps(filters)
if 'ChangeNotificationsFilter' in filter_data:
query = query.join(models.VnfLcmFilters)
if 'and' in filters:
filters_and = []
for filter in filters['and']:
if filter['field'] in binary_columns:
converted_value = utils.str_to_bytes(filter['value'])
filter['value'] = converted_value
filters_and.append(filter)
filters = {'and': filters_and}
else:
if filters['field'] in binary_columns:
converted_value = utils.str_to_bytes(filters['value'])
filters.update({'value': converted_value})
query = apply_filters(query, filters)
if nextpage_opaque_marker:
start_offset = CONF.vnf_lcm.subscription_num * nextpage_opaque_marker
return query.order_by(
models.VnfLcmSubscriptions.created_at).limit(
CONF.vnf_lcm.subscription_num + 1).offset(
start_offset).all()
else:
return query.order_by(models.VnfLcmSubscriptions.created_at).all()
@db_api.context_manager.writer
def _vnf_lcm_subscriptions_create(context, values, filter):
with db_api.context_manager.writer.using(context):
@ -370,6 +415,47 @@ def _destroy_vnf_lcm_subscription(context, subscriptionId):
raise e
@db_api.context_manager.reader
def _subscription_get_by_id(context, subscription_uuid, columns_to_join=None):
query = api.model_query(context, models.VnfLcmSubscriptions,
read_deleted="no", project_only=True). \
filter_by(id=subscription_uuid)
if columns_to_join:
for column in columns_to_join:
query = query.options(joinedload(column))
result = query.first()
if not result:
raise exceptions.NotFound(resource='Subscription',
id=subscription_uuid)
return result
def _make_subscription_list(context, subscription_list, db_subscription_list,
expected_attrs=None):
subscription_cls = LccnSubscription
subscription_list.objects = []
cnt = 0
last_flg = True
for db_subscription in db_subscription_list:
cnt = cnt + 1
if cnt == CONF.vnf_lcm.subscription_num + 1:
last_flg = False
break
subscription_obj = subscription_cls._from_db_object(
context, subscription_cls(context), db_subscription,
expected_attrs=expected_attrs)
subscription_list.objects.append(subscription_obj)
subscription_list.obj_reset_changes()
return subscription_list, last_flg
@base.TackerObjectRegistry.register
class LccnSubscriptionRequest(base.TackerObject, base.TackerPersistentObject):
@ -440,3 +526,178 @@ class LccnSubscriptionRequest(base.TackerObject, base.TackerPersistentObject):
raise e
return 204
@base.TackerObjectRegistry.register
class ChangeNotificationsFilter(
base.TackerObject, base.TackerPersistentObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'id': fields.UUIDField(nullable=False),
'subscription_uuid': fields.UUIDField(nullable=False),
'filter': fields.StringField(nullable=True),
'vnf_products_from_providers':
fields.StringField(nullable=True),
'vnfd_ids': fields.StringField(nullable=True),
'vnfd_ids_len': fields.IntegerField(
nullable=True, default=0),
'vnf_provider': fields.StringField(nullable=True),
'vnf_product_name': fields.StringField(nullable=True),
'vnf_software_version': fields.StringField(nullable=True),
'vnfd_versions': fields.StringField(nullable=True),
'vnfd_versions_len': fields.IntegerField(
nullable=True, default=0),
'vnf_instance_ids': fields.StringField(nullable=True),
'vnf_instance_ids_len': fields.IntegerField(
nullable=True, default=0),
'vnf_instance_names': fields.StringField(nullable=True),
'vnf_instance_names_len': fields.IntegerField(
nullable=True, default=0),
'notification_types': fields.StringField(nullable=True),
'notification_types_len': fields.IntegerField(
nullable=True, default=0),
'operation_types': fields.StringField(nullable=True),
'operation_types_len': fields.IntegerField(
nullable=True, default=0),
'operation_states': fields.StringField(nullable=True),
'operation_states_len': fields.IntegerField(
nullable=True, default=0),
}
@base.TackerObjectRegistry.register
class ChangeNotificationsFilterList(
ovoo_base.ObjectListBase, base.TackerObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'objects': fields.ListOfObjectsField('ChangeNotificationsFilter')
}
@base.TackerObjectRegistry.register
class LccnSubscription(base.TackerObject, base.TackerPersistentObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'id': fields.UUIDField(nullable=False),
'callback_uri': fields.StringField(nullable=False),
'filter': fields.ObjectField(
'ChangeNotificationsFilter', nullable=True),
}
ALL_ATTRIBUTES = {
'id': ('id', 'string',
'VnfLcmSubscriptions'),
'vnfdIds': ('vnfd_ids', 'string',
'VnfLcmFilters'),
'vnfProvider': ('vnf_provider', 'string',
'VnfLcmFilters'),
'vnfProductName': ('vnf_product_name', 'string',
'VnfLcmFilters'),
'vnfSoftwareVersion': ('vnf_software_version', 'string',
'VnfLcmFilters'),
'vnfdVersions': ('vnfd_versions', 'string',
'VnfLcmFilters'),
'vnfInstanceIds': ('vnf_instance_ids', 'string',
'VnfLcmFilters'),
'vnfInstanceNames': ('vnf_instance_names', 'string',
'VnfLcmFilters'),
'notificationTypes': ('notification_types', 'string',
'VnfLcmFilters'),
'operationTypes': ('operation_types', 'string',
'VnfLcmFilters'),
'operationStates': ('operation_states', 'string',
'VnfLcmFilters'),
'callbackUri': ('callback_uri', 'string',
'VnfLcmSubscriptions'),
}
FLATTEN_ATTRIBUTES = utils.flatten_dict(ALL_ATTRIBUTES.copy())
@staticmethod
def _from_db_object(context, subscription, db_subscription,
expected_attrs=None):
expected_attrs = expected_attrs or ['filter']
subscription._context = context
for key in subscription.fields:
if key in ['filter']:
continue
db_key = key
setattr(subscription, key, db_subscription[db_key])
subscription._context = context
subscription._extra_attributes_from_db_object(
subscription, db_subscription, expected_attrs)
subscription.obj_reset_changes()
return subscription
@staticmethod
def _extra_attributes_from_db_object(subscription, db_subscription,
expected_attrs=None):
"""Method to help with migration of extra attributes to objects."""
if expected_attrs is None:
expected_attrs = ['filter']
if 'filter' in expected_attrs:
subscription._load_subscription_filter(
db_subscription.get('filter'))
def _load_subscription_filter(self, db_filter=_NO_DATA_SENTINEL):
if db_filter is _NO_DATA_SENTINEL:
subscription = self.get_by_id(
self._context, self.id,
expected_attrs=['filter'])
if 'filter' in subscription:
self.filter = \
subscription.filter
self.filter.obj_reset_changes(recursive=True)
self.obj_reset_changes(['filter'])
else:
self.filter = \
objects.ChangeNotificationsFilterList(objects=[])
elif db_filter:
self.filter = base.obj_make_list(
self._context, objects.ChangeNotificationsFilterList(
self._context), objects.ChangeNotificationsFilter,
db_filter)
self.obj_reset_changes(['filter'])
@base.remotable_classmethod
def get_by_id(cls, context, id, expected_attrs=None):
db_subscription = _subscription_get_by_id(
context, id, columns_to_join=expected_attrs)
return cls._from_db_object(context, cls(), db_subscription,
expected_attrs=expected_attrs)
@base.TackerObjectRegistry.register
class LccnSubscriptionList(ovoo_base.ObjectListBase, base.TackerObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'objects': fields.ListOfObjectsField('LccnSubscription')
}
@base.remotable_classmethod
def get_by_filters(cls, context, read_deleted=None,
filters=None, nextpage_opaque_marker=None):
db_subscriptions = _vnf_lcm_subscription_list_by_filters(context,
read_deleted=read_deleted,
filters=filters,
nextpage_opaque_marker=nextpage_opaque_marker)
return _make_subscription_list(context, cls(), db_subscriptions)

11
tacker/tests/functional/sol/vnflcm/base.py

@ -372,6 +372,17 @@ class BaseVnfLcmTest(base.BaseTackerTest):
return resp, body
def _list_subscription_filter(self, **kwargs):
params = kwargs.get('params', {})
filter_variable = params['filter']
subscriptions_list_filter_url = "%s?%s" % (
self.base_subscriptions_url, filter_variable)
resp, subscription_body = self.http_client.do_request(
subscriptions_list_filter_url, "GET")
return resp, subscription_body
def _create_vnf_instance(self, vnfd_id, vnf_instance_name=None,
vnf_instance_description=None):
request_body = {'vnfdId': vnfd_id}

66
tacker/tests/functional/sol/vnflcm/test_vnf_instance_with_user_data.py

@ -226,6 +226,7 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
- Create subscription.
- Get subscription informations.
- Get list of subscriptions
- Get list of subscriptions with filter
- Create VNF package.
- Upload VNF package.
- Create VNF instance.
@ -261,6 +262,71 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
resp, _ = self._list_subscription()
self.assertEqual(200, resp.status_code)
# Subscription list filter 1
filter_expr = {
'filter': "filter=(eq,id,{})".format(body.get('id'))}
resp, subscription_body = self._list_subscription_filter(
params=filter_expr)
self.assertEqual(200, resp.status_code)
self.assertEqual(1, len(subscription_body))
# Subscription list filter 2
filter_expr = {
'filter': "filter=(neq,callbackUri,{})".format(
'http://localhost:{}{}'.format(
vnflcm_base.FAKE_SERVER_MANAGER.SERVER_PORT,
os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)))}
resp, subscription_body = self._list_subscription_filter(
params=filter_expr)
self.assertEqual(200, resp.status_code)
self.assertEqual(0, len(subscription_body))
# Subscription list filter 3
filter_expr = {
'filter': "filter=(neq,id,{})".format(body.get('id'))}
resp, subscription_body = self._list_subscription_filter(
params=filter_expr)
self.assertEqual(200, resp.status_code)
self.assertEqual(0, len(subscription_body))
# Subscription list filter 4
filter_expr = {
'filter': "filter=(eq,callbackUri,{})".format(
'http://localhost:{}{}'.format(
vnflcm_base.FAKE_SERVER_MANAGER.SERVER_PORT,
os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)))}
resp, subscription_body = self._list_subscription_filter(
params=filter_expr)
self.assertEqual(200, resp.status_code)
self.assertEqual(1, len(subscription_body))
# Subscription list filter 5
filter_expr = {
'filter': "filter=(in,operationTypes,{})".format("sample")}
resp, subscription_body = self._list_subscription_filter(
params=filter_expr)
self.assertEqual(400, resp.status_code)
self.assertEqual(3, len(subscription_body))
# Subscription list filter 6
filter_expr = {
'filter': "filter=(eq,vnfSoftwareVersion,{})".format('1.0')}
resp, subscription_body = self._list_subscription_filter(
params=filter_expr)
self.assertEqual(200, resp.status_code)
self.assertEqual(1, len(subscription_body))
# Subscription list filter 7
filter_expr = {
'filter': "filter=(eq,operationTypes,{})".format(
"SCALE_TO_LEVEL")}
resp, subscription_body = self._list_subscription_filter(
params=filter_expr)
self.assertEqual(200, resp.status_code)
self.assertEqual(0, len(subscription_body))
# Pre Setting: Create vnf package.
sample_name = 'functional'
csar_package_path = os.path.abspath(

51
tacker/tests/unit/vnflcm/fakes.py

@ -1662,3 +1662,54 @@ def get_change_ext_conn_request_obj():
get_change_ext_conn_request_body())
return ChangeExtConnRequest.obj_from_primitive(
body, context)
def _fake_subscription_obj(**updates):
subscription = {
'id': uuidsentinel.subscription_id,
'filter': {
"vnfInstanceSubscriptionFilter": {
"vnfdIds": [uuidsentinel.vnfd_id],
"vnfProductsFromProviders": {
"vnfProvider": "Vnf Provider 1",
"vnfProducts": [
{
"vnfProductName": "Vnf Product 1",
"versions": [
{
"vnfSoftwareVersion": "v1",
"vnfdVersions": [
"vnfd.v1.1"
]
}
]
}
]
},
"vnfInstanceIds": [
uuidsentinel.vnf_instance_id
],
"vnfInstanceNames": ["Vnf Name 1"]
},
"notificationTypes": [
"VnfLcmOperationOccurrenceNotification"
],
"operationTypes": ["INSTANTIATE"],
"operationStates": ["STARTING"]
},
'callback_uri': 'http://localhost/sample_callback_uri'}
if updates:
subscription.update(**updates)
return subscription
def return_subscription_object(**updates):
vnf_lcm_subscription = _fake_subscription_obj(**updates)
return vnf_lcm_subscription
def return_vnf_subscription_list(**updates):
vnc_lcm_subscription = return_subscription_object(**updates)
return [vnc_lcm_subscription]

138
tacker/tests/unit/vnflcm/test_controller.py

@ -27,6 +27,7 @@ from webob import exc
from oslo_config import cfg
from oslo_serialization import jsonutils
from tacker.api.views import vnf_subscriptions as vnf_subscription_view
from tacker.api.vnflcm.v1 import controller
from tacker.api.vnflcm.v1 import sync_resource
from tacker.common import exceptions
@ -3873,3 +3874,140 @@ class TestController(base.TestCase):
resp = req.get_response(self.app)
self.assertEqual(http_client.CREATED, resp.status_code)
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch.object(vnf_subscription_view.ViewBuilder,
"subscription_list")
@mock.patch.object(vnf_subscription_view.ViewBuilder,
"validate_filter")
@mock.patch.object(objects.LccnSubscriptionList,
"get_by_filters")
def test_subscription_list_all(self,
mock_subscription_list,
mock_subscription_filter,
mock_subscription_view,
mock_get_service_plugins):
mock_subscription_filter.return_value = None
last = True
req = fake_request.HTTPRequest.blank('/subscriptions')
req.method = 'GET'
mock_subscription_list.return_value = [fakes.
return_vnf_subscription_list(), last]
mock_subscription_view.return_value = fakes. \
return_vnf_subscription_list()
resp = req.get_response(self.app)
expected_result = fakes.return_vnf_subscription_list()
self.assertEqual(200, resp.status_code)
self.assertEqual(expected_result, resp.json)
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch.object(vnf_subscription_view.ViewBuilder,
"subscription_list")
@mock.patch.object(vnf_subscription_view.ViewBuilder,
"validate_filter")
@mock.patch.object(objects.LccnSubscriptionList,
"get_by_filters")
def test_subscription_list_empty(self,
mock_subscription_list,
mock_subscription_filter,
mock_subscription_view,
mock_get_service_plugins):
mock_subscription_filter.return_value = None
last = True
req = fake_request.HTTPRequest.blank('/subscriptions')
req.method = 'GET'
mock_subscription_list.return_value = [fakes.
return_vnf_subscription_list(), last]
mock_subscription_view.return_value = []
resp = req.get_response(self.app)
self.assertEqual(200, resp.status_code)
self.assertEqual([], resp.json)
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch.object(vnf_subscription_view.ViewBuilder,
"validate_filter")
def test_subscription_list_error(self,
mock_subscription_filter,
mock_get_service_plugins):
req = fake_request.HTTPRequest.blank(
'/subscriptions')
req.method = 'GET'
mock_subscription_filter.side_effect = Exception
resp = req.get_response(self.app)
self.assertEqual(500, resp.status_code)
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch.object(vnf_subscription_view.ViewBuilder,
"subscription_list")
@mock.patch.object(vnf_subscription_view.ViewBuilder,
"validate_filter")
@mock.patch.object(objects.LccnSubscriptionList,
"get_by_filters")
@ddt.data(
{'operator': "eq", 'key': 'id',
'value': uuidsentinel.subscription_id},
{'operator': "cont", 'key': 'callbackUri',
'value': 'http://localhost/sample_callback_uri'},
{'operator': "neq", 'key': 'id',
'value': uuidsentinel.subscription_id},
{'operator': "eq", 'key': 'notificationTypes',
'value': 'VnfLcmOperationOccurrenceNotification'},
{'operator': "neq", 'key': 'operationTypes', 'value': 'INSTANTIATE'},
{'operator': "cont", 'key': 'operationStates', 'value': 'STARTING'}
)
@ddt.unpack
def test_subscription_list_filter(self,
mock_subscription_list,
mock_subscription_filter,
mock_subscription_view,
mock_get_service_plugins,
operator, key, value):
"""Tests all supported operators in filter expression."""
filters = {
'filter': ("(%s,%s,%s)" % (operator, key, value))
}
query = urllib.parse.urlencode(filters)
req = fake_request.HTTPRequest.blank(
'/subscriptions?' + query)
req.method = 'GET'
body_2 = {}
mock_subscription_filter.return_value = filters
last = True
if operator == 'neq':
body_2 = {key: uuidsentinel.subscription_id_2}
mock_subscription_list.return_value = [fakes.
return_vnf_subscription_list(), last]
mock_subscription_view.return_value = fakes. \
return_vnf_subscription_list()
resp = req.get_response(self.app)
expected_result = fakes.return_vnf_subscription_list(**body_2)
if operator == 'neq':
self.assertNotEqual(expected_result, resp.json)
else:
self.assertEqual(expected_result, resp.json)
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
def test_subscription_list_filter_error(self,
mock_get_service_plugins):
req = fake_request.HTTPRequest.blank(
'/subscriptions?filter')
req.method = 'GET'
resp = req.get_response(self.app)
self.assertEqual(400, resp.status_code)

Loading…
Cancel
Save