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:
parent
dcb63f0498
commit
af41accff9
@ -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)
|
||||
|
@ -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': {
|
||||
|
@ -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',
|
||||
|
@ -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':
|
||||
|
@ -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']
|
||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user