Merge "libvirt: Acquire TCP ports for console during live migration"

This commit is contained in:
Jenkins 2016-11-28 23:46:41 +00:00 committed by Gerrit Code Review
commit 00b359ce14
2 changed files with 73 additions and 17 deletions

View File

@ -3623,15 +3623,21 @@ class LibvirtConnTestCase(test.NoDBTestCase):
self.assertEqual(cfg.devices[5].type, "spice")
self.assertEqual(cfg.devices[6].type, "qxl")
@mock.patch.object(host.Host, 'get_guest')
@mock.patch.object(libvirt_driver.LibvirtDriver,
'_get_serial_ports_from_guest')
@mock.patch('nova.console.serial.acquire_port')
@mock.patch('nova.virt.hardware.get_number_of_serial_ports',
return_value=1)
@mock.patch.object(libvirt_driver.libvirt_utils, 'get_arch',)
def test_create_serial_console_devices_based_on_arch(self, mock_get_arch,
mock_get_port_number,
mock_acquire_port):
mock_get_port_number,
mock_acquire_port,
mock_ports,
mock_guest):
self.flags(enabled=True, group='serial_console')
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
instance = objects.Instance(**self.test_instance)
expected = {
fields.Architecture.X86_64: vconfig.LibvirtConfigGuestSerial,
@ -3644,13 +3650,17 @@ class LibvirtConnTestCase(test.NoDBTestCase):
mock_get_arch.return_value = guest_arch
guest = vconfig.LibvirtConfigGuest()
drvr._create_consoles(virt_type="kvm", guest=guest, log_path="",
flavor={}, image_meta={}, caps=caps)
drvr._create_consoles(virt_type="kvm", guest=guest,
instance=instance, flavor={},
image_meta={}, caps=caps)
self.assertEqual(2, len(guest.devices))
console_device = guest.devices[0]
self.assertIsInstance(console_device, device_type)
self.assertEqual("tcp", console_device.type)
@mock.patch.object(host.Host, 'get_guest')
@mock.patch.object(libvirt_driver.LibvirtDriver,
'_get_serial_ports_from_guest')
@mock.patch('nova.virt.hardware.get_number_of_serial_ports',
return_value=4)
@mock.patch.object(libvirt_driver.libvirt_utils, 'get_arch',
@ -3658,7 +3668,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
fields.Architecture.S390,
fields.Architecture.S390X])
def test_create_serial_console_devices_with_limit_exceeded_based_on_arch(
self, mock_get_arch, mock_get_port_number):
self, mock_get_arch, mock_get_port_number, mock_ports, mock_guest):
self.flags(enabled=True, group='serial_console')
self.flags(virt_type="qemu", group='libvirt')
flavor = 'fake_flavor'
@ -3666,20 +3676,20 @@ class LibvirtConnTestCase(test.NoDBTestCase):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
caps = drvr._host.get_capabilities()
guest = vconfig.LibvirtConfigGuest()
log_path = ""
instance = objects.Instance(**self.test_instance)
self.assertRaises(exception.SerialPortNumberLimitExceeded,
drvr._create_consoles,
"kvm", guest, log_path, flavor, image_meta, caps)
"kvm", guest, instance, flavor, image_meta, caps)
mock_get_arch.assert_called_with(image_meta)
mock_get_port_number.assert_called_with(flavor,
image_meta)
drvr._create_consoles("kvm", guest, log_path, flavor, image_meta, caps)
drvr._create_consoles("kvm", guest, instance, flavor, image_meta, caps)
mock_get_arch.assert_called_with(image_meta)
mock_get_port_number.assert_called_with(flavor,
image_meta)
drvr._create_consoles("kvm", guest, log_path, flavor, image_meta, caps)
drvr._create_consoles("kvm", guest, instance, flavor, image_meta, caps)
mock_get_arch.assert_called_with(image_meta)
mock_get_port_number.assert_called_with(flavor,
image_meta)
@ -7915,24 +7925,27 @@ class LibvirtConnTestCase(test.NoDBTestCase):
drvr._get_volume_config)
self.assertEqual(target_xml, config)
@mock.patch.object(libvirt_driver.LibvirtDriver,
'_get_serial_ports_from_guest')
@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):
mock_migrate, mock_get):
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'/>"
"<source mode='bind' host='{addr}' service='{port}'/>"
"<target type='serial' port='0'/>"
"</console>"
"</devices>"
"</domain>")
initial_xml = xml_tmpl.format(addr='9.0.0.1')
initial_xml = xml_tmpl.format(addr='9.0.0.1', port='10100')
target_xml = xml_tmpl.format(addr='9.0.0.12')
target_xml = xml_tmpl.format(addr='9.0.0.12', port='10200')
target_xml = etree.tostring(etree.fromstring(target_xml))
# Preparing mocks
@ -7947,7 +7960,8 @@ class LibvirtConnTestCase(test.NoDBTestCase):
serial_listen_addr='9.0.0.12',
target_connect_addr=None,
bdms=[],
block_migration=False)
block_migration=False,
serial_listen_ports=[10200])
dom = fakelibvirt.virDomain
guest = libvirt_guest.Guest(dom)
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)

