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
This commit is contained in:
parent
8242c132ad
commit
f5f29b34fa
@ -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
|
||||
|
||||
|
@ -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
Normal file
80
tacker/api/views/vnf_subscriptions.py
Normal file
@ -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]
|
@ -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())
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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}
|
||||
|
@ -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(
|
||||
|
@ -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]
|
||||
|
@ -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…
x
Reference in New Issue
Block a user