diff --git a/heat/api/openstack/v1/events.py b/heat/api/openstack/v1/events.py index 0db3923db..2fd4896bb 100644 --- a/heat/api/openstack/v1/events.py +++ b/heat/api/openstack/v1/events.py @@ -18,6 +18,7 @@ from webob import exc from heat.api.openstack.v1 import util from heat.common.i18n import _ from heat.common import identifier +from heat.common import param_utils from heat.common import serializers from heat.common import wsgi from heat.rpc import api as rpc_api @@ -117,6 +118,11 @@ class EventController(object): if not filter_params: filter_params = None + key = rpc_api.PARAM_LIMIT + if key in params: + limit = param_utils.extract_int(key, params[key], allow_zero=True) + params[key] = limit + if resource_name is None: events = self._event_list(req, identity, filters=filter_params, **params) diff --git a/heat/common/param_utils.py b/heat/common/param_utils.py index 6504325d1..af94e1d5d 100644 --- a/heat/common/param_utils.py +++ b/heat/common/param_utils.py @@ -25,3 +25,31 @@ def extract_bool(subject): raise ValueError(_('Unrecognized value "%(value)s", acceptable ' 'values are: true, false.') % {'value': subject}) return strutils.bool_from_string(subject, strict=True) + + +def extract_int(name, value, allow_zero=True, allow_negative=False): + if value is None: + return None + + if not strutils.is_int_like(value): + raise ValueError(_("Only integer is acceptable by " + "'%(name)s'.") % {'name': name}) + + if value in ('0', 0): + if allow_zero: + return int(value) + raise ValueError(_("Only non-zero integer is acceptable by " + "'%(name)s'.") % {'name': name}) + try: + result = int(value) + except (TypeError, ValueError): + raise ValueError(_("Value '%(value)s' is invalid for '%(name)s' " + "which only accepts integer.") % + {'name': name, 'value': value}) + + if allow_negative is False and result < 0: + raise ValueError(_("Value '%(value)s' is invalid for '%(name)s' " + "which only accepts non-negative integer.") % + {'name': name, 'value': value}) + + return result diff --git a/heat/rpc/api.py b/heat/rpc/api.py index 819e04a6d..e1b0703ad 100644 --- a/heat/rpc/api.py +++ b/heat/rpc/api.py @@ -16,11 +16,11 @@ ENGINE_TOPIC = 'engine' PARAM_KEYS = ( PARAM_TIMEOUT, PARAM_DISABLE_ROLLBACK, PARAM_ADOPT_STACK_DATA, PARAM_SHOW_DELETED, PARAM_SHOW_NESTED, PARAM_EXISTING, - PARAM_CLEAR_PARAMETERS, PARAM_GLOBAL_TENANT, + PARAM_CLEAR_PARAMETERS, PARAM_GLOBAL_TENANT, PARAM_LIMIT, ) = ( 'timeout_mins', 'disable_rollback', 'adopt_stack_data', 'show_deleted', 'show_nested', 'existing', - 'clear_parameters', 'global_tenant' + 'clear_parameters', 'global_tenant', 'limit', ) STACK_KEYS = ( diff --git a/heat/tests/test_api_openstack_v1.py b/heat/tests/test_api_openstack_v1.py index d5548cc22..8e8634398 100644 --- a/heat/tests/test_api_openstack_v1.py +++ b/heat/tests/test_api_openstack_v1.py @@ -2835,7 +2835,7 @@ class EventControllerTest(ControllerTest, common.HeatTestCase): def test_index_whitelists_pagination_params(self, mock_call, mock_enforce): self._mock_enforce_setup(mock_enforce, 'index', True) params = { - 'limit': 'fake limit', + 'limit': 10, 'sort_keys': 'fake sort keys', 'marker': 'fake marker', 'sort_dir': 'fake sort dir', @@ -2857,7 +2857,7 @@ class EventControllerTest(ControllerTest, common.HeatTestCase): engine_args = rpc_call_args[1][1] self.assertEqual(6, len(engine_args)) self.assertIn('limit', engine_args) - self.assertEqual('fake limit', engine_args['limit']) + self.assertEqual(10, engine_args['limit']) self.assertIn('sort_keys', engine_args) self.assertEqual(['fake sort keys'], engine_args['sort_keys']) self.assertIn('marker', engine_args) diff --git a/heat/tests/test_common_param_utils.py b/heat/tests/test_common_param_utils.py index 2038cb000..29b497041 100644 --- a/heat/tests/test_common_param_utils.py +++ b/heat/tests/test_common_param_utils.py @@ -23,3 +23,68 @@ class TestExtractBool(common.HeatTestCase): self.assertFalse(param_utils.extract_bool(value)) for value in ('foo', 't', 'f', 'yes', 'no', 'y', 'n', '1', '0', None): self.assertRaises(ValueError, param_utils.extract_bool, value) + + +class TestExtractInt(common.HeatTestCase): + def test_extract_int(self): + # None case + self.assertIsNone(param_utils.extract_int('num', None)) + + # 0 case + self.assertEqual(0, param_utils.extract_int('num', 0)) + self.assertEqual(0, param_utils.extract_int('num', 0, allow_zero=True)) + self.assertEqual(0, param_utils.extract_int('num', '0')) + self.assertEqual(0, param_utils.extract_int('num', '0', + allow_zero=True)) + self.assertRaises(ValueError, + param_utils.extract_int, + 'num', 0, allow_zero=False) + self.assertRaises(ValueError, + param_utils.extract_int, + 'num', '0', allow_zero=False) + + # positive values + self.assertEqual(1, param_utils.extract_int('num', 1)) + self.assertEqual(1, param_utils.extract_int('num', '1')) + self.assertRaises(ValueError, param_utils.extract_int, 'num', '1.1') + self.assertRaises(ValueError, param_utils.extract_int, 'num', 1.1) + + # negative values + self.assertEqual(-1, param_utils.extract_int('num', -1, + allow_negative=True)) + self.assertEqual(-1, param_utils.extract_int('num', '-1', + allow_negative=True)) + self.assertRaises(ValueError, + param_utils.extract_int, 'num', '-1.1', + allow_negative=True) + self.assertRaises(ValueError, + param_utils.extract_int, 'num', -1.1, + allow_negative=True) + + self.assertRaises(ValueError, param_utils.extract_int, 'num', -1) + self.assertRaises(ValueError, param_utils.extract_int, 'num', '-1') + self.assertRaises(ValueError, param_utils.extract_int, 'num', '-1.1') + self.assertRaises(ValueError, param_utils.extract_int, 'num', -1.1) + + self.assertRaises(ValueError, + param_utils.extract_int, 'num', -1, + allow_negative=False) + self.assertRaises(ValueError, + param_utils.extract_int, 'num', '-1', + allow_negative=False) + self.assertRaises(ValueError, + param_utils.extract_int, 'num', '-1.1', + allow_negative=False) + self.assertRaises(ValueError, + param_utils.extract_int, 'num', -1.1, + allow_negative=False) + + # Non-int value + self.assertRaises(ValueError, + param_utils.extract_int, 'num', 'abc') + self.assertRaises(ValueError, + param_utils.extract_int, 'num', '') + self.assertRaises(ValueError, + param_utils.extract_int, 'num', 'true') + self.assertRaises(ValueError, + param_utils.extract_int, 'num', True)