View File

@ -4349,13 +4349,27 @@ class LibvirtDriver(driver.ComputeDriver):
else:
guest.os_boot_dev = blockinfo.get_boot_order(disk_info)
def _create_consoles(self, virt_type, guest, log_path, flavor, image_meta,
def _create_consoles(self, virt_type, guest, instance, flavor, image_meta,
caps):
log_path = self._get_console_log_path(instance)
if virt_type in ("qemu", "kvm"):
# Create the serial console char devices
guest_arch = libvirt_utils.get_arch(image_meta)
if CONF.serial_console.enabled:
try:
# TODO(sahid): the guest param of this method should
# be renamed as guest_cfg then guest_obj to guest.
guest_obj = self._host.get_guest(instance)
if list(self._get_serial_ports_from_guest(guest_obj)):
# Serial port are already configured for instance that
# means we are in a context of migration.
return
except exception.InstanceNotFound:
LOG.debug(
"Instance does not exist yet on libvirt, we can "
"safely pass on looking for already defined serial "
"ports in its domain XML", instance=instance)
num_ports = hardware.get_number_of_serial_ports(
flavor, image_meta)
@ -4558,8 +4572,7 @@ class LibvirtDriver(driver.ComputeDriver):
flavor, virt_type, self._host)
guest.add_device(config)
log_path = self._get_console_log_path(instance)
self._create_consoles(virt_type, guest, log_path, flavor,
self._create_consoles(virt_type, guest, instance, flavor,
image_meta, caps)
pointer = self._get_guest_pointer_model(guest.os_type, image_meta)
@ -5950,12 +5963,25 @@ class LibvirtDriver(driver.ComputeDriver):
libvirt.VIR_MIGRATE_TUNNELLED != 0):
params.pop('migrate_disks')
# TODO(sahid): This should be in
# post_live_migration_at_source but no way to retrieve
# ports acquired on the host for the guest at this
# step. Since the domain is going to be removed from
# libvird on source host after migration, we backup the
# serial ports to release them if all went well.
serial_ports = []
if CONF.serial_console.enabled:
serial_ports = list(self._get_serial_ports_from_guest(guest))
guest.migrate(self._live_migration_uri(dest),
migrate_uri=migrate_uri,
flags=migration_flags,
params=params,
domain_xml=new_xml_str,
bandwidth=CONF.libvirt.live_migration_bandwidth)
for hostname, port in serial_ports:
serial_console.release_port(host=hostname, port=port)
except Exception as e:
with excutils.save_and_reraise_exception():
LOG.error(_LE("Live Migration failure: %s"), e,
@ -6464,6 +6490,13 @@ class LibvirtDriver(driver.ComputeDriver):
is_shared_instance_path = True
if migrate_data:
is_shared_instance_path = migrate_data.is_shared_instance_path
if (migrate_data.obj_attr_is_set("serial_listen_ports")
and migrate_data.serial_listen_ports):
# Releases serial ports reserved.
for port in migrate_data.serial_listen_ports:
serial_console.release_port(
host=migrate_data.serial_listen_addr, port=port)
if not is_shared_instance_path:
instance_dir = libvirt_utils.get_instance_path_at_destination(
instance, migrate_data)
@ -6591,6 +6624,15 @@ class LibvirtDriver(driver.ComputeDriver):
CONF.libvirt.live_migration_inbound_addr
migrate_data.supported_perf_events = self._supported_perf_events
migrate_data.serial_listen_ports = []
if CONF.serial_console.enabled:
num_ports = hardware.get_number_of_serial_ports(
instance.flavor, instance.image_meta)
for port in six.moves.range(num_ports):
migrate_data.serial_listen_ports.append(
serial_console.acquire_port(
migrate_data.serial_listen_addr))
for vol in block_device_mapping:
connection_info = vol['connection_info']
if connection_info.get('serial'):