diff --git a/nova/cells/manager.py b/nova/cells/manager.py index c096def970..d19f11a4b5 100644 --- a/nova/cells/manager.py +++ b/nova/cells/manager.py @@ -76,7 +76,7 @@ class CellsManager(manager.Manager): Scheduling requests get passed to the scheduler class. """ - target = oslo_messaging.Target(version='1.34') + target = oslo_messaging.Target(version='1.35') def __init__(self, *args, **kwargs): LOG.warning(_LW('The cells feature of Nova is considered experimental ' @@ -179,7 +179,7 @@ class CellsManager(manager.Manager): if not instance_uuid: return try: - instance = self.db.instance_get_by_uuid(rd_context, + instance = objects.Instance.get_by_uuid(rd_context, instance_uuid) except exception.InstanceNotFound: continue @@ -190,7 +190,7 @@ class CellsManager(manager.Manager): """Broadcast an instance_update or instance_destroy message up to parent cells. """ - if instance['deleted']: + if instance.deleted: self.instance_destroy_at_top(ctxt, instance) else: self.instance_update_at_top(ctxt, instance) @@ -428,11 +428,11 @@ class CellsManager(manager.Manager): def validate_console_port(self, ctxt, instance_uuid, console_port, console_type): """Validate console port with child cell compute node.""" - instance = self.db.instance_get_by_uuid(ctxt, instance_uuid) - if not instance['cell_name']: + instance = objects.Instance.get_by_uuid(ctxt, instance_uuid) + if not instance.cell_name: raise exception.InstanceUnknownCell(instance_uuid=instance_uuid) response = self.msg_runner.validate_console_port(ctxt, - instance['cell_name'], instance_uuid, console_port, + instance.cell_name, instance_uuid, console_port, console_type) return response.value_or_raise() diff --git a/nova/cells/messaging.py b/nova/cells/messaging.py index ad68d8f914..178c2f9f06 100644 --- a/nova/cells/messaging.py +++ b/nova/cells/messaging.py @@ -53,7 +53,6 @@ from nova import context from nova.db import base from nova import exception from nova.i18n import _, _LE, _LI, _LW -from nova.network import model as network_model from nova import objects from nova.objects import base as objects_base from nova import rpc @@ -683,7 +682,8 @@ class _TargetedMessageMethods(_BaseMessageMethods): # Must be a race condition. Let's try to resolve it by # telling the top level cells that this instance doesn't # exist. - instance = {'uuid': instance_uuid} + instance = objects.Instance(context=message.ctxt, + uuid=instance_uuid) self.msg_runner.instance_destroy_at_top(message.ctxt, instance) return fn(message.ctxt, *args, **method_info['method_kwargs']) @@ -782,14 +782,15 @@ class _TargetedMessageMethods(_BaseMessageMethods): # 1st arg is instance_uuid that we need to turn into the # instance object. try: - instance = self.db.instance_get_by_uuid(message.ctxt, + instance = objects.Instance.get_by_uuid(message.ctxt, instance_uuid) except exception.InstanceNotFound: with excutils.save_and_reraise_exception(): # Must be a race condition. Let's try to resolve it by # telling the top level cells that this instance doesn't # exist. - instance = {'uuid': instance_uuid} + instance = objects.Instance(context=message.ctxt, + uuid=instance_uuid) self.msg_runner.instance_destroy_at_top(message.ctxt, instance) return self.compute_rpcapi.validate_console_port(message.ctxt, @@ -826,7 +827,8 @@ class _TargetedMessageMethods(_BaseMessageMethods): # Must be a race condition. Let's try to resolve it by # telling the top level cells that this instance doesn't # exist. - instance = {'uuid': instance.uuid} + instance = objects.Instance(context=ctxt, + uuid=instance.uuid) self.msg_runner.instance_destroy_at_top(ctxt, instance) except exception.InstanceInfoCacheNotFound: @@ -953,11 +955,10 @@ class _BroadcastMessageMethods(_BaseMessageMethods): """Are we the API level?""" return not self.state_manager.get_parent_cells() - def _apply_expected_states(self, instance_info): + def _get_expected_vm_state(self, instance): """To attempt to address out-of-order messages, do some sanity - checking on the VM and task states. Add some requirements for - vm_state and task_state to the instance_update() DB call if - necessary. + checking on the VM states. Add some requirements for + vm_state to the instance.save() call if necessary. """ expected_vm_state_map = { # For updates containing 'vm_state' of 'building', @@ -967,6 +968,14 @@ class _BroadcastMessageMethods(_BaseMessageMethods): # start out in 'building' anyway.. but just in case. vm_states.BUILDING: [vm_states.BUILDING, None]} + if instance.obj_attr_is_set('vm_state'): + return expected_vm_state_map.get(instance.vm_state) + + def _get_expected_task_state(self, instance): + """To attempt to address out-of-order messages, do some sanity + checking on the task states. Add some requirements for + task_state to the instance.save() call if necessary. + """ expected_task_state_map = { # Always allow updates when task_state doesn't change, # but also make sure we don't set resize/rebuild task @@ -994,20 +1003,13 @@ class _BroadcastMessageMethods(_BaseMessageMethods): task_states.RESIZE_MIGRATING, task_states.RESIZE_PREP]} - if 'vm_state' in instance_info: - expected = expected_vm_state_map.get(instance_info['vm_state']) - if expected is not None: - instance_info['expected_vm_state'] = expected - if 'task_state' in instance_info: - expected = expected_task_state_map.get(instance_info['task_state']) - if expected is not None: - instance_info['expected_task_state'] = expected + if instance.obj_attr_is_set('task_state'): + return expected_task_state_map.get(instance.task_state) def instance_update_at_top(self, message, instance, **kwargs): """Update an instance in the DB if we're a top level cell.""" if not self._at_the_top(): return - instance_uuid = instance['uuid'] # Remove things that we can't update in the top level cells. # 'metadata' is only updated in the API cell, so don't overwrite @@ -1015,63 +1017,40 @@ class _BroadcastMessageMethods(_BaseMessageMethods): # 'cell_name' based on the routing path. items_to_remove = ['id', 'security_groups', 'volumes', 'cell_name', 'name', 'metadata'] - for key in items_to_remove: - instance.pop(key, None) - instance['cell_name'] = _reverse_path(message.routing_path) - - # Fixup info_cache. We'll have to update this separately if - # it exists. - info_cache = instance.pop('info_cache', None) - if info_cache is not None: - info_cache.pop('id', None) - info_cache.pop('instance', None) - - if 'system_metadata' in instance: - # Make sure we have the dict form that we need for - # instance_update. - instance['system_metadata'] = utils.instance_sys_meta(instance) + instance.obj_reset_changes(items_to_remove) + instance.cell_name = _reverse_path(message.routing_path) LOG.debug("Got update for instance: %(instance)s", - {'instance': instance}, instance_uuid=instance_uuid) + {'instance': instance}, instance_uuid=instance.uuid) - self._apply_expected_states(instance) + expected_vm_state = self._get_expected_vm_state(instance) + expected_task_state = self._get_expected_task_state(instance) # It's possible due to some weird condition that the instance # was already set as deleted... so we'll attempt to update # it with permissions that allows us to read deleted. with utils.temporary_mutation(message.ctxt, read_deleted="yes"): try: - self.db.instance_update(message.ctxt, instance_uuid, - instance, update_cells=False) - except exception.NotFound: + with instance.skip_cells_sync(): + instance.save(expected_vm_state=expected_vm_state, + expected_task_state=expected_task_state) + except exception.InstanceNotFound: # FIXME(comstud): Strange. Need to handle quotas here, # if we actually want this code to remain.. - self.db.instance_create(message.ctxt, instance) - if info_cache: - network_info = info_cache.get('network_info') - if isinstance(network_info, list): - if not isinstance(network_info, network_model.NetworkInfo): - network_info = network_model.NetworkInfo.hydrate( - network_info) - info_cache['network_info'] = network_info.json() - try: - self.db.instance_info_cache_update( - message.ctxt, instance_uuid, info_cache) - except exception.InstanceInfoCacheNotFound: + instance.create() + except exception.NotFound: # Can happen if we try to update a deleted instance's - # network information. + # network information, for example. pass def instance_destroy_at_top(self, message, instance, **kwargs): """Destroy an instance from the DB if we're a top level cell.""" if not self._at_the_top(): return - instance_uuid = instance['uuid'] LOG.debug("Got update to delete instance", - instance_uuid=instance_uuid) + instance_uuid=instance.uuid) try: - self.db.instance_destroy(message.ctxt, instance_uuid, - update_cells=False) + instance.destroy() except exception.InstanceNotFound: pass @@ -1108,7 +1087,7 @@ class _BroadcastMessageMethods(_BaseMessageMethods): self.db.bw_usage_update(message.ctxt, **bw_update_info) def _sync_instance(self, ctxt, instance): - if instance['deleted']: + if instance.deleted: self.msg_runner.instance_destroy_at_top(ctxt, instance) else: self.msg_runner.instance_update_at_top(ctxt, instance) diff --git a/nova/cells/rpcapi.py b/nova/cells/rpcapi.py index 13f4a0feeb..5b5b8a4f4b 100644 --- a/nova/cells/rpcapi.py +++ b/nova/cells/rpcapi.py @@ -30,6 +30,7 @@ from oslo_serialization import jsonutils from nova import exception from nova.i18n import _LE +from nova import objects from nova.objects import base as objects_base from nova import rpc @@ -112,6 +113,9 @@ class CellsAPI(object): ... Kilo supports message version 1.34. So, any changes to existing methods in 1.x after that point should be done such that they can handle the version_cap being set to 1.34. + + * 1.35 - Make instance_update_at_top, instance_destroy_at_top + and instance_info_cache_update_at_top use instance objects ''' VERSION_ALIASES = { @@ -191,16 +195,23 @@ class CellsAPI(object): """Update instance at API level.""" if not CONF.cells.enable: return - # Make sure we have a dict, not a SQLAlchemy model - instance_p = jsonutils.to_primitive(instance) - self.client.cast(ctxt, 'instance_update_at_top', instance=instance_p) + version = '1.35' + if not self.client.can_send_version('1.35'): + instance = objects_base.obj_to_primitive(instance) + version = '1.34' + cctxt = self.client.prepare(version=version) + cctxt.cast(ctxt, 'instance_update_at_top', instance=instance) def instance_destroy_at_top(self, ctxt, instance): """Destroy instance at API level.""" if not CONF.cells.enable: return - instance_p = jsonutils.to_primitive(instance) - self.client.cast(ctxt, 'instance_destroy_at_top', instance=instance_p) + version = '1.35' + if not self.client.can_send_version('1.35'): + instance = objects_base.obj_to_primitive(instance) + version = '1.34' + cctxt = self.client.prepare(version=version) + cctxt.cast(ctxt, 'instance_destroy_at_top', instance=instance) def instance_delete_everywhere(self, ctxt, instance, delete_type): """Delete instance everywhere. delete_type may be 'soft' @@ -246,10 +257,14 @@ class CellsAPI(object): """Broadcast up that an instance's info_cache has changed.""" if not CONF.cells.enable: return - iicache = jsonutils.to_primitive(instance_info_cache) - instance = {'uuid': iicache['instance_uuid'], - 'info_cache': iicache} - self.client.cast(ctxt, 'instance_update_at_top', instance=instance) + version = '1.35' + instance = objects.Instance(uuid=instance_info_cache.instance_uuid, + info_cache=instance_info_cache) + if not self.client.can_send_version('1.35'): + instance = objects_base.obj_to_primitive(instance) + version = '1.34' + cctxt = self.client.prepare(version=version) + cctxt.cast(ctxt, 'instance_update_at_top', instance=instance) def get_cell_info_for_neighbors(self, ctxt): """Get information about our neighbor cells from the manager.""" diff --git a/nova/cells/scheduler.py b/nova/cells/scheduler.py index be1beac14a..f625cd7a89 100644 --- a/nova/cells/scheduler.py +++ b/nova/cells/scheduler.py @@ -121,8 +121,7 @@ class CellsScheduler(base.Base): num_instances, i) instances.append(instance) - instance_p = obj_base.obj_to_primitive(instance) - self.msg_runner.instance_update_at_top(ctxt, instance_p) + self.msg_runner.instance_update_at_top(ctxt, instance) return instances def _create_action_here(self, ctxt, instance_uuids): @@ -247,12 +246,11 @@ class CellsScheduler(base.Base): {'instance_uuids': instance_uuids}) ctxt = message.ctxt for instance_uuid in instance_uuids: - self.msg_runner.instance_update_at_top(ctxt, - {'uuid': instance_uuid, - 'vm_state': vm_states.ERROR}) + instance = objects.Instance(context=ctxt, uuid=instance_uuid, + vm_state=vm_states.ERROR) + self.msg_runner.instance_update_at_top(ctxt, instance) try: - self.db.instance_update(ctxt, - instance_uuid, - {'vm_state': vm_states.ERROR}) + instance.vm_state = vm_states.ERROR + instance.save() except Exception: pass diff --git a/nova/cells/utils.py b/nova/cells/utils.py index 03b3e09ad0..9c975bac9b 100644 --- a/nova/cells/utils.py +++ b/nova/cells/utils.py @@ -21,7 +21,6 @@ import sys import six -from nova import db from nova import exception from nova import objects from nova.objects import base as obj_base @@ -141,13 +140,15 @@ def get_instances_to_sync(context, updated_since=None, project_id=None, if not deleted: filters['deleted'] = False # Active instances first. - instances = db.instance_get_all_by_filters( - context, filters, 'deleted', 'asc') + instances = objects.InstanceList.get_by_filters( + context, filters, sort_key='deleted', sort_dir='asc') if shuffle: + # NOTE(melwitt): Need a list that supports assignment for shuffle. + instances = [instance for instance in instances] random.shuffle(instances) for instance in instances: if uuids_only: - yield instance['uuid'] + yield instance.uuid else: yield instance diff --git a/nova/conductor/manager.py b/nova/conductor/manager.py index d06d621a4a..3a8acef0f9 100644 --- a/nova/conductor/manager.py +++ b/nova/conductor/manager.py @@ -40,7 +40,6 @@ from nova import image from nova import manager from nova import network from nova.network.security_group import openstack_driver -from nova import notifications from nova import objects from nova.objects import base as nova_object from nova import quota @@ -121,17 +120,11 @@ class ConductorManager(manager.Manager): if key in datetime_fields and isinstance(value, six.string_types): updates[key] = timeutils.parse_strtime(value) - # NOTE(danms): the send_update() call below is going to want to know - # about the flavor, so we need to join the appropriate things here, - # and objectify the result. - old_ref, instance_ref = self.db.instance_update_and_get_original( - context, instance_uuid, updates, - columns_to_join=['system_metadata']) - inst_obj = objects.Instance._from_db_object( - context, objects.Instance(), - instance_ref, expected_attrs=['system_metadata']) - notifications.send_update(context, old_ref, inst_obj, service) - return jsonutils.to_primitive(instance_ref) + instance = objects.Instance(context=context, uuid=instance_uuid, + **updates) + instance.obj_reset_changes(['uuid']) + instance.save() + return nova_object.obj_to_primitive(instance) # NOTE(hanlind): This can be removed in version 3.0 of the RPC API @messaging.expected_exceptions(exception.InstanceNotFound) @@ -244,8 +237,12 @@ class ConductorManager(manager.Manager): # NOTE(hanlind): This can be removed in version 3.0 of the RPC API def instance_destroy(self, context, instance): - result = self.db.instance_destroy(context, instance['uuid']) - return jsonutils.to_primitive(result) + if not isinstance(instance, objects.Instance): + instance = objects.Instance._from_db_object(context, + objects.Instance(), + instance) + instance.destroy() + return nova_object.obj_to_primitive(instance) # NOTE(hanlind): This can be removed in version 3.0 of the RPC API def instance_fault_create(self, context, values): diff --git a/nova/db/api.py b/nova/db/api.py index de913c951b..e6abad35a6 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -633,16 +633,9 @@ def instance_create(context, values): return IMPL.instance_create(context, values) -def instance_destroy(context, instance_uuid, constraint=None, - update_cells=True): +def instance_destroy(context, instance_uuid, constraint=None): """Destroy the instance or raise if it does not exist.""" - rv = IMPL.instance_destroy(context, instance_uuid, constraint) - if update_cells: - try: - cells_rpcapi.CellsAPI().instance_destroy_at_top(context, rv) - except Exception: - LOG.exception(_LE("Failed to notify cells of instance destroy")) - return rv + return IMPL.instance_destroy(context, instance_uuid, constraint) def instance_get_by_uuid(context, uuid, columns_to_join=None, use_slave=False): @@ -737,26 +730,16 @@ def instance_get_all_hung_in_rebooting(context, reboot_window): return IMPL.instance_get_all_hung_in_rebooting(context, reboot_window) -def instance_update(context, instance_uuid, values, update_cells=True): +def instance_update(context, instance_uuid, values): """Set the given properties on an instance and update it. Raises NotFound if instance does not exist. """ - rv = IMPL.instance_update(context, instance_uuid, values) - if update_cells: - try: - cells_rpcapi.CellsAPI().instance_update_at_top(context, rv) - except Exception: - LOG.exception(_LE("Failed to notify cells of instance update")) - return rv + return IMPL.instance_update(context, instance_uuid, values) -# FIXME(comstud): 'update_cells' is temporary as we transition to using -# objects. When everything is using Instance.save(), we can remove the -# argument and the RPC to nova-cells. def instance_update_and_get_original(context, instance_uuid, values, - update_cells=True, columns_to_join=None): """Set the given properties on an instance and update it. Return a shallow copy of the original instance reference, as well as the @@ -772,11 +755,6 @@ def instance_update_and_get_original(context, instance_uuid, values, """ rv = IMPL.instance_update_and_get_original(context, instance_uuid, values, columns_to_join=columns_to_join) - if update_cells: - try: - cells_rpcapi.CellsAPI().instance_update_at_top(context, rv[1]) - except Exception: - LOG.exception(_LE("Failed to notify cells of instance update")) return rv diff --git a/nova/objects/instance.py b/nova/objects/instance.py index 0b9e918b8e..0a6dc7e0bd 100644 --- a/nova/objects/instance.py +++ b/nova/objects/instance.py @@ -657,6 +657,10 @@ class Instance(base.NovaPersistentObject, base.NovaObject, else: constraint = None + cell_type = cells_opts.get_cell_type() + if cell_type is not None: + stale_instance = self.obj_clone() + try: db_inst = db.instance_destroy(self._context, self.uuid, constraint=constraint) @@ -664,6 +668,9 @@ class Instance(base.NovaPersistentObject, base.NovaObject, except exception.ConstraintNotMet: raise exception.ObjectActionError(action='destroy', reason='host changed') + if cell_type == 'compute': + cells_api = cells_rpcapi.CellsAPI() + cells_api.instance_destroy_at_top(self._context, stale_instance) delattr(self, base.get_attrname('id')) def _save_info_cache(self, context): @@ -791,7 +798,8 @@ class Instance(base.NovaPersistentObject, base.NovaObject, context = self._context cell_type = cells_opts.get_cell_type() - if cell_type == 'api' and self.cell_name: + + if cell_type is not None: # NOTE(comstud): We need to stash a copy of ourselves # before any updates are applied. When we call the save # methods on nested objects, we will lose any changes to @@ -803,15 +811,16 @@ class Instance(base.NovaPersistentObject, base.NovaObject, # authoritative for their view of vm_state and task_state. stale_instance = self.obj_clone() + cells_update_from_api = (cell_type == 'api' and self.cell_name and + self._sync_cells) + + if cells_update_from_api: def _handle_cell_update_from_api(): - if self._sync_cells: - cells_api = cells_rpcapi.CellsAPI() - cells_api.instance_update_from_api(context, stale_instance, + cells_api = cells_rpcapi.CellsAPI() + cells_api.instance_update_from_api(context, stale_instance, expected_vm_state, expected_task_state, admin_state_reset) - else: - stale_instance = None self._maybe_upgrade_flavor() updates = {} @@ -836,7 +845,7 @@ class Instance(base.NovaPersistentObject, base.NovaObject, updates[field] = self[field] if not updates: - if stale_instance: + if cells_update_from_api: _handle_cell_update_from_api() return @@ -874,24 +883,23 @@ class Instance(base.NovaPersistentObject, base.NovaObject, expected_attrs.append('system_metadata') expected_attrs.append('flavor') old_ref, inst_ref = db.instance_update_and_get_original( - context, self.uuid, updates, update_cells=False, + context, self.uuid, updates, columns_to_join=_expected_cols(expected_attrs)) self._from_db_object(context, self, inst_ref, expected_attrs=expected_attrs) - # NOTE(danms): We have to be super careful here not to trigger - # any lazy-loads that will unmigrate or unbackport something. So, - # make a copy of the instance for notifications first. - new_ref = self.obj_clone() - - if stale_instance: + if cells_update_from_api: _handle_cell_update_from_api() elif cell_type == 'compute': if self._sync_cells: cells_api = cells_rpcapi.CellsAPI() - cells_api.instance_update_at_top(context, - base.obj_to_primitive(new_ref)) + cells_api.instance_update_at_top(context, stale_instance) + + # NOTE(danms): We have to be super careful here not to trigger + # any lazy-loads that will unmigrate or unbackport something. So, + # make a copy of the instance for notifications first. + new_ref = self.obj_clone() notifications.send_update(context, old_ref, new_ref) self.obj_reset_changes() diff --git a/nova/objects/instance_info_cache.py b/nova/objects/instance_info_cache.py index 5ec3b1bc1e..b1e5424898 100644 --- a/nova/objects/instance_info_cache.py +++ b/nova/objects/instance_info_cache.py @@ -88,14 +88,18 @@ class InstanceInfoCache(base.NovaPersistentObject, base.NovaObject, @base.remotable def save(self, update_cells=True): if 'network_info' in self.obj_what_changed(): + if update_cells: + stale_instance = self.obj_clone() nw_info_json = self.fields['network_info'].to_primitive( self, 'network_info', self.network_info) rv = db.instance_info_cache_update(self._context, self.instance_uuid, {'network_info': nw_info_json}) self._from_db_object(self._context, self, rv) - if update_cells and rv: - self._info_cache_cells_update(self._context, rv) + if update_cells: + # Send a copy of ourselves before updates are applied so + # that cells can tell what changed. + self._info_cache_cells_update(self._context, stale_instance) self.obj_reset_changes() @base.remotable diff --git a/nova/scheduler/utils.py b/nova/scheduler/utils.py index df6be247a6..d8d27c207e 100644 --- a/nova/scheduler/utils.py +++ b/nova/scheduler/utils.py @@ -27,7 +27,6 @@ from nova.compute import flavors from nova.compute import utils as compute_utils from nova import exception from nova.i18n import _, _LE, _LW -from nova import notifications from nova import objects from nova.objects import base as obj_base from nova.objects import instance as instance_obj @@ -92,19 +91,12 @@ def set_vm_state_and_notify(context, instance_uuid, service, method, updates, LOG.warning(_LW('Setting instance to %s state.'), state, instance_uuid=instance_uuid) - # update instance state and notify on the transition - # NOTE(hanlind): the send_update() call below is going to want to - # know about the flavor, so we need to join the appropriate things - # here and objectify the results. - (old_ref, new_ref) = db.instance_update_and_get_original( - context, instance_uuid, updates, - columns_to_join=['system_metadata']) - inst_obj = objects.Instance._from_db_object( - context, objects.Instance(), new_ref, - expected_attrs=['system_metadata']) - notifications.send_update(context, old_ref, inst_obj, service=service) + instance = objects.Instance(context=context, uuid=instance_uuid, + **updates) + instance.obj_reset_changes(['uuid']) + instance.save() compute_utils.add_instance_fault_from_exc(context, - inst_obj, ex, sys.exc_info()) + instance, ex, sys.exc_info()) payload = dict(request_spec=request_spec, instance_properties=properties, diff --git a/nova/tests/unit/api/openstack/compute/contrib/test_multiple_create.py b/nova/tests/unit/api/openstack/compute/contrib/test_multiple_create.py index f76d70cdfd..b6fad96fc0 100644 --- a/nova/tests/unit/api/openstack/compute/contrib/test_multiple_create.py +++ b/nova/tests/unit/api/openstack/compute/contrib/test_multiple_create.py @@ -110,7 +110,7 @@ class MultiCreateExtensionTestV21(test.TestCase): instance.update(values) return instance - def server_update(context, instance_uuid, params, update_cells=True, + def server_update(context, instance_uuid, params, columns_to_join=None): inst = self.instance_cache_by_uuid[instance_uuid] inst.update(params) diff --git a/nova/tests/unit/api/openstack/compute/plugins/v3/test_servers.py b/nova/tests/unit/api/openstack/compute/plugins/v3/test_servers.py index a5f3af56df..c28f6c3dec 100644 --- a/nova/tests/unit/api/openstack/compute/plugins/v3/test_servers.py +++ b/nova/tests/unit/api/openstack/compute/plugins/v3/test_servers.py @@ -85,7 +85,6 @@ def return_servers_empty(context, *args, **kwargs): def instance_update_and_get_original(context, instance_uuid, values, - update_cells=True, columns_to_join=None, ): inst = fakes.stub_instance(INSTANCE_IDS.get(instance_uuid), @@ -94,7 +93,7 @@ def instance_update_and_get_original(context, instance_uuid, values, return (inst, inst) -def instance_update(context, instance_uuid, values, update_cells=True): +def instance_update(context, instance_uuid, values): inst = fakes.stub_instance(INSTANCE_IDS.get(instance_uuid), name=values.get('display_name')) inst = dict(inst, **values) @@ -1938,8 +1937,7 @@ class ServersControllerCreateTest(test.TestCase): return instance def server_update_and_get_original( - context, instance_uuid, params, update_cells=False, - columns_to_join=None): + context, instance_uuid, params, columns_to_join=None): inst = self.instance_cache_by_uuid[instance_uuid] inst.update(params) return (inst, inst) diff --git a/nova/tests/unit/api/openstack/compute/test_server_actions.py b/nova/tests/unit/api/openstack/compute/test_server_actions.py index 90b5420c06..30d15465e2 100644 --- a/nova/tests/unit/api/openstack/compute/test_server_actions.py +++ b/nova/tests/unit/api/openstack/compute/test_server_actions.py @@ -51,7 +51,6 @@ def return_server_not_found(*arg, **kwarg): def instance_update_and_get_original(context, instance_uuid, values, - update_cells=True, columns_to_join=None, ): inst = fakes.stub_instance(INSTANCE_IDS[instance_uuid], host='fake_host') @@ -59,7 +58,7 @@ def instance_update_and_get_original(context, instance_uuid, values, return (inst, inst) -def instance_update(context, instance_uuid, kwargs, update_cells=True): +def instance_update(context, instance_uuid, kwargs): inst = fakes.stub_instance(INSTANCE_IDS[instance_uuid], host='fake_host') return inst diff --git a/nova/tests/unit/api/openstack/compute/test_servers.py b/nova/tests/unit/api/openstack/compute/test_servers.py index faa058975c..60082f3b35 100644 --- a/nova/tests/unit/api/openstack/compute/test_servers.py +++ b/nova/tests/unit/api/openstack/compute/test_servers.py @@ -89,7 +89,6 @@ def return_security_group(context, instance_id, security_group_id): def instance_update_and_get_original(context, instance_uuid, values, - update_cells=True, columns_to_join=None, ): inst = fakes.stub_instance(INSTANCE_IDS.get(instance_uuid), @@ -98,7 +97,7 @@ def instance_update_and_get_original(context, instance_uuid, values, return (inst, inst) -def instance_update(context, instance_uuid, values, update_cells=True): +def instance_update(context, instance_uuid, values): inst = fakes.stub_instance(INSTANCE_IDS.get(instance_uuid), name=values.get('display_name')) inst = dict(inst, **values) @@ -1900,8 +1899,7 @@ class ServersControllerCreateTest(test.TestCase): return instance def server_update_and_get_original( - context, instance_uuid, params, update_cells=False, - columns_to_join=None): + context, instance_uuid, params, columns_to_join=None): inst = self.instance_cache_by_uuid[instance_uuid] inst.update(params) return (inst, inst) diff --git a/nova/tests/unit/cells/test_cells_manager.py b/nova/tests/unit/cells/test_cells_manager.py index f4077871a0..d4439d3bda 100644 --- a/nova/tests/unit/cells/test_cells_manager.py +++ b/nova/tests/unit/cells/test_cells_manager.py @@ -230,6 +230,7 @@ class CellsManagerClassTestCase(test.NoDBTestCase): call_info['get_instances'] += 1 return iter(instances) + @staticmethod def instance_get_by_uuid(context, uuid): return instances[int(uuid[-1]) - 1] @@ -239,7 +240,7 @@ class CellsManagerClassTestCase(test.NoDBTestCase): self.stubs.Set(cells_utils, 'get_instances_to_sync', get_instances_to_sync) - self.stubs.Set(self.cells_manager.db, 'instance_get_by_uuid', + self.stubs.Set(objects.Instance, 'get_by_uuid', instance_get_by_uuid) self.stubs.Set(self.cells_manager, '_sync_instance', sync_instance) @@ -608,17 +609,16 @@ class CellsManagerClassTestCase(test.NoDBTestCase): def test_validate_console_port(self): instance_uuid = 'fake-instance-uuid' cell_name = 'fake-cell-name' - instance = {'cell_name': cell_name} + instance = objects.Instance(cell_name=cell_name) console_port = 'fake-console-port' console_type = 'fake-console-type' self.mox.StubOutWithMock(self.msg_runner, 'validate_console_port') - self.mox.StubOutWithMock(self.cells_manager.db, - 'instance_get_by_uuid') + self.mox.StubOutWithMock(objects.Instance, 'get_by_uuid') fake_response = self._get_fake_response() - self.cells_manager.db.instance_get_by_uuid(self.ctxt, + objects.Instance.get_by_uuid(self.ctxt, instance_uuid).AndReturn(instance) self.msg_runner.validate_console_port(self.ctxt, cell_name, instance_uuid, console_port, diff --git a/nova/tests/unit/cells/test_cells_messaging.py b/nova/tests/unit/cells/test_cells_messaging.py index 626f88809b..df7f92b565 100644 --- a/nova/tests/unit/cells/test_cells_messaging.py +++ b/nova/tests/unit/cells/test_cells_messaging.py @@ -36,7 +36,6 @@ from nova.compute import vm_states from nova import context from nova import db from nova import exception -from nova.network import model as network_model from nova import objects from nova.objects import base as objects_base from nova.objects import fields as objects_fields @@ -740,7 +739,7 @@ class CellsTargetedMethodsTestCase(test.TestCase): 'security_groups', 'info_cache']).AndRaise( exception.InstanceNotFound(instance_id=instance_uuid)) self.tgt_msg_runner.instance_destroy_at_top(self.ctxt, - {'uuid': instance.uuid}) + mox.IsA(objects.Instance)) self.mox.ReplayAll() @@ -1011,25 +1010,24 @@ class CellsTargetedMethodsTestCase(test.TestCase): def test_validate_console_port(self): instance_uuid = 'fake_instance_uuid' - instance = {'uuid': instance_uuid} + instance = objects.Instance(uuid=instance_uuid) console_port = 'fake-port' console_type = 'fake-type' - self.mox.StubOutWithMock(self.tgt_c_rpcapi, 'validate_console_port') - self.mox.StubOutWithMock(self.tgt_db_inst, 'instance_get_by_uuid') - - self.tgt_db_inst.instance_get_by_uuid(self.ctxt, - instance_uuid).AndReturn(instance) - self.tgt_c_rpcapi.validate_console_port(self.ctxt, - instance, console_port, console_type).AndReturn('fake_result') - - self.mox.ReplayAll() - - response = self.src_msg_runner.validate_console_port(self.ctxt, - self.tgt_cell_name, instance_uuid, console_port, - console_type) - result = response.value_or_raise() - self.assertEqual('fake_result', result) + @mock.patch.object(objects.Instance, 'get_by_uuid', + return_value=instance) + @mock.patch.object(self.tgt_c_rpcapi, 'validate_console_port', + return_value='fake_result') + def do_test(mock_validate, mock_get): + response = self.src_msg_runner.validate_console_port(self.ctxt, + self.tgt_cell_name, instance_uuid, console_port, + console_type) + result = response.value_or_raise() + self.assertEqual('fake_result', result) + mock_get.assert_called_once_with(self.ctxt, instance_uuid) + mock_validate.assert_called_once_with(self.ctxt, instance, + console_port, console_type) + do_test() def test_get_migrations_for_a_given_cell(self): filters = {'cell_name': 'child-cell2', 'status': 'confirmed'} @@ -1112,7 +1110,7 @@ class CellsTargetedMethodsTestCase(test.TestCase): exception.InstanceNotFound(instance_id=instance.uuid)) self.tgt_msg_runner.instance_destroy_at_top(self.ctxt, - {'uuid': instance.uuid}) + mox.IsA(objects.Instance)) self.mox.ReplayAll() self.assertRaises(exception.InstanceNotFound, @@ -1465,152 +1463,105 @@ class CellsBroadcastMethodsTestCase(test.TestCase): self.assertFalse(self.src_methods_cls._at_the_top()) def test_apply_expected_states_building(self): - instance_info = {'vm_state': vm_states.BUILDING} - expected = dict(instance_info, - expected_vm_state=[vm_states.BUILDING, None]) - self.src_methods_cls._apply_expected_states(instance_info) - self.assertEqual(expected, instance_info) + instance_info = objects.Instance(vm_state=vm_states.BUILDING) + expected = instance_info.obj_clone() + expected.expected_vm_state = [vm_states.BUILDING, None] + expected_vm_state = self.src_methods_cls._get_expected_vm_state( + instance_info) + self.assertEqual(expected.expected_vm_state, expected_vm_state) def test_apply_expected_states_resize_finish(self): - instance_info = {'task_state': task_states.RESIZE_FINISH} + instance_info = objects.Instance(task_state=task_states.RESIZE_FINISH) exp_states = [task_states.RESIZE_FINISH, task_states.RESIZE_MIGRATED, task_states.RESIZE_MIGRATING, task_states.RESIZE_PREP] - expected = dict(instance_info, expected_task_state=exp_states) - self.src_methods_cls._apply_expected_states(instance_info) - self.assertEqual(expected, instance_info) + expected = instance_info.obj_clone() + expected.expected_task_state = exp_states + expected_task_state = self.src_methods_cls._get_expected_task_state( + instance_info) + self.assertEqual(expected.expected_task_state, expected_task_state) - def _test_instance_update_at_top(self, net_info, exists=True): - fake_info_cache = {'id': 1, - 'instance': 'fake_instance', - 'network_info': net_info} - fake_sys_metadata = [{'id': 1, - 'key': 'key1', - 'value': 'value1'}, - {'id': 2, - 'key': 'key2', - 'value': 'value2'}] - fake_instance = {'id': 2, - 'uuid': 'fake_uuid', - 'security_groups': 'fake', - 'volumes': 'fake', - 'cell_name': 'fake', - 'name': 'fake', - 'metadata': 'fake', - 'info_cache': fake_info_cache, - 'system_metadata': fake_sys_metadata, - 'other': 'meow'} - expected_sys_metadata = {'key1': 'value1', - 'key2': 'value2'} - expected_info_cache = {'network_info': "[]"} + def _test_instance_update_at_top(self, exists=True): + fake_uuid = fake_server_actions.FAKE_UUID + fake_info_cache = objects.InstanceInfoCache(instance_uuid='fake-uuid') + fake_sys_metadata = {'key1': 'value1', + 'key2': 'value2'} + fake_attrs = {'uuid': fake_uuid, + 'cell_name': 'fake', + 'info_cache': fake_info_cache, + 'system_metadata': fake_sys_metadata} + fake_instance = objects.Instance(**fake_attrs) expected_cell_name = 'api-cell!child-cell2!grandchild-cell1' - expected_instance = {'system_metadata': expected_sys_metadata, - 'cell_name': expected_cell_name, - 'other': 'meow', - 'uuid': 'fake_uuid'} - # To show these should not be called in src/mid-level cell - self.mox.StubOutWithMock(self.src_db_inst, 'instance_update') - self.mox.StubOutWithMock(self.src_db_inst, - 'instance_info_cache_update') - self.mox.StubOutWithMock(self.mid_db_inst, 'instance_update') - self.mox.StubOutWithMock(self.mid_db_inst, - 'instance_info_cache_update') + def fake_save(instance): + self.assertEqual(fake_uuid, instance.uuid) + self.assertEqual(expected_cell_name, instance.cell_name) + self.assertEqual(fake_info_cache, instance.info_cache) + self.assertEqual(fake_sys_metadata, instance.system_metadata) - self.mox.StubOutWithMock(self.tgt_db_inst, 'instance_update') - self.mox.StubOutWithMock(self.tgt_db_inst, 'instance_create') - self.mox.StubOutWithMock(self.tgt_db_inst, - 'instance_info_cache_update') - mock = self.tgt_db_inst.instance_update(self.ctxt, 'fake_uuid', - expected_instance, - update_cells=False) - if not exists: - mock.AndRaise(exception.InstanceNotFound(instance_id='fake_uuid')) - self.tgt_db_inst.instance_create(self.ctxt, - expected_instance) - self.tgt_db_inst.instance_info_cache_update(self.ctxt, 'fake_uuid', - expected_info_cache) - self.mox.ReplayAll() + @mock.patch.object(objects.Instance, 'save') + @mock.patch.object(objects.Instance, 'create') + def do_test(mock_create, mock_save): + if exists: + mock_save.side_effect = fake_save + else: + error = exception.InstanceNotFound(instance_id='fake_uuid') + mock_save.side_effect = error - self.src_msg_runner.instance_update_at_top(self.ctxt, fake_instance) + self.src_msg_runner.instance_update_at_top(self.ctxt, + fake_instance) + if exists: + mock_save.assert_called_once_with(expected_vm_state=None, + expected_task_state=None) + self.assertFalse(mock_create.called) + else: + mock_save.assert_called_once_with(expected_vm_state=None, + expected_task_state=None) + mock_create.assert_called_once_with() + do_test() def test_instance_update_at_top(self): - self._test_instance_update_at_top("[]") - - def test_instance_update_at_top_netinfo_list(self): - self._test_instance_update_at_top([]) - - def test_instance_update_at_top_netinfo_model(self): - self._test_instance_update_at_top(network_model.NetworkInfo()) + self._test_instance_update_at_top() def test_instance_update_at_top_does_not_already_exist(self): - self._test_instance_update_at_top([], exists=False) + self._test_instance_update_at_top(exists=False) def test_instance_update_at_top_with_building_state(self): - fake_info_cache = {'id': 1, - 'instance': 'fake_instance', - 'other': 'moo'} - fake_sys_metadata = [{'id': 1, - 'key': 'key1', - 'value': 'value1'}, - {'id': 2, - 'key': 'key2', - 'value': 'value2'}] - fake_instance = {'id': 2, - 'uuid': 'fake_uuid', - 'security_groups': 'fake', - 'volumes': 'fake', - 'cell_name': 'fake', - 'name': 'fake', - 'metadata': 'fake', - 'info_cache': fake_info_cache, - 'system_metadata': fake_sys_metadata, - 'vm_state': vm_states.BUILDING, - 'other': 'meow'} - expected_sys_metadata = {'key1': 'value1', - 'key2': 'value2'} - expected_info_cache = {'other': 'moo'} + fake_uuid = fake_server_actions.FAKE_UUID + fake_info_cache = objects.InstanceInfoCache(instance_uuid='fake-uuid') + fake_sys_metadata = {'key1': 'value1', + 'key2': 'value2'} + fake_attrs = {'uuid': fake_uuid, + 'cell_name': 'fake', + 'info_cache': fake_info_cache, + 'system_metadata': fake_sys_metadata, + 'vm_state': vm_states.BUILDING} + fake_instance = objects.Instance(**fake_attrs) expected_cell_name = 'api-cell!child-cell2!grandchild-cell1' - expected_instance = {'system_metadata': expected_sys_metadata, - 'cell_name': expected_cell_name, - 'other': 'meow', - 'vm_state': vm_states.BUILDING, - 'expected_vm_state': [vm_states.BUILDING, None], - 'uuid': 'fake_uuid'} + expected_vm_state = [vm_states.BUILDING, None] - # To show these should not be called in src/mid-level cell - self.mox.StubOutWithMock(self.src_db_inst, 'instance_update') - self.mox.StubOutWithMock(self.src_db_inst, - 'instance_info_cache_update') - self.mox.StubOutWithMock(self.mid_db_inst, 'instance_update') - self.mox.StubOutWithMock(self.mid_db_inst, - 'instance_info_cache_update') + def fake_save(instance): + self.assertEqual(fake_uuid, instance.uuid) + self.assertEqual(expected_cell_name, instance.cell_name) + self.assertEqual(fake_info_cache, instance.info_cache) + self.assertEqual(fake_sys_metadata, instance.system_metadata) - self.mox.StubOutWithMock(self.tgt_db_inst, 'instance_update') - self.mox.StubOutWithMock(self.tgt_db_inst, - 'instance_info_cache_update') - self.tgt_db_inst.instance_update(self.ctxt, 'fake_uuid', - expected_instance, - update_cells=False) - self.tgt_db_inst.instance_info_cache_update(self.ctxt, 'fake_uuid', - expected_info_cache) - self.mox.ReplayAll() - - self.src_msg_runner.instance_update_at_top(self.ctxt, fake_instance) + with mock.patch.object(objects.Instance, 'save', + side_effect=fake_save) as mock_save: + self.src_msg_runner.instance_update_at_top(self.ctxt, + fake_instance) + # Check that save is called with the right expected states. + mock_save.assert_called_once_with( + expected_vm_state=expected_vm_state, expected_task_state=None) def test_instance_destroy_at_top(self): - fake_instance = {'uuid': 'fake_uuid'} + fake_instance = objects.Instance(uuid='fake_uuid') - # To show these should not be called in src/mid-level cell - self.mox.StubOutWithMock(self.src_db_inst, 'instance_destroy') - - self.mox.StubOutWithMock(self.tgt_db_inst, 'instance_destroy') - self.tgt_db_inst.instance_destroy(self.ctxt, 'fake_uuid', - update_cells=False) - self.mox.ReplayAll() - - self.src_msg_runner.instance_destroy_at_top(self.ctxt, fake_instance) + with mock.patch.object(objects.Instance, 'destroy') as mock_destroy: + self.src_msg_runner.instance_destroy_at_top(self.ctxt, + fake_instance) + mock_destroy.assert_called_once_with() def test_instance_hard_delete_everywhere(self): # Reset this, as this is a broadcast down. @@ -1703,8 +1654,8 @@ class CellsBroadcastMethodsTestCase(test.TestCase): updated_since_parsed = 'fake_updated_since_parsed' deleted = 'fake_deleted' - instance1 = dict(uuid='fake_uuid1', deleted=False) - instance2 = dict(uuid='fake_uuid2', deleted=True) + instance1 = objects.Instance(uuid='fake_uuid1', deleted=False) + instance2 = objects.Instance(uuid='fake_uuid2', deleted=True) fake_instances = [instance1, instance2] self.mox.StubOutWithMock(self.tgt_msg_runner, diff --git a/nova/tests/unit/cells/test_cells_rpcapi.py b/nova/tests/unit/cells/test_cells_rpcapi.py index 78e827c84f..568d9c9d6c 100644 --- a/nova/tests/unit/cells/test_cells_rpcapi.py +++ b/nova/tests/unit/cells/test_cells_rpcapi.py @@ -152,25 +152,15 @@ class CellsAPITestCase(test.NoDBTestCase): self.assertEqual(capacity_info, result) def test_instance_update_at_top(self): - fake_info_cache = {'id': 1, - 'instance': 'fake_instance', - 'other': 'moo'} - fake_sys_metadata = [{'id': 1, - 'key': 'key1', - 'value': 'value1'}, - {'id': 2, - 'key': 'key2', - 'value': 'value2'}] - fake_instance = {'id': 2, - 'security_groups': 'fake', - 'instance_type': 'fake', - 'volumes': 'fake', - 'cell_name': 'fake', - 'name': 'fake', - 'metadata': 'fake', - 'info_cache': fake_info_cache, - 'system_metadata': fake_sys_metadata, - 'other': 'meow'} + fake_info_cache = objects.InstanceInfoCache(instance_uuid='fake-uuid') + fake_sys_metadata = {'key1': 'value1', + 'key2': 'value2'} + fake_attrs = {'id': 2, + 'cell_name': 'fake', + 'metadata': {'fake': 'fake'}, + 'info_cache': fake_info_cache, + 'system_metadata': fake_sys_metadata} + fake_instance = objects.Instance(**fake_attrs) call_info = self._stub_rpc_method('cast', None) @@ -179,10 +169,10 @@ class CellsAPITestCase(test.NoDBTestCase): expected_args = {'instance': fake_instance} self._check_result(call_info, 'instance_update_at_top', - expected_args) + expected_args, version='1.35') def test_instance_destroy_at_top(self): - fake_instance = {'uuid': 'fake-uuid'} + fake_instance = objects.Instance(uuid='fake-uuid') call_info = self._stub_rpc_method('cast', None) @@ -191,7 +181,7 @@ class CellsAPITestCase(test.NoDBTestCase): expected_args = {'instance': fake_instance} self._check_result(call_info, 'instance_destroy_at_top', - expected_args) + expected_args, version='1.35') def test_instance_delete_everywhere(self): instance = fake_instance.fake_instance_obj(self.fake_context) diff --git a/nova/tests/unit/cells/test_cells_scheduler.py b/nova/tests/unit/cells/test_cells_scheduler.py index 079b1e2db8..a2c0c84e10 100644 --- a/nova/tests/unit/cells/test_cells_scheduler.py +++ b/nova/tests/unit/cells/test_cells_scheduler.py @@ -254,9 +254,9 @@ class CellsSchedulerTestCase(test.TestCase): def fake_sleep(_secs): return - def fake_instance_update(ctxt, instance_uuid, values): - self.assertEqual(vm_states.ERROR, values['vm_state']) - call_info['errored_uuids'].append(instance_uuid) + def fake_instance_save(inst): + self.assertEqual(vm_states.ERROR, inst.vm_state) + call_info['errored_uuids'].append(inst.uuid) def fake_build_request_spec(ctxt, image, instances): request_spec = { @@ -267,7 +267,7 @@ class CellsSchedulerTestCase(test.TestCase): self.stubs.Set(self.scheduler, '_grab_target_cells', fake_grab_target_cells) self.stubs.Set(time, 'sleep', fake_sleep) - self.stubs.Set(db, 'instance_update', fake_instance_update) + self.stubs.Set(objects.Instance, 'save', fake_instance_save) self.stubs.Set(scheduler_utils, 'build_request_spec', fake_build_request_spec) @@ -280,7 +280,8 @@ class CellsSchedulerTestCase(test.TestCase): def test_schedule_method_on_random_exception(self): self.flags(scheduler_retries=7, group='cells') - instances = [{'uuid': uuid} for uuid in self.instance_uuids] + instances = [objects.Instance(uuid=uuid) for uuid in + self.instance_uuids] method_kwargs = { 'image': 'fake_image', 'instances': instances, @@ -294,9 +295,9 @@ class CellsSchedulerTestCase(test.TestCase): call_info['num_tries'] += 1 raise test.TestingException() - def fake_instance_update(ctxt, instance_uuid, values): - self.assertEqual(vm_states.ERROR, values['vm_state']) - call_info['errored_uuids1'].append(instance_uuid) + def fake_instance_save(inst): + self.assertEqual(vm_states.ERROR, inst.vm_state) + call_info['errored_uuids1'].append(inst.uuid) def fake_instance_update_at_top(ctxt, instance): self.assertEqual(vm_states.ERROR, instance['vm_state']) @@ -310,7 +311,7 @@ class CellsSchedulerTestCase(test.TestCase): self.stubs.Set(self.scheduler, '_grab_target_cells', fake_grab_target_cells) - self.stubs.Set(db, 'instance_update', fake_instance_update) + self.stubs.Set(objects.Instance, 'save', fake_instance_save) self.stubs.Set(self.msg_runner, 'instance_update_at_top', fake_instance_update_at_top) self.stubs.Set(scheduler_utils, 'build_request_spec', diff --git a/nova/tests/unit/cells/test_cells_utils.py b/nova/tests/unit/cells/test_cells_utils.py index 48fd42e6ec..a21f970ede 100644 --- a/nova/tests/unit/cells/test_cells_utils.py +++ b/nova/tests/unit/cells/test_cells_utils.py @@ -20,7 +20,6 @@ import mock import random from nova.cells import utils as cells_utils -from nova import db from nova import exception from nova import objects from nova import test @@ -36,16 +35,17 @@ class CellsUtilsTestCase(test.NoDBTestCase): def random_shuffle(_list): call_info['shuffle'] += 1 + @staticmethod def instance_get_all_by_filters(context, filters, - sort_key, sort_order): + sort_key, sort_dir): self.assertEqual(context, fake_context) self.assertEqual(sort_key, 'deleted') - self.assertEqual(sort_order, 'asc') + self.assertEqual(sort_dir, 'asc') call_info['got_filters'] = filters call_info['get_all'] += 1 return ['fake_instance1', 'fake_instance2', 'fake_instance3'] - self.stubs.Set(db, 'instance_get_all_by_filters', + self.stubs.Set(objects.InstanceList, 'get_by_filters', instance_get_all_by_filters) self.stubs.Set(random, 'shuffle', random_shuffle) diff --git a/nova/tests/unit/compute/test_compute.py b/nova/tests/unit/compute/test_compute.py index d235c21427..b59995e33b 100644 --- a/nova/tests/unit/compute/test_compute.py +++ b/nova/tests/unit/compute/test_compute.py @@ -2658,7 +2658,6 @@ class ComputeTestCase(BaseTestCase): {'task_state': task_pending, 'expected_task_state': expected_tasks, 'power_state': fake_power_state1}, - update_cells=False, columns_to_join=['system_metadata', 'extra', 'extra.flavor'] @@ -2669,7 +2668,6 @@ class ComputeTestCase(BaseTestCase): updated_dbinstance1['uuid'], {'task_state': task_started, 'expected_task_state': task_pending}, - update_cells=False, columns_to_join=['system_metadata', 'extra', 'extra.flavor'] @@ -2718,7 +2716,6 @@ class ComputeTestCase(BaseTestCase): {'power_state': new_power_state, 'task_state': None, 'vm_state': vm_states.ACTIVE}, - update_cells=False, columns_to_join=['system_metadata', 'extra', 'extra.flavor'], ).AndRaise(fault) self.compute._notify_about_instance_usage( @@ -2729,7 +2726,6 @@ class ComputeTestCase(BaseTestCase): db.instance_update_and_get_original( econtext, updated_dbinstance1['uuid'], {'vm_state': vm_states.ERROR}, - update_cells=False, columns_to_join=['system_metadata', 'extra', 'extra.flavor'], ).AndRaise(fault) else: @@ -2738,7 +2734,6 @@ class ComputeTestCase(BaseTestCase): {'power_state': new_power_state, 'task_state': None, 'vm_state': vm_states.ACTIVE}, - update_cells=False, columns_to_join=['system_metadata', 'extra', 'extra.flavor'], ).AndReturn((None, updated_dbinstance2)) if fail_running: @@ -6474,14 +6469,14 @@ class ComputeTestCase(BaseTestCase): self.stubs.Set(self.compute, '_get_resource_tracker', fail_get) instance = self._create_fake_instance_obj({'host': 'someotherhost'}) - self.compute._instance_update(self.context, instance.uuid) + self.compute._instance_update(self.context, instance.uuid, vcpus=4) instance = self._create_fake_instance_obj({'node': 'someothernode'}) - self.compute._instance_update(self.context, instance.uuid) + self.compute._instance_update(self.context, instance.uuid, vcpus=4) params = {'host': 'someotherhost', 'node': 'someothernode'} instance = self._create_fake_instance_obj(params) - self.compute._instance_update(self.context, instance.uuid) + self.compute._instance_update(self.context, instance.uuid, vcpus=4) def test_destroy_evacuated_instance_on_shared_storage(self): fake_context = context.get_admin_context() diff --git a/nova/tests/unit/conductor/test_conductor.py b/nova/tests/unit/conductor/test_conductor.py index 0df9c89562..c3f2ad288a 100644 --- a/nova/tests/unit/conductor/test_conductor.py +++ b/nova/tests/unit/conductor/test_conductor.py @@ -584,12 +584,17 @@ class ConductorTestCase(_BaseTestCase, test.TestCase): 'migration') def test_instance_destroy(self): - self.mox.StubOutWithMock(db, 'instance_destroy') - db.instance_destroy(self.context, 'fake-uuid').AndReturn('fake-result') - self.mox.ReplayAll() - result = self.conductor.instance_destroy(self.context, - {'uuid': 'fake-uuid'}) - self.assertEqual(result, 'fake-result') + instance = objects.Instance(id=1, uuid='fake-uuid') + + @mock.patch.object(instance, 'destroy') + @mock.patch.object(obj_base, 'obj_to_primitive', + return_value='fake-result') + def do_test(mock_to_primitive, mock_destroy): + result = self.conductor.instance_destroy(self.context, instance) + mock_destroy.assert_called_once_with() + mock_to_primitive.assert_called_once_with(instance) + self.assertEqual(result, 'fake-result') + do_test() def test_compute_unrescue(self): self.mox.StubOutWithMock(self.conductor_manager.compute_api, @@ -856,23 +861,33 @@ class ConductorImportTest(test.TestCase): class ConductorPolicyTest(test.TestCase): def test_all_allowed_keys(self): - - def fake_db_instance_update(self, *args, **kwargs): - return None, None - self.stubs.Set(db, 'instance_update_and_get_original', - fake_db_instance_update) - ctxt = context.RequestContext('fake-user', 'fake-project') conductor = conductor_api.LocalAPI() updates = {} for key in conductor_manager.allowed_updates: if key in conductor_manager.datetime_fields: updates[key] = timeutils.utcnow() + elif key == 'access_ip_v4': + updates[key] = '10.0.0.2' + elif key == 'access_ip_v6': + updates[key] = '2001:db8:0:1::1' + elif key in ('instance_type_id', 'memory_mb', 'ephemeral_gb', + 'root_gb', 'vcpus', 'power_state', 'progress'): + updates[key] = 5 + elif key == 'system_metadata': + updates[key] = {'foo': 'foo'} else: updates[key] = 'foo' - with mock.patch('nova.objects.Instance._from_db_object'): + def fake_save(inst): + # id that comes back from db after updating + inst.id = 1 + + with mock.patch.object(objects.Instance, 'save', + side_effect=fake_save, + autospec=True) as mock_save: conductor.instance_update(ctxt, 'fake-instance', **updates) + mock_save.assert_called_once_with(mock.ANY) def test_allowed_keys_are_real(self): instance = models.Instance() diff --git a/nova/tests/unit/network/test_neutronv2.py b/nova/tests/unit/network/test_neutronv2.py index b514a34013..3c7bb65fbd 100644 --- a/nova/tests/unit/network/test_neutronv2.py +++ b/nova/tests/unit/network/test_neutronv2.py @@ -2209,12 +2209,10 @@ class TestNeutronv2(TestNeutronv2Base): api.release_floating_ip, self.context, address) def _setup_mock_for_refresh_cache(self, api, instances): - nw_info = self.mox.CreateMock(model.NetworkInfo) + nw_info = model.NetworkInfo() self.mox.StubOutWithMock(api, '_get_instance_nw_info') self.mox.StubOutWithMock(api.db, 'instance_info_cache_update') for instance in instances: - nw_info.__str__().AndReturn('') - nw_info.json().AndReturn({}) api._get_instance_nw_info(mox.IgnoreArg(), instance).\ AndReturn(nw_info) api.db.instance_info_cache_update(mox.IgnoreArg(), diff --git a/nova/tests/unit/objects/test_instance.py b/nova/tests/unit/objects/test_instance.py index 1ea42cebe7..de65b1e10c 100644 --- a/nova/tests/unit/objects/test_instance.py +++ b/nova/tests/unit/objects/test_instance.py @@ -319,7 +319,6 @@ class _TestInstanceObject(object): ).AndReturn(old_ref) db.instance_update_and_get_original( self.context, fake_uuid, expected_updates, - update_cells=False, columns_to_join=['info_cache', 'security_groups', 'system_metadata', 'extra', 'extra.flavor'] ).AndReturn((old_ref, new_ref)) @@ -330,12 +329,8 @@ class _TestInstanceObject(object): exp_vm_state, exp_task_state, admin_reset) elif cell_type == 'compute': cells_rpcapi.CellsAPI().AndReturn(cells_api_mock) - expected = ['info_cache', 'security_groups', 'system_metadata', - 'flavor', 'new_flavor', 'old_flavor'] - new_ref_obj = objects.Instance._from_db_object(self.context, - objects.Instance(), new_ref, expected_attrs=expected) - instance_ref_p = base.obj_to_primitive(new_ref_obj) - cells_api_mock.instance_update_at_top(self.context, instance_ref_p) + cells_api_mock.instance_update_at_top(self.context, + mox.IsA(instance.Instance)) notifications.send_update(self.context, mox.IgnoreArg(), mox.IgnoreArg()) @@ -405,7 +400,7 @@ class _TestInstanceObject(object): use_slave=False ).AndReturn(old_ref) db.instance_update_and_get_original( - self.context, fake_uuid, expected_updates, update_cells=False, + self.context, fake_uuid, expected_updates, columns_to_join=['info_cache', 'security_groups', 'system_metadata', 'extra', 'extra.flavor'] ).AndReturn((old_ref, new_ref)) @@ -533,8 +528,12 @@ class _TestInstanceObject(object): self.assertEqual('foo!bar@baz', inst.cell_name) if cell_type == 'compute': - mock_update_at_top.assert_called_once_with(self.context, - base.obj_to_primitive(inst)) + mock_update_at_top.assert_called_once_with(self.context, mock.ANY) + # Compare primitives since we can't check instance object equality + expected_inst_p = base.obj_to_primitive(inst) + actual_inst = mock_update_at_top.call_args[0][1] + actual_inst_p = base.obj_to_primitive(actual_inst) + self.assertEqual(expected_inst_p, actual_inst_p) self.assertFalse(fake_update_from_api.called) elif cell_type == 'api': self.assertFalse(mock_update_at_top.called) @@ -544,12 +543,12 @@ class _TestInstanceObject(object): expected_calls = [ mock.call(self.context, inst.uuid, {'vm_state': 'foo', 'task_state': 'bar', - 'cell_name': 'foo!bar@baz'}, update_cells=False, + 'cell_name': 'foo!bar@baz'}, columns_to_join=['system_metadata', 'extra', 'extra.flavor']), mock.call(self.context, inst.uuid, {'vm_state': 'bar', 'task_state': 'foo'}, - update_cells=False, columns_to_join=['system_metadata', + columns_to_join=['system_metadata', 'extra', 'extra.flavor'])] mock_db_update.assert_has_calls(expected_calls) @@ -944,6 +943,28 @@ class _TestInstanceObject(object): self.assertRaises(exception.ObjectActionError, inst.destroy) + @mock.patch.object(cells_rpcapi.CellsAPI, 'instance_destroy_at_top') + @mock.patch.object(db, 'instance_destroy') + def test_destroy_cell_sync_to_top(self, mock_destroy, mock_destroy_at_top): + self.flags(enable=True, cell_type='compute', group='cells') + fake_inst = fake_instance.fake_db_instance(deleted=True) + mock_destroy.return_value = fake_inst + inst = instance.Instance(context=self.context, id=1, uuid='fake-uuid') + inst.destroy() + mock_destroy_at_top.assert_called_once_with(self.context, mock.ANY) + actual_inst = mock_destroy_at_top.call_args[0][1] + self.assertIsInstance(actual_inst, objects.Instance) + + @mock.patch.object(cells_rpcapi.CellsAPI, 'instance_destroy_at_top') + @mock.patch.object(db, 'instance_destroy') + def test_destroy_no_cell_sync_to_top(self, mock_destroy, + mock_destroy_at_top): + fake_inst = fake_instance.fake_db_instance(deleted=True) + mock_destroy.return_value = fake_inst + inst = instance.Instance(context=self.context, id=1, uuid='fake-uuid') + inst.destroy() + self.assertFalse(mock_destroy_at_top.called) + def test_name_does_not_trigger_lazy_loads(self): values = {'user_id': self.context.user_id, 'project_id': self.context.project_id, diff --git a/nova/tests/unit/scheduler/test_scheduler_utils.py b/nova/tests/unit/scheduler/test_scheduler_utils.py index d037fd773c..b7a35c62f0 100644 --- a/nova/tests/unit/scheduler/test_scheduler_utils.py +++ b/nova/tests/unit/scheduler/test_scheduler_utils.py @@ -26,7 +26,6 @@ from nova.compute import flavors from nova.compute import utils as compute_utils from nova import db from nova import exception -from nova import notifications from nova import objects from nova import rpc from nova.scheduler import utils as scheduler_utils @@ -71,7 +70,10 @@ class SchedulerUtilsTestCase(test.NoDBTestCase): mock_get.assert_called_once_with() self.assertIsInstance(request_spec['instance_properties'], dict) - def test_set_vm_state_and_notify(self): + @mock.patch.object(rpc, 'get_notifier', return_value=mock.Mock()) + @mock.patch.object(compute_utils, 'add_instance_fault_from_exc') + @mock.patch.object(objects.Instance, 'save') + def test_set_vm_state_and_notify(self, mock_save, mock_add, mock_get): expected_uuid = 'fake-uuid' request_spec = dict(instance_properties=dict(uuid='other-uuid')) updates = dict(vm_state='fake-vm-state') @@ -79,28 +81,6 @@ class SchedulerUtilsTestCase(test.NoDBTestCase): method = 'fake-method' exc_info = 'exc_info' - self.mox.StubOutWithMock(compute_utils, - 'add_instance_fault_from_exc') - self.mox.StubOutWithMock(notifications, 'send_update') - self.mox.StubOutWithMock(db, 'instance_update_and_get_original') - - self.mox.StubOutWithMock(rpc, 'get_notifier') - notifier = self.mox.CreateMockAnything() - rpc.get_notifier(service).AndReturn(notifier) - - old_ref = 'old_ref' - new_ref = 'new_ref' - inst_obj = 'inst_obj' - - db.instance_update_and_get_original( - self.context, expected_uuid, updates, - columns_to_join=['system_metadata']).AndReturn((old_ref, new_ref)) - notifications.send_update(self.context, old_ref, inst_obj, - service=service) - compute_utils.add_instance_fault_from_exc( - self.context, - inst_obj, exc_info, mox.IsA(tuple)) - payload = dict(request_spec=request_spec, instance_properties=request_spec.get( 'instance_properties', {}), @@ -109,20 +89,23 @@ class SchedulerUtilsTestCase(test.NoDBTestCase): method=method, reason=exc_info) event_type = '%s.%s' % (service, method) - notifier.error(self.context, event_type, payload) - self.mox.ReplayAll() - - with mock.patch.object(objects.Instance, '_from_db_object', - return_value=inst_obj): - scheduler_utils.set_vm_state_and_notify(self.context, - expected_uuid, - service, - method, - updates, - exc_info, - request_spec, - db) + scheduler_utils.set_vm_state_and_notify(self.context, + expected_uuid, + service, + method, + updates, + exc_info, + request_spec, + db) + mock_save.assert_called_once_with() + mock_add.assert_called_once_with(self.context, mock.ANY, + exc_info, mock.ANY) + self.assertIsInstance(mock_add.call_args[0][1], objects.Instance) + self.assertIsInstance(mock_add.call_args[0][3], tuple) + mock_get.return_value.error.assert_called_once_with(self.context, + event_type, + payload) def _test_populate_filter_props(self, host_state_obj=True, with_retry=True,