Refactor console checks in live migration process
_check_graphics_addresses_can_live_migrate was added to check whether we can live migrate a VM with VNC/SPICE enabled when: * libvirt did not allow to change and migrate guest XML with updated graphics listen addresses. Right now we always can update VNC/SPICE listen address. * Destination node was running old-code that does not set the fields. Currently when live migrating between two versions of OpenStack (N and N-1) the fields are always set. Therefore this check is redundant and can be removed. Going deeper in this code - graphics_listen_addr_vnc and graphics_listen_addr_spice in libvirt migrate data object are of type IPAddressField. It means that both need to contain valid IP address. By default in nova.conf it is 127.0.0.1. It can't be set to None because IP address is taken from nova.conf and even if set to None it will be passed as a string 'None' and will fail IPAddressField validation. graphics_listen_addrs in migration.py currently always returns a dict which contains IP addresses of both VNC and spice graphics consoles. This means that: * check 'if listen_addrs' is always True * check 'if not listen_addrs' is always False So we really never passed through 'if not listen_addrs' since migrate_data is objectified as those addresses are always there. However, serial_listen_addr is handled different way. The type of this field in libvirt migrate data object is StringField that might be set to None or empty string through nova.conf. So we still need to validate whether serial console can be migrated so that we will be sure that particular live migration will not break serial console in case when serial listen address is not configured at destination. Change-Id: I73f7bfafa4554bf1c2dfc980289be88154170282 Closes-Bug: #1639312
This commit is contained in:
parent
f8e35e5bf9
commit
f84ae10c66
|
@ -7950,8 +7950,10 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
drvr._live_migration_uri('dest'), miguri=None,
|
||||
dxml=target_xml, flags=mock.ANY, bandwidth=bandwidth)
|
||||
|
||||
def test_live_migration_uses_migrateToURI_without_dest_listen_addrs(self):
|
||||
def test_live_migration_fails_without_serial_console_address(self):
|
||||
self.compute = importutils.import_object(CONF.compute_manager)
|
||||
self.flags(enabled=True, group='serial_console')
|
||||
self.flags(proxyclient_address='', group='serial_console')
|
||||
instance_dict = dict(self.test_instance)
|
||||
instance_dict.update({'host': 'fake',
|
||||
'power_state': power_state.RUNNING,
|
||||
|
@ -7961,14 +7963,8 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
|
||||
# Preparing mocks
|
||||
vdmock = self.mox.CreateMock(fakelibvirt.virDomain)
|
||||
guest = libvirt_guest.Guest(vdmock)
|
||||
self.mox.StubOutWithMock(vdmock, "migrateToURI")
|
||||
_bandwidth = CONF.libvirt.live_migration_bandwidth
|
||||
vdmock.migrateToURI(drvr._live_migration_uri('dest'),
|
||||
flags=mox.IgnoreArg(),
|
||||
bandwidth=_bandwidth).AndRaise(
|
||||
fakelibvirt.libvirtError("ERR"))
|
||||
dom = fakelibvirt.virDomain
|
||||
guest = libvirt_guest.Guest(dom)
|
||||
|
||||
# start test
|
||||
migrate_data = objects.LibvirtLiveMigrateData(
|
||||
|
@ -7976,8 +7972,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
target_connect_addr=None,
|
||||
bdms=[],
|
||||
block_migration=False)
|
||||
self.mox.ReplayAll()
|
||||
self.assertRaises(fakelibvirt.libvirtError,
|
||||
self.assertRaises(exception.MigrationError,
|
||||
drvr._live_migration_operation,
|
||||
self.context, instance_ref, 'dest',
|
||||
False, migrate_data, guest, [])
|
||||
|
@ -17051,20 +17046,6 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
|
|||
self.assertRaises(fakelibvirt.libvirtError,
|
||||
self.drvr.trigger_crash_dump, instance)
|
||||
|
||||
def test_valid_graphic_addresses_for_migration(self):
|
||||
LOCALS = ("127.0.0.1", "::1", "::", "0000::", "::0001", "0.0.0.0")
|
||||
for addr in LOCALS:
|
||||
self.assertTrue(
|
||||
libvirt_driver.LibvirtDriver._check_ip_address_local(
|
||||
addr, "Address is not local/catch all"))
|
||||
|
||||
def test_invalid_graphic_addresses_for_migration(self):
|
||||
NONLOCALS = ("::2", "192.0.4.1", "bogus")
|
||||
for addr in NONLOCALS:
|
||||
self.assertFalse(
|
||||
libvirt_driver.LibvirtDriver._check_ip_address_local(
|
||||
addr, "Address is not local/catch all"))
|
||||
|
||||
|
||||
class LibvirtVolumeUsageTestCase(test.NoDBTestCase):
|
||||
"""Test for LibvirtDriver.get_all_volume_usage."""
|
||||
|
|
|
@ -646,7 +646,8 @@ class _VirtDriverTestCase(_FakeDriverBackendTestCase):
|
|||
fake_context = context.RequestContext('fake', 'fake')
|
||||
migration = objects.Migration(context=fake_context, id=1)
|
||||
migrate_data = objects.LibvirtLiveMigrateData(
|
||||
migration=migration, bdms=[], block_migration=False)
|
||||
migration=migration, bdms=[], block_migration=False,
|
||||
serial_listen_addr='127.0.0.1')
|
||||
self.connection.live_migration(self.ctxt, instance_ref, 'otherhost',
|
||||
lambda *a: None, lambda *a: None,
|
||||
migrate_data=migrate_data)
|
||||
|
|
|
@ -33,7 +33,6 @@ import functools
|
|||
import glob
|
||||
import itertools
|
||||
import mmap
|
||||
import netaddr
|
||||
import operator
|
||||
import os
|
||||
import shutil
|
||||
|
@ -5830,66 +5829,6 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
e, instance=instance)
|
||||
raise
|
||||
|
||||
@staticmethod
|
||||
def _check_ip_address_local(addr, error_msg=None):
|
||||
LOCAL_ADDRS = ('0.0.0.0', '127.0.0.1', '::', '::1')
|
||||
try:
|
||||
val = netaddr.IPAddress(addr).format()
|
||||
except (netaddr.AddrFormatError, ValueError):
|
||||
LOG.warning(_LW('Invalid Address - %(addr)s: '
|
||||
'%(errmsg)s'),
|
||||
{'addr': addr, 'errmsg': error_msg})
|
||||
val = addr
|
||||
return val in LOCAL_ADDRS
|
||||
|
||||
def _check_graphics_addresses_can_live_migrate(self, listen_addrs):
|
||||
error_msg = _LW("CONF.vnc.vncserver_listen network "
|
||||
"address is not correctly formatted.")
|
||||
local_vnc = self._check_ip_address_local(
|
||||
CONF.vnc.vncserver_listen, error_msg)
|
||||
|
||||
error_msg = _LW("CONF.spice.server_listen network "
|
||||
"address is not correctly formatted.")
|
||||
local_spice = self._check_ip_address_local(
|
||||
CONF.spice.server_listen, error_msg)
|
||||
|
||||
if ((CONF.vnc.enabled and not local_vnc) or
|
||||
(CONF.spice.enabled and not local_spice)):
|
||||
|
||||
msg = _('Your destination node does not support'
|
||||
' retrieving listen addresses. In order'
|
||||
' for live migration to work properly, you'
|
||||
' must configure the graphics (VNC and/or'
|
||||
' SPICE) listen addresses to be either'
|
||||
' the catch-all address (0.0.0.0 or ::) or'
|
||||
' the local address (127.0.0.1 or ::1).')
|
||||
raise exception.MigrationError(reason=msg)
|
||||
|
||||
if listen_addrs:
|
||||
error_msg = _LW("VNC listen address network "
|
||||
"address is not correctly formatted.")
|
||||
dest_local_vnc = self._check_ip_address_local(
|
||||
listen_addrs.get('vnc'), error_msg)
|
||||
|
||||
error_msg = _LW("SPICE listen address network "
|
||||
"address is not correctly formatted.")
|
||||
dest_local_spice = self._check_ip_address_local(
|
||||
listen_addrs.get('spice'), error_msg)
|
||||
|
||||
if ((CONF.vnc.enabled and not dest_local_vnc) or
|
||||
(CONF.spice.enabled and not dest_local_spice)):
|
||||
|
||||
LOG.warning(_LW('The graphics (VNC and/or SPICE) listen'
|
||||
' addresses on the destination node do not'
|
||||
' match the addresses on the source node.'
|
||||
' Since the source node has listen'
|
||||
' addresses set to either the catch-all'
|
||||
' address (0.0.0.0 or ::) or the local'
|
||||
' address (127.0.0.1 or ::1), the live'
|
||||
' migration will succeed, but the VM will'
|
||||
' continue to listen on the current'
|
||||
' addresses.'))
|
||||
|
||||
def _verify_serial_console_is_disabled(self):
|
||||
if CONF.serial_console.enabled:
|
||||
|
||||
|
@ -5924,16 +5863,14 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
else:
|
||||
migration_flags = self._live_migration_flags
|
||||
|
||||
listen_addrs = libvirt_migrate.graphics_listen_addrs(
|
||||
serial_listen_addr = libvirt_migrate.serial_listen_addr(
|
||||
migrate_data)
|
||||
|
||||
if not listen_addrs:
|
||||
# In this context want to ensure we do not have to migrate
|
||||
# graphic or serial consoles since we can't update guest's
|
||||
# domain XML to make it handle destination host.
|
||||
# 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)
|
||||
if not serial_listen_addr:
|
||||
# In this context we want to ensure that serial console is
|
||||
# disabled on source node. This is because nova couldn't
|
||||
# retrieve serial listen address from destination node, so we
|
||||
# consider that destination node might have serial console
|
||||
# disabled as well.
|
||||
self._verify_serial_console_is_disabled()
|
||||
|
||||
# NOTE(aplanas) migrate_uri will have a value only in the
|
||||
|
@ -5947,14 +5884,12 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
libvirt.VIR_MIGRATE_TUNNELLED == 0):
|
||||
migrate_uri = self._migrate_uri(dest)
|
||||
|
||||
new_xml_str = None
|
||||
params = None
|
||||
if listen_addrs or migrate_data.bdms:
|
||||
new_xml_str = libvirt_migrate.get_updated_guest_xml(
|
||||
# TODO(sahid): It's not a really well idea to pass
|
||||
# the method _get_volume_config and we should to find
|
||||
# a way to avoid this in future.
|
||||
guest, migrate_data, self._get_volume_config)
|
||||
new_xml_str = libvirt_migrate.get_updated_guest_xml(
|
||||
# TODO(sahid): It's not a really good idea to pass
|
||||
# the method _get_volume_config and we should to find
|
||||
# a way to avoid this in future.
|
||||
guest, migrate_data, self._get_volume_config)
|
||||
if self._host.has_min_version(
|
||||
MIN_LIBVIRT_BLOCK_LM_WITH_VOLUMES_VERSION):
|
||||
params = {
|
||||
|
|
Loading…
Reference in New Issue