Fix API pagination problem in multi servers

This patch fixes the problem regarding pagination of target APIs
in case of running by multiple Tacker servers.

It changes the method of getting paginated records so that they
are able to be done by specifying the last record's id in the last
page.

Closes-Bug: #1978901
Change-Id: Ie0a51f1b837b7426595c40efcbbc022f0ea66f65
This commit is contained in:
Koichi Edagawa 2022-07-22 12:06:40 +09:00 committed by Yasufumi Ogawa
parent a71771ac85
commit fd41f08914
12 changed files with 604 additions and 310 deletions

View File

@ -44,7 +44,8 @@ class ViewBuilder(base.BaseViewBuilder):
def _basic_subscription_info(self, vnf_lcm_subscription, filter=None): def _basic_subscription_info(self, vnf_lcm_subscription, filter=None):
if filter is None: if filter is None:
if 'filter' in vnf_lcm_subscription: if 'filter' in vnf_lcm_subscription:
filter_dict = {} filter_dict = json.loads(
vnf_lcm_subscription.filter)
if 'filter' in vnf_lcm_subscription.filter: if 'filter' in vnf_lcm_subscription.filter:
filter_dict = json.loads( filter_dict = json.loads(

View File

@ -56,8 +56,11 @@ from tacker import manager
from tacker import objects from tacker import objects
from tacker.objects import fields from tacker.objects import fields
from tacker.objects.fields import ErrorPoint as EP from tacker.objects.fields import ErrorPoint as EP
from tacker.objects.vnf_instance import VnfInstanceList as vnf_instance_list
from tacker.objects import vnf_lcm_op_occs as vnf_lcm_op_occs_obj from tacker.objects import vnf_lcm_op_occs as vnf_lcm_op_occs_obj
from tacker.objects.vnf_lcm_op_occs import VnfLcmOpOccList as vnf_lcm_op_list
from tacker.objects import vnf_lcm_subscriptions as subscription_obj from tacker.objects import vnf_lcm_subscriptions as subscription_obj
from tacker.objects.vnf_lcm_subscriptions import LccnSubscriptionList as s_list
from tacker.plugins.common import constants from tacker.plugins.common import constants
from tacker.policies import vnf_lcm as vnf_lcm_policies from tacker.policies import vnf_lcm as vnf_lcm_policies
from tacker.tosca import utils as toscautils from tacker.tosca import utils as toscautils
@ -192,9 +195,6 @@ class VnfLcmController(wsgi.Controller):
self._vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM'] self._vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
self._view_builder_op_occ = vnf_op_occs_view.ViewBuilder() self._view_builder_op_occ = vnf_op_occs_view.ViewBuilder()
self._view_builder_subscription = vnf_subscription_view.ViewBuilder() self._view_builder_subscription = vnf_subscription_view.ViewBuilder()
self._nextpages_vnf_instances = {}
self._nextpages_lcm_op_occs = {}
self._nextpages_subscriptions = {}
def _get_vnf_instance_href(self, vnf_instance): def _get_vnf_instance_href(self, vnf_instance):
return '{}vnflcm/v1/vnf_instances/{}'.format( return '{}vnflcm/v1/vnf_instances/{}'.format(
@ -560,13 +560,6 @@ class VnfLcmController(wsgi.Controller):
return vnf_package_info[0] return vnf_package_info[0]
def _delete_expired_nextpages(self, nextpages):
for k, v in list(nextpages.items()):
if timeutils.is_older_than(v['created_time'],
CONF.vnf_lcm.nextpage_expiration_time):
LOG.debug('Old nextpages are deleted. id: %s' % k)
nextpages.pop(k)
@wsgi.response(http_client.OK) @wsgi.response(http_client.OK)
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND)) @wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND))
def show(self, request, id): def show(self, request, id):
@ -596,39 +589,43 @@ class VnfLcmController(wsgi.Controller):
nextpage = request.GET.get('nextpage_opaque_marker') nextpage = request.GET.get('nextpage_opaque_marker')
allrecords = request.GET.get('all_records') allrecords = request.GET.get('all_records')
limit = None
marker_obj = None
result = [] result = []
if allrecords != 'yes' and nextpage: # get maximum record size per page
self._delete_expired_nextpages(self._nextpages_vnf_instances) if allrecords != 'yes':
limit = CONF.vnf_lcm.vnf_instance_num
if nextpage in self._nextpages_vnf_instances: # get next page marker object from nextpage id
result = self._nextpages_vnf_instances.pop( if nextpage:
nextpage)['nextpage'] marker_obj = objects.VnfInstance.get_by_id(request.context,
else: nextpage)
vnf_instances = objects.VnfInstanceList.get_by_filters( try:
request.context, filters=filters) # get records from DB within maximum record size per page
# except for getting all records case
result = vnf_instance_list.get_by_marker_filter(request.context,
limit, marker_obj, filters=filters)
except Exception as e:
LOG.exception(traceback.format_exc())
return self._make_problem_detail(
str(e), 500, title='Internal Server Error')
result = self._view_builder.index(vnf_instances) result = self._view_builder.index(result)
res = webob.Response(content_type='application/json') res = webob.Response(content_type='application/json')
res.status_int = 200 res.status_int = 200
if (allrecords != 'yes' and # if the number of records obtained from DB is equal to maximum record
len(result) > CONF.vnf_lcm.vnf_instance_num): # size per page, the id of the last record is used as next page marker
nextpageid = uuidutils.generate_uuid() # and set it to Link header of the response
if (allrecords != 'yes' and len(result) >= limit):
nextpageid = result[(limit - 1)]['id']
links = ('Link', '<%s?nextpage_opaque_marker=%s>; rel="next"' % ( links = ('Link', '<%s?nextpage_opaque_marker=%s>; rel="next"' % (
request.path_url, nextpageid)) request.path_url, nextpageid))
res.headerlist.append(links) res.headerlist.append(links)
res.body = jsonutils.dump_as_bytes(
result[: CONF.vnf_lcm.vnf_instance_num])
self._delete_expired_nextpages(self._nextpages_vnf_instances) res.body = jsonutils.dump_as_bytes(result)
remain = result[CONF.vnf_lcm.vnf_instance_num:]
self._nextpages_vnf_instances.update({nextpageid:
{'created_time': timeutils.utcnow(), 'nextpage': remain}})
else:
res.body = jsonutils.dump_as_bytes(result)
return res return res
@ -1061,9 +1058,9 @@ class VnfLcmController(wsgi.Controller):
@wsgi.response(http_client.OK) @wsgi.response(http_client.OK)
def subscription_list(self, request): def subscription_list(self, request):
nextpage_opaque_marker = None nextpage_opaque_marker = None
paging = 1
filter_string = "" filter_string = ""
ignore_nextpages = False limit = None
marker_obj = None
subscription_data = [] subscription_data = []
query_params = request.query_string query_params = request.query_string
@ -1083,9 +1080,6 @@ class VnfLcmController(wsgi.Controller):
filter_string = query_param_key_value[1] filter_string = query_param_key_value[1]
if query_param_key_value[0] == 'nextpage_opaque_marker': if query_param_key_value[0] == 'nextpage_opaque_marker':
nextpage_opaque_marker = query_param_key_value[1] nextpage_opaque_marker = query_param_key_value[1]
if query_param_key_value[0] == 'page':
paging = int(query_param_key_value[1])
ignore_nextpages = True
if filter_string: if filter_string:
# check enumerations columns # check enumerations columns
@ -1114,55 +1108,48 @@ class VnfLcmController(wsgi.Controller):
title='Bad Request') title='Bad Request')
nextpage = nextpage_opaque_marker nextpage = nextpage_opaque_marker
if allrecords != 'yes' and not ignore_nextpages and nextpage:
self._delete_expired_nextpages(self._nextpages_subscriptions)
if nextpage in self._nextpages_subscriptions: try:
subscription_data = self._nextpages_subscriptions.pop( filter_string_parsed = self._view_builder_subscription. \
nextpage)['nextpage'] validate_filter(filter_string)
else:
try:
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 = ( # get maximum record size per page
subscription_obj.LccnSubscriptionList. if allrecords != 'yes':
get_by_filters(request.context, limit = CONF.vnf_lcm.subscription_num
read_deleted='no',
filters=filter_string_parsed,
nextpage_opaque_marker=start_index))
LOG.debug("vnf_lcm_subscriptions %s" % vnf_lcm_subscriptions) # get next page marker object from nextpage id
subscription_data = self._view_builder_subscription. \ if nextpage:
subscription_list(vnf_lcm_subscriptions) marker_obj = objects.LccnSubscription.get_by_id(
except Exception as e: request.context, nextpage)
LOG.error(traceback.format_exc())
return self._make_problem_detail( # get records from DB within maximum record size per page
str(e), 500, title='Internal Server Error') # except for getting all records case
result = s_list.get_by_marker_filter(
request.context, limit,
marker_obj, filters=filter_string_parsed,
read_deleted='no')
LOG.debug("vnf_lcm_subscriptions %s" % result)
subscription_data = self._view_builder_subscription. \
subscription_list(result)
except Exception as e:
LOG.error(traceback.format_exc())
return self._make_problem_detail(
str(e), 500, title='Internal Server Error')
# make response # make response
res = webob.Response(content_type='application/json') res = webob.Response(content_type='application/json')
res.status_int = 200 res.status_int = 200
if (allrecords != 'yes' and not ignore_nextpages and # if the number of records obtained from DB is equal to maximum record
len(subscription_data) > CONF.vnf_lcm.subscription_num): # size per page, the id of the last record is used as next page marker
nextpageid = uuidutils.generate_uuid() # and set it to Link header of the response
if (allrecords != 'yes' and len(subscription_data) >= limit):
nextpageid = subscription_data[(limit - 1)]['id']
links = ('Link', '<%s?nextpage_opaque_marker=%s>; rel="next"' % ( links = ('Link', '<%s?nextpage_opaque_marker=%s>; rel="next"' % (
request.path_url, nextpageid)) request.path_url, nextpageid))
res.headerlist.append(links) res.headerlist.append(links)
remain = subscription_data[CONF.vnf_lcm.subscription_num:]
subscription_data = (
subscription_data[: CONF.vnf_lcm.subscription_num])
self._delete_expired_nextpages(self._nextpages_subscriptions)
self._nextpages_subscriptions.update({nextpageid:
{'created_time': timeutils.utcnow(), 'nextpage': remain}})
res.body = jsonutils.dump_as_bytes(subscription_data) res.body = jsonutils.dump_as_bytes(subscription_data)
LOG.debug("subscription_list res %s" % res) LOG.debug("subscription_list res %s" % res)
@ -1748,52 +1735,53 @@ class VnfLcmController(wsgi.Controller):
nextpage = request.GET.get('nextpage_opaque_marker') nextpage = request.GET.get('nextpage_opaque_marker')
allrecords = request.GET.get('all_records') allrecords = request.GET.get('all_records')
limit = None
marker_obj = None
result = [] result = []
if allrecords != 'yes' and nextpage: self._view_builder_op_occ.validate_attribute_fields(
self._delete_expired_nextpages(self._nextpages_lcm_op_occs) all_fields=all_fields, fields=fields,
exclude_fields=exclude_fields,
exclude_default=exclude_default)
if nextpage in self._nextpages_lcm_op_occs: filters = self._view_builder_op_occ.validate_filter(filters)
result = self._nextpages_lcm_op_occs.pop(nextpage)['nextpage']
else:
self._view_builder_op_occ.validate_attribute_fields(
all_fields=all_fields, fields=fields,
exclude_fields=exclude_fields,
exclude_default=exclude_default)
filters = self._view_builder_op_occ.validate_filter(filters) # get maximum record size per page
if allrecords != 'yes':
limit = CONF.vnf_lcm.lcm_op_occ_num
try: # get next page marker object from nextpage id
vnf_lcm_op_occs = ( if nextpage:
vnf_lcm_op_occs_obj.VnfLcmOpOccList.get_by_filters( marker_obj = vnf_lcm_op_occs_obj.VnfLcmOpOcc.get_by_id(
request.context, read_deleted='no', filters=filters)) request.context, nextpage)
except Exception as e:
LOG.exception(traceback.format_exc())
return self._make_problem_detail(
str(e), 500, title='Internal Server Error')
result = self._view_builder_op_occ.index(request, vnf_lcm_op_occs, try:
all_fields=all_fields, exclude_fields=exclude_fields, # get records from DB within maximum record size per page
fields=fields, exclude_default=exclude_default) # except for getting all records case
result = vnf_lcm_op_list.get_by_marker_filter(request.context,
limit, marker_obj, filters=filters, read_deleted='no')
except Exception as e:
LOG.exception(traceback.format_exc())
return self._make_problem_detail(
str(e), 500, title='Internal Server Error')
result = self._view_builder_op_occ.index(request, result,
all_fields=all_fields, exclude_fields=exclude_fields,
fields=fields, exclude_default=exclude_default)
res = webob.Response(content_type='application/json') res = webob.Response(content_type='application/json')
res.status_int = 200 res.status_int = 200
if allrecords != 'yes' and len(result) > CONF.vnf_lcm.lcm_op_occ_num: # if the number of records obtained from DB is equal to maximum record
nextpageid = uuidutils.generate_uuid() # size per page, the id of the last record is used as next page marker
# and set it to Link header of the response
if (allrecords != 'yes' and len(result) >= limit):
nextpageid = result[(limit - 1)]['id']
links = ('Link', '<%s?nextpage_opaque_marker=%s>; rel="next"' % ( links = ('Link', '<%s?nextpage_opaque_marker=%s>; rel="next"' % (
request.path_url, nextpageid)) request.path_url, nextpageid))
res.headerlist.append(links) res.headerlist.append(links)
res.body = jsonutils.dump_as_bytes(
result[: CONF.vnf_lcm.lcm_op_occ_num])
self._delete_expired_nextpages(self._nextpages_lcm_op_occs) res.body = jsonutils.dump_as_bytes(result)
remain = result[CONF.vnf_lcm.lcm_op_occ_num:]
self._nextpages_lcm_op_occs.update({nextpageid:
{'created_time': timeutils.utcnow(), 'nextpage': remain}})
else:
res.body = jsonutils.dump_as_bytes(result)
return res return res

