diff options
author | Phil Day <philip.day@hp.com> | 2013-02-11 10:52:35 +0000 |
---|---|---|
committer | Phil Day <philip.day@hp.com> | 2013-02-11 10:52:35 +0000 |
commit | ccb665e6e18a5eba7cf6152f9f5aba6b004476f1 (patch) | |
tree | a4e6af5156343d6d24edcc12fac4dac46e6e9b2f | |
parent | b09aa29c8882b8284aebd03617ae758397bce6f0 (diff) | |
download | oslo-serialization-ccb665e6e18a5eba7cf6152f9f5aba6b004476f1.tar.gz |
to_primitive imposes what seems to be an arbitary data structure
depth of 3, but there is at least on case in Nova (Security
group Rules) which requires a depth beyond this.
https://bugs.launchpad.net/nova/+bug/1118608
Specifically security_group_rule_get_by_security_group
returns a set of rules which have the structure:
rule -> grantee_group -> Instance -> Instance_type
Rather than just bumping the depth limit, which might break some
other user of to_primitive we make it a specific parameter that
defaults to the current value but can be over-ridden when
required and log a warning when the depth is exceeded
Change-Id: I1eaebd484e20cb2eae09a693289709973de9943c
-rw-r--r-- | openstack/common/jsonutils.py | 13 | ||||
-rw-r--r-- | tests/unit/test_jsonutils.py | 26 |
2 files changed, 36 insertions, 3 deletions
diff --git a/openstack/common/jsonutils.py b/openstack/common/jsonutils.py index b7f6bcd..1ea7755 100644 --- a/openstack/common/jsonutils.py +++ b/openstack/common/jsonutils.py @@ -38,13 +38,17 @@ import functools import inspect import itertools import json +import logging import xmlrpclib +from openstack.common.gettextutils import _ from openstack.common import timeutils +LOG = logging.getLogger(__name__) + def to_primitive(value, convert_instances=False, convert_datetime=True, - level=0): + level=0, max_depth=3): """Convert a complex object into primitives. Handy for JSON serialization. We can optionally handle instances, @@ -80,7 +84,9 @@ def to_primitive(value, convert_instances=False, convert_datetime=True, if getattr(value, '__module__', None) == 'mox': return 'mock' - if level > 3: + if level > max_depth: + LOG.error(_('Max serialization depth exceeded on object: %d %s'), + level, value) return '?' # The try block may not be necessary after the class check above, @@ -89,7 +95,8 @@ def to_primitive(value, convert_instances=False, convert_datetime=True, recursive = functools.partial(to_primitive, convert_instances=convert_instances, convert_datetime=convert_datetime, - level=level) + level=level, + max_depth=max_depth) # It's not clear why xmlrpclib created their own DateTime type, but # for our purposes, make it a datetime type which is explicitly # handled diff --git a/tests/unit/test_jsonutils.py b/tests/unit/test_jsonutils.py index 87ac367..276ae97 100644 --- a/tests/unit/test_jsonutils.py +++ b/tests/unit/test_jsonutils.py @@ -143,3 +143,29 @@ class ToPrimitiveTestCase(utils.BaseTestCase): self.assertTrue(ret[0].startswith(u"<module 'datetime' from ")) self.assertTrue(ret[1].startswith('<function foo at 0x')) self.assertEquals(ret[2], '<built-in function dir>') + + def test_depth(self): + class LevelsGenerator(object): + def __init__(self, levels): + self._levels = levels + + def iteritems(self): + if self._levels == 0: + return iter([]) + else: + return iter([(0, LevelsGenerator(self._levels - 1))]) + + l4_obj = LevelsGenerator(4) + + json_l2 = {0: {0: '?'}} + json_l3 = {0: {0: {0: '?'}}} + json_l4 = {0: {0: {0: {0: '?'}}}} + + ret = jsonutils.to_primitive(l4_obj, max_depth=2) + self.assertEquals(ret, json_l2) + + ret = jsonutils.to_primitive(l4_obj, max_depth=3) + self.assertEquals(ret, json_l3) + + ret = jsonutils.to_primitive(l4_obj, max_depth=4) + self.assertEquals(ret, json_l4) |