summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/api/ec2/__init__.py11
-rw-r--r--nova/api/openstack/wsgi.py8
-rw-r--r--nova/compute/api.py5
-rw-r--r--nova/compute/manager.py9
-rw-r--r--nova/network/manager.py3
-rw-r--r--nova/network/quantum/client.py11
-rw-r--r--nova/notifier/api.py3
-rw-r--r--nova/openstack/common/jsonutils.py133
-rw-r--r--nova/rpc/common.py6
-rw-r--r--nova/scheduler/driver.py3
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_disk_config.py32
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_scheduler_hints.py8
-rw-r--r--nova/tests/api/openstack/fakes.py3
-rw-r--r--nova/tests/baremetal/test_proxy_bare_metal.py6
-rw-r--r--nova/tests/notifier/test_capacity_notifier.py4
-rw-r--r--nova/tests/test_libvirt.py9
-rw-r--r--nova/tests/test_utils.py87
-rw-r--r--nova/utils.py100
-rw-r--r--nova/virt/baremetal/dom.py6
-rw-r--r--nova/virt/libvirt/connection.py18
-rw-r--r--openstack-common.conf2
21 files changed, 210 insertions, 257 deletions
diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py
index ebcdc6aed..33a5c4af4 100644
--- a/nova/api/ec2/__init__.py
+++ b/nova/api/ec2/__init__.py
@@ -38,6 +38,7 @@ from nova import flags
from nova import log as logging
from nova.openstack.common import cfg
from nova.openstack.common import importutils
+from nova.openstack.common import jsonutils
from nova import utils
from nova import wsgi
@@ -226,7 +227,7 @@ class EC2Token(wsgi.Middleware):
'path': req.path,
'params': auth_params,
}}}
- creds_json = utils.dumps(creds)
+ creds_json = jsonutils.dumps(creds)
headers = {'Content-Type': 'application/json'}
# Disable "has no x member" pylint error
@@ -245,7 +246,7 @@ class EC2Token(wsgi.Middleware):
# having keystone return token, tenant,
# user, and roles from this call.
- result = utils.loads(response)
+ result = jsonutils.loads(response)
try:
token_id = result['access']['token']['id']
except (AttributeError, KeyError), e:
@@ -289,7 +290,7 @@ class EC2KeystoneAuth(wsgi.Middleware):
creds = {'ec2Credentials': cred_dict}
else:
creds = {'auth': {'OS-KSEC2:ec2Credentials': cred_dict}}
- creds_json = utils.dumps(creds)
+ creds_json = jsonutils.dumps(creds)
headers = {'Content-Type': 'application/json'}
o = urlparse.urlparse(FLAGS.keystone_ec2_url)
@@ -306,7 +307,7 @@ class EC2KeystoneAuth(wsgi.Middleware):
else:
msg = _("Failure communicating with keystone")
return ec2_error(req, request_id, "Unauthorized", msg)
- result = utils.loads(data)
+ result = jsonutils.loads(data)
conn.close()
try:
@@ -645,7 +646,7 @@ class Executor(wsgi.Application):
env.pop(k)
LOG.exception(_('Unexpected error raised: %s'), unicode(ex))
- LOG.error(_('Environment: %s') % utils.dumps(env))
+ LOG.error(_('Environment: %s') % jsonutils.dumps(env))
return ec2_error(req, request_id, 'UnknownError',
_('An unknown error has occurred. '
'Please try your request again.'))
diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py
index f65425246..04c87c21a 100644
--- a/nova/api/openstack/wsgi.py
+++ b/nova/api/openstack/wsgi.py
@@ -26,7 +26,7 @@ import webob
from nova import exception
from nova import log as logging
-from nova import utils
+from nova.openstack.common import jsonutils
from nova import wsgi
@@ -130,7 +130,7 @@ class JSONDeserializer(TextDeserializer):
def _from_json(self, datastring):
try:
- return utils.loads(datastring)
+ return jsonutils.loads(datastring)
except ValueError:
msg = _("cannot understand JSON")
raise exception.MalformedRequestBody(reason=msg)
@@ -242,7 +242,7 @@ class JSONDictSerializer(DictSerializer):
"""Default JSON request body serialization"""
def default(self, data):
- return utils.dumps(data)
+ return jsonutils.dumps(data)
class XMLDictSerializer(DictSerializer):
@@ -533,7 +533,7 @@ def action_peek_json(body):
"""Determine action to invoke."""
try:
- decoded = utils.loads(body)
+ decoded = jsonutils.loads(body)
except ValueError:
msg = _("cannot understand JSON")
raise exception.MalformedRequestBody(reason=msg)
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 538bfbcf1..de4042de9 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -40,6 +40,7 @@ import nova.image
from nova import log as logging
from nova import network
from nova.openstack.common import cfg
+from nova.openstack.common import jsonutils
import nova.policy
from nova import quota
from nova import rpc
@@ -652,7 +653,7 @@ class API(BaseAPI):
locals())
request_spec = {
- 'image': utils.to_primitive(image),
+ 'image': jsonutils.to_primitive(image),
'instance_properties': base_options,
'instance_type': instance_type,
'num_instances': num_instances,
@@ -1482,7 +1483,7 @@ class API(BaseAPI):
"instance_type_id": new_instance_type['id'],
"image": image,
"update_db": False,
- "request_spec": utils.to_primitive(request_spec),
+ "request_spec": jsonutils.to_primitive(request_spec),
"filter_properties": filter_properties,
}
self._cast_scheduler_message(context,
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 9f405a934..75c401acc 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -64,6 +64,7 @@ from nova.notifier import api as notifier
from nova.openstack.common import cfg
from nova.openstack.common import excutils
from nova.openstack.common import importutils
+from nova.openstack.common import jsonutils
from nova import rpc
from nova import utils
from nova.virt import driver
@@ -397,7 +398,7 @@ class ComputeManager(manager.SchedulerDependentManager):
bdm['device_name'])
self.db.block_device_mapping_update(
context, bdm['id'],
- {'connection_info': utils.dumps(cinfo)})
+ {'connection_info': jsonutils.dumps(cinfo)})
block_device_mapping.append({'connection_info': cinfo,
'mount_device':
bdm['device_name']})
@@ -635,7 +636,7 @@ class ComputeManager(manager.SchedulerDependentManager):
bdms = self._get_instance_volume_bdms(context, instance_uuid)
block_device_mapping = []
for bdm in bdms:
- cinfo = utils.loads(bdm['connection_info'])
+ cinfo = jsonutils.loads(bdm['connection_info'])
block_device_mapping.append({'connection_info': cinfo,
'mount_device':
bdm['device_name']})
@@ -1775,7 +1776,7 @@ class ComputeManager(manager.SchedulerDependentManager):
mountpoint)
values = {
'instance_uuid': instance_ref['uuid'],
- 'connection_info': utils.dumps(connection_info),
+ 'connection_info': jsonutils.dumps(connection_info),
'device_name': mountpoint,
'delete_on_termination': False,
'virtual_name': None,
@@ -1799,7 +1800,7 @@ class ComputeManager(manager.SchedulerDependentManager):
if instance_name not in self.driver.list_instances():
LOG.warn(_('Detaching volume from unknown instance'),
context=context, instance=instance)
- self.driver.detach_volume(utils.loads(bdm['connection_info']),
+ self.driver.detach_volume(jsonutils.loads(bdm['connection_info']),
instance_name,
mp)
diff --git a/nova/network/manager.py b/nova/network/manager.py
index c0d51ef5a..bc47c36ea 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -65,6 +65,7 @@ from nova.network import model as network_model
from nova.notifier import api as notifier
from nova.openstack.common import cfg
from nova.openstack.common import importutils
+from nova.openstack.common import jsonutils
import nova.policy
from nova import quota
from nova import rpc
@@ -194,7 +195,7 @@ class RPCAllocateFixedIP(object):
host = rpc.call(context, FLAGS.network_topic,
{'method': 'set_network_host',
'args': {'network_ref':
- utils.to_primitive(network)}})
+ jsonutils.to_primitive(network)}})
if host != self.host:
# need to call allocate_fixed_ip to correct network host
topic = self.db.queue_get_for(context,
diff --git a/nova/network/quantum/client.py b/nova/network/quantum/client.py
index d2637ae6d..f57b0fa93 100644
--- a/nova/network/quantum/client.py
+++ b/nova/network/quantum/client.py
@@ -21,7 +21,7 @@ import json
import socket
import urllib
-from nova import utils
+from nova.openstack.common import jsonutils
# FIXME(danwent): All content in this file should be removed once the
@@ -35,14 +35,11 @@ class JSONSerializer(object):
the standard serializer from the quantum library.
"""
def serialize(self, data, content_type):
- try:
- return json.dumps(data)
- except TypeError:
- pass
- return json.dumps(utils.to_primitive(data))
+ jsonutils.dumps(data)
def deserialize(self, data, content_type):
- return json.loads(data)
+ return jsonutils.loads(data)
+
# Quantum API v1.0 uses 420 + 430 for network + port not found
# Quantum API v1.1 uses 404 for network + port not found
diff --git a/nova/notifier/api.py b/nova/notifier/api.py
index b3bfbf9c4..bc2690077 100644
--- a/nova/notifier/api.py
+++ b/nova/notifier/api.py
@@ -21,6 +21,7 @@ from nova import flags
from nova import log as logging
from nova.openstack.common import cfg
from nova.openstack.common import importutils
+from nova.openstack.common import jsonutils
from nova import utils
@@ -121,7 +122,7 @@ def notify(context, publisher_id, event_type, priority, payload):
_('%s not in valid priorities') % priority)
# Ensure everything is JSON serializable.
- payload = utils.to_primitive(payload, convert_instances=True)
+ payload = jsonutils.to_primitive(payload, convert_instances=True)
driver = importutils.import_module(FLAGS.notification_driver)
msg = dict(message_id=str(uuid.uuid4()),
diff --git a/nova/openstack/common/jsonutils.py b/nova/openstack/common/jsonutils.py
new file mode 100644
index 000000000..fa8b8f9d1
--- /dev/null
+++ b/nova/openstack/common/jsonutils.py
@@ -0,0 +1,133 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# Copyright 2011 Justin Santa Barbara
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+'''
+JSON related utilities.
+
+This module provides a few things:
+
+ 1) A handy function for getting an object down to something that can be
+ JSON serialized. See to_primitive().
+
+ 2) Wrappers around loads() and dumps(). The dumps() wrapper will
+ automatically use to_primitive() for you if needed.
+
+ 3) This sets up anyjson to use the loads() and dumps() wrappers if anyjson
+ is available.
+'''
+
+
+import datetime
+import inspect
+import itertools
+import json
+
+
+def to_primitive(value, convert_instances=False, level=0):
+ """Convert a complex object into primitives.
+
+ Handy for JSON serialization. We can optionally handle instances,
+ but since this is a recursive function, we could have cyclical
+ data structures.
+
+ To handle cyclical data structures we could track the actual objects
+ visited in a set, but not all objects are hashable. Instead we just
+ track the depth of the object inspections and don't go too deep.
+
+ Therefore, convert_instances=True is lossy ... be aware.
+
+ """
+ nasty = [inspect.ismodule, inspect.isclass, inspect.ismethod,
+ inspect.isfunction, inspect.isgeneratorfunction,
+ inspect.isgenerator, inspect.istraceback, inspect.isframe,
+ inspect.iscode, inspect.isbuiltin, inspect.isroutine,
+ inspect.isabstract]
+ for test in nasty:
+ if test(value):
+ return unicode(value)
+
+ # value of itertools.count doesn't get caught by inspects
+ # above and results in infinite loop when list(value) is called.
+ if type(value) == itertools.count:
+ return unicode(value)
+
+ # FIXME(vish): Workaround for LP bug 852095. Without this workaround,
+ # tests that raise an exception in a mocked method that
+ # has a @wrap_exception with a notifier will fail. If
+ # we up the dependency to 0.5.4 (when it is released) we
+ # can remove this workaround.
+ if getattr(value, '__module__', None) == 'mox':
+ return 'mock'
+
+ if level > 3:
+ return '?'
+
+ # The try block may not be necessary after the class check above,
+ # but just in case ...
+ try:
+ if isinstance(value, (list, tuple)):
+ o = []
+ for v in value:
+ o.append(to_primitive(v, convert_instances=convert_instances,
+ level=level))
+ return o
+ elif isinstance(value, dict):
+ o = {}
+ for k, v in value.iteritems():
+ o[k] = to_primitive(v, convert_instances=convert_instances,
+ level=level)
+ return o
+ elif isinstance(value, datetime.datetime):
+ return str(value)
+ elif hasattr(value, 'iteritems'):
+ return to_primitive(dict(value.iteritems()),
+ convert_instances=convert_instances,
+ level=level)
+ elif hasattr(value, '__iter__'):
+ return to_primitive(list(value), level)
+ elif convert_instances and hasattr(value, '__dict__'):
+ # Likely an instance of something. Watch for cycles.
+ # Ignore class member vars.
+ return to_primitive(value.__dict__,
+ convert_instances=convert_instances,
+ level=level + 1)
+ else:
+ return value
+ except TypeError, e:
+ # Class objects are tricky since they may define something like
+ # __iter__ defined but it isn't callable as list().
+ return unicode(value)
+
+
+def dumps(value):
+ return json.dumps(value, default=to_primitive)
+
+
+def loads(s):
+ return json.loads(s)
+
+
+try:
+ import anyjson
+except ImportError:
+ pass
+else:
+ anyjson._modules.append((__name__, 'dumps', TypeError,
+ 'loads', ValueError))
+ anyjson.force_implementation(__name__)
diff --git a/nova/rpc/common.py b/nova/rpc/common.py
index 33aea9bc3..23a191a9b 100644
--- a/nova/rpc/common.py
+++ b/nova/rpc/common.py
@@ -25,7 +25,7 @@ from nova import exception
from nova import log as logging
from nova.openstack.common import cfg
from nova.openstack.common import importutils
-from nova import utils
+from nova.openstack.common import jsonutils
LOG = logging.getLogger(__name__)
@@ -174,13 +174,13 @@ def serialize_remote_exception(failure_info):
'kwargs': kwargs
}
- json_data = utils.dumps(data)
+ json_data = jsonutils.dumps(data)
return json_data
def deserialize_remote_exception(conf, data):
- failure = utils.loads(str(data))
+ failure = jsonutils.loads(str(data))
trace = failure.get('tb', [])
message = failure.get('message', "") + "\n" + "\n".join(trace)
diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py
index 3fcef8402..fc705780b 100644
--- a/nova/scheduler/driver.py
+++ b/nova/scheduler/driver.py
@@ -30,6 +30,7 @@ from nova import flags
from nova import log as logging
from nova.openstack.common import cfg
from nova.openstack.common import importutils
+from nova.openstack.common import jsonutils
from nova import rpc
from nova.rpc import common as rpc_common
from nova import utils
@@ -442,7 +443,7 @@ class Scheduler(object):
ret = rpc.call(context, topic,
{"method": 'get_instance_disk_info',
"args": {'instance_name': instance_ref['name']}})
- disk_infos = utils.loads(ret)
+ disk_infos = jsonutils.loads(ret)
necessary = 0
if disk_over_commit:
diff --git a/nova/tests/api/openstack/compute/contrib/test_disk_config.py b/nova/tests/api/openstack/compute/contrib/test_disk_config.py
index 87b21beb1..c9e4145e4 100644
--- a/nova/tests/api/openstack/compute/contrib/test_disk_config.py
+++ b/nova/tests/api/openstack/compute/contrib/test_disk_config.py
@@ -22,10 +22,10 @@ from nova.api.openstack.compute import extensions
from nova.api.openstack import wsgi
import nova.db.api
from nova import flags
+from nova.openstack.common import jsonutils
import nova.rpc
from nova import test
from nova.tests.api.openstack import fakes
-from nova import utils
MANUAL_INSTANCE_UUID = fakes.FAKE_UUID
@@ -130,19 +130,19 @@ class DiskConfigTestCase(test.TestCase):
req = fakes.HTTPRequest.blank(
'/fake/servers/%s' % MANUAL_INSTANCE_UUID)
res = req.get_response(self.app)
- server_dict = utils.loads(res.body)['server']
+ server_dict = jsonutils.loads(res.body)['server']
self.assertDiskConfig(server_dict, 'MANUAL')
req = fakes.HTTPRequest.blank(
'/fake/servers/%s' % AUTO_INSTANCE_UUID)
res = req.get_response(self.app)
- server_dict = utils.loads(res.body)['server']
+ server_dict = jsonutils.loads(res.body)['server']
self.assertDiskConfig(server_dict, 'AUTO')
def test_detail_servers(self):
req = fakes.HTTPRequest.blank('/fake/servers/detail')
res = req.get_response(self.app)
- server_dicts = utils.loads(res.body)['servers']
+ server_dicts = jsonutils.loads(res.body)['servers']
expectations = ['MANUAL', 'AUTO']
for server_dict, expected in zip(server_dicts, expectations):
@@ -152,19 +152,19 @@ class DiskConfigTestCase(test.TestCase):
req = fakes.HTTPRequest.blank(
'/fake/images/a440c04b-79fa-479c-bed1-0b816eaec379')
res = req.get_response(self.app)
- image_dict = utils.loads(res.body)['image']
+ image_dict = jsonutils.loads(res.body)['image']
self.assertDiskConfig(image_dict, 'MANUAL')
req = fakes.HTTPRequest.blank(
'/fake/images/70a599e0-31e7-49b7-b260-868f441e862b')
res = req.get_response(self.app)
- image_dict = utils.loads(res.body)['image']
+ image_dict = jsonutils.loads(res.body)['image']
self.assertDiskConfig(image_dict, 'AUTO')
def test_detail_image(self):
req = fakes.HTTPRequest.blank('/fake/images/detail')
res = req.get_response(self.app)
- image_dicts = utils.loads(res.body)['images']
+ image_dicts = jsonutils.loads(res.body)['images']
expectations = ['MANUAL', 'AUTO']
for image_dict, expected in zip(image_dicts, expectations):
@@ -184,9 +184,9 @@ class DiskConfigTestCase(test.TestCase):
API_DISK_CONFIG: 'AUTO'
}}
- req.body = utils.dumps(body)
+ req.body = jsonutils.dumps(body)
res = req.get_response(self.app)
- server_dict = utils.loads(res.body)['server']
+ server_dict = jsonutils.loads(res.body)['server']
self.assertDiskConfig(server_dict, 'AUTO')
def test_create_server_override_manual(self):
@@ -200,9 +200,9 @@ class DiskConfigTestCase(test.TestCase):
API_DISK_CONFIG: 'MANUAL'
}}
- req.body = utils.dumps(body)
+ req.body = jsonutils.dumps(body)
res = req.get_response(self.app)
- server_dict = utils.loads(res.body)['server']
+ server_dict = jsonutils.loads(res.body)['server']
self.assertDiskConfig(server_dict, 'MANUAL')
def test_create_server_detect_from_image(self):
@@ -218,9 +218,9 @@ class DiskConfigTestCase(test.TestCase):
'flavorRef': '1',
}}
- req.body = utils.dumps(body)
+ req.body = jsonutils.dumps(body)
res = req.get_response(self.app)
- server_dict = utils.loads(res.body)['server']
+ server_dict = jsonutils.loads(res.body)['server']
self.assertDiskConfig(server_dict, 'MANUAL')
req = fakes.HTTPRequest.blank('/fake/servers')
@@ -232,9 +232,9 @@ class DiskConfigTestCase(test.TestCase):
'flavorRef': '1',
}}
- req.body = utils.dumps(body)
+ req.body = jsonutils.dumps(body)
res = req.get_response(self.app)
- server_dict = utils.loads(res.body)['server']
+ server_dict = jsonutils.loads(res.body)['server']
self.assertDiskConfig(server_dict, 'AUTO')
def test_update_server_invalid_disk_config(self):
@@ -244,7 +244,7 @@ class DiskConfigTestCase(test.TestCase):
req.method = 'PUT'
req.content_type = 'application/json'
body = {'server': {API_DISK_CONFIG: 'server_test'}}
- req.body = utils.dumps(body)
+ req.body = jsonutils.dumps(body)
res = req.get_response(self.app)
self.assertEqual(res.status_int, 400)
expected_msg = ('{"badRequest": {"message": "%s must be either'
diff --git a/nova/tests/api/openstack/compute/contrib/test_scheduler_hints.py b/nova/tests/api/openstack/compute/contrib/test_scheduler_hints.py
index 167d46d02..59ad8abbc 100644
--- a/nova/tests/api/openstack/compute/contrib/test_scheduler_hints.py
+++ b/nova/tests/api/openstack/compute/contrib/test_scheduler_hints.py
@@ -19,10 +19,10 @@ from nova.api.openstack import compute
from nova.api.openstack.compute import extensions
from nova.api.openstack import wsgi
import nova.db.api
+from nova.openstack.common import jsonutils
import nova.rpc
from nova import test
from nova.tests.api.openstack import fakes
-from nova import utils
UUID = fakes.FAKE_UUID
@@ -52,7 +52,7 @@ class SchedulerHintsTestCase(test.TestCase):
'flavorRef': '1',
}}
- req.body = utils.dumps(body)
+ req.body = jsonutils.dumps(body)
res = req.get_response(self.app)
self.assertEqual(202, res.status_int)
@@ -76,7 +76,7 @@ class SchedulerHintsTestCase(test.TestCase):
'os:scheduler_hints': {'a': 'b'},
}
- req.body = utils.dumps(body)
+ req.body = jsonutils.dumps(body)
res = req.get_response(self.app)
self.assertEqual(202, res.status_int)
@@ -93,6 +93,6 @@ class SchedulerHintsTestCase(test.TestCase):
'os:scheduler_hints': 'here',
}
- req.body = utils.dumps(body)
+ req.body = jsonutils.dumps(body)
res = req.get_response(self.app)
self.assertEqual(400, res.status_int)
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index f06878362..1b51249d6 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -39,6 +39,7 @@ from nova import context
from nova.db.sqlalchemy import models
from nova import exception as exc
import nova.image.fake
+from nova.openstack.common import jsonutils
from nova.tests import fake_network
from nova.tests.glance import stubs as glance_stubs
from nova import utils
@@ -464,7 +465,7 @@ def create_info_cache(nw_cache):
return {"info_cache": {"network_info": nw_cache}}
if not isinstance(nw_cache, basestring):
- nw_cache = utils.dumps(nw_cache)
+ nw_cache = jsonutils.dumps(nw_cache)
return {"info_cache": {"network_info": nw_cache}}
diff --git a/nova/tests/baremetal/test_proxy_bare_metal.py b/nova/tests/baremetal/test_proxy_bare_metal.py
index 323043800..128b16cb8 100644
--- a/nova/tests/baremetal/test_proxy_bare_metal.py
+++ b/nova/tests/baremetal/test_proxy_bare_metal.py
@@ -20,9 +20,9 @@ import mox
import StringIO
from nova import flags
-from nova import utils
from nova import test
from nova.compute import power_state
+from nova.openstack.common import jsonutils
from nova.tests import fake_utils
from nova import exception
@@ -95,8 +95,8 @@ class DomainReadWriteTestCase(test.TestCase):
def assertJSONEquals(self, x, y):
"""Check if two json strings represent the equivalent Python object"""
- self.assertEquals(utils.loads(x), utils.loads(y))
- return utils.loads(x) == utils.loads(y)
+ self.assertEquals(jsonutils.loads(x), jsonutils.loads(y))
+ return jsonutils.loads(x) == jsonutils.loads(y)
def test_write_domain(self):
"""Write the domain to file"""
diff --git a/nova/tests/notifier/test_capacity_notifier.py b/nova/tests/notifier/test_capacity_notifier.py
index e41c52700..3b642a77b 100644
--- a/nova/tests/notifier/test_capacity_notifier.py
+++ b/nova/tests/notifier/test_capacity_notifier.py
@@ -15,8 +15,8 @@
import nova.db.api
from nova.notifier import capacity_notifier as cn
+from nova.openstack.common import jsonutils
from nova import test
-from nova import utils
class CapacityNotifierTestCase(test.TestCase):
@@ -24,7 +24,7 @@ class CapacityNotifierTestCase(test.TestCase):
def _make_msg(self, host, event):
usage_info = dict(memory_mb=123, disk_gb=456)
- payload = utils.to_primitive(usage_info, convert_instances=True)
+ payload = jsonutils.to_primitive(usage_info, convert_instances=True)
return dict(
publisher_id="compute.%s" % host,
event_type="compute.instance.%s" % event,
diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py
index b0b165c70..db804acee 100644
--- a/nova/tests/test_libvirt.py
+++ b/nova/tests/test_libvirt.py
@@ -38,6 +38,7 @@ from nova import exception
from nova import flags
from nova import log as logging
from nova.openstack.common import importutils
+from nova.openstack.common import jsonutils
from nova import test
from nova.tests import fake_network
from nova.tests import fake_libvirt_utils
@@ -1405,7 +1406,7 @@ class LibvirtConnTestCase(test.TestCase):
self.mox.ReplayAll()
conn = connection.LibvirtConnection(False)
info = conn.get_instance_disk_info(instance_ref.name)
- info = utils.loads(info)
+ info = jsonutils.loads(info)
self.assertEquals(info[0]['type'], 'raw')
self.assertEquals(info[0]['path'], '/test/disk')
self.assertEquals(info[0]['disk_size'], 10737418240)
@@ -2507,7 +2508,7 @@ class LibvirtConnectionTestCase(test.TestCase):
'virt_disk_size': '10737418240',
'backing_file': '/base/disk.local',
'disk_size':'83886080'}]
- disk_info_text = utils.dumps(disk_info)
+ disk_info_text = jsonutils.dumps(disk_info)
def fake_get_instance_disk_info(instance):
return disk_info_text
@@ -2578,7 +2579,7 @@ class LibvirtConnectionTestCase(test.TestCase):
'local_gb': 10, 'backing_file': '/base/disk'},
{'type': 'raw', 'path': '/test/disk.local',
'local_gb': 10, 'backing_file': '/base/disk.local'}]
- disk_info_text = utils.dumps(disk_info)
+ disk_info_text = jsonutils.dumps(disk_info)
def fake_extend(path, size):
pass
@@ -2668,4 +2669,4 @@ class LibvirtNonblockingTestCase(test.TestCase):
"""Test bug 962840"""
import nova.virt.libvirt.connection
connection = nova.virt.libvirt.connection.get_connection('')
- utils.to_primitive(connection._conn, convert_instances=True)
+ jsonutils.to_primitive(connection._conn, convert_instances=True)
diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py
index a8de63449..0abc19c6d 100644
--- a/nova/tests/test_utils.py
+++ b/nova/tests/test_utils.py
@@ -443,93 +443,6 @@ class IsUUIDLikeTestCase(test.TestCase):
self.assertUUIDLike(str(utils.gen_uuid()), True)
-class ToPrimitiveTestCase(test.TestCase):
- def test_list(self):
- self.assertEquals(utils.to_primitive([1, 2, 3]), [1, 2, 3])
-
- def test_empty_list(self):
- self.assertEquals(utils.to_primitive([]), [])
-
- def test_tuple(self):
- self.assertEquals(utils.to_primitive((1, 2, 3)), [1, 2, 3])
-
- def test_dict(self):
- self.assertEquals(utils.to_primitive(dict(a=1, b=2, c=3)),
- dict(a=1, b=2, c=3))
-
- def test_empty_dict(self):
- self.assertEquals(utils.to_primitive({}), {})
-
- def test_datetime(self):
- x = datetime.datetime(1, 2, 3, 4, 5, 6, 7)
- self.assertEquals(utils.to_primitive(x), "0001-02-03 04:05:06.000007")
-
- def test_iter(self):
- class IterClass(object):
- def __init__(self):
- self.data = [1, 2, 3, 4, 5]
- self.index = 0
-
- def __iter__(self):
- return self
-
- def next(self):
- if self.index == len(self.data):
- raise StopIteration
- self.index = self.index + 1
- return self.data[self.index - 1]
-
- x = IterClass()
- self.assertEquals(utils.to_primitive(x), [1, 2, 3, 4, 5])
-
- def test_iteritems(self):
- class IterItemsClass(object):
- def __init__(self):
- self.data = dict(a=1, b=2, c=3).items()
- self.index = 0
-
- def __iter__(self):
- return self
-
- def next(self):
- if self.index == len(self.data):
- raise StopIteration
- self.index = self.index + 1
- return self.data[self.index - 1]
-
- x = IterItemsClass()
- ordered = utils.to_primitive(x)
- ordered.sort()
- self.assertEquals(ordered, [['a', 1], ['b', 2], ['c', 3]])
-
- def test_instance(self):
- class MysteryClass(object):
- a = 10
-
- def __init__(self):
- self.b = 1
-
- x = MysteryClass()
- self.assertEquals(utils.to_primitive(x, convert_instances=True),
- dict(b=1))
-
- self.assertEquals(utils.to_primitive(x), x)
-
- def test_typeerror(self):
- x = bytearray # Class, not instance
- self.assertEquals(utils.to_primitive(x), u"<type 'bytearray'>")
-
- def test_nasties(self):
- def foo():
- pass
- x = [datetime, foo, dir]
- ret = utils.to_primitive(x)
- self.assertEquals(len(ret), 3)
- 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>')
-
-
class MonkeyPatchTestCase(test.TestCase):
"""Unit test for utils.monkey_patch()."""
def setUp(self):
diff --git a/nova/utils.py b/nova/utils.py
index 2b495d1ff..9577ff269 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -25,8 +25,6 @@ import errno
import functools
import hashlib
import inspect
-import itertools
-import json
import os
import pyclbr
import random
@@ -677,104 +675,6 @@ def utf8(value):
return value
-def to_primitive(value, convert_instances=False, level=0):
- """Convert a complex object into primitives.
-
- Handy for JSON serialization. We can optionally handle instances,
- but since this is a recursive function, we could have cyclical
- data structures.
-
- To handle cyclical data structures we could track the actual objects
- visited in a set, but not all objects are hashable. Instead we just
- track the depth of the object inspections and don't go too deep.
-
- Therefore, convert_instances=True is lossy ... be aware.
-
- """
- nasty = [inspect.ismodule, inspect.isclass, inspect.ismethod,
- inspect.isfunction, inspect.isgeneratorfunction,
- inspect.isgenerator, inspect.istraceback, inspect.isframe,
- inspect.iscode, inspect.isbuiltin, inspect.isroutine,
- inspect.isabstract]
- for test in nasty:
- if test(value):
- return unicode(value)
-
- # value of itertools.count doesn't get caught by inspects
- # above and results in infinite loop when list(value) is called.
- if type(value) == itertools.count:
- return unicode(value)
-
- # FIXME(vish): Workaround for LP bug 852095. Without this workaround,
- # tests that raise an exception in a mocked method that
- # has a @wrap_exception with a notifier will fail. If
- # we up the dependency to 0.5.4 (when it is released) we
- # can remove this workaround.
- if getattr(value, '__module__', None) == 'mox':
- return 'mock'
-
- if level > 3:
- return '?'
-
- # The try block may not be necessary after the class check above,
- # but just in case ...
- try:
- if isinstance(value, (list, tuple)):
- o = []
- for v in value:
- o.append(to_primitive(v, convert_instances=convert_instances,
- level=level))
- return o
- elif isinstance(value, dict):
- o = {}
- for k, v in value.iteritems():
- o[k] = to_primitive(v, convert_instances=convert_instances,
- level=level)
- return o
- elif isinstance(value, datetime.datetime):
- return str(value)
- elif hasattr(value, 'iteritems'):
- return to_primitive(dict(value.iteritems()),
- convert_instances=convert_instances,
- level=level)
- elif hasattr(value, '__iter__'):
- return to_primitive(list(value), level)
- elif convert_instances and hasattr(value, '__dict__'):
- # Likely an instance of something. Watch for cycles.
- # Ignore class member vars.
- return to_primitive(value.__dict__,
- convert_instances=convert_instances,
- level=level + 1)
- else:
- return value
- except TypeError, e:
- # Class objects are tricky since they may define something like
- # __iter__ defined but it isn't callable as list().
- return unicode(value)
-
-
-def dumps(value):
- try:
- return json.dumps(value)
- except TypeError:
- pass
- return json.dumps(to_primitive(value))
-
-
-def loads(s):
- return json.loads(s)
-
-
-try:
- import anyjson
-except ImportError:
- pass
-else:
- anyjson._modules.append(("nova.utils", "dumps", TypeError,
- "loads", ValueError))
- anyjson.force_implementation("nova.utils")
-
-
class GreenLockFile(lockfile.FileLock):
"""Implementation of lockfile that allows for a lock per greenthread.
diff --git a/nova/virt/baremetal/dom.py b/nova/virt/baremetal/dom.py
index 04c889277..de027be2e 100644
--- a/nova/virt/baremetal/dom.py
+++ b/nova/virt/baremetal/dom.py
@@ -18,7 +18,7 @@ from nova.compute import power_state
from nova import exception
from nova import flags
from nova import log as logging
-from nova import utils
+from nova.openstack.common import jsonutils
from nova.virt.baremetal import nodes
FLAGS = flags.FLAGS
@@ -31,14 +31,14 @@ def read_domains(fname):
f = open(fname, 'r')
json = f.read()
f.close()
- domains = utils.loads(json)
+ domains = jsonutils.loads(json)
return domains
except IOError:
raise exception.NotFound()
def write_domains(fname, domains):
- json = utils.dumps(domains)
+ json = jsonutils.dumps(domains)
f = open(fname, 'w')
f.write(json)
f.close()
diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py
index a5c65ff04..77ee8cad2 100644
--- a/nova/virt/libvirt/connection.py
+++ b/nova/virt/libvirt/connection.py
@@ -66,6 +66,7 @@ from nova import log as logging
from nova.openstack.common import cfg
from nova.openstack.common import excutils
from nova.openstack.common import importutils
+from nova.openstack.common import jsonutils
from nova import utils
from nova.virt import driver
from nova.virt.disk import api as disk
@@ -2017,7 +2018,7 @@ class LibvirtConnection(driver.ComputeDriver):
cpu_info['topology'] = topology
cpu_info['features'] = features
- return utils.dumps(cpu_info)
+ return jsonutils.dumps(cpu_info)
def block_stats(self, instance_name, disk):
"""
@@ -2102,7 +2103,7 @@ class LibvirtConnection(driver.ComputeDriver):
"""
- info = utils.loads(cpu_info)
+ info = jsonutils.loads(cpu_info)
LOG.info(_('Instance launched has CPU info:\n%s') % cpu_info)
cpu = config.LibvirtConfigCPU()
cpu.arch = info['arch']
@@ -2277,7 +2278,7 @@ class LibvirtConnection(driver.ComputeDriver):
json strings specified in get_instance_disk_info
"""
- disk_info = utils.loads(disk_info_json)
+ disk_info = jsonutils.loads(disk_info_json)
# make instance directory
instance_dir = os.path.join(FLAGS.instances_path, instance_ref['name'])
@@ -2414,7 +2415,7 @@ class LibvirtConnection(driver.ComputeDriver):
'virt_disk_size': virt_size,
'backing_file': backing_file,
'disk_size': dk_size})
- return utils.dumps(disk_info)
+ return jsonutils.dumps(disk_info)
def get_disk_available_least(self):
"""Return disk available least size.
@@ -2434,7 +2435,8 @@ class LibvirtConnection(driver.ComputeDriver):
instances_sz = 0
for i_name in instances_name:
try:
- disk_infos = utils.loads(self.get_instance_disk_info(i_name))
+ disk_infos = jsonutils.loads(
+ self.get_instance_disk_info(i_name))
for info in disk_infos:
i_vt_sz = int(info['virt_disk_size'])
i_dk_sz = int(info['disk_size'])
@@ -2495,7 +2497,7 @@ class LibvirtConnection(driver.ComputeDriver):
LOG.debug(_("Starting migrate_disk_and_power_off"),
instance=instance)
disk_info_text = self.get_instance_disk_info(instance['name'])
- disk_info = utils.loads(disk_info_text)
+ disk_info = jsonutils.loads(disk_info_text)
self._destroy(instance, network_info, cleanup=False)
@@ -2561,7 +2563,7 @@ class LibvirtConnection(driver.ComputeDriver):
LOG.debug(_("Starting finish_migration"), instance=instance)
# resize disks. only "disk" and "disk.local" are necessary.
- disk_info = utils.loads(disk_info)
+ disk_info = jsonutils.loads(disk_info)
for info in disk_info:
fname = os.path.basename(info['path'])
if fname == 'disk':
@@ -2647,7 +2649,7 @@ class HostState(object):
data = {}
data["vcpus"] = self.connection.get_vcpu_total()
data["vcpus_used"] = self.connection.get_vcpu_used()
- data["cpu_info"] = utils.loads(self.connection.get_cpu_info())
+ data["cpu_info"] = jsonutils.loads(self.connection.get_cpu_info())
data["disk_total"] = self.connection.get_local_gb_total()
data["disk_used"] = self.connection.get_local_gb_used()
data["disk_available"] = data["disk_total"] - data["disk_used"]
diff --git a/openstack-common.conf b/openstack-common.conf
index 538d4b0c0..61690b895 100644
--- a/openstack-common.conf
+++ b/openstack-common.conf
@@ -1,7 +1,7 @@
[DEFAULT]
# The list of modules to copy from openstack-common
-modules=cfg,excutils,local,importutils,iniparser,setup
+modules=cfg,excutils,local,importutils,iniparser,jsonutils,setup
# The base module to hold the copy of openstack.common
base=nova