mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-02-25 18:55:27 -06:00
serialcon: Enable remote text console connections
Done via libvirt's console streaming API. We need to hook into our local libvirt glib impl to make this work.
This commit is contained in:
@@ -395,6 +395,10 @@ def main():
|
||||
logging.exception("Could not get connection to session bus, "
|
||||
"disabling DBus service")
|
||||
|
||||
# Hook libvirt events into glib main loop
|
||||
import virtManager.libvirtglib
|
||||
virtManager.libvirtglib.register_event_impl()
|
||||
|
||||
# At this point we're either starting a brand new controlling instance,
|
||||
# or the dbus comms to existing instance has failed
|
||||
|
||||
|
||||
@@ -151,6 +151,7 @@ class vmmDomain(vmmLibvirtObject):
|
||||
self.getjobinfo_supported = False
|
||||
self.managedsave_supported = False
|
||||
self.getvcpus_supported = False
|
||||
self.remote_console_supported = False
|
||||
|
||||
self._guest = None
|
||||
self._guest_to_define = None
|
||||
@@ -181,6 +182,10 @@ class vmmDomain(vmmLibvirtObject):
|
||||
self.getjobinfo_supported = support.check_domain_support(self._backend,
|
||||
support.SUPPORT_DOMAIN_JOB_INFO)
|
||||
|
||||
self.remote_console_supported = support.check_domain_support(
|
||||
self._backend,
|
||||
support.SUPPORT_DOMAIN_CONSOLE_STREAM)
|
||||
|
||||
# Determine available XML flags (older libvirt versions will error
|
||||
# out if passed SECURE_XML, INACTIVE_XML, etc)
|
||||
(self._inactive_xml_flags,
|
||||
@@ -741,6 +746,8 @@ class vmmDomain(vmmLibvirtObject):
|
||||
def abort_job(self):
|
||||
self._backend.abortJob()
|
||||
|
||||
def open_console(self, devname, stream, flags=0):
|
||||
return self._backend.openConsole(devname, stream, flags)
|
||||
|
||||
########################
|
||||
# XML Parsing routines #
|
||||
|
||||
@@ -129,11 +129,125 @@ class LocalConsoleConnection(ConsoleConnection):
|
||||
terminal.feed(data, len(data))
|
||||
return True
|
||||
|
||||
class LibvirtConsoleConnection(ConsoleConnection):
|
||||
def __init__(self, vm):
|
||||
ConsoleConnection.__init__(self, vm)
|
||||
|
||||
self.stream = None
|
||||
|
||||
self.streamToTerminal = ""
|
||||
self.terminalToStream = ""
|
||||
|
||||
def cleanup(self):
|
||||
ConsoleConnection.cleanup(self)
|
||||
self.close()
|
||||
|
||||
def _event_on_stream(self, stream, events, opaque):
|
||||
ignore = stream
|
||||
terminal = opaque
|
||||
|
||||
if (events & libvirt.VIR_EVENT_HANDLE_ERROR or
|
||||
events & libvirt.VIR_EVENT_HANDLE_HANGUP):
|
||||
logging.debug("Received stream ERROR/HANGUP, closing console")
|
||||
self.close()
|
||||
|
||||
if events & libvirt.VIR_EVENT_HANDLE_READABLE:
|
||||
try:
|
||||
got = self.stream.recv(1024)
|
||||
except:
|
||||
logging.exception("Error receiving stream data")
|
||||
self.close()
|
||||
return
|
||||
|
||||
if got == -2:
|
||||
return
|
||||
|
||||
self.streamToTerminal += got
|
||||
if self.streamToTerminal:
|
||||
self.safe_idle_add(self.display_data, terminal)
|
||||
|
||||
if (events & libvirt.VIR_EVENT_HANDLE_WRITABLE and
|
||||
self.terminalToStream):
|
||||
|
||||
try:
|
||||
done = self.stream.send(self.terminalToStream)
|
||||
except:
|
||||
logging.exception("Error sending stream data")
|
||||
self.close()
|
||||
return
|
||||
|
||||
if done == -2:
|
||||
return
|
||||
|
||||
self.terminalToStream = self.terminalToStream[done:]
|
||||
|
||||
if not self.terminalToStream:
|
||||
self.stream.eventUpdateCallback(libvirt.VIR_STREAM_EVENT_READABLE |
|
||||
libvirt.VIR_STREAM_EVENT_ERROR |
|
||||
libvirt.VIR_STREAM_EVENT_HANGUP)
|
||||
|
||||
|
||||
def open(self, dev, terminal):
|
||||
if self.stream:
|
||||
self.close()
|
||||
|
||||
self.stream = self.conn.vmm.newStream(libvirt.VIR_STREAM_NONBLOCK)
|
||||
|
||||
name = dev and dev.alias.name or None
|
||||
logging.debug("Opening console stream for dev=%s alias=%s" %
|
||||
(dev, name))
|
||||
self.vm.open_console(name, self.stream)
|
||||
|
||||
self.stream.eventAddCallback((libvirt.VIR_STREAM_EVENT_READABLE |
|
||||
libvirt.VIR_STREAM_EVENT_ERROR |
|
||||
libvirt.VIR_STREAM_EVENT_HANGUP),
|
||||
self._event_on_stream,
|
||||
terminal)
|
||||
|
||||
def close(self):
|
||||
if self.stream:
|
||||
try:
|
||||
self.stream.eventRemoveCallback()
|
||||
except:
|
||||
logging.exception("Error removing stream callback")
|
||||
try:
|
||||
self.stream.finish()
|
||||
except:
|
||||
logging.exception("Error finishing stream")
|
||||
|
||||
self.stream = None
|
||||
|
||||
def send_data(self, src, text, length, terminal):
|
||||
ignore = src
|
||||
ignore = length
|
||||
ignore = terminal
|
||||
|
||||
if self.stream is None:
|
||||
return
|
||||
|
||||
self.terminalToStream += text
|
||||
if self.terminalToStream:
|
||||
self.stream.eventUpdateCallback(libvirt.VIR_STREAM_EVENT_READABLE |
|
||||
libvirt.VIR_STREAM_EVENT_WRITABLE |
|
||||
libvirt.VIR_STREAM_EVENT_ERROR |
|
||||
libvirt.VIR_STREAM_EVENT_HANGUP)
|
||||
|
||||
def display_data(self, terminal):
|
||||
if not self.streamToTerminal:
|
||||
return
|
||||
|
||||
terminal.feed(self.streamToTerminal, len(self.streamToTerminal))
|
||||
self.streamToTerminal = ""
|
||||
|
||||
class vmmSerialConsole(vmmGObject):
|
||||
|
||||
@staticmethod
|
||||
def support_remote_console(vm):
|
||||
"""
|
||||
Check if we can connect to a remote console
|
||||
"""
|
||||
return bool(vm.remote_console_supported)
|
||||
|
||||
@staticmethod
|
||||
def can_connect(vm, dev):
|
||||
"""
|
||||
@@ -143,18 +257,23 @@ class vmmSerialConsole(vmmGObject):
|
||||
|
||||
ctype = dev.char_type
|
||||
path = dev.source_path
|
||||
is_remote = vm.get_connection().is_remote()
|
||||
support_tunnel = vmmSerialConsole.support_remote_console(vm)
|
||||
|
||||
err = ""
|
||||
|
||||
if vm.get_connection().is_remote():
|
||||
err = _("Serial console not yet supported over remote "
|
||||
"connection")
|
||||
if is_remote:
|
||||
if not support_tunnel:
|
||||
err = _("Serial console not yet supported over remote "
|
||||
"connection")
|
||||
elif not vm.is_active():
|
||||
err = _("Serial console not available for inactive guest")
|
||||
elif not ctype in usable_types:
|
||||
err = (_("Console for device type '%s' not yet supported") %
|
||||
ctype)
|
||||
elif path and not os.access(path, os.R_OK | os.W_OK):
|
||||
elif (not is_remote and
|
||||
not support_tunnel and
|
||||
(path and not os.access(path, os.R_OK | os.W_OK))):
|
||||
err = _("Can not access console path '%s'") % str(path)
|
||||
|
||||
return err
|
||||
@@ -167,7 +286,12 @@ class vmmSerialConsole(vmmGObject):
|
||||
self.name = name
|
||||
self.lastpath = None
|
||||
|
||||
self.console = LocalConsoleConnection(self.vm)
|
||||
# Always use libvirt console streaming if available, so
|
||||
# we exercise the same code path (it's what virsh console does)
|
||||
if vmmSerialConsole.support_remote_console(self.vm):
|
||||
self.console = LibvirtConsoleConnection(self.vm)
|
||||
else:
|
||||
self.console = LocalConsoleConnection(self.vm)
|
||||
|
||||
self.serial_popup = None
|
||||
self.serial_copy = None
|
||||
|
||||
Reference in New Issue
Block a user