details: Track serial pages as console tabs

A bit cleaner conceptually, and shrinks the ever growing details.py
This commit is contained in:
Cole Robinson 2013-09-01 17:40:38 -04:00
parent cdc82e62a2
commit 10161a8fdb
3 changed files with 468 additions and 471 deletions

View File

@ -692,246 +692,6 @@
<property name="can_focus">True</property>
<property name="show_border">False</property>
<signal name="switch-page" handler="on_details_pages_switch_page" after="yes" swapped="no"/>
<child>
<object class="GtkNotebook" id="console-pages">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="show_border">False</property>
<signal name="switch-page" handler="on_console_pages_switch_page" after="yes" swapped="no"/>
<child>
<object class="GtkLabel" id="console-unavailable">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;The console is currently unavailable&lt;/b&gt;</property>
<property name="use_markup">True</property>
<property name="justify">center</property>
<property name="wrap">True</property>
<property name="width_chars">40</property>
</object>
<packing>
<property name="tab_expand">True</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label434">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">Unavailable</property>
</object>
<packing>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkTable" id="console-auth">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">3</property>
<property name="n_rows">3</property>
<property name="n_columns">3</property>
<property name="column_spacing">3</property>
<property name="row_spacing">3</property>
<child>
<object class="GtkLabel" id="label-auth-password">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Password:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">console-auth-password</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkEntry" id="console-auth-password">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="visibility">False</property>
<signal name="activate" handler="on_console_auth_password_activate" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="console-auth-remember">
<property name="label" translatable="yes">_Save this password in your keyring</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label-auth-username">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Username:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">console-auth-username</property>
</object>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkEntry" id="console-auth-username">
<property name="visible">True</property>
<property name="can_focus">True</property>
<signal name="activate" handler="on_console_auth_password_activate" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkButton" id="console-auth-login">
<property name="label" translatable="yes">_Login</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="on_console_auth_login_clicked" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment17">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment18">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment19">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label438">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">Auth</property>
</object>
<packing>
<property name="position">1</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="console-gfx-scroll">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
<object class="GtkViewport" id="console-gfx-viewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="resize_mode">queue</property>
<property name="shadow_type">none</property>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label439">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">Graphics</property>
</object>
<packing>
<property name="position">2</property>
<property name="tab_fill">False</property>
</packing>
</child>
</object>
</child>
<child type="tab">
<object class="GtkLabel" id="label420">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">Console</property>
</object>
<packing>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox1">
<property name="visible">True</property>
@ -7309,9 +7069,6 @@ I/O:</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label77">
@ -7319,6 +7076,249 @@ I/O:</property>
<property name="can_focus">False</property>
<property name="label">Details</property>
</object>
<packing>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkNotebook" id="console-pages">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="show_border">False</property>
<signal name="switch-page" handler="on_console_pages_switch_page" after="yes" swapped="no"/>
<child>
<object class="GtkLabel" id="console-unavailable">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">&lt;b&gt;The console is currently unavailable&lt;/b&gt;</property>
<property name="use_markup">True</property>
<property name="justify">center</property>
<property name="wrap">True</property>
<property name="width_chars">40</property>
</object>
<packing>
<property name="tab_expand">True</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label434">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">Unavailable</property>
</object>
<packing>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkTable" id="console-auth">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">3</property>
<property name="n_rows">3</property>
<property name="n_columns">3</property>
<property name="column_spacing">3</property>
<property name="row_spacing">3</property>
<child>
<object class="GtkLabel" id="label-auth-password">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Password:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">console-auth-password</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkEntry" id="console-auth-password">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="visibility">False</property>
<signal name="activate" handler="on_console_auth_password_activate" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="console-auth-remember">
<property name="label" translatable="yes">_Save this password in your keyring</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="xalign">0.5</property>
<property name="yalign">0</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkLabel" id="label-auth-username">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Username:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">console-auth-username</property>
</object>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkEntry" id="console-auth-username">
<property name="visible">True</property>
<property name="can_focus">True</property>
<signal name="activate" handler="on_console_auth_password_activate" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkButton" id="console-auth-login">
<property name="label" translatable="yes">_Login</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="on_console_auth_login_clicked" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"/>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment17">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment18">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment19">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label438">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">Auth</property>
</object>
<packing>
<property name="position">1</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="console-gfx-scroll">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
<object class="GtkViewport" id="console-gfx-viewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="resize_mode">queue</property>
<property name="shadow_type">none</property>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label439">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">Graphics</property>
</object>
<packing>
<property name="position">2</property>
<property name="tab_fill">False</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label420">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">Console</property>
</object>
<packing>
<property name="position">1</property>
<property name="tab_fill">False</property>

