Added support for connecting to & displaying the serial console for guest VMs using the 'vte' module

This commit is contained in:
Daniel P. Berrange 2006-08-10 16:47:14 -04:00
parent 074b847844
commit 9470056682
7 changed files with 173 additions and 12 deletions

2
README
View File

@ -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
-------

View File

@ -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())

View File

@ -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())

View File

@ -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."

View File

@ -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)

View File

@ -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

View File

@ -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