1#
2# Copyright 2009, 2013 Red Hat, Inc.
3#
4# This work is licensed under the GNU GPLv2 or later.
5# See the COPYING file in the top-level directory.
6
7from .device import Device, DeviceSeclabel
8from ..xmlbuilder import XMLBuilder, XMLChildProperty, XMLProperty
9from .. import xmlutil
10
11
12def _set_host_helper(obj, hostparam, portparam, val):
13    def parse_host(val):
14        host, ignore, port = (val or "").partition(":")
15        return host or None, port or None
16
17    host, port = parse_host(val)
18    if port and not host:
19        host = "127.0.0.1"
20    xmlutil.set_prop_path(obj, hostparam, host)
21    if port:
22        xmlutil.set_prop_path(obj, portparam, port)
23
24
25class CharSource(XMLBuilder):
26    XML_NAME = "source"
27    _XML_PROP_ORDER = ["bind_host", "bind_service",
28                       "mode", "connect_host", "connect_service",
29                       "path", "channel"]
30
31    def set_friendly_connect(self, val):
32        _set_host_helper(self, "connect_host", "connect_service", val)
33    def set_friendly_bind(self, val):
34        _set_host_helper(self, "bind_host", "bind_service", val)
35    def set_friendly_host(self, val):
36        _set_host_helper(self, "host", "service", val)
37
38    seclabels = XMLChildProperty(DeviceSeclabel)
39
40    host = XMLProperty("./@host")
41    service = XMLProperty("./@service", is_int=True)
42    path = XMLProperty("./@path")
43    channel = XMLProperty("./@channel")
44    master = XMLProperty("./@master")
45    slave = XMLProperty("./@slave")
46    mode = XMLProperty("./@mode")
47
48    # It's weird to track these properties here, since the XML is set on
49    # the parent, but this is how libvirt does it internally, which means
50    # everything that shares a charsource has these values too.
51    protocol = XMLProperty("./../protocol/@type")
52    log_file = XMLProperty("./../log/@file")
53    log_append = XMLProperty("./../log/@append", is_onoff=True)
54
55
56    # Convenience source helpers for setting connect/bind host and service
57    connect_host = XMLProperty("./../source[@mode='connect']/@host")
58    connect_service = XMLProperty(
59            "./../source[@mode='connect']/@service", is_int=True)
60    bind_host = XMLProperty("./../source[@mode='bind']/@host")
61    bind_service = XMLProperty("./../source[@mode='bind']/@service", is_int=True)
62
63
64class _DeviceChar(Device):
65    """
66    Base class for all character devices. Shouldn't be instantiated
67    directly.
68    """
69
70    TYPE_PTY      = "pty"
71    TYPE_DEV      = "dev"
72    TYPE_STDIO    = "stdio"
73    TYPE_PIPE     = "pipe"
74    TYPE_FILE     = "file"
75    TYPE_VC       = "vc"
76    TYPE_NULL     = "null"
77    TYPE_TCP      = "tcp"
78    TYPE_UDP      = "udp"
79    TYPE_UNIX     = "unix"
80    TYPE_SPICEVMC = "spicevmc"
81    TYPE_SPICEPORT = "spiceport"
82    TYPE_NMDM = "nmdm"
83
84    CHANNEL_NAME_SPICE = "com.redhat.spice.0"
85    CHANNEL_NAME_QEMUGA = "org.qemu.guest_agent.0"
86    CHANNEL_NAME_LIBGUESTFS = "org.libguestfs.channel.0"
87    CHANNEL_NAME_SPICE_WEBDAV = "org.spice-space.webdav.0"
88    CHANNEL_NAMES = [CHANNEL_NAME_SPICE,
89                     CHANNEL_NAME_QEMUGA,
90                     CHANNEL_NAME_LIBGUESTFS,
91                     CHANNEL_NAME_SPICE_WEBDAV]
92
93    def set_friendly_target(self, val):
94        _set_host_helper(self, "target_address", "target_port", val)
95
96    _XML_PROP_ORDER = ["type", "source",
97                       "target_type", "target_name", "target_state"]
98
99    type = XMLProperty("./@type")
100    source = XMLChildProperty(CharSource, is_single=True)
101
102    target_address = XMLProperty("./target/@address")
103    target_port = XMLProperty("./target/@port", is_int=True)
104    target_type = XMLProperty("./target/@type")
105    target_name = XMLProperty("./target/@name")
106    target_state = XMLProperty("./target/@state")
107    target_model_name = XMLProperty("./target/model/@name")
108
109
110    ##################
111    # Default config #
112    ##################
113
114    def set_defaults(self, _guest):
115        if (not self.source.mode and
116            self.type in [self.TYPE_UNIX, self.TYPE_TCP]):
117            self.source.mode = "bind"
118        if not self.target_type and self.DEVICE_TYPE == "channel":
119            self.target_type = "virtio"
120        if not self.target_name and self.type == self.TYPE_SPICEVMC:
121            self.target_name = self.CHANNEL_NAME_SPICE
122
123
124
125class DeviceConsole(_DeviceChar):
126    @staticmethod
127    def get_console_duplicate(guest, serial):
128        """
129        Determine if the passed serial device has a duplicate
130        <console> device in the passed Guest
131        """
132        if serial.DEVICE_TYPE != "serial":
133            return
134
135        consoles = guest.devices.console
136        if not consoles:
137            return  # pragma: no cover
138
139        console = consoles[0]
140        if (console.type == serial.type and
141            (console.target_type is None or console.target_type == "serial")):
142            return console
143
144    XML_NAME = "console"
145
146
147class DeviceSerial(_DeviceChar):
148    XML_NAME = "serial"
149
150
151class DeviceParallel(_DeviceChar):
152    XML_NAME = "parallel"
153
154
155class DeviceChannel(_DeviceChar):
156    XML_NAME = "channel"
157