Implement query param schema for simple_tenant_usage

GET simple_tenant_usage API accept query param to filter the
tenant usage.
This commit adds json schema to validate the valid
query parameters.

There is no change in API behaviour and additionalProperties
is kept True for backward compatibility.

Partially implements blueprint json-schema-validation-for-query-param

Change-Id: I24726fd9a82617798a13597ad9883db22a4a9846
This commit is contained in:
ghanshyam 2017-09-26 13:50:57 +03:00 committed by Ken'ichi Ohmichi
parent 2463c962ee
commit 3fc7609931
3 changed files with 158 additions and 0 deletions

View File

@ -0,0 +1,52 @@
# Copyright 2017 NEC Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import copy
from nova.api.validation import parameter_types
index_query = {
'type': 'object',
'properties': {
'start': parameter_types.multi_params({'type': 'string'}),
'end': parameter_types.multi_params({'type': 'string'}),
'detailed': parameter_types.multi_params({'type': 'string'})
},
# NOTE(gmann): This is kept True to keep backward compatibility.
# As of now Schema validation stripped out the additional parameters and
# does not raise 400. In the future, we may block the additional parameters
# by bump in Microversion.
'additionalProperties': True
}
show_query = {
'type': 'object',
'properties': {
'start': parameter_types.multi_params({'type': 'string'}),
'end': parameter_types.multi_params({'type': 'string'})
},
# NOTE(gmann): This is kept True to keep backward compatibility.
# As of now Schema validation stripped out the additional parameters and
# does not raise 400. In the future, we may block the additional parameters
# by bump in Microversion.
'additionalProperties': True
}
index_query_v240 = copy.deepcopy(index_query)
index_query_v240['properties'].update(
parameter_types.pagination_parameters)
show_query_v240 = copy.deepcopy(show_query)
show_query_v240['properties'].update(
parameter_types.pagination_parameters)

View File

@ -22,9 +22,11 @@ import six.moves.urllib.parse as urlparse
from webob import exc
from nova.api.openstack import common
from nova.api.openstack.compute.schemas import simple_tenant_usage as schema
from nova.api.openstack.compute.views import usages as usages_view
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api import validation
import nova.conf
from nova import context as nova_context
from nova import exception
@ -261,24 +263,28 @@ class SimpleTenantUsageController(wsgi.Controller):
return (period_start, period_stop, detailed)
@wsgi.Controller.api_version("2.40")
@validation.query_schema(schema.index_query_v240)
@extensions.expected_errors(400)
def index(self, req):
"""Retrieve tenant_usage for all tenants."""
return self._index(req, links=True)
@wsgi.Controller.api_version("2.1", "2.39") # noqa
@validation.query_schema(schema.index_query)
@extensions.expected_errors(400)
def index(self, req):
"""Retrieve tenant_usage for all tenants."""
return self._index(req)
@wsgi.Controller.api_version("2.40")
@validation.query_schema(schema.show_query_v240)
@extensions.expected_errors(400)
def show(self, req, id):
"""Retrieve tenant_usage for a specified tenant."""
return self._show(req, id, links=True)
@wsgi.Controller.api_version("2.1", "2.39") # noqa
@validation.query_schema(schema.show_query)
@extensions.expected_errors(400)
def show(self, req, id):
"""Retrieve tenant_usage for a specified tenant."""

View File

