Allow sending the migrate data objects over the wire

This makes us actually capable of sending migrate_data objects
over the wire and handles all the compatibility work to tolerate
older nodes without this support.

Note this also fixes a bug in the libvirt migration data
object where the serial console field should be nullable
(per existing code) but wasn't. Since this is the patch that
actually lets this object be sent over the wire, we can update
the definition and the hash without needing a version bump.

Related to blueprint objectify-live-migrate-data

Change-Id: I8ad7a91910e269c27bc74389a9d4d2c7388313d6
This commit is contained in:
Dan Smith 2015-11-30 16:12:38 -08:00
parent 954cb8ca3e
commit 2d7a851199
13 changed files with 349 additions and 120 deletions

View File

@ -13,7 +13,7 @@
"disabled_reason": null,
"report_count": 1,
"forced_down": false,
"version": 5
"version": 6
}
},
"event_type": "service.update",

View File

@ -674,7 +674,7 @@ class ComputeVirtAPI(virtapi.VirtAPI):
class ComputeManager(manager.Manager):
"""Manages the running instances from creation to destruction."""
target = messaging.Target(version='4.7')
target = messaging.Target(version='4.8')
# How long to wait in seconds before re-issuing a shutdown
# signal to an instance during power off. The overall
@ -5054,9 +5054,7 @@ class ComputeManager(manager.Manager):
dest_check_data = self.driver.check_can_live_migrate_destination(ctxt,
instance, src_compute_info, dst_compute_info,
block_migration, disk_over_commit)
if isinstance(dest_check_data, migrate_data_obj.LiveMigrateData):
dest_check_data = dest_check_data.to_legacy_dict()
migrate_data = {}
LOG.debug('destination check data is %s', dest_check_data)
try:
migrate_data = self.compute_rpcapi.\
check_can_live_migrate_source(ctxt, instance,
@ -5064,8 +5062,6 @@ class ComputeManager(manager.Manager):
finally:
self.driver.check_can_live_migrate_destination_cleanup(ctxt,
dest_check_data)
if 'migrate_data' in dest_check_data:
migrate_data.update(dest_check_data['migrate_data'])
return migrate_data
@wrap_exception()
@ -5084,14 +5080,21 @@ class ComputeManager(manager.Manager):
"""
is_volume_backed = self.compute_api.is_volume_backed_instance(ctxt,
instance)
dest_check_data['is_volume_backed'] = is_volume_backed
got_migrate_data_object = isinstance(dest_check_data,
migrate_data_obj.LiveMigrateData)
if not got_migrate_data_object:
dest_check_data = \
migrate_data_obj.LiveMigrateData.detect_implementation(
dest_check_data)
dest_check_data.is_volume_backed = is_volume_backed
block_device_info = self._get_instance_block_device_info(
ctxt, instance, refresh_conn_info=True)
result = self.driver.check_can_live_migrate_source(ctxt, instance,
dest_check_data,
block_device_info)
if isinstance(result, migrate_data_obj.LiveMigrateData):
if not got_migrate_data_object:
result = result.to_legacy_dict()
LOG.debug('source check data is %s', result)
return result
@wrap_exception()
@ -5109,6 +5112,13 @@ class ComputeManager(manager.Manager):
storage.
"""
LOG.debug('pre_live_migration data is %s', migrate_data)
got_migrate_data_object = isinstance(migrate_data,
migrate_data_obj.LiveMigrateData)
if not got_migrate_data_object:
migrate_data = \
migrate_data_obj.LiveMigrateData.detect_implementation(
migrate_data)
block_device_info = self._get_instance_block_device_info(
context, instance, refresh_conn_info=True)
@ -5117,18 +5127,13 @@ class ComputeManager(manager.Manager):
context, instance, "live_migration.pre.start",
network_info=network_info)
pre_live_migration_data = self.driver.pre_live_migration(context,
migrate_data = self.driver.pre_live_migration(context,
instance,
block_device_info,
network_info,
disk,
migrate_data)
if isinstance(pre_live_migration_data,
migrate_data_obj.LiveMigrateData):
pre_live_migration_data = pre_live_migration_data.to_legacy_dict(
pre_migration_result=True)
pre_live_migration_data = pre_live_migration_data[
'pre_live_migration_result']
LOG.debug('driver pre_live_migration data is %s' % migrate_data)
# NOTE(tr3buchet): setup networks on destination host
self.network_api.setup_networks_on_host(context, instance,
@ -5147,22 +5152,12 @@ class ComputeManager(manager.Manager):
context, instance, "live_migration.pre.end",
network_info=network_info)
return pre_live_migration_data
def _get_migrate_data_obj(self):
# FIXME(danms): A couple patches from now, we'll be able to
# avoid this failure _if_ we get a new-style call with the
# object.
if CONF.compute_driver.startswith('libvirt'):
return objects.LibvirtLiveMigrateData()
elif CONF.compute_driver.startswith('xenapi'):
return objects.XenapiLiveMigrateData()
else:
LOG.error(_('Older RPC caller and unsupported virt driver in '
'use. Unable to handle this!'))
raise exception.MigrationError(
_('Unknown compute driver while providing compatibility '
'with older RPC formats'))
if not got_migrate_data_object and migrate_data:
migrate_data = migrate_data.to_legacy_dict(
pre_migration_result=True)
migrate_data = migrate_data['pre_live_migration_result']
LOG.debug('pre_live_migration result data is %s', migrate_data)
return migrate_data
def _do_live_migration(self, context, dest, instance, block_migration,
migration, migrate_data):
@ -5172,8 +5167,13 @@ class ComputeManager(manager.Manager):
# reporting
self._set_migration_status(migration, 'preparing')
# Create a local copy since we'll be modifying the dictionary
migrate_data = dict(migrate_data or {})
got_migrate_data_object = isinstance(migrate_data,
migrate_data_obj.LiveMigrateData)
if not got_migrate_data_object:
migrate_data = \
migrate_data_obj.LiveMigrateData.detect_implementation(
migrate_data)
try:
if block_migration:
block_device_info = self._get_instance_block_device_info(
@ -5183,12 +5183,9 @@ class ComputeManager(manager.Manager):
else:
disk = None
pre_migration_data = self.compute_rpcapi.pre_live_migration(
migrate_data = self.compute_rpcapi.pre_live_migration(
context, instance,
block_migration, disk, dest, migrate_data)
migrate_data['pre_live_migration_result'] = pre_migration_data
migrate_data_object = self._get_migrate_data_obj()
migrate_data_object.from_legacy_dict(migrate_data)
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_LE('Pre live migration failed at %s'),
@ -5199,12 +5196,14 @@ class ComputeManager(manager.Manager):
self._set_migration_status(migration, 'running')
migrate_data_object.migration = migration
if migrate_data:
migrate_data.migration = migration
LOG.debug('live_migration data is %s', migrate_data)
try:
self.driver.live_migration(context, instance, dest,
self._post_live_migration,
self._rollback_live_migration,
block_migration, migrate_data_object)
block_migration, migrate_data)
except Exception:
# Executing live migration
# live_migration might raises exceptions, but
@ -5481,11 +5480,13 @@ class ComputeManager(manager.Manager):
instance.task_state = None
instance.save(expected_task_state=[task_states.MIGRATING])
# NOTE(danms): Pop out the migration object so we don't pass
# it over RPC unintentionally below
if isinstance(migrate_data, dict):
migration = migrate_data.pop('migration', None)
elif isinstance(migrate_data, migrate_data_obj.LiveMigrateData):
migrate_data = \
migrate_data_obj.LiveMigrateData.detect_implementation(
migrate_data)
elif (isinstance(migrate_data, migrate_data_obj.LiveMigrateData) and
migrate_data.obj_attr_is_set('migration')):
migration = migrate_data.migration
else:
migration = None
@ -5507,8 +5508,6 @@ class ComputeManager(manager.Manager):
block_migration, migrate_data)
if do_cleanup:
if isinstance(migrate_data, migrate_data_obj.LiveMigrateData):
migrate_data = migrate_data.to_legacy_dict()
self.compute_rpcapi.rollback_live_migration_at_destination(
context, instance, dest, destroy_disks=destroy_disks,
migrate_data=migrate_data)
@ -5549,6 +5548,10 @@ class ComputeManager(manager.Manager):
# from remote volumes if necessary
block_device_info = self._get_instance_block_device_info(context,
instance)
if isinstance(migrate_data, dict):
migrate_data = \
migrate_data_obj.LiveMigrateData.detect_implementation(
migrate_data)
self.driver.rollback_live_migration_at_destination(
context, instance, network_info, block_device_info,
destroy_disks=destroy_disks, migrate_data=migrate_data)

View File

@ -25,6 +25,7 @@ from nova import exception
from nova.i18n import _, _LI, _LE
from nova import objects
from nova.objects import base as objects_base
from nova.objects import migrate_data as migrate_data_obj
from nova.objects import service as service_obj
from nova import rpc
@ -320,6 +321,9 @@ class ComputeAPI(object):
* ... - Remove refresh_security_group_rules()
* 4.6 - Add trigger_crash_dump()
* 4.7 - Add attachment_id argument to detach_volume()
* 4.8 - Send migrate_data in object format for live_migration,
rollback_live_migration_at_destination, and
pre_live_migration.
'''
VERSION_ALIASES = {
@ -430,18 +434,37 @@ class ComputeAPI(object):
block_migration, disk_over_commit):
version = '4.0'
cctxt = self.client.prepare(server=destination, version=version)
return cctxt.call(ctxt, 'check_can_live_migrate_destination',
instance=instance,
block_migration=block_migration,
disk_over_commit=disk_over_commit)
result = cctxt.call(ctxt, 'check_can_live_migrate_destination',
instance=instance,
block_migration=block_migration,
disk_over_commit=disk_over_commit)
if isinstance(result, migrate_data_obj.LiveMigrateData):
return result
elif result:
return migrate_data_obj.LiveMigrateData.detect_implementation(
result)
else:
return result
def check_can_live_migrate_source(self, ctxt, instance, dest_check_data):
version = '4.0'
dest_check_data_obj = dest_check_data
version = '4.8'
if not self.client.can_send_version(version):
version = '4.0'
if dest_check_data:
dest_check_data = dest_check_data.to_legacy_dict()
source = _compute_host(None, instance)
cctxt = self.client.prepare(server=source, version=version)
return cctxt.call(ctxt, 'check_can_live_migrate_source',
instance=instance,
dest_check_data=dest_check_data)
result = cctxt.call(ctxt, 'check_can_live_migrate_source',
instance=instance,
dest_check_data=dest_check_data)
if isinstance(result, migrate_data_obj.LiveMigrateData):
return result
elif dest_check_data_obj and result:
dest_check_data_obj.from_legacy_dict(result)
return dest_check_data_obj
else:
return result
def check_instance_shared_storage(self, ctxt, instance, data, host=None):
version = '4.0'
@ -599,7 +622,12 @@ class ComputeAPI(object):
def live_migration(self, ctxt, instance, dest, block_migration, host,
migration, migrate_data=None):
args = {'migration': migration}
version = '4.2'
version = '4.8'
if not self.client.can_send_version(version):
version = '4.2'
if migrate_data:
migrate_data = migrate_data.to_legacy_dict(
pre_migration_result=True)
if not self.client.can_send_version(version):
version = '4.0'
cctxt = self.client.prepare(server=host, version=version)
@ -622,12 +650,24 @@ class ComputeAPI(object):
def pre_live_migration(self, ctxt, instance, block_migration, disk,
host, migrate_data=None):
version = '4.0'
migrate_data_orig = migrate_data
version = '4.8'
if not self.client.can_send_version(version):
version = '4.0'
if migrate_data:
migrate_data = migrate_data.to_legacy_dict()
cctxt = self.client.prepare(server=host, version=version)
return cctxt.call(ctxt, 'pre_live_migration',
instance=instance,
block_migration=block_migration,
disk=disk, migrate_data=migrate_data)
result = cctxt.call(ctxt, 'pre_live_migration',
instance=instance,
block_migration=block_migration,
disk=disk, migrate_data=migrate_data)
if isinstance(result, migrate_data_obj.LiveMigrateData):
return result
elif migrate_data_orig and result:
migrate_data_orig.from_legacy_dict(result)
return migrate_data_orig
else:
return result
def prep_resize(self, ctxt, image, instance, instance_type, host,
reservations=None, request_spec=None,
@ -773,7 +813,11 @@ class ComputeAPI(object):
def rollback_live_migration_at_destination(self, ctxt, instance, host,
destroy_disks=True,
migrate_data=None):
version = '4.0'
version = '4.8'
if not self.client.can_send_version(version):
version = '4.0'
if migrate_data:
migrate_data = migrate_data.to_legacy_dict()
extra = {'destroy_disks': destroy_disks,
'migrate_data': migrate_data,
}

View File

@ -47,6 +47,19 @@ class LiveMigrateData(obj_base.NovaObject):
if 'migration' in legacy:
self.migration = legacy['migration']
@classmethod
def detect_implementation(cls, legacy_dict):
if 'instance_relative_path' in legacy_dict:
obj = LibvirtLiveMigrateData()
elif 'image_type' in legacy_dict:
obj = LibvirtLiveMigrateData()
elif 'migrate_data' in legacy_dict:
obj = XenapiLiveMigrateData()
else:
obj = LiveMigrateData()
obj.from_legacy_dict(legacy_dict)
return obj
@obj_base.NovaObjectRegistry.register
class LibvirtLiveMigrateBDMInfo(obj_base.NovaObject):
@ -105,7 +118,7 @@ class LibvirtLiveMigrateData(LiveMigrateData):
'instance_relative_path': fields.StringField(),
'graphics_listen_addr_vnc': fields.IPAddressField(nullable=True),
'graphics_listen_addr_spice': fields.IPAddressField(nullable=True),
'serial_listen_addr': fields.StringField(),
'serial_listen_addr': fields.StringField(nullable=True),
'bdms': fields.ListOfObjectsField('LibvirtLiveMigrateBDMInfo'),
}

View File

@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__)
# NOTE(danms): This is the global service version counter
SERVICE_VERSION = 5
SERVICE_VERSION = 6
# NOTE(danms): This is our SERVICE_VERSION history. The idea is that any
@ -63,6 +63,8 @@ SERVICE_VERSION_HISTORY = (
{'compute_rpc': '4.6'},
# Version 5: Add attachment_id kwarg to detach_volume()
{'compute_rpc': '4.7'},
# Version 6: Compute RPC version 4.8
{'compute_rpc': '4.8'},
)

View File

@ -5566,6 +5566,7 @@ class ComputeTestCase(BaseTestCase):
'source_type': 'volume',
'destination_type': 'volume'}))
]
migrate_data = migrate_data_obj.LiveMigrateData()
# creating mocks
self.mox.StubOutWithMock(self.compute.driver,
@ -5589,7 +5590,7 @@ class ComputeTestCase(BaseTestCase):
block_device_info=block_device_info).AndReturn('fake_disk')
self.compute.compute_rpcapi.pre_live_migration(c,
instance, True, 'fake_disk', dest_host,
{}).AndRaise(test.TestingException())
migrate_data).AndRaise(test.TestingException())
self.compute.network_api.setup_networks_on_host(c,
instance, self.compute.host)
@ -5600,7 +5601,8 @@ class ComputeTestCase(BaseTestCase):
self.compute.compute_rpcapi.remove_volume_connection(
c, instance, uuids.volume_id_2, dest_host)
self.compute.compute_rpcapi.rollback_live_migration_at_destination(
c, instance, dest_host, destroy_disks=True, migrate_data={})
c, instance, dest_host, destroy_disks=True,
migrate_data=mox.IsA(migrate_data_obj.LiveMigrateData))
# start test
self.mox.ReplayAll()
@ -5609,7 +5611,7 @@ class ComputeTestCase(BaseTestCase):
self.compute.live_migration,
c, dest=dest_host, block_migration=True,
instance=instance, migration=migration,
migrate_data={})
migrate_data=migrate_data)
instance.refresh()
self.assertEqual('src_host', instance.host)
self.assertEqual(vm_states.ACTIVE, instance.vm_state)
@ -5626,12 +5628,15 @@ class ComputeTestCase(BaseTestCase):
instance.host = self.compute.host
dest = 'desthost'
migrate_data = {'is_shared_instance_path': False}
migrate_data = migrate_data_obj.LibvirtLiveMigrateData(
is_shared_instance_path=False,
is_shared_block_storage=False)
self.mox.StubOutWithMock(self.compute.compute_rpcapi,
'pre_live_migration')
self.compute.compute_rpcapi.pre_live_migration(
c, instance, False, None, dest, migrate_data)
c, instance, False, None, dest, migrate_data).AndReturn(
migrate_data)
self.mox.StubOutWithMock(self.compute.network_api,
'migrate_instance_start')
@ -5655,13 +5660,11 @@ class ComputeTestCase(BaseTestCase):
migration = objects.Migration()
with mock.patch.object(self.compute, '_get_migrate_data_obj') as gmdo:
gmdo.return_value = migrate_data_obj.LiveMigrateData()
ret = self.compute.live_migration(c, dest=dest,
instance=instance,
block_migration=False,
migration=migration,
migrate_data=migrate_data)
ret = self.compute.live_migration(c, dest=dest,
instance=instance,
block_migration=False,
migration=migration,
migrate_data=migrate_data)
self.assertIsNone(ret)
event_mock.assert_called_with(
@ -5983,16 +5986,19 @@ class ComputeTestCase(BaseTestCase):
side_effect=test.TestingException)
@mock.patch('nova.virt.driver.ComputeDriver.'
'rollback_live_migration_at_destination')
@mock.patch('nova.objects.migrate_data.LiveMigrateData.'
'detect_implementation')
def test_rollback_live_migration_at_destination_network_fails(
self, mock_rollback, net_mock):
self, mock_detect, mock_rollback, net_mock):
c = context.get_admin_context()
instance = self._create_fake_instance_obj()
self.assertRaises(test.TestingException,
self.compute.rollback_live_migration_at_destination,
c, instance, destroy_disks=True, migrate_data={})
mock_rollback.assert_called_once_with(c, instance, mock.ANY, mock.ANY,
destroy_disks=True,
migrate_data={})
mock_rollback.assert_called_once_with(
c, instance, mock.ANY, mock.ANY,
destroy_disks=True,
migrate_data=mock_detect.return_value)
def test_run_kill_vm(self):
# Detect when a vm is terminated behind the scenes.

View File

@ -1766,12 +1766,10 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
@mock.patch.object(compute_utils, 'EventReporter')
def test_check_can_live_migrate_source(self, event_mock):
is_volume_backed = 'volume_backed'
dest_check_data = dict(foo='bar')
dest_check_data = migrate_data_obj.LiveMigrateData()
db_instance = fake_instance.fake_db_instance()
instance = objects.Instance._from_db_object(
self.context, objects.Instance(), db_instance)
expected_dest_check_data = dict(dest_check_data,
is_volume_backed=is_volume_backed)
self.mox.StubOutWithMock(self.compute.compute_api,
'is_volume_backed_instance')
@ -1786,7 +1784,7 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
self.context, instance, refresh_conn_info=True
).AndReturn({'block_device_mapping': 'fake'})
self.compute.driver.check_can_live_migrate_source(
self.context, instance, expected_dest_check_data,
self.context, instance, dest_check_data,
{'block_device_mapping': 'fake'})
self.mox.ReplayAll()
@ -1797,11 +1795,11 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
event_mock.assert_called_once_with(
self.context, 'compute_check_can_live_migrate_source',
instance.uuid)
self.assertTrue(dest_check_data.is_volume_backed)
@mock.patch.object(compute_utils, 'EventReporter')
def _test_check_can_live_migrate_destination(self, event_mock,
do_raise=False,
has_mig_data=False):
do_raise=False):
db_instance = fake_instance.fake_db_instance(host='fake-host')
instance = objects.Instance._from_db_object(
self.context, objects.Instance(), db_instance)
@ -1812,10 +1810,6 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
dest_info = 'dest_info'
dest_check_data = dict(foo='bar')
mig_data = dict(cow='moo')
expected_result = dict(mig_data)
if has_mig_data:
dest_check_data['migrate_data'] = dict(cat='meow')
expected_result.update(cat='meow')
self.mox.StubOutWithMock(self.compute, '_get_compute_info')
self.mox.StubOutWithMock(self.compute.driver,
@ -1852,7 +1846,7 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
self.context, instance=instance,
block_migration=block_migration,
disk_over_commit=disk_over_commit)
self.assertEqual(expected_result, result)
self.assertEqual(mig_data, result)
event_mock.assert_called_once_with(
self.context, 'compute_check_can_live_migrate_destination',
instance.uuid)
@ -1860,9 +1854,6 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
def test_check_can_live_migrate_destination_success(self):
self._test_check_can_live_migrate_destination()
def test_check_can_live_migrate_destination_success_w_mig_data(self):
self._test_check_can_live_migrate_destination(has_mig_data=True)
def test_check_can_live_migrate_destination_fail(self):
self.assertRaises(
test.TestingException,
@ -4270,7 +4261,7 @@ class ComputeManagerMigrationTestCase(test.NoDBTestCase):
def test_check_migrate_source_converts_object(self):
# NOTE(danms): Make sure that we legacy-ify any data objects
# the drivers give us back, until we're ready for them
# the drivers give us back, if we were passed a non-object
data = migrate_data_obj.LiveMigrateData(is_volume_backed=False)
compute = manager.ComputeManager()
@ -4283,28 +4274,60 @@ class ComputeManagerMigrationTestCase(test.NoDBTestCase):
compute.check_can_live_migrate_source(
self.context, {'uuid': uuids.instance}, {}),
dict)
self.assertIsInstance(mock_cclms.call_args_list[0][0][2],
migrate_data_obj.LiveMigrateData)
_test()
def test_check_migrate_destination_converts_object(self):
# NOTE(danms): Make sure that we legacy-ify any data objects
# the drivers give us back, until we're ready for them
data = migrate_data_obj.LiveMigrateData(is_volume_backed=False)
inst = objects.Instance(id=1, uuid=uuids.instance, host='bar')
def test_pre_live_migration_handles_dict(self):
compute = manager.ComputeManager()
@mock.patch.object(compute.driver,
'check_can_live_migrate_destination')
@mock.patch.object(compute.compute_rpcapi,
'check_can_live_migrate_source')
@mock.patch.object(compute, '_get_compute_info')
def _test(mock_gci, mock_cclms, mock_cclmd):
mock_gci.return_value = inst
mock_cclmd.return_value = data
mock_cclms.return_value = {}
result = compute.check_can_live_migrate_destination(
self.context, inst, False, False)
self.assertIsInstance(mock_cclms.call_args_list[0][0][2], dict)
self.assertIsInstance(result, dict)
@mock.patch.object(compute, '_notify_about_instance_usage')
@mock.patch.object(compute, 'network_api')
@mock.patch.object(compute.driver, 'pre_live_migration')
@mock.patch.object(compute, '_get_instance_block_device_info')
@mock.patch.object(compute.compute_api, 'is_volume_backed_instance')
def _test(mock_ivbi, mock_gibdi, mock_plm, mock_nwapi, mock_notify):
migrate_data = migrate_data_obj.LiveMigrateData()
mock_plm.return_value = migrate_data
r = compute.pre_live_migration(self.context, {'uuid': 'foo'},
False, {}, {})
self.assertIsInstance(r, dict)
self.assertIsInstance(mock_plm.call_args_list[0][0][5],
migrate_data_obj.LiveMigrateData)
_test()
def test_live_migration_handles_dict(self):
compute = manager.ComputeManager()
@mock.patch.object(compute, 'compute_rpcapi')
@mock.patch.object(compute, 'driver')
def _test(mock_driver, mock_rpc):
migrate_data = migrate_data_obj.LiveMigrateData()
migration = objects.Migration()
migration.save = mock.MagicMock()
mock_rpc.pre_live_migration.return_value = migrate_data
compute._do_live_migration(self.context, 'foo', {'uuid': 'foo'},
False, migration, {})
self.assertIsInstance(
mock_rpc.pre_live_migration.call_args_list[0][0][5],
migrate_data_obj.LiveMigrateData)
_test()
def test_rollback_live_migration_handles_dict(self):
compute = manager.ComputeManager()
@mock.patch.object(compute, 'network_api')
@mock.patch.object(compute, '_notify_about_instance_usage')
@mock.patch.object(compute, '_live_migration_cleanup_flags')
@mock.patch('nova.objects.BlockDeviceMappingList.get_by_instance_uuid')
def _test(mock_bdm, mock_lmcf, mock_notify, mock_nwapi):
mock_bdm.return_value = []
mock_lmcf.return_value = False, False
self.compute._rollback_live_migration(self.context,
mock.MagicMock(),
'foo', False, {})
self.assertIsInstance(mock_lmcf.call_args_list[0][0][1],
migrate_data_obj.LiveMigrateData)

View File

@ -24,6 +24,7 @@ from nova.compute import rpcapi as compute_rpcapi
from nova import context
from nova import exception
from nova.objects import block_device as objects_block_dev
from nova.objects import migrate_data as migrate_data_obj
from nova import test
from nova.tests.unit import fake_block_device
from nova.tests.unit import fake_flavor
@ -301,7 +302,7 @@ class ComputeRpcAPITestCase(test.NoDBTestCase):
instance=self.fake_instance_obj, dest='dest',
block_migration='blockity_block', host='tsoh',
migration='migration',
migrate_data={}, version='4.2')
migrate_data={}, version='4.8')
def test_post_live_migration_at_destination(self):
self._test_compute_api('post_live_migration_at_destination', 'cast',
@ -330,7 +331,7 @@ class ComputeRpcAPITestCase(test.NoDBTestCase):
self._test_compute_api('pre_live_migration', 'call',
instance=self.fake_instance_obj,
block_migration='block_migration', disk='disk', host='host',
migrate_data=None, version='4.0')
migrate_data=None, version='4.8')
def test_prep_resize(self):
self._test_compute_api('prep_resize', 'cast',
@ -551,3 +552,111 @@ class ComputeRpcAPITestCase(test.NoDBTestCase):
self._test_compute_api,
'trigger_crash_dump', 'cast',
instance=self.fake_instance_obj, version='4.6')
def _test_simple_call(self, method, inargs, callargs, callret,
calltype='call', can_send=False):
rpc = compute_rpcapi.ComputeAPI()
@mock.patch.object(rpc, 'client')
@mock.patch.object(compute_rpcapi, '_compute_host')
def _test(mock_ch, mock_client):
mock_client.can_send_version.return_value = can_send
call = getattr(mock_client.prepare.return_value, calltype)
call.return_value = callret
ctxt = mock.MagicMock()
result = getattr(rpc, method)(ctxt, **inargs)
call.assert_called_once_with(ctxt, method, **callargs)
return result
return _test()
def test_check_can_live_migrate_source_converts_objects(self):
obj = migrate_data_obj.LiveMigrateData()
result = self._test_simple_call('check_can_live_migrate_source',
inargs={'instance': 'foo',
'dest_check_data': obj},
callargs={'instance': 'foo',
'dest_check_data': {}},
callret=obj)
self.assertEqual(obj, result)
result = self._test_simple_call('check_can_live_migrate_source',
inargs={'instance': 'foo',
'dest_check_data': obj},
callargs={'instance': 'foo',
'dest_check_data': {}},
callret={'foo': 'bar'})
self.assertIsInstance(result, migrate_data_obj.LiveMigrateData)
@mock.patch('nova.objects.migrate_data.LiveMigrateData.'
'detect_implementation')
def test_check_can_live_migrate_destination_converts_dict(self,
mock_det):
result = self._test_simple_call('check_can_live_migrate_destination',
inargs={'instance': 'foo',
'destination': 'bar',
'block_migration': False,
'disk_over_commit': False},
callargs={'instance': 'foo',
'block_migration': False,
'disk_over_commit': False},
callret={'foo': 'bar'})
self.assertEqual(mock_det.return_value, result)
def test_live_migration_converts_objects(self):
obj = migrate_data_obj.LiveMigrateData()
self._test_simple_call('live_migration',
inargs={'instance': 'foo',
'dest': 'foo',
'block_migration': False,
'host': 'foo',
'migration': None,
'migrate_data': obj},
callargs={'instance': 'foo',
'dest': 'foo',
'block_migration': False,
'migration': None,
'migrate_data': {
'pre_live_migration_result': {}}},
callret=None,
calltype='cast')
def test_pre_live_migration_converts_objects(self):
obj = migrate_data_obj.LiveMigrateData()
result = self._test_simple_call('pre_live_migration',
inargs={'instance': 'foo',
'block_migration': False,
'disk': None,
'host': 'foo',
'migrate_data': obj},
callargs={'instance': 'foo',
'block_migration': False,
'disk': None,
'migrate_data': {}},
callret=obj)
self.assertEqual(obj, result)
result = self._test_simple_call('pre_live_migration',
inargs={'instance': 'foo',
'block_migration': False,
'disk': None,
'host': 'foo',
'migrate_data': obj},
callargs={'instance': 'foo',
'block_migration': False,
'disk': None,
'migrate_data': {}},
callret={'foo': 'bar'})
self.assertIsInstance(result, migrate_data_obj.LiveMigrateData)
def test_rollback_live_migration_at_destination_converts_objects(self):
obj = migrate_data_obj.LiveMigrateData()
method = 'rollback_live_migration_at_destination'
self._test_simple_call(method,
inargs={'instance': 'foo',
'host': 'foo',
'destroy_disks': False,
'migrate_data': obj},
callargs={'instance': 'foo',
'destroy_disks': False,
'migrate_data': {}},
callret=None,
calltype='cast')

View File

@ -42,6 +42,34 @@ class _TestLiveMigrateData(object):
'is_volume_backed': False},
obj.to_legacy_dict(pre_migration_result=True))
def test_detect_implementation_none(self):
legacy = migrate_data.LiveMigrateData().to_legacy_dict()
self.assertIsInstance(
migrate_data.LiveMigrateData.detect_implementation(legacy),
migrate_data.LiveMigrateData)
def test_detect_implementation_libvirt(self):
legacy = migrate_data.LibvirtLiveMigrateData(
instance_relative_path='foo').to_legacy_dict()
self.assertIsInstance(
migrate_data.LiveMigrateData.detect_implementation(legacy),
migrate_data.LibvirtLiveMigrateData)
def test_detect_implementation_libvirt_early(self):
legacy = migrate_data.LibvirtLiveMigrateData(
image_type='foo').to_legacy_dict()
self.assertIsInstance(
migrate_data.LiveMigrateData.detect_implementation(legacy),
migrate_data.LibvirtLiveMigrateData)
def test_detect_implementation_xenapi(self):
legacy = migrate_data.XenapiLiveMigrateData(
migrate_send_data={},
destination_sr_ref='foo').to_legacy_dict()
self.assertIsInstance(
migrate_data.LiveMigrateData.detect_implementation(legacy),
migrate_data.XenapiLiveMigrateData)
class TestLiveMigrateData(test_objects._LocalTest,
_TestLiveMigrateData):

View File

@ -1156,7 +1156,7 @@ object_data = {
'InstancePCIRequest': '1.1-b1d75ebc716cb12906d9d513890092bf',
'InstancePCIRequests': '1.1-65e38083177726d806684cb1cc0136d2',
'LibvirtLiveMigrateBDMInfo': '1.0-252aabb723ca79d5469fa56f64b57811',
'LibvirtLiveMigrateData': '1.0-eb8b5f6c49ae3858213a7012558a2f3d',
'LibvirtLiveMigrateData': '1.0-7d257257e7f98198b2e4982c33cf3fa5',
'KeyPair': '1.3-bfaa2a8b148cdf11e0c72435d9dd097a',
'KeyPairList': '1.2-58b94f96e776bedaf1e192ddb2a24c4e',
'Migration': '1.2-8784125bedcea0a9227318511904e853',

View File

@ -6358,10 +6358,11 @@ class LibvirtConnTestCase(test.NoDBTestCase):
def test_check_can_live_migrate_dest_cleanup_works_correctly(self):
objects.Instance(**self.test_instance)
dest_check_data = {"filename": "file",
"block_migration": True,
"disk_over_commit": False,
"disk_available_mb": 1024}
dest_check_data = objects.LibvirtLiveMigrateData(
filename="file",
block_migration=True,
disk_over_commit=False,
disk_available_mb=1024)
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
self.mox.StubOutWithMock(drvr, '_cleanup_shared_storage_test_file')

View File

@ -954,7 +954,7 @@ class ComputeDriver(object):
:param dst_compute_info: Info about the receiving machine
:param block_migration: if true, prepare for block migration
:param disk_over_commit: if true, allow disk over commit
:returns: a dict containing migration info (hypervisor-dependent)
:returns: a LiveMigrateData object (hypervisor-dependent)
"""
raise NotImplementedError()

View File

@ -5396,7 +5396,7 @@ class LibvirtDriver(driver.ComputeDriver):
:param context: security context
"""
filename = dest_check_data["filename"]
filename = dest_check_data.filename
self._cleanup_shared_storage_test_file(filename)
def check_can_live_migrate_source(self, context, instance,