Added support for connecting to & displaying the serial console for guest VMs using the 'vte' module
This commit is contained in:
parent
074b847844
commit
9470056682
2
README
2
README
|
@ -32,6 +32,8 @@ please report any success to the mailing lists
|
|||
dbus-python >= 0.61
|
||||
gnome-keyring >= 0.4.9
|
||||
python-ctypes >= 0.9.9.6
|
||||
libxml2-python >= 2.6.23
|
||||
vte >= 0.12.2
|
||||
|
||||
Contact
|
||||
-------
|
||||
|
|
|
@ -29,7 +29,7 @@ class vmmConsole(gobject.GObject):
|
|||
__gsignals__ = {
|
||||
"action-show-details": (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE, (str,str)),
|
||||
"action-launch-terminal": (gobject.SIGNAL_RUN_FIRST,
|
||||
"action-show-terminal": (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE, (str,str)),
|
||||
"action-save-domain": (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE, (str,str))
|
||||
|
@ -126,7 +126,7 @@ class vmmConsole(gobject.GObject):
|
|||
|
||||
def try_login(self, src=None):
|
||||
password = self.window.get_widget("console-auth-password").get_text()
|
||||
protocol, host, port = self.vm.get_console_info()
|
||||
protocol, host, port = self.vm.get_graphics_console()
|
||||
|
||||
if self.vm.get_id() == 0:
|
||||
return
|
||||
|
@ -208,7 +208,7 @@ class vmmConsole(gobject.GObject):
|
|||
print _("Resume requested, but machine is already running")
|
||||
|
||||
def control_vm_terminal(self, src):
|
||||
self.emit("action-launch-terminal", self.vm.get_connection().get_uri(), self.vm.get_uuid())
|
||||
self.emit("action-show-terminal", self.vm.get_connection().get_uri(), self.vm.get_uuid())
|
||||
|
||||
def control_vm_save_domain(self, src):
|
||||
self.emit("action-save-domain", self.vm.get_connection().get_uri(), self.vm.get_uuid())
|
||||
|
|
|
@ -27,7 +27,7 @@ class vmmDetails(gobject.GObject):
|
|||
__gsignals__ = {
|
||||
"action-show-console": (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE, (str,str)),
|
||||
"action-launch-terminal": (gobject.SIGNAL_RUN_FIRST,
|
||||
"action-show-terminal": (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE, (str,str)),
|
||||
"action-save-domain": (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE, (str,str))
|
||||
|
@ -186,7 +186,7 @@ class vmmDetails(gobject.GObject):
|
|||
print _("Resume requested, but machine is already running")
|
||||
|
||||
def control_vm_terminal(self, src):
|
||||
self.emit("action-launch-terminal", self.vm.get_connection().get_uri(), self.vm.get_uuid())
|
||||
self.emit("action-show-terminal", self.vm.get_connection().get_uri(), self.vm.get_uuid())
|
||||
|
||||
def control_vm_console(self, src):
|
||||
self.emit("action-show-console", self.vm.get_connection().get_uri(), self.vm.get_uuid())
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import gobject
|
||||
import libvirt
|
||||
|
||||
import libxml2
|
||||
|
||||
class vmmDomain(gobject.GObject):
|
||||
__gsignals__ = {
|
||||
|
@ -264,11 +264,40 @@ class vmmDomain(gobject.GObject):
|
|||
def run_status_icon(self):
|
||||
return self.config.get_vm_status_icon(self.status())
|
||||
|
||||
def get_console_info(self):
|
||||
# XXX don't hardcode me! need to really extract info from
|
||||
# the libvirt XML as & when the display device info gets
|
||||
# added
|
||||
return ["vnc", "localhost", 5900 + self.get_id()]
|
||||
def get_xml_string(self, path):
|
||||
xml = self.vm.XMLDesc(0)
|
||||
doc = None
|
||||
try:
|
||||
doc = libxml2.parseDoc(xml)
|
||||
except:
|
||||
return None
|
||||
ctx = doc.xpathNewContext()
|
||||
try:
|
||||
ret = ctx.xpathEval(path)
|
||||
tty = None
|
||||
if len(ret) == 1:
|
||||
tty = ret[0].content
|
||||
ctx.xpathFreeContext()
|
||||
doc.freeDoc()
|
||||
return tty
|
||||
except:
|
||||
ctx.xpathFreeContext()
|
||||
doc.freeDoc()
|
||||
return None
|
||||
|
||||
def get_serial_console_tty(self):
|
||||
return self.get_xml_string("/domain/devices/console/@tty")
|
||||
|
||||
def get_graphics_console(self):
|
||||
type = self.get_xml_string("/domain/devices/graphics/@type")
|
||||
port = None
|
||||
if type == "vnc":
|
||||
port = self.get_xml_string("/domain/devices/graphics[type='vnc']/@port")
|
||||
if port == None:
|
||||
return []
|
||||
else:
|
||||
port = int(port)
|
||||
return [type, "localhost", port]
|
||||
|
||||
def set_vcpu_count(self, vcpus):
|
||||
print "If this was implemented, it would set this domain to have " + `vcpus` + " virtual cpus."
|
||||
|
|
|
@ -30,6 +30,7 @@ from virtManager.details import vmmDetails
|
|||
from virtManager.console import vmmConsole
|
||||
from virtManager.asyncjob import vmmAsyncJob
|
||||
from virtManager.create import vmmCreate
|
||||
from virtManager.serialcon import vmmSerialConsole
|
||||
|
||||
class vmmEngine:
|
||||
def __init__(self, config):
|
||||
|
@ -126,6 +127,8 @@ class vmmEngine:
|
|||
self.show_create()
|
||||
def _do_show_console(self, src, uri, uuid):
|
||||
self.show_console(uri, uuid)
|
||||
def _do_show_terminal(self, src, uri, uuid):
|
||||
self.show_serial_console(uri, uuid)
|
||||
def _do_save_domain(self, src, uri, uuid):
|
||||
self.save_domain(src, uri, uuid)
|
||||
|
||||
|
@ -153,10 +156,20 @@ class vmmEngine:
|
|||
console = vmmConsole(self.get_config(),
|
||||
con.get_vm(uuid))
|
||||
console.connect("action-show-details", self._do_show_details)
|
||||
console.connect("action-show-terminal", self._do_show_terminal)
|
||||
console.connect("action-save-domain", self._do_save_domain)
|
||||
self.connections[uri]["windowConsole"][uuid] = console
|
||||
self.connections[uri]["windowConsole"][uuid].show()
|
||||
|
||||
def show_serial_console(self, uri, uuid):
|
||||
con = self.get_connection(uri)
|
||||
|
||||
if not(self.connections[uri]["windowSerialConsole"].has_key(uuid)):
|
||||
console = vmmSerialConsole(self.get_config(),
|
||||
con.get_vm(uuid))
|
||||
self.connections[uri]["windowSerialConsole"][uuid] = console
|
||||
self.connections[uri]["windowSerialConsole"][uuid].show()
|
||||
|
||||
def show_details_performance(self, uri, uuid):
|
||||
win = self.show_details(uri, uuid)
|
||||
win.activate_performance_page()
|
||||
|
@ -172,6 +185,7 @@ class vmmEngine:
|
|||
details = vmmDetails(self.get_config(),
|
||||
con.get_vm(uuid))
|
||||
details.connect("action-show-console", self._do_show_console)
|
||||
details.connect("action-show-terminal", self._do_show_terminal)
|
||||
details.connect("action-save-domain", self._do_save_domain)
|
||||
self.connections[uri]["windowDetails"][uuid] = details
|
||||
self.connections[uri]["windowDetails"][uuid].show()
|
||||
|
@ -204,7 +218,8 @@ class vmmEngine:
|
|||
"connection": vmmConnection(self.get_config(), uri, readOnly),
|
||||
"windowManager": None,
|
||||
"windowDetails": {},
|
||||
"windowConsole": {}
|
||||
"windowConsole": {},
|
||||
"windowSerialConsole": {},
|
||||
}
|
||||
self.connections[uri]["connection"].connect("disconnected", self._do_connection_disconnected)
|
||||
self.connections[uri]["connection"].connect("vm-removed", self._do_vm_removed)
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
#
|
||||
# Copyright (C) 2006 Red Hat, Inc.
|
||||
# Copyright (C) 2006 Daniel P. Berrange <berrange@redhat.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
#
|
||||
|
||||
import gtk
|
||||
import vte
|
||||
import os
|
||||
import gobject
|
||||
import termios
|
||||
import tty
|
||||
|
||||
class vmmSerialConsole:
|
||||
def __init__(self, config, vm):
|
||||
|
||||
self.vm = vm
|
||||
self.config = config
|
||||
|
||||
self.window = gtk.Window()
|
||||
self.window.hide()
|
||||
self.window.set_title(vm.get_name() + " " + "serial console")
|
||||
|
||||
self.terminal = vte.Terminal()
|
||||
self.terminal.set_cursor_blinks(True)
|
||||
self.terminal.set_emulation("xterm")
|
||||
self.terminal.set_font_from_string("fixed 10")
|
||||
self.terminal.set_scrollback_lines(1000)
|
||||
self.terminal.set_audible_bell(False)
|
||||
self.terminal.set_visible_bell(True)
|
||||
|
||||
self.terminal.connect("commit", self.send_data)
|
||||
self.terminal.show()
|
||||
|
||||
scrollbar = gtk.VScrollbar()
|
||||
scrollbar.set_adjustment(self.terminal.get_adjustment())
|
||||
|
||||
box = gtk.HBox()
|
||||
box.pack_start(self.terminal)
|
||||
box.pack_start(scrollbar)
|
||||
|
||||
self.window.add(box)
|
||||
|
||||
self.ptyio = None
|
||||
self.ptysrc = None
|
||||
self.ptytermios = None
|
||||
|
||||
self.window.connect("delete-event", self.hide)
|
||||
|
||||
|
||||
def show(self):
|
||||
self.open()
|
||||
self.window.show_all()
|
||||
|
||||
def hide(self, src=None, ignore=None):
|
||||
self.close()
|
||||
self.window.hide()
|
||||
return True
|
||||
|
||||
def open(self):
|
||||
if self.ptyio != None:
|
||||
self.close()
|
||||
pty = self.vm.get_serial_console_tty()
|
||||
|
||||
if pty == None:
|
||||
return
|
||||
self.ptyio = os.open(pty, os.O_RDWR | os.O_NONBLOCK | os.O_NOCTTY)
|
||||
self.ptysrc = gobject.io_add_watch(self.ptyio, gobject.IO_IN | gobject.IO_ERR | gobject.IO_HUP, self.display_data)
|
||||
|
||||
# Save term settings & set to raw mode
|
||||
self.ptytermios = termios.tcgetattr(self.ptyio)
|
||||
tty.setraw(self.ptyio)
|
||||
|
||||
def close(self):
|
||||
if self.ptyio == None:
|
||||
return
|
||||
# Restore term settings
|
||||
termios.tcsetattr(self.ptyio, termios.TCSADRAIN, self.ptytermios)
|
||||
os.close(self.ptyio)
|
||||
gobject.source_remove(self.ptysrc)
|
||||
self.ptyio = None
|
||||
self.ptysrc = None
|
||||
self.ptytermios = None
|
||||
|
||||
def send_data(self, src, text, length):
|
||||
if self.ptyio != None:
|
||||
os.write(self.ptyio, text)
|
||||
|
||||
def display_data(self, src, cond):
|
||||
if cond == gobject.IO_IN:
|
||||
data = os.read(self.ptyio, 1024)
|
||||
self.terminal.feed(data, len(data))
|
||||
return True
|
||||
else:
|
||||
self.close()
|
||||
return False
|
||||
|
|
@ -29,6 +29,11 @@ Requires: dbus-python >= 0.61
|
|||
Requires: gnome-keyring >= 0.4.9
|
||||
# Minimum we've tested with
|
||||
Requires: python-ctypes >= 0.9.9.6
|
||||
# Minimum we've tested with
|
||||
Requires: libxml2-python >= 2.6.23
|
||||
|
||||
# Earlier vte hand broken python binding module
|
||||
Requires: vte >= 0.12.2
|
||||
|
||||
BuildRequires: pygtk2-devel
|
||||
BuildRequires: gtk2-devel
|
||||
|
|
Loading…
Reference in New Issue