Split-network-plane-for-live-migration

When we do live migration with QEMU/KVM driver,
we use hostname of target compute node as the
target of live migration. So the RPC call and live
migration traffic will be in same network plane.

This patch adds a new option live_migration_inbound_addr
in configuration file, set None as default value.
When pre_live_migration() executes on destination host, set
the option into pre_migration_data, if it's not None.
When driver.live_migration() executes on source host,
if this option is present in pre_migration_data, the ip/hostname
address is used instead of CONF.libvirt.live_migration_uri
as the uri for live migration, if it's None, then the
mechanism remains as it is now.

This patch (BP) focuses only on the QEMU/KVM driver,
the implementations for other drivers should be done
in a separate blueprint.

DocImpact:new config option "live_migration_inbound_addr" will be added.

Change-Id: I81c783886497a844fb4b38d0f2a3d6c18a99831c
Co-Authored-By: Rui Chen <chenrui.momo@gmail.com>
Implements: blueprint split-network-plane-for-live-migration
This commit is contained in:
Kevin_Zheng 2015-11-13 14:14:28 +08:00
parent dcb63f0498
commit af41accff9
6 changed files with 127 additions and 21 deletions

View File

@ -14,6 +14,7 @@
from oslo_log import log
from oslo_serialization import jsonutils
from oslo_utils import versionutils
from nova import objects
from nova.objects import base as obj_base
@ -104,7 +105,9 @@ class LibvirtLiveMigrateBDMInfo(obj_base.NovaObject):
@obj_base.NovaObjectRegistry.register
class LibvirtLiveMigrateData(LiveMigrateData):
VERSION = '1.0'
# Version 1.0: Initial version
# Version 1.1: Added target_connect_addr
VERSION = '1.1'
fields = {
'filename': fields.StringField(),
@ -120,8 +123,16 @@ class LibvirtLiveMigrateData(LiveMigrateData):
'graphics_listen_addr_spice': fields.IPAddressField(nullable=True),
'serial_listen_addr': fields.StringField(nullable=True),
'bdms': fields.ListOfObjectsField('LibvirtLiveMigrateBDMInfo'),
'target_connect_addr': fields.StringField(nullable=True),
}
def obj_make_compatible(self, primitive, target_version):
super(LibvirtLiveMigrateData, self).obj_make_compatible(
primitive, target_version)
target_version = versionutils.convert_version_to_tuple(target_version)
if target_version < (1, 1) and 'target_connect_addr' in primitive:
del primitive['target_connect_addr']
def _bdms_to_legacy(self, legacy):
if not self.obj_attr_is_set('bdms'):
return
@ -157,12 +168,14 @@ class LibvirtLiveMigrateData(LiveMigrateData):
graphics_vnc = legacy.pop('graphics_listen_addr_vnc', None)
graphics_spice = legacy.pop('graphics_listen_addr_spice', None)
transport_target = legacy.pop('target_connect_addr', None)
live_result = {
'graphics_listen_addrs': {
'vnc': graphics_vnc and str(graphics_vnc),
'spice': graphics_spice and str(graphics_spice),
},
'serial_listen_addr': legacy.pop('serial_listen_addr', None),
'target_connect_addr': transport_target,
}
if pre_migration_result:
@ -185,6 +198,7 @@ class LibvirtLiveMigrateData(LiveMigrateData):
pre_result['graphics_listen_addrs'].get('vnc')
self.graphics_listen_addr_spice = \
pre_result['graphics_listen_addrs'].get('spice')
self.target_connect_addr = pre_result.get('target_connect_addr')
if 'serial_listen_addr' in pre_result:
self.serial_listen_addr = pre_result['serial_listen_addr']
self._bdms_from_legacy(pre_result)

View File

