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:
@@ -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._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(
|
||||||
@@ -555,6 +558,13 @@ 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):
|
||||||
@@ -571,18 +581,54 @@ class VnfLcmController(wsgi.Controller):
|
|||||||
|
|
||||||
@wsgi.response(http_client.OK)
|
@wsgi.response(http_client.OK)
|
||||||
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.BAD_REQUEST))
|
@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):
|
def index(self, request):
|
||||||
|
if 'tacker.context' in request.environ:
|
||||||
context = request.environ['tacker.context']
|
context = request.environ['tacker.context']
|
||||||
context.can(vnf_lcm_policies.VNFLCM % 'index')
|
context.can(vnf_lcm_policies.VNFLCM % 'index')
|
||||||
|
|
||||||
filters = request.GET.get('filter')
|
filters = request.GET.get('filter')
|
||||||
filters = self._view_builder.validate_filter(filters)
|
filters = self._view_builder.validate_filter(filters)
|
||||||
|
|
||||||
|
nextpage = request.GET.get('nextpage_opaque_marker')
|
||||||
|
allrecords = request.GET.get('all_records')
|
||||||
|
|
||||||
|
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(
|
vnf_instances = objects.VnfInstanceList.get_by_filters(
|
||||||
request.context, filters=filters)
|
request.context, filters=filters)
|
||||||
|
|
||||||
return self._view_builder.index(vnf_instances)
|
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",
|
@check_vnf_state(action="delete",
|
||||||
instantiation_state=[fields.VnfInstanceState.NOT_INSTANTIATED],
|
instantiation_state=[fields.VnfInstanceState.NOT_INSTANTIATED],
|
||||||
@@ -1012,13 +1058,16 @@ 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 = ""
|
nextpage_opaque_marker = None
|
||||||
paging = 1
|
paging = 1
|
||||||
filter_string = ""
|
filter_string = ""
|
||||||
|
ignore_nextpages = False
|
||||||
|
subscription_data = []
|
||||||
|
|
||||||
re_url = request.path_url
|
|
||||||
query_params = request.query_string
|
query_params = request.query_string
|
||||||
|
|
||||||
|
allrecords = request.GET.get('all_records')
|
||||||
|
|
||||||
if query_params:
|
if query_params:
|
||||||
query_params = parse.unquote(query_params)
|
query_params = parse.unquote(query_params)
|
||||||
query_param_list = query_params.split('&')
|
query_param_list = query_params.split('&')
|
||||||
@@ -1034,6 +1083,7 @@ class VnfLcmController(wsgi.Controller):
|
|||||||
nextpage_opaque_marker = query_param_key_value[1]
|
nextpage_opaque_marker = query_param_key_value[1]
|
||||||
if query_param_key_value[0] == 'page':
|
if query_param_key_value[0] == 'page':
|
||||||
paging = int(query_param_key_value[1])
|
paging = int(query_param_key_value[1])
|
||||||
|
ignore_nextpages = True
|
||||||
|
|
||||||
if filter_string:
|
if filter_string:
|
||||||
# check enumerations columns
|
# check enumerations columns
|
||||||
@@ -1061,6 +1111,14 @@ class VnfLcmController(wsgi.Controller):
|
|||||||
return self._make_problem_detail(msg, 400,
|
return self._make_problem_detail(msg, 400,
|
||||||
title='Bad Request')
|
title='Bad Request')
|
||||||
|
|
||||||
|
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:
|
||||||
|
subscription_data = self._nextpages_subscriptions.pop(
|
||||||
|
nextpage)['nextpage']
|
||||||
|
else:
|
||||||
try:
|
try:
|
||||||
filter_string_parsed = self._view_builder_subscription. \
|
filter_string_parsed = self._view_builder_subscription. \
|
||||||
validate_filter(filter_string)
|
validate_filter(filter_string)
|
||||||
@@ -1069,7 +1127,7 @@ class VnfLcmController(wsgi.Controller):
|
|||||||
else:
|
else:
|
||||||
start_index = None
|
start_index = None
|
||||||
|
|
||||||
vnf_lcm_subscriptions, last = (
|
vnf_lcm_subscriptions = (
|
||||||
subscription_obj.LccnSubscriptionList.
|
subscription_obj.LccnSubscriptionList.
|
||||||
get_by_filters(request.context,
|
get_by_filters(request.context,
|
||||||
read_deleted='no',
|
read_deleted='no',
|
||||||
@@ -1079,33 +1137,32 @@ class VnfLcmController(wsgi.Controller):
|
|||||||
LOG.debug("vnf_lcm_subscriptions %s" % vnf_lcm_subscriptions)
|
LOG.debug("vnf_lcm_subscriptions %s" % vnf_lcm_subscriptions)
|
||||||
subscription_data = self._view_builder_subscription. \
|
subscription_data = self._view_builder_subscription. \
|
||||||
subscription_list(vnf_lcm_subscriptions)
|
subscription_list(vnf_lcm_subscriptions)
|
||||||
LOG.debug("last %s" % last)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.error(traceback.format_exc())
|
LOG.error(traceback.format_exc())
|
||||||
return self._make_problem_detail(
|
return self._make_problem_detail(
|
||||||
str(e), 500, title='Internal Server Error')
|
str(e), 500, title='Internal Server Error')
|
||||||
|
|
||||||
if subscription_data == 400:
|
|
||||||
msg = _("Number of records exceeds nextpage_opaque_marker")
|
|
||||||
return self._make_problem_detail(msg, 400, title='Bad Request')
|
|
||||||
|
|
||||||
# make response
|
# make response
|
||||||
res = webob.Response(content_type='application/json')
|
res = webob.Response(content_type='application/json')
|
||||||
res.body = jsonutils.dump_as_bytes(subscription_data)
|
|
||||||
res.status_int = 200
|
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)
|
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)
|
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)
|
LOG.debug("subscription_list res %s" % res)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
@@ -1674,6 +1731,7 @@ class VnfLcmController(wsgi.Controller):
|
|||||||
@wsgi.response(http_client.OK)
|
@wsgi.response(http_client.OK)
|
||||||
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.BAD_REQUEST))
|
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.BAD_REQUEST))
|
||||||
def list_lcm_op_occs(self, request):
|
def list_lcm_op_occs(self, request):
|
||||||
|
if 'tacker.context' in request.environ:
|
||||||
context = request.environ['tacker.context']
|
context = request.environ['tacker.context']
|
||||||
context.can(vnf_lcm_policies.VNFLCM % 'list_lcm_op_occs')
|
context.can(vnf_lcm_policies.VNFLCM % 'list_lcm_op_occs')
|
||||||
|
|
||||||
@@ -1685,6 +1743,17 @@ class VnfLcmController(wsgi.Controller):
|
|||||||
if not (all_fields or fields or exclude_fields):
|
if not (all_fields or fields or exclude_fields):
|
||||||
exclude_default = True
|
exclude_default = True
|
||||||
|
|
||||||
|
nextpage = request.GET.get('nextpage_opaque_marker')
|
||||||
|
allrecords = request.GET.get('all_records')
|
||||||
|
|
||||||
|
result = []
|
||||||
|
|
||||||
|
if allrecords != 'yes' and nextpage:
|
||||||
|
self._delete_expired_nextpages(self._nextpages_lcm_op_occs)
|
||||||
|
|
||||||
|
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(
|
self._view_builder_op_occ.validate_attribute_fields(
|
||||||
all_fields=all_fields, fields=fields,
|
all_fields=all_fields, fields=fields,
|
||||||
exclude_fields=exclude_fields,
|
exclude_fields=exclude_fields,
|
||||||
@@ -1693,18 +1762,39 @@ class VnfLcmController(wsgi.Controller):
|
|||||||
filters = self._view_builder_op_occ.validate_filter(filters)
|
filters = self._view_builder_op_occ.validate_filter(filters)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
vnf_lcm_op_occs = \
|
vnf_lcm_op_occs = (
|
||||||
vnf_lcm_op_occs_obj.VnfLcmOpOccList.get_by_filters(
|
vnf_lcm_op_occs_obj.VnfLcmOpOccList.get_by_filters(
|
||||||
request.context, read_deleted='no', filters=filters)
|
request.context, read_deleted='no', filters=filters))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.exception(traceback.format_exc())
|
LOG.exception(traceback.format_exc())
|
||||||
return self._make_problem_detail(
|
return self._make_problem_detail(
|
||||||
str(e), 500, title='Internal Server Error')
|
str(e), 500, title='Internal Server Error')
|
||||||
|
|
||||||
return self._view_builder_op_occ.index(request, vnf_lcm_op_occs,
|
result = self._view_builder_op_occ.index(request, vnf_lcm_op_occs,
|
||||||
all_fields=all_fields, exclude_fields=exclude_fields,
|
all_fields=all_fields, exclude_fields=exclude_fields,
|
||||||
fields=fields, exclude_default=exclude_default)
|
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(
|
def _make_problem_detail(
|
||||||
self,
|
self,
|
||||||
detail,
|
detail,
|
||||||
|
|||||||
@@ -47,7 +47,19 @@ OPTS = [
|
|||||||
cfg.BoolOpt(
|
cfg.BoolOpt(
|
||||||
'verify_notification_ssl',
|
'verify_notification_ssl',
|
||||||
default=True,
|
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',
|
vnf_lcm_group = cfg.OptGroup('vnf_lcm',
|
||||||
title='vnf_lcm options',
|
title='vnf_lcm options',
|
||||||
|
|||||||
@@ -457,20 +457,14 @@ def _make_subscription_list(context, subscription_list, db_subscription_list,
|
|||||||
subscription_cls = LccnSubscription
|
subscription_cls = LccnSubscription
|
||||||
|
|
||||||
subscription_list.objects = []
|
subscription_list.objects = []
|
||||||
cnt = 0
|
|
||||||
last_flg = True
|
|
||||||
for db_subscription in db_subscription_list:
|
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(
|
subscription_obj = subscription_cls._from_db_object(
|
||||||
context, subscription_cls(context), db_subscription,
|
context, subscription_cls(context), db_subscription,
|
||||||
expected_attrs=expected_attrs)
|
expected_attrs=expected_attrs)
|
||||||
subscription_list.objects.append(subscription_obj)
|
subscription_list.objects.append(subscription_obj)
|
||||||
|
|
||||||
subscription_list.obj_reset_changes()
|
subscription_list.obj_reset_changes()
|
||||||
return subscription_list, last_flg
|
return subscription_list
|
||||||
|
|
||||||
|
|
||||||
@base.TackerObjectRegistry.register
|
@base.TackerObjectRegistry.register
|
||||||
|
|||||||
@@ -1682,8 +1682,12 @@ def fake_vnf_lcm_op_occs():
|
|||||||
return 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()
|
vnf_lcm_op_occs = fake_vnf_lcm_op_occs()
|
||||||
|
|
||||||
|
if updates:
|
||||||
|
vnf_lcm_op_occs.update(**updates)
|
||||||
|
|
||||||
obj = objects.VnfLcmOpOcc(**vnf_lcm_op_occs)
|
obj = objects.VnfLcmOpOcc(**vnf_lcm_op_occs)
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import ddt
|
|||||||
from http import client as http_client
|
from http import client as http_client
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
import urllib
|
import urllib
|
||||||
import webob
|
import webob
|
||||||
@@ -1697,14 +1698,14 @@ class TestController(base.TestCase):
|
|||||||
expected_result = [fakes.fake_vnf_instance_response(),
|
expected_result = [fakes.fake_vnf_instance_response(),
|
||||||
fakes.fake_vnf_instance_response(
|
fakes.fake_vnf_instance_response(
|
||||||
fields.VnfInstanceState.INSTANTIATED)]
|
fields.VnfInstanceState.INSTANTIATED)]
|
||||||
self.assertEqual(expected_result, resp)
|
self.assertEqual(expected_result, resp.json)
|
||||||
|
|
||||||
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
|
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
|
||||||
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 = []
|
||||||
resp = self.controller.index(req)
|
resp = self.controller.index(req)
|
||||||
self.assertEqual([], resp)
|
self.assertEqual([], resp.json)
|
||||||
|
|
||||||
@mock.patch.object(TackerManager, 'get_service_plugins',
|
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||||
return_value={'VNFM':
|
return_value={'VNFM':
|
||||||
@@ -1859,7 +1860,7 @@ class TestController(base.TestCase):
|
|||||||
expected_result = [fakes.fake_vnf_instance_response(),
|
expected_result = [fakes.fake_vnf_instance_response(),
|
||||||
fakes.fake_vnf_instance_response(
|
fakes.fake_vnf_instance_response(
|
||||||
fields.VnfInstanceState.INSTANTIATED)]
|
fields.VnfInstanceState.INSTANTIATED)]
|
||||||
self.assertEqual(expected_result, res_dict)
|
self.assertEqual(expected_result, res_dict.json)
|
||||||
|
|
||||||
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
|
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
|
||||||
def test_index_filter_combination(self, mock_vnf_list):
|
def test_index_filter_combination(self, mock_vnf_list):
|
||||||
@@ -1882,7 +1883,7 @@ class TestController(base.TestCase):
|
|||||||
expected_result = [fakes.fake_vnf_instance_response(),
|
expected_result = [fakes.fake_vnf_instance_response(),
|
||||||
fakes.fake_vnf_instance_response(
|
fakes.fake_vnf_instance_response(
|
||||||
fields.VnfInstanceState.INSTANTIATED)]
|
fields.VnfInstanceState.INSTANTIATED)]
|
||||||
self.assertEqual(expected_result, res_dict)
|
self.assertEqual(expected_result, res_dict.json)
|
||||||
|
|
||||||
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
|
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
|
||||||
@ddt.data(
|
@ddt.data(
|
||||||
@@ -1935,7 +1936,7 @@ class TestController(base.TestCase):
|
|||||||
expected_result = [fakes.fake_vnf_instance_response(),
|
expected_result = [fakes.fake_vnf_instance_response(),
|
||||||
fakes.fake_vnf_instance_response(
|
fakes.fake_vnf_instance_response(
|
||||||
fields.VnfInstanceState.INSTANTIATED)]
|
fields.VnfInstanceState.INSTANTIATED)]
|
||||||
self.assertEqual(expected_result, res_dict)
|
self.assertEqual(expected_result, res_dict.json)
|
||||||
|
|
||||||
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
|
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
|
||||||
@ddt.data(
|
@ddt.data(
|
||||||
@@ -2017,6 +2018,46 @@ 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")
|
||||||
|
@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")
|
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
|
||||||
@ddt.data(
|
@ddt.data(
|
||||||
{'attribute_not_exist': 'some_value'},
|
{'attribute_not_exist': 'some_value'},
|
||||||
@@ -2024,7 +2065,6 @@ class TestController(base.TestCase):
|
|||||||
{'fields': {}},
|
{'fields': {}},
|
||||||
{'exclude_fields': {}},
|
{'exclude_fields': {}},
|
||||||
{'exclude_default': {}},
|
{'exclude_default': {}},
|
||||||
{'nextpage_opaque_marker': 1},
|
|
||||||
{'attribute_not_exist': 'some_value', 'filter': {}},
|
{'attribute_not_exist': 'some_value', 'filter': {}},
|
||||||
{'attribute_not_exist': 'some_value', 'fields': {}}
|
{'attribute_not_exist': 'some_value', 'fields': {}}
|
||||||
)
|
)
|
||||||
@@ -2044,7 +2084,6 @@ class TestController(base.TestCase):
|
|||||||
{'fields': {}},
|
{'fields': {}},
|
||||||
{'exclude_fields': {}},
|
{'exclude_fields': {}},
|
||||||
{'exclude_default': {}},
|
{'exclude_default': {}},
|
||||||
{'nextpage_opaque_marker': 1},
|
|
||||||
{'attribute_not_exist': 'some_value', 'filter': {}},
|
{'attribute_not_exist': 'some_value', 'filter': {}},
|
||||||
{'attribute_not_exist': 'some_value', 'fields': {}}
|
{'attribute_not_exist': 'some_value', 'fields': {}}
|
||||||
)
|
)
|
||||||
@@ -3760,9 +3799,61 @@ class TestController(base.TestCase):
|
|||||||
expected_result = fakes.index_response(
|
expected_result = fakes.index_response(
|
||||||
remove_attrs=complex_attributes)
|
remove_attrs=complex_attributes)
|
||||||
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
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")
|
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
|
||||||
@ddt.data(
|
@ddt.data(
|
||||||
@@ -3797,7 +3888,9 @@ class TestController(base.TestCase):
|
|||||||
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
||||||
res_dict = self.controller.list_lcm_op_occs(req)
|
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")
|
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
|
||||||
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):
|
||||||
@@ -3822,7 +3915,9 @@ class TestController(base.TestCase):
|
|||||||
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
||||||
res_dict = self.controller.list_lcm_op_occs(req)
|
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")
|
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
|
||||||
@ddt.data(
|
@ddt.data(
|
||||||
@@ -3850,7 +3945,9 @@ class TestController(base.TestCase):
|
|||||||
expected_result = fakes.index_response(remove_attrs=remove_attributes)
|
expected_result = fakes.index_response(remove_attrs=remove_attributes)
|
||||||
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
||||||
res_dict = self.controller.list_lcm_op_occs(req)
|
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")
|
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
|
||||||
@ddt.data(
|
@ddt.data(
|
||||||
@@ -3871,7 +3968,9 @@ class TestController(base.TestCase):
|
|||||||
expected_result = fakes.index_response(remove_attrs=remove_attributes)
|
expected_result = fakes.index_response(remove_attrs=remove_attributes)
|
||||||
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
mock_op_occ_list.return_value = vnf_lcm_op_occ
|
||||||
res_dict = self.controller.list_lcm_op_occs(req)
|
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")
|
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
|
||||||
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):
|
||||||
@@ -4326,6 +4425,67 @@ 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',
|
||||||
|
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',
|
@mock.patch.object(TackerManager, 'get_service_plugins',
|
||||||
return_value={'VNFM':
|
return_value={'VNFM':
|
||||||
test_nfvo_plugin.FakeVNFMPlugin()})
|
test_nfvo_plugin.FakeVNFMPlugin()})
|
||||||
|
|||||||
Reference in New Issue
Block a user