virt-manager/virtinst/devices/graphics.py
2019-06-07 16:26:03 -04:00

285 lines
8.8 KiB
Python

#
# Copyright 2006-2009, 2013 Red Hat, Inc.
#
# This work is licensed under the GNU GPLv2 or later.
# See the COPYING file in the top-level directory.
import logging
import os
from .device import Device
from ..xmlbuilder import XMLBuilder, XMLChildProperty, XMLProperty
def _get_mode_prop(channel_type):
xpath = "./channel[@name='%s']/@mode" % channel_type
return XMLProperty(xpath)
def _validate_port(name, val):
if val is None:
return val
val = int(val)
if val < 5900 and val != -1:
raise ValueError(_("%s must be above 5900, or "
"-1 for auto allocation") % name)
return val
class _GraphicsListen(XMLBuilder):
XML_NAME = "listen"
type = XMLProperty("./@type")
address = XMLProperty("./@address")
network = XMLProperty("./@network")
socket = XMLProperty("./@socket")
class DeviceGraphics(Device):
XML_NAME = "graphics"
TYPE_SDL = "sdl"
TYPE_VNC = "vnc"
TYPE_RDP = "rdp"
TYPE_SPICE = "spice"
CHANNEL_TYPE_MAIN = "main"
CHANNEL_TYPE_DISPLAY = "display"
CHANNEL_TYPE_INPUTS = "inputs"
CHANNEL_TYPE_CURSOR = "cursor"
CHANNEL_TYPE_PLAYBACK = "playback"
CHANNEL_TYPE_RECORD = "record"
KEYMAP_LOCAL = "local"
KEYMAP_DEFAULT = "default"
_special_keymaps = [KEYMAP_LOCAL, KEYMAP_DEFAULT]
@staticmethod
def valid_keymaps():
"""
Return a list of valid keymap values.
"""
from .. import hostkeymap
orig_list = list(hostkeymap.keytable.values())
sort_list = []
orig_list.sort()
for k in orig_list:
if k not in sort_list:
sort_list.append(k)
return sort_list
@staticmethod
def pretty_type_simple(gtype):
if (gtype in [DeviceGraphics.TYPE_VNC,
DeviceGraphics.TYPE_SDL,
DeviceGraphics.TYPE_RDP]):
return str(gtype).upper()
return str(gtype).capitalize()
def __init__(self, *args, **kwargs):
Device.__init__(self, *args, **kwargs)
self._local_keymap = -1
_XML_PROP_ORDER = ["type", "gl", "_port", "_tlsPort", "autoport",
"_keymap", "_listen",
"passwd", "display", "xauth"]
def _get_local_keymap(self):
if self._local_keymap == -1:
from .. import hostkeymap
self._local_keymap = hostkeymap.default_keymap()
return self._local_keymap
def _set_keymap(self, val):
if val == self.KEYMAP_DEFAULT:
# Leave it up to the hypervisor
val = None
elif val == self.KEYMAP_LOCAL:
val = self._get_local_keymap()
self._keymap = val
def _get_keymap(self):
return self._keymap
_keymap = XMLProperty("./@keymap")
keymap = property(_get_keymap, _set_keymap)
def _set_port(self, val):
val = _validate_port("Port", val)
self.autoport = self._get_default_autoport()
self._port = val
def _get_port(self):
return self._port
_port = XMLProperty("./@port", is_int=True)
port = property(_get_port, _set_port)
def _set_tlsport(self, val):
val = _validate_port("TLS Port", val)
self.autoport = self._get_default_autoport()
self._tlsPort = val
def _get_tlsport(self):
return self._tlsPort
_tlsPort = XMLProperty("./@tlsPort", is_int=True)
tlsPort = property(_get_tlsport, _set_tlsport)
autoport = XMLProperty("./@autoport", is_yesno=True)
channel_main_mode = _get_mode_prop(CHANNEL_TYPE_MAIN)
channel_display_mode = _get_mode_prop(CHANNEL_TYPE_DISPLAY)
channel_inputs_mode = _get_mode_prop(CHANNEL_TYPE_INPUTS)
channel_cursor_mode = _get_mode_prop(CHANNEL_TYPE_CURSOR)
channel_playback_mode = _get_mode_prop(CHANNEL_TYPE_PLAYBACK)
channel_record_mode = _get_mode_prop(CHANNEL_TYPE_RECORD)
xauth = XMLProperty("./@xauth")
display = XMLProperty("./@display")
def _set_listen(self, val):
if val == "none":
self._set_listen_none()
elif val == "socket":
self._remove_all_listens()
obj = self.listens.add_new()
obj.type = "socket"
else:
self._remove_all_listens()
self._listen = val
def _get_listen(self):
return self._listen
_listen = XMLProperty("./@listen")
listen = property(_get_listen, _set_listen)
type = XMLProperty("./@type")
passwd = XMLProperty("./@passwd")
passwdValidTo = XMLProperty("./@passwdValidTo")
socket = XMLProperty("./@socket")
connected = XMLProperty("./@connected")
defaultMode = XMLProperty("./@defaultMode")
listens = XMLChildProperty(_GraphicsListen)
def _remove_all_listens(self):
for listen in self.listens:
self.remove_child(listen)
def get_first_listen_type(self):
if len(self.listens) > 0:
return self.listens[0].type
return None
def _set_listen_none(self):
self._remove_all_listens()
self.listen = None
self.port = None
self.tlsPort = None
self.autoport = None
self.socket = None
if self.conn.support.conn_graphics_listen_none():
obj = self.listens.add_new()
obj.type = "none"
# Spice bits
image_compression = XMLProperty("./image/@compression")
streaming_mode = XMLProperty("./streaming/@mode")
clipboard_copypaste = XMLProperty("./clipboard/@copypaste", is_yesno=True)
mouse_mode = XMLProperty("./mouse/@mode")
filetransfer_enable = XMLProperty("./filetransfer/@enable", is_yesno=True)
gl = XMLProperty("./gl/@enable", is_yesno=True)
rendernode = XMLProperty("./gl/@rendernode")
zlib_compression = XMLProperty("./zlib/@compression")
##################
# Default config #
##################
def _spice_supported(self):
if not self.conn.is_qemu() and not self.conn.is_test():
return False
# Spice has issues on some host arches, like ppc, so whitelist it
if self.conn.caps.host.cpu.arch not in ["i686", "x86_64"]:
return False
return True
def _listen_need_port(self):
listen = self.get_first_listen_type()
return not listen or listen in ["address", "network"]
def _get_default_port(self):
if self.type in ["vnc", "spice"] and self._listen_need_port():
return -1
return None
def _get_default_tlsport(self):
if self.type == "spice" and self._listen_need_port():
return -1
return None
def _get_default_autoport(self):
# By default, don't do this for VNC to maintain back compat with
# old libvirt that didn't support 'autoport'
if self.type != "spice":
return None
if (self.port == -1 and self.tlsPort == -1):
return True
return None
def _default_type(self, guest):
gtype = guest.default_graphics_type
logging.debug("Using default_graphics=%s", gtype)
if gtype == "spice" and not self._spice_supported():
logging.debug("spice requested but HV doesn't support it. "
"Using vnc.")
gtype = "vnc"
return gtype
def _default_image_compression(self, _guest):
if self.type != "spice":
return None
if not self.conn.is_remote():
logging.debug("Local connection, disabling spice image "
"compression.")
return "off"
return None
def _default_spice_gl(self, _guest):
if not self.conn.support.conn_spice_gl():
raise ValueError(_("Host does not support spice GL"))
# If spice GL but rendernode wasn't specified, hardcode
# the first one
if not self.rendernode and self.conn.support.conn_spice_rendernode():
for nodedev in self.conn.fetch_all_nodedevs():
if not nodedev.is_drm_render():
continue
self.rendernode = nodedev.get_devnode().path
break
def set_defaults(self, guest):
if not self.type:
self.type = self._default_type(guest)
if self.type == "sdl":
if not self.xauth:
self.xauth = os.path.expanduser("~/.Xauthority")
if not self.display:
self.display = os.environ.get("DISPLAY")
if self.port is None:
self.port = self._get_default_port()
if self.tlsPort is None:
self.tlsPort = self._get_default_tlsport()
if self.autoport is None:
self.autoport = self._get_default_autoport()
if not self.image_compression:
self.image_compression = self._default_image_compression(guest)
if self.type == "spice" and self.gl:
self._default_spice_gl(guest)