summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/cells/manager.py13
-rw-r--r--nova/cells/messaging.py76
-rw-r--r--nova/cells/rpcapi.py34
-rw-r--r--nova/compute/cells_api.py4
-rw-r--r--nova/conductor/manager.py39
-rw-r--r--nova/db/sqlalchemy/api.py9
-rw-r--r--nova/tests/api/ec2/test_cloud.py1
-rw-r--r--nova/tests/cells/test_cells_manager.py24
-rw-r--r--nova/tests/cells/test_cells_messaging.py170
-rw-r--r--nova/tests/cells/test_cells_rpcapi.py26
-rw-r--r--nova/tests/conductor/test_conductor.py61
-rw-r--r--nova/tests/db/test_db_api.py9
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 = {