libvirt: virtlogd: use virtlogd for char devices
This change makes actual usage of the "logd" sub-element for char devices. The two REST APIs ``os-getConsoleOutput`` and ``os-getSerialConsole`` can now be satisfied at the same time. This is valid for any combination of: * char device element: "console", "serial" * char device type: "tcp", "pty" There is also no need to create multiple different device types anymore. If we have a tcp device, we don't need the pty device anymore. The logging will be done in the tcp device. Implements blueprint libvirt-virtlogd Closes-Bug: 832507 Change-Id: Ia412f55bd988f6e11cd78c4c5a50a86389e648b0
This commit is contained in:
parent
e718aadeb4
commit
1f659251c7
|
@ -3829,6 +3829,45 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
self.assertEqual("tcp", cfg.devices[3].type)
|
||||
self.assertEqual("tcp", cfg.devices[4].type)
|
||||
|
||||
@mock.patch.object(host.Host, 'has_min_version', return_value=True)
|
||||
@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_guest_config_char_device_logd(self, mock_get_arch,
|
||||
mock_get_number_serial_ports,
|
||||
mock_acquire_port,
|
||||
mock_host_has_min_version):
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||
|
||||
def _test_consoles(arch_to_mock, serial_enabled,
|
||||
expected_device_type, expected_device_cls):
|
||||
guest_cfg = vconfig.LibvirtConfigGuest()
|
||||
mock_get_arch.return_value = arch_to_mock
|
||||
self.flags(enabled=serial_enabled, group='serial_console')
|
||||
instance = objects.Instance(**self.test_instance)
|
||||
|
||||
drvr._create_consoles("qemu", guest_cfg, instance=instance,
|
||||
flavor=None, image_meta=None)
|
||||
|
||||
self.assertEqual(1, len(guest_cfg.devices))
|
||||
device = guest_cfg.devices[0]
|
||||
self.assertEqual(expected_device_type, device.type)
|
||||
self.assertIsInstance(device, expected_device_cls)
|
||||
self.assertIsInstance(device.log,
|
||||
vconfig.LibvirtConfigGuestCharDeviceLog)
|
||||
self.assertEqual("off", device.log.append)
|
||||
self.assertTrue(device.log.file.endswith("console.log"))
|
||||
|
||||
_test_consoles(fields.Architecture.X86_64, True,
|
||||
"tcp", vconfig.LibvirtConfigGuestSerial)
|
||||
_test_consoles(fields.Architecture.X86_64, False,
|
||||
"pty", vconfig.LibvirtConfigGuestSerial)
|
||||
_test_consoles(fields.Architecture.S390, True,
|
||||
"tcp", vconfig.LibvirtConfigGuestConsole)
|
||||
_test_consoles(fields.Architecture.S390X, False,
|
||||
"pty", vconfig.LibvirtConfigGuestConsole)
|
||||
|
||||
@mock.patch('nova.console.serial.acquire_port')
|
||||
def test_get_guest_config_serial_console_through_port_rng_exhausted(
|
||||
self, acquire_port):
|
||||
|
|
|
@ -269,6 +269,10 @@ MIN_QEMU_S390_VERSION = (2, 3, 0)
|
|||
# libvirt 1.3 fix f391889f4e942e22b9ef8ecca492de05106ce41e
|
||||
MIN_LIBVIRT_PF_WITH_NO_VFS_CAP_VERSION = (1, 3, 0)
|
||||
|
||||
# Use the "logd" backend for handling stdout/stderr from QEMU processes.
|
||||
MIN_LIBVIRT_VIRTLOGD = (1, 3, 3)
|
||||
MIN_QEMU_VIRTLOGD = (2, 7, 0)
|
||||
|
||||
# ppc64/ppc64le architectures with KVM
|
||||
# NOTE(rfolco): Same levels for Libvirt/Qemu on Big Endian and Little
|
||||
# Endian giving the nuance around guest vs host architectures
|
||||
|
@ -595,6 +599,10 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
return True
|
||||
return False
|
||||
|
||||
def _is_virtlogd_available(self):
|
||||
return self._host.has_min_version(MIN_LIBVIRT_VIRTLOGD,
|
||||
MIN_QEMU_VIRTLOGD)
|
||||
|
||||
def _handle_live_migration_post_copy(self, migration_flags):
|
||||
if CONF.libvirt.live_migration_permit_post_copy:
|
||||
if self._is_post_copy_available():
|
||||
|
@ -4364,6 +4372,21 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
|
||||
def _create_consoles(self, virt_type, guest_cfg, instance, flavor,
|
||||
image_meta):
|
||||
# NOTE(markus_z): Beware! Below are so many conditionals that it is
|
||||
# easy to lose track. Use this chart to figure out your case:
|
||||
#
|
||||
# case | is serial | has | is qemu | resulting
|
||||
# | enabled? | virtlogd? | or kvm? | devices
|
||||
# --------------------------------------------------
|
||||
# 1 | no | no | no | pty*
|
||||
# 2 | no | no | yes | file + pty
|
||||
# 3 | no | yes | no | see case 1
|
||||
# 4 | no | yes | yes | pty with logd
|
||||
# 5 | yes | no | no | see case 1
|
||||
# 6 | yes | no | yes | tcp + pty
|
||||
# 7 | yes | yes | no | see case 1
|
||||
# 8 | yes | yes | yes | tcp with logd
|
||||
# * exception: virt_type "parallels" doesn't create a device
|
||||
if virt_type == 'parallels':
|
||||
pass
|
||||
elif virt_type not in ("qemu", "kvm"):
|
||||
|
@ -4384,38 +4407,73 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
def _create_consoles_qemu_kvm(self, guest_cfg, instance, flavor,
|
||||
image_meta):
|
||||
char_dev_cls = vconfig.LibvirtConfigGuestSerial
|
||||
log_path = self._get_console_log_path(instance)
|
||||
if CONF.serial_console.enabled:
|
||||
if not self._serial_ports_already_defined(instance):
|
||||
num_ports = hardware.get_number_of_serial_ports(flavor,
|
||||
image_meta)
|
||||
self._check_number_of_serial_console(num_ports)
|
||||
self._create_serial_consoles(guest_cfg, num_ports,
|
||||
char_dev_cls)
|
||||
char_dev_cls, log_path)
|
||||
else:
|
||||
self._create_file_device(guest_cfg, instance, char_dev_cls)
|
||||
self._create_pty_device(guest_cfg, char_dev_cls)
|
||||
self._create_pty_device(guest_cfg, char_dev_cls, log_path=log_path)
|
||||
|
||||
def _create_consoles_s390x(self, guest_cfg, instance, flavor, image_meta):
|
||||
char_dev_cls = vconfig.LibvirtConfigGuestConsole
|
||||
log_path = self._get_console_log_path(instance)
|
||||
if CONF.serial_console.enabled:
|
||||
if not self._serial_ports_already_defined(instance):
|
||||
num_ports = hardware.get_number_of_serial_ports(flavor,
|
||||
image_meta)
|
||||
self._create_serial_consoles(guest_cfg, num_ports,
|
||||
char_dev_cls)
|
||||
char_dev_cls, log_path)
|
||||
else:
|
||||
self._create_file_device(guest_cfg, instance, char_dev_cls,
|
||||
"sclplm")
|
||||
self._create_pty_device(guest_cfg, char_dev_cls, "sclp")
|
||||
self._create_pty_device(guest_cfg, char_dev_cls, "sclp", log_path)
|
||||
|
||||
def _create_pty_device(self, guest_cfg, char_dev_cls, target_type=None):
|
||||
def _create_pty_device(self, guest_cfg, char_dev_cls, target_type=None,
|
||||
log_path=None):
|
||||
def _create_base_dev():
|
||||
consolepty = char_dev_cls()
|
||||
consolepty.target_type = target_type
|
||||
consolepty.type = "pty"
|
||||
guest_cfg.add_device(consolepty)
|
||||
return consolepty
|
||||
|
||||
def _create_logd_dev():
|
||||
consolepty = _create_base_dev()
|
||||
log = vconfig.LibvirtConfigGuestCharDeviceLog()
|
||||
log.file = log_path
|
||||
consolepty.log = log
|
||||
return consolepty
|
||||
|
||||
if CONF.serial_console.enabled:
|
||||
if self._is_virtlogd_available():
|
||||
return
|
||||
else:
|
||||
# NOTE(markus_z): You may wonder why this is necessary and
|
||||
# so do I. I'm certain that this is *not* needed in any
|
||||
# real use case. It is, however, useful if you want to
|
||||
# pypass the Nova API and use "virsh console <guest>" on
|
||||
# an hypervisor, as this CLI command doesn't work with TCP
|
||||
# devices (like the serial console is).
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=781467
|
||||
# Pypassing the Nova API however is a thing we don't want.
|
||||
# Future changes should remove this and fix the unit tests
|
||||
# which ask for the existence.
|
||||
guest_cfg.add_device(_create_base_dev())
|
||||
else:
|
||||
if self._is_virtlogd_available():
|
||||
guest_cfg.add_device(_create_logd_dev())
|
||||
else:
|
||||
guest_cfg.add_device(_create_base_dev())
|
||||
|
||||
def _create_file_device(self, guest_cfg, instance, char_dev_cls,
|
||||
target_type=None):
|
||||
if self._is_virtlogd_available():
|
||||
return
|
||||
|
||||
consolelog = char_dev_cls()
|
||||
consolelog.target_type = target_type
|
||||
consolelog.type = "file"
|
||||
|
@ -4436,7 +4494,8 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
"ports in its domain XML", instance=instance)
|
||||
return False
|
||||
|
||||
def _create_serial_consoles(self, guest_cfg, num_ports, char_dev_cls):
|
||||
def _create_serial_consoles(self, guest_cfg, num_ports, char_dev_cls,
|
||||
log_path):
|
||||
for port in six.moves.range(num_ports):
|
||||
console = char_dev_cls()
|
||||
console.port = port
|
||||
|
@ -4444,6 +4503,12 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
console.listen_host = CONF.serial_console.proxyclient_address
|
||||
listen_port = serial_console.acquire_port(console.listen_host)
|
||||
console.listen_port = listen_port
|
||||
# NOTE: only the first serial console gets the boot messages,
|
||||
# that's why we attach the logd subdevice only to that.
|
||||
if port == 0 and self._is_virtlogd_available():
|
||||
log = vconfig.LibvirtConfigGuestCharDeviceLog()
|
||||
log.file = log_path
|
||||
console.log = log
|
||||
guest_cfg.add_device(console)
|
||||
|
||||
def _cpu_config_to_vcpu_model(self, cpu_config, vcpu_model):
|
||||
|
|
Loading…
Reference in New Issue