@ -367,6 +367,53 @@ class SimpleTenantUsageTestV21(test.TestCase):
self._test_get_tenants_usage_with_one_date(
'start=%s' % (NOW - datetime.timedelta(5)).isoformat())
def test_index_additional_query_parameters(self):
req = fakes.HTTPRequest.blank('?start=%s&end=%s&additional=1' %
(START.isoformat(), STOP.isoformat()),
version=self.version)
res = self.controller.index(req)
self.assertIn('tenant_usages', res)
def _test_index_duplicate_query_parameters_validation(self, params):
for param, value in params.items():
req = fakes.HTTPRequest.blank('?start=%s&%s=%s&%s=%s' %
(START.isoformat(), param, value, param, value),
version=self.version)
res = self.controller.index(req)
self.assertIn('tenant_usages', res)
def test_index_duplicate_query_parameters_validation(self):
params = {
'start': START.isoformat(),
'end': STOP.isoformat(),
'detailed': 1
}
self._test_index_duplicate_query_parameters_validation(params)
def test_show_additional_query_parameters(self):
req = fakes.HTTPRequest.blank('?start=%s&end=%s&additional=1' %
(START.isoformat(), STOP.isoformat()),
version=self.version)
res = self.controller.show(req, 1)
self.assertIn('tenant_usage', res)
def _test_show_duplicate_query_parameters_validation(self, params):
for param, value in params.items():
req = fakes.HTTPRequest.blank('?start=%s&%s=%s&%s=%s' %
(START.isoformat(), param, value, param, value),
version=self.version)
res = self.controller.show(req, 1)
self.assertIn('tenant_usage', res)
def test_show_duplicate_query_parameters_validation(self):
params = {
'start': START.isoformat(),
'end': STOP.isoformat()
}
self._test_show_duplicate_query_parameters_validation(params)
class SimpleTenantUsageTestV40(SimpleTenantUsageTestV21):
version = '2.40'
@ -383,6 +430,29 @@ class SimpleTenantUsageTestV40(SimpleTenantUsageTestV21):
self._test_verify_index(START, STOP,
limit=SERVERS * TENANTS)
@mock.patch('nova.objects.InstanceList.get_active_by_window_joined',
fake_get_active_by_window_joined)
def test_index_duplicate_query_parameters_validation(self):
params = {
'start': START.isoformat(),
'end': STOP.isoformat(),
'detailed': 1,
'limit': 1,
'marker': 1
}
self._test_index_duplicate_query_parameters_validation(params)
@mock.patch('nova.objects.InstanceList.get_active_by_window_joined',
fake_get_active_by_window_joined)
def test_show_duplicate_query_parameters_validation(self):
params = {
'start': START.isoformat(),
'end': STOP.isoformat(),
'limit': 1,
'marker': 1
}
self._test_show_duplicate_query_parameters_validation(params)
class SimpleTenantUsageLimitsTestV21(test.TestCase):
version = '2.1'
@ -449,6 +519,36 @@ class SimpleTenantUsageLimitsTestV240(SimpleTenantUsageLimitsTestV21):
self.assertRaises(
webob.exc.HTTPBadRequest, self.controller.index, req)
def test_index_with_invalid_non_int_limit(self):
req = self._get_request('?start=%s&end=%s&limit=-3')
self.assertRaises(exception.ValidationError,
self.controller.index, req)
def test_index_with_invalid_string_limit(self):
req = self._get_request('?start=%s&end=%s&limit=abc')
self.assertRaises(exception.ValidationError,
self.controller.index, req)
def test_index_duplicate_query_with_invalid_string_limit(self):
req = self._get_request('?start=%s&end=%s&limit=3&limit=abc')
self.assertRaises(exception.ValidationError,
self.controller.index, req)
def test_show_with_invalid_non_int_limit(self):
req = self._get_request('?start=%s&end=%s&limit=-3')
self.assertRaises(exception.ValidationError,
self.controller.show, req)
def test_show_with_invalid_string_limit(self):
req = self._get_request('?start=%s&end=%s&limit=abc')
self.assertRaises(exception.ValidationError,
self.controller.show, req)
def test_show_duplicate_query_with_invalid_string_limit(self):
req = self._get_request('?start=%s&end=%s&limit=3&limit=abc')
self.assertRaises(exception.ValidationError,
self.controller.show, req)
class SimpleTenantUsageControllerTestV21(test.TestCase):
controller = simple_tenant_usage_v21.SimpleTenantUsageController()