libvirt: create consoles in an understandable/extensible way

This change refactors the way the consoles of a libvirt guest get
created. This is basically just a reshuffle of code and an extraction
of methods with the goal to make the plethora of conditionals easier
to understand. Also, future enhancements should be easier this way.
For example, the blueprint libvirt-virtlogd (targeted for Ocata) will
have to be integrated in this console creation flow.

During the implementation I noticed that the host caps are *not*
needed for creation. That was an unnecessary special case for s390x
which didn't make any sense as the guest arch is the important piece.
That's the reason I dropped the "caps" parameter of the method
"_create_consoles". That also made it necessary to adjust the unit
tests.

I also took the chance to rename the "guest" parameter, which represents
the domain *configuration object*, to "guest_cfg". This is (almost) used
in every other place in the libvirt driver.

Related blueprint libvirt-virtlogd

Change-Id: I93a4ac78aaf0ac0c0a99a27ef7e145949f706d45
This commit is contained in:
Markus Zoeller 2016-11-07 10:01:45 +01:00
parent 4c5955930f
commit e718aadeb4
2 changed files with 85 additions and 73 deletions

View File

@ -3644,15 +3644,13 @@ class LibvirtConnTestCase(test.NoDBTestCase):
fields.Architecture.S390: vconfig.LibvirtConfigGuestConsole,
fields.Architecture.S390X: vconfig.LibvirtConfigGuestConsole}
caps = drvr._host.get_capabilities()
for guest_arch, device_type in expected.items():
mock_get_arch.return_value = guest_arch
guest = vconfig.LibvirtConfigGuest()
drvr._create_consoles(virt_type="kvm", guest=guest,
drvr._create_consoles(virt_type="kvm", guest_cfg=guest,
instance=instance, flavor={},
image_meta={}, caps=caps)
image_meta={})
self.assertEqual(2, len(guest.devices))
console_device = guest.devices[0]
self.assertIsInstance(console_device, device_type)
@ -3674,22 +3672,21 @@ class LibvirtConnTestCase(test.NoDBTestCase):
flavor = 'fake_flavor'
image_meta = objects.ImageMeta()
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
caps = drvr._host.get_capabilities()
guest = vconfig.LibvirtConfigGuest()
instance = objects.Instance(**self.test_instance)
self.assertRaises(exception.SerialPortNumberLimitExceeded,
drvr._create_consoles,
"kvm", guest, instance, flavor, image_meta, caps)
"kvm", guest, instance, flavor, image_meta)
mock_get_arch.assert_called_with(image_meta)
mock_get_port_number.assert_called_with(flavor,
image_meta)
drvr._create_consoles("kvm", guest, instance, flavor, image_meta, caps)
drvr._create_consoles("kvm", guest, instance, flavor, image_meta)
mock_get_arch.assert_called_with(image_meta)
mock_get_port_number.assert_called_with(flavor,
image_meta)
drvr._create_consoles("kvm", guest, instance, flavor, image_meta, caps)
drvr._create_consoles("kvm", guest, instance, flavor, image_meta)
mock_get_arch.assert_called_with(image_meta)
mock_get_port_number.assert_called_with(flavor,
image_meta)

View File

