summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Day <philip.day@hp.com>2013-02-11 10:52:35 +0000
committerPhil Day <philip.day@hp.com>2013-02-11 10:52:35 +0000
commitccb665e6e18a5eba7cf6152f9f5aba6b004476f1 (patch)
treea4e6af5156343d6d24edcc12fac4dac46e6e9b2f
parentb09aa29c8882b8284aebd03617ae758397bce6f0 (diff)
downloadoslo-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.py13
-rw-r--r--tests/unit/test_jsonutils.py26
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)