diff --git a/ui/vmm-details.ui b/ui/vmm-details.ui index 172573185..8ad0246e4 100644 --- a/ui/vmm-details.ui +++ b/ui/vmm-details.ui @@ -692,246 +692,6 @@ True False - - - True - True - False - - - - True - False - <b>The console is currently unavailable</b> - True - center - True - 40 - - - True - - - - - True - False - Unavailable - - - False - - - - - True - False - 3 - 3 - 3 - 3 - 3 - - - True - False - 0 - _Password: - True - console-auth-password - - - 1 - 2 - GTK_FILL - - - - - - True - True - False - - - - 1 - 2 - 1 - 2 - - - - - - _Save this password in your keyring - True - True - False - True - 0.5 - 0 - True - - - 1 - 2 - 2 - 3 - GTK_FILL - - - - - - True - False - 0 - _Username: - True - console-auth-username - - - GTK_FILL - - - - - - True - True - - - - 1 - 2 - - - - - - _Login - True - True - False - True - - - - 2 - 3 - 1 - 2 - GTK_FILL - - - - - - True - False - - - - - - 2 - 3 - GTK_FILL - GTK_FILL - - - - - True - False - - - - - - 2 - 3 - GTK_FILL - GTK_FILL - - - - - True - False - - - - - - 2 - 3 - 2 - 3 - GTK_FILL - GTK_FILL - - - - - 1 - - - - - True - False - Auth - - - 1 - False - - - - - True - True - - - True - False - queue - none - - - - - - - - 2 - - - - - True - False - Graphics - - - 2 - False - - - - - - - True - False - Console - - - False - - True @@ -7309,9 +7069,6 @@ I/O: - - 1 - @@ -7319,6 +7076,249 @@ I/O: False Details + + False + + + + + True + True + False + + + + True + False + <b>The console is currently unavailable</b> + True + center + True + 40 + + + True + + + + + True + False + Unavailable + + + False + + + + + True + False + 3 + 3 + 3 + 3 + 3 + + + True + False + 0 + _Password: + True + console-auth-password + + + 1 + 2 + GTK_FILL + + + + + + True + True + False + + + + 1 + 2 + 1 + 2 + + + + + + _Save this password in your keyring + True + True + False + True + 0.5 + 0 + True + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + + True + False + 0 + _Username: + True + console-auth-username + + + GTK_FILL + + + + + + True + True + + + + 1 + 2 + + + + + + _Login + True + True + False + True + + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + True + False + + + + + + 2 + 3 + GTK_FILL + GTK_FILL + + + + + True + False + + + + + + 2 + 3 + GTK_FILL + GTK_FILL + + + + + True + False + + + + + + 2 + 3 + 2 + 3 + GTK_FILL + GTK_FILL + + + + + 1 + + + + + True + False + Auth + + + 1 + False + + + + + True + True + + + True + False + queue + none + + + + + + + + 2 + + + + + True + False + Graphics + + + 2 + False + + + + + 1 + + + + + True + False + Console + 1 False diff --git a/virtManager/console.py b/virtManager/console.py index 9f2eea12c..5a043b5fb 100644 --- a/virtManager/console.py +++ b/virtManager/console.py @@ -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("" + msg + "") @@ -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() diff --git a/virtManager/details.py b/virtManager/details.py index 8a5457587..bd07ca56b 100644 --- a/virtManager/details.py +++ b/virtManager/details.py @@ -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