View File

@ -18,6 +18,7 @@ from io import BytesIO
import json import json
import mimetypes import mimetypes
import os import os
import traceback
import webob import webob
import zipfile import zipfile
from zipfile import ZipFile from zipfile import ZipFile
@ -28,7 +29,6 @@ from oslo_log import log as logging
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from oslo_utils import encodeutils from oslo_utils import encodeutils
from oslo_utils import excutils from oslo_utils import excutils
from oslo_utils import timeutils
from oslo_utils import uuidutils from oslo_utils import uuidutils
from tacker._i18n import _ from tacker._i18n import _
@ -40,8 +40,10 @@ from tacker.common import exceptions
from tacker.common import utils from tacker.common import utils
from tacker.conductor.conductorrpc import vnf_pkgm_rpc from tacker.conductor.conductorrpc import vnf_pkgm_rpc
from tacker.glance_store import store as glance_store from tacker.glance_store import store as glance_store
from tacker import objects
from tacker.objects import fields from tacker.objects import fields
from tacker.objects import vnf_package as vnf_package_obj from tacker.objects import vnf_package as vnf_package_obj
from tacker.objects.vnf_package import VnfPackagesList as vnf_package_list
from tacker.policies import vnf_package as vnf_package_policies from tacker.policies import vnf_package as vnf_package_policies
from tacker import wsgi from tacker import wsgi
@ -75,13 +77,6 @@ class VnfPkgmController(wsgi.Controller):
raise webob.exc.HTTPNotFound(explanation=msg) raise webob.exc.HTTPNotFound(explanation=msg)
return vnf_package return vnf_package
def _delete_expired_nextpages(self, nextpages):
for k, v in nextpages.items():
if timeutils.is_older_than(v['created_time'],
CONF.vnf_package.nextpage_expiration_time):
LOG.debug('Old nextpages are deleted. id: %s' % k)
nextpages.pop(k)
@wsgi.response(http_client.CREATED) @wsgi.response(http_client.CREATED)
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN)) @wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN))
@validation.schema(vnf_packages.create) @validation.schema(vnf_packages.create)
@ -162,42 +157,47 @@ class VnfPkgmController(wsgi.Controller):
filters = self._view_builder.validate_filter(filters) filters = self._view_builder.validate_filter(filters)
results = [] results = []
limit = None
marker_obj = None
if allrecords != 'yes' and nextpage: if allrecords != 'yes':
self._delete_expired_nextpages(self._nextpages) limit = CONF.vnf_package.vnf_package_num
if nextpage in self._nextpages: # get next page marker object from nextpage id
results = self._nextpages.pop( if nextpage:
nextpage)['nextpage'] marker_obj = objects.VnfPackage.get_by_id(request.context,
else: nextpage)
vnf_packages = vnf_package_obj.VnfPackagesList.get_by_filters(
request.context, read_deleted='no', filters=filters)
results = self._view_builder.index(vnf_packages, try:
all_fields=all_fields, # get records from DB within maximum record size per page
exclude_fields=exclude_fields, # except for getting all records case
fields=fields, result = vnf_package_list.get_by_marker_filter(request.context,
exclude_default=exclude_default) limit, marker_obj, filters=filters, read_deleted='no')
except Exception as e:
LOG.exception(traceback.format_exc())
return self._make_problem_detail(
str(e), 500, title='Internal Server Error')
results = self._view_builder.index(result,
all_fields=all_fields,
exclude_fields=exclude_fields,
fields=fields,
exclude_default=exclude_default)
res = webob.Response(content_type='application/json') res = webob.Response(content_type='application/json')
res.status_int = 200 res.status_int = 200
# if the number of records obtained from DB is equal to maximum record
# size per page, the id of the last record is used as next page marker
# and set it to Link header of the response
if (allrecords != 'yes' and if (allrecords != 'yes' and
len(results) > CONF.vnf_package.vnf_package_num): len(results) >= limit):
nextpageid = uuidutils.generate_uuid() nextpageid = result[(limit - 1)]['id']
links = ('Link', '<%s?nextpage_opaque_marker=%s>; rel="next"' % ( links = ('Link', '<%s?nextpage_opaque_marker=%s>; rel="next"' % (
request.path_url, nextpageid)) request.path_url, nextpageid))
res.headerlist.append(links) res.headerlist.append(links)
res.body = jsonutils.dump_as_bytes(
results[: CONF.vnf_package.vnf_package_num], default=str)
self._delete_expired_nextpages(self._nextpages) res.body = jsonutils.dump_as_bytes(results, default=str)
remain = results[CONF.vnf_package.vnf_package_num:]
self._nextpages.update({nextpageid:
{'created_time': timeutils.utcnow(), 'nextpage': remain}})
else:
res.body = jsonutils.dump_as_bytes(results, default=str)
return res return res

View File

@ -55,11 +55,7 @@ OPTS = [
cfg.IntOpt( cfg.IntOpt(
'vnf_instance_num', 'vnf_instance_num',
default=100, default=100,
help="Number of vnf_instances contained in 1 page"), help="Number of vnf_instances contained in 1 page")]
cfg.IntOpt(
'nextpage_expiration_time',
default=3600,
help="Expiration time (sec) for paging")]
vnf_lcm_group = cfg.OptGroup('vnf_lcm', vnf_lcm_group = cfg.OptGroup('vnf_lcm',
title='vnf_lcm options', title='vnf_lcm options',

View File

@ -78,10 +78,7 @@ Related options:
help=_("List of del inputs from lower-vnfd")), help=_("List of del inputs from lower-vnfd")),
cfg.IntOpt('vnf_package_num', cfg.IntOpt('vnf_package_num',
default=100, default=100,
help=_("Number of vnf_packages contained in 1 page")), help=_("Number of vnf_packages contained in 1 page"))
cfg.IntOpt('nextpage_expiration_time',
default=3600,
help=_("Expiration time (sec) for paging")),
] ]

