diff options
| -rw-r--r-- | nova/api/ec2/__init__.py | 11 | ||||
| -rw-r--r-- | nova/api/openstack/wsgi.py | 8 | ||||
| -rw-r--r-- | nova/compute/api.py | 5 | ||||
| -rw-r--r-- | nova/compute/manager.py | 9 | ||||
| -rw-r--r-- | nova/network/manager.py | 3 | ||||
| -rw-r--r-- | nova/network/quantum/client.py | 11 | ||||
| -rw-r--r-- | nova/notifier/api.py | 3 | ||||
| -rw-r--r-- | nova/openstack/common/jsonutils.py | 133 | ||||
| -rw-r--r-- | nova/rpc/common.py | 6 | ||||
| -rw-r--r-- | nova/scheduler/driver.py | 3 | ||||
| -rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_disk_config.py | 32 | ||||
| -rw-r--r-- | nova/tests/api/openstack/compute/contrib/test_scheduler_hints.py | 8 | ||||
| -rw-r--r-- | nova/tests/api/openstack/fakes.py | 3 | ||||
| -rw-r--r-- | nova/tests/baremetal/test_proxy_bare_metal.py | 6 | ||||
| -rw-r--r-- | nova/tests/notifier/test_capacity_notifier.py | 4 | ||||
| -rw-r--r-- | nova/tests/test_libvirt.py | 9 | ||||
| -rw-r--r-- | nova/tests/test_utils.py | 87 | ||||
| -rw-r--r-- | nova/utils.py | 100 | ||||
| -rw-r--r-- | nova/virt/baremetal/dom.py | 6 | ||||
| -rw-r--r-- | nova/virt/libvirt/connection.py | 18 | ||||
| -rw-r--r-- | openstack-common.conf | 2 |
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 |
