diff options
-rw-r--r-- | nova/api/ec2/apirequest.py | 4 | ||||
-rw-r--r-- | nova/api/openstack/compute/servers.py | 10 | ||||
-rw-r--r-- | nova/cmd/all.py | 10 | ||||
-rw-r--r-- | nova/cmd/api.py | 4 | ||||
-rw-r--r-- | nova/compute/api.py | 35 | ||||
-rw-r--r-- | nova/compute/cells_api.py | 10 | ||||
-rwxr-xr-x | nova/compute/manager.py | 23 | ||||
-rw-r--r-- | nova/openstack/common/rpc/common.py | 4 | ||||
-rw-r--r-- | nova/openstack/common/rpc/dispatcher.py | 29 | ||||
-rw-r--r-- | nova/openstack/common/rpc/proxy.py | 58 | ||||
-rw-r--r-- | nova/openstack/common/rpc/serializer.py | 52 | ||||
-rw-r--r-- | nova/service.py | 318 | ||||
-rw-r--r-- | nova/servicegroup/drivers/db.py | 8 | ||||
-rw-r--r-- | nova/servicegroup/drivers/mc.py | 8 | ||||
-rw-r--r-- | nova/tests/api/openstack/compute/test_servers.py | 36 | ||||
-rw-r--r-- | nova/tests/compute/test_compute.py | 157 | ||||
-rw-r--r-- | nova/tests/compute/test_compute_cells.py | 3 | ||||
-rw-r--r-- | nova/tests/integrated/test_api_samples.py | 2 | ||||
-rw-r--r-- | nova/tests/integrated/test_multiprocess_api.py | 4 | ||||
-rw-r--r-- | nova/tests/test_migrations.py | 2 | ||||
-rw-r--r-- | nova/tests/test_service.py | 5 | ||||
-rw-r--r-- | nova/tests/test_virt_drivers.py | 8 | ||||
-rw-r--r-- | nova/tests/test_xenapi.py | 5 | ||||
-rw-r--r-- | nova/tests/virt/baremetal/__init__.py (renamed from nova/tests/baremetal/__init__.py) | 2 | ||||
-rw-r--r-- | nova/tests/virt/baremetal/db/__init__.py (renamed from nova/tests/baremetal/db/__init__.py) | 2 | ||||
-rw-r--r-- | nova/tests/virt/baremetal/db/base.py (renamed from nova/tests/baremetal/db/base.py) | 0 | ||||
-rw-r--r-- | nova/tests/virt/baremetal/db/test_bm_interface.py (renamed from nova/tests/baremetal/db/test_bm_interface.py) | 2 | ||||
-rw-r--r-- | nova/tests/virt/baremetal/db/test_bm_node.py (renamed from nova/tests/baremetal/db/test_bm_node.py) | 4 | ||||
-rw-r--r-- | nova/tests/virt/baremetal/db/test_bm_pxe_ip.py (renamed from nova/tests/baremetal/db/test_bm_pxe_ip.py) | 4 | ||||
-rw-r--r-- | nova/tests/virt/baremetal/db/utils.py (renamed from nova/tests/baremetal/db/utils.py) | 0 | ||||
-rw-r--r-- | nova/tests/virt/baremetal/test_baremetal_migrations.conf (renamed from nova/tests/test_baremetal_migrations.conf) | 0 | ||||
-rw-r--r-- | nova/tests/virt/baremetal/test_driver.py (renamed from nova/tests/baremetal/test_driver.py) | 4 | ||||
-rw-r--r-- | nova/tests/virt/baremetal/test_ipmi.py (renamed from nova/tests/baremetal/test_ipmi.py) | 2 | ||||
-rw-r--r-- | nova/tests/virt/baremetal/test_nova_baremetal_deploy_helper.py (renamed from nova/tests/baremetal/test_nova_baremetal_deploy_helper.py) | 2 | ||||
-rw-r--r-- | nova/tests/virt/baremetal/test_nova_baremetal_manage.py (renamed from nova/tests/baremetal/test_nova_baremetal_manage.py) | 2 | ||||
-rw-r--r-- | nova/tests/virt/baremetal/test_pxe.py (renamed from nova/tests/baremetal/test_pxe.py) | 4 | ||||
-rwxr-xr-x | nova/tests/virt/baremetal/test_tilera.py (renamed from nova/tests/baremetal/test_tilera.py) | 4 | ||||
-rwxr-xr-x | nova/tests/virt/baremetal/test_tilera_pdu.py (renamed from nova/tests/baremetal/test_tilera_pdu.py) | 2 | ||||
-rw-r--r-- | nova/tests/virt/baremetal/test_utils.py (renamed from nova/tests/baremetal/test_utils.py) | 0 | ||||
-rw-r--r-- | nova/tests/virt/baremetal/test_virtual_power_driver.py (renamed from nova/tests/baremetal/test_virtual_power_driver.py) | 4 | ||||
-rw-r--r-- | nova/tests/virt/baremetal/test_volume_driver.py (renamed from nova/tests/baremetal/test_volume_driver.py) | 0 | ||||
-rw-r--r-- | nova/tests/virt/libvirt/__init__.py | 15 | ||||
-rw-r--r-- | nova/tests/virt/libvirt/fake_imagebackend.py (renamed from nova/tests/fake_imagebackend.py) | 0 | ||||
-rw-r--r-- | nova/tests/virt/libvirt/fake_libvirt_utils.py (renamed from nova/tests/fake_libvirt_utils.py) | 0 | ||||
-rw-r--r-- | nova/tests/virt/libvirt/fakelibvirt.py (renamed from nova/tests/fakelibvirt.py) | 0 | ||||
-rw-r--r-- | nova/tests/virt/libvirt/test_fakelibvirt.py (renamed from nova/tests/test_fakelibvirt.py) | 2 | ||||
-rw-r--r-- | nova/tests/virt/libvirt/test_imagebackend.py (renamed from nova/tests/test_imagebackend.py) | 2 | ||||
-rw-r--r-- | nova/tests/virt/libvirt/test_imagecache.py (renamed from nova/tests/test_imagecache.py) | 0 | ||||
-rw-r--r-- | nova/tests/virt/libvirt/test_libvirt.py (renamed from nova/tests/test_libvirt.py) | 7 | ||||
-rw-r--r-- | nova/tests/virt/libvirt/test_libvirt_blockinfo.py (renamed from nova/tests/test_libvirt_blockinfo.py) | 0 | ||||
-rw-r--r-- | nova/tests/virt/libvirt/test_libvirt_config.py (renamed from nova/tests/test_libvirt_config.py) | 0 | ||||
-rw-r--r-- | nova/tests/virt/libvirt/test_libvirt_utils.py (renamed from nova/tests/test_libvirt_utils.py) | 0 | ||||
-rw-r--r-- | nova/tests/virt/libvirt/test_libvirt_vif.py (renamed from nova/tests/test_libvirt_vif.py) | 2 | ||||
-rw-r--r-- | nova/tests/virt/libvirt/test_libvirt_volume.py (renamed from nova/tests/test_libvirt_volume.py) | 2 | ||||
-rw-r--r-- | openstack-common.conf | 1 |
55 files changed, 459 insertions, 405 deletions
diff --git a/nova/api/ec2/apirequest.py b/nova/api/ec2/apirequest.py index b43b63287..a2c20efc0 100644 --- a/nova/api/ec2/apirequest.py +++ b/nova/api/ec2/apirequest.py @@ -21,6 +21,7 @@ APIRequest class """ import datetime +from lxml import etree # TODO(termie): replace minidom with etree from xml.dom import minidom @@ -95,6 +96,9 @@ class APIRequest(object): xml.appendChild(response_el) response = xml.toxml() + root = etree.fromstring(response) + response = etree.tostring(root, pretty_print=True) + xml.unlink() # Don't write private key to log diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py index 166c8b10e..44d8dce3b 100644 --- a/nova/api/openstack/compute/servers.py +++ b/nova/api/openstack/compute/servers.py @@ -1282,18 +1282,16 @@ class Controller(wsgi.Controller): except exception.InstanceNotFound: msg = _("Instance could not be found") raise exc.HTTPNotFound(explanation=msg) - except exception.InvalidMetadata as error: - raise exc.HTTPBadRequest( - explanation=error.format_message()) except exception.InvalidMetadataSize as error: raise exc.HTTPRequestEntityTooLarge( explanation=error.format_message()) except exception.ImageNotFound: msg = _("Cannot find image for rebuild") raise exc.HTTPBadRequest(explanation=msg) - except exception.InstanceTypeMemoryTooSmall as error: - raise exc.HTTPBadRequest(explanation=error.format_message()) - except exception.InstanceTypeDiskTooSmall as error: + except (exception.InvalidMetadata, + exception.InstanceTypeMemoryTooSmall, + exception.InstanceTypeDiskTooSmall, + exception.ImageNotActive) as error: raise exc.HTTPBadRequest(explanation=error.format_message()) instance = self._get_server(context, req, id) diff --git a/nova/cmd/all.py b/nova/cmd/all.py index f510069b6..517033d05 100644 --- a/nova/cmd/all.py +++ b/nova/cmd/all.py @@ -49,19 +49,19 @@ def main(): logging.setup("nova") LOG = logging.getLogger('nova.all') utils.monkey_patch() - launcher = service.ProcessLauncher() + launcher = service.process_launcher() # nova-api for api in CONF.enabled_apis: try: server = service.WSGIService(api) - launcher.launch_server(server, workers=server.workers or 1) + launcher.launch_service(server, workers=server.workers or 1) except (Exception, SystemExit): LOG.exception(_('Failed to load %s') % '%s-api' % api) for mod in [s3server, xvp_proxy]: try: - launcher.launch_server(mod.get_wsgi_server()) + launcher.launch_service(mod.get_wsgi_server()) except (Exception, SystemExit): LOG.exception(_('Failed to load %s') % mod.__name__) @@ -82,8 +82,8 @@ def main(): manager = None try: - launcher.launch_server(service.Service.create(binary=binary, - topic=topic, + launcher.launch_service(service.Service.create(binary=binary, + topic=topic, manager=manager)) except (Exception, SystemExit): LOG.exception(_('Failed to load %s'), binary) diff --git a/nova/cmd/api.py b/nova/cmd/api.py index a7f6313b0..f838d988f 100644 --- a/nova/cmd/api.py +++ b/nova/cmd/api.py @@ -41,7 +41,7 @@ def main(): logging.setup("nova") utils.monkey_patch() - launcher = service.ProcessLauncher() + launcher = service.process_launcher() for api in CONF.enabled_apis: should_use_ssl = api in CONF.enabled_ssl_apis if api == 'ec2': @@ -49,5 +49,5 @@ def main(): max_url_len=16384) else: server = service.WSGIService(api, use_ssl=should_use_ssl) - launcher.launch_server(server, workers=server.workers or 1) + launcher.launch_service(server, workers=server.workers or 1) launcher.wait() diff --git a/nova/compute/api.py b/nova/compute/api.py index f31aefb9b..24d73ec0d 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -1757,25 +1757,13 @@ class API(base.Base): block_device_info=block_info, reboot_type=reboot_type) - def _get_image(self, context, image_href): - """Throws an ImageNotFound exception if image_href does not exist.""" - (image_service, image_id) = glance.get_remote_image_service(context, - image_href) - return image_service.show(context, image_id) - @wrap_check_policy @check_instance_lock @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED], task_state=[None]) def rebuild(self, context, instance, image_href, admin_password, **kwargs): """Rebuild the given instance with the provided attributes.""" - - if instance['image_ref']: - orig_image_ref = instance['image_ref'] - image = self._get_image(context, image_href) - else: - orig_image_ref = '' - image = {} + orig_image_ref = instance['image_ref'] or '' files_to_inject = kwargs.pop('files_to_inject', []) self._check_injected_file_quota(context, files_to_inject) @@ -1783,18 +1771,21 @@ class API(base.Base): metadata = kwargs.get('metadata', {}) self._check_metadata_properties_quota(context, metadata) + if image_href: + (image_service, image_id) = glance.get_remote_image_service( + context, image_href) + image = image_service.show(context, image_id) + if image['status'] != 'active': + raise exception.ImageNotActive(image_id=image_id) + else: + image = {} + instance_type = flavors.extract_instance_type(instance) if instance_type['memory_mb'] < int(image.get('min_ram') or 0): raise exception.InstanceTypeMemoryTooSmall() if instance_type['root_gb'] < int(image.get('min_disk') or 0): raise exception.InstanceTypeDiskTooSmall() - if image_href: - (image_service, image_id) = glance.get_remote_image_service( - context, image_href) - image = image_service.show(context, image_id) - else: - image = {} kernel_id, ramdisk_id = self._handle_kernel_and_ramdisk( context, None, None, image) @@ -2020,7 +2011,11 @@ class API(base.Base): raise exception.FlavorNotFound(flavor_id=flavor_id) # NOTE(markwash): look up the image early to avoid auth problems later - image = self.image_service.show(context, instance['image_ref']) + image_ref = instance.get('image_ref') + if image_ref: + image = self.image_service.show(context, image_ref) + else: + image = {} if same_instance_type and flavor_id: raise exception.CannotResizeToSameFlavor() diff --git a/nova/compute/cells_api.py b/nova/compute/cells_api.py index 7168313ec..4beeaeb3d 100644 --- a/nova/compute/cells_api.py +++ b/nova/compute/cells_api.py @@ -559,6 +559,16 @@ class ComputeCellsAPI(compute_api.API): pass return rv + @validate_cell + def live_migrate(self, context, instance, block_migration, + disk_over_commit, host_name): + """Migrate a server lively to a new host.""" + super(ComputeCellsAPI, self).live_migrate(context, + instance, block_migration, disk_over_commit, host_name) + + self._cast_to_cells(context, instance, 'live_migrate', + block_migration, disk_over_commit, host_name) + class HostAPI(compute_api.HostAPI): """HostAPI() class for cells. diff --git a/nova/compute/manager.py b/nova/compute/manager.py index b63ca10a1..91a814f98 100755 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -3188,6 +3188,7 @@ class ComputeManager(manager.SchedulerDependentManager): return self.driver.check_can_live_migrate_source(ctxt, instance, dest_check_data) + @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) def pre_live_migration(self, context, instance, block_migration=False, disk=None, migrate_data=None): @@ -3206,6 +3207,9 @@ class ComputeManager(manager.SchedulerDependentManager): context, instance, bdms=bdms) network_info = self._get_instance_nw_info(context, instance) + self._notify_about_instance_usage( + context, instance, "live_migration.pre.start", + network_info=network_info) # TODO(tr3buchet): figure out how on the earth this is necessary fixed_ips = network_info.fixed_ips() @@ -3236,8 +3240,13 @@ class ComputeManager(manager.SchedulerDependentManager): if block_migration: self.driver.pre_block_migration(context, instance, disk) + self._notify_about_instance_usage( + context, instance, "live_migration.pre.end", + network_info=network_info) + return pre_live_migration_data + @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) def live_migration(self, context, dest, instance, block_migration=False, migrate_data=None): """Executing live migration. @@ -3354,6 +3363,7 @@ class ComputeManager(manager.SchedulerDependentManager): "This error can be safely ignored."), instance=instance_ref) + @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) def post_live_migration_at_destination(self, context, instance, block_migration=False): """Post operations for live migration . @@ -3379,6 +3389,9 @@ class ComputeManager(manager.SchedulerDependentManager): migration) network_info = self._get_instance_nw_info(context, instance) + self._notify_about_instance_usage( + context, instance, "live_migration.post.dest.start", + network_info=network_info) block_device_info = self._get_instance_volume_block_device_info( context, instance) @@ -3402,6 +3415,9 @@ class ComputeManager(manager.SchedulerDependentManager): # NOTE(vish): this is necessary to update dhcp self.network_api.setup_networks_on_host(context, instance, self.host) + self._notify_about_instance_usage( + context, instance, "live_migration.post.dest.end", + network_info=network_info) def _rollback_live_migration(self, context, instance, dest, block_migration, migrate_data=None): @@ -3445,6 +3461,7 @@ class ComputeManager(manager.SchedulerDependentManager): self.compute_rpcapi.rollback_live_migration_at_destination(context, instance, dest) + @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) def rollback_live_migration_at_destination(self, context, instance): """Cleaning up image directory that is created pre_live_migration. @@ -3452,6 +3469,9 @@ class ComputeManager(manager.SchedulerDependentManager): :param instance: an Instance dict sent over rpc """ network_info = self._get_instance_nw_info(context, instance) + self._notify_about_instance_usage( + context, instance, "live_migration.rollback.dest.start", + network_info=network_info) # NOTE(tr3buchet): tear down networks on destination host self.network_api.setup_networks_on_host(context, instance, @@ -3463,6 +3483,9 @@ class ComputeManager(manager.SchedulerDependentManager): context, instance) self.driver.destroy(instance, self._legacy_nw_info(network_info), block_device_info) + self._notify_about_instance_usage( + context, instance, "live_migration.rollback.dest.end", + network_info=network_info) @periodic_task.periodic_task def _heal_instance_info_cache(self, context): diff --git a/nova/openstack/common/rpc/common.py b/nova/openstack/common/rpc/common.py index 4b2e5a71f..fcd954dd5 100644 --- a/nova/openstack/common/rpc/common.py +++ b/nova/openstack/common/rpc/common.py @@ -158,6 +158,10 @@ class UnsupportedRpcEnvelopeVersion(RPCException): "not supported by this endpoint.") +class RpcVersionCapError(RPCException): + message = _("Specified RPC version cap, %(version_cap)s, is too low") + + class Connection(object): """A connection, returned by rpc.create_connection(). diff --git a/nova/openstack/common/rpc/dispatcher.py b/nova/openstack/common/rpc/dispatcher.py index 3c84671df..81d16bcee 100644 --- a/nova/openstack/common/rpc/dispatcher.py +++ b/nova/openstack/common/rpc/dispatcher.py @@ -84,6 +84,7 @@ minimum version that supports the new parameter should be specified. """ from nova.openstack.common.rpc import common as rpc_common +from nova.openstack.common.rpc import serializer as rpc_serializer class RpcDispatcher(object): @@ -93,16 +94,38 @@ class RpcDispatcher(object): contains a list of underlying managers that have an API_VERSION attribute. """ - def __init__(self, callbacks): + def __init__(self, callbacks, serializer=None): """Initialize the rpc dispatcher. :param callbacks: List of proxy objects that are an instance of a class with rpc methods exposed. Each proxy object should have an RPC_API_VERSION attribute. + :param serializer: The Serializer object that will be used to + deserialize arguments before the method call and + to serialize the result after it returns. """ self.callbacks = callbacks + if serializer is None: + serializer = rpc_serializer.NoOpSerializer() + self.serializer = serializer super(RpcDispatcher, self).__init__() + def _deserialize_args(self, context, kwargs): + """Helper method called to deserialize args before dispatch. + + This calls our serializer on each argument, returning a new set of + args that have been deserialized. + + :param context: The request context + :param kwargs: The arguments to be deserialized + :returns: A new set of deserialized args + """ + new_kwargs = dict() + for argname, arg in kwargs.iteritems(): + new_kwargs[argname] = self.serializer.deserialize_entity(context, + arg) + return new_kwargs + def dispatch(self, ctxt, version, method, namespace, **kwargs): """Dispatch a message based on a requested version. @@ -145,7 +168,9 @@ class RpcDispatcher(object): if not hasattr(proxyobj, method): continue if is_compatible: - return getattr(proxyobj, method)(ctxt, **kwargs) + kwargs = self._deserialize_args(ctxt, kwargs) + result = getattr(proxyobj, method)(ctxt, **kwargs) + return self.serializer.serialize_entity(ctxt, result) if had_compatible: raise AttributeError("No such RPC function '%s'" % method) diff --git a/nova/openstack/common/rpc/proxy.py b/nova/openstack/common/rpc/proxy.py index daebcbfa7..dcdfc0864 100644 --- a/nova/openstack/common/rpc/proxy.py +++ b/nova/openstack/common/rpc/proxy.py @@ -1,6 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2012 Red Hat, Inc. +# Copyright 2012-2013 Red Hat, Inc. # # 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 @@ -23,6 +23,8 @@ For more information about rpc API version numbers, see: from nova.openstack.common import rpc +from nova.openstack.common.rpc import common as rpc_common +from nova.openstack.common.rpc import serializer as rpc_serializer class RpcProxy(object): @@ -34,16 +36,28 @@ class RpcProxy(object): rpc API. """ - def __init__(self, topic, default_version): + # The default namespace, which can be overriden in a subclass. + RPC_API_NAMESPACE = None + + def __init__(self, topic, default_version, version_cap=None, + serializer=None): """Initialize an RpcProxy. :param topic: The topic to use for all messages. :param default_version: The default API version to request in all outgoing messages. This can be overridden on a per-message basis. + :param version_cap: Optionally cap the maximum version used for sent + messages. + :param serializer: Optionaly (de-)serialize entities with a + provided helper. """ self.topic = topic self.default_version = default_version + self.version_cap = version_cap + if serializer is None: + serializer = rpc_serializer.NoOpSerializer() + self.serializer = serializer super(RpcProxy, self).__init__() def _set_version(self, msg, vers): @@ -52,7 +66,11 @@ class RpcProxy(object): :param msg: The message having a version added to it. :param vers: The version number to add to the message. """ - msg['version'] = vers if vers else self.default_version + v = vers if vers else self.default_version + if (self.version_cap and not + rpc_common.version_is_compatible(self.version_cap, v)): + raise rpc_common.RpcVersionCapError(version=self.version_cap) + msg['version'] = v def _get_topic(self, topic): """Return the topic to use for a message.""" @@ -62,9 +80,25 @@ class RpcProxy(object): def make_namespaced_msg(method, namespace, **kwargs): return {'method': method, 'namespace': namespace, 'args': kwargs} - @staticmethod - def make_msg(method, **kwargs): - return RpcProxy.make_namespaced_msg(method, None, **kwargs) + def make_msg(self, method, **kwargs): + return self.make_namespaced_msg(method, self.RPC_API_NAMESPACE, + **kwargs) + + def _serialize_msg_args(self, context, kwargs): + """Helper method called to serialize message arguments. + + This calls our serializer on each argument, returning a new + set of args that have been serialized. + + :param context: The request context + :param kwargs: The arguments to serialize + :returns: A new set of serialized arguments + """ + new_kwargs = dict() + for argname, arg in kwargs.iteritems(): + new_kwargs[argname] = self.serializer.serialize_entity(context, + arg) + return new_kwargs def call(self, context, msg, topic=None, version=None, timeout=None): """rpc.call() a remote method. @@ -81,9 +115,11 @@ class RpcProxy(object): :returns: The return value from the remote method. """ self._set_version(msg, version) + msg['args'] = self._serialize_msg_args(context, msg['args']) real_topic = self._get_topic(topic) try: - return rpc.call(context, real_topic, msg, timeout) + result = rpc.call(context, real_topic, msg, timeout) + return self.serializer.deserialize_entity(context, result) except rpc.common.Timeout as exc: raise rpc.common.Timeout( exc.info, real_topic, msg.get('method')) @@ -104,9 +140,11 @@ class RpcProxy(object): from the remote method as they arrive. """ self._set_version(msg, version) + msg['args'] = self._serialize_msg_args(context, msg['args']) real_topic = self._get_topic(topic) try: - return rpc.multicall(context, real_topic, msg, timeout) + result = rpc.multicall(context, real_topic, msg, timeout) + return self.serializer.deserialize_entity(context, result) except rpc.common.Timeout as exc: raise rpc.common.Timeout( exc.info, real_topic, msg.get('method')) @@ -124,6 +162,7 @@ class RpcProxy(object): remote method. """ self._set_version(msg, version) + msg['args'] = self._serialize_msg_args(context, msg['args']) rpc.cast(context, self._get_topic(topic), msg) def fanout_cast(self, context, msg, topic=None, version=None): @@ -139,6 +178,7 @@ class RpcProxy(object): from the remote method. """ self._set_version(msg, version) + msg['args'] = self._serialize_msg_args(context, msg['args']) rpc.fanout_cast(context, self._get_topic(topic), msg) def cast_to_server(self, context, server_params, msg, topic=None, @@ -157,6 +197,7 @@ class RpcProxy(object): return values. """ self._set_version(msg, version) + msg['args'] = self._serialize_msg_args(context, msg['args']) rpc.cast_to_server(context, server_params, self._get_topic(topic), msg) def fanout_cast_to_server(self, context, server_params, msg, topic=None, @@ -175,5 +216,6 @@ class RpcProxy(object): return values. """ self._set_version(msg, version) + msg['args'] = self._serialize_msg_args(context, msg['args']) rpc.fanout_cast_to_server(context, server_params, self._get_topic(topic), msg) diff --git a/nova/openstack/common/rpc/serializer.py b/nova/openstack/common/rpc/serializer.py new file mode 100644 index 000000000..0a2c9c4f1 --- /dev/null +++ b/nova/openstack/common/rpc/serializer.py @@ -0,0 +1,52 @@ +# Copyright 2013 IBM Corp. +# +# 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. + +"""Provides the definition of an RPC serialization handler""" + +import abc + + +class Serializer(object): + """Generic (de-)serialization definition base class""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def serialize_entity(self, context, entity): + """Serialize something to primitive form. + + :param context: Security context + :param entity: Entity to be serialized + :returns: Serialized form of entity + """ + pass + + @abc.abstractmethod + def deserialize_entity(self, context, entity): + """Deserialize something from primitive form. + + :param context: Security context + :param entity: Primitive to be deserialized + :returns: Deserialized form of entity + """ + pass + + +class NoOpSerializer(Serializer): + """A serializer that does nothing""" + + def serialize_entity(self, context, entity): + return entity + + def deserialize_entity(self, context, entity): + return entity diff --git a/nova/service.py b/nova/service.py index 12bab14c3..3731986c4 100644 --- a/nova/service.py +++ b/nova/service.py @@ -19,26 +19,20 @@ """Generic Node base class for all workers that run on hosts.""" -import errno import inspect import os import random -import signal import sys -import time -import eventlet -import greenlet from oslo.config import cfg from nova import conductor from nova import context from nova import exception -from nova.openstack.common import eventlet_backdoor from nova.openstack.common import importutils from nova.openstack.common import log as logging -from nova.openstack.common import loopingcall from nova.openstack.common import rpc +from nova.openstack.common import service from nova import servicegroup from nova import utils from nova import version @@ -119,275 +113,7 @@ CONF.register_opts(service_opts) CONF.import_opt('host', 'nova.netconf') -class SignalExit(SystemExit): - def __init__(self, signo, exccode=1): - super(SignalExit, self).__init__(exccode) - self.signo = signo - - -class Launcher(object): - """Launch one or more services and wait for them to complete.""" - - def __init__(self): - """Initialize the service launcher. - - :returns: None - - """ - self._services = [] - self.backdoor_port = eventlet_backdoor.initialize_if_enabled() - - @staticmethod - def run_server(server): - """Start and wait for a server to finish. - - :param service: Server to run and wait for. - :returns: None - - """ - server.start() - server.wait() - - def launch_server(self, server): - """Load and start the given server. - - :param server: The server you would like to start. - :returns: None - - """ - if self.backdoor_port is not None: - server.backdoor_port = self.backdoor_port - gt = eventlet.spawn(self.run_server, server) - self._services.append(gt) - - def stop(self): - """Stop all services which are currently running. - - :returns: None - - """ - for service in self._services: - service.kill() - - def wait(self): - """Waits until all services have been stopped, and then returns. - - :returns: None - - """ - for service in self._services: - try: - service.wait() - except greenlet.GreenletExit: - pass - - -class ServiceLauncher(Launcher): - def _handle_signal(self, signo, frame): - # Allow the process to be killed again and die from natural causes - signal.signal(signal.SIGTERM, signal.SIG_DFL) - signal.signal(signal.SIGINT, signal.SIG_DFL) - - raise SignalExit(signo) - - def wait(self): - signal.signal(signal.SIGTERM, self._handle_signal) - signal.signal(signal.SIGINT, self._handle_signal) - - LOG.debug(_('Full set of CONF:')) - for flag in CONF: - flag_get = CONF.get(flag, None) - # hide flag contents from log if contains a password - # should use secret flag when switch over to openstack-common - if ("_password" in flag or "_key" in flag or - (flag == "sql_connection" and "mysql:" in flag_get)): - LOG.debug(_('%(flag)s : FLAG SET ') % locals()) - else: - LOG.debug('%(flag)s : %(flag_get)s' % locals()) - - status = None - try: - super(ServiceLauncher, self).wait() - except SignalExit as exc: - signame = {signal.SIGTERM: 'SIGTERM', - signal.SIGINT: 'SIGINT'}[exc.signo] - LOG.info(_('Caught %s, exiting'), signame) - status = exc.code - except SystemExit as exc: - status = exc.code - finally: - self.stop() - rpc.cleanup() - - if status is not None: - sys.exit(status) - - -class ServerWrapper(object): - def __init__(self, server, workers): - self.server = server - self.workers = workers - self.children = set() - self.forktimes = [] - - -class ProcessLauncher(object): - def __init__(self): - self.children = {} - self.sigcaught = None - self.running = True - rfd, self.writepipe = os.pipe() - self.readpipe = eventlet.greenio.GreenPipe(rfd, 'r') - - signal.signal(signal.SIGTERM, self._handle_signal) - signal.signal(signal.SIGINT, self._handle_signal) - - def _handle_signal(self, signo, frame): - self.sigcaught = signo - self.running = False - - # Allow the process to be killed again and die from natural causes - signal.signal(signal.SIGTERM, signal.SIG_DFL) - signal.signal(signal.SIGINT, signal.SIG_DFL) - - def _pipe_watcher(self): - # This will block until the write end is closed when the parent - # dies unexpectedly - self.readpipe.read() - - LOG.info(_('Parent process has died unexpectedly, exiting')) - - sys.exit(1) - - def _child_process(self, server): - # Setup child signal handlers differently - def _sigterm(*args): - signal.signal(signal.SIGTERM, signal.SIG_DFL) - raise SignalExit(signal.SIGTERM) - - signal.signal(signal.SIGTERM, _sigterm) - # Block SIGINT and let the parent send us a SIGTERM - signal.signal(signal.SIGINT, signal.SIG_IGN) - - # Reopen the eventlet hub to make sure we don't share an epoll - # fd with parent and/or siblings, which would be bad - eventlet.hubs.use_hub() - - # Close write to ensure only parent has it open - os.close(self.writepipe) - # Create greenthread to watch for parent to close pipe - eventlet.spawn(self._pipe_watcher) - - # Reseed random number generator - random.seed() - - launcher = Launcher() - launcher.run_server(server) - - def _start_child(self, wrap): - if len(wrap.forktimes) > wrap.workers: - # Limit ourselves to one process a second (over the period of - # number of workers * 1 second). This will allow workers to - # start up quickly but ensure we don't fork off children that - # die instantly too quickly. - if time.time() - wrap.forktimes[0] < wrap.workers: - LOG.info(_('Forking too fast, sleeping')) - time.sleep(1) - - wrap.forktimes.pop(0) - - wrap.forktimes.append(time.time()) - - pid = os.fork() - if pid == 0: - # NOTE(johannes): All exceptions are caught to ensure this - # doesn't fallback into the loop spawning children. It would - # be bad for a child to spawn more children. - status = 0 - try: - self._child_process(wrap.server) - except SignalExit as exc: - signame = {signal.SIGTERM: 'SIGTERM', - signal.SIGINT: 'SIGINT'}[exc.signo] - LOG.info(_('Caught %s, exiting'), signame) - status = exc.code - except SystemExit as exc: - status = exc.code - except BaseException: - LOG.exception(_('Unhandled exception')) - status = 2 - finally: - wrap.server.stop() - - os._exit(status) - - LOG.info(_('Started child %d'), pid) - - wrap.children.add(pid) - self.children[pid] = wrap - - return pid - - def launch_server(self, server, workers=1): - wrap = ServerWrapper(server, workers) - - LOG.info(_('Starting %d workers'), wrap.workers) - while self.running and len(wrap.children) < wrap.workers: - self._start_child(wrap) - - def _wait_child(self): - try: - pid, status = os.wait() - except OSError as exc: - if exc.errno not in (errno.EINTR, errno.ECHILD): - raise - return None - - if os.WIFSIGNALED(status): - sig = os.WTERMSIG(status) - LOG.info(_('Child %(pid)d killed by signal %(sig)d'), locals()) - else: - code = os.WEXITSTATUS(status) - LOG.info(_('Child %(pid)d exited with status %(code)d'), locals()) - - if pid not in self.children: - LOG.warning(_('pid %d not in child list'), pid) - return None - - wrap = self.children.pop(pid) - wrap.children.remove(pid) - return wrap - - def wait(self): - """Loop waiting on children to die and respawning as necessary.""" - while self.running: - wrap = self._wait_child() - if not wrap: - continue - - while self.running and len(wrap.children) < wrap.workers: - self._start_child(wrap) - - if self.sigcaught: - signame = {signal.SIGTERM: 'SIGTERM', - signal.SIGINT: 'SIGINT'}[self.sigcaught] - LOG.info(_('Caught %s, stopping children'), signame) - - for pid in self.children: - try: - os.kill(pid, signal.SIGTERM) - except OSError as exc: - if exc.errno != errno.ESRCH: - raise - - # Wait for children to die - if self.children: - LOG.info(_('Waiting on %d children to exit'), len(self.children)) - while self.children: - self._wait_child() - - -class Service(object): +class Service(service.Service): """Service object for binaries running on hosts. A service takes a manager and enables rpc by listening to queues based @@ -398,6 +124,7 @@ class Service(object): periodic_enable=None, periodic_fuzzy_delay=None, periodic_interval_max=None, db_allowed=True, *args, **kwargs): + super(Service, self).__init__() self.host = host self.binary = binary self.topic = topic @@ -417,7 +144,6 @@ class Service(object): self.periodic_fuzzy_delay = periodic_fuzzy_delay self.periodic_interval_max = periodic_interval_max self.saved_args, self.saved_kwargs = args, kwargs - self.timers = [] self.backdoor_port = None self.conductor_api = conductor.API(use_local=db_allowed) self.conductor_api.wait_until_ready(context.get_admin_context()) @@ -464,9 +190,7 @@ class Service(object): LOG.debug(_("Join ServiceGroup membership for this service %s") % self.topic) # Add service to the ServiceGroup membership group. - pulse = self.servicegroup_api.join(self.host, self.topic, self) - if pulse: - self.timers.append(pulse) + self.servicegroup_api.join(self.host, self.topic, self) if self.periodic_enable: if self.periodic_fuzzy_delay: @@ -474,10 +198,10 @@ class Service(object): else: initial_delay = None - periodic = loopingcall.DynamicLoopingCall(self.periodic_tasks) - periodic.start(initial_delay=initial_delay, - periodic_interval_max=self.periodic_interval_max) - self.timers.append(periodic) + self.tg.add_dynamic_timer(self.periodic_tasks, + initial_delay=initial_delay, + periodic_interval_max= + self.periodic_interval_max) def _create_service_ref(self, context): svc_values = { @@ -546,25 +270,12 @@ class Service(object): LOG.warn(_('Service killed that has no database entry')) def stop(self): - # Try to shut the connection down, but if we get any sort of - # errors, go ahead and ignore them.. as we're shutting down anyway try: self.conn.close() except Exception: pass - for x in self.timers: - try: - x.stop() - except Exception: - pass - self.timers = [] - def wait(self): - for x in self.timers: - try: - x.wait() - except Exception: - pass + super(Service, self).stop() def periodic_tasks(self, raise_on_error=False): """Tasks to be run at a periodic interval.""" @@ -667,6 +378,10 @@ class WSGIService(object): self.server.wait() +def process_launcher(): + return service.ProcessLauncher() + + # NOTE(vish): the global launcher is to maintain the existing # functionality of calling service.serve + # service.wait @@ -678,12 +393,7 @@ def serve(server, workers=None): if _launcher: raise RuntimeError(_('serve() can only be called once')) - if workers: - _launcher = ProcessLauncher() - _launcher.launch_server(server, workers=workers) - else: - _launcher = ServiceLauncher() - _launcher.launch_server(server) + _launcher = service.launch(server, workers=workers) def wait(): diff --git a/nova/servicegroup/drivers/db.py b/nova/servicegroup/drivers/db.py index 24fcfd04f..22b25d61c 100644 --- a/nova/servicegroup/drivers/db.py +++ b/nova/servicegroup/drivers/db.py @@ -18,7 +18,6 @@ from oslo.config import cfg from nova import conductor from nova import context from nova.openstack.common import log as logging -from nova.openstack.common import loopingcall from nova.openstack.common import timeutils from nova.servicegroup import api from nova import utils @@ -47,11 +46,8 @@ class DbDriver(api.ServiceGroupDriver): ' ServiceGroup driver')) report_interval = service.report_interval if report_interval: - pulse = loopingcall.FixedIntervalLoopingCall(self._report_state, - service) - pulse.start(interval=report_interval, - initial_delay=report_interval) - return pulse + service.tg.add_timer(report_interval, self._report_state, + report_interval, service) def is_up(self, service_ref): """Moved from nova.utils diff --git a/nova/servicegroup/drivers/mc.py b/nova/servicegroup/drivers/mc.py index 86c27a3aa..6e8cda456 100644 --- a/nova/servicegroup/drivers/mc.py +++ b/nova/servicegroup/drivers/mc.py @@ -22,7 +22,6 @@ from oslo.config import cfg from nova import conductor from nova import context from nova.openstack.common import log as logging -from nova.openstack.common import loopingcall from nova.openstack.common import memorycache from nova.openstack.common import timeutils from nova.servicegroup import api @@ -58,11 +57,8 @@ class MemcachedDriver(api.ServiceGroupDriver): 'Memcached based ServiceGroup driver')) report_interval = service.report_interval if report_interval: - pulse = loopingcall.FixedIntervalLoopingCall(self._report_state, - service) - pulse.start(interval=report_interval, - initial_delay=report_interval) - return pulse + service.tg.add_timer(report_interval, self._report_state, + report_interval, service) def is_up(self, service_ref): """Moved from nova.utils diff --git a/nova/tests/api/openstack/compute/test_servers.py b/nova/tests/api/openstack/compute/test_servers.py index 22aecf020..993451263 100644 --- a/nova/tests/api/openstack/compute/test_servers.py +++ b/nova/tests/api/openstack/compute/test_servers.py @@ -1402,8 +1402,9 @@ class ServersControllerTest(test.TestCase): name='public image', is_public=True, status='active', properties={'key1': 'value1'}, min_ram="4096", min_disk="10") - self.stubs.Set(compute_api.API, '_get_image', - fake_get_image) + + self.stubs.Set(fake._FakeImageService, 'show', fake_get_image) + self.stubs.Set(db, 'instance_get_by_uuid', fakes.fake_instance_get(vm_state=vm_states.ACTIVE)) image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' @@ -1429,8 +1430,35 @@ class ServersControllerTest(test.TestCase): name='public image', is_public=True, status='active', properties={'key1': 'value1'}, min_ram="128", min_disk="100000") - self.stubs.Set(compute_api.API, '_get_image', - fake_get_image) + + self.stubs.Set(fake._FakeImageService, 'show', fake_get_image) + + self.stubs.Set(db, 'instance_get_by_uuid', + fakes.fake_instance_get(vm_state=vm_states.ACTIVE)) + image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' + image_href = 'http://localhost/v2/fake/images/%s' % image_uuid + body = { + 'rebuild': { + 'name': 'new_name', + 'imageRef': image_href, + }, + } + + req = fakes.HTTPRequest.blank('/v2/fake/servers/a/action') + req.method = 'POST' + req.body = jsonutils.dumps(body) + req.headers["content-type"] = "application/json" + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller._action_rebuild, req, FAKE_UUID, body) + + def test_rebuild_instance_with_deleted_image(self): + def fake_get_image(self, context, image_href): + return dict(id='76fa36fc-c930-4bf3-8c8a-ea2a2420deb6', + name='public image', is_public=True, + status='DELETED') + + self.stubs.Set(fake._FakeImageService, 'show', fake_get_image) + self.stubs.Set(db, 'instance_get_by_uuid', fakes.fake_instance_get(vm_state=vm_states.ACTIVE)) image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6' diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index 3101e3aa2..933d40d59 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -3493,6 +3493,7 @@ class ComputeTestCase(BaseTestCase): self.compute.driver.ensure_filtering_rules_for_instance( mox.IsA(instance), nw_info) + test_notifier.NOTIFICATIONS = [] # start test self.mox.ReplayAll() migrate_data = {'is_shared_storage': False} @@ -3500,6 +3501,13 @@ class ComputeTestCase(BaseTestCase): block_migration=False, migrate_data=migrate_data) self.assertEqual(ret, None) + self.assertEqual(len(test_notifier.NOTIFICATIONS), 2) + msg = test_notifier.NOTIFICATIONS[0] + self.assertEqual(msg['event_type'], + 'compute.instance.live_migration.pre.start') + msg = test_notifier.NOTIFICATIONS[1] + self.assertEqual(msg['event_type'], + 'compute.instance.live_migration.pre.end') # cleanup db.instance_destroy(c, instance['uuid']) @@ -3719,11 +3727,20 @@ class ComputeTestCase(BaseTestCase): self.compute.network_api.setup_networks_on_host(self.admin_ctxt, mox.IgnoreArg(), self.compute.host) + test_notifier.NOTIFICATIONS = [] self.mox.ReplayAll() self.compute.post_live_migration_at_destination(self.admin_ctxt, self.instance) + self.assertEqual(len(test_notifier.NOTIFICATIONS), 2) + msg = test_notifier.NOTIFICATIONS[0] + self.assertEqual(msg['event_type'], + 'compute.instance.live_migration.post.dest.start') + msg = test_notifier.NOTIFICATIONS[1] + self.assertEqual(msg['event_type'], + 'compute.instance.live_migration.post.dest.end') + return self.compute.conductor_api.instance_get_by_uuid(self.admin_ctxt, self.instance['uuid']) @@ -3749,6 +3766,31 @@ class ComputeTestCase(BaseTestCase): updated = self._finish_post_live_migration_at_destination() self.assertIsNone(updated['node']) + def test_rollback_live_migration_at_destination_correctly(self): + # creating instance testdata + c = context.get_admin_context() + instance_ref = self._create_fake_instance({'host': 'dummy'}) + inst_uuid = instance_ref['uuid'] + inst_id = instance_ref['id'] + + instance = jsonutils.to_primitive(db.instance_get(c, inst_id)) + test_notifier.NOTIFICATIONS = [] + # start test + self.mox.ReplayAll() + ret = self.compute.rollback_live_migration_at_destination(c, + instance=instance) + self.assertEqual(ret, None) + self.assertEqual(len(test_notifier.NOTIFICATIONS), 2) + msg = test_notifier.NOTIFICATIONS[0] + self.assertEqual(msg['event_type'], + 'compute.instance.live_migration.rollback.dest.start') + msg = test_notifier.NOTIFICATIONS[1] + self.assertEqual(msg['event_type'], + 'compute.instance.live_migration.rollback.dest.end') + + # cleanup + db.instance_destroy(c, inst_uuid) + def test_run_kill_vm(self): # Detect when a vm is terminated behind the scenes. self.stubs.Set(compute_manager.ComputeManager, @@ -5501,6 +5543,105 @@ class ComputeAPITestCase(BaseTestCase): instance = db.instance_get_by_uuid(self.context, instance_uuid) self.assertEqual(instance['task_state'], task_states.REBUILDING) + def test_rebuild_with_deleted_image(self): + # If we're given a deleted image by glance, we should not be able to + # rebuild from it + instance = jsonutils.to_primitive( + self._create_fake_instance(params={'image_ref': '1'})) + + self.fake_image['name'] = 'fake_name' + self.fake_image['status'] = 'DELETED' + self.stubs.Set(fake_image._FakeImageService, 'show', self.fake_show) + + expected_message = ( + exception.ImageNotActive.message % {'image_id': + self.fake_image['id']}) + with testtools.ExpectedException(exception.ImageNotActive, + expected_message): + self.compute_api.rebuild(self.context, instance, + self.fake_image['id'], 'new_password') + + def test_rebuild_with_too_little_ram(self): + instance = jsonutils.to_primitive( + self._create_fake_instance(params={'image_ref': '1'})) + + def fake_extract_instance_type(_inst): + return dict(memory_mb=64, root_gb=1) + + self.stubs.Set(flavors, 'extract_instance_type', + fake_extract_instance_type) + + self.fake_image['min_ram'] = 128 + self.stubs.Set(fake_image._FakeImageService, 'show', self.fake_show) + + self.assertRaises(exception.InstanceTypeMemoryTooSmall, + self.compute_api.rebuild, self.context, + instance, self.fake_image['id'], 'new_password') + + # Reduce image memory requirements and make sure it works + self.fake_image['min_ram'] = 64 + + self.compute_api.rebuild(self.context, + instance, self.fake_image['id'], 'new_password') + db.instance_destroy(self.context, instance['uuid']) + + def test_rebuild_with_too_little_disk(self): + instance = jsonutils.to_primitive( + self._create_fake_instance(params={'image_ref': '1'})) + + def fake_extract_instance_type(_inst): + return dict(memory_mb=64, root_gb=1) + + self.stubs.Set(flavors, 'extract_instance_type', + fake_extract_instance_type) + + self.fake_image['min_disk'] = 2 + self.stubs.Set(fake_image._FakeImageService, 'show', self.fake_show) + + self.assertRaises(exception.InstanceTypeDiskTooSmall, + self.compute_api.rebuild, self.context, + instance, self.fake_image['id'], 'new_password') + + # Reduce image disk requirements and make sure it works + self.fake_image['min_disk'] = 1 + + self.compute_api.rebuild(self.context, + instance, self.fake_image['id'], 'new_password') + db.instance_destroy(self.context, instance['uuid']) + + def test_rebuild_with_just_enough_ram_and_disk(self): + instance = jsonutils.to_primitive( + self._create_fake_instance(params={'image_ref': '1'})) + + def fake_extract_instance_type(_inst): + return dict(memory_mb=64, root_gb=1) + + self.stubs.Set(flavors, 'extract_instance_type', + fake_extract_instance_type) + + self.fake_image['min_ram'] = 64 + self.fake_image['min_disk'] = 1 + self.stubs.Set(fake_image._FakeImageService, 'show', self.fake_show) + + self.compute_api.rebuild(self.context, + instance, self.fake_image['id'], 'new_password') + db.instance_destroy(self.context, instance['uuid']) + + def test_rebuild_with_no_ram_and_disk_reqs(self): + instance = jsonutils.to_primitive( + self._create_fake_instance(params={'image_ref': '1'})) + + def fake_extract_instance_type(_inst): + return dict(memory_mb=64, root_gb=1) + + self.stubs.Set(flavors, 'extract_instance_type', + fake_extract_instance_type) + self.stubs.Set(fake_image._FakeImageService, 'show', self.fake_show) + + self.compute_api.rebuild(self.context, + instance, self.fake_image['id'], 'new_password') + db.instance_destroy(self.context, instance['uuid']) + def _stub_out_reboot(self, device_name): def fake_reboot_instance(rpcapi, context, instance, block_device_info, @@ -6155,6 +6296,22 @@ class ComputeAPITestCase(BaseTestCase): self.context, instance) self.compute.terminate_instance(self.context, instance=instance) + def test_resize_no_image(self): + def _fake_prep_resize(_context, **args): + image = args['image'] + self.assertEqual(image, {}) + + instance = self._create_fake_instance(params={'image_ref': ''}) + instance = db.instance_get_by_uuid(self.context, instance['uuid']) + instance = jsonutils.to_primitive(instance) + self.compute.run_instance(self.context, instance=instance) + + self.stubs.Set(self.compute_api.scheduler_rpcapi, + 'prep_resize', _fake_prep_resize) + + self.compute_api.resize(self.context, instance, None) + self.compute.terminate_instance(self.context, instance=instance) + def test_migrate(self): instance = self._create_fake_instance() instance = db.instance_get_by_uuid(self.context, instance['uuid']) diff --git a/nova/tests/compute/test_compute_cells.py b/nova/tests/compute/test_compute_cells.py index 40ae4e3de..ad4e6c754 100644 --- a/nova/tests/compute/test_compute_cells.py +++ b/nova/tests/compute/test_compute_cells.py @@ -145,9 +145,6 @@ class CellsComputeAPITestCase(test_compute.ComputeAPITestCase): def test_instance_metadata(self): self.skipTest("Test is incompatible with cells.") - def test_live_migrate(self): - self.skipTest("Test is incompatible with cells.") - def test_snapshot_given_image_uuid(self): self.skipTest("Test doesn't apply to API cell.") diff --git a/nova/tests/integrated/test_api_samples.py b/nova/tests/integrated/test_api_samples.py index cb2d14289..9b678ddd3 100644 --- a/nova/tests/integrated/test_api_samples.py +++ b/nova/tests/integrated/test_api_samples.py @@ -52,12 +52,12 @@ from nova.tests.api.openstack.compute.contrib import test_fping from nova.tests.api.openstack.compute.contrib import test_networks from nova.tests.api.openstack.compute.contrib import test_services from nova.tests.api.openstack import fakes -from nova.tests.baremetal.db import base as bm_db_base from nova.tests import fake_instance_actions from nova.tests import fake_network from nova.tests.image import fake from nova.tests.integrated import integrated_helpers from nova.tests import utils as test_utils +from nova.tests.virt.baremetal.db import base as bm_db_base from nova import utils from nova.volume import cinder diff --git a/nova/tests/integrated/test_multiprocess_api.py b/nova/tests/integrated/test_multiprocess_api.py index c85a43a0c..2610cdcb7 100644 --- a/nova/tests/integrated/test_multiprocess_api.py +++ b/nova/tests/integrated/test_multiprocess_api.py @@ -54,8 +54,8 @@ class MultiprocessWSGITest(integrated_helpers._IntegratedTestBase): # os._exit() which doesn't have this problem. status = 0 try: - launcher = service.ProcessLauncher() - launcher.launch_server(self.osapi, workers=self.osapi.workers) + launcher = service.process_launcher() + launcher.launch_service(self.osapi, workers=self.osapi.workers) launcher.wait() except SystemExit as exc: status = exc.code diff --git a/nova/tests/test_migrations.py b/nova/tests/test_migrations.py index de5eb1b40..688db500d 100644 --- a/nova/tests/test_migrations.py +++ b/nova/tests/test_migrations.py @@ -1339,7 +1339,7 @@ class TestBaremetalMigrations(BaseMigrationTestCase, CommonTestsMixIn): super(TestBaremetalMigrations, self).__init__(*args, **kwargs) self.DEFAULT_CONFIG_FILE = os.path.join(os.path.dirname(__file__), - 'test_baremetal_migrations.conf') + 'virt/baremetal/test_baremetal_migrations.conf') # Test machines can set the NOVA_TEST_MIGRATIONS_CONF variable # to override the location of the config file for migration testing self.CONFIG_FILE_PATH = os.environ.get( diff --git a/nova/tests/test_service.py b/nova/tests/test_service.py index 2cbc82fda..3ca6d7bc1 100644 --- a/nova/tests/test_service.py +++ b/nova/tests/test_service.py @@ -201,7 +201,6 @@ class TestLauncher(test.TestCase): self.service = service.WSGIService("test_service") def test_launch_app(self): - launcher = service.Launcher() - launcher.launch_server(self.service) + service.serve(self.service) self.assertNotEquals(0, self.service.port) - launcher.stop() + service._launcher.stop() diff --git a/nova/tests/test_virt_drivers.py b/nova/tests/test_virt_drivers.py index 395663600..c054b9624 100644 --- a/nova/tests/test_virt_drivers.py +++ b/nova/tests/test_virt_drivers.py @@ -25,9 +25,9 @@ from nova import exception from nova.openstack.common import importutils from nova.openstack.common import log as logging from nova import test -from nova.tests import fake_libvirt_utils from nova.tests.image import fake as fake_image from nova.tests import utils as test_utils +from nova.tests.virt.libvirt import fake_libvirt_utils from nova.virt import event as virtevent from nova.virt import fake @@ -67,9 +67,9 @@ class _FakeDriverBackendTestCase(object): else: self.saved_libvirt = None - import nova.tests.fake_imagebackend as fake_imagebackend - import nova.tests.fake_libvirt_utils as fake_libvirt_utils - import nova.tests.fakelibvirt as fakelibvirt + import nova.tests.virt.libvirt.fake_imagebackend as fake_imagebackend + import nova.tests.virt.libvirt.fake_libvirt_utils as fake_libvirt_utils + import nova.tests.virt.libvirt.fakelibvirt as fakelibvirt sys.modules['libvirt'] = fakelibvirt import nova.virt.libvirt.driver diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 45ebfaab3..2628f2582 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -2117,8 +2117,9 @@ class XenAPIBWCountersTestCase(stubs.XenAPITestBase): # TODO(salvatore-orlando): this class and -# nova.tests.test_libvirt.IPTablesFirewallDriverTestCase share a lot of code. -# Consider abstracting common code in a base class for firewall driver testing. +# nova.tests.virt.test_libvirt.IPTablesFirewallDriverTestCase share a lot of +# code. Consider abstracting common code in a base class for firewall driver +# testing. class XenAPIDom0IptablesFirewallTestCase(stubs.XenAPITestBase): _in_rules = [ diff --git a/nova/tests/baremetal/__init__.py b/nova/tests/virt/baremetal/__init__.py index f15d84efc..34b09daac 100644 --- a/nova/tests/baremetal/__init__.py +++ b/nova/tests/virt/baremetal/__init__.py @@ -12,4 +12,4 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from nova.tests.baremetal import * +from nova.tests.virt.baremetal import * diff --git a/nova/tests/baremetal/db/__init__.py b/nova/tests/virt/baremetal/db/__init__.py index 543dfc1ae..0f1f4e846 100644 --- a/nova/tests/baremetal/db/__init__.py +++ b/nova/tests/virt/baremetal/db/__init__.py @@ -12,4 +12,4 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -from nova.tests.baremetal.db import * +from nova.tests.virt.baremetal.db import * diff --git a/nova/tests/baremetal/db/base.py b/nova/tests/virt/baremetal/db/base.py index 499eee32a..499eee32a 100644 --- a/nova/tests/baremetal/db/base.py +++ b/nova/tests/virt/baremetal/db/base.py diff --git a/nova/tests/baremetal/db/test_bm_interface.py b/nova/tests/virt/baremetal/db/test_bm_interface.py index e870ec5e0..24b6ae1e1 100644 --- a/nova/tests/baremetal/db/test_bm_interface.py +++ b/nova/tests/virt/baremetal/db/test_bm_interface.py @@ -19,7 +19,7 @@ Bare-metal DB testcase for BareMetalInterface from nova import exception from nova.openstack.common.db import exception as db_exc -from nova.tests.baremetal.db import base +from nova.tests.virt.baremetal.db import base from nova.virt.baremetal import db diff --git a/nova/tests/baremetal/db/test_bm_node.py b/nova/tests/virt/baremetal/db/test_bm_node.py index 204a6bf7b..0acd5a994 100644 --- a/nova/tests/baremetal/db/test_bm_node.py +++ b/nova/tests/virt/baremetal/db/test_bm_node.py @@ -18,8 +18,8 @@ Bare-Metal DB testcase for BareMetalNode """ from nova import exception -from nova.tests.baremetal.db import base -from nova.tests.baremetal.db import utils +from nova.tests.virt.baremetal.db import base +from nova.tests.virt.baremetal.db import utils from nova.virt.baremetal import db diff --git a/nova/tests/baremetal/db/test_bm_pxe_ip.py b/nova/tests/virt/baremetal/db/test_bm_pxe_ip.py index fe8ba5b3e..85f3e2f4b 100644 --- a/nova/tests/baremetal/db/test_bm_pxe_ip.py +++ b/nova/tests/virt/baremetal/db/test_bm_pxe_ip.py @@ -19,8 +19,8 @@ Bare-metal DB testcase for BareMetalPxeIp from nova import exception from nova.openstack.common.db import exception as db_exc -from nova.tests.baremetal.db import base -from nova.tests.baremetal.db import utils +from nova.tests.virt.baremetal.db import base +from nova.tests.virt.baremetal.db import utils from nova.virt.baremetal import db diff --git a/nova/tests/baremetal/db/utils.py b/nova/tests/virt/baremetal/db/utils.py index c3b3cff5f..c3b3cff5f 100644 --- a/nova/tests/baremetal/db/utils.py +++ b/nova/tests/virt/baremetal/db/utils.py diff --git a/nova/tests/test_baremetal_migrations.conf b/nova/tests/virt/baremetal/test_baremetal_migrations.conf index 774f14994..774f14994 100644 --- a/nova/tests/test_baremetal_migrations.conf +++ b/nova/tests/virt/baremetal/test_baremetal_migrations.conf diff --git a/nova/tests/baremetal/test_driver.py b/nova/tests/virt/baremetal/test_driver.py index 3b0295a92..0564faf1e 100644 --- a/nova/tests/baremetal/test_driver.py +++ b/nova/tests/virt/baremetal/test_driver.py @@ -25,10 +25,10 @@ from oslo.config import cfg from nova.compute import power_state from nova import exception from nova import test -from nova.tests.baremetal.db import base as bm_db_base -from nova.tests.baremetal.db import utils as bm_db_utils from nova.tests.image import fake as fake_image from nova.tests import utils +from nova.tests.virt.baremetal.db import base as bm_db_base +from nova.tests.virt.baremetal.db import utils as bm_db_utils from nova.virt.baremetal import baremetal_states from nova.virt.baremetal import db from nova.virt.baremetal import driver as bm_driver diff --git a/nova/tests/baremetal/test_ipmi.py b/nova/tests/virt/baremetal/test_ipmi.py index 01bb58d8b..ba7a875cf 100644 --- a/nova/tests/baremetal/test_ipmi.py +++ b/nova/tests/virt/baremetal/test_ipmi.py @@ -26,7 +26,7 @@ import tempfile from oslo.config import cfg from nova import test -from nova.tests.baremetal.db import utils as bm_db_utils +from nova.tests.virt.baremetal.db import utils as bm_db_utils from nova import utils from nova.virt.baremetal import baremetal_states from nova.virt.baremetal import ipmi diff --git a/nova/tests/baremetal/test_nova_baremetal_deploy_helper.py b/nova/tests/virt/baremetal/test_nova_baremetal_deploy_helper.py index 42ee6c8e4..28f40bcf9 100644 --- a/nova/tests/baremetal/test_nova_baremetal_deploy_helper.py +++ b/nova/tests/virt/baremetal/test_nova_baremetal_deploy_helper.py @@ -25,7 +25,7 @@ import mox from nova.cmd import baremetal_deploy_helper as bmdh from nova.openstack.common import log as logging from nova import test -from nova.tests.baremetal.db import base as bm_db_base +from nova.tests.virt.baremetal.db import base as bm_db_base from nova.virt.baremetal import db as bm_db bmdh.LOG = logging.getLogger('nova.virt.baremetal.deploy_helper') diff --git a/nova/tests/baremetal/test_nova_baremetal_manage.py b/nova/tests/virt/baremetal/test_nova_baremetal_manage.py index 6be63aac2..6651e6ad4 100644 --- a/nova/tests/baremetal/test_nova_baremetal_manage.py +++ b/nova/tests/virt/baremetal/test_nova_baremetal_manage.py @@ -17,7 +17,7 @@ # under the License. from nova.cmd import baremetal_manage as bm_man -from nova.tests.baremetal.db import base as bm_db_base +from nova.tests.virt.baremetal.db import base as bm_db_base class BareMetalDbCommandsTestCase(bm_db_base.BMDBTestCase): diff --git a/nova/tests/baremetal/test_pxe.py b/nova/tests/virt/baremetal/test_pxe.py index 994a8ee35..022f9c692 100644 --- a/nova/tests/baremetal/test_pxe.py +++ b/nova/tests/virt/baremetal/test_pxe.py @@ -28,10 +28,10 @@ from testtools import matchers from nova import exception from nova.openstack.common.db import exception as db_exc -from nova.tests.baremetal.db import base as bm_db_base -from nova.tests.baremetal.db import utils as bm_db_utils from nova.tests.image import fake as fake_image from nova.tests import utils +from nova.tests.virt.baremetal.db import base as bm_db_base +from nova.tests.virt.baremetal.db import utils as bm_db_utils from nova.virt.baremetal import baremetal_states from nova.virt.baremetal import db from nova.virt.baremetal import pxe diff --git a/nova/tests/baremetal/test_tilera.py b/nova/tests/virt/baremetal/test_tilera.py index a30732a3d..488cba4df 100755 --- a/nova/tests/baremetal/test_tilera.py +++ b/nova/tests/virt/baremetal/test_tilera.py @@ -24,10 +24,10 @@ from oslo.config import cfg from nova import exception from nova.openstack.common.db import exception as db_exc -from nova.tests.baremetal.db import base as bm_db_base -from nova.tests.baremetal.db import utils as bm_db_utils from nova.tests.image import fake as fake_image from nova.tests import utils +from nova.tests.virt.baremetal.db import base as bm_db_base +from nova.tests.virt.baremetal.db import utils as bm_db_utils from nova.virt.baremetal import baremetal_states from nova.virt.baremetal import db from nova.virt.baremetal import tilera diff --git a/nova/tests/baremetal/test_tilera_pdu.py b/nova/tests/virt/baremetal/test_tilera_pdu.py index fee5bc49e..95d840574 100755 --- a/nova/tests/baremetal/test_tilera_pdu.py +++ b/nova/tests/virt/baremetal/test_tilera_pdu.py @@ -21,7 +21,7 @@ from oslo.config import cfg from nova import test -from nova.tests.baremetal.db import utils as bm_db_utils +from nova.tests.virt.baremetal.db import utils as bm_db_utils from nova import utils from nova.virt.baremetal import baremetal_states from nova.virt.baremetal import tilera_pdu diff --git a/nova/tests/baremetal/test_utils.py b/nova/tests/virt/baremetal/test_utils.py index df5112deb..df5112deb 100644 --- a/nova/tests/baremetal/test_utils.py +++ b/nova/tests/virt/baremetal/test_utils.py diff --git a/nova/tests/baremetal/test_virtual_power_driver.py b/nova/tests/virt/baremetal/test_virtual_power_driver.py index 5f4b5f0cb..58a1e4f08 100644 --- a/nova/tests/baremetal/test_virtual_power_driver.py +++ b/nova/tests/virt/baremetal/test_virtual_power_driver.py @@ -23,10 +23,10 @@ from oslo.config import cfg from nova import exception from nova.openstack.common import processutils -from nova.tests.baremetal.db import base as bm_db_base -from nova.tests.baremetal.db import utils as bm_db_utils from nova.tests.image import fake as fake_image from nova.tests import utils +from nova.tests.virt.baremetal.db import base as bm_db_base +from nova.tests.virt.baremetal.db import utils as bm_db_utils from nova.virt.baremetal import db from nova.virt.baremetal import virtual_power_driver import nova.virt.powervm.common as connection diff --git a/nova/tests/baremetal/test_volume_driver.py b/nova/tests/virt/baremetal/test_volume_driver.py index 24dadac94..24dadac94 100644 --- a/nova/tests/baremetal/test_volume_driver.py +++ b/nova/tests/virt/baremetal/test_volume_driver.py diff --git a/nova/tests/virt/libvirt/__init__.py b/nova/tests/virt/libvirt/__init__.py new file mode 100644 index 000000000..a02a20177 --- /dev/null +++ b/nova/tests/virt/libvirt/__init__.py @@ -0,0 +1,15 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2013 OpenStack Foundation +# +# 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. diff --git a/nova/tests/fake_imagebackend.py b/nova/tests/virt/libvirt/fake_imagebackend.py index 48426505e..48426505e 100644 --- a/nova/tests/fake_imagebackend.py +++ b/nova/tests/virt/libvirt/fake_imagebackend.py diff --git a/nova/tests/fake_libvirt_utils.py b/nova/tests/virt/libvirt/fake_libvirt_utils.py index ba00a7091..ba00a7091 100644 --- a/nova/tests/fake_libvirt_utils.py +++ b/nova/tests/virt/libvirt/fake_libvirt_utils.py diff --git a/nova/tests/fakelibvirt.py b/nova/tests/virt/libvirt/fakelibvirt.py index 30c3e4d9c..30c3e4d9c 100644 --- a/nova/tests/fakelibvirt.py +++ b/nova/tests/virt/libvirt/fakelibvirt.py diff --git a/nova/tests/test_fakelibvirt.py b/nova/tests/virt/libvirt/test_fakelibvirt.py index ee2008544..52183ab5f 100644 --- a/nova/tests/test_fakelibvirt.py +++ b/nova/tests/virt/libvirt/test_fakelibvirt.py @@ -18,7 +18,7 @@ from nova import test from lxml import etree -import nova.tests.fakelibvirt as libvirt +import nova.tests.virt.libvirt.fakelibvirt as libvirt def get_vm_xml(name="testname", uuid=None, source_type='file', diff --git a/nova/tests/test_imagebackend.py b/nova/tests/virt/libvirt/test_imagebackend.py index b5d64a218..6548ab76c 100644 --- a/nova/tests/test_imagebackend.py +++ b/nova/tests/virt/libvirt/test_imagebackend.py @@ -23,8 +23,8 @@ from oslo.config import cfg from nova import exception from nova.openstack.common import uuidutils from nova import test -from nova.tests import fake_libvirt_utils from nova.tests import fake_processutils +from nova.tests.virt.libvirt import fake_libvirt_utils from nova.virt.libvirt import imagebackend CONF = cfg.CONF diff --git a/nova/tests/test_imagecache.py b/nova/tests/virt/libvirt/test_imagecache.py index bdc895474..bdc895474 100644 --- a/nova/tests/test_imagecache.py +++ b/nova/tests/virt/libvirt/test_imagecache.py diff --git a/nova/tests/test_libvirt.py b/nova/tests/virt/libvirt/test_libvirt.py index 338a1559f..f72c37cd1 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/virt/libvirt/test_libvirt.py @@ -45,10 +45,10 @@ from nova.openstack.common import jsonutils from nova.openstack.common import loopingcall from nova.openstack.common import uuidutils from nova import test -from nova.tests import fake_libvirt_utils from nova.tests import fake_network import nova.tests.image.fake from nova.tests import matchers +from nova.tests.virt.libvirt import fake_libvirt_utils from nova import utils from nova import version from nova.virt.disk import api as disk @@ -68,7 +68,7 @@ from nova.virt import netutils try: import libvirt except ImportError: - import nova.tests.fakelibvirt as libvirt + import nova.tests.virt.libvirt.fakelibvirt as libvirt libvirt_driver.libvirt = libvirt @@ -351,7 +351,8 @@ class LibvirtConnTestCase(test.TestCase): return FakeVirtDomain() # Creating mocks - volume_driver = 'iscsi=nova.tests.test_libvirt.FakeVolumeDriver' + volume_driver = ('iscsi=nova.tests.virt.libvirt.test_libvirt' + '.FakeVolumeDriver') self.flags(libvirt_volume_drivers=[volume_driver]) fake = FakeLibvirtDriver() # Customizing above fake if necessary diff --git a/nova/tests/test_libvirt_blockinfo.py b/nova/tests/virt/libvirt/test_libvirt_blockinfo.py index aae5bec58..aae5bec58 100644 --- a/nova/tests/test_libvirt_blockinfo.py +++ b/nova/tests/virt/libvirt/test_libvirt_blockinfo.py diff --git a/nova/tests/test_libvirt_config.py b/nova/tests/virt/libvirt/test_libvirt_config.py index 8eed7136e..8eed7136e 100644 --- a/nova/tests/test_libvirt_config.py +++ b/nova/tests/virt/libvirt/test_libvirt_config.py diff --git a/nova/tests/test_libvirt_utils.py b/nova/tests/virt/libvirt/test_libvirt_utils.py index 60f0682a8..60f0682a8 100644 --- a/nova/tests/test_libvirt_utils.py +++ b/nova/tests/virt/libvirt/test_libvirt_utils.py diff --git a/nova/tests/test_libvirt_vif.py b/nova/tests/virt/libvirt/test_libvirt_vif.py index 0b661d147..c9c6aef12 100644 --- a/nova/tests/test_libvirt_vif.py +++ b/nova/tests/virt/libvirt/test_libvirt_vif.py @@ -20,7 +20,7 @@ from oslo.config import cfg from nova import exception from nova.network import model as network_model from nova import test -from nova.tests import fakelibvirt +from nova.tests.virt.libvirt import fakelibvirt from nova import utils from nova.virt.libvirt import config as vconfig from nova.virt.libvirt import vif diff --git a/nova/tests/test_libvirt_volume.py b/nova/tests/virt/libvirt/test_libvirt_volume.py index 6d2dc9dce..86773dd10 100644 --- a/nova/tests/test_libvirt_volume.py +++ b/nova/tests/virt/libvirt/test_libvirt_volume.py @@ -23,7 +23,7 @@ from oslo.config import cfg from nova import exception from nova.storage import linuxscsi from nova import test -from nova.tests import fake_libvirt_utils +from nova.tests.virt.libvirt import fake_libvirt_utils from nova import utils from nova.virt import fake from nova.virt.libvirt import utils as libvirt_utils diff --git a/openstack-common.conf b/openstack-common.conf index 87be18491..086796b12 100644 --- a/openstack-common.conf +++ b/openstack-common.conf @@ -17,6 +17,7 @@ module=local module=lockutils module=log module=loopingcall +module=memorycache module=network_utils module=notifier module=patch_tox_venv |