From b8b28bd679746040bb520e7b273d0e822820cac1 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Fri, 2 Aug 2013 11:47:09 -0700 Subject: [PATCH] Enable conversion for composite attribute items Bug 1207881 Enable 'dict validators' to convert composite attributes' items using a 'convert_to' specification in a way similar to first-level API attributes. This is needed in order to ensure boolean sub-attributes are properly handled. Change-Id: I7f466befaa88112cf7e9b77d854ac292b2af638f --- neutron/api/v2/attributes.py | 46 +++++++++++++++++---------- neutron/extensions/l3_ext_gw_mode.py | 4 ++- neutron/tests/unit/test_attributes.py | 14 ++++++++ 3 files changed, 46 insertions(+), 18 deletions(-) diff --git a/neutron/api/v2/attributes.py b/neutron/api/v2/attributes.py index d7aea8c2ae..1b0e1a2498 100644 --- a/neutron/api/v2/attributes.py +++ b/neutron/api/v2/attributes.py @@ -320,6 +320,29 @@ def _validate_uuid_list(data, valid_values=None): return msg +def _validate_dict_item(key, key_validator, data): + # Find conversion function, if any, and apply it + conv_func = key_validator.get('convert_to') + if conv_func: + data[key] = conv_func(data.get(key)) + # Find validator function + # TODO(salv-orlando): Structure of dict attributes should be improved + # to avoid iterating over items + val_func = val_params = None + for (k, v) in key_validator.iteritems(): + if k.startswith('type:'): + # ask forgiveness, not permission + try: + val_func = validators[k] + except KeyError: + return _("Validator '%s' does not exist.") % k + val_params = v + break + # Process validation + if val_func: + return val_func(data.get(key), val_params) + + def _validate_dict(data, key_specs=None): if not isinstance(data, dict): msg = _("'%s' is not a dictionary") % data @@ -339,25 +362,14 @@ def _validate_dict(data, key_specs=None): LOG.debug(msg) return msg - # Perform validation of all values according to the specifications. + # Perform validation and conversion of all values + # according to the specifications. for key, key_validator in [(k, v) for k, v in key_specs.iteritems() if k in data]: - - for val_name in [n for n in key_validator.iterkeys() - if n.startswith('type:')]: - # Check whether specified validator exists. - if val_name not in validators: - msg = _("Validator '%s' does not exist.") % val_name - LOG.debug(msg) - return msg - - val_func = validators[val_name] - val_params = key_validator[val_name] - - msg = val_func(data.get(key), val_params) - if msg: - LOG.debug(msg) - return msg + msg = _validate_dict_item(key, key_validator, data) + if msg: + LOG.debug(msg) + return msg def _validate_dict_or_none(data, key_specs=None): diff --git a/neutron/extensions/l3_ext_gw_mode.py b/neutron/extensions/l3_ext_gw_mode.py index 18e7acc98f..073b8209d5 100644 --- a/neutron/extensions/l3_ext_gw_mode.py +++ b/neutron/extensions/l3_ext_gw_mode.py @@ -19,6 +19,7 @@ # from neutron.api import extensions +from neutron.api.v2 import attributes as attrs from neutron.common import exceptions as qexception from neutron.extensions import l3 @@ -37,7 +38,8 @@ EXTENDED_ATTRIBUTES_2_0 = { 'validate': {'type:dict_or_nodata': {'network_id': {'type:uuid': None, 'required': True}, - 'enable_snat': {'type:boolean': None, 'required': False}} + 'enable_snat': {'type:boolean': None, 'required': False, + 'convert_to': attrs.convert_to_boolean}} }}}} diff --git a/neutron/tests/unit/test_attributes.py b/neutron/tests/unit/test_attributes.py index 49b57e8198..a79a67e02c 100644 --- a/neutron/tests/unit/test_attributes.py +++ b/neutron/tests/unit/test_attributes.py @@ -537,6 +537,20 @@ class TestAttributes(base.BaseTestCase): msg = attributes._validate_dict(dictionary, constraints) self.assertIsNotNone(msg) + def test_validate_dict_convert_boolean(self): + dictionary, constraints = self._construct_dict_and_constraints() + + constraints['key_bool'] = { + 'type:boolean': None, + 'required': False, + 'convert_to': attributes.convert_to_boolean} + dictionary['key_bool'] = 'true' + msg = attributes._validate_dict(dictionary, constraints) + self.assertIsNone(msg) + # Explicitly comparing with literal 'True' as assertTrue + # succeeds also for 'true' + self.assertIs(True, dictionary['key_bool']) + def test_subdictionary(self): dictionary, constraints = self._construct_dict_and_constraints()