Merge "Support handling large queries for vnf packages"
This commit is contained in:
commit
8bddc176e9
|
@ -25,8 +25,10 @@ from zipfile import ZipFile
|
|||
from glance_store import exceptions as store_exceptions
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import encodeutils
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import timeutils
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from tacker._i18n import _
|
||||
|
@ -57,6 +59,7 @@ class VnfPkgmController(wsgi.Controller):
|
|||
super(VnfPkgmController, self).__init__()
|
||||
self.rpc_api = vnf_pkgm_rpc.VNFPackageRPCAPI()
|
||||
glance_store.initialize_glance_store()
|
||||
self._nextpages = {}
|
||||
|
||||
def _get_vnf_package(self, id, request):
|
||||
# check if id is of type uuid format
|
||||
|
@ -72,6 +75,13 @@ class VnfPkgmController(wsgi.Controller):
|
|||
raise webob.exc.HTTPNotFound(explanation=msg)
|
||||
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.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN))
|
||||
@validation.schema(vnf_packages.create)
|
||||
|
@ -120,8 +130,9 @@ class VnfPkgmController(wsgi.Controller):
|
|||
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN))
|
||||
@validation.query_schema(vnf_packages.query_params_v1)
|
||||
def index(self, request):
|
||||
context = request.environ['tacker.context']
|
||||
context.can(vnf_package_policies.VNFPKGM % 'index')
|
||||
if 'tacker.context' in request.environ:
|
||||
context = request.environ['tacker.context']
|
||||
context.can(vnf_package_policies.VNFPKGM % 'index')
|
||||
|
||||
search_opts = {}
|
||||
search_opts.update(request.GET)
|
||||
|
@ -139,6 +150,8 @@ class VnfPkgmController(wsgi.Controller):
|
|||
fields = request.GET.get('fields')
|
||||
exclude_fields = request.GET.get('exclude_fields')
|
||||
filters = request.GET.get('filter')
|
||||
nextpage = request.GET.get('nextpage_opaque_marker')
|
||||
allrecords = request.GET.get('all_records')
|
||||
if not (all_fields or fields or exclude_fields):
|
||||
exclude_default = True
|
||||
|
||||
|
@ -148,14 +161,45 @@ class VnfPkgmController(wsgi.Controller):
|
|||
|
||||
filters = self._view_builder.validate_filter(filters)
|
||||
|
||||
vnf_packages = vnf_package_obj.VnfPackagesList.get_by_filters(
|
||||
request.context, read_deleted='no', filters=filters)
|
||||
results = []
|
||||
|
||||
return self._view_builder.index(vnf_packages,
|
||||
all_fields=all_fields,
|
||||
exclude_fields=exclude_fields,
|
||||
fields=fields,
|
||||
exclude_default=exclude_default)
|
||||
if allrecords != 'yes' and nextpage:
|
||||
self._delete_expired_nextpages(self._nextpages)
|
||||
|
||||
if nextpage in self._nextpages:
|
||||
results = self._nextpages.pop(
|
||||
nextpage)['nextpage']
|
||||
else:
|
||||
vnf_packages = vnf_package_obj.VnfPackagesList.get_by_filters(
|
||||
request.context, read_deleted='no', filters=filters)
|
||||
|
||||
results = self._view_builder.index(vnf_packages,
|
||||
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(results) > CONF.vnf_package.vnf_package_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(
|
||||
results[: CONF.vnf_package.vnf_package_num], default=str)
|
||||
|
||||
self._delete_expired_nextpages(self._nextpages)
|
||||
|
||||
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
|
||||
|
||||
@wsgi.response(http_client.NO_CONTENT)
|
||||
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND,
|
||||
|
|
|
@ -76,6 +76,12 @@ Related options:
|
|||
'provider', 'product_name', 'software_version',
|
||||
'vnfm_info', 'flavour_id', 'flavour_description'],
|
||||
help=_("List of del inputs from lower-vnfd")),
|
||||
cfg.IntOpt('vnf_package_num',
|
||||
default=100,
|
||||
help=_("Number of vnf_packages contained in 1 page")),
|
||||
cfg.IntOpt('nextpage_expiration_time',
|
||||
default=3600,
|
||||
help=_("Expiration time (sec) for paging")),
|
||||
|
||||
]
|
||||
|
||||
|
|
|
@ -18,10 +18,12 @@ import ddt
|
|||
from http import client as http_client
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from unittest import mock
|
||||
import urllib
|
||||
from webob import exc
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from tacker.api.vnfpkgm.v1 import controller
|
||||
|
@ -127,7 +129,9 @@ class TestController(base.TestCase):
|
|||
'checksum',
|
||||
'userDefinedData',
|
||||
'additionalArtifacts'])
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(
|
||||
jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
|
||||
default=str)), res_dict.json)
|
||||
|
||||
@mock.patch.object(VnfPackagesList, "get_by_filters")
|
||||
def test_index_attribute_selector_all_fields(self, mock_vnf_list):
|
||||
|
@ -138,7 +142,9 @@ class TestController(base.TestCase):
|
|||
mock_vnf_list.return_value = fakes.return_vnf_package_list()
|
||||
res_dict = self.controller.index(req)
|
||||
expected_result = fakes.index_response()
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(
|
||||
jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
|
||||
default=str)), res_dict.json)
|
||||
|
||||
@mock.patch.object(VnfPackagesList, "get_by_filters")
|
||||
def test_index_attribute_selector_exclude_default(self, mock_vnf_list):
|
||||
|
@ -154,7 +160,9 @@ class TestController(base.TestCase):
|
|||
'checksum',
|
||||
'userDefinedData',
|
||||
'additionalArtifacts'])
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(
|
||||
jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
|
||||
default=str)), res_dict.json)
|
||||
|
||||
@mock.patch.object(VnfPackagesList, "get_by_filters")
|
||||
@ddt.data(
|
||||
|
@ -172,7 +180,9 @@ class TestController(base.TestCase):
|
|||
res_dict = self.controller.index(req)
|
||||
remove_attrs = [params['exclude_fields']]
|
||||
expected_result = fakes.index_response(remove_attrs=remove_attrs)
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(
|
||||
jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
|
||||
default=str)), res_dict.json)
|
||||
|
||||
@mock.patch.object(VnfPackagesList, "get_by_filters")
|
||||
@ddt.data(
|
||||
|
@ -199,7 +209,9 @@ class TestController(base.TestCase):
|
|||
res_dict = self.controller.index(req)
|
||||
remove_attrs = [x for x in complex_attrs if x != params['fields']]
|
||||
expected_result = fakes.index_response(remove_attrs=remove_attrs)
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(
|
||||
jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
|
||||
default=str)), res_dict.json)
|
||||
|
||||
@mock.patch.object(VnfPackagesList, "get_by_filters")
|
||||
def test_index_attribute_selector_user_defined_data_combination(self,
|
||||
|
@ -226,7 +238,9 @@ class TestController(base.TestCase):
|
|||
'checksum',
|
||||
'additionalArtifacts'],
|
||||
vnf_package_updates=vnf_package_updates)
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(
|
||||
jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
|
||||
default=str)), res_dict.json)
|
||||
|
||||
@mock.patch.object(VnfPackagesList, "get_by_filters")
|
||||
def test_index_attribute_selector_user_defined_data(self, mock_vnf_list):
|
||||
|
@ -238,7 +252,9 @@ class TestController(base.TestCase):
|
|||
res_dict = self.controller.index(req)
|
||||
expected_result = fakes.index_response(remove_attrs=[
|
||||
'checksum', 'softwareImages', 'additionalArtifacts'])
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(
|
||||
jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
|
||||
default=str)), res_dict.json)
|
||||
|
||||
@mock.patch.object(VnfPackagesList, "get_by_filters")
|
||||
def test_index_attribute_selector_nested_complex_attribute(self,
|
||||
|
@ -264,7 +280,9 @@ class TestController(base.TestCase):
|
|||
expected_result = fakes.index_response(remove_attrs=[
|
||||
'checksum', 'userDefinedData'],
|
||||
vnf_package_updates=vnf_package_updates)
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(
|
||||
jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
|
||||
default=str)), res_dict.json)
|
||||
|
||||
@mock.patch.object(VnfPackagesList, "get_by_filters")
|
||||
@ddt.data(
|
||||
|
@ -303,7 +321,9 @@ class TestController(base.TestCase):
|
|||
'checksum',
|
||||
'userDefinedData',
|
||||
'additionalArtifacts'])
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(
|
||||
jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
|
||||
default=str)), res_dict.json)
|
||||
|
||||
@mock.patch.object(VnfPackagesList, "get_by_filters")
|
||||
def test_index_filter_combination(self, mock_vnf_list):
|
||||
|
@ -321,7 +341,9 @@ class TestController(base.TestCase):
|
|||
'checksum',
|
||||
'userDefinedData',
|
||||
'additionalArtifacts'])
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(
|
||||
jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
|
||||
default=str)), res_dict.json)
|
||||
|
||||
@mock.patch.object(VnfPackagesList, "get_by_filters")
|
||||
@ddt.data(
|
||||
|
@ -368,7 +390,9 @@ class TestController(base.TestCase):
|
|||
'checksum',
|
||||
'userDefinedData',
|
||||
'additionalArtifacts'])
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(
|
||||
jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
|
||||
default=str)), res_dict.json)
|
||||
|
||||
@mock.patch.object(VnfPackagesList, "get_by_filters")
|
||||
@ddt.data(
|
||||
|
@ -400,7 +424,9 @@ class TestController(base.TestCase):
|
|||
'checksum',
|
||||
'userDefinedData',
|
||||
'additionalArtifacts'])
|
||||
self.assertEqual(expected_result, res_dict)
|
||||
self.assertEqual(
|
||||
jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
|
||||
default=str)), res_dict.json)
|
||||
|
||||
@mock.patch.object(VnfPackagesList, "get_by_filters")
|
||||
@ddt.data(
|
||||
|
@ -534,6 +560,52 @@ class TestController(base.TestCase):
|
|||
self.assertRaises(tacker_exc.ValidationError, self.controller.index,
|
||||
req)
|
||||
|
||||
@mock.patch.object(VnfPackagesList, "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_package_num', 1, group='vnf_package')
|
||||
query = urllib.parse.urlencode(values['params'])
|
||||
req = fake_request.HTTPRequest.blank('/vnfpkgm/v1/vnf_packages?' +
|
||||
query)
|
||||
mock_vnf_list.return_value = [
|
||||
fakes.return_vnfpkg_obj(
|
||||
vnfd_updates={'vnf_product_name': 'sample1'}),
|
||||
fakes.return_vnfpkg_obj(
|
||||
vnfd_updates={'vnf_product_name': 'sample2'}),
|
||||
fakes.return_vnfpkg_obj(
|
||||
vnfd_updates={'vnf_product_name': 'sample3'}),
|
||||
fakes.return_vnfpkg_obj(
|
||||
vnfd_updates={'vnf_product_name': 'sample4'})
|
||||
]
|
||||
expected_result = []
|
||||
for name in values['result_names']:
|
||||
expected_result += fakes.index_response(
|
||||
remove_attrs=[
|
||||
'softwareImages',
|
||||
'checksum',
|
||||
'userDefinedData',
|
||||
'additionalArtifacts'],
|
||||
vnf_package_updates={'vnfProductName': 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('/vnfpkgm/v1/vnf_packages?' +
|
||||
query)
|
||||
res_dict = self.controller.index(req)
|
||||
self.assertEqual(
|
||||
jsonutils.loads(jsonutils.dump_as_bytes(expected_result,
|
||||
default=str)), res_dict.json)
|
||||
|
||||
@mock.patch.object(vnf_package.VnfPackage, "get_by_id")
|
||||
@mock.patch.object(VNFPackageRPCAPI, "delete_vnf_package")
|
||||
def test_delete_with_204_status(self, mock_delete_rpc, mock_vnf_by_id):
|
||||
|
|
Loading…
Reference in New Issue