diff options
-rw-r--r-- | nova/cells/manager.py | 13 | ||||
-rw-r--r-- | nova/cells/messaging.py | 76 | ||||
-rw-r--r-- | nova/cells/rpcapi.py | 34 | ||||
-rw-r--r-- | nova/compute/cells_api.py | 4 | ||||
-rw-r--r-- | nova/conductor/manager.py | 39 | ||||
-rw-r--r-- | nova/db/sqlalchemy/api.py | 9 | ||||
-rw-r--r-- | nova/tests/api/ec2/test_cloud.py | 1 | ||||
-rw-r--r-- | nova/tests/cells/test_cells_manager.py | 24 | ||||
-rw-r--r-- | nova/tests/cells/test_cells_messaging.py | 170 | ||||
-rw-r--r-- | nova/tests/cells/test_cells_rpcapi.py | 26 | ||||
-rw-r--r-- | nova/tests/conductor/test_conductor.py | 61 | ||||
-rw-r--r-- | nova/tests/db/test_db_api.py | 9 |
12 files changed, 444 insertions, 22 deletions
diff --git a/nova/cells/manager.py b/nova/cells/manager.py index f776c542e..4dc9f2d82 100644 --- a/nova/cells/manager.py +++ b/nova/cells/manager.py @@ -64,7 +64,7 @@ class CellsManager(manager.Manager): Scheduling requests get passed to the scheduler class. """ - RPC_API_VERSION = '1.9' + RPC_API_VERSION = '1.10' def __init__(self, *args, **kwargs): # Mostly for tests. @@ -390,3 +390,14 @@ class CellsManager(manager.Manager): def get_capacities(self, ctxt, cell_name): return self.state_manager.get_capacities(cell_name) + + def bdm_update_or_create_at_top(self, ctxt, bdm, create=None): + """BDM was created/updated in this cell. Tell the API cells.""" + self.msg_runner.bdm_update_or_create_at_top(ctxt, bdm, create=create) + + def bdm_destroy_at_top(self, ctxt, instance_uuid, device_name=None, + volume_id=None): + """BDM was destroyed for instance in this cell. Tell the API cells.""" + self.msg_runner.bdm_destroy_at_top(ctxt, instance_uuid, + device_name=device_name, + volume_id=volume_id) diff --git a/nova/cells/messaging.py b/nova/cells/messaging.py index 6f4183f5d..24ad29e08 100644 --- a/nova/cells/messaging.py +++ b/nova/cells/messaging.py @@ -846,7 +846,8 @@ class _BroadcastMessageMethods(_BaseMessageMethods): except exception.NotFound: # FIXME(comstud): Strange. Need to handle quotas here, # if we actually want this code to remain.. - self.db.instance_create(message.ctxt, instance) + self.db.instance_create(message.ctxt, instance, + legacy=False) if info_cache: try: self.db.instance_info_cache_update( @@ -957,6 +958,60 @@ class _BroadcastMessageMethods(_BaseMessageMethods): self.consoleauth_rpcapi.delete_tokens_for_instance(message.ctxt, instance_uuid) + def bdm_update_or_create_at_top(self, message, bdm, create): + """Create or update a block device mapping in API cells. If + create is True, only try to create. If create is None, try to + update but fall back to create. If create is False, only attempt + to update. This maps to nova-conductor's behavior. + """ + if not self._at_the_top(): + return + items_to_remove = ['id'] + for key in items_to_remove: + bdm.pop(key, None) + if create is None: + self.db.block_device_mapping_update_or_create(message.ctxt, + bdm, + legacy=False) + return + elif create is True: + self.db.block_device_mapping_create(message.ctxt, bdm, + legacy=False) + return + # Unfortunately this update call wants BDM ID... but we don't know + # what it is in this cell. Search for it.. try matching either + # device_name or volume_id. + dev_name = bdm['device_name'] + vol_id = bdm['volume_id'] + instance_bdms = self.db.block_device_mapping_get_all_by_instance( + message.ctxt, bdm['instance_uuid']) + for instance_bdm in instance_bdms: + if dev_name and instance_bdm['device_name'] == dev_name: + break + if vol_id and instance_bdm['volume_id'] == vol_id: + break + else: + LOG.warn(_("No match when trying to update BDM: %(bdm)s"), + dict(bdm=bdm)) + return + self.db.block_device_mapping_update(message.ctxt, + instance_bdm['id'], bdm, + legacy=False) + + def bdm_destroy_at_top(self, message, instance_uuid, device_name, + volume_id): + """Destroy a block device mapping in API cells by device name + or volume_id. device_name or volume_id can be None, but not both. + """ + if not self._at_the_top(): + return + if device_name: + self.db.block_device_mapping_destroy_by_instance_and_device( + message.ctxt, instance_uuid, device_name) + elif volume_id: + self.db.block_device_mapping_destroy_by_instance_and_volume( + message.ctxt, instance_uuid, volume_id) + _CELL_MESSAGE_TYPE_TO_MESSAGE_CLS = {'targeted': _TargetedMessage, 'broadcast': _BroadcastMessage, @@ -1342,6 +1397,25 @@ class MessageRunner(object): cell_name, need_response=True) return message.process() + def bdm_update_or_create_at_top(self, ctxt, bdm, create=None): + """Update/Create a BDM at top level cell.""" + message = _BroadcastMessage(self, ctxt, + 'bdm_update_or_create_at_top', + dict(bdm=bdm, create=create), + 'up', run_locally=False) + message.process() + + def bdm_destroy_at_top(self, ctxt, instance_uuid, device_name=None, + volume_id=None): + """Destroy a BDM at top level cell.""" + method_kwargs = dict(instance_uuid=instance_uuid, + device_name=device_name, + volume_id=volume_id) + message = _BroadcastMessage(self, ctxt, 'bdm_destroy_at_top', + method_kwargs, + 'up', run_locally=False) + message.process() + @staticmethod def get_message_types(): return _CELL_MESSAGE_TYPE_TO_MESSAGE_CLS.keys() diff --git a/nova/cells/rpcapi.py b/nova/cells/rpcapi.py index e65d8f490..6a5480073 100644 --- a/nova/cells/rpcapi.py +++ b/nova/cells/rpcapi.py @@ -26,9 +26,11 @@ from oslo.config import cfg from nova import exception from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova.openstack.common.rpc import proxy as rpc_proxy +LOG = logging.getLogger(__name__) CONF = cfg.CONF CONF.import_opt('enable', 'nova.cells.opts', group='cells') CONF.import_opt('topic', 'nova.cells.opts', group='cells') @@ -62,6 +64,7 @@ class CellsAPI(rpc_proxy.RpcProxy): 1.7 - Adds service_update() 1.8 - Adds build_instances(), deprecates schedule_run_instance() 1.9 - Adds get_capacities() + 1.10 - Adds bdm_update_or_create_at_top(), and bdm_destroy_at_top() ''' BASE_RPC_API_VERSION = '1.0' @@ -315,3 +318,34 @@ class CellsAPI(rpc_proxy.RpcProxy): return self.call(ctxt, self.make_msg('get_capacities', cell_name=cell_name), version='1.9') + + def bdm_update_or_create_at_top(self, ctxt, bdm, create=None): + """Create or update a block device mapping in API cells. If + create is True, only try to create. If create is None, try to + update but fall back to create. If create is False, only attempt + to update. This maps to nova-conductor's behavior. + """ + if not CONF.cells.enable: + return + try: + self.cast(ctxt, self.make_msg('bdm_update_or_create_at_top', + bdm=bdm, create=create), + version='1.10') + except Exception: + LOG.exception(_("Failed to notify cells of BDM update/create.")) + + def bdm_destroy_at_top(self, ctxt, instance_uuid, device_name=None, + volume_id=None): + """Broadcast upwards that a block device mapping was destroyed. + One of device_name or volume_id should be specified. + """ + if not CONF.cells.enable: + return + try: + self.cast(ctxt, self.make_msg('bdm_destroy_at_top', + instance_uuid=instance_uuid, + device_name=device_name, + volume_id=volume_id), + version='1.10') + except Exception: + LOG.exception(_("Failed to notify cells of BDM destroy.")) diff --git a/nova/compute/cells_api.py b/nova/compute/cells_api.py index 6f1e12480..ddf959ef3 100644 --- a/nova/compute/cells_api.py +++ b/nova/compute/cells_api.py @@ -520,8 +520,6 @@ class ComputeCellsAPI(compute_api.API): """Attach an existing volume to an existing instance.""" if device and not block_device.match_device(device): raise exception.InvalidDevicePath(path=device) - device = self.compute_rpcapi.reserve_block_device_name( - context, device=device, instance=instance, volume_id=volume_id) try: volume = self.volume_api.get(context, volume_id) self.volume_api.check_attach(context, volume, instance=instance) @@ -529,7 +527,7 @@ class ComputeCellsAPI(compute_api.API): with excutils.save_and_reraise_exception(): self.db.block_device_mapping_destroy_by_instance_and_device( context, instance['uuid'], device) - self._cast_to_cells(context, instance, 'attach_volume', + return self._call_to_cells(context, instance, 'attach_volume', volume_id, device) @validate_cell diff --git a/nova/conductor/manager.py b/nova/conductor/manager.py index cc1b05cc4..40e7fa69b 100644 --- a/nova/conductor/manager.py +++ b/nova/conductor/manager.py @@ -18,6 +18,7 @@ import copy from nova.api.ec2 import ec2utils from nova import block_device +from nova.cells import rpcapi as cells_rpcapi from nova.compute import api as compute_api from nova.compute import utils as compute_utils from nova import exception @@ -77,6 +78,7 @@ class ConductorManager(manager.Manager): self._compute_api = None self.compute_task_mgr = ComputeTaskManager() self.quotas = quota.QUOTAS + self.cells_rpcapi = cells_rpcapi.CellsAPI() def create_rpc_dispatcher(self, *args, **kwargs): kwargs['additional_apis'] = [self.compute_task_mgr] @@ -258,11 +260,18 @@ class ConductorManager(manager.Manager): def block_device_mapping_update_or_create(self, context, values, create=None): if create is None: - self.db.block_device_mapping_update_or_create(context, values) + bdm = self.db.block_device_mapping_update_or_create(context, + values) elif create is True: - self.db.block_device_mapping_create(context, values) + bdm = self.db.block_device_mapping_create(context, values) else: - self.db.block_device_mapping_update(context, values['id'], values) + bdm = self.db.block_device_mapping_update(context, + values['id'], + values) + # NOTE:comstud): 'bdm' is always in the new format, so we + # account for this in cells/messaging.py + self.cells_rpcapi.bdm_update_or_create_at_top(context, bdm, + create=create) def block_device_mapping_get_all_by_instance(self, context, instance, legacy=True): @@ -278,12 +287,36 @@ class ConductorManager(manager.Manager): if bdms is not None: for bdm in bdms: self.db.block_device_mapping_destroy(context, bdm['id']) + # NOTE(comstud): bdm['id'] will be different in API cell, + # so we must try to destroy by device_name or volume_id. + # We need an instance_uuid in order to do this properly, + # too. + # I hope to clean a lot of this up in the object + # implementation. + instance_uuid = (bdm['instance_uuid'] or + (instance and instance['uuid'])) + if not instance_uuid: + continue + # Better to be safe than sorry. device_name is not + # NULLable, however it could be an empty string. + if bdm['device_name']: + self.cells_rpcapi.bdm_destroy_at_top( + context, instance_uuid, + device_name=bdm['device_name']) + elif bdm['volume_id']: + self.cells_rpcapi.bdm_destroy_at_top( + context, instance_uuid, + volume_id=bdm['volume_id']) elif instance is not None and volume_id is not None: self.db.block_device_mapping_destroy_by_instance_and_volume( context, instance['uuid'], volume_id) + self.cells_rpcapi.bdm_destroy_at_top( + context, instance['uuid'], volume_id=volume_id) elif instance is not None and device_name is not None: self.db.block_device_mapping_destroy_by_instance_and_device( context, instance['uuid'], device_name) + self.cells_rpcapi.bdm_destroy_at_top( + context, instance['uuid'], device_name=device_name) else: # NOTE(danms): This shouldn't happen raise exception.Invalid(_("Invalid block_device_mapping_destroy" diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index c44f62206..b6fc19af0 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -3123,15 +3123,16 @@ def block_device_mapping_create(context, values, legacy=True): bdm_ref = models.BlockDeviceMapping() bdm_ref.update(values) bdm_ref.save() + return bdm_ref @require_context def block_device_mapping_update(context, bdm_id, values, legacy=True): _scrub_empty_str_values(values, ['volume_size']) values = _from_legacy_values(values, legacy, allow_updates=True) - _block_device_mapping_get_query(context).\ - filter_by(id=bdm_id).\ - update(values) + query = _block_device_mapping_get_query(context).filter_by(id=bdm_id) + query.update(values) + return query.first() def block_device_mapping_update_or_create(context, values, legacy=True): @@ -3147,6 +3148,7 @@ def block_device_mapping_update_or_create(context, values, legacy=True): bdm_ref = models.BlockDeviceMapping() bdm_ref.update(values) bdm_ref.save(session=session) + result = bdm_ref else: values = _from_legacy_values(values, legacy, allow_updates=True) result.update(values) @@ -3169,6 +3171,7 @@ def block_device_mapping_update_or_create(context, values, legacy=True): models.BlockDeviceMapping.guest_format == None, models.BlockDeviceMapping.guest_format != 'swap')). soft_delete()) + return result @require_context diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py index 560474847..c9e8ff42f 100644 --- a/nova/tests/api/ec2/test_cloud.py +++ b/nova/tests/api/ec2/test_cloud.py @@ -2124,6 +2124,7 @@ class CloudTestCase(test.TestCase): return [dict(id=1, source_type='snapshot', destination_type='volume', + instance_uuid=inst_id, snapshot_id=snapshots[0], volume_id=volumes[0], volume_size=1, diff --git a/nova/tests/cells/test_cells_manager.py b/nova/tests/cells/test_cells_manager.py index 4e35cd818..89a60cb35 100644 --- a/nova/tests/cells/test_cells_manager.py +++ b/nova/tests/cells/test_cells_manager.py @@ -548,3 +548,27 @@ class CellsManagerClassTestCase(test.TestCase): instance_uuid=instance_uuid, console_port=console_port, console_type=console_type) self.assertEqual('fake-response', response) + + def test_bdm_update_or_create_at_top(self): + self.mox.StubOutWithMock(self.msg_runner, + 'bdm_update_or_create_at_top') + self.msg_runner.bdm_update_or_create_at_top(self.ctxt, + 'fake-bdm', + create='foo') + self.mox.ReplayAll() + self.cells_manager.bdm_update_or_create_at_top(self.ctxt, + 'fake-bdm', + create='foo') + + def test_bdm_destroy_at_top(self): + self.mox.StubOutWithMock(self.msg_runner, 'bdm_destroy_at_top') + self.msg_runner.bdm_destroy_at_top(self.ctxt, + 'fake_instance_uuid', + device_name='fake_device_name', + volume_id='fake_volume_id') + + self.mox.ReplayAll() + self.cells_manager.bdm_destroy_at_top(self.ctxt, + 'fake_instance_uuid', + device_name='fake_device_name', + volume_id='fake_volume_id') diff --git a/nova/tests/cells/test_cells_messaging.py b/nova/tests/cells/test_cells_messaging.py index d4d9b052e..f9e97d717 100644 --- a/nova/tests/cells/test_cells_messaging.py +++ b/nova/tests/cells/test_cells_messaging.py @@ -1426,3 +1426,173 @@ class CellsBroadcastMethodsTestCase(test.TestCase): self.mox.ReplayAll() self.src_msg_runner.consoleauth_delete_tokens(self.ctxt, fake_uuid) + + def test_bdm_update_or_create_with_none_create(self): + fake_bdm = {'id': 'fake_id', + 'volume_id': 'fake_volume_id'} + expected_bdm = fake_bdm.copy() + expected_bdm.pop('id') + + # Shouldn't be called for these 2 cells + self.mox.StubOutWithMock(self.src_db_inst, + 'block_device_mapping_update_or_create') + self.mox.StubOutWithMock(self.mid_db_inst, + 'block_device_mapping_update_or_create') + + self.mox.StubOutWithMock(self.tgt_db_inst, + 'block_device_mapping_update_or_create') + self.tgt_db_inst.block_device_mapping_update_or_create( + self.ctxt, expected_bdm, legacy=False) + + self.mox.ReplayAll() + + self.src_msg_runner.bdm_update_or_create_at_top(self.ctxt, + fake_bdm, + create=None) + + def test_bdm_update_or_create_with_true_create(self): + fake_bdm = {'id': 'fake_id', + 'volume_id': 'fake_volume_id'} + expected_bdm = fake_bdm.copy() + expected_bdm.pop('id') + + # Shouldn't be called for these 2 cells + self.mox.StubOutWithMock(self.src_db_inst, + 'block_device_mapping_create') + self.mox.StubOutWithMock(self.mid_db_inst, + 'block_device_mapping_create') + + self.mox.StubOutWithMock(self.tgt_db_inst, + 'block_device_mapping_create') + self.tgt_db_inst.block_device_mapping_create( + self.ctxt, fake_bdm, legacy=False) + + self.mox.ReplayAll() + + self.src_msg_runner.bdm_update_or_create_at_top(self.ctxt, + fake_bdm, + create=True) + + def test_bdm_update_or_create_with_false_create_vol_id(self): + fake_bdm = {'id': 'fake_id', + 'instance_uuid': 'fake_instance_uuid', + 'device_name': 'fake_device_name', + 'volume_id': 'fake_volume_id'} + expected_bdm = fake_bdm.copy() + expected_bdm.pop('id') + + fake_inst_bdms = [{'id': 1, + 'volume_id': 'not-a-match', + 'device_name': 'not-a-match'}, + {'id': 2, + 'volume_id': 'fake_volume_id', + 'device_name': 'not-a-match'}, + {'id': 3, + 'volume_id': 'not-a-match', + 'device_name': 'not-a-match'}] + + # Shouldn't be called for these 2 cells + self.mox.StubOutWithMock(self.src_db_inst, + 'block_device_mapping_update') + self.mox.StubOutWithMock(self.mid_db_inst, + 'block_device_mapping_update') + + self.mox.StubOutWithMock(self.tgt_db_inst, + 'block_device_mapping_get_all_by_instance') + self.mox.StubOutWithMock(self.tgt_db_inst, + 'block_device_mapping_update') + + self.tgt_db_inst.block_device_mapping_get_all_by_instance( + self.ctxt, 'fake_instance_uuid').AndReturn( + fake_inst_bdms) + # Should try to update ID 2. + self.tgt_db_inst.block_device_mapping_update( + self.ctxt, 2, expected_bdm, legacy=False) + + self.mox.ReplayAll() + + self.src_msg_runner.bdm_update_or_create_at_top(self.ctxt, + fake_bdm, + create=False) + + def test_bdm_update_or_create_with_false_create_dev_name(self): + fake_bdm = {'id': 'fake_id', + 'instance_uuid': 'fake_instance_uuid', + 'device_name': 'fake_device_name', + 'volume_id': 'fake_volume_id'} + expected_bdm = fake_bdm.copy() + expected_bdm.pop('id') + + fake_inst_bdms = [{'id': 1, + 'volume_id': 'not-a-match', + 'device_name': 'not-a-match'}, + {'id': 2, + 'volume_id': 'not-a-match', + 'device_name': 'fake_device_name'}, + {'id': 3, + 'volume_id': 'not-a-match', + 'device_name': 'not-a-match'}] + + # Shouldn't be called for these 2 cells + self.mox.StubOutWithMock(self.src_db_inst, + 'block_device_mapping_update') + self.mox.StubOutWithMock(self.mid_db_inst, + 'block_device_mapping_update') + + self.mox.StubOutWithMock(self.tgt_db_inst, + 'block_device_mapping_get_all_by_instance') + self.mox.StubOutWithMock(self.tgt_db_inst, + 'block_device_mapping_update') + + self.tgt_db_inst.block_device_mapping_get_all_by_instance( + self.ctxt, 'fake_instance_uuid').AndReturn( + fake_inst_bdms) + # Should try to update ID 2. + self.tgt_db_inst.block_device_mapping_update( + self.ctxt, 2, expected_bdm, legacy=False) + + self.mox.ReplayAll() + + self.src_msg_runner.bdm_update_or_create_at_top(self.ctxt, + fake_bdm, + create=False) + + def test_bdm_destroy_by_volume(self): + fake_instance_uuid = 'fake-instance-uuid' + fake_volume_id = 'fake-volume-name' + + # Shouldn't be called for these 2 cells + self.mox.StubOutWithMock(self.src_db_inst, + 'block_device_mapping_destroy_by_instance_and_volume') + self.mox.StubOutWithMock(self.mid_db_inst, + 'block_device_mapping_destroy_by_instance_and_volume') + + self.mox.StubOutWithMock(self.tgt_db_inst, + 'block_device_mapping_destroy_by_instance_and_volume') + self.tgt_db_inst.block_device_mapping_destroy_by_instance_and_volume( + self.ctxt, fake_instance_uuid, fake_volume_id) + + self.mox.ReplayAll() + + self.src_msg_runner.bdm_destroy_at_top(self.ctxt, fake_instance_uuid, + volume_id=fake_volume_id) + + def test_bdm_destroy_by_device(self): + fake_instance_uuid = 'fake-instance-uuid' + fake_device_name = 'fake-device-name' + + # Shouldn't be called for these 2 cells + self.mox.StubOutWithMock(self.src_db_inst, + 'block_device_mapping_destroy_by_instance_and_device') + self.mox.StubOutWithMock(self.mid_db_inst, + 'block_device_mapping_destroy_by_instance_and_device') + + self.mox.StubOutWithMock(self.tgt_db_inst, + 'block_device_mapping_destroy_by_instance_and_device') + self.tgt_db_inst.block_device_mapping_destroy_by_instance_and_device( + self.ctxt, fake_instance_uuid, fake_device_name) + + self.mox.ReplayAll() + + self.src_msg_runner.bdm_destroy_at_top(self.ctxt, fake_instance_uuid, + device_name=fake_device_name) diff --git a/nova/tests/cells/test_cells_rpcapi.py b/nova/tests/cells/test_cells_rpcapi.py index 4d58bdb9e..6eeff1730 100644 --- a/nova/tests/cells/test_cells_rpcapi.py +++ b/nova/tests/cells/test_cells_rpcapi.py @@ -425,3 +425,29 @@ class CellsAPITestCase(test.TestCase): self._check_result(call_info, 'validate_console_port', expected_args, version='1.6') self.assertEqual(result, 'fake_response') + + def test_bdm_update_or_create_at_top(self): + fake_bdm = {'id': 2, 'other': 'meow'} + + call_info = self._stub_rpc_method('cast', None) + + self.cells_rpcapi.bdm_update_or_create_at_top( + self.fake_context, fake_bdm, create='fake-create') + + expected_args = {'bdm': fake_bdm, 'create': 'fake-create'} + self._check_result(call_info, 'bdm_update_or_create_at_top', + expected_args, version='1.10') + + def test_bdm_destroy_at_top(self): + call_info = self._stub_rpc_method('cast', None) + + self.cells_rpcapi.bdm_destroy_at_top(self.fake_context, + 'fake-uuid', + device_name='fake-device', + volume_id='fake-vol') + + expected_args = {'instance_uuid': 'fake-uuid', + 'device_name': 'fake-device', + 'volume_id': 'fake-vol'} + self._check_result(call_info, 'bdm_destroy_at_top', + expected_args, version='1.10') diff --git a/nova/tests/conductor/test_conductor.py b/nova/tests/conductor/test_conductor.py index a2a015313..7df829a53 100644 --- a/nova/tests/conductor/test_conductor.py +++ b/nova/tests/conductor/test_conductor.py @@ -616,13 +616,28 @@ class ConductorTestCase(_BaseTestCase, test.TestCase): self.conductor_manager = self.conductor def test_block_device_mapping_update_or_create(self): - fake_bdm = {'id': 'fake-id'} + fake_bdm = {'id': 'fake-id', 'device_name': 'foo'} + fake_bdm2 = {'id': 'fake-id', 'device_name': 'foo2'} + cells_rpcapi = self.conductor.cells_rpcapi self.mox.StubOutWithMock(db, 'block_device_mapping_create') self.mox.StubOutWithMock(db, 'block_device_mapping_update') self.mox.StubOutWithMock(db, 'block_device_mapping_update_or_create') - db.block_device_mapping_create(self.context, fake_bdm) - db.block_device_mapping_update(self.context, fake_bdm['id'], fake_bdm) - db.block_device_mapping_update_or_create(self.context, fake_bdm) + self.mox.StubOutWithMock(cells_rpcapi, + 'bdm_update_or_create_at_top') + db.block_device_mapping_create(self.context, + fake_bdm).AndReturn(fake_bdm2) + cells_rpcapi.bdm_update_or_create_at_top(self.context, fake_bdm2, + create=True) + db.block_device_mapping_update(self.context, fake_bdm['id'], + fake_bdm).AndReturn(fake_bdm2) + cells_rpcapi.bdm_update_or_create_at_top(self.context, + fake_bdm2, + create=False) + db.block_device_mapping_update_or_create( + self.context, fake_bdm).AndReturn(fake_bdm2) + cells_rpcapi.bdm_update_or_create_at_top(self.context, + fake_bdm2, + create=None) self.mox.ReplayAll() self.conductor.block_device_mapping_update_or_create(self.context, fake_bdm, @@ -634,22 +649,44 @@ class ConductorTestCase(_BaseTestCase, test.TestCase): fake_bdm) def test_block_device_mapping_destroy(self): - fake_bdm = {'id': 'fake-bdm'} - fake_bdm2 = {'id': 'fake-bdm-2'} + fake_bdm = {'id': 'fake-bdm', + 'instance_uuid': 'fake-uuid', + 'device_name': 'fake-device1', + 'volume_id': 'fake-vol-id1'} + fake_bdm2 = {'id': 'fake-bdm-2', + 'instance_uuid': 'fake-uuid2', + 'device_name': '', + 'volume_id': 'fake-vol-id2'} fake_inst = {'uuid': 'fake-uuid'} + + cells_rpcapi = self.conductor.cells_rpcapi + self.mox.StubOutWithMock(db, 'block_device_mapping_destroy') self.mox.StubOutWithMock( db, 'block_device_mapping_destroy_by_instance_and_device') self.mox.StubOutWithMock( db, 'block_device_mapping_destroy_by_instance_and_volume') + self.mox.StubOutWithMock(cells_rpcapi, 'bdm_destroy_at_top') + db.block_device_mapping_destroy(self.context, 'fake-bdm') + cells_rpcapi.bdm_destroy_at_top(self.context, + fake_bdm['instance_uuid'], + device_name=fake_bdm['device_name']) db.block_device_mapping_destroy(self.context, 'fake-bdm-2') + cells_rpcapi.bdm_destroy_at_top(self.context, + fake_bdm2['instance_uuid'], + volume_id=fake_bdm2['volume_id']) db.block_device_mapping_destroy_by_instance_and_device(self.context, 'fake-uuid', 'fake-device') + cells_rpcapi.bdm_destroy_at_top(self.context, fake_inst['uuid'], + device_name='fake-device') db.block_device_mapping_destroy_by_instance_and_volume(self.context, 'fake-uuid', 'fake-volume') + cells_rpcapi.bdm_destroy_at_top(self.context, fake_inst['uuid'], + volume_id='fake-volume') + self.mox.ReplayAll() self.conductor.block_device_mapping_destroy(self.context, [fake_bdm, @@ -794,8 +831,12 @@ class ConductorRPCAPITestCase(_BaseTestCase, test.TestCase): fake_bdm) def test_block_device_mapping_destroy(self): - fake_bdm = {'id': 'fake-bdm'} + fake_bdm = {'id': 'fake-bdm', + 'instance_uuid': 'fake-uuid', + 'device_name': 'fake-device1', + 'volume_id': 'fake-vol-id1'} fake_inst = {'uuid': 'fake-uuid'} + self.mox.StubOutWithMock(db, 'block_device_mapping_destroy') self.mox.StubOutWithMock( db, 'block_device_mapping_destroy_by_instance_and_device') @@ -945,8 +986,12 @@ class ConductorAPITestCase(_BaseTestCase, test.TestCase): 'fake-bdm') def test_block_device_mapping_destroy(self): - fake_bdm = {'id': 'fake-bdm'} + fake_bdm = {'id': 'fake-bdm', + 'instance_uuid': 'fake-uuid', + 'device_name': 'fake-device1', + 'volume_id': 'fake-vol-id1'} fake_inst = {'uuid': 'fake-uuid'} + self.mox.StubOutWithMock(db, 'block_device_mapping_destroy') self.mox.StubOutWithMock( db, 'block_device_mapping_destroy_by_instance_and_device') diff --git a/nova/tests/db/test_db_api.py b/nova/tests/db/test_db_api.py index 7ba431695..deaf8d035 100644 --- a/nova/tests/db/test_db_api.py +++ b/nova/tests/db/test_db_api.py @@ -3986,12 +3986,15 @@ class BlockDeviceMappingTestCase(test.TestCase): def test_block_device_mapping_update(self): bdm = self._create_bdm({}) - db.block_device_mapping_update(self.ctxt, bdm['id'], - {'destination_type': 'moon'}, - legacy=False) + result = db.block_device_mapping_update( + self.ctxt, bdm['id'], {'destination_type': 'moon'}, + legacy=False) uuid = bdm['instance_uuid'] bdm_real = db.block_device_mapping_get_all_by_instance(self.ctxt, uuid) self.assertEqual(bdm_real[0]['destination_type'], 'moon') + # Also make sure the update call returned correct data + self.assertEqual(dict(bdm_real[0].iteritems()), + dict(result.iteritems())) def test_block_device_mapping_update_or_create(self): values = { |