@ -165,6 +165,7 @@ class _TestLibvirtLiveMigrateData(object):
expected = {
'graphics_listen_addrs': {'vnc': '127.0.0.1',
'spice': None},
'target_connect_addr': None,
'serial_listen_addr': '127.0.0.1',
'volume': {
'123': {

View File

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

View File

@ -6843,6 +6843,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
graphics_listen_addr_vnc='10.0.0.1',
graphics_listen_addr_spice='10.0.0.2',
serial_listen_addr='127.0.0.1',
target_connect_addr=None,
bdms=[])
self.mox.ReplayAll()
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
@ -6880,6 +6881,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
connection_info=connection_info)
migrate_data = objects.LibvirtLiveMigrateData(
serial_listen_addr='',
target_connect_addr=None,
bdms=[bdm])
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
@ -6901,6 +6903,51 @@ class LibvirtConnTestCase(test.NoDBTestCase):
mupdate.assert_called_once_with(target_xml, migrate_data.bdms,
{}, '')
def test_live_migration_with_valid_target_connect_addr(self):
self.compute = importutils.import_object(CONF.compute_manager)
instance_dict = dict(self.test_instance)
instance_dict.update({'host': 'fake',
'power_state': power_state.RUNNING,
'vm_state': vm_states.ACTIVE})
instance_ref = objects.Instance(**instance_dict)
target_xml = self.device_xml_tmpl.format(
device_path='/dev/disk/by-path/'
'ip-1.2.3.4:3260-iqn.'
'cde.67890.opst-lun-Z')
# start test
connection_info = {
u'driver_volume_type': u'iscsi',
u'serial': u'58a84f6d-3f0c-4e19-a0af-eb657b790657',
u'data': {
u'access_mode': u'rw', u'target_discovered': False,
u'target_iqn': u'ip-1.2.3.4:3260-iqn.cde.67890.opst-lun-Z',
u'volume_id': u'58a84f6d-3f0c-4e19-a0af-eb657b790657',
'device_path':
u'/dev/disk/by-path/ip-1.2.3.4:3260-iqn.cde.67890.opst-lun-Z',
},
}
bdm = objects.LibvirtLiveMigrateBDMInfo(
serial='58a84f6d-3f0c-4e19-a0af-eb657b790657',
bus='virtio', type='disk', dev='vdb',
connection_info=connection_info)
migrate_data = objects.LibvirtLiveMigrateData(
serial_listen_addr='',
target_connect_addr='127.0.0.2',
bdms=[bdm])
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
test_mock = mock.MagicMock()
with mock.patch.object(drvr, '_update_xml') as mupdate:
test_mock.XMLDesc.return_value = target_xml
drvr._live_migration_operation(self.context, instance_ref,
'dest', False, migrate_data,
test_mock)
test_mock.migrateToURI2.assert_called_once_with(
'qemu+tcp://127.0.0.2/system',
None, mupdate(), None, None, 0)
def test_update_volume_xml(self):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
@ -7061,6 +7108,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
graphics_listen_addr_vnc='10.0.0.1',
graphics_listen_addr_spice='10.0.0.2',
serial_listen_addr='9.0.0.12',
target_connect_addr=None,
bdms=[])
dom = fakelibvirt.virDomain
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
@ -7083,7 +7131,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
CONF.set_override("enabled", True, "serial_console")
dom = fakelibvirt.virDomain
migrate_data = objects.LibvirtLiveMigrateData(
serial_listen_addr='')
serial_listen_addr='', target_connect_addr=None)
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
self.assertRaises(exception.MigrationError,
@ -7116,6 +7164,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
graphics_listen_addr_vnc='0.0.0.0',
graphics_listen_addr_spice='0.0.0.0',
serial_listen_addr='127.0.0.1',
target_connect_addr=None,
bdms=[])
self.mox.ReplayAll()
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
@ -7145,6 +7194,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
# start test
migrate_data = objects.LibvirtLiveMigrateData(
serial_listen_addr='',
target_connect_addr=None,
bdms=[])
self.mox.ReplayAll()
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
@ -7172,7 +7222,8 @@ class LibvirtConnTestCase(test.NoDBTestCase):
migrate_data = objects.LibvirtLiveMigrateData(
graphics_listen_addr_vnc='1.2.3.4',
graphics_listen_addr_spice='1.2.3.4',
serial_listen_addr='127.0.0.1')
serial_listen_addr='127.0.0.1',
target_connect_addr=None)
self.mox.ReplayAll()
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
self.assertRaises(exception.MigrationError,
@ -7216,6 +7267,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
graphics_listen_addr_vnc='127.0.0.1',
graphics_listen_addr_spice='127.0.0.1',
serial_listen_addr='127.0.0.1',
target_connect_addr=None,
bdms=[])
self.mox.ReplayAll()
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
@ -7261,6 +7313,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
graphics_listen_addr_vnc='0.0.0.0',
graphics_listen_addr_spice='127.0.0.1',
serial_listen_addr='127.0.0.1',
target_connect_addr=None,
bdms=[])
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
@ -7954,7 +8007,37 @@ class LibvirtConnTestCase(test.NoDBTestCase):
drvr._create_images_and_backing(self.context, self.test_instance,
"/fake/instance/dir", None)
def _generate_target_ret(self, target_connect_addr=None):
target_ret = {
'graphics_listen_addrs': {'spice': '127.0.0.1', 'vnc': '127.0.0.1'},
'target_connect_addr': target_connect_addr,
'serial_listen_addr': '127.0.0.1',
'volume': {
'12345': {'connection_info': {u'data': {'device_path':
u'/dev/disk/by-path/ip-1.2.3.4:3260-iqn.abc.12345.opst-lun-X'},
'serial': '12345'},
'disk_info': {'bus': 'scsi',
'dev': 'sda',
'type': 'disk'}},
'67890': {'connection_info': {u'data': {'device_path':
u'/dev/disk/by-path/ip-1.2.3.4:3260-iqn.cde.67890.opst-lun-Z'},
'serial': '67890'},
'disk_info': {'bus': 'scsi',
'dev': 'sdb',
'type': 'disk'}}}}
return target_ret
def test_pre_live_migration_works_correctly_mocked(self):
self._test_pre_live_migration_works_correctly_mocked()
def test_pre_live_migration_with_transport_ip(self):
self.flags(live_migration_inbound_addr='127.0.0.2',
group='libvirt')
target_ret = self._generate_target_ret('127.0.0.2')
self._test_pre_live_migration_works_correctly_mocked(target_ret)
def _test_pre_live_migration_works_correctly_mocked(self,
target_ret=None):
# Creating testdata
vol = {'block_device_mapping': [
{'connection_info': {'serial': '12345', u'data':
@ -8007,23 +8090,8 @@ class LibvirtConnTestCase(test.NoDBTestCase):
result = drvr.pre_live_migration(
c, instance, vol, nw_info, None,
migrate_data=migrate_data)
target_ret = {
'graphics_listen_addrs': {'spice': '127.0.0.1', 'vnc': '127.0.0.1'},
'serial_listen_addr': '127.0.0.1',
'volume': {
'12345': {'connection_info': {u'data': {'device_path':
u'/dev/disk/by-path/ip-1.2.3.4:3260-iqn.abc.12345.opst-lun-X'},
'serial': '12345'},
'disk_info': {'bus': 'scsi',
'dev': 'sda',
'type': 'disk'}},
'67890': {'connection_info': {u'data': {'device_path':
u'/dev/disk/by-path/ip-1.2.3.4:3260-iqn.cde.67890.opst-lun-Z'},
'serial': '67890'},
'disk_info': {'bus': 'scsi',
'dev': 'sdb',
'type': 'disk'}}}}
if not target_ret:
target_ret = self._generate_target_ret()
self.assertEqual(
result.to_legacy_dict(
pre_migration_result=True)['pre_live_migration_result'],
@ -8081,6 +8149,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
)
self.assertEqual({'graphics_listen_addrs': {'spice': '127.0.0.1',
'vnc': '127.0.0.1'},
'target_connect_addr': None,
'serial_listen_addr': '127.0.0.1',
'volume': {}}, res_data['pre_live_migration_result'])
@ -8139,6 +8208,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
target_ret = {
'graphics_listen_addrs': {'spice': '127.0.0.1',
'vnc': '127.0.0.1'},
'target_connect_addr': None,
'serial_listen_addr': '127.0.0.1',
'volume': {
'12345': {'connection_info': {u'data': {'device_path':

View File

@ -156,6 +156,12 @@ libvirt_opts = [
cfg.BoolOpt('use_usb_tablet',
default=True,
help='Sync virtual and real mouse cursors in Windows VMs'),
cfg.StrOpt('live_migration_inbound_addr',
default=None,
help='Live migration target ip or hostname '
'(if this option is set to be None,'
'the hostname of the migration target'
'compute node will be used)'),
cfg.StrOpt('live_migration_uri',
default="qemu+tcp://%s/system",
help='Migration target URI '
@ -5927,6 +5933,8 @@ class LibvirtDriver(driver.ComputeDriver):
listen_addrs['spice'] = str(
migrate_data.graphics_listen_addr_spice)
serial_listen_addr = migrate_data.serial_listen_addr
if migrate_data.target_connect_addr is not None:
dest = migrate_data.target_connect_addr
migratable_flag = getattr(libvirt, 'VIR_DOMAIN_XML_MIGRATABLE',
None)
@ -6585,6 +6593,9 @@ class LibvirtDriver(driver.ComputeDriver):
migrate_data.graphics_listen_addr_spice = CONF.spice.server_listen
migrate_data.serial_listen_addr = \
CONF.serial_console.proxyclient_address
# Store live_migration_inbound_addr
migrate_data.target_connect_addr = \
CONF.libvirt.live_migration_inbound_addr
for vol in block_device_mapping:
connection_info = vol['connection_info']

View File

@ -0,0 +1,10 @@
---
features:
- |
A new option "live_migration_inbound_addr" has been added
in the configuration file, set None as default value.
If this option is present in pre_migration_data, the ip
address/hostname provided will be used instead of
the migration target compute node's hostname as the
uri for live migration, if it's None, then the
mechanism remains as it is before.