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:
Pawel Koniszewski 2016-11-16 11:05:10 +01:00
parent f8e35e5bf9
commit f84ae10c66
3 changed files with 20 additions and 103 deletions

View File

@ -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."""

View File

@ -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)

View File

@ -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 = {