xapi: Fix live block migration

Fixes bug 1040332.

Xapi VM.migrate_send and VM.assert_can_migrate calls require that
vdi_map parameter is a (source_vdi_ref -> target_sr_ref) mapping, for block
live migration to work, as of XenServer 6.0 RC1.

On the destination side:
This fix modifies the check_can_live_migrate_destination call, so that the
value returned contains the "destination_sr_ref" (XenAPI specific data is
stored under the "migrate_send_data key").

On the source side:
check_can_live_migrate_source and live_migrate calls assemble the
vdi_map by mapping all the local sr contained vdis of the VM to
destination_sr_ref, and passing this parameter to the VM.migrate_send and
VM.assert_can_migrate Xapi calls.

Change-Id: I95f3dca651d2e72fc727646580092a25f558d6ba
This commit is contained in:
John Garbutt 2012-09-07 12:17:48 +01:00 committed by Mate Lakat
parent f6e9582788
commit dcff384889
2 changed files with 158 additions and 17 deletions

View File

@ -49,7 +49,6 @@ from nova.virt.xenapi import vm_utils
from nova.virt.xenapi import vmops from nova.virt.xenapi import vmops
from nova.virt.xenapi import volume_utils from nova.virt.xenapi import volume_utils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
@ -2171,18 +2170,18 @@ class XenAPILiveMigrateTestCase(stubs.XenAPITestBase):
def test_check_can_live_migrate_destination_with_block_migration(self): def test_check_can_live_migrate_destination_with_block_migration(self):
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
self.conn = xenapi_conn.XenAPIDriver(False) self.conn = xenapi_conn.XenAPIDriver(False)
self.stubs.Set(vm_utils, "safe_find_sr", lambda _x: "asdf")
expected = {'block_migration': True, expected = {'block_migration': True,
'migrate_data': {'xenops': '', 'migrate_data': {
'host': '', 'migrate_send_data': "fake_migrate_data",
'master': '', 'destination_sr_ref': 'asdf'
'session_id': '',
'SM': ''}
} }
fake_data = self.conn.check_can_live_migrate_destination(self.context, }
result = self.conn.check_can_live_migrate_destination(self.context,
{'host': 'host'}, True, False) {'host': 'host'}, True, False)
self.assertEqual(expected.keys(), fake_data.keys()) self.assertEqual(expected, result)
self.assertEqual(expected['migrate_data'].keys(),
fake_data['migrate_data'].keys())
def test_check_can_live_migrate_destination_block_migration_fails(self): def test_check_can_live_migrate_destination_block_migration_fails(self):
stubs.stubout_session(self.stubs, stubs.stubout_session(self.stubs,
@ -2196,13 +2195,22 @@ class XenAPILiveMigrateTestCase(stubs.XenAPITestBase):
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
self.conn = xenapi_conn.XenAPIDriver(False) self.conn = xenapi_conn.XenAPIDriver(False)
def fake_generate_vdi_map(destination_sr_ref, _vm_ref):
pass
self.stubs.Set(self.conn._vmops, "_generate_vdi_map",
fake_generate_vdi_map)
def fake_get_vm_opaque_ref(instance): def fake_get_vm_opaque_ref(instance):
return "fake_vm" return "fake_vm"
self.stubs.Set(self.conn._vmops, "_get_vm_opaque_ref", self.stubs.Set(self.conn._vmops, "_get_vm_opaque_ref",
fake_get_vm_opaque_ref) fake_get_vm_opaque_ref)
dest_check_data = {'block_migration': True, dest_check_data = {'block_migration': True,
'migrate_data': {}} 'migrate_data': {
'destination_sr_ref': None,
'migrate_send_data': None
}}
self.assertNotRaises(None, self.assertNotRaises(None,
self.conn.check_can_live_migrate_source, self.conn.check_can_live_migrate_source,
self.context, self.context,
@ -2210,16 +2218,27 @@ class XenAPILiveMigrateTestCase(stubs.XenAPITestBase):
dest_check_data) dest_check_data)
def test_check_can_live_migrate_source_with_block_migrate_fails(self): def test_check_can_live_migrate_source_with_block_migrate_fails(self):
def fake_get_vm_opaque_ref(instance):
return "fake_vm"
stubs.stubout_session(self.stubs, stubs.stubout_session(self.stubs,
stubs.FakeSessionForFailedMigrateTests) stubs.FakeSessionForFailedMigrateTests)
self.conn = xenapi_conn.XenAPIDriver(False) self.conn = xenapi_conn.XenAPIDriver(False)
def fake_generate_vdi_map(destination_sr_ref, _vm_ref):
pass
self.stubs.Set(self.conn._vmops, "_generate_vdi_map",
fake_generate_vdi_map)
def fake_get_vm_opaque_ref(instance):
return "fake_vm"
self.stubs.Set(self.conn._vmops, "_get_vm_opaque_ref", self.stubs.Set(self.conn._vmops, "_get_vm_opaque_ref",
fake_get_vm_opaque_ref) fake_get_vm_opaque_ref)
dest_check_data = {'block_migration': True, dest_check_data = {'block_migration': True,
'migrate_data': {}} 'migrate_data': {
'destination_sr_ref': None,
'migrate_send_data': None
}}
self.assertRaises(exception.MigrationError, self.assertRaises(exception.MigrationError,
self.conn.check_can_live_migrate_source, self.conn.check_can_live_migrate_source,
self.context, self.context,
@ -2310,12 +2329,19 @@ class XenAPILiveMigrateTestCase(stubs.XenAPITestBase):
self.conn, None, None, None, recover_method) self.conn, None, None, None, recover_method)
self.assertTrue(recover_method.called, "recover_method.called") self.assertTrue(recover_method.called, "recover_method.called")
def test_live_migration_with_block_migration(self): def test_live_migration_calls_post_migration(self):
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
self.conn = xenapi_conn.XenAPIDriver(False) self.conn = xenapi_conn.XenAPIDriver(False)
def fake_generate_vdi_map(destination_sr_ref, _vm_ref):
pass
self.stubs.Set(self.conn._vmops, "_generate_vdi_map",
fake_generate_vdi_map)
def fake_get_vm_opaque_ref(instance): def fake_get_vm_opaque_ref(instance):
return "fake_vm" return "fake_vm"
self.stubs.Set(self.conn._vmops, "_get_vm_opaque_ref", self.stubs.Set(self.conn._vmops, "_get_vm_opaque_ref",
fake_get_vm_opaque_ref) fake_get_vm_opaque_ref)
@ -2324,7 +2350,8 @@ class XenAPILiveMigrateTestCase(stubs.XenAPITestBase):
post_method.called = True post_method.called = True
# pass block_migration = True and migrate data # pass block_migration = True and migrate data
migrate_data = {"test": "data"} migrate_data = {"destination_sr_ref": "foo",
"migrate_send_data": "bar"}
self.conn.live_migration(self.conn, None, None, post_method, None, self.conn.live_migration(self.conn, None, None, post_method, None,
True, migrate_data) True, migrate_data)
self.assertTrue(post_method.called, "post_method.called") self.assertTrue(post_method.called, "post_method.called")
@ -2357,16 +2384,82 @@ class XenAPILiveMigrateTestCase(stubs.XenAPITestBase):
self.stubs.Set(self.conn._vmops, "_get_vm_opaque_ref", self.stubs.Set(self.conn._vmops, "_get_vm_opaque_ref",
fake_get_vm_opaque_ref) fake_get_vm_opaque_ref)
def fake_generate_vdi_map(destination_sr_ref, _vm_ref):
pass
self.stubs.Set(self.conn._vmops, "_generate_vdi_map",
fake_generate_vdi_map)
def recover_method(context, instance, destination_hostname, def recover_method(context, instance, destination_hostname,
block_migration): block_migration):
recover_method.called = True recover_method.called = True
# pass block_migration = True and migrate data # pass block_migration = True and migrate data
migrate_data = {"test": "data"} migrate_data = dict(destination_sr_ref='foo', migrate_send_data='bar')
self.assertRaises(exception.MigrationError, self.assertRaises(exception.MigrationError,
self.conn.live_migration, self.conn, self.conn.live_migration, self.conn,
None, None, None, recover_method, True, migrate_data) None, None, None, recover_method, True, migrate_data)
self.assertTrue(recover_method.called, "recover_method.called") self.assertTrue(recover_method.called, "recover_method.called")
def test_live_migrate_block_migration_xapi_call_parameters(self):
fake_vdi_map = object()
class Session(xenapi_fake.SessionBase):
def VM_migrate_send(self_, session, vmref, migrate_data, islive,
vdi_map, vif_map, options):
self.assertEquals('SOMEDATA', migrate_data)
self.assertEquals(fake_vdi_map, vdi_map)
stubs.stubout_session(self.stubs, Session)
conn = xenapi_conn.XenAPIDriver(False)
def fake_get_vm_opaque_ref(instance):
return "fake_vm"
self.stubs.Set(conn._vmops, "_get_vm_opaque_ref",
fake_get_vm_opaque_ref)
def fake_generate_vdi_map(destination_sr_ref, _vm_ref):
return fake_vdi_map
self.stubs.Set(conn._vmops, "_generate_vdi_map",
fake_generate_vdi_map)
def dummy_callback(*args, **kwargs):
pass
conn.live_migration(
self.context, instance_ref=dict(name='ignore'), dest=None,
post_method=dummy_callback, recover_method=dummy_callback,
block_migration="SOMEDATA",
migrate_data=dict(migrate_send_data='SOMEDATA',
destination_sr_ref="TARGET_SR_OPAQUE_REF"))
def test_generate_vdi_map(self):
stubs.stubout_session(self.stubs, xenapi_fake.SessionBase)
conn = xenapi_conn.XenAPIDriver(False)
vm_ref = "fake_vm_ref"
def fake_find_sr(_session):
self.assertEquals(conn._session, _session)
return "source_sr_ref"
self.stubs.Set(vm_utils, "safe_find_sr", fake_find_sr)
def fake_get_instance_vdis_for_sr(_session, _vm_ref, _sr_ref):
self.assertEquals(conn._session, _session)
self.assertEquals(vm_ref, _vm_ref)
self.assertEquals("source_sr_ref", _sr_ref)
return ["vdi0", "vdi1"]
self.stubs.Set(vm_utils, "get_instance_vdis_for_sr",
fake_get_instance_vdis_for_sr)
result = conn._vmops._generate_vdi_map("dest_sr_ref", vm_ref)
self.assertEquals({"vdi0": "dest_sr_ref",
"vdi1": "dest_sr_ref"}, result)
class XenAPIInjectMetadataTestCase(stubs.XenAPITestBase): class XenAPIInjectMetadataTestCase(stubs.XenAPITestBase):
def setUp(self): def setUp(self):

View File

@ -0,0 +1,48 @@
from nova.tests.xenapi import stubs
from nova.virt.xenapi import driver as xenapi_conn
from nova.virt.xenapi import fake
from nova.virt.xenapi import vm_utils
class GetInstanceForVdisForSrTestCase(stubs.XenAPITestBase):
def setUp(self):
super(GetInstanceForVdisForSrTestCase, self).setUp()
self.flags(disable_process_locking=True,
instance_name_template='%d',
firewall_driver='nova.virt.xenapi.firewall.'
'Dom0IptablesFirewallDriver',
xenapi_connection_url='test_url',
xenapi_connection_password='test_pass',)
def tearDown(self):
super(GetInstanceForVdisForSrTestCase, self).tearDown()
def test_get_instance_vdis_for_sr(self):
vm_ref = fake.create_vm("foo", "Running")
sr_ref = fake.create_sr()
vdi_1 = fake.create_vdi('vdiname1', sr_ref)
vdi_2 = fake.create_vdi('vdiname2', sr_ref)
for vdi_ref in [vdi_1, vdi_2]:
fake.create_vbd(vm_ref, vdi_ref)
stubs.stubout_session(self.stubs, fake.SessionBase)
driver = xenapi_conn.XenAPIDriver(False)
result = list(vm_utils.get_instance_vdis_for_sr(
driver._session, vm_ref, sr_ref))
self.assertEquals([vdi_1, vdi_2], result)
def test_get_instance_vdis_for_sr_no_vbd(self):
vm_ref = fake.create_vm("foo", "Running")
sr_ref = fake.create_sr()
stubs.stubout_session(self.stubs, fake.SessionBase)
driver = xenapi_conn.XenAPIDriver(False)
result = list(vm_utils.get_instance_vdis_for_sr(
driver._session, vm_ref, sr_ref))
self.assertEquals([], result)