@ -4362,73 +4362,89 @@ class LibvirtDriver(driver.ComputeDriver):
else:
guest.os_boot_dev = blockinfo.get_boot_order(disk_info)
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)
def _create_consoles(self, virt_type, guest_cfg, instance, flavor,
image_meta):
if virt_type == 'parallels':
pass
elif virt_type not in ("qemu", "kvm"):
self._create_pty_device(guest_cfg,
vconfig.LibvirtConfigGuestConsole)
elif (virt_type in ("qemu", "kvm") and
self._is_s390x_guest(image_meta)):
self._create_consoles_s390x(guest_cfg, instance,
flavor, image_meta)
elif virt_type in ("qemu", "kvm"):
self._create_consoles_qemu_kvm(guest_cfg, instance,
flavor, 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)
def _is_s390x_guest(self, image_meta):
s390x_archs = (fields.Architecture.S390, fields.Architecture.S390X)
return libvirt_utils.get_arch(image_meta) in s390x_archs
if guest_arch in (fields.Architecture.S390,
fields.Architecture.S390X):
console_cls = vconfig.LibvirtConfigGuestConsole
else:
console_cls = vconfig.LibvirtConfigGuestSerial
self._check_number_of_serial_console(num_ports)
for port in six.moves.range(num_ports):
console = console_cls()
console.port = port
console.type = "tcp"
console.listen_host = (
CONF.serial_console.proxyclient_address)
console.listen_port = (
serial_console.acquire_port(
console.listen_host))
guest.add_device(console)
else:
# The QEMU 'pty' driver throws away any data if no
# client app is connected. Thus we can't get away
# with a single type=pty console. Instead we have
# to configure two separate consoles.
if guest_arch in (fields.Architecture.S390,
fields.Architecture.S390X):
consolelog = vconfig.LibvirtConfigGuestConsole()
consolelog.target_type = "sclplm"
else:
consolelog = vconfig.LibvirtConfigGuestSerial()
consolelog.type = "file"
consolelog.source_path = log_path
guest.add_device(consolelog)
if caps.host.cpu.arch in (fields.Architecture.S390,
fields.Architecture.S390X):
consolepty = vconfig.LibvirtConfigGuestConsole()
consolepty.target_type = "sclp"
else:
consolepty = vconfig.LibvirtConfigGuestSerial()
def _create_consoles_qemu_kvm(self, guest_cfg, instance, flavor,
image_meta):
char_dev_cls = vconfig.LibvirtConfigGuestSerial
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)
else:
consolepty = vconfig.LibvirtConfigGuestConsole()
self._create_file_device(guest_cfg, instance, char_dev_cls)
self._create_pty_device(guest_cfg, char_dev_cls)
if virt_type != 'parallels':
consolepty.type = "pty"
guest.add_device(consolepty)
def _create_consoles_s390x(self, guest_cfg, instance, flavor, image_meta):
char_dev_cls = vconfig.LibvirtConfigGuestConsole
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)
else:
self._create_file_device(guest_cfg, instance, char_dev_cls,
"sclplm")
self._create_pty_device(guest_cfg, char_dev_cls, "sclp")
def _create_pty_device(self, guest_cfg, char_dev_cls, target_type=None):
consolepty = char_dev_cls()
consolepty.target_type = target_type
consolepty.type = "pty"
guest_cfg.add_device(consolepty)
def _create_file_device(self, guest_cfg, instance, char_dev_cls,
target_type=None):
consolelog = char_dev_cls()
consolelog.target_type = target_type
consolelog.type = "file"
consolelog.source_path = self._get_console_log_path(instance)
guest_cfg.add_device(consolelog)
def _serial_ports_already_defined(self, instance):
try:
guest = self._host.get_guest(instance)
if list(self._get_serial_ports_from_guest(guest)):
# Serial port are already configured for instance that
# means we are in a context of migration.
return True
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)
return False
def _create_serial_consoles(self, guest_cfg, num_ports, char_dev_cls):
for port in six.moves.range(num_ports):
console = char_dev_cls()
console.port = port
console.type = "tcp"
console.listen_host = CONF.serial_console.proxyclient_address
listen_port = serial_console.acquire_port(console.listen_host)
console.listen_port = listen_port
guest_cfg.add_device(console)
def _cpu_config_to_vcpu_model(self, cpu_config, vcpu_model):
"""Update VirtCPUModel object according to libvirt CPU config.
@ -4585,8 +4601,7 @@ class LibvirtDriver(driver.ComputeDriver):
flavor, virt_type, self._host)
guest.add_device(config)
self._create_consoles(virt_type, guest, instance, flavor,
image_meta, caps)
self._create_consoles(virt_type, guest, instance, flavor, image_meta)
pointer = self._get_guest_pointer_model(guest.os_type, image_meta)
if pointer: