virt: cleanup the consoles and serials support

This commit is contained in:
Cédric Bosdonnat 2020-10-30 12:05:43 +01:00 committed by Daniel Wozniak
parent a859c6cc84
commit 6cb22d64c5
6 changed files with 183 additions and 111 deletions

1
changelog/58844.added Normal file
View file

@ -0,0 +1 @@
Enhance console and serial support in virt module

View file

@ -184,10 +184,6 @@ VIRT_STATE_NAME_MAP = {
6: "crashed",
}
default_port = 23023
default_serial_type = "tcp"
default_console_type = "tcp"
def __virtual__():
if not HAS_LIBVIRT:
@ -1027,70 +1023,57 @@ def _gen_xml(
context["boot"]["kernel"] = "/usr/lib/grub2/x86_64-xen/grub.xen"
context["boot_dev"] = []
context["serials"] = []
if serials is not None:
for serial in serials:
serial_type = serial.get("type") or default_serial_type
if not serial_type:
raise SaltInvocationError("Missing type in serial")
default_port = 23023
default_chardev_type = "tcp"
serial_context = {"type": serial_type}
if serial_context["type"] == "tcp":
serial_context["port"] = serial.get("port", default_port)
serial_context["protocol"] = serial.get("protocol", "telnet")
context["serials"].append(serial_context)
chardev_types = ["serial", "console"]
for chardev_type in chardev_types:
context[chardev_type + "s"] = []
parameter_value = locals()[chardev_type + "s"]
if parameter_value is not None:
for chardev in parameter_value:
chardev_context = chardev
chardev_context["type"] = chardev.get("type", default_chardev_type)
context["consoles"] = []
if consoles is not None:
for console in consoles:
console_type = console.get("type") or default_console_type
if not console_type:
raise SaltInvocationError("Missing type in console")
console_context = {"type": console_type}
if console_context["type"] == "tcp":
console_context["port"] = console.get("port", default_port)
console_context["protocol"] = console.get("protocol", "telnet")
context["consoles"].append(console_context)
if chardev_context["type"] == "tcp":
chardev_context["port"] = chardev.get("port", default_port)
chardev_context["protocol"] = chardev.get("protocol", "telnet")
context[chardev_type + "s"].append(chardev_context)
# processing of deprecated parameters
old_port = kwargs.get("telnet_port")
if old_port:
salt.utils.versions.warn_until(
"Aluminium",
"Phosphorus",
"'telnet_port' parameter has been deprecated, use the 'serials' and 'consoles' parameters instead. "
"It will be removed in {version}.",
"'telnet_port' parameter has been deprecated, use the 'serials' parameter with a value "
"like ``{{{{'type': 'tcp', 'protocol': 'telnet', 'port': {}}}}}`` instead and a similar `consoles` parameter. "
"It will be removed in {{version}}.".format(old_port),
)
old_serial_type = kwargs.get("serial_type")
if old_serial_type:
salt.utils.versions.warn_until(
"Aluminium",
"'serial_type' parameter has been deprecated, use the 'serials' and 'consoles' parameters instead. "
"It will be removed in {version}.",
"Phosphorus",
"'serial_type' parameter has been deprecated, use the 'serials' parameter with a value "
"like ``{{{{'type': '{}', 'protocol': 'telnet' }}}}`` instead and a similar `consoles` parameter. "
"It will be removed in {{version}}.".format(old_serial_type),
)
serial_context = {"type": serial_type}
serial_context = {"type": old_serial_type}
if serial_context["type"] == "tcp":
serial_context["port"] = old_port if old_port is not None else default_port
serial_context["protocol"] = serial.get("protocol", "telnet")
serial_context["port"] = old_port or default_port
serial_context["protocol"] = "telnet"
context["serials"].append(serial_context)
old_console = kwargs.get("console")
if old_console:
salt.utils.versions.warn_until(
"Aluminium",
"'console' parameter has been deprecated, use the 'serials' and 'consoles' parameters instead. "
"It will be removed in {version}.",
)
if old_console is True:
console_context = {
"type": old_serial_type if old_serial_type else default_console_type
}
if console_context["type"] == "tcp":
console_context["port"] = old_port if old_port else default_port
console_context["protocol"] = console.get("protocol", "telnet")
context["consoles"].append(console_context)
# end processing of deprecated parameters
old_console = kwargs.get("console")
if old_console:
salt.utils.versions.warn_until(
"Phosphorus",
"'console' parameter has been deprecated, use the 'serials' and 'consoles' parameters instead. "
"It will be removed in {version}.",
)
if old_console is True:
context["consoles"].append(serial_context)
context["disks"] = []
disk_bus_map = {"virtio": "vd", "xen": "xvd", "fdc": "fd", "ide": "hd"}
@ -2275,13 +2258,13 @@ def init(
:param serials:
Dictionary providing details on the serials connection to create. (Default: ``None``)
See :ref:`init-serials-def` for more details on the possible values.
See :ref:`init-chardevs-def` for more details on the possible values.
.. versionadded:: Aluminium
:param consoles:
Dictionary providing details on the consoles device to create. (Default: ``None``)
See :ref:`init-consoles-def` for more details on the possible values.
See :ref:`init-chardevs-def` for more details on the possible values.
.. versionadded:: Aluminium
@ -2657,40 +2640,42 @@ def init(
By default, not setting the ``listen`` part of the dictionary will default to
listen on all addresses.
.. _init-serials-def:
.. _init-chardevs-def:
.. rubric:: Serials Definitions
.. rubric:: Serials and Consoles Definitions
Serial dictionaries can contain the following properties:
type
Type of the serial connection, like ``'tcp'``, ``'pty'``.
Type of the serial connection, like ``'tcp'``, ``'pty'``, ``'file'``, ``'udp'``, ``'dev'``,
``'pipe'``, ``'unix'``.
port
The serial port number.
path
Path to the source device. Can be a log file, a host character device to pass through,
a unix socket, a named pipe path.
host
The serial UDP or TCP host name.
(Default: 23023)
Protocol
Name of connection protocol.
(Default: telnet)
.. _init-consoles-def:
.. rubric:: Consoles Definitions
Consol dictionaries can contain the following properties:
type
Type of the serial connection, like ``'tcp'``, ``'pty'``.
port
The serial port number.
The serial UDP or TCP port number.
(Default: 23023)
Protocol
Name of connection protocol.
protocol
Name of the TCP connection protocol.
(Default: telnet)
tls
Boolean value indicating whether to use hypervisor TLS certificates environment for TCP devices.
target_port
The guest device port number starting from 0
target_type
The guest device type. Common values are ``serial``, ``virtio`` or ``usb-serial``, but more are documented in
`the libvirt documentation <https://libvirt.org/formatdomain.html#consoles-serial-parallel-channel-devices>`_.
.. rubric:: CLI Example
.. code-block:: bash
@ -3135,21 +3120,21 @@ def _serial_or_concole_equal(old, new):
def _diff_serial_list(old, new):
"""
Compare serial definitions to extract the changes
Compare serial definitions to extract the changes
:param old: list of ElementTree nodes representing the old serials
:param new: list of ElementTree nodes representing the new serials
"""
:param old: list of ElementTree nodes representing the old serials
:param new: list of ElementTree nodes representing the new serials
"""
return _diff_lists(old, new, _serial_or_concole_equal)
def _diff_console_list(old, new):
"""
Compare console definitions to extract the changes
Compare console definitions to extract the changes
:param old: list of ElementTree nodes representing the old consoles
:param new: list of ElementTree nodes representing the new consoles
"""
:param old: list of ElementTree nodes representing the old consoles
:param new: list of ElementTree nodes representing the new consoles
"""
return _diff_lists(old, new, _serial_or_concole_equal)
@ -3273,13 +3258,13 @@ def update(
:param serials:
Dictionary providing details on the serials connection to create. (Default: ``None``)
See :ref:`init-serials-def` for more details on the possible values.
See :ref:`init-chardevs-def` for more details on the possible values.
.. versionadded:: Aluminium
:param consoles:
Dictionary providing details on the consoles device to create. (Default: ``None``)
See :ref:`init-consoles-def` for more details on the possible values.
See :ref:`init-chardevs-def` for more details on the possible values.
.. versionadded:: Aluminium

View file

@ -567,12 +567,12 @@ def defined(
:param serials:
Dictionary providing details on the serials connection to create. (Default: ``None``)
See :ref:`init-serials-def` for more details on the possible values.
See :ref:`init-chardevs-def` for more details on the possible values.
.. versionadded:: Aluminium
:param consoles:
Dictionary providing details on the consoles device to create. (Default: ``None``)
See :ref:`init-consoles-def` for more details on the possible values.
See :ref:`init-chardevs-def` for more details on the possible values.
.. versionadded:: Aluminium
@ -841,12 +841,12 @@ def running(
.. versionadded:: 3000
:param serials:
Dictionary providing details on the serials connection to create. (Default: ``None``)
See :ref:`init-serials-def` for more details on the possible values.
See :ref:`init-chardevs-def` for more details on the possible values.
.. versionadded:: Aluminium
:param consoles:
Dictionary providing details on the consoles device to create. (Default: ``None``)
See :ref:`init-consoles-def` for more details on the possible values.
See :ref:`init-chardevs-def` for more details on the possible values.
.. versionadded:: Aluminium

View file

@ -0,0 +1,16 @@
{% macro chardev(dev) -%}
{% if dev.type == "unix" -%}
<source mode="bind" path="{{ dev.path }}"/>
{% elif dev.type in ["udp", "tcp"] -%}
<source mode="bind" host="{{ dev.get('host', '0.0.0.0') }}" service="{{ dev.port }}"
{% if dev.get('tls') is not none %}tls="{{'yes' if dev.tls else 'no'}}"{% endif %}/>
{% elif dev.type in ["pipe", "dev", "pty", "file"] and dev.path -%}
<source path="{{ dev.path }}"/>
{%- endif %}
{% if dev.type == "tcp" -%}
<protocol type="{{ dev.protocol }}"/>
{%- endif %}
{% if "target_port" in dev or "target_type" in dev -%}
<target port="{{ dev.get('target_port', 0) }}" {% if dev.target_type %}type="{{ dev.target_type }}"{% endif %}/>
{%- endif %}
{%- endmacro %}

View file

@ -2,6 +2,7 @@
{%- macro opt_attribute(obj, name, conv=none) %}
{%- if obj.get(name) is not none %} {{ name }}='{{ obj[name] if conv is none else conv(obj[name]) }}'{% endif -%}
{%- endmacro %}
{%- import 'libvirt_chardevs.jinja' as libvirt_chardevs -%}
<domain type='{{ hypervisor }}'>
<name>{{ name }}</name>
{%- if cpu %}
@ -302,34 +303,24 @@
address='{{ graphics.listen.address }}'
{% endif %}/>
</graphics>
{% if graphics.type == "spice" -%}
<channel type='spicevmc'>
<target type='virtio' name='com.redhat.spice.0'/>
</channel>
{%- endif %}
{% endif %}
{% for serial in serials %}
{% if serial.type == 'pty' %}
<serial type='pty'>
<target port='0'/>
{%- for serial in serials %}
<serial type='{{ serial.type }}'>
{{ libvirt_chardevs.chardev(serial) }}
</serial>
{% elif serial.type == 'tcp' %}
<serial type='tcp'>
<source mode='bind' host='' service='{{ serial.port }}'/>
<protocol type='{{ serial.protocol }}'/>
<target port='0'/>
</serial>
{% endif %}
{% endfor %}
{%- endfor %}
{% for console in consoles %}
{% if console.type == 'pty' %}
<console type='pty'>
<target type='serial' port='0'/>
{%- for console in consoles %}
<console type='{{ console.type }}'>
{{ libvirt_chardevs.chardev(console) }}
</console>
{% elif console.type == 'tcp' %}
<console type='tcp'>
<source mode='bind' host='' service='{{ console.port }}'/>
<protocol type='{{ console.protocol }}'/>
<target type='serial' port='0'/>
</console>
{% endif %}
{% endfor %}
{%- if hypervisor in ["qemu", "kvm"] %}
<channel type='unix'>

View file

@ -268,8 +268,87 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin):
root = ET.fromstring(xml_data)
self.assertEqual(root.find("devices/serial").attrib["type"], "tcp")
self.assertEqual(root.find("devices/serial/source").attrib["service"], "23023")
self.assertFalse("tls" in root.find("devices/serial/source").keys())
self.assertEqual(root.find("devices/serial/protocol").attrib["type"], "telnet")
def test_gen_xml_for_chardev_types(self):
"""
Test virt._gen_xml() consoles and serials of various types
"""
diskp = virt._disk_profile(self.mock_conn, "default", "kvm", [], "hello")
nicp = virt._nic_profile("default", "kvm")
xml_data = virt._gen_xml(
self.mock_conn,
"hello",
1,
512,
diskp,
nicp,
"kvm",
"hvm",
"x86_64",
consoles=[
{"type": "pty", "path": "/dev/pts/2", "target_port": 2},
{"type": "pty", "target_type": "usb-serial"},
{"type": "stdio"},
{"type": "file", "path": "/path/to/serial.log"},
],
serials=[
{"type": "pipe", "path": "/tmp/mypipe"},
{"type": "udp", "host": "127.0.0.1", "port": 1234},
{"type": "tcp", "port": 22223, "protocol": "raw", "tls": True},
{"type": "unix", "path": "/path/to/socket"},
],
)
root = ET.fromstring(xml_data)
self.assertEqual(root.find("devices/console[1]").attrib["type"], "pty")
self.assertEqual(
root.find("devices/console[1]/source").attrib["path"], "/dev/pts/2"
)
self.assertEqual(root.find("devices/console[1]/target").attrib["port"], "2")
self.assertEqual(root.find("devices/console[2]").attrib["type"], "pty")
self.assertIsNone(root.find("devices/console[2]/source"))
self.assertEqual(
root.find("devices/console[2]/target").attrib["type"], "usb-serial"
)
self.assertEqual(root.find("devices/console[3]").attrib["type"], "stdio")
self.assertIsNone(root.find("devices/console[3]/source"))
self.assertEqual(root.find("devices/console[4]").attrib["type"], "file")
self.assertEqual(
root.find("devices/console[4]/source").attrib["path"], "/path/to/serial.log"
)
self.assertEqual(root.find("devices/serial[1]").attrib["type"], "pipe")
self.assertEqual(
root.find("devices/serial[1]/source").attrib["path"], "/tmp/mypipe"
)
self.assertEqual(root.find("devices/serial[2]").attrib["type"], "udp")
self.assertEqual(root.find("devices/serial[2]/source").attrib["mode"], "bind")
self.assertEqual(
root.find("devices/serial[2]/source").attrib["service"], "1234"
)
self.assertEqual(
root.find("devices/serial[2]/source").attrib["host"], "127.0.0.1"
)
self.assertEqual(root.find("devices/serial[3]").attrib["type"], "tcp")
self.assertEqual(root.find("devices/serial[3]/source").attrib["mode"], "bind")
self.assertEqual(
root.find("devices/serial[3]/source").attrib["service"], "22223"
)
self.assertEqual(root.find("devices/serial[3]/source").attrib["tls"], "yes")
self.assertEqual(root.find("devices/serial[3]/protocol").attrib["type"], "raw")
self.assertEqual(root.find("devices/serial[4]").attrib["type"], "unix")
self.assertEqual(
root.find("devices/serial[4]/source").attrib["path"], "/path/to/socket"
)
def test_gen_xml_no_nic_console(self):
"""
Test virt._gen_xml() console