libvirt: enable live migration with serial console
When migrating an instance that uses serial console, the domain XML must be changed to reflect the serial console configuration of the target host. Also prevent live migration when serial console is enabled and the libvirt version is not able to process modified domain XML. Change-Id: I654e444584124334e34d4673db6464cc2f2b822e Partial-Bug: #1455252
This commit is contained in:
parent
a2d5492e8a
commit
984cc474ef
|
@ -6102,7 +6102,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
self.assertFalse(drvr._live_migration_operation(
|
||||
self.context, instance_ref, 'dest', False,
|
||||
migrate_data, test_mock))
|
||||
mupdate.assert_called_once_with(target_xml, volume, None)
|
||||
mupdate.assert_called_once_with(target_xml, volume, None, None)
|
||||
|
||||
def test_update_volume_xml(self):
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
|
@ -6237,6 +6237,63 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
volume_xml['volume'])
|
||||
self.assertEqual(target_xml, etree.tostring(config))
|
||||
|
||||
@mock.patch.object(fakelibvirt.virDomain, "migrateToURI2")
|
||||
@mock.patch.object(fakelibvirt.virDomain, "XMLDesc")
|
||||
def test_live_migration_update_serial_console_xml(self, mock_xml,
|
||||
mock_migrate):
|
||||
self.compute = importutils.import_object(CONF.compute_manager)
|
||||
instance_ref = self.test_instance
|
||||
|
||||
xml_tmpl = ("<domain type='kvm'>"
|
||||
"<devices>"
|
||||
"<console type='tcp'>"
|
||||
"<source mode='bind' host='{addr}' service='10000'/>"
|
||||
"</console>"
|
||||
"</devices>"
|
||||
"</domain>")
|
||||
|
||||
initial_xml = xml_tmpl.format(addr='9.0.0.1')
|
||||
|
||||
target_xml = xml_tmpl.format(addr='9.0.0.12')
|
||||
target_xml = etree.tostring(etree.fromstring(target_xml))
|
||||
|
||||
# Preparing mocks
|
||||
mock_xml.return_value = initial_xml
|
||||
mock_migrate.side_effect = fakelibvirt.libvirtError("ERR")
|
||||
|
||||
# start test
|
||||
bandwidth = CONF.libvirt.live_migration_bandwidth
|
||||
migrate_data = {'pre_live_migration_result':
|
||||
{'graphics_listen_addrs':
|
||||
{'vnc': '10.0.0.1', 'spice': '10.0.0.2'},
|
||||
'serial_listen_addr': '9.0.0.12'}}
|
||||
dom = fakelibvirt.virDomain
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
self.assertRaises(fakelibvirt.libvirtError,
|
||||
drvr._live_migration_operation,
|
||||
self.context, instance_ref, 'dest',
|
||||
False, migrate_data, dom)
|
||||
mock_xml.assert_called_once_with(
|
||||
flags=fakelibvirt.VIR_DOMAIN_XML_MIGRATABLE)
|
||||
mock_migrate.assert_called_once_with(
|
||||
CONF.libvirt.live_migration_uri % 'dest',
|
||||
None, target_xml, mock.ANY, None, bandwidth)
|
||||
|
||||
@mock.patch.object(fakelibvirt, 'VIR_DOMAIN_XML_MIGRATABLE', None,
|
||||
create=True)
|
||||
def test_live_migration_fails_with_serial_console_without_migratable(self):
|
||||
self.compute = importutils.import_object(CONF.compute_manager)
|
||||
instance_ref = self.test_instance
|
||||
|
||||
CONF.set_override("enabled", True, "serial_console")
|
||||
dom = fakelibvirt.virDomain
|
||||
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
self.assertRaises(exception.MigrationError,
|
||||
drvr._live_migration_operation,
|
||||
self.context, instance_ref, 'dest',
|
||||
False, None, dom)
|
||||
|
||||
@mock.patch.object(fakelibvirt, 'VIR_DOMAIN_XML_MIGRATABLE', None,
|
||||
create=True)
|
||||
def test_live_migration_uses_migrateToURI_without_migratable_flag(self):
|
||||
|
@ -7048,6 +7105,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
|
||||
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'},
|
||||
|
@ -7110,6 +7168,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
)
|
||||
self.assertEqual({'graphics_listen_addrs': {'spice': '127.0.0.1',
|
||||
'vnc': '127.0.0.1'},
|
||||
'serial_listen_addr': '127.0.0.1',
|
||||
'volume': {}}, res_data)
|
||||
|
||||
def test_pre_live_migration_vol_backed_works_correctly_mocked(self):
|
||||
|
@ -7162,6 +7221,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
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'},
|
||||
|
|
|
@ -5496,7 +5496,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
post_method, recover_method, block_migration,
|
||||
migrate_data)
|
||||
|
||||
def _update_xml(self, xml_str, volume, listen_addrs):
|
||||
def _update_xml(self, xml_str, volume, listen_addrs, serial_listen_addr):
|
||||
xml_doc = etree.fromstring(xml_str)
|
||||
|
||||
if volume:
|
||||
|
@ -5505,6 +5505,10 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
xml_doc = self._update_graphics_xml(xml_doc, listen_addrs)
|
||||
else:
|
||||
self._check_graphics_addresses_can_live_migrate(listen_addrs)
|
||||
if serial_listen_addr:
|
||||
xml_doc = self._update_serial_xml(xml_doc, serial_listen_addr)
|
||||
else:
|
||||
self._verify_serial_console_is_disabled()
|
||||
|
||||
return etree.tostring(xml_doc)
|
||||
|
||||
|
@ -5565,6 +5569,17 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
|
||||
return xml_doc
|
||||
|
||||
def _update_serial_xml(self, xml_doc, listen_addr):
|
||||
for dev in xml_doc.findall("./devices/serial[@type='tcp']/source"):
|
||||
if dev.get('host') is not None:
|
||||
dev.set('host', listen_addr)
|
||||
|
||||
for dev in xml_doc.findall("./devices/console[@type='tcp']/source"):
|
||||
if dev.get('host') is not None:
|
||||
dev.set('host', listen_addr)
|
||||
|
||||
return xml_doc
|
||||
|
||||
def _check_graphics_addresses_can_live_migrate(self, listen_addrs):
|
||||
LOCAL_ADDRS = ('0.0.0.0', '127.0.0.1', '::', '::1')
|
||||
|
||||
|
@ -5605,6 +5620,18 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
' continue to listen on the current'
|
||||
' addresses.'))
|
||||
|
||||
def _verify_serial_console_is_disabled(self):
|
||||
if CONF.serial_console.enabled:
|
||||
|
||||
msg = _('Your libvirt version does not support the'
|
||||
' VIR_DOMAIN_XML_MIGRATABLE flag or your'
|
||||
' destination node does not support'
|
||||
' retrieving listen addresses. In order'
|
||||
' for live migration to work properly you'
|
||||
' must either disable serial console or'
|
||||
' upgrade your libvirt version.')
|
||||
raise exception.MigrationError(reason=msg)
|
||||
|
||||
def _live_migration_operation(self, context, instance, dest,
|
||||
block_migration, migrate_data, dom):
|
||||
"""Invoke the live migration operation
|
||||
|
@ -5636,13 +5663,18 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
'pre_live_migration_result', {})
|
||||
listen_addrs = pre_live_migrate_data.get('graphics_listen_addrs')
|
||||
volume = pre_live_migrate_data.get('volume')
|
||||
serial_listen_addr = pre_live_migrate_data.get(
|
||||
'serial_listen_addr')
|
||||
|
||||
migratable_flag = getattr(libvirt, 'VIR_DOMAIN_XML_MIGRATABLE',
|
||||
None)
|
||||
|
||||
if (migratable_flag is None or
|
||||
(listen_addrs is None and not volume)):
|
||||
# TODO(alexs-h): These checks could be moved to the
|
||||
# check_can_live_migrate_destination/source phase
|
||||
self._check_graphics_addresses_can_live_migrate(listen_addrs)
|
||||
self._verify_serial_console_is_disabled()
|
||||
dom.migrateToURI(CONF.libvirt.live_migration_uri % dest,
|
||||
logical_sum,
|
||||
None,
|
||||
|
@ -5651,7 +5683,8 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
old_xml_str = guest.get_xml_desc(dump_migratable=True)
|
||||
new_xml_str = self._update_xml(old_xml_str,
|
||||
volume,
|
||||
listen_addrs)
|
||||
listen_addrs,
|
||||
serial_listen_addr)
|
||||
try:
|
||||
dom.migrateToURI2(CONF.libvirt.live_migration_uri % dest,
|
||||
None,
|
||||
|
@ -5679,6 +5712,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
instance=instance)
|
||||
self._check_graphics_addresses_can_live_migrate(
|
||||
listen_addrs)
|
||||
self._verify_serial_console_is_disabled()
|
||||
dom.migrateToURI(
|
||||
CONF.libvirt.live_migration_uri % dest,
|
||||
logical_sum,
|
||||
|
@ -6242,9 +6276,12 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
greenthread.sleep(1)
|
||||
|
||||
# Store vncserver_listen and latest disk device info
|
||||
res_data = {'graphics_listen_addrs': {}, 'volume': {}}
|
||||
res_data = {'graphics_listen_addrs': {}, 'volume': {},
|
||||
'serial_listen_addr': {}}
|
||||
res_data['graphics_listen_addrs']['vnc'] = CONF.vnc.vncserver_listen
|
||||
res_data['graphics_listen_addrs']['spice'] = CONF.spice.server_listen
|
||||
res_data['serial_listen_addr'] = \
|
||||
CONF.serial_console.proxyclient_address
|
||||
for vol in block_device_mapping:
|
||||
connection_info = vol['connection_info']
|
||||
if connection_info.get('serial'):
|
||||
|
|
Loading…
Reference in New Issue