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:
parent
954cb8ca3e
commit
2d7a851199
@ -13,7 +13,7 @@
|
||||
"disabled_reason": null,
|
||||
"report_count": 1,
|
||||
"forced_down": false,
|
||||
"version": 5
|
||||
"version": 6
|
||||
}
|
||||
},
|
||||
"event_type": "service.update",
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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'),
|
||||
}
|
||||
|
||||
|
@ -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'},
|
||||
)
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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')
|
||||
|
@ -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):
|
||||
|
@ -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',
|
||||
|
@ -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')
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user