Support handling large query results by ETSI NFV
This patch provides supporting handling large query results in a response of target APIs. The features to be added are like the following. - Paging query results according to ETSI NFV SOL013 - Fetching entire records forcibly Implements: blueprint paging-query-result Change-Id: I587fd9a998032cc1d23b72d755c60fb7a859c7ee
This commit is contained in:
parent
17b03eb78f
commit
81dca79807
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Support handling large query results according to ETSI NFV SOL013.
|
||||
This feature provides paged response regarding a query request of
|
||||
target APIs. In addition to that, fetching entire records at once
|
||||
becomes available.
|
||||
issues:
|
||||
- Regarding handling large query results according to ETSI NFV SOL013,
|
||||
"vnfpkgm/v1/vnf_packages" API does not have the paging feature yet
|
||||
because of longer time to be implemented than the other APIs. Since
|
||||
there is less possibility to be paged in actual use case, the
|
||||
implementation will be done in the next release.
|
|
@ -192,6 +192,9 @@ class VnfLcmController(wsgi.Controller):
|
|||
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()
|
||||
self._nextpages_vnf_instances = {}
|
||||
self._nextpages_lcm_op_occs = {}
|
||||
self._nextpages_subscriptions = {}
|
||||
|
||||
def _get_vnf_instance_href(self, vnf_instance):
|
||||
return '{}vnflcm/v1/vnf_instances/{}'.format(
|
||||
|
@ -555,6 +558,13 @@ class VnfLcmController(wsgi.Controller):
|
|||
|
||||
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.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND))
|
||||
def show(self, request, id):
|
||||
|
@ -571,18 +581,54 @@ class VnfLcmController(wsgi.Controller):
|
|||
|
||||
@wsgi.response(http_client.OK)
|
||||
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.BAD_REQUEST))
|
||||
@api_common.validate_supported_params({'filter'})
|
||||
@api_common.validate_supported_params({'filter', 'nextpage_opaque_marker',
|
||||
'all_records'})
|
||||
def index(self, request):
|
||||
context = request.environ['tacker.context']
|
||||
context.can(vnf_lcm_policies.VNFLCM % 'index')
|
||||
if 'tacker.context' in request.environ:
|
||||
context = request.environ['tacker.context']
|
||||
context.can(vnf_lcm_policies.VNFLCM % 'index')
|
||||
|
||||
filters = request.GET.get('filter')
|
||||
filters = self._view_builder.validate_filter(filters)
|
||||
|
||||
vnf_instances = objects.VnfInstanceList.get_by_filters(
|
||||
request.context, filters=filters)
|
||||
nextpage = request.GET.get('nextpage_opaque_marker')
|
||||
allrecords = request.GET.get('all_records')
|
||||
|
||||
return self._view_builder.index(vnf_instances)
|
||||
result = []
|
||||
|
||||
if allrecords != 'yes' and nextpage:
|
||||
self._delete_expired_nextpages(self._nextpages_vnf_instances)
|
||||
|
||||
if nextpage in self._nextpages_vnf_instances:
|
||||
result = self._nextpages_vnf_instances.pop(
|
||||
nextpage)['nextpage']
|
||||
else:
|
||||
vnf_instances = objects.VnfInstanceList.get_by_filters(
|
||||
request.context, filters=filters)
|
||||
|
||||
result = self._view_builder.index(vnf_instances)
|
||||
|
||||
res = webob.Response(content_type='application/json')
|
||||
res.status_int = 200
|
||||
|
||||
if (allrecords != 'yes' and
|
||||
len(result) > CONF.vnf_lcm.vnf_instance_num):
|
||||
nextpageid = uuidutils.generate_uuid()
|
||||
links = ('Link', '<%s?nextpage_opaque_marker=%s>; rel="next"' % (
|
||||
request.path_url, nextpageid))
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
@check_vnf_state(action="delete",
|
||||
instantiation_state=[fields.VnfInstanceState.NOT_INSTANTIATED],
|
||||
|
@ -1012,13 +1058,16 @@ class VnfLcmController(wsgi.Controller):
|
|||
|
||||
@wsgi.response(http_client.OK)
|
||||
def subscription_list(self, request):
|
||||
nextpage_opaque_marker = ""
|
||||
nextpage_opaque_marker = None
|
||||
paging = 1
|
||||
filter_string = ""
|
||||
ignore_nextpages = False
|
||||
subscription_data = []
|
||||
|
||||
re_url = request.path_url
|
||||
query_params = request.query_string
|
||||
|
||||
allrecords = request.GET.get('all_records')
|
||||
|
||||
if query_params:
|
||||
query_params = parse.unquote(query_params)
|
||||
query_param_list = query_params.split('&')
|
||||
|
@ -1034,6 +1083,7 @@ class VnfLcmController(wsgi.Controller):
|
|||
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:
|
||||
# check enumerations columns
|
||||
|
@ -1061,51 +1111,58 @@ class VnfLcmController(wsgi.Controller):
|
|||
return self._make_problem_detail(msg, 400,
|
||||
title='Bad Request')
|
||||
|
||||
try:
|
||||
filter_string_parsed = self._view_builder_subscription. \
|
||||
validate_filter(filter_string)
|
||||
if nextpage_opaque_marker:
|
||||
start_index = paging - 1
|
||||
else:
|
||||
start_index = None
|
||||
nextpage = nextpage_opaque_marker
|
||||
if allrecords != 'yes' and not ignore_nextpages and nextpage:
|
||||
self._delete_expired_nextpages(self._nextpages_subscriptions)
|
||||
|
||||
vnf_lcm_subscriptions, last = (
|
||||
subscription_obj.LccnSubscriptionList.
|
||||
get_by_filters(request.context,
|
||||
read_deleted='no',
|
||||
filters=filter_string_parsed,
|
||||
nextpage_opaque_marker=start_index))
|
||||
if nextpage in self._nextpages_subscriptions:
|
||||
subscription_data = self._nextpages_subscriptions.pop(
|
||||
nextpage)['nextpage']
|
||||
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
|
||||
|
||||
LOG.debug("vnf_lcm_subscriptions %s" % vnf_lcm_subscriptions)
|
||||
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())
|
||||
return self._make_problem_detail(
|
||||
str(e), 500, title='Internal Server Error')
|
||||
vnf_lcm_subscriptions = (
|
||||
subscription_obj.LccnSubscriptionList.
|
||||
get_by_filters(request.context,
|
||||
read_deleted='no',
|
||||
filters=filter_string_parsed,
|
||||
nextpage_opaque_marker=start_index))
|
||||
|
||||
if subscription_data == 400:
|
||||
msg = _("Number of records exceeds nextpage_opaque_marker")
|
||||
return self._make_problem_detail(msg, 400, title='Bad Request')
|
||||
LOG.debug("vnf_lcm_subscriptions %s" % vnf_lcm_subscriptions)
|
||||
subscription_data = self._view_builder_subscription. \
|
||||
subscription_list(vnf_lcm_subscriptions)
|
||||
except Exception as e:
|
||||
LOG.error(traceback.format_exc())
|
||||
return self._make_problem_detail(
|
||||
str(e), 500, title='Internal Server Error')
|
||||
|
||||
# make response
|
||||
res = webob.Response(content_type='application/json')
|
||||
res.body = jsonutils.dump_as_bytes(subscription_data)
|
||||
res.status_int = 200
|
||||
if nextpage_opaque_marker:
|
||||
if not last:
|
||||
ln = '<%s?page=%s>;rel="next"; title*="next chapter"' % (
|
||||
re_url, paging + 1)
|
||||
# Regarding the setting in http header related to
|
||||
# nextpage control, RFC8288 and NFV-SOL013
|
||||
# specifications have not been confirmed.
|
||||
# Therefore, it is implemented by setting "page",
|
||||
# which is a general control method of WebAPI,
|
||||
# as "URI-Reference" of Link header.
|
||||
|
||||
links = ('Link', ln)
|
||||
res.headerlist.append(links)
|
||||
if (allrecords != 'yes' and not ignore_nextpages and
|
||||
len(subscription_data) > CONF.vnf_lcm.subscription_num):
|
||||
nextpageid = uuidutils.generate_uuid()
|
||||
links = ('Link', '<%s?nextpage_opaque_marker=%s>; rel="next"' % (
|
||||
request.path_url, nextpageid))
|
||||
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)
|
||||
|
||||
LOG.debug("subscription_list res %s" % res)
|
||||
|
||||
return res
|
||||
|
@ -1674,8 +1731,9 @@ class VnfLcmController(wsgi.Controller):
|
|||
@wsgi.response(http_client.OK)
|
||||
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.BAD_REQUEST))
|
||||
def list_lcm_op_occs(self, request):
|
||||
context = request.environ['tacker.context']
|
||||
context.can(vnf_lcm_policies.VNFLCM % 'list_lcm_op_occs')
|
||||
if 'tacker.context' in request.environ:
|
||||
context = request.environ['tacker.context']
|
||||
context.can(vnf_lcm_policies.VNFLCM % 'list_lcm_op_occs')
|
||||
|
||||
all_fields = request.GET.get('all_fields')
|
||||
exclude_default = request.GET.get('exclude_default')
|
||||
|
@ -1685,25 +1743,57 @@ class VnfLcmController(wsgi.Controller):
|
|||
if not (all_fields or fields or exclude_fields):
|
||||
exclude_default = True
|
||||
|
||||
self._view_builder_op_occ.validate_attribute_fields(
|
||||
all_fields=all_fields, fields=fields,
|
||||
exclude_fields=exclude_fields,
|
||||
exclude_default=exclude_default)
|
||||
nextpage = request.GET.get('nextpage_opaque_marker')
|
||||
allrecords = request.GET.get('all_records')
|
||||
|
||||
filters = self._view_builder_op_occ.validate_filter(filters)
|
||||
result = []
|
||||
|
||||
try:
|
||||
vnf_lcm_op_occs = \
|
||||
vnf_lcm_op_occs_obj.VnfLcmOpOccList.get_by_filters(
|
||||
request.context, read_deleted='no', filters=filters)
|
||||
except Exception as e:
|
||||
LOG.exception(traceback.format_exc())
|
||||
return self._make_problem_detail(
|
||||
str(e), 500, title='Internal Server Error')
|
||||
if allrecords != 'yes' and nextpage:
|
||||
self._delete_expired_nextpages(self._nextpages_lcm_op_occs)
|
||||
|
||||
return self._view_builder_op_occ.index(request, vnf_lcm_op_occs,
|
||||
all_fields=all_fields, exclude_fields=exclude_fields,
|
||||
fields=fields, exclude_default=exclude_default)
|
||||
if nextpage in self._nextpages_lcm_op_occs:
|
||||
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)
|
||||
|
||||
try:
|
||||
vnf_lcm_op_occs = (
|
||||
vnf_lcm_op_occs_obj.VnfLcmOpOccList.get_by_filters(
|
||||
request.context, read_deleted='no', 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_op_occ.index(request, vnf_lcm_op_occs,
|
||||
all_fields=all_fields, exclude_fields=exclude_fields,
|
||||
fields=fields, exclude_default=exclude_default)
|
||||
|
||||
res = webob.Response(content_type='application/json')
|
||||
res.status_int = 200
|
||||
|
||||
if allrecords != 'yes' and len(result) > CONF.vnf_lcm.lcm_op_occ_num:
|
||||
nextpageid = uuidutils.generate_uuid()
|
||||
links = ('Link', '<%s?nextpage_opaque_marker=%s>; rel="next"' % (
|
||||
request.path_url, nextpageid))
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
def _make_problem_detail(
|
||||
self,
|
||||
|
|
|
@ -47,7 +47,19 @@ OPTS = [
|
|||
cfg.BoolOpt(
|
||||
'verify_notification_ssl',
|
||||
default=True,
|
||||
help="Verify the certificate to send notification by ssl")]
|
||||
help="Verify the certificate to send notification by ssl"),
|
||||
cfg.IntOpt(
|
||||
'lcm_op_occ_num',
|
||||
default=100,
|
||||
help="Number of lcm_op_occs contained in 1 page"),
|
||||
cfg.IntOpt(
|
||||
'vnf_instance_num',
|
||||
default=100,
|
||||
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',
|
||||
title='vnf_lcm options',
|
||||
|
|
|
@ -457,20 +457,14 @@ def _make_subscription_list(context, subscription_list, db_subscription_list,
|
|||
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
|
||||
return subscription_list
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
|
|
|
@ -1682,8 +1682,12 @@ def fake_vnf_lcm_op_occs():
|
|||
return vnf_lcm_op_occs
|
||||
|
||||
|
||||
def return_vnf_lcm_opoccs_obj():
|
||||
def return_vnf_lcm_opoccs_obj(**updates):
|
||||
vnf_lcm_op_occs = fake_vnf_lcm_op_occs()
|
||||
|
||||
if updates:
|
||||
vnf_lcm_op_occs.update(**updates)
|
||||
|
||||
obj = objects.VnfLcmOpOcc(**vnf_lcm_op_occs)
|
||||
|
||||
return obj
|
||||
|
|
|
@ -19,6 +19,7 @@ import ddt
|
|||
from http import client as http_client
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from unittest import mock
|
||||
import urllib
|
||||
import webob
|
||||
|
@ -1697,14 +1698,14 @@ class TestController(base.TestCase):
|
|||
expected_result = [fakes.fake_vnf_instance_response(),
|
||||
fakes.fake_vnf_instance_response(
|
||||
fields.VnfInstanceState.INSTANTIATED)]
|
||||
self.assertEqual(expected_result, resp)
|
||||
self.assertEqual(expected_result, resp.json)
|
||||
|
||||
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
|
||||
def test_index_empty_response(self, mock_vnf_list):
|
||||
req = fake_request.HTTPRequest.blank('/vnf_instances')
|
||||
mock_vnf_list.return_value = []
|
||||
resp = self.controller.index(req)
|
||||
self.assertEqual([], resp)
|
||||
self.assertEqual([], resp.json)
|
||||
|
||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||
return_value={'VNFM':
|
||||
|
@ -1859,7 +1860,7 @@ class TestController(base.TestCase):
|
|||
expected_result = [fakes.fake_vnf_instance_response(),
|
||||
fakes.fake_vnf_instance_response(
|
||||
fields.VnfInstanceState.INSTANTIATED)]
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(expected_result, res_dict.json)
|
||||
|
||||
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
|
||||
def test_index_filter_combination(self, mock_vnf_list):
|
||||
|
@ -1882,7 +1883,7 @@ class TestController(base.TestCase):
|
|||
expected_result = [fakes.fake_vnf_instance_response(),
|
||||
fakes.fake_vnf_instance_response(
|
||||
fields.VnfInstanceState.INSTANTIATED)]
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(expected_result, res_dict.json)
|
||||
|
||||
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
|
||||
@ddt.data(
|
||||
|
@ -1935,7 +1936,7 @@ class TestController(base.TestCase):
|
|||
expected_result = [fakes.fake_vnf_instance_response(),
|
||||
fakes.fake_vnf_instance_response(
|
||||
fields.VnfInstanceState.INSTANTIATED)]
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(expected_result, res_dict.json)
|
||||
|
||||
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
|
||||
@ddt.data(
|
||||
|
@ -2017,6 +2018,46 @@ class TestController(base.TestCase):
|
|||
self.assertRaises(exceptions.ValidationError,
|
||||
self.controller.index, req)
|
||||
|
||||
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
|
||||
@ddt.data(
|
||||
{'params': {'all_records': 'yes'},
|
||||
'result_names': ['sample1', 'sample2', 'sample3', 'sample4']},
|
||||
{'params': {'all_records': 'yes', 'nextpage_opaque_marker': 'abc'},
|
||||
'result_names': ['sample1', 'sample2', 'sample3', 'sample4']},
|
||||
{'params': {'nextpage_opaque_marker': 'abc'},
|
||||
'result_names': []},
|
||||
{'params': {},
|
||||
'result_names': ['sample2']}
|
||||
)
|
||||
def test_index_paging(self, values, mock_vnf_list):
|
||||
cfg.CONF.set_override('vnf_instance_num', 1, group='vnf_lcm')
|
||||
query = urllib.parse.urlencode(values['params'])
|
||||
req = fake_request.HTTPRequest.blank(
|
||||
'/vnflcm/v1/vnf_instances?' + query)
|
||||
|
||||
mock_vnf_list.return_value = [
|
||||
fakes.return_vnf_instance(**{'vnf_instance_name': 'sample1'}),
|
||||
fakes.return_vnf_instance(**{'vnf_instance_name': 'sample2'}),
|
||||
fakes.return_vnf_instance(**{'vnf_instance_name': 'sample3'}),
|
||||
fakes.return_vnf_instance(**{'vnf_instance_name': 'sample4'})
|
||||
]
|
||||
|
||||
expected_result = []
|
||||
for name in values['result_names']:
|
||||
expected_result.append(fakes.fake_vnf_instance_response(
|
||||
**{'vnfInstanceName': name}))
|
||||
|
||||
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)
|
||||
|
||||
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
|
||||
@ddt.data(
|
||||
{'attribute_not_exist': 'some_value'},
|
||||
|
@ -2024,7 +2065,6 @@ class TestController(base.TestCase):
|
|||
{'fields': {}},
|
||||
{'exclude_fields': {}},
|
||||
{'exclude_default': {}},
|
||||
{'nextpage_opaque_marker': 1},
|
||||
{'attribute_not_exist': 'some_value', 'filter': {}},
|
||||
{'attribute_not_exist': 'some_value', 'fields': {}}
|
||||
)
|
||||
|
@ -2044,7 +2084,6 @@ class TestController(base.TestCase):
|
|||
{'fields': {}},
|
||||
{'exclude_fields': {}},
|
||||
{'exclude_default': {}},
|
||||
{'nextpage_opaque_marker': 1},
|
||||
{'attribute_not_exist': 'some_value', 'filter': {}},
|
||||
{'attribute_not_exist': 'some_value', 'fields': {}}
|
||||
)
|
||||
|
@ -3760,9 +3799,61 @@ class TestController(base.TestCase):
|
|||
expected_result = fakes.index_response(
|
||||
remove_attrs=complex_attributes)
|
||||
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
||||
res_dict = self.controller.list_lcm_op_occs(req)
|
||||
resp = self.controller.list_lcm_op_occs(req)
|
||||
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(
|
||||
jsonutils.loads(jsonutils.dump_as_bytes(expected_result)),
|
||||
resp.json)
|
||||
|
||||
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
|
||||
@ddt.data(
|
||||
{'params': {'all_records': 'yes'},
|
||||
'result_names': ['INSTANTIATE', 'SCALE', 'HEAL', 'TERMINATE']},
|
||||
{'params': {'all_records': 'yes', 'nextpage_opaque_marker': 'abc'},
|
||||
'result_names': ['INSTANTIATE', 'SCALE', 'HEAL', 'TERMINATE']},
|
||||
{'params': {'nextpage_opaque_marker': 'abc'},
|
||||
'result_names': []},
|
||||
{'params': {},
|
||||
'result_names': ['SCALE']}
|
||||
)
|
||||
def test_op_occ_list_paging(self, values, mock_op_occ_list):
|
||||
cfg.CONF.set_override('lcm_op_occ_num', 1, group='vnf_lcm')
|
||||
query = urllib.parse.urlencode(values['params'])
|
||||
req = fake_request.HTTPRequest.blank(
|
||||
'/vnflcm/v1/vnf_lcm_op_occs?' + query)
|
||||
|
||||
complex_attributes = [
|
||||
'error',
|
||||
'resourceChanges',
|
||||
'operationParams',
|
||||
'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 = []
|
||||
for name in values['result_names']:
|
||||
expected_result += fakes.index_response(
|
||||
remove_attrs=complex_attributes,
|
||||
vnf_lcm_op_occs_updates={'operation': name})
|
||||
|
||||
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
||||
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(
|
||||
jsonutils.loads(jsonutils.dump_as_bytes(expected_result)),
|
||||
resp.json)
|
||||
|
||||
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
|
||||
@ddt.data(
|
||||
|
@ -3797,7 +3888,9 @@ class TestController(base.TestCase):
|
|||
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
||||
res_dict = self.controller.list_lcm_op_occs(req)
|
||||
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(
|
||||
jsonutils.loads(jsonutils.dump_as_bytes(expected_result)),
|
||||
res_dict.json)
|
||||
|
||||
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
|
||||
def test_op_occ_filter_attributes_invalid_filter(self, mock_op_occ_list):
|
||||
|
@ -3822,7 +3915,9 @@ class TestController(base.TestCase):
|
|||
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
||||
res_dict = self.controller.list_lcm_op_occs(req)
|
||||
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(
|
||||
jsonutils.loads(jsonutils.dump_as_bytes(expected_result)),
|
||||
res_dict.json)
|
||||
|
||||
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
|
||||
@ddt.data(
|
||||
|
@ -3850,7 +3945,9 @@ class TestController(base.TestCase):
|
|||
expected_result = fakes.index_response(remove_attrs=remove_attributes)
|
||||
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
||||
res_dict = self.controller.list_lcm_op_occs(req)
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(
|
||||
jsonutils.loads(jsonutils.dump_as_bytes(expected_result)),
|
||||
res_dict.json)
|
||||
|
||||
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
|
||||
@ddt.data(
|
||||
|
@ -3871,7 +3968,9 @@ class TestController(base.TestCase):
|
|||
expected_result = fakes.index_response(remove_attrs=remove_attributes)
|
||||
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
||||
res_dict = self.controller.list_lcm_op_occs(req)
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(
|
||||
jsonutils.loads(jsonutils.dump_as_bytes(expected_result)),
|
||||
res_dict.json)
|
||||
|
||||
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
|
||||
def test_op_occ_attribute_selector_fields_error(self, mock_op_occ_list):
|
||||
|
@ -4326,6 +4425,67 @@ class TestController(base.TestCase):
|
|||
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(
|
||||
{'params': {'all_records': 'yes'},
|
||||
'result_names': ['subscription_id_1', 'subscription_id_2',
|
||||
'subscription_id_3', 'subscription_id_4']},
|
||||
{'params': {'all_records': 'yes', 'nextpage_opaque_marker': 'abc'},
|
||||
'result_names': ['subscription_id_1', 'subscription_id_2',
|
||||
'subscription_id_3', 'subscription_id_4']},
|
||||
{'params': {'nextpage_opaque_marker': 'abc'},
|
||||
'result_names': []},
|
||||
{'params': {},
|
||||
'result_names': ['subscription_id_2']}
|
||||
)
|
||||
def test_subscription_list_paging(self,
|
||||
values,
|
||||
mock_subscription_list,
|
||||
mock_subscription_filter,
|
||||
mock_subscription_view,
|
||||
mock_get_service_plugins):
|
||||
mock_subscription_filter.return_value = None
|
||||
last = True
|
||||
cfg.CONF.set_override('subscription_num', 1, group='vnf_lcm')
|
||||
query = urllib.parse.urlencode(values['params'])
|
||||
req = fake_request.HTTPRequest.blank('/subscriptions?' + query)
|
||||
req.method = 'GET'
|
||||
subscription_list = [
|
||||
fakes.return_subscription_object(
|
||||
**{'id': uuidsentinel.subscription_id_1}),
|
||||
fakes.return_subscription_object(
|
||||
**{'id': uuidsentinel.subscription_id_2}),
|
||||
fakes.return_subscription_object(
|
||||
**{'id': uuidsentinel.subscription_id_3}),
|
||||
fakes.return_subscription_object(
|
||||
**{'id': uuidsentinel.subscription_id_4})
|
||||
]
|
||||
mock_subscription_list.return_value = [subscription_list, last]
|
||||
mock_subscription_view.return_value = subscription_list
|
||||
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 = []
|
||||
for name in values['result_names']:
|
||||
expected_result.append(fakes.return_subscription_object(
|
||||
**{'id': eval('uuidsentinel.' + name)}))
|
||||
|
||||
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()})
|
||||
|
|
Loading…
Reference in New Issue