View File

@ -39,11 +39,14 @@ import logging
import virtManager.uihelpers as uihelpers
from virtManager.autodrawer import AutoDrawer
from virtManager.baseclass import vmmGObjectUI, vmmGObject
from virtManager.serialcon import vmmSerialConsole
from virtManager.details import DETAILS_PAGE_CONSOLE
# Console pages
PAGE_UNAVAILABLE = 0
PAGE_AUTHENTICATE = 1
PAGE_VIEWER = 2
(CONSOLE_PAGE_UNAVAILABLE,
CONSOLE_PAGE_AUTHENTICATE,
CONSOLE_PAGE_VIEWER,
CONSOLE_PAGE_OFFSET) = range(4)
def has_property(obj, setting):
@ -713,6 +716,10 @@ class vmmConsolePages(vmmGObjectUI):
self.widget("console-gfx-viewport").modify_bg(Gtk.StateType.NORMAL,
black)
self.serial_tabs = []
self.last_gfx_page = 0
self._init_menus()
# Signals are added by vmmDetails. Don't use connect_signals here
# or it changes will be overwritten
@ -749,6 +756,11 @@ class vmmConsolePages(vmmGObjectUI):
self.fs_toolbar.destroy()
self.fs_toolbar = None
for serial in self.serial_tabs:
serial.cleanup()
self.serial_tabs = []
##########################
# Initialization helpers #
##########################
@ -803,6 +815,12 @@ class vmmConsolePages(vmmGObjectUI):
pages.add(self.fs_drawer)
def _init_menus(self):
# Serial list menu
smenu = Gtk.Menu()
smenu.connect("show", self.populate_serial_menu)
self.widget("details-menu-view-serial-list").set_submenu(smenu)
def change_title(self, ignore1=None):
title = self.vm.get_name() + " " + _("Virtual Machine")
@ -1006,13 +1024,13 @@ class vmmConsolePages(vmmGObjectUI):
page = pages.get_current_page()
if runable:
if page != PAGE_UNAVAILABLE:
pages.set_current_page(PAGE_UNAVAILABLE)
if page != CONSOLE_PAGE_UNAVAILABLE:
pages.set_current_page(CONSOLE_PAGE_UNAVAILABLE)
self.view_vm_status()
return
elif page in [PAGE_UNAVAILABLE, PAGE_VIEWER]:
elif page in [CONSOLE_PAGE_UNAVAILABLE, CONSOLE_PAGE_VIEWER]:
if self.viewer and self.viewer.is_open():
self.activate_viewer_page()
else:
@ -1022,6 +1040,7 @@ class vmmConsolePages(vmmGObjectUI):
return
###################
# Page Navigation #
###################
@ -1032,7 +1051,7 @@ class vmmConsolePages(vmmGObjectUI):
with care
"""
self.close_viewer()
self.widget("console-pages").set_current_page(PAGE_UNAVAILABLE)
self.widget("console-pages").set_current_page(CONSOLE_PAGE_UNAVAILABLE)
self.widget("details-menu-vm-screenshot").set_sensitive(False)
self.widget("details-menu-usb-redirection").set_sensitive(False)
self.widget("console-unavailable").set_label("<b>" + msg + "</b>")
@ -1042,58 +1061,53 @@ class vmmConsolePages(vmmGObjectUI):
self.widget("details-menu-vm-screenshot").set_sensitive(False)
self.widget("details-menu-usb-redirection").set_sensitive(False)
if withPassword:
self.widget("console-auth-password").show()
self.widget("label-auth-password").show()
else:
self.widget("console-auth-password").hide()
self.widget("label-auth-password").hide()
self.widget("console-auth-password").set_visible(withPassword)
self.widget("label-auth-password").set_visible(withPassword)
if withUsername:
self.widget("console-auth-username").show()
self.widget("label-auth-username").show()
else:
self.widget("console-auth-username").hide()
self.widget("label-auth-username").hide()
self.widget("console-auth-username").set_visible(withUsername)
self.widget("label-auth-username").set_visible(withUsername)
self.widget("console-auth-username").set_text(username)
self.widget("console-auth-password").set_text(pw)
if self.config.has_keyring():
self.widget("console-auth-remember").set_sensitive(True)
if pw != "" or username != "":
self.widget("console-auth-remember").set_active(True)
else:
self.widget("console-auth-remember").set_active(False)
else:
self.widget("console-auth-remember").set_sensitive(False)
self.widget("console-pages").set_current_page(PAGE_AUTHENTICATE)
if withUsername:
self.widget("console-auth-username").grab_focus()
else:
self.widget("console-auth-password").grab_focus()
self.widget("console-auth-username").set_text(username)
self.widget("console-auth-password").set_text(pw)
self.widget("console-auth-remember").set_sensitive(
bool(self.config.has_keyring()))
if self.config.has_keyring():
self.widget("console-auth-remember").set_active(bool(pw and
username))
self.widget("console-pages").set_current_page(
CONSOLE_PAGE_AUTHENTICATE)
def activate_viewer_page(self):
self.widget("console-pages").set_current_page(PAGE_VIEWER)
self.widget("console-pages").set_current_page(CONSOLE_PAGE_VIEWER)
self.widget("details-menu-vm-screenshot").set_sensitive(True)
if self.viewer and self.viewer.display:
self.viewer.display.grab_focus()
if self.viewer.has_usb_redirection() and \
self.vm.has_spicevmc_type_redirdev():
if (self.viewer.has_usb_redirection() and
self.vm.has_spicevmc_type_redirdev()):
self.widget("details-menu-usb-redirection").set_sensitive(True)
return
def page_changed(self, ignore1=None, ignore2=None, ignore3=None):
pagenum = self.widget("console-pages").get_current_page()
if pagenum < CONSOLE_PAGE_OFFSET:
self.last_gfx_page = pagenum
self.set_allow_fullscreen()
def set_allow_fullscreen(self):
cpage = self.widget("console-pages").get_current_page()
dpage = self.widget("details-pages").get_current_page()
allow_fullscreen = (dpage == 0 and
cpage == PAGE_VIEWER and
allow_fullscreen = (dpage == DETAILS_PAGE_CONSOLE and
cpage == CONSOLE_PAGE_VIEWER and
self.viewer_connected)
self.widget("control-fullscreen").set_sensitive(allow_fullscreen)
@ -1358,3 +1372,159 @@ class vmmConsolePages(vmmGObjectUI):
viewer_alloc.width = desktop_w
viewer_alloc.height = desktop_h
self.viewer.display.size_allocate(viewer_alloc)
###########################
# Serial console handling #
###########################
def activate_default_console_page(self):
if self.vm.get_graphics_devices() or not self.vm.get_serial_devs():
return
# Show serial console
devs = self.build_serial_list()
for name, ignore, sensitive, ignore, cb, serialidx in devs:
if not sensitive or not cb:
continue
self._show_serial_tab(name, serialidx)
break
def build_serial_list(self):
ret = []
def add_row(text, err, sensitive, do_radio, cb, serialidx):
ret.append([text, err, sensitive, do_radio, cb, serialidx])
devs = self.vm.get_serial_devs()
if len(devs) == 0:
add_row(_("No text console available"),
None, False, False, None, None)
def build_desc(dev):
if dev.virtual_device_type == "console":
return "Text Console %d" % (dev.vmmindex + 1)
return "Serial %d" % (dev.vmmindex + 1)
for dev in devs:
desc = build_desc(dev)
idx = dev.vmmindex
err = vmmSerialConsole.can_connect(self.vm, dev)
sensitive = not bool(err)
def cb(src):
return self.control_serial_tab(src, desc, idx)
add_row(desc, err, sensitive, True, cb, idx)
return ret
def current_serial_dev(self):
current_page = self.widget("console-pages").get_current_page()
if not current_page >= CONSOLE_PAGE_OFFSET:
return
serial_idx = current_page - CONSOLE_PAGE_OFFSET
if len(self.serial_tabs) < serial_idx:
return
return self.serial_tabs[serial_idx]
def control_serial_tab(self, src_ignore, name, target_port):
self.widget("details-pages").set_current_page(DETAILS_PAGE_CONSOLE)
if name == "graphics":
self.widget("console-pages").set_current_page(self.last_gfx_page)
else:
self._show_serial_tab(name, target_port)
def _show_serial_tab(self, name, target_port):
serial = None
for s in self.serial_tabs:
if s.name == name:
serial = s
break
if not serial:
serial = vmmSerialConsole(self.vm, target_port, name)
title = Gtk.Label(label=name)
self.widget("console-pages").append_page(serial.box, title)
self.serial_tabs.append(serial)
serial.open_console()
page_idx = self.serial_tabs.index(serial) + CONSOLE_PAGE_OFFSET
self.widget("console-pages").set_current_page(page_idx)
def _close_serial_tab(self, serial):
if not serial in self.serial_tabs:
return
page_idx = self.serial_tabs.index(serial) + CONSOLE_PAGE_OFFSET
self.widget("console-pages").remove_page(page_idx)
serial.cleanup()
self.serial_tabs.remove(serial)
def populate_serial_menu(self, src):
for ent in src:
src.remove(ent)
serial_page_dev = self.current_serial_dev()
showing_graphics = (
self.widget("console-pages").get_current_page() ==
CONSOLE_PAGE_VIEWER)
# Populate serial devices
group = None
itemlist = self.build_serial_list()
for msg, err, sensitive, do_radio, cb, ignore in itemlist:
if do_radio:
item = Gtk.RadioMenuItem(group)
item.set_label(msg)
if group is None:
group = item
else:
item = Gtk.MenuItem(msg)
item.set_sensitive(sensitive)
if err and not sensitive:
item.set_tooltip_text(err)
if cb:
item.connect("toggled", cb)
# Tab is already open, make sure marked as such
if (sensitive and
serial_page_dev and
serial_page_dev.name == msg):
item.set_active(True)
src.add(item)
src.add(Gtk.SeparatorMenuItem())
# Populate graphical devices
devs = self.vm.get_graphics_devices()
if len(devs) == 0:
item = Gtk.MenuItem(_("No graphical console available"))
item.set_sensitive(False)
src.add(item)
else:
dev = devs[0]
item = Gtk.RadioMenuItem(group)
item.set_label(_("Graphical Console %s") %
dev.pretty_type_simple(dev.type))
if group is None:
group = item
if showing_graphics:
item.set_active(True)
item.connect("toggled", self.control_serial_tab,
dev.virtual_device_type, dev.type)
src.add(item)
src.show_all()

View File

@ -34,9 +34,7 @@ from virtManager.storagebrowse import vmmStorageBrowser
from virtManager.baseclass import vmmGObjectUI
from virtManager.addhardware import vmmAddHardware
from virtManager.choosecd import vmmChooseCD
from virtManager.console import vmmConsolePages
from virtManager.snapshots import vmmSnapshotPage
from virtManager.serialcon import vmmSerialConsole
from virtManager.graphwidgets import Sparkline
import virtinst
@ -140,10 +138,9 @@ remove_pages = [HW_LIST_TYPE_NIC, HW_LIST_TYPE_INPUT,
BOOT_ACTIVE) = range(4)
# Main tab pages
(PAGE_CONSOLE,
PAGE_DETAILS,
PAGE_SNAPSHOTS,
PAGE_DYNAMIC_OFFSET) = range(4)
(DETAILS_PAGE_DETAILS,
DETAILS_PAGE_CONSOLE,
DETAILS_PAGE_SNAPSHOTS) = range(3)
def prettyify_disk_bus(bus):
@ -359,13 +356,11 @@ class vmmDetails(vmmGObjectUI):
self.widget("details-toolbar").hide()
self.widget("details-menubar").hide()
pages = self.widget("details-pages")
pages.set_current_page(PAGE_DETAILS)
pages.set_current_page(DETAILS_PAGE_DETAILS)
self.active_edits = []
self.serial_tabs = []
self.last_console_page = PAGE_CONSOLE
self.addhw = None
self.media_choosers = {"cdrom": None, "floppy": None}
self.storage_browser = None
@ -374,6 +369,7 @@ class vmmDetails(vmmGObjectUI):
self.ignoreDetails = False
self._cpu_copy_host = False
from virtManager.console import vmmConsolePages
self.console = vmmConsolePages(self.vm, self.builder, self.topwin)
self.snapshots = vmmSnapshotPage(self.vm, self.builder, self.topwin)
self.widget("snapshot-placeholder").add(self.snapshots.top_box)
@ -579,10 +575,6 @@ class vmmDetails(vmmGObjectUI):
self.media_choosers[key].cleanup()
self.media_choosers = {}
for serial in self.serial_tabs:
serial.cleanup()
self.serial_tabs = []
self.console.cleanup()
self.console = None
self.snapshots.cleanup()
@ -686,11 +678,6 @@ class vmmDetails(vmmGObjectUI):
self.addhwmenu.add(addHW)
self.addhwmenu.add(rmHW)
# Serial list menu
smenu = Gtk.Menu()
smenu.connect("show", self.populate_serial_menu)
self.widget("details-menu-view-serial-list").set_submenu(smenu)
# Don't allowing changing network/disks for Dom0
dom0 = self.vm.is_management_domain()
self.widget("add-hardware-button").set_sensitive(not dom0)
@ -1115,106 +1102,6 @@ class vmmDetails(vmmGObjectUI):
self.addhwmenu.popup(None, None, None, None, 0, event.time)
def build_serial_list(self):
ret = []
def add_row(text, err, sensitive, do_radio, cb, serialidx):
ret.append([text, err, sensitive, do_radio, cb, serialidx])
devs = self.vm.get_serial_devs()
if len(devs) == 0:
add_row(_("No text console available"),
None, False, False, None, None)
def build_desc(dev):
if dev.virtual_device_type == "console":
return "Text Console %d" % (dev.vmmindex + 1)
return "Serial %d" % (dev.vmmindex + 1)
for dev in devs:
desc = build_desc(dev)
idx = dev.vmmindex
err = vmmSerialConsole.can_connect(self.vm, dev)
sensitive = not bool(err)
def cb(src):
return self.control_serial_tab(src, desc, idx)
add_row(desc, err, sensitive, True, cb, idx)
return ret
def current_serial_dev(self):
showing_serial = (self.last_console_page >= PAGE_DYNAMIC_OFFSET)
if not showing_serial:
return
serial_idx = self.last_console_page - PAGE_DYNAMIC_OFFSET
if len(self.serial_tabs) < serial_idx:
return
return self.serial_tabs[serial_idx]
def populate_serial_menu(self, src):
for ent in src:
src.remove(ent)
serial_page_dev = self.current_serial_dev()
showing_graphics = (self.last_console_page == PAGE_CONSOLE)
# Populate serial devices
group = None
itemlist = self.build_serial_list()
for msg, err, sensitive, do_radio, cb, ignore in itemlist:
if do_radio:
item = Gtk.RadioMenuItem(group)
item.set_label(msg)
if group is None:
group = item
else:
item = Gtk.MenuItem(msg)
item.set_sensitive(sensitive)
if err and not sensitive:
item.set_tooltip_text(err)
if cb:
item.connect("toggled", cb)
# Tab is already open, make sure marked as such
if (sensitive and
serial_page_dev and
serial_page_dev.name == msg):
item.set_active(True)
src.add(item)
src.add(Gtk.SeparatorMenuItem())
# Populate graphical devices
devs = self.vm.get_graphics_devices()
if len(devs) == 0:
item = Gtk.MenuItem(_("No graphical console available"))
item.set_sensitive(False)
src.add(item)
else:
dev = devs[0]
item = Gtk.RadioMenuItem(group)
item.set_label(_("Graphical Console %s") %
dev.pretty_type_simple(dev.type))
if group is None:
group = item
if showing_graphics:
item.set_active(True)
item.connect("toggled", self.control_serial_tab,
dev.virtual_device_type, dev.type)
src.add(item)
src.show_all()
def control_fullscreen(self, src):
menu = self.widget("details-menu-view-fullscreen")
if src.get_active() != menu.get_active():
@ -1386,19 +1273,19 @@ class vmmDetails(vmmGObjectUI):
src == self.widget("details-menu-view-snapshots"))
pages = self.widget("details-pages")
if pages.get_current_page() == PAGE_DETAILS:
if pages.get_current_page() == DETAILS_PAGE_DETAILS:
if self.has_unapplied_changes(self.get_hw_row()):
self.sync_details_console_view(True)
return
self.disable_apply()
if is_details:
pages.set_current_page(PAGE_DETAILS)
pages.set_current_page(DETAILS_PAGE_DETAILS)
elif is_snapshot:
self.snapshots.show_page()
pages.set_current_page(PAGE_SNAPSHOTS)
pages.set_current_page(DETAILS_PAGE_SNAPSHOTS)
else:
pages.set_current_page(self.last_console_page)
pages.set_current_page(DETAILS_PAGE_CONSOLE)
def sync_details_console_view(self, newpage):
details = self.widget("control-vm-details")
@ -1408,8 +1295,8 @@ class vmmDetails(vmmGObjectUI):
snapshot = self.widget("control-snapshots")
snapshot_menu = self.widget("details-menu-view-snapshots")
is_details = newpage == PAGE_DETAILS
is_snapshot = newpage == PAGE_SNAPSHOTS
is_details = newpage == DETAILS_PAGE_DETAILS
is_snapshot = newpage == DETAILS_PAGE_SNAPSHOTS
is_console = not is_details and not is_snapshot
try:
@ -1430,9 +1317,6 @@ class vmmDetails(vmmGObjectUI):
self.sync_details_console_view(newpage)
self.console.set_allow_fullscreen()
if newpage == PAGE_CONSOLE or newpage >= PAGE_DYNAMIC_OFFSET:
self.last_console_page = newpage
def change_run_text(self, can_restore):
if can_restore:
text = _("_Restore")
@ -1511,41 +1395,27 @@ class vmmDetails(vmmGObjectUI):
self.emit("action-exit-app")
def activate_default_console_page(self):
if self.vm.get_graphics_devices() or not self.vm.get_serial_devs():
return
# Only show serial page if we are already on console view
pages = self.widget("details-pages")
if pages.get_current_page() != PAGE_CONSOLE:
if pages.get_current_page() != DETAILS_PAGE_CONSOLE:
return
# Show serial console
devs = self.build_serial_list()
for name, ignore, sensitive, ignore, cb, serialidx in devs:
if not sensitive or not cb:
continue
self._show_serial_tab(name, serialidx)
break
self.console.activate_default_console_page()
# activate_* are called from engine.py via CLI options
def activate_default_page(self):
pages = self.widget("details-pages")
pages.set_current_page(PAGE_CONSOLE)
pages.set_current_page(DETAILS_PAGE_CONSOLE)
self.activate_default_console_page()
def activate_console_page(self):
pages = self.widget("details-pages")
pages.set_current_page(PAGE_CONSOLE)
pages.set_current_page(DETAILS_PAGE_CONSOLE)
def activate_performance_page(self):
self.widget("details-pages").set_current_page(PAGE_DETAILS)
self.widget("details-pages").set_current_page(DETAILS_PAGE_DETAILS)
self.set_hw_selection(HW_LIST_TYPE_STATS)
def activate_config_page(self):
self.widget("details-pages").set_current_page(PAGE_DETAILS)
self.widget("details-pages").set_current_page(DETAILS_PAGE_DETAILS)
def add_hardware(self, src_ignore):
try:
@ -1700,49 +1570,6 @@ class vmmDetails(vmmGObjectUI):
file(filename, "wb").write(ret)
#########################
# Serial Console pieces #
#########################
def control_serial_tab(self, src_ignore, name, target_port):
pages = self.widget("details-pages")
is_graphics = (name == "graphics")
is_serial = not is_graphics
if is_graphics:
pages.set_current_page(PAGE_CONSOLE)
elif is_serial:
self._show_serial_tab(name, target_port)
def _show_serial_tab(self, name, target_port):
serial = None
for s in self.serial_tabs:
if s.name == name:
serial = s
break
if not serial:
serial = vmmSerialConsole(self.vm, target_port, name)
title = Gtk.Label(label=name)
self.widget("details-pages").append_page(serial.box, title)
self.serial_tabs.append(serial)
serial.open_console()
page_idx = self.serial_tabs.index(serial) + PAGE_DYNAMIC_OFFSET
self.widget("details-pages").set_current_page(page_idx)
def _close_serial_tab(self, serial):
if not serial in self.serial_tabs:
return
page_idx = self.serial_tabs.index(serial) + PAGE_DYNAMIC_OFFSET
self.widget("details-pages").remove_page(page_idx)
serial.cleanup()
self.serial_tabs.remove(serial)
############################
# Details/Hardware getters #
############################
@ -2688,12 +2515,12 @@ class vmmDetails(vmmGObjectUI):
raise
# Stats page needs to be refreshed every tick
if (page == PAGE_DETAILS and
if (page == DETAILS_PAGE_DETAILS and
self.get_hw_selection(HW_LIST_COL_TYPE) == HW_LIST_TYPE_STATS):
self.refresh_stats_page()
def page_refresh(self, page):
if page != PAGE_DETAILS:
if page != DETAILS_PAGE_DETAILS:
return
# This function should only be called when the VM xml actually