Merge "libvirt: enable live migration with serial console"
This commit is contained in:
@@ -6197,7 +6197,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)
|
||||
@@ -6332,6 +6332,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):
|
||||
@@ -7230,6 +7287,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'},
|
||||
@@ -7292,6 +7350,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):
|
||||
@@ -7344,6 +7403,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'},
|
||||
|
||||
@@ -5420,7 +5420,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:
|
||||
@@ -5429,6 +5429,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)
|
||||
|
||||
@@ -5489,6 +5493,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')
|
||||
|
||||
@@ -5529,6 +5544,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
|
||||
@@ -5560,13 +5587,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,
|
||||
@@ -5575,7 +5607,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,
|
||||
@@ -5603,6 +5636,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,
|
||||
@@ -6217,9 +6251,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'):
|
||||
|
||||
Reference in New Issue
Block a user