View File

@ -27,6 +27,7 @@ from tacker.common import utils
from tacker.db import api as db_api from tacker.db import api as db_api
from tacker.db.db_sqlalchemy import api from tacker.db.db_sqlalchemy import api
from tacker.db.db_sqlalchemy import models from tacker.db.db_sqlalchemy import models
from tacker.db import sqlalchemyutils
from tacker.db.vnfm import vnfm_db from tacker.db.vnfm import vnfm_db
from tacker import objects from tacker import objects
from tacker.objects import base from tacker.objects import base
@ -127,6 +128,23 @@ def _vnf_instance_list_by_filter(context, columns_to_join=None,
return query.all() return query.all()
@db_api.context_manager.reader
def _vnf_instance_list_by_filter_query(context, columns_to_join=None,
filters=None):
query = api.model_query(context, models.VnfInstance,
read_deleted="no",
project_only=True)
if columns_to_join:
for column in columns_to_join:
query = query.options(joinedload(column))
if filters:
query = common.apply_filters(query, filters)
return query
def _make_vnf_instance_list(context, vnf_instance_list, db_vnf_instance_list, def _make_vnf_instance_list(context, vnf_instance_list, db_vnf_instance_list,
expected_attrs): expected_attrs):
vnf_instance_cls = VnfInstance vnf_instance_cls = VnfInstance
@ -545,6 +563,22 @@ class VnfInstanceList(ovoo_base.ObjectListBase, base.TackerObject):
return _make_vnf_instance_list(context, cls(), db_vnf_instances, return _make_vnf_instance_list(context, cls(), db_vnf_instances,
expected_attrs) expected_attrs)
@base.remotable_classmethod
def get_by_marker_filter(cls, context, limit, marker_obj,
filters=None, expected_attrs=None):
expected_attrs = ["instantiated_vnf_info"]
query = _vnf_instance_list_by_filter_query(context,
columns_to_join=expected_attrs,
filters=filters)
query = sqlalchemyutils.paginate_query(query, model=models.VnfInstance,
limit=limit,
sorts=[['id', 'asc']],
marker_obj=marker_obj)
db_vnf_instances = query.all()
return _make_vnf_instance_list(context, cls(), db_vnf_instances,
expected_attrs)
@base.remotable_classmethod @base.remotable_classmethod
def get_by_filters(cls, context, filters=None, def get_by_filters(cls, context, filters=None,
expected_attrs=None): expected_attrs=None):

View File

@ -24,6 +24,7 @@ from tacker.common import utils
from tacker.db import api as db_api from tacker.db import api as db_api
from tacker.db.db_sqlalchemy import api from tacker.db.db_sqlalchemy import api
from tacker.db.db_sqlalchemy import models from tacker.db.db_sqlalchemy import models
from tacker.db import sqlalchemyutils
from tacker import objects from tacker import objects
from tacker.objects import base from tacker.objects import base
from tacker.objects import common from tacker.objects import common
@ -151,6 +152,19 @@ def _vnf_lcm_op_occs_get_by_filters(context, read_deleted=None,
return query.all() return query.all()
@db_api.context_manager.reader
def _vnf_lcm_op_occs_get_by_filters_query(context, read_deleted=None,
filters=None):
query = api.model_query(context, models.VnfLcmOpOccs,
read_deleted=read_deleted, project_only=True)
if filters:
query = common.apply_filters(query, filters)
return query
@db_api.context_manager.reader @db_api.context_manager.reader
def _vnf_notify_get_by_id(context, vnf_instance_id, columns_to_join=None): def _vnf_notify_get_by_id(context, vnf_instance_id, columns_to_join=None):
@ -484,6 +498,19 @@ class VnfLcmOpOccList(ovoo_base.ObjectListBase, base.TackerObject):
context, read_deleted=read_deleted, filters=filters) context, read_deleted=read_deleted, filters=filters)
return _make_vnf_lcm_op_occs_list(context, cls(), db_vnf_lcm_op_occs) return _make_vnf_lcm_op_occs_list(context, cls(), db_vnf_lcm_op_occs)
@base.remotable_classmethod
def get_by_marker_filter(cls, context, limit,
marker_obj, filters=None, read_deleted=None):
query = _vnf_lcm_op_occs_get_by_filters_query(
context, read_deleted=read_deleted, filters=filters)
query = sqlalchemyutils.paginate_query(query,
model=models.VnfLcmOpOccs,
limit=limit,
sorts=[['id', 'asc']],
marker_obj=marker_obj)
db_vnf_lcm_op_occs = query.all()
return _make_vnf_lcm_op_occs_list(context, cls(), db_vnf_lcm_op_occs)
@base.TackerObjectRegistry.register @base.TackerObjectRegistry.register
class ResourceChanges(base.TackerObject, class ResourceChanges(base.TackerObject,

View File

@ -24,6 +24,7 @@ import tacker.conf
from tacker.db import api as db_api from tacker.db import api as db_api
from tacker.db.db_sqlalchemy import api from tacker.db.db_sqlalchemy import api
from tacker.db.db_sqlalchemy import models from tacker.db.db_sqlalchemy import models
from tacker.db import sqlalchemyutils
from tacker import objects from tacker import objects
from tacker.objects import base from tacker.objects import base
from tacker.objects import common from tacker.objects import common
@ -381,6 +382,37 @@ def _vnf_lcm_subscription_list_by_filters(context,
return query.order_by(models.VnfLcmSubscriptions.created_at).all() return query.order_by(models.VnfLcmSubscriptions.created_at).all()
@db_api.context_manager.reader
def _vnf_lcm_subscription_get_query(context, read_deleted=None, filters=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 = common.apply_filters(query, filters)
return query.order_by(models.VnfLcmSubscriptions.created_at)
@db_api.context_manager.writer @db_api.context_manager.writer
def _vnf_lcm_subscriptions_create(context, values, filter): def _vnf_lcm_subscriptions_create(context, values, filter):
with db_api.context_manager.writer.using(context): with db_api.context_manager.writer.using(context):
@ -721,6 +753,22 @@ class LccnSubscriptionList(ovoo_base.ObjectListBase, base.TackerObject):
'objects': fields.ListOfObjectsField('LccnSubscription') 'objects': fields.ListOfObjectsField('LccnSubscription')
} }
@base.remotable_classmethod
def get_by_marker_filter(cls, context,
limit, marker_obj,
filters=None, read_deleted=None):
query = _vnf_lcm_subscription_get_query(context,
read_deleted=read_deleted,
filters=filters)
query = sqlalchemyutils.paginate_query(query,
model=models.VnfLcmSubscriptions,
limit=limit,
sorts=[['id', 'asc']],
marker_obj=marker_obj)
db_subscriptions = query.all()
return _make_subscription_list(context, cls(), db_subscriptions)
@base.remotable_classmethod @base.remotable_classmethod
def get_by_filters(cls, context, read_deleted=None, def get_by_filters(cls, context, read_deleted=None,
filters=None, nextpage_opaque_marker=None): filters=None, nextpage_opaque_marker=None):

View File

@ -29,6 +29,7 @@ from tacker.common import utils
from tacker.db import api as db_api from tacker.db import api as db_api
from tacker.db.db_sqlalchemy import api from tacker.db.db_sqlalchemy import api
from tacker.db.db_sqlalchemy import models from tacker.db.db_sqlalchemy import models
from tacker.db import sqlalchemyutils
from tacker import objects from tacker import objects
from tacker.objects import base from tacker.objects import base
from tacker.objects import common from tacker.objects import common
@ -115,6 +116,39 @@ def _update_user_defined_data(context, package_uuid, user_data):
return result return result
@db_api.context_manager.reader
def _vnf_packages_get_by_filters_query(context, read_deleted=None,
filters=None):
query = api.model_query(context, models.VnfPackage,
read_deleted=read_deleted,
project_only=True).options(joinedload('_metadata'))
if filters:
# Need to join VnfDeploymentFlavour, VnfSoftwareImage and
# VnfSoftwareImageMetadata db table explicitly
# only when filters contains one of the column matching
# from VnfSoftwareImage or VnfSoftwareImageMetadata db table.
filter_data = json.dumps(filters)
if 'VnfSoftwareImageMetadata' in filter_data:
query = query.join(models.VnfDeploymentFlavour).join(
models.VnfSoftwareImage).join(
models.VnfSoftwareImageMetadata)
elif 'VnfSoftwareImage' in filter_data:
query = query.join(models.VnfDeploymentFlavour).join(
models.VnfSoftwareImage)
if 'VnfPackageArtifactInfo' in filter_data:
query = query.join(models.VnfPackageArtifactInfo)
if 'VnfPackageVnfd' in filter_data:
query = query.join(models.VnfPackageVnfd)
query = common.apply_filters(query, filters)
return query
@db_api.context_manager.reader @db_api.context_manager.reader
def _vnf_package_get_by_id(context, package_uuid, columns_to_join=None): def _vnf_package_get_by_id(context, package_uuid, columns_to_join=None):
@ -678,6 +712,22 @@ class VnfPackagesList(ovoo_base.ObjectListBase, base.TackerObject):
return _make_vnf_packages_list(context, cls(), db_vnf_packages, return _make_vnf_packages_list(context, cls(), db_vnf_packages,
expected_attrs) expected_attrs)
@base.remotable_classmethod
def get_by_marker_filter(cls, context,
limit, marker_obj,
filters=None, read_deleted=None):
query = _vnf_packages_get_by_filters_query(context,
read_deleted=read_deleted,
filters=filters)
query = sqlalchemyutils.paginate_query(query,
model=models.VnfPackage,
limit=limit,
sorts=[['id', 'asc']],
marker_obj=marker_obj)
db_vnf_packages = query.all()
return _make_vnf_packages_list(context, cls(), db_vnf_packages)
@base.remotable_classmethod @base.remotable_classmethod
def get_by_filters(cls, context, read_deleted=None, filters=None): def get_by_filters(cls, context, read_deleted=None, filters=None):
db_vnf_packages = _vnf_package_list_by_filters(context, db_vnf_packages = _vnf_package_list_by_filters(context,

View File

@ -1882,7 +1882,15 @@ def _subscription_links(subscription_dict):
return subscription_dict return subscription_dict
def return_subscription_obj(**updates): def return_lccn_subscription(**updates):
subscription = _fake_subscription_obj(**updates)
subscription['filter'] = json.dumps(subscription['filter'])
obj = objects.LccnSubscription(**subscription)
return obj
def return_lccn_subscription_obj(**updates):
subscription = _fake_subscription_obj(**updates) subscription = _fake_subscription_obj(**updates)
subscription['filter'] = json.dumps(subscription['filter']) subscription['filter'] = json.dumps(subscription['filter'])
obj = objects.LccnSubscriptionRequest(**subscription) obj = objects.LccnSubscriptionRequest(**subscription)

View File

@ -1687,7 +1687,7 @@ class TestController(base.TestCase):
self.assertEqual(expected_message, exception.msg) self.assertEqual(expected_message, exception.msg)
@mock.patch.object(objects.VnfInstanceList, "get_by_filters") @mock.patch.object(objects.VnfInstanceList, "get_by_marker_filter")
def test_index(self, mock_vnf_list): def test_index(self, mock_vnf_list):
req = fake_request.HTTPRequest.blank('/vnf_instances') req = fake_request.HTTPRequest.blank('/vnf_instances')
vnf_instance_1 = fakes.return_vnf_instance() vnf_instance_1 = fakes.return_vnf_instance()
@ -1701,7 +1701,8 @@ class TestController(base.TestCase):
fields.VnfInstanceState.INSTANTIATED)] fields.VnfInstanceState.INSTANTIATED)]
self.assertEqual(expected_result, resp.json) self.assertEqual(expected_result, resp.json)
@mock.patch.object(objects.VnfInstanceList, "get_by_filters") @mock.patch.object(objects.VnfInstanceList, "get_by_marker_filter",
return_value=[])
def test_index_empty_response(self, mock_vnf_list): def test_index_empty_response(self, mock_vnf_list):
req = fake_request.HTTPRequest.blank('/vnf_instances') req = fake_request.HTTPRequest.blank('/vnf_instances')
mock_vnf_list.return_value = [] mock_vnf_list.return_value = []
@ -1831,7 +1832,7 @@ class TestController(base.TestCase):
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id, self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
resp.json['detail']) resp.json['detail'])
@mock.patch.object(objects.VnfInstanceList, "get_by_filters") @mock.patch.object(objects.VnfInstanceList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'filter': "(eq,vnfInstanceName,'dummy_name')"}, {'filter': "(eq,vnfInstanceName,'dummy_name')"},
{'filter': "(in,vnfInstanceName,'dummy_name')"}, {'filter': "(in,vnfInstanceName,'dummy_name')"},
@ -1863,7 +1864,7 @@ class TestController(base.TestCase):
fields.VnfInstanceState.INSTANTIATED)] fields.VnfInstanceState.INSTANTIATED)]
self.assertEqual(expected_result, res_dict.json) self.assertEqual(expected_result, res_dict.json)
@mock.patch.object(objects.VnfInstanceList, "get_by_filters") @mock.patch.object(objects.VnfInstanceList, "get_by_marker_filter")
def test_index_filter_combination(self, mock_vnf_list): def test_index_filter_combination(self, mock_vnf_list):
"""Test multiple filter parameters separated by semicolon.""" """Test multiple filter parameters separated by semicolon."""
params = { params = {
@ -1886,7 +1887,7 @@ class TestController(base.TestCase):
fields.VnfInstanceState.INSTANTIATED)] fields.VnfInstanceState.INSTANTIATED)]
self.assertEqual(expected_result, res_dict.json) self.assertEqual(expected_result, res_dict.json)
@mock.patch.object(objects.VnfInstanceList, "get_by_filters") @mock.patch.object(objects.VnfInstanceList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'filter': "(eq,vnfInstanceName,dummy_value)"}, {'filter': "(eq,vnfInstanceName,dummy_value)"},
{'filter': "(eq,vnfInstanceDescription,dummy value)"}, {'filter': "(eq,vnfInstanceDescription,dummy value)"},
@ -1921,7 +1922,7 @@ class TestController(base.TestCase):
{'filter': "(eq,instantiatedInfo/additionalParams/error,'dummy')"}, {'filter': "(eq,instantiatedInfo/additionalParams/error,'dummy')"},
) )
def test_index_filter_attributes(self, filter_params, def test_index_filter_attributes(self, filter_params,
mock_vnf_list): mock_vnf_list):
"""Test various attributes supported for filter parameter.""" """Test various attributes supported for filter parameter."""
query = urllib.parse.urlencode(filter_params) query = urllib.parse.urlencode(filter_params)
req = fake_request.HTTPRequest.blank( req = fake_request.HTTPRequest.blank(
@ -1939,7 +1940,7 @@ class TestController(base.TestCase):
fields.VnfInstanceState.INSTANTIATED)] fields.VnfInstanceState.INSTANTIATED)]
self.assertEqual(expected_result, res_dict.json) self.assertEqual(expected_result, res_dict.json)
@mock.patch.object(objects.VnfInstanceList, "get_by_filters") @mock.patch.object(objects.VnfInstanceList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'filter': "(eq,vnfInstanceName,value"}, {'filter': "(eq,vnfInstanceName,value"},
{'filter': "eq,vnfInstanceName,value)"}, {'filter': "eq,vnfInstanceName,value)"},
@ -1956,7 +1957,7 @@ class TestController(base.TestCase):
self.assertRaises(exceptions.ValidationError, self.assertRaises(exceptions.ValidationError,
self.controller.index, req) self.controller.index, req)
@mock.patch.object(objects.VnfInstanceList, "get_by_filters") @mock.patch.object(objects.VnfInstanceList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'filter': "(eq,vnfInstanceName,singl'quote)"}, {'filter': "(eq,vnfInstanceName,singl'quote)"},
{'filter': "(eq,vnfInstanceName,three''' quotes)"}, {'filter': "(eq,vnfInstanceName,three''' quotes)"},
@ -1974,7 +1975,7 @@ class TestController(base.TestCase):
self.assertRaises(exceptions.ValidationError, self.assertRaises(exceptions.ValidationError,
self.controller.index, req) self.controller.index, req)
@mock.patch.object(objects.VnfInstanceList, "get_by_filters") @mock.patch.object(objects.VnfInstanceList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'filter': '(eq,vnfdId,value1,value2)'}, {'filter': '(eq,vnfdId,value1,value2)'},
{'filter': '(fake,vnfdId,dummy_vnfd_id)'}, {'filter': '(fake,vnfdId,dummy_vnfd_id)'},
@ -1989,7 +1990,7 @@ class TestController(base.TestCase):
self.assertRaises(exceptions.ValidationError, self.assertRaises(exceptions.ValidationError,
self.controller.index, req) self.controller.index, req)
@mock.patch.object(objects.VnfInstanceList, "get_by_filters") @mock.patch.object(objects.VnfInstanceList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'filter': '(eq,fakeattr,fakevalue)'}, {'filter': '(eq,fakeattr,fakevalue)'},
{'filter': '(eq,,fakevalue)'}, {'filter': '(eq,,fakevalue)'},
@ -2003,7 +2004,7 @@ class TestController(base.TestCase):
self.assertRaises(exceptions.ValidationError, self.assertRaises(exceptions.ValidationError,
self.controller.index, req) self.controller.index, req)
@mock.patch.object(objects.VnfInstanceList, "get_by_filters") @mock.patch.object(objects.VnfInstanceList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'filter': '(eq,data/size,fake_value)'}, {'filter': '(eq,data/size,fake_value)'},
{'filter': '(gt,data/createdAt,fake_value)'}, {'filter': '(gt,data/createdAt,fake_value)'},
@ -2019,47 +2020,85 @@ class TestController(base.TestCase):
self.assertRaises(exceptions.ValidationError, self.assertRaises(exceptions.ValidationError,
self.controller.index, req) self.controller.index, req)
@mock.patch.object(objects.VnfInstanceList, "get_by_filters") @mock.patch.object(objects.vnf_instance.VnfInstanceList,
"get_by_marker_filter")
@mock.patch.object(objects.VnfInstance, "get_by_id")
@ddt.data( @ddt.data(
{'params': {'all_records': 'yes'}, {'params': {'all_records': 'yes'},
'result_names': ['sample1', 'sample2', 'sample3', 'sample4']}, 'result_names': ['sample1', 'sample2', 'sample3', 'sample4']},
{'params': {'all_records': 'yes', 'nextpage_opaque_marker': 'abc'}, {'params': {'all_records': 'yes', 'nextpage_opaque_marker':
'22222222-2222-2222-2222-222222222222'},
'result_names': ['sample1', 'sample2', 'sample3', 'sample4']}, 'result_names': ['sample1', 'sample2', 'sample3', 'sample4']},
{'params': {'nextpage_opaque_marker': 'abc'}, {'params': {'nextpage_opaque_marker':
'44444444-4444-4444-4444-444444444444'},
'result_names': []}, 'result_names': []},
{'params': {'nextpage_opaque_marker':
'22222222-2222-2222-2222-222222222222'},
'result_names': ['sample3', 'sample4']},
{'params': {}, {'params': {},
'result_names': ['sample2']} 'result_names': ['sample1', 'sample2']}
) )
def test_index_paging(self, values, mock_vnf_list): def test_index_paging(self, values,
cfg.CONF.set_override('vnf_instance_num', 1, group='vnf_lcm') mock_marker_obj,
mock_vnf_instance_list):
ids = ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222',
'33333333-3333-3333-3333-333333333333',
'44444444-4444-4444-4444-444444444444',
None]
cfg.CONF.set_override('vnf_instance_num', 2, group='vnf_lcm')
query = urllib.parse.urlencode(values['params']) query = urllib.parse.urlencode(values['params'])
req = fake_request.HTTPRequest.blank( req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_instances?' + query) '/vnflcm/v1/vnf_instances?' + query)
mock_vnf_list.return_value = [ target_index = []
fakes.return_vnf_instance(**{'vnf_instance_name': 'sample1'}), if 'all_records' in values['params'] \
fakes.return_vnf_instance(**{'vnf_instance_name': 'sample2'}), and values['params']['all_records'] == 'yes':
fakes.return_vnf_instance(**{'vnf_instance_name': 'sample3'}), mock_marker_obj.return_value = None
fakes.return_vnf_instance(**{'vnf_instance_name': 'sample4'}) target_index = [0, 1, 2, 3]
] elif 'nextpage_opaque_marker' in values['params']:
mock_marker_obj.return_value = fakes.return_vnf_instance(
**{'id': values['params']['nextpage_opaque_marker']})
marker_obj_index = ids.index(mock_marker_obj.return_value['id'])
for i in range(marker_obj_index + 1, len(ids) - 1):
target_index.append(i)
else:
mock_marker_obj.return_value = None
target_index = [0, 1]
mock_vnf_instance_list.return_value = []
for index in range(len(target_index)):
mock_vnf_instance_list.return_value.append(
fakes.return_vnf_instance(**{'id': ids[target_index[index]],
'vnf_instance_name':
values['result_names'][index]}))
expected_result = [] expected_result = []
for name in values['result_names']: for index in range(len(target_index)):
_links = fakes.fake_vnf_instance_response()['_links']
expected_links = (re.sub("vnf_instances/[a-zA-Z0-9-]*",
"vnf_instances/{}".format(ids[target_index[index]]),
str(_links)))
expected_links = json.loads(expected_links.replace("'", '"'))
expected_result.append(fakes.fake_vnf_instance_response( expected_result.append(fakes.fake_vnf_instance_response(
**{'vnfInstanceName': name})) **{'vnfInstanceName': values['result_names'][index],
'id': ids[target_index[index]],
'_links': expected_links}))
expected_result_link = None
if 'all_records' not in values['params'] and len(target_index) >= 2:
expected_result_link = (
'<http://localhost//vnflcm/v1/vnf_instances' +
'?nextpage_opaque_marker=%s>; rel="next"'
% ids[target_index[1]])
res_dict = self.controller.index(req) res_dict = self.controller.index(req)
if 'Link' in res_dict.headers:
next_url = re.findall('<(.*)>', res_dict.headers['Link'])[0]
query = urllib.parse.urlparse(next_url).query
req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_instances?' + query)
res_dict = self.controller.index(req)
self.assertEqual(expected_result, res_dict.json) self.assertEqual(expected_result, res_dict.json)
if expected_result_link is not None:
self.assertEqual(expected_result_link, res_dict.headers['Link'])
@mock.patch.object(objects.VnfInstanceList, "get_by_filters") @mock.patch.object(objects.VnfInstanceList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'attribute_not_exist': 'some_value'}, {'attribute_not_exist': 'some_value'},
{'all_fields': {}}, {'all_fields': {}},
@ -2078,7 +2117,7 @@ class TestController(base.TestCase):
self.assertRaises(webob.exc.HTTPBadRequest, self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.index, req) self.controller.index, req)
@mock.patch.object(objects.VnfInstanceList, "get_by_filters") @mock.patch.object(objects.VnfInstanceList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'attribute_not_exist': 'some_value'}, {'attribute_not_exist': 'some_value'},
{'all_fields': {}}, {'all_fields': {}},
@ -3795,7 +3834,7 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertEqual(http_client.INTERNAL_SERVER_ERROR, resp.status_code) self.assertEqual(http_client.INTERNAL_SERVER_ERROR, resp.status_code)
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters") @mock.patch.object(objects.VnfLcmOpOccList, "get_by_marker_filter")
def test_op_occ_list(self, mock_op_occ_list): def test_op_occ_list(self, mock_op_occ_list):
req = fake_request.HTTPRequest.blank('/vnflcm/v1/vnf_lcm_op_occs') req = fake_request.HTTPRequest.blank('/vnflcm/v1/vnf_lcm_op_occs')
@ -3815,22 +3854,59 @@ class TestController(base.TestCase):
jsonutils.loads(jsonutils.dump_as_bytes(expected_result)), jsonutils.loads(jsonutils.dump_as_bytes(expected_result)),
resp.json) resp.json)
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters") @mock.patch.object(objects.vnf_lcm_op_occs.VnfLcmOpOccList,
"get_by_marker_filter")
@mock.patch.object(objects.vnf_lcm_op_occs.VnfLcmOpOcc, "get_by_id")
@ddt.data( @ddt.data(
{'params': {'all_records': 'yes'}, {'params': {'all_records': 'yes'},
'result_names': ['INSTANTIATE', 'SCALE', 'HEAL', 'TERMINATE']}, 'result_names': ['INSTANTIATE', 'SCALE', 'HEAL', 'TERMINATE']},
{'params': {'all_records': 'yes', 'nextpage_opaque_marker': 'abc'}, {'params': {'all_records': 'yes', 'nextpage_opaque_marker':
'22222222-2222-2222-2222-222222222222'},
'result_names': ['INSTANTIATE', 'SCALE', 'HEAL', 'TERMINATE']}, 'result_names': ['INSTANTIATE', 'SCALE', 'HEAL', 'TERMINATE']},
{'params': {'nextpage_opaque_marker': 'abc'}, {'params': {'nextpage_opaque_marker':
'44444444-4444-4444-4444-444444444444'},
'result_names': []}, 'result_names': []},
{'params': {'nextpage_opaque_marker':
'22222222-2222-2222-2222-222222222222'},
'result_names': ['HEAL', 'TERMINATE']},
{'params': {}, {'params': {},
'result_names': ['SCALE']} 'result_names': ['INSTANTIATE', 'SCALE']}
) )
def test_op_occ_list_paging(self, values, mock_op_occ_list): def test_op_occ_list_paging(self, values,
cfg.CONF.set_override('lcm_op_occ_num', 1, group='vnf_lcm') mock_marker_obj,
mock_op_occ_list):
ids = ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222',
'33333333-3333-3333-3333-333333333333',
'44444444-4444-4444-4444-444444444444',
None]
cfg.CONF.set_override('lcm_op_occ_num', 2, group='vnf_lcm')
query = urllib.parse.urlencode(values['params']) query = urllib.parse.urlencode(values['params'])
req = fake_request.HTTPRequest.blank( req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_lcm_op_occs?' + query) '/vnflcm/v1/vnf_lcm_op_occs?' + query)
target_index = []
if 'all_records' in values['params'] \
and values['params']['all_records'] == 'yes':
mock_marker_obj.return_value = None
target_index = [0, 1, 2, 3]
elif 'nextpage_opaque_marker' in values['params']:
mock_marker_obj.return_value = fakes.return_vnf_lcm_opoccs_obj(
**{'id': values['params']['nextpage_opaque_marker']})
if len(values['result_names']) > 0:
target_index.append(
ids.index(mock_marker_obj.return_value['id']) + 1)
target_index.append(
ids.index(mock_marker_obj.return_value['id']) + 2)
else:
mock_marker_obj.return_value = None
target_index = [0, 1]
mock_op_occ_list.return_value = []
for index in range(len(target_index)):
mock_op_occ_list.return_value.append(
fakes.return_vnf_lcm_opoccs_obj(
**{'id': ids[target_index[index]],
'operation': values['result_names'][index]}))
complex_attributes = [ complex_attributes = [
'error', 'error',
@ -3838,34 +3914,36 @@ class TestController(base.TestCase):
'operationParams', 'operationParams',
'changedInfo'] 'changedInfo']
vnf_lcm_op_occ = [
fakes.return_vnf_lcm_opoccs_obj(**{'operation': 'INSTANTIATE'}),
fakes.return_vnf_lcm_opoccs_obj(**{'operation': 'SCALE'}),
fakes.return_vnf_lcm_opoccs_obj(**{'operation': 'HEAL'}),
fakes.return_vnf_lcm_opoccs_obj(**{'operation': 'TERMINATE'})
]
expected_result = [] expected_result = []
for name in values['result_names']: for index in range(len(target_index)):
_links = fakes.index_response()[0]['_links']
expected_links = re.sub("vnf_lcm_op_occs/[a-zA-Z0-9-]*",
"vnf_lcm_op_occs/{}".format(
ids[target_index[index]]), str(_links))
expected_links = json.loads(expected_links.replace("'", '"'))
expected_result += fakes.index_response( expected_result += fakes.index_response(
remove_attrs=complex_attributes, remove_attrs=complex_attributes,
vnf_lcm_op_occs_updates={'operation': name}) vnf_lcm_op_occs_updates={'operation':
values['result_names'][index],
'id': ids[target_index[index]],
'_links': expected_links})
expected_result_link = None
if 'all_records' not in values['params'] and len(target_index) >= 2:
expected_result_link = (
'<http://localhost//vnflcm/v1/vnf_lcm_op_occs' +
'?nextpage_opaque_marker=%s>; rel="next"'
% ids[target_index[1]])
mock_op_occ_list.return_value = vnf_lcm_op_occ
resp = self.controller.list_lcm_op_occs(req) resp = self.controller.list_lcm_op_occs(req)
if 'Link' in resp.headers:
next_url = re.findall('<(.*)>', resp.headers['Link'])[0]
query = urllib.parse.urlparse(next_url).query
req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_lcm_op_occs?' + query)
resp = self.controller.list_lcm_op_occs(req)
self.assertEqual( self.assertEqual(
jsonutils.loads(jsonutils.dump_as_bytes(expected_result)), jsonutils.loads(jsonutils.dump_as_bytes(expected_result)),
resp.json) resp.json)
if expected_result_link is not None:
self.assertEqual(expected_result_link, resp.headers['Link'])
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters") @mock.patch.object(objects.VnfLcmOpOccList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'filter': '(eq,id,f26f181d-7891-4720-b022-b074ec1733ef)'}, {'filter': '(eq,id,f26f181d-7891-4720-b022-b074ec1733ef)'},
{'filter': '(neq,operationState,COMPLETED)'}, {'filter': '(neq,operationState,COMPLETED)'},
@ -3881,7 +3959,7 @@ class TestController(base.TestCase):
"""(neq,operationParams,'"{"terminationType": "FORCEFUL"}"')"""}, """(neq,operationParams,'"{"terminationType": "FORCEFUL"}"')"""},
) )
def test_op_occ_filter_attributes(self, filter_params, def test_op_occ_filter_attributes(self, filter_params,
mock_op_occ_list): mock_op_occ_list):
query = urllib.parse.urlencode(filter_params) query = urllib.parse.urlencode(filter_params)
req = fake_request.HTTPRequest.blank( req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_lcm_op_occs?' + query) '/vnflcm/v1/vnf_lcm_op_occs?' + query)
@ -3902,8 +3980,9 @@ class TestController(base.TestCase):
jsonutils.loads(jsonutils.dump_as_bytes(expected_result)), jsonutils.loads(jsonutils.dump_as_bytes(expected_result)),
res_dict.json) res_dict.json)
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters") @mock.patch.object(objects.VnfLcmOpOccList, "get_by_marker_filter")
def test_op_occ_filter_attributes_invalid_filter(self, mock_op_occ_list): def test_op_occ_filter_attributes_invalid_filter(self,
mock_op_occ_list):
query = urllib.parse.urlencode({'filter': '(lt,non_existing,4)'}) query = urllib.parse.urlencode({'filter': '(lt,non_existing,4)'})
req = fake_request.HTTPRequest.blank( req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_lcm_op_occs?' + query) '/vnflcm/v1/vnf_lcm_op_occs?' + query)
@ -3913,8 +3992,9 @@ class TestController(base.TestCase):
self.assertRaises( self.assertRaises(
exceptions.ValidationError, self.controller.list_lcm_op_occs, req) exceptions.ValidationError, self.controller.list_lcm_op_occs, req)
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters") @mock.patch.object(objects.VnfLcmOpOccList, "get_by_marker_filter")
def test_op_occ_attribute_selector_all_fields(self, mock_op_occ_list): def test_op_occ_attribute_selector_all_fields(self,
mock_op_occ_list):
params = {'all_fields': 'True'} params = {'all_fields': 'True'}
query = urllib.parse.urlencode(params) query = urllib.parse.urlencode(params)
req = fake_request.HTTPRequest.blank('/vnflcm/v1/vnf_lcm_op_occs?' + req = fake_request.HTTPRequest.blank('/vnflcm/v1/vnf_lcm_op_occs?' +
@ -3929,7 +4009,7 @@ class TestController(base.TestCase):
jsonutils.loads(jsonutils.dump_as_bytes(expected_result)), jsonutils.loads(jsonutils.dump_as_bytes(expected_result)),
res_dict.json) res_dict.json)
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters") @mock.patch.object(objects.VnfLcmOpOccList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'fields': 'error'}, {'fields': 'error'},
{'fields': 'resourceChanges'}, {'fields': 'resourceChanges'},
@ -3937,7 +4017,7 @@ class TestController(base.TestCase):
{'fields': 'changedInfo'} {'fields': 'changedInfo'}
) )
def test_op_occ_attribute_selector_fields(self, filter_params, def test_op_occ_attribute_selector_fields(self, filter_params,
mock_op_occ_list): mock_op_occ_list):
query = urllib.parse.urlencode(filter_params) query = urllib.parse.urlencode(filter_params)
req = fake_request.HTTPRequest.blank( req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_lcm_op_occs?' + query) '/vnflcm/v1/vnf_lcm_op_occs?' + query)
@ -3959,7 +4039,7 @@ class TestController(base.TestCase):
jsonutils.loads(jsonutils.dump_as_bytes(expected_result)), jsonutils.loads(jsonutils.dump_as_bytes(expected_result)),
res_dict.json) res_dict.json)
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters") @mock.patch.object(objects.VnfLcmOpOccList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'exclude_fields': 'error'}, {'exclude_fields': 'error'},
{'exclude_fields': 'resourceChanges'}, {'exclude_fields': 'resourceChanges'},
@ -3967,7 +4047,7 @@ class TestController(base.TestCase):
{'exclude_fields': 'changedInfo'} {'exclude_fields': 'changedInfo'}
) )
def test_op_occ_attribute_selector_exclude_fields(self, filter_params, def test_op_occ_attribute_selector_exclude_fields(self, filter_params,
mock_op_occ_list): mock_op_occ_list):
query = urllib.parse.urlencode(filter_params) query = urllib.parse.urlencode(filter_params)
req = fake_request.HTTPRequest.blank( req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_lcm_op_occs?' + query) '/vnflcm/v1/vnf_lcm_op_occs?' + query)
@ -3982,8 +4062,9 @@ class TestController(base.TestCase):
jsonutils.loads(jsonutils.dump_as_bytes(expected_result)), jsonutils.loads(jsonutils.dump_as_bytes(expected_result)),
res_dict.json) res_dict.json)
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters") @mock.patch.object(objects.VnfLcmOpOccList, "get_by_marker_filter")
def test_op_occ_attribute_selector_fields_error(self, mock_op_occ_list): def test_op_occ_attribute_selector_fields_error(self,
mock_op_occ_list):
query = urllib.parse.urlencode({'fields': 'non_existent_column'}) query = urllib.parse.urlencode({'fields': 'non_existent_column'})
req = fake_request.HTTPRequest.blank( req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_lcm_op_occs?' + query) '/vnflcm/v1/vnf_lcm_op_occs?' + query)
@ -4409,7 +4490,7 @@ class TestController(base.TestCase):
@mock.patch.object(vnf_subscription_view.ViewBuilder, @mock.patch.object(vnf_subscription_view.ViewBuilder,
"validate_filter") "validate_filter")
@mock.patch.object(objects.LccnSubscriptionList, @mock.patch.object(objects.LccnSubscriptionList,
"get_by_filters") "get_by_marker_filter")
def test_subscription_list_all(self, def test_subscription_list_all(self,
mock_subscription_list, mock_subscription_list,
mock_subscription_filter, mock_subscription_filter,
@ -4436,7 +4517,7 @@ class TestController(base.TestCase):
@mock.patch.object(vnf_subscription_view.ViewBuilder, @mock.patch.object(vnf_subscription_view.ViewBuilder,
"validate_filter") "validate_filter")
@mock.patch.object(objects.LccnSubscriptionList, @mock.patch.object(objects.LccnSubscriptionList,
"get_by_filters") "get_by_marker_filter")
def test_subscription_list_empty(self, def test_subscription_list_empty(self,
mock_subscription_list, mock_subscription_list,
mock_subscription_filter, mock_subscription_filter,
@ -4469,66 +4550,89 @@ class TestController(base.TestCase):
resp = req.get_response(self.app) resp = req.get_response(self.app)
self.assertEqual(500, resp.status_code) self.assertEqual(500, resp.status_code)
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(objects.vnf_lcm_subscriptions.LccnSubscriptionList,
return_value={'VNFM': "get_by_marker_filter")
test_nfvo_plugin.FakeVNFMPlugin()}) @mock.patch.object(objects.LccnSubscription, "get_by_id")
@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( @ddt.data(
{'params': {'all_records': 'yes'}, {'params': {'all_records': 'yes'},
'result_names': ['subscription_id_1', 'subscription_id_2', 'result_names': ['subscription_id_1', 'subscription_id_2',
'subscription_id_3', 'subscription_id_4']}, 'subscription_id_3', 'subscription_id_4']},
{'params': {'all_records': 'yes', 'nextpage_opaque_marker': 'abc'}, {'params': {'all_records': 'yes', 'nextpage_opaque_marker':
'22222222-2222-2222-2222-222222222222'},
'result_names': ['subscription_id_1', 'subscription_id_2', 'result_names': ['subscription_id_1', 'subscription_id_2',
'subscription_id_3', 'subscription_id_4']}, 'subscription_id_3', 'subscription_id_4']},
{'params': {'nextpage_opaque_marker': 'abc'}, {'params': {'nextpage_opaque_marker':
'44444444-4444-4444-4444-444444444444'},
'result_names': []}, 'result_names': []},
{'params': {'nextpage_opaque_marker':
'22222222-2222-2222-2222-222222222222'},
'result_names': ['subscription_id_3', 'subscription_id_4']},
{'params': {}, {'params': {},
'result_names': ['subscription_id_2']} 'result_names': ['subscription_id_1', 'subscription_id_2']}
) )
def test_subscription_list_paging(self, def test_subscription_list_paging(self,
values, values,
mock_subscription_list, mock_marker_obj,
mock_subscription_filter, mock_subscription_list):
mock_subscription_view, ids = ['11111111-1111-1111-1111-111111111111',
mock_get_service_plugins): '22222222-2222-2222-2222-222222222222',
mock_subscription_filter.return_value = None '33333333-3333-3333-3333-333333333333',
last = True '44444444-4444-4444-4444-444444444444',
cfg.CONF.set_override('subscription_num', 1, group='vnf_lcm') None]
mock_marker_obj.return_value = None
cfg.CONF.set_override('subscription_num', 2, group='vnf_lcm')
query = urllib.parse.urlencode(values['params']) query = urllib.parse.urlencode(values['params'])
req = fake_request.HTTPRequest.blank('/subscriptions?' + query) req = fake_request.HTTPRequest.blank('/subscriptions?' + query)
req.method = 'GET' req.method = 'GET'
subscription_list = [
fakes.return_subscription_object( target_index = []
**{'id': uuidsentinel.subscription_id_1}), if 'all_records' in values['params'] \
fakes.return_subscription_object( and values['params']['all_records'] == 'yes':
**{'id': uuidsentinel.subscription_id_2}), mock_marker_obj = None
fakes.return_subscription_object( target_index = [0, 1, 2, 3]
**{'id': uuidsentinel.subscription_id_3}), elif 'nextpage_opaque_marker' in values['params']:
fakes.return_subscription_object( mock_marker_obj.return_value = fakes.return_subscription_object(
**{'id': uuidsentinel.subscription_id_4}) **{'id': values['params']['nextpage_opaque_marker']})
] marker_obj_index = ids.index(mock_marker_obj.return_value['id'])
mock_subscription_list.return_value = [subscription_list, last] for i in range(marker_obj_index + 1, len(ids) - 1):
mock_subscription_view.return_value = subscription_list target_index.append(i)
else:
mock_marker_obj.return_value = None
target_index = [0, 1]
mock_subscription_list.return_value = []
for index in range(len(target_index)):
mock_subscription_list.return_value.append(
fakes.return_lccn_subscription_obj(**{'id':
ids[target_index[index]]}))
resp = self.controller.subscription_list(req) resp = self.controller.subscription_list(req)
if 'Link' in resp.headers:
next_url = re.findall('<(.*)>', resp.headers['Link'])[0]
query = urllib.parse.urlparse(next_url).query
req = fake_request.HTTPRequest.blank('/subscriptions?' + query)
resp = self.controller.subscription_list(req)
expected_result = [] expected_result = []
for name in values['result_names']: for index in range(len(target_index)):
expected_result.append(fakes.return_subscription_object( href = ("http://localhost:9890//vnflcm/v1/subscriptions/{}"
**{'id': eval('uuidsentinel.' + name)})) .format(ids[target_index[index]]))
_link = {'self': {'href': href}}
subscription_object = fakes.return_subscription_object(
**{'id': ids[target_index[index]], '_links': _link})
subscription_object['callbackUri'] = \
subscription_object.pop('callback_uri')
expected_result.append(subscription_object)
expected_result_link = None
if 'all_records' not in values['params'] and len(target_index) >= 2:
expected_result_link = (
'<http://localhost//subscriptions' +
'?nextpage_opaque_marker=%s>; rel="next"'
% ids[target_index[1]])
self.assertEqual(200, resp.status_code) self.assertEqual(200, resp.status_code)
self.assertEqual(expected_result, resp.json) self.assertEqual(expected_result, resp.json)
if expected_result_link is not None:
self.assertEqual(expected_result_link, resp.headers['Link'])
@mock.patch.object(TackerManager, 'get_service_plugins', @mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': return_value={'VNFM':
@ -4538,7 +4642,7 @@ class TestController(base.TestCase):
def test_subscription_show(self, mock_get_subscription, def test_subscription_show(self, mock_get_subscription,
mock_get_service_plugins): mock_get_service_plugins):
mock_get_subscription.return_value =\ mock_get_subscription.return_value =\
fakes.return_subscription_obj() fakes.return_lccn_subscription_obj()
req = fake_request.HTTPRequest.blank( req = fake_request.HTTPRequest.blank(
'/subscriptions/%s' % uuidsentinel.subscription_id) '/subscriptions/%s' % uuidsentinel.subscription_id)
@ -4559,7 +4663,7 @@ class TestController(base.TestCase):
@mock.patch.object(vnf_subscription_view.ViewBuilder, @mock.patch.object(vnf_subscription_view.ViewBuilder,
"validate_filter") "validate_filter")
@mock.patch.object(objects.LccnSubscriptionList, @mock.patch.object(objects.LccnSubscriptionList,
"get_by_filters") "get_by_marker_filter")
@ddt.data( @ddt.data(
{'operator': "eq", 'key': 'id', {'operator': "eq", 'key': 'id',
'value': uuidsentinel.subscription_id}, 'value': uuidsentinel.subscription_id},

View File

@ -117,7 +117,7 @@ class TestController(base.TestCase):
self.assertRaises(exc.HTTPNotFound, self.controller.show, self.assertRaises(exc.HTTPNotFound, self.controller.show,
req, constants.UUID) req, constants.UUID)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
@ddt.data('/vnfpkgm/v1/vnf_packages') @ddt.data('/vnfpkgm/v1/vnf_packages')
def test_index(self, path, mock_vnf_list): def test_index(self, path, mock_vnf_list):
req = fake_request.HTTPRequest.blank(path) req = fake_request.HTTPRequest.blank(path)
@ -133,7 +133,7 @@ class TestController(base.TestCase):
jsonutils.loads(jsonutils.dump_as_bytes(expected_result, jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
default=str)), res_dict.json) default=str)), res_dict.json)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
def test_index_attribute_selector_all_fields(self, mock_vnf_list): def test_index_attribute_selector_all_fields(self, mock_vnf_list):
params = {'all_fields': ''} params = {'all_fields': ''}
query = urllib.parse.urlencode(params) query = urllib.parse.urlencode(params)
@ -146,7 +146,7 @@ class TestController(base.TestCase):
jsonutils.loads(jsonutils.dump_as_bytes(expected_result, jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
default=str)), res_dict.json) default=str)), res_dict.json)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
def test_index_attribute_selector_exclude_default(self, mock_vnf_list): def test_index_attribute_selector_exclude_default(self, mock_vnf_list):
params = {'exclude_default': ''} params = {'exclude_default': ''}
query = urllib.parse.urlencode(params) query = urllib.parse.urlencode(params)
@ -164,7 +164,7 @@ class TestController(base.TestCase):
jsonutils.loads(jsonutils.dump_as_bytes(expected_result, jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
default=str)), res_dict.json) default=str)), res_dict.json)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'exclude_fields': 'softwareImages'}, {'exclude_fields': 'softwareImages'},
{'exclude_fields': 'checksum'}, {'exclude_fields': 'checksum'},
@ -184,7 +184,7 @@ class TestController(base.TestCase):
jsonutils.loads(jsonutils.dump_as_bytes(expected_result, jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
default=str)), res_dict.json) default=str)), res_dict.json)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'fields': 'softwareImages'}, {'fields': 'softwareImages'},
{'fields': 'checksum'}, {'fields': 'checksum'},
@ -213,7 +213,7 @@ class TestController(base.TestCase):
jsonutils.loads(jsonutils.dump_as_bytes(expected_result, jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
default=str)), res_dict.json) default=str)), res_dict.json)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
def test_index_attribute_selector_user_defined_data_combination(self, def test_index_attribute_selector_user_defined_data_combination(self,
mock_vnf_list): mock_vnf_list):
"""Query user defined data with fields parameter """Query user defined data with fields parameter
@ -242,7 +242,7 @@ class TestController(base.TestCase):
jsonutils.loads(jsonutils.dump_as_bytes(expected_result, jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
default=str)), res_dict.json) default=str)), res_dict.json)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
def test_index_attribute_selector_user_defined_data(self, mock_vnf_list): def test_index_attribute_selector_user_defined_data(self, mock_vnf_list):
params = {'fields': 'userDefinedData/key1,userDefinedData/key2'} params = {'fields': 'userDefinedData/key1,userDefinedData/key2'}
query = urllib.parse.urlencode(params) query = urllib.parse.urlencode(params)
@ -256,7 +256,7 @@ class TestController(base.TestCase):
jsonutils.loads(jsonutils.dump_as_bytes(expected_result, jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
default=str)), res_dict.json) default=str)), res_dict.json)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
def test_index_attribute_selector_nested_complex_attribute(self, def test_index_attribute_selector_nested_complex_attribute(self,
mock_vnf_list): mock_vnf_list):
params = {'fields': 'softwareImages/checksum/algorithm,' params = {'fields': 'softwareImages/checksum/algorithm,'
@ -284,7 +284,7 @@ class TestController(base.TestCase):
jsonutils.loads(jsonutils.dump_as_bytes(expected_result, jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
default=str)), res_dict.json) default=str)), res_dict.json)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'filter': '(eq,vnfdId,%s)' % constants.UUID}, {'filter': '(eq,vnfdId,%s)' % constants.UUID},
{'filter': '(in,vnfdId,%s)' % constants.UUID}, {'filter': '(in,vnfdId,%s)' % constants.UUID},
@ -325,7 +325,7 @@ class TestController(base.TestCase):
jsonutils.loads(jsonutils.dump_as_bytes(expected_result, jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
default=str)), res_dict.json) default=str)), res_dict.json)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
def test_index_filter_combination(self, mock_vnf_list): def test_index_filter_combination(self, mock_vnf_list):
"""Test multiple filter parameters separated by semicolon """ """Test multiple filter parameters separated by semicolon """
params = {'filter': '(eq,vnfdId,%s);(eq,id,%s)' % params = {'filter': '(eq,vnfdId,%s);(eq,id,%s)' %
@ -345,7 +345,7 @@ class TestController(base.TestCase):
jsonutils.loads(jsonutils.dump_as_bytes(expected_result, jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
default=str)), res_dict.json) default=str)), res_dict.json)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'filter': '(eq,id,%s)' % constants.UUID}, {'filter': '(eq,id,%s)' % constants.UUID},
{'filter': '(eq,vnfdId,%s)' % constants.UUID}, {'filter': '(eq,vnfdId,%s)' % constants.UUID},
@ -394,7 +394,7 @@ class TestController(base.TestCase):
jsonutils.loads(jsonutils.dump_as_bytes(expected_result, jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
default=str)), res_dict.json) default=str)), res_dict.json)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'filter': "(eq,vnfProductName,dummy_value)"}, {'filter': "(eq,vnfProductName,dummy_value)"},
{'filter': "(eq,vnfProductName,dummy value)"}, {'filter': "(eq,vnfProductName,dummy value)"},
@ -428,7 +428,7 @@ class TestController(base.TestCase):
jsonutils.loads(jsonutils.dump_as_bytes(expected_result, jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
default=str)), res_dict.json) default=str)), res_dict.json)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'filter': "(eq,vnfProductName,value"}, {'filter': "(eq,vnfProductName,value"},
{'filter': "eq,vnfProductName,value)"}, {'filter': "eq,vnfProductName,value)"},
@ -446,7 +446,7 @@ class TestController(base.TestCase):
self.assertRaises(tacker_exc.ValidationError, self.controller.index, self.assertRaises(tacker_exc.ValidationError, self.controller.index,
req) req)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'filter': "(eq,vnfProductName,singl'quote)"}, {'filter': "(eq,vnfProductName,singl'quote)"},
{'filter': "(eq,vnfProductName,three''' quotes)"}, {'filter': "(eq,vnfProductName,three''' quotes)"},
@ -466,7 +466,7 @@ class TestController(base.TestCase):
self.assertRaises(tacker_exc.ValidationError, self.controller.index, self.assertRaises(tacker_exc.ValidationError, self.controller.index,
req) req)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'filter': '(eq,vnfdId,value1,value2)'}, {'filter': '(eq,vnfdId,value1,value2)'},
{'filter': '(fake,vnfdId,dummy_vnfd_id)'}, {'filter': '(fake,vnfdId,dummy_vnfd_id)'},
@ -481,7 +481,7 @@ class TestController(base.TestCase):
self.assertRaises(tacker_exc.ValidationError, self.controller.index, self.assertRaises(tacker_exc.ValidationError, self.controller.index,
req) req)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'filter': '(eq,fakeattr,fakevalue)'}, {'filter': '(eq,fakeattr,fakevalue)'},
{'filter': '(eq,,fakevalue)'}, {'filter': '(eq,,fakevalue)'},
@ -495,7 +495,7 @@ class TestController(base.TestCase):
self.assertRaises(tacker_exc.ValidationError, self.controller.index, self.assertRaises(tacker_exc.ValidationError, self.controller.index,
req) req)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'filter': '(eq,id,fake_value)'}, {'filter': '(eq,id,fake_value)'},
{'filter': '(eq,vnfd_id,fake_value)'}, {'filter': '(eq,vnfd_id,fake_value)'},
@ -515,7 +515,7 @@ class TestController(base.TestCase):
self.assertRaises(tacker_exc.ValidationError, self.controller.index, self.assertRaises(tacker_exc.ValidationError, self.controller.index,
req) req)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'fields': 'nonExistentField'}, {'fields': 'nonExistentField'},
{'exclude_fields': 'nonExistentField'} {'exclude_fields': 'nonExistentField'}
@ -529,7 +529,7 @@ class TestController(base.TestCase):
self.assertRaises(tacker_exc.ValidationError, self.controller.index, self.assertRaises(tacker_exc.ValidationError, self.controller.index,
req) req)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'fields': 'softwareImages', 'all_fields': ''}, {'fields': 'softwareImages', 'all_fields': ''},
{'exclude_fields': 'checksum', 'all_fields': ''}, {'exclude_fields': 'checksum', 'all_fields': ''},
@ -545,7 +545,7 @@ class TestController(base.TestCase):
self.assertRaises(tacker_exc.ValidationError, self.controller.index, self.assertRaises(tacker_exc.ValidationError, self.controller.index,
req) req)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
@ddt.data( @ddt.data(
{'exclude_default': 'softwareImages'}, {'exclude_default': 'softwareImages'},
{'all_fields': 'checksum'}, {'all_fields': 'checksum'},
@ -560,51 +560,92 @@ class TestController(base.TestCase):
self.assertRaises(tacker_exc.ValidationError, self.controller.index, self.assertRaises(tacker_exc.ValidationError, self.controller.index,
req) req)
@mock.patch.object(VnfPackagesList, "get_by_filters") @mock.patch.object(VnfPackagesList, "get_by_marker_filter")
@mock.patch.object(objects.VnfPackage, "get_by_id")
@ddt.data( @ddt.data(
{'params': {'all_records': 'yes'}, {'params': {'all_records': 'yes'},
'result_names': ['sample1', 'sample2', 'sample3', 'sample4']}, 'result_names': ['sample1', 'sample2', 'sample3', 'sample4']},
{'params': {'all_records': 'yes', 'nextpage_opaque_marker': 'abc'}, {'params': {'all_records': 'yes', 'nextpage_opaque_marker':
'22222222-2222-2222-2222-222222222222'},
'result_names': ['sample1', 'sample2', 'sample3', 'sample4']}, 'result_names': ['sample1', 'sample2', 'sample3', 'sample4']},
{'params': {'nextpage_opaque_marker': 'abc'}, {'params': {'nextpage_opaque_marker':
'44444444-4444-4444-4444-444444444444'},
'result_names': []}, 'result_names': []},
{'params': {'nextpage_opaque_marker':
'22222222-2222-2222-2222-222222222222'},
'result_names': ['sample3', 'sample4']},
{'params': {}, {'params': {},
'result_names': ['sample2']} 'result_names': ['sample1', 'sample2']}
) )
def test_index_paging(self, values, mock_vnf_list): def test_index_paging(self, values,
cfg.CONF.set_override('vnf_package_num', 1, group='vnf_package') mock_marker_obj, mock_vnf_package_list):
ids = ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222',
'33333333-3333-3333-3333-333333333333',
'44444444-4444-4444-4444-444444444444',
None]
cfg.CONF.set_override('vnf_package_num', 2, group='vnf_package')
query = urllib.parse.urlencode(values['params']) query = urllib.parse.urlencode(values['params'])
req = fake_request.HTTPRequest.blank('/vnfpkgm/v1/vnf_packages?' + req = fake_request.HTTPRequest.blank('/vnfpkgm/v1/vnf_packages?' +
query) query)
mock_vnf_list.return_value = [
fakes.return_vnfpkg_obj( target_index = []
vnfd_updates={'vnf_product_name': 'sample1'}), if 'all_records' in values['params'] \
fakes.return_vnfpkg_obj( and values['params']['all_records'] == 'yes':
vnfd_updates={'vnf_product_name': 'sample2'}), mock_marker_obj.return_value = None
fakes.return_vnfpkg_obj( target_index = [0, 1, 2, 3]
vnfd_updates={'vnf_product_name': 'sample3'}), elif 'nextpage_opaque_marker' in values['params']:
fakes.return_vnfpkg_obj( mock_marker_obj.return_value = fakes.return_vnfpkg_obj(
vnfd_updates={'vnf_product_name': 'sample4'}) vnf_package_updates={'id':
] values['params']['nextpage_opaque_marker']})
marker_obj_index = ids.index(mock_marker_obj.return_value['id'])
for i in range(marker_obj_index + 1, len(ids) - 1):
target_index.append(i)
else:
mock_marker_obj.return_value = None
target_index = [0, 1]
mock_vnf_package_list.return_value = []
for index in range(len(target_index)):
mock_vnf_package_list.return_value.append(
fakes.return_vnfpkg_obj(
vnf_package_updates={'id':
ids[target_index[index]]},
vnfd_updates={'vnf_product_name':
values['result_names'][index]}))
expected_result = [] expected_result = []
for name in values['result_names']: for index in range(len(target_index)):
expected_result += fakes.index_response( _links = fakes.index_response()[0]['_links']
expected_links = (re.sub("vnf_packages/[a-zA-Z0-9-]*",
"vnf_packages/{}".format(ids[target_index[index]]),
str(_links)))
expected_links = json.loads(expected_links.replace("'", '"'))
print("expected_links", expected_links)
expected_result.append(fakes.index_response(
remove_attrs=[ remove_attrs=[
'softwareImages', 'softwareImages',
'checksum', 'checksum',
'userDefinedData', 'userDefinedData',
'additionalArtifacts'], 'additionalArtifacts'],
vnf_package_updates={'vnfProductName': name}) vnf_package_updates={'vnfProductName':
values['result_names'][index],
'id': ids[target_index[index]],
'_links': expected_links})[0])
res_dict = self.controller.index(req) res_dict = self.controller.index(req)
if 'Link' in res_dict.headers:
next_url = re.findall('<(.*)>', res_dict.headers['Link'])[0] expected_result_link = None
query = urllib.parse.urlparse(next_url).query if 'all_records' not in values['params'] and len(target_index) >= 2:
req = fake_request.HTTPRequest.blank('/vnfpkgm/v1/vnf_packages?' + expected_result_link = (
query) '<http://localhost//vnfpkgm/v1/vnf_packages' +
res_dict = self.controller.index(req) '?nextpage_opaque_marker=%s>; rel="next"'
self.assertEqual( % ids[target_index[1]])
jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
default=str)), res_dict.json) self.assertEqual(expected_result, res_dict.json)
if expected_result_link is not None:
self.assertEqual(expected_result_link, res_dict.headers['Link'])
@mock.patch.object(vnf_package.VnfPackage, "get_by_id") @mock.patch.object(vnf_package.VnfPackage, "get_by_id")
@mock.patch.object(VNFPackageRPCAPI, "delete_vnf_package") @mock.patch.object(VNFPackageRPCAPI, "delete_vnf_package")