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
This commit is contained in:
Salvatore Orlando 2013-08-02 11:47:09 -07:00
parent 256f9743a3
commit b8b28bd679
3 changed files with 46 additions and 18 deletions

View File

@ -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):

View File

@ -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}}
}}}}

View File

@ -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()