mirror of
https://github.com/virt-manager/virt-manager.git
synced 2024-12-23 17:34:21 +03:00
285 lines
8.8 KiB
Python
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)
|