# # Copyright (C) 2006-2008, 2013, 2014 Red Hat, Inc. # Copyright (C) 2006 Daniel P. Berrange # # 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., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301 USA. # import logging import traceback from gi.repository import GObject from gi.repository import Gtk from gi.repository import Gdk import libvirt from . import vmmenu from . import uiutil from .baseclass import vmmGObjectUI from .addhardware import vmmAddHardware from .choosecd import vmmChooseCD from .fsdetails import vmmFSDetails from .gfxdetails import vmmGraphicsDetails from .netlist import vmmNetworkList from .snapshots import vmmSnapshotPage from .storagebrowse import vmmStorageBrowser from .graphwidgets import Sparkline import virtinst from virtinst import util from virtinst import VirtualRNGDevice # Parameters that can be edited in the details window (EDIT_NAME, EDIT_TITLE, EDIT_MACHTYPE, EDIT_FIRMWARE, EDIT_DESC, EDIT_IDMAP, EDIT_VCPUS, EDIT_MAXVCPUS, EDIT_CPUSET, EDIT_CPU, EDIT_TOPOLOGY, EDIT_MEM, EDIT_AUTOSTART, EDIT_BOOTORDER, EDIT_BOOTMENU, EDIT_KERNEL, EDIT_INIT, EDIT_DISK_RO, EDIT_DISK_SHARE, EDIT_DISK_REMOVABLE, EDIT_DISK_CACHE, EDIT_DISK_IO, EDIT_DISK_BUS, EDIT_DISK_SERIAL, EDIT_DISK_FORMAT, EDIT_DISK_IOTUNE, EDIT_DISK_SGIO, EDIT_SOUND_MODEL, EDIT_SMARTCARD_MODE, EDIT_NET_MODEL, EDIT_NET_VPORT, EDIT_NET_SOURCE, EDIT_NET_MAC, EDIT_GFX_PASSWD, EDIT_GFX_TYPE, EDIT_GFX_KEYMAP, EDIT_GFX_ADDRESS, EDIT_GFX_TLSPORT, EDIT_GFX_PORT, EDIT_VIDEO_MODEL, EDIT_WATCHDOG_MODEL, EDIT_WATCHDOG_ACTION, EDIT_CONTROLLER_MODEL, EDIT_TPM_TYPE, EDIT_FS, EDIT_HOSTDEV_ROMBAR, ) = range(1, 47) # Columns in hw list model (HW_LIST_COL_LABEL, HW_LIST_COL_ICON_NAME, HW_LIST_COL_ICON_SIZE, HW_LIST_COL_TYPE, HW_LIST_COL_DEVICE) = range(5) # Types for the hw list model: numbers specify what order they will be listed (HW_LIST_TYPE_GENERAL, HW_LIST_TYPE_INSPECTION, HW_LIST_TYPE_STATS, HW_LIST_TYPE_CPU, HW_LIST_TYPE_MEMORY, HW_LIST_TYPE_BOOT, HW_LIST_TYPE_DISK, HW_LIST_TYPE_NIC, HW_LIST_TYPE_INPUT, HW_LIST_TYPE_GRAPHICS, HW_LIST_TYPE_SOUND, HW_LIST_TYPE_CHAR, HW_LIST_TYPE_HOSTDEV, HW_LIST_TYPE_VIDEO, HW_LIST_TYPE_WATCHDOG, HW_LIST_TYPE_CONTROLLER, HW_LIST_TYPE_FILESYSTEM, HW_LIST_TYPE_SMARTCARD, HW_LIST_TYPE_REDIRDEV, HW_LIST_TYPE_TPM, HW_LIST_TYPE_RNG, HW_LIST_TYPE_PANIC) = range(22) remove_pages = [HW_LIST_TYPE_NIC, HW_LIST_TYPE_INPUT, HW_LIST_TYPE_GRAPHICS, HW_LIST_TYPE_SOUND, HW_LIST_TYPE_CHAR, HW_LIST_TYPE_HOSTDEV, HW_LIST_TYPE_DISK, HW_LIST_TYPE_VIDEO, HW_LIST_TYPE_WATCHDOG, HW_LIST_TYPE_CONTROLLER, HW_LIST_TYPE_FILESYSTEM, HW_LIST_TYPE_SMARTCARD, HW_LIST_TYPE_REDIRDEV, HW_LIST_TYPE_TPM, HW_LIST_TYPE_RNG, HW_LIST_TYPE_PANIC] # Boot device columns (BOOT_KEY, BOOT_LABEL, BOOT_ICON, BOOT_ACTIVE, BOOT_CAN_SELECT) = range(5) # Main tab pages (DETAILS_PAGE_DETAILS, DETAILS_PAGE_CONSOLE, DETAILS_PAGE_SNAPSHOTS) = range(3) _remove_tooltip = _("Remove this device from the virtual machine") def _label_for_device(dev, vm): devtype = dev.virtual_device_type if devtype == "disk": def _find_matching_disk_controller(): for controller in vm.get_controller_devices(): if (dev.address.controller is not None and controller.type == dev.bus and controller.index == dev.address.controller): return controller bus = dev.bus if dev.address.type == "spapr-vio": bus = "spapr-vscsi" busstr = virtinst.VirtualDisk.pretty_disk_bus(bus) or "" if bus == "scsi": matching_controller = _find_matching_disk_controller() if (matching_controller and matching_controller.model == "virtio-scsi"): busstr = "Virtio SCSI" if dev.device == "floppy": devstr = "Floppy" busstr = "" elif dev.device == "cdrom": devstr = "CDROM" else: devstr = dev.device.capitalize() if busstr: ret = "%s %s" % (busstr, devstr) else: ret = devstr return "%s %s" % (ret, dev.disk_bus_index) if devtype == "interface": if dev.macaddr: return "NIC %s" % dev.macaddr[-9:] else: return "NIC" if devtype == "input": if dev.type == "tablet": return _("Tablet") elif dev.type == "mouse": return _("Mouse") elif dev.type == "keyboard": return _("Keyboard") return _("Input") if devtype in ["serial", "parallel", "console"]: label = devtype.capitalize() if dev.target_port is not None: label += " %s" % (int(dev.target_port) + 1) return label if devtype == "channel": label = devtype.capitalize() name = dev.pretty_channel_name(dev.target_name) if name: label += " %s" % name return label if devtype == "graphics": return _("Display %s") % dev.pretty_type_simple(dev.type) if devtype == "redirdev": return _("%s Redirector %s") % (dev.bus.upper(), dev.vmmindex + 1) if devtype == "hostdev": return dev.pretty_name() if devtype == "sound": return _("Sound: %s" % dev.model) if devtype == "video": return _("Video %s") % dev.pretty_model(dev.model) if devtype == "filesystem": return _("Filesystem %s") % dev.target[:8] if devtype == "controller": return _("Controller %s") % dev.pretty_desc() devmap = { "rng": _("RNG"), "tpm": _("TPM"), "panic": _("Panic Notifier"), "smartcard": _("Smartcard"), "watchdog": _("Watchdog"), } return devmap[devtype] def _icon_for_device(dev): devtype = dev.virtual_device_type if devtype == "disk": if dev.device == "cdrom": return "media-optical" elif dev.device == "floppy": return "media-floppy" return "drive-harddisk" if devtype == "input": if dev.type == "keyboard": return "input-keyboard" if dev.type == "tablet": return "input-tablet" return "input-mouse" if devtype == "redirdev": if dev.bus == "usb": return "device_usb" return "device_pci" if devtype == "hostdev": if dev.type == "usb": return "device_usb" return "device_pci" typemap = { "interface": "network-idle", "graphics": "video-display", "serial": "device_serial", "parallel": "device_serial", "console": "device_serial", "channel": "device_serial", "video": "video-display", "watchdog": "device_pci", "sound": "audio-card", "rng": "system-run", "tpm": "device_cpu", "smartcard": "device_serial", "filesystem": "folder", "controller": "device_pci", "panic": "system-run", } return typemap[devtype] def _chipset_label_from_machine(machine): if machine and "q35" in machine: return "Q35" return "i440FX" def _warn_cpu_thread_topo(threads, cpu_model): if (threads < 2): return False non_ht_cpus = ["athlon", "phenom", "opteron"] for cpu in non_ht_cpus: if (cpu in cpu_model.lower()): return True return False class vmmDetails(vmmGObjectUI): __gsignals__ = { "action-save-domain": (GObject.SignalFlags.RUN_FIRST, None, [str, str]), "action-destroy-domain": (GObject.SignalFlags.RUN_FIRST, None, [str, str]), "action-suspend-domain": (GObject.SignalFlags.RUN_FIRST, None, [str, str]), "action-resume-domain": (GObject.SignalFlags.RUN_FIRST, None, [str, str]), "action-run-domain": (GObject.SignalFlags.RUN_FIRST, None, [str, str]), "action-shutdown-domain": (GObject.SignalFlags.RUN_FIRST, None, [str, str]), "action-reset-domain": (GObject.SignalFlags.RUN_FIRST, None, [str, str]), "action-reboot-domain": (GObject.SignalFlags.RUN_FIRST, None, [str, str]), "action-exit-app": (GObject.SignalFlags.RUN_FIRST, None, []), "action-view-manager": (GObject.SignalFlags.RUN_FIRST, None, []), "action-migrate-domain": (GObject.SignalFlags.RUN_FIRST, None, [str, str]), "action-delete-domain": (GObject.SignalFlags.RUN_FIRST, None, [str, str]), "action-clone-domain": (GObject.SignalFlags.RUN_FIRST, None, [str, str]), "details-closed": (GObject.SignalFlags.RUN_FIRST, None, []), "details-opened": (GObject.SignalFlags.RUN_FIRST, None, []), "customize-finished": (GObject.SignalFlags.RUN_FIRST, None, []), } def __init__(self, vm, parent=None): vmmGObjectUI.__init__(self, "details.ui", "vmm-details") self.vm = vm self.conn = self.vm.conn self.is_customize_dialog = False if parent: # Details window is being abused as a 'configure before install' # dialog, set things as appropriate self.is_customize_dialog = True self.topwin.set_type_hint(Gdk.WindowTypeHint.DIALOG) self.topwin.set_transient_for(parent) self.widget("toolbar-box").show() self.widget("customize-toolbar").show() self.widget("details-toolbar").hide() self.widget("details-menubar").hide() pages = self.widget("details-pages") pages.set_current_page(DETAILS_PAGE_DETAILS) self.active_edits = [] self.addhw = None self.media_choosers = {"cdrom": None, "floppy": None} self.storage_browser = None self.ignorePause = False self.ignoreDetails = False from .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) self.fsDetails = vmmFSDetails(self.vm, self.builder, self.topwin) self.widget("fs-alignment").add(self.fsDetails.top_box) self.fsDetails.connect("changed", lambda *x: self.enable_apply(x, EDIT_FS)) self.gfxdetails = vmmGraphicsDetails( self.vm, self.builder, self.topwin) self.widget("graphics-align").add(self.gfxdetails.top_box) self.gfxdetails.connect("changed-type", lambda *x: self.enable_apply(x, EDIT_GFX_TYPE)) self.gfxdetails.connect("changed-port", lambda *x: self.enable_apply(x, EDIT_GFX_PORT)) self.gfxdetails.connect("changed-tlsport", lambda *x: self.enable_apply(x, EDIT_GFX_TLSPORT)) self.gfxdetails.connect("changed-address", lambda *x: self.enable_apply(x, EDIT_GFX_ADDRESS)) self.gfxdetails.connect("changed-keymap", lambda *x: self.enable_apply(x, EDIT_GFX_KEYMAP)) self.gfxdetails.connect("changed-password", lambda *x: self.enable_apply(x, EDIT_GFX_PASSWD)) self.netlist = vmmNetworkList(self.conn, self.builder, self.topwin) self.widget("network-source-label-align").add(self.netlist.top_label) self.widget("network-source-ui-align").add(self.netlist.top_box) self.widget("network-vport-align").add(self.netlist.top_vport) self.netlist.connect("changed", lambda x: self.enable_apply(x, EDIT_NET_SOURCE)) self.netlist.connect("changed-vport", lambda x: self.enable_apply(x, EDIT_NET_VPORT)) # Set default window size w, h = self.vm.get_details_window_size() if w <= 0: w = 800 if h <= 0: h = 600 self.topwin.set_default_size(w, h) self.oldhwkey = None self.addhwmenu = None self._addhwmenuitems = None self.keycombo_menu = None self.init_menus() self.init_details() self.cpu_usage_graph = None self.memory_usage_graph = None self.disk_io_graph = None self.network_traffic_graph = None self.init_graphs() self.builder.connect_signals({ "on_close_details_clicked": self.close, "on_details_menu_close_activate": self.close, "on_vmm_details_delete_event": self._window_delete_event, "on_vmm_details_configure_event": self.window_resized, "on_details_menu_quit_activate": self.exit_app, "on_hw_list_changed": self.hw_changed, "on_config_boot_list_changed": self.config_bootdev_selected, "on_control_vm_details_toggled": self.details_console_changed, "on_control_vm_console_toggled": self.details_console_changed, "on_control_snapshots_toggled": self.details_console_changed, "on_control_run_clicked": self.control_vm_run, "on_control_shutdown_clicked": self.control_vm_shutdown, "on_control_pause_toggled": self.control_vm_pause, "on_control_fullscreen_toggled": self.control_fullscreen, "on_details_customize_finish_clicked": self.customize_finish, "on_details_cancel_customize_clicked": self._customize_cancel_clicked, "on_details_menu_virtual_manager_activate": self.control_vm_menu, "on_details_menu_run_activate": self.control_vm_run, "on_details_menu_poweroff_activate": self.control_vm_shutdown, "on_details_menu_reboot_activate": self.control_vm_reboot, "on_details_menu_save_activate": self.control_vm_save, "on_details_menu_reset_activate": self.control_vm_reset, "on_details_menu_destroy_activate": self.control_vm_destroy, "on_details_menu_pause_activate": self.control_vm_pause, "on_details_menu_clone_activate": self.control_vm_clone, "on_details_menu_migrate_activate": self.control_vm_migrate, "on_details_menu_delete_activate": self.control_vm_delete, "on_details_menu_screenshot_activate": self.control_vm_screenshot, "on_details_menu_usb_redirection": self.control_vm_usb_redirection, "on_details_menu_view_toolbar_activate": self.toggle_toolbar, "on_details_menu_view_manager_activate": self.view_manager, "on_details_menu_view_details_toggled": self.details_console_changed, "on_details_menu_view_console_toggled": self.details_console_changed, "on_details_menu_view_snapshots_toggled": self.details_console_changed, "on_details_pages_switch_page": self.switch_page, "on_overview_name_changed": lambda *x: self.enable_apply(x, EDIT_NAME), "on_overview_title_changed": lambda *x: self.enable_apply(x, EDIT_TITLE), "on_machine_type_changed": lambda *x: self.enable_apply(x, EDIT_MACHTYPE), "on_overview_firmware_changed": lambda *x: self.enable_apply(x, EDIT_FIRMWARE), "on_overview_chipset_changed": lambda *x: self.enable_apply(x, EDIT_MACHTYPE), "on_idmap_uid_target_changed": lambda *x: self.enable_apply(x, EDIT_IDMAP), "on_idmap_uid_count_changed": lambda *x: self.enable_apply(x, EDIT_IDMAP), "on_idmap_gid_target_changed": lambda *x: self.enable_apply(x, EDIT_IDMAP), "on_idmap_gid_count_changed": lambda *x: self.enable_apply(x, EDIT_IDMAP), "on_config_idmap_check_toggled": self.config_idmap_enable, "on_config_vcpus_changed": self.config_vcpus_changed, "on_config_maxvcpus_changed": self.config_maxvcpus_changed, "on_config_vcpupin_changed": lambda *x: self.enable_apply(x, EDIT_CPUSET), "on_config_vcpupin_generate_clicked": self.config_vcpupin_generate, "on_cpu_model_changed": lambda *x: self.config_cpu_model_changed(x), "on_cpu_copy_host_clicked": self.on_cpu_copy_host_clicked, "on_cpu_cores_changed": self.config_cpu_topology_changed, "on_cpu_sockets_changed": self.config_cpu_topology_changed, "on_cpu_threads_changed": self.config_cpu_topology_changed, "on_cpu_topology_enable_toggled": self.config_cpu_topology_enable, "on_config_memory_changed": self.config_memory_changed, "on_config_maxmem_changed": self.config_maxmem_changed, "on_config_boot_moveup_clicked" : lambda *x: self.config_boot_move(x, True), "on_config_boot_movedown_clicked" : lambda *x: self.config_boot_move(x, False), "on_config_autostart_changed": lambda *x: self.enable_apply(x, x, EDIT_AUTOSTART), "on_boot_menu_changed": lambda *x: self.enable_apply(x, EDIT_BOOTMENU), "on_boot_kernel_enable_toggled": self.boot_kernel_toggled, "on_boot_kernel_changed": lambda *x: self.enable_apply(x, EDIT_KERNEL), "on_boot_initrd_changed": lambda *x: self.enable_apply(x, EDIT_KERNEL), "on_boot_dtb_changed": lambda *x: self.enable_apply(x, EDIT_KERNEL), "on_boot_kernel_args_changed": lambda *x: self.enable_apply(x, EDIT_KERNEL), "on_boot_kernel_browse_clicked": self.browse_kernel, "on_boot_initrd_browse_clicked": self.browse_initrd, "on_boot_dtb_browse_clicked": self.browse_dtb, "on_boot_init_path_changed": lambda *x: self.enable_apply(x, EDIT_INIT), "on_boot_init_args_changed": lambda *x: self.enable_apply(x, EDIT_INIT), "on_disk_readonly_changed": lambda *x: self.enable_apply(x, EDIT_DISK_RO), "on_disk_shareable_changed": lambda *x: self.enable_apply(x, EDIT_DISK_SHARE), "on_disk_removable_changed": lambda *x: self.enable_apply(x, EDIT_DISK_REMOVABLE), "on_disk_cache_combo_changed": lambda *x: self.enable_apply(x, EDIT_DISK_CACHE), "on_disk_io_combo_changed": lambda *x: self.enable_apply(x, EDIT_DISK_IO), "on_disk_bus_combo_changed": lambda *x: self.enable_apply(x, EDIT_DISK_BUS), "on_disk_format_changed": self.disk_format_changed, "on_disk_serial_changed": lambda *x: self.enable_apply(x, EDIT_DISK_SERIAL), "on_disk_iotune_changed": self.iotune_changed, "on_disk_sgio_entry_changed": lambda *x: self.enable_apply(x, EDIT_DISK_SGIO), "on_network_model_combo_changed": lambda *x: self.enable_apply(x, EDIT_NET_MODEL), "on_network_mac_entry_changed": lambda *x: self.enable_apply(x, EDIT_NET_MAC), "on_sound_model_combo_changed": lambda *x: self.enable_apply(x, EDIT_SOUND_MODEL), "on_video_model_combo_changed": lambda *x: self.enable_apply(x, EDIT_VIDEO_MODEL), "on_watchdog_model_combo_changed": lambda *x: self.enable_apply(x, EDIT_WATCHDOG_MODEL), "on_watchdog_action_combo_changed": lambda *x: self.enable_apply(x, EDIT_WATCHDOG_ACTION), "on_smartcard_mode_combo_changed": lambda *x: self.enable_apply(x, EDIT_SMARTCARD_MODE), "on_hostdev_rombar_toggled": lambda *x: self.enable_apply( x, EDIT_HOSTDEV_ROMBAR), "on_controller_model_combo_changed": (lambda *x: self.enable_apply(x, EDIT_CONTROLLER_MODEL)), "on_config_apply_clicked": self.config_apply, "on_config_cancel_clicked": self.config_cancel, "on_config_cdrom_connect_clicked": self.toggle_storage_media, "on_config_remove_clicked": self.remove_xml_dev, "on_add_hardware_button_clicked": self.add_hardware, "on_hw_list_button_press_event": self.popup_addhw_menu, # Listeners stored in vmmConsolePages "on_details_menu_view_fullscreen_activate": ( self.console.details_toggle_fullscreen), "on_details_menu_view_size_to_vm_activate": ( self.console.details_size_to_vm), "on_details_menu_view_scale_always_toggled": ( self.console.details_scaling_ui_changed_cb), "on_details_menu_view_scale_fullscreen_toggled": ( self.console.details_scaling_ui_changed_cb), "on_details_menu_view_scale_never_toggled": ( self.console.details_scaling_ui_changed_cb), "on_details_menu_view_resizeguest_toggled": ( self.console.details_resizeguest_ui_changed_cb), "on_console_pages_switch_page": ( self.console.details_page_changed), "on_console_auth_password_activate": ( self.console.details_auth_login), "on_console_auth_login_clicked": ( self.console.details_auth_login), }) # Deliberately keep all this after signal connection self.vm.connect("state-changed", self.refresh_vm_state) self.vm.connect("resources-sampled", self.refresh_resources) self.populate_hw_list() self.hw_selected() self.refresh_vm_state() def _cleanup(self): self.oldhwkey = None if self.addhw: self.addhw.cleanup() self.addhw = None if self.storage_browser: self.storage_browser.cleanup() self.storage_browser = None for key in self.media_choosers: if self.media_choosers[key]: self.media_choosers[key].cleanup() self.media_choosers = {} self.console.cleanup() self.console = None self.snapshots.cleanup() self.snapshots = None self.vm = None self.conn = None self.addhwmenu = None self._addhwmenuitems = None self.gfxdetails.cleanup() self.gfxdetails = None self.fsDetails.cleanup() self.fsDetails = None self.netlist.cleanup() self.netlist = None def show(self): logging.debug("Showing VM details: %s", self.vm) vis = self.is_visible() self.topwin.present() if vis: return self.emit("details-opened") self.refresh_vm_state() def customize_finish(self, src): ignore = src if self.has_unapplied_changes(self.get_hw_row()): return return self._close(customize_finish=True) def _customize_cancel(self): logging.debug("Asking to cancel customization") result = self.err.yes_no( _("This will abort the installation. Are you sure?")) if not result: logging.debug("Customize cancel aborted") return logging.debug("Canceling customization") return self._close() def _customize_cancel_clicked(self, src): ignore = src return self._customize_cancel() def _window_delete_event(self, ignore1=None, ignore2=None): return self._close() def close(self, ignore1=None, ignore2=None): logging.debug("Closing VM details: %s", self.vm) return self._close() def _close(self, customize_finish=False): fs = self.widget("details-menu-view-fullscreen") if fs.get_active(): fs.set_active(False) if not self.is_visible(): return self.topwin.hide() if self.console.details_viewer_is_visible(): try: self.console.details_close_viewer() except: logging.error("Failure when disconnecting from desktop server") if customize_finish: self.emit("customize-finished") else: self.emit("details-closed") return 1 def is_visible(self): return bool(self.topwin.get_visible()) ########################## # Initialization helpers # ########################## def init_menus(self): # Virtual Machine menu menu = vmmenu.VMShutdownMenu(self, lambda: self.vm) self.widget("control-shutdown").set_menu(menu) self.widget("control-shutdown").set_icon_name("system-shutdown") topmenu = self.widget("details-vm-menu") submenu = topmenu.get_submenu() newmenu = vmmenu.VMActionMenu(self, lambda: self.vm, show_open=False) for child in submenu.get_children(): submenu.remove(child) newmenu.add(child) topmenu.set_submenu(newmenu) topmenu.show_all() # Add HW popup menu self.addhwmenu = Gtk.Menu() addHW = Gtk.ImageMenuItem.new_with_label(_("_Add Hardware")) addHW.set_use_underline(True) addHWImg = Gtk.Image() addHWImg.set_from_stock(Gtk.STOCK_ADD, Gtk.IconSize.MENU) addHW.set_image(addHWImg) addHW.show() addHW.connect("activate", self.add_hardware) rmHW = Gtk.ImageMenuItem.new_with_label(_("_Remove Hardware")) rmHW.set_use_underline(True) rmHWImg = Gtk.Image() rmHWImg.set_from_stock(Gtk.STOCK_REMOVE, Gtk.IconSize.MENU) rmHW.set_image(rmHWImg) rmHW.show() rmHW.connect("activate", self.remove_xml_dev) self._addhwmenuitems = {"add" : addHW, "remove" : rmHW} for i in self._addhwmenuitems.values(): self.addhwmenu.add(i) # Don't allowing changing network/disks for Dom0 dom0 = self.vm.is_management_domain() self.widget("add-hardware-button").set_sensitive(not dom0) self.widget("hw-panel").set_show_tabs(False) self.widget("details-pages").set_show_tabs(False) self.widget("console-pages").set_show_tabs(False) self.widget("details-menu-view-toolbar").set_active( self.config.get_details_show_toolbar()) # Keycombo menu (ctrl+alt+del etc.) self.keycombo_menu = self.console.details_build_keycombo_menu( self.console.details_send_key) self.widget("details-menu-send-key").set_submenu(self.keycombo_menu) def init_graphs(self): def _make_graph(): g = Sparkline() g.set_property("reversed", True) g.show() return g self.cpu_usage_graph = _make_graph() self.widget("overview-cpu-usage-align").add(self.cpu_usage_graph) self.memory_usage_graph = _make_graph() self.widget("overview-memory-usage-align").add(self.memory_usage_graph) self.disk_io_graph = _make_graph() self.disk_io_graph.set_property("filled", False) self.disk_io_graph.set_property("num_sets", 2) self.disk_io_graph.set_property("rgb", [x / 255.0 for x in [0x82, 0x00, 0x3B, 0x29, 0x5C, 0x45]]) self.widget("overview-disk-usage-align").add(self.disk_io_graph) self.network_traffic_graph = _make_graph() self.network_traffic_graph.set_property("filled", False) self.network_traffic_graph.set_property("num_sets", 2) self.network_traffic_graph.set_property("rgb", [x / 255.0 for x in [0x82, 0x00, 0x3B, 0x29, 0x5C, 0x45]]) self.widget("overview-network-traffic-align").add( self.network_traffic_graph) def init_details(self): # Hardware list # [ label, icon name, icon size, hw type, hw data/class] hw_list_model = Gtk.ListStore(str, str, int, int, object) self.widget("hw-list").set_model(hw_list_model) hwCol = Gtk.TreeViewColumn("Hardware") hwCol.set_spacing(6) hwCol.set_min_width(165) hw_txt = Gtk.CellRendererText() hw_img = Gtk.CellRendererPixbuf() hwCol.pack_start(hw_img, False) hwCol.pack_start(hw_txt, True) hwCol.add_attribute(hw_txt, 'text', HW_LIST_COL_LABEL) hwCol.add_attribute(hw_img, 'stock-size', HW_LIST_COL_ICON_SIZE) hwCol.add_attribute(hw_img, 'icon-name', HW_LIST_COL_ICON_NAME) self.widget("hw-list").append_column(hwCol) # Description text view desc = self.widget("overview-description") buf = Gtk.TextBuffer() buf.connect("changed", self.enable_apply, EDIT_DESC) desc.set_buffer(buf) arch = self.vm.get_arch() caps = self.vm.conn.caps # Machine type machtype_combo = self.widget("machine-type") machtype_model = Gtk.ListStore(str) machtype_combo.set_model(machtype_model) uiutil.init_combo_text_column(machtype_combo, 0) machtype_model.set_sort_column_id(0, Gtk.SortType.ASCENDING) machines = [] try: capsinfo = caps.guest_lookup( os_type=self.vm.get_abi_type(), arch=self.vm.get_arch(), typ=self.vm.get_hv_type(), machine=self.vm.get_machtype()) machines = capsinfo.machines[:] except: logging.exception("Error determining machine list") show_machine = (arch not in ["i686", "x86_64"] and not self.vm.is_management_domain()) uiutil.set_grid_row_visible(self.widget("machine-type-title"), show_machine) if show_machine: for machine in machines: if machine == "none": continue machtype_model.append([machine]) self.widget("machine-type").set_visible(self.is_customize_dialog) self.widget("machine-type-label").set_visible( not self.is_customize_dialog) # Firmware combo = self.widget("overview-firmware") # [label, path, is_sensitive] model = Gtk.ListStore(str, str, bool) combo.set_model(model) text = Gtk.CellRendererText() combo.pack_start(text, True) combo.add_attribute(text, "text", 0) combo.add_attribute(text, "sensitive", 2) domcaps = self.vm.get_domain_capabilities() uefipaths = [v.value for v in domcaps.os.loader.values] warn_icon = self.widget("overview-firmware-warn") hv_supports_uefi = domcaps.supports_uefi_xml() if not hv_supports_uefi: warn_icon.set_tooltip_text( _("Libvirt or hypervisor does not support UEFI.")) elif not uefipaths: warn_icon.set_tooltip_text( _("Libvirt did not detect any UEFI/OVMF firmware image " "installed on the host.")) model.append([domcaps.label_for_firmware_path(None), None, True]) if not uefipaths: model.append([_("UEFI not found"), None, False]) else: for path in uefipaths: model.append([domcaps.label_for_firmware_path(path), path, True]) combo.set_active(0) self.widget("overview-firmware-warn").set_visible( not (uefipaths and hv_supports_uefi) and self.is_customize_dialog) self.widget("overview-firmware").set_visible(self.is_customize_dialog) self.widget("overview-firmware-label").set_visible( not self.is_customize_dialog) show_firmware = ((self.conn.is_qemu() or self.conn.is_test_conn()) and domcaps.arch_can_uefi() and not self.vm.is_management_domain()) uiutil.set_grid_row_visible( self.widget("overview-firmware-title"), show_firmware) # Chipset combo = self.widget("overview-chipset") model = Gtk.ListStore(str, str) combo.set_model(model) model.append([_chipset_label_from_machine("pc"), "pc"]) if "q35" in machines: model.append([_chipset_label_from_machine("q35"), "q35"]) combo.set_active(0) def chipset_changed(*args): ignore = args combo = self.widget("overview-chipset") model = combo.get_model() show_warn = (combo.get_active() >= 0 and model[combo.get_active()][1] == "q35") uiutil.set_grid_row_visible( self.widget("overview-chipset-warn-box"), show_warn) combo.connect("changed", chipset_changed) self.widget("overview-chipset").set_visible(self.is_customize_dialog) self.widget("overview-chipset-label").set_visible( not self.is_customize_dialog) show_chipset = ((self.conn.is_qemu() or self.conn.is_test_conn()) and arch in ["i686", "x86_64"] and not self.vm.is_management_domain()) uiutil.set_grid_row_visible( self.widget("overview-chipset-title"), show_chipset) # Inspection page apps_list = self.widget("inspection-apps") apps_model = Gtk.ListStore(str, str, str) apps_list.set_model(apps_model) name_col = Gtk.TreeViewColumn(_("Name")) version_col = Gtk.TreeViewColumn(_("Version")) summary_col = Gtk.TreeViewColumn() apps_list.append_column(name_col) apps_list.append_column(version_col) apps_list.append_column(summary_col) name_text = Gtk.CellRendererText() name_col.pack_start(name_text, True) name_col.add_attribute(name_text, 'text', 0) name_col.set_sort_column_id(0) version_text = Gtk.CellRendererText() version_col.pack_start(version_text, True) version_col.add_attribute(version_text, 'text', 1) version_col.set_sort_column_id(1) summary_text = Gtk.CellRendererText() summary_col.pack_start(summary_text, True) summary_col.add_attribute(summary_text, 'text', 2) summary_col.set_sort_column_id(2) # VCPU Pinning list generate_cpuset = self.widget("config-vcpupin-generate") generate_warn = self.widget("config-vcpupin-generate-err") if not self.conn.caps.host.topology: generate_cpuset.set_sensitive(False) generate_warn.show() generate_warn.set_tooltip_text( _("Libvirt did not detect NUMA capabilities.")) # Boot device list boot_list = self.widget("config-boot-list") # [XML boot type, display name, icon name, enabled, can select] boot_list_model = Gtk.ListStore(str, str, str, bool, bool) boot_list.set_model(boot_list_model) chkCol = Gtk.TreeViewColumn() txtCol = Gtk.TreeViewColumn() boot_list.append_column(chkCol) boot_list.append_column(txtCol) chk = Gtk.CellRendererToggle() chk.connect("toggled", self.config_boot_toggled) chkCol.pack_start(chk, False) chkCol.add_attribute(chk, 'active', BOOT_ACTIVE) chkCol.add_attribute(chk, 'visible', BOOT_CAN_SELECT) icon = Gtk.CellRendererPixbuf() txtCol.pack_start(icon, False) txtCol.add_attribute(icon, 'icon-name', BOOT_ICON) text = Gtk.CellRendererText() txtCol.pack_start(text, True) txtCol.add_attribute(text, 'text', BOOT_LABEL) txtCol.add_attribute(text, 'sensitive', BOOT_ACTIVE) no_default = not self.is_customize_dialog try: cpu_names = caps.get_cpu_values(self.vm.get_arch()) except: cpu_names = [] logging.exception("Error populating CPU model list") # CPU model combo cpu_model = self.widget("cpu-model") def sep_func(model, it, ignore): return model[it][3] # [label, sortkey, idstring, is sep] model = Gtk.ListStore(str, str, str, bool) cpu_model.set_model(model) cpu_model.set_entry_text_column(0) cpu_model.set_row_separator_func(sep_func, None) model.set_sort_column_id(1, Gtk.SortType.ASCENDING) model.append([_("Application Default"), "1", "appdefault", False]) model.append([_("Hypervisor Default"), "2", virtinst.CPU.SPECIAL_MODE_HV_DEFAULT, False]) model.append([_("Clear CPU configuration"), "3", virtinst.CPU.SPECIAL_MODE_CLEAR, False]) model.append([None, None, None, True]) for name in cpu_names: model.append([name, name, name, False]) # Remove button tooltip self.widget("config-remove").set_tooltip_text(_remove_tooltip) # Disk cache combo disk_cache = self.widget("disk-cache") vmmAddHardware.build_disk_cache_combo(self.vm, disk_cache) # Disk io combo disk_io = self.widget("disk-io") vmmAddHardware.build_disk_io_combo(self.vm, disk_io) # Disk format combo format_list = self.widget("disk-format") vmmAddHardware.populate_disk_format_combo(self.vm, format_list, False) # Disk bus combo disk_bus = self.widget("disk-bus") vmmAddHardware.build_disk_bus_combo(self.vm, disk_bus) # Disk iotune expander if not (self.conn.is_qemu() or self.conn.is_test_conn()): self.widget("iotune-expander").set_visible(False) # Network model net_model = self.widget("network-model") vmmAddHardware.build_network_model_combo(self.vm, net_model) # Network mac self.widget("network-mac-label").set_visible( not self.is_customize_dialog) self.widget("network-mac-entry").set_visible(self.is_customize_dialog) # Sound model sound_dev = self.widget("sound-model") vmmAddHardware.build_sound_combo(self.vm, sound_dev, no_default=no_default) # Video model combo video_dev = self.widget("video-model") vmmAddHardware.build_video_combo(self.vm, video_dev, no_default=no_default) # Watchdog model combo combo = self.widget("watchdog-model") vmmAddHardware.build_watchdogmodel_combo(self.vm, combo, no_default=no_default) # Watchdog action combo combo = self.widget("watchdog-action") vmmAddHardware.build_watchdogaction_combo(self.vm, combo, no_default=no_default) # Smartcard mode sc_mode = self.widget("smartcard-mode") vmmAddHardware.build_smartcard_mode_combo(self.vm, sc_mode) # Controller model combo = self.widget("controller-model") model = Gtk.ListStore(str, str) combo.set_model(model) uiutil.init_combo_text_column(combo, 1) combo.set_active(-1) ########################## # Window state listeners # ########################## def window_resized(self, ignore, event): # Sometimes dimensions change when window isn't visible if not self.is_visible(): return self.vm.set_details_window_size(event.width, event.height) def popup_addhw_menu(self, widget, event): ignore = widget if event.button != 3: return devobj = self.get_hw_selection(HW_LIST_COL_DEVICE) if not devobj: return # force select the list entry before showing popup_menu path_tuple = widget.get_path_at_pos(int(event.x), int(event.y)) if path_tuple is None: return False path = path_tuple[0] _iter = widget.get_model().get_iter(path) widget.get_selection().select_iter(_iter) rmdev = self._addhwmenuitems["remove"] rmdev.set_visible(self.widget("config-remove").get_visible()) rmdev.set_sensitive(self.widget("config-remove").get_sensitive()) self.addhwmenu.popup(None, None, None, None, 0, event.time) def control_fullscreen(self, src): menu = self.widget("details-menu-view-fullscreen") if src.get_active() != menu.get_active(): menu.set_active(src.get_active()) def toggle_toolbar(self, src): if self.is_customize_dialog: return active = src.get_active() self.config.set_details_show_toolbar(active) if (active and not self.widget("details-menu-view-fullscreen").get_active()): self.widget("toolbar-box").show() else: self.widget("toolbar-box").hide() def get_boot_selection(self): return uiutil.get_list_selection(self.widget("config-boot-list"), None) def set_hw_selection(self, page, disable_apply=True): if disable_apply: self.disable_apply() uiutil.select_list_row_by_number(self.widget("hw-list"), page) def get_hw_row(self): return uiutil.get_list_selection(self.widget("hw-list"), None) def get_hw_selection(self, field): row = self.get_hw_row() if not row: return None return row[field] def force_get_hw_pagetype(self, page=None): if page: return page page = self.get_hw_selection(HW_LIST_COL_TYPE) if page is None: page = HW_LIST_TYPE_GENERAL self.set_hw_selection(0) return page def has_unapplied_changes(self, row): if not row: return False if not self.widget("config-apply").get_sensitive(): return False if not self.err.chkbox_helper( self.config.get_confirm_unapplied, self.config.set_confirm_unapplied, text1=(_("There are unapplied changes. Would you like to apply " "them now?")), chktext=_("Don't warn me again."), default=False): return False return not self.config_apply(row=row) def hw_changed(self, ignore): newrow = self.get_hw_row() model = self.widget("hw-list").get_model() if not newrow or newrow[HW_LIST_COL_DEVICE] == self.oldhwkey: return oldhwrow = None for row in model: if row[HW_LIST_COL_DEVICE] == self.oldhwkey: oldhwrow = row break if self.has_unapplied_changes(oldhwrow): # Unapplied changes, and syncing them failed pageidx = 0 for idx in range(len(model)): if model[idx][HW_LIST_COL_DEVICE] == self.oldhwkey: pageidx = idx break self.set_hw_selection(pageidx, disable_apply=False) else: self.oldhwkey = newrow[HW_LIST_COL_DEVICE] self.hw_selected() def hw_selected(self, page=None): pagetype = self.force_get_hw_pagetype(page) self.widget("config-remove").set_sensitive(True) self.widget("hw-panel").set_sensitive(True) self.widget("hw-panel").show() try: if pagetype == HW_LIST_TYPE_GENERAL: self.refresh_overview_page() elif pagetype == HW_LIST_TYPE_INSPECTION: self.refresh_inspection_page() elif pagetype == HW_LIST_TYPE_STATS: self.refresh_stats_page() elif pagetype == HW_LIST_TYPE_CPU: self.refresh_config_cpu() elif pagetype == HW_LIST_TYPE_MEMORY: self.refresh_config_memory() elif pagetype == HW_LIST_TYPE_BOOT: self.refresh_boot_page() elif pagetype == HW_LIST_TYPE_DISK: self.refresh_disk_page() elif pagetype == HW_LIST_TYPE_NIC: self.refresh_network_page() elif pagetype == HW_LIST_TYPE_INPUT: self.refresh_input_page() elif pagetype == HW_LIST_TYPE_GRAPHICS: self.refresh_graphics_page() elif pagetype == HW_LIST_TYPE_SOUND: self.refresh_sound_page() elif pagetype == HW_LIST_TYPE_CHAR: self.refresh_char_page() elif pagetype == HW_LIST_TYPE_HOSTDEV: self.refresh_hostdev_page() elif pagetype == HW_LIST_TYPE_VIDEO: self.refresh_video_page() elif pagetype == HW_LIST_TYPE_WATCHDOG: self.refresh_watchdog_page() elif pagetype == HW_LIST_TYPE_CONTROLLER: self.refresh_controller_page() elif pagetype == HW_LIST_TYPE_FILESYSTEM: self.refresh_filesystem_page() elif pagetype == HW_LIST_TYPE_SMARTCARD: self.refresh_smartcard_page() elif pagetype == HW_LIST_TYPE_REDIRDEV: self.refresh_redir_page() elif pagetype == HW_LIST_TYPE_TPM: self.refresh_tpm_page() elif pagetype == HW_LIST_TYPE_RNG: self.refresh_rng_page() elif pagetype == HW_LIST_TYPE_PANIC: self.refresh_panic_page() else: pagetype = -1 except Exception, e: self.err.show_err(_("Error refreshing hardware page: %s") % str(e)) # Don't return, we want the rest of the bits to run regardless self.disable_apply() rem = pagetype in remove_pages self.widget("config-remove").set_visible(rem) self.widget("hw-panel").set_current_page(pagetype) def details_console_changed(self, src): if self.ignoreDetails: return if not src.get_active(): return is_details = (src == self.widget("control-vm-details") or src == self.widget("details-menu-view-details")) is_snapshot = (src == self.widget("control-snapshots") or src == self.widget("details-menu-view-snapshots")) pages = self.widget("details-pages") 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(DETAILS_PAGE_DETAILS) elif is_snapshot: self.snapshots.show_page() pages.set_current_page(DETAILS_PAGE_SNAPSHOTS) else: pages.set_current_page(DETAILS_PAGE_CONSOLE) def sync_details_console_view(self, newpage): details = self.widget("control-vm-details") details_menu = self.widget("details-menu-view-details") console = self.widget("control-vm-console") console_menu = self.widget("details-menu-view-console") snapshot = self.widget("control-snapshots") snapshot_menu = self.widget("details-menu-view-snapshots") is_details = newpage == DETAILS_PAGE_DETAILS is_snapshot = newpage == DETAILS_PAGE_SNAPSHOTS is_console = not is_details and not is_snapshot try: self.ignoreDetails = True details.set_active(is_details) details_menu.set_active(is_details) snapshot.set_active(is_snapshot) snapshot_menu.set_active(is_snapshot) console.set_active(is_console) console_menu.set_active(is_console) finally: self.ignoreDetails = False def switch_page(self, notebook=None, ignore2=None, newpage=None): for i in range(notebook.get_n_pages()): w = notebook.get_nth_page(i) w.set_visible(i == newpage) self.page_refresh(newpage) self.sync_details_console_view(newpage) self.console.details_refresh_can_fullscreen() def change_run_text(self, can_restore): if can_restore: text = _("_Restore") else: text = _("_Run") strip_text = text.replace("_", "") self.widget("details-vm-menu").get_submenu().change_run_text(text) self.widget("control-run").set_label(strip_text) def refresh_vm_state(self, ignore=None): vm = self.vm status = self.vm.status() self.widget("details-menu-view-toolbar").set_active( self.config.get_details_show_toolbar()) self.toggle_toolbar(self.widget("details-menu-view-toolbar")) active = vm.is_active() run = vm.is_runable() stop = vm.is_stoppable() paused = vm.is_paused() ro = vm.is_read_only() if vm.managedsave_supported: self.change_run_text(vm.has_managed_save()) self.widget("control-run").set_sensitive(run) self.widget("control-shutdown").set_sensitive(stop) self.widget("control-shutdown").get_menu().update_widget_states(vm) self.widget("control-pause").set_sensitive(stop) self.widget("details-vm-menu").get_submenu().update_widget_states(vm) self.set_pause_state(paused) self.widget("overview-name").set_editable(not active) self.widget("config-vcpus").set_sensitive(not ro) self.widget("config-vcpupin").set_sensitive(not ro) self.widget("config-memory").set_sensitive(not ro) self.widget("config-maxmem").set_sensitive(not ro) # Disable send key menu entries for offline VM send_key = self.widget("details-menu-send-key") for c in send_key.get_submenu().get_children(): c.set_sensitive(not (run or paused)) self.console.details_update_widget_states() if not run: self.activate_default_console_page() reason = self.vm.run_status_reason() if reason: status = "%s (%s)" % (self.vm.run_status(), reason) else: status = self.vm.run_status() self.widget("overview-status-text").set_text(status) self.widget("overview-status-icon").set_from_icon_name( self.vm.run_status_icon_name(), Gtk.IconSize.BUTTON) details = self.widget("details-pages") self.page_refresh(details.get_current_page()) errmsg = self.vm.snapshots_supported() cansnap = not bool(errmsg) self.widget("control-snapshots").set_sensitive(cansnap) self.widget("details-menu-view-snapshots").set_sensitive(cansnap) tooltip = _("Manage VM snapshots") if not cansnap: tooltip += "\n" + errmsg self.widget("control-snapshots").set_tooltip_text(tooltip) ############################# # External action listeners # ############################# def view_manager(self, src_ignore): self.emit("action-view-manager") def exit_app(self, src_ignore): self.emit("action-exit-app") def activate_default_console_page(self): pages = self.widget("details-pages") # console.activate_default_console_page() will as a side effect # switch to DETAILS_PAGE_CONSOLE. However this code path is triggered # when the user runs a VM while they are focused on the details page, # and we don't want to switch pages out from under them. origpage = pages.get_current_page() self.console.details_activate_default_console_page() pages.set_current_page(origpage) # activate_* are called from engine.py via CLI options def activate_default_page(self): pages = self.widget("details-pages") 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(DETAILS_PAGE_CONSOLE) def activate_performance_page(self): self.widget("details-pages").set_current_page(DETAILS_PAGE_DETAILS) index = 0 model = self.widget("hw-list").get_model() for i in range(len(model)): if model[i][HW_LIST_COL_TYPE] == HW_LIST_TYPE_STATS: index = i break self.set_hw_selection(index) def activate_config_page(self): self.widget("details-pages").set_current_page(DETAILS_PAGE_DETAILS) def add_hardware(self, src_ignore): try: if self.addhw is None: self.addhw = vmmAddHardware(self.vm, self.is_customize_dialog) self.addhw.show(self.topwin) except Exception, e: self.err.show_err((_("Error launching hardware dialog: %s") % str(e))) def remove_xml_dev(self, src_ignore): devobj = self.get_hw_selection(HW_LIST_COL_DEVICE) if not devobj: return self.remove_device(devobj) def set_pause_state(self, paused): # Set pause widget states try: self.ignorePause = True self.widget("control-pause").set_property("active", paused) finally: self.ignorePause = False def control_vm_pause(self, src): if self.ignorePause: return # Let state handler listener change things if nec. self.set_pause_state(not src.get_active()) if not self.vm.is_paused(): self.emit("action-suspend-domain", self.vm.conn.get_uri(), self.vm.get_connkey()) else: self.emit("action-resume-domain", self.vm.conn.get_uri(), self.vm.get_connkey()) def control_vm_menu(self, src_ignore): can_usb = bool(self.console.details_viewer_has_usb_redirection() and self.vm.has_spicevmc_type_redirdev()) self.widget("details-menu-usb-redirection").set_sensitive(can_usb) def control_vm_run(self, src_ignore): self.emit("action-run-domain", self.vm.conn.get_uri(), self.vm.get_connkey()) def control_vm_shutdown(self, src_ignore): self.emit("action-shutdown-domain", self.vm.conn.get_uri(), self.vm.get_connkey()) def control_vm_reboot(self, src_ignore): self.emit("action-reboot-domain", self.vm.conn.get_uri(), self.vm.get_connkey()) def control_vm_save(self, src_ignore): self.emit("action-save-domain", self.vm.conn.get_uri(), self.vm.get_connkey()) def control_vm_reset(self, src_ignore): self.emit("action-reset-domain", self.vm.conn.get_uri(), self.vm.get_connkey()) def control_vm_destroy(self, src_ignore): self.emit("action-destroy-domain", self.vm.conn.get_uri(), self.vm.get_connkey()) def control_vm_clone(self, src_ignore): self.emit("action-clone-domain", self.vm.conn.get_uri(), self.vm.get_connkey()) def control_vm_migrate(self, src_ignore): self.emit("action-migrate-domain", self.vm.conn.get_uri(), self.vm.get_connkey()) def control_vm_delete(self, src_ignore): self.emit("action-delete-domain", self.vm.conn.get_uri(), self.vm.get_connkey()) def control_vm_screenshot(self, src): ignore = src try: return self._take_screenshot() except Exception, e: self.err.show_err(_("Error taking screenshot: %s") % str(e)) def control_vm_usb_redirection(self, src): ignore = src spice_usbdev_dialog = self.err spice_usbdev_widget = self.console.details_viewer_get_usb_widget() if not spice_usbdev_widget: self.err.show_err(_("Error initializing spice USB device widget")) return spice_usbdev_widget.show() spice_usbdev_dialog.show_info(_("Select USB devices for redirection"), widget=spice_usbdev_widget) def _take_screenshot(self): image = self.console.details_viewer_get_pixbuf() metadata = { 'tEXt::Hypervisor URI': self.vm.conn.get_uri(), 'tEXt::Domain Name': self.vm.get_name(), 'tEXt::Domain UUID': self.vm.get_uuid(), 'tEXt::Generator App': self.config.get_appname(), 'tEXt::Generator Version': self.config.get_appversion(), } ret = image.save_to_bufferv('png', metadata.keys(), metadata.values()) # On Fedora 19, ret is (bool, str) # Someday the bindings might be fixed to just return the str, try # and future proof it a bit if type(ret) is tuple and len(ret) >= 2: ret = ret[1] import datetime now = str(datetime.datetime.now()).split(".")[0].replace(" ", "_") default = "Screenshot_%s_%s.png" % (self.vm.get_name(), now) path = self.err.browse_local( self.vm.conn, _("Save Virtual Machine Screenshot"), _type=("png", "PNG files"), dialog_type=Gtk.FileChooserAction.SAVE, browse_reason=self.config.CONFIG_DIR_SCREENSHOT, default_name=default) if not path: logging.debug("No screenshot path given, skipping save.") return filename = path if not filename.endswith(".png"): filename += ".png" file(filename, "wb").write(ret) ############################ # Details/Hardware getters # ############################ def get_config_boot_order(self): boot_model = self.widget("config-boot-list").get_model() devs = [] for row in boot_model: if row[BOOT_ACTIVE]: devs.append(row[BOOT_KEY]) return devs def get_config_cpu_model(self): cpu_list = self.widget("cpu-model") text = cpu_list.get_child().get_text() if self.widget("cpu-copy-host").get_active(): return virtinst.CPU.SPECIAL_MODE_HOST_MODEL key = None for row in cpu_list.get_model(): if text == row[0]: key = row[2] break if not key: return text if key == "appdefault": return self.config.get_default_cpu_setting(for_cpu=True) return key ############################## # Details/Hardware listeners # ############################## def _browse_file(self, callback, is_media=False): if is_media: reason = self.config.CONFIG_DIR_ISO_MEDIA else: reason = self.config.CONFIG_DIR_IMAGE if self.storage_browser is None: self.storage_browser = vmmStorageBrowser(self.conn) self.storage_browser.set_finish_cb(callback) self.storage_browser.set_browse_reason(reason) self.storage_browser.show(self.topwin) def boot_kernel_toggled(self, src): self.widget("boot-kernel-box").set_sensitive(src.get_active()) self.enable_apply(EDIT_KERNEL) def browse_kernel(self, src_ignore): def cb(ignore, path): self.widget("boot-kernel").set_text(path) self._browse_file(cb) def browse_initrd(self, src_ignore): def cb(ignore, path): self.widget("boot-initrd").set_text(path) self._browse_file(cb) def browse_dtb(self, src_ignore): def cb(ignore, path): self.widget("boot-dtb").set_text(path) self._browse_file(cb) def disable_apply(self): self.active_edits = [] self.widget("config-apply").set_sensitive(False) self.widget("config-cancel").set_sensitive(False) def enable_apply(self, *arglist): edittype = arglist[-1] self.widget("config-apply").set_sensitive(True) self.widget("config-cancel").set_sensitive(True) if edittype not in self.active_edits: self.active_edits.append(edittype) # Idmap def config_idmap_enable(self, src): do_enable = src.get_active() self.widget("idmap-spin-grid").set_sensitive(do_enable) self.config_idmap_changed() def config_idmap_changed(self, ignore=None): self.enable_apply(EDIT_IDMAP) # Memory def config_get_maxmem(self): return uiutil.spin_get_helper(self.widget("config-maxmem")) def config_get_memory(self): return uiutil.spin_get_helper(self.widget("config-memory")) def config_maxmem_changed(self, src_ignore): self.enable_apply(EDIT_MEM) def config_memory_changed(self, src_ignore): self.enable_apply(EDIT_MEM) maxadj = self.widget("config-maxmem") mem = self.config_get_memory() if maxadj.get_value() < mem: maxadj.set_value(mem) ignore, upper = maxadj.get_range() maxadj.set_range(mem, upper) def generate_cpuset(self): mem = int(self.vm.get_memory()) / 1024 return virtinst.DomainNumatune.generate_cpuset(self.conn.get_backend(), mem) # VCPUS def config_get_vcpus(self): return uiutil.spin_get_helper(self.widget("config-vcpus")) def config_get_maxvcpus(self): return uiutil.spin_get_helper(self.widget("config-maxvcpus")) def config_vcpupin_generate(self, ignore): try: pinstr = self.generate_cpuset() except Exception, e: return self.err.val_err( _("Error generating CPU configuration"), e) self.widget("config-vcpupin").set_text("") self.widget("config-vcpupin").set_text(pinstr) def config_vcpus_changed(self, src): self.enable_apply(EDIT_VCPUS) conn = self.vm.conn host_active_count = conn.host_active_processor_count() cur = self.config_get_vcpus() # Warn about overcommit warn = bool(cur > host_active_count) self.widget("config-vcpus-warn-box").set_visible(warn) maxadj = self.widget("config-maxvcpus") maxval = self.config_get_maxvcpus() if maxval < cur: if maxadj.get_sensitive(): maxadj.set_value(cur) else: src.set_value(maxval) cur = maxval ignore, upper = maxadj.get_range() maxadj.set_range(cur, upper) def config_maxvcpus_changed(self, ignore): if self.widget("config-maxvcpus").get_sensitive(): self.config_cpu_topology_changed() # As this callback can be triggered by other events, set EDIT_MAXVCPUS # only when the value is changed. if self.config_get_maxvcpus() != self.vm.vcpu_max_count(): self.enable_apply(EDIT_MAXVCPUS) def on_cpu_copy_host_clicked(self, src): uiutil.set_grid_row_visible( self.widget("cpu-model"), not src.get_active()) self.enable_apply(EDIT_CPU) def config_cpu_model_changed(self, ignore): # Warn about hyper-threading setting cpu_model = self.get_config_cpu_model() threads = self.widget("cpu-threads").get_value() warn_ht = _warn_cpu_thread_topo(threads, cpu_model) self.widget("config-topology-warn-box").set_visible(warn_ht) self.enable_apply(EDIT_CPU) def config_cpu_topology_changed(self, ignore=None): manual_top = self.widget("cpu-topology-table").is_sensitive() self.widget("config-maxvcpus").set_sensitive(not manual_top) if manual_top: cores = uiutil.spin_get_helper(self.widget("cpu-cores")) or 1 sockets = uiutil.spin_get_helper(self.widget("cpu-sockets")) or 1 threads = uiutil.spin_get_helper(self.widget("cpu-threads")) or 1 total = cores * sockets * threads if uiutil.spin_get_helper(self.widget("config-vcpus")) > total: self.widget("config-vcpus").set_value(total) self.widget("config-maxvcpus").set_value(total) # Warn about hyper-threading setting cpu_model = self.get_config_cpu_model() warn_ht = _warn_cpu_thread_topo(threads, cpu_model) self.widget("config-topology-warn-box").set_visible(warn_ht) else: maxvcpus = uiutil.spin_get_helper(self.widget("config-maxvcpus")) self.widget("cpu-sockets").set_value(maxvcpus or 1) self.widget("cpu-cores").set_value(1) self.widget("cpu-threads").set_value(1) self.enable_apply(EDIT_TOPOLOGY) def config_cpu_topology_enable(self, src): do_enable = src.get_active() self.widget("cpu-topology-table").set_sensitive(do_enable) self.config_cpu_topology_changed() # Boot device / Autostart def config_bootdev_selected(self, ignore=None): boot_row = self.get_boot_selection() boot_selection = boot_row and boot_row[BOOT_KEY] boot_devs = self.get_config_boot_order() up_widget = self.widget("config-boot-moveup") down_widget = self.widget("config-boot-movedown") down_widget.set_sensitive(bool(boot_devs and boot_selection and boot_selection in boot_devs and boot_selection != boot_devs[-1])) up_widget.set_sensitive(bool(boot_devs and boot_selection and boot_selection in boot_devs and boot_selection != boot_devs[0])) def config_boot_toggled(self, ignore, index): model = self.widget("config-boot-list").get_model() row = model[index] row[BOOT_ACTIVE] = not row[BOOT_ACTIVE] self.config_bootdev_selected() self.enable_apply(EDIT_BOOTORDER) def config_boot_move(self, src, move_up): ignore = src row = self.get_boot_selection() if not row: return row_key = row[BOOT_KEY] boot_order = self.get_config_boot_order() key_idx = boot_order.index(row_key) if move_up: new_idx = key_idx - 1 else: new_idx = key_idx + 1 if new_idx < 0 or new_idx >= len(boot_order): # Somehow we went out of bounds return boot_list = self.widget("config-boot-list") model = boot_list.get_model() prev_row = None for row in model: if prev_row and prev_row[BOOT_KEY] == row_key: model.swap(prev_row.iter, row.iter) break if row[BOOT_KEY] == row_key and prev_row and move_up: model.swap(prev_row.iter, row.iter) break prev_row = row boot_list.get_selection().emit("changed") self.enable_apply(EDIT_BOOTORDER) def disk_format_changed(self, ignore): self.widget("disk-format-warn").show() self.enable_apply(EDIT_DISK_FORMAT) # IO Tuning def iotune_changed(self, ignore): iotune_rbs = int(self.get_text("disk-iotune-rbs") or 0) iotune_ris = int(self.get_text("disk-iotune-ris") or 0) iotune_tbs = int(self.get_text("disk-iotune-tbs") or 0) iotune_tis = int(self.get_text("disk-iotune-tis") or 0) iotune_wbs = int(self.get_text("disk-iotune-wbs") or 0) iotune_wis = int(self.get_text("disk-iotune-wis") or 0) # libvirt doesn't support having read/write settings along side total # settings, so disable the widgets accordingly. have_rw_bytes = (iotune_rbs > 0 or iotune_wbs > 0) have_t_bytes = (not have_rw_bytes and iotune_tbs > 0) self.widget("disk-iotune-rbs").set_sensitive(have_rw_bytes or not have_t_bytes) self.widget("disk-iotune-wbs").set_sensitive(have_rw_bytes or not have_t_bytes) self.widget("disk-iotune-tbs").set_sensitive(have_t_bytes or not have_rw_bytes) if have_rw_bytes: self.widget("disk-iotune-tbs").set_value(0) elif have_t_bytes: self.widget("disk-iotune-rbs").set_value(0) self.widget("disk-iotune-wbs").set_value(0) have_rw_iops = (iotune_ris > 0 or iotune_wis > 0) have_t_iops = (not have_rw_iops and iotune_tis > 0) self.widget("disk-iotune-ris").set_sensitive(have_rw_iops or not have_t_iops) self.widget("disk-iotune-wis").set_sensitive(have_rw_iops or not have_t_iops) self.widget("disk-iotune-tis").set_sensitive(have_t_iops or not have_rw_iops) if have_rw_iops: self.widget("disk-iotune-tis").set_value(0) elif have_t_iops: self.widget("disk-iotune-ris").set_value(0) self.widget("disk-iotune-wis").set_value(0) self.enable_apply(EDIT_DISK_IOTUNE) # CDROM Eject/Connect def toggle_storage_media(self, src_ignore): disk = self.get_hw_selection(HW_LIST_COL_DEVICE) if not disk: return curpath = disk.path devtype = disk.device try: if curpath: # Disconnect cdrom self.change_storage_media(disk, None) return except Exception, e: self.err.show_err((_("Error disconnecting media: %s") % e)) return try: def change_cdrom_wrapper(src_ignore, disk, newpath): return self.change_storage_media(disk, newpath) # Launch 'Choose CD' dialog if self.media_choosers[devtype] is None: ret = vmmChooseCD(self.vm, disk) ret.connect("cdrom-chosen", change_cdrom_wrapper) self.media_choosers[devtype] = ret dialog = self.media_choosers[devtype] dialog.disk = disk dialog.show(self.topwin) except Exception, e: self.err.show_err((_("Error launching media dialog: %s") % e)) return ################################################## # Details/Hardware config changes (apply button) # ################################################## def config_cancel(self, ignore=None): # Remove current changes and deactive 'apply' button self.hw_selected() def config_apply(self, ignore=None, row=None): pagetype = None devobj = None if not row: row = self.get_hw_row() if row: pagetype = row[HW_LIST_COL_TYPE] devobj = row[HW_LIST_COL_DEVICE] key = devobj ret = False try: if pagetype is HW_LIST_TYPE_GENERAL: ret = self.config_overview_apply() elif pagetype is HW_LIST_TYPE_CPU: ret = self.config_vcpus_apply() elif pagetype is HW_LIST_TYPE_MEMORY: ret = self.config_memory_apply() elif pagetype is HW_LIST_TYPE_BOOT: ret = self.config_boot_options_apply() elif pagetype is HW_LIST_TYPE_DISK: ret = self.config_disk_apply(key) elif pagetype is HW_LIST_TYPE_NIC: ret = self.config_network_apply(key) elif pagetype is HW_LIST_TYPE_GRAPHICS: ret = self.config_graphics_apply(key) elif pagetype is HW_LIST_TYPE_SOUND: ret = self.config_sound_apply(key) elif pagetype is HW_LIST_TYPE_VIDEO: ret = self.config_video_apply(key) elif pagetype is HW_LIST_TYPE_WATCHDOG: ret = self.config_watchdog_apply(key) elif pagetype is HW_LIST_TYPE_SMARTCARD: ret = self.config_smartcard_apply(key) elif pagetype is HW_LIST_TYPE_CONTROLLER: ret = self.config_controller_apply(key) elif pagetype is HW_LIST_TYPE_FILESYSTEM: ret = self.config_filesystem_apply(key) elif pagetype is HW_LIST_TYPE_HOSTDEV: ret = self.config_hostdev_apply(key) else: ret = False except Exception, e: return self.err.show_err(_("Error apply changes: %s") % e) if ret is not False: self.disable_apply() return True def get_text(self, widgetname, strip=True, checksens=False): widget = self.widget(widgetname) if (checksens and (not widget.is_sensitive() or not widget.is_visible())): return "" ret = widget.get_text() if strip: ret = ret.strip() return ret def edited(self, pagetype): return pagetype in self.active_edits def config_overview_apply(self): kwargs = {} hotplug_args = {} if self.edited(EDIT_TITLE): kwargs["title"] = self.widget("overview-title").get_text() hotplug_args["title"] = kwargs["title"] if self.edited(EDIT_FIRMWARE): kwargs["loader"] = uiutil.get_list_selection( self.widget("overview-firmware"), 1) if self.edited(EDIT_MACHTYPE): if self.widget("overview-chipset").is_visible(): kwargs["machine"] = uiutil.get_list_selection( self.widget("overview-chipset"), 1) else: kwargs["machine"] = uiutil.get_combo_entry( self.widget("machine-type")) if self.edited(EDIT_DESC): desc_widget = self.widget("overview-description") kwargs["description"] = ( desc_widget.get_buffer().get_property("text") or "") hotplug_args["description"] = kwargs["description"] if self.edited(EDIT_IDMAP): enable_idmap = self.widget("config-idmap-checkbutton").get_active() if enable_idmap: uid_target = self.widget("uid-target").get_text().strip() uid_count = self.widget("uid-count").get_text().strip() gid_target = self.widget("gid-target").get_text().strip() gid_count = self.widget("gid-count").get_text().strip() idmap_list = [uid_target, uid_count, gid_target, gid_count] else: idmap_list = None kwargs["idmap_list"] = idmap_list # This needs to be last if self.edited(EDIT_NAME): # Renaming is pretty convoluted, so do it here synchronously self.vm.define_name(self.widget("overview-name").get_text()) if not kwargs and not hotplug_args: # Saves some useless redefine attempts return return vmmAddHardware.change_config_helper(self.vm.define_overview, kwargs, self.vm, self.err, hotplug_args=hotplug_args) def config_vcpus_apply(self): kwargs = {} hotplug_args = {} if self.edited(EDIT_VCPUS): kwargs["vcpus"] = self.config_get_vcpus() hotplug_args["vcpus"] = kwargs["vcpus"] if self.edited(EDIT_MAXVCPUS): kwargs["maxvcpus"] = self.config_get_maxvcpus() if self.edited(EDIT_CPUSET): kwargs["cpuset"] = self.get_text("config-vcpupin") if self.edited(EDIT_CPU): kwargs["model"] = self.get_config_cpu_model() if self.edited(EDIT_TOPOLOGY): do_top = self.widget("cpu-topology-enable").get_active() kwargs["sockets"] = self.widget("cpu-sockets").get_value() kwargs["cores"] = self.widget("cpu-cores").get_value() kwargs["threads"] = self.widget("cpu-threads").get_value() if not do_top: kwargs["sockets"] = None kwargs["cores"] = None kwargs["threads"] = None return vmmAddHardware.change_config_helper(self.vm.define_cpu, kwargs, self.vm, self.err, hotplug_args=hotplug_args) def config_memory_apply(self): kwargs = {} hotplug_args = {} if self.edited(EDIT_MEM): curmem = None maxmem = self.config_get_maxmem() if self.widget("config-memory").get_sensitive(): curmem = self.config_get_memory() if curmem: curmem = int(curmem) * 1024 if maxmem: maxmem = int(maxmem) * 1024 kwargs["memory"] = curmem kwargs["maxmem"] = maxmem hotplug_args["memory"] = kwargs["memory"] hotplug_args["maxmem"] = kwargs["maxmem"] return vmmAddHardware.change_config_helper(self.vm.define_memory, kwargs, self.vm, self.err, hotplug_args=hotplug_args) def config_boot_options_apply(self): kwargs = {} if self.edited(EDIT_AUTOSTART): auto = self.widget("config-autostart") try: self.vm.set_autostart(auto.get_active()) except Exception, e: self.err.show_err( (_("Error changing autostart value: %s") % str(e))) return False if self.edited(EDIT_BOOTORDER): kwargs["boot_order"] = self.get_config_boot_order() if self.edited(EDIT_BOOTMENU): kwargs["boot_menu"] = self.widget("boot-menu").get_active() if self.edited(EDIT_KERNEL): kwargs["kernel"] = self.get_text("boot-kernel", checksens=True) kwargs["initrd"] = self.get_text("boot-initrd", checksens=True) kwargs["dtb"] = self.get_text("boot-dtb", checksens=True) kwargs["kernel_args"] = self.get_text("boot-kernel-args", checksens=True) if kwargs["initrd"] and not kwargs["kernel"]: return self.err.val_err( _("Cannot set initrd without specifying a kernel path")) if kwargs["kernel_args"] and not kwargs["kernel"]: return self.err.val_err( _("Cannot set kernel arguments without specifying a kernel path")) if self.edited(EDIT_INIT): kwargs["init"] = self.get_text("boot-init-path") kwargs["initargs"] = self.get_text("boot-init-args") or "" if not kwargs["init"]: return self.err.val_err(_("An init path must be specified")) return vmmAddHardware.change_config_helper(self.vm.define_boot, kwargs, self.vm, self.err) # defining def change_storage_media(self, devobj, newpath): kwargs = {"path": newpath} return vmmAddHardware.change_config_helper(self.vm.define_disk, kwargs, self.vm, self.err, devobj=devobj) def config_disk_apply(self, devobj): kwargs = {} if self.edited(EDIT_DISK_RO): kwargs["readonly"] = self.widget("disk-readonly").get_active() if self.edited(EDIT_DISK_SHARE): kwargs["shareable"] = self.widget("disk-shareable").get_active() if self.edited(EDIT_DISK_REMOVABLE): kwargs["removable"] = bool( self.widget("disk-removable").get_active()) if self.edited(EDIT_DISK_CACHE): kwargs["cache"] = uiutil.get_combo_entry(self.widget("disk-cache")) if self.edited(EDIT_DISK_IO): kwargs["io"] = uiutil.get_combo_entry(self.widget("disk-io")) if self.edited(EDIT_DISK_FORMAT): kwargs["driver_type"] = uiutil.get_combo_entry( self.widget("disk-format")) if self.edited(EDIT_DISK_SERIAL): kwargs["serial"] = self.get_text("disk-serial") if self.edited(EDIT_DISK_SGIO): sgio = uiutil.get_combo_entry(self.widget("disk-sgio")) kwargs["sgio"] = sgio if self.edited(EDIT_DISK_IOTUNE): kwargs["iotune_rbs"] = int( self.widget("disk-iotune-rbs").get_value() * 1024) kwargs["iotune_ris"] = int( self.widget("disk-iotune-ris").get_value()) kwargs["iotune_tbs"] = int( self.widget("disk-iotune-tbs").get_value() * 1024) kwargs["iotune_tis"] = int( self.widget("disk-iotune-tis").get_value()) kwargs["iotune_wbs"] = int( self.widget("disk-iotune-wbs").get_value() * 1024) kwargs["iotune_wis"] = int( self.widget("disk-iotune-wis").get_value()) if self.edited(EDIT_DISK_BUS): bus = uiutil.get_combo_entry(self.widget("disk-bus")) addr = None if bus == "spapr-vscsi": bus = "scsi" addr = "spapr-vio" kwargs["bus"] = bus kwargs["addrstr"] = addr return vmmAddHardware.change_config_helper(self.vm.define_disk, kwargs, self.vm, self.err, devobj=devobj) def config_sound_apply(self, devobj): kwargs = {} if self.edited(EDIT_SOUND_MODEL): model = uiutil.get_combo_entry(self.widget("sound-model")) if model: kwargs["model"] = model return vmmAddHardware.change_config_helper(self.vm.define_sound, kwargs, self.vm, self.err, devobj=devobj) def config_smartcard_apply(self, devobj): kwargs = {} if self.edited(EDIT_SMARTCARD_MODE): model = uiutil.get_combo_entry(self.widget("smartcard-mode")) if model: kwargs["model"] = model return vmmAddHardware.change_config_helper(self.vm.define_smartcard, kwargs, self.vm, self.err, devobj=devobj) def config_network_apply(self, devobj): kwargs = {} if self.edited(EDIT_NET_MODEL): model = uiutil.get_combo_entry(self.widget("network-model")) addrstr = None if model == "spapr-vlan": addrstr = "spapr-vio" kwargs["model"] = model kwargs["addrstr"] = addrstr if self.edited(EDIT_NET_SOURCE): (kwargs["ntype"], kwargs["source"], kwargs["mode"], kwargs["portgroup"]) = ( self.netlist.get_network_selection()) if self.edited(EDIT_NET_VPORT): (kwargs["vtype"], kwargs["managerid"], kwargs["typeid"], kwargs["typeidversion"], kwargs["instanceid"]) = self.netlist.get_vport() if self.edited(EDIT_NET_MAC): kwargs["macaddr"] = self.widget("network-mac-entry").get_text() return vmmAddHardware.change_config_helper(self.vm.define_network, kwargs, self.vm, self.err, devobj=devobj) def config_graphics_apply(self, devobj): (gtype, port, tlsport, addr, passwd, keymap) = self.gfxdetails.get_values() kwargs = {} if self.edited(EDIT_GFX_PASSWD): kwargs["passwd"] = passwd if self.edited(EDIT_GFX_ADDRESS): kwargs["listen"] = addr if self.edited(EDIT_GFX_KEYMAP): kwargs["keymap"] = keymap if self.edited(EDIT_GFX_PORT): kwargs["port"] = port if self.edited(EDIT_GFX_TLSPORT): kwargs["tlsport"] = tlsport if self.edited(EDIT_GFX_TYPE): kwargs["gtype"] = gtype return vmmAddHardware.change_config_helper(self.vm.define_graphics, kwargs, self.vm, self.err, devobj=devobj) def config_video_apply(self, devobj): kwargs = {} if self.edited(EDIT_VIDEO_MODEL): model = uiutil.get_combo_entry(self.widget("video-model")) if model: kwargs["model"] = model return vmmAddHardware.change_config_helper(self.vm.define_video, kwargs, self.vm, self.err, devobj=devobj) def config_controller_apply(self, devobj): kwargs = {} if self.edited(EDIT_CONTROLLER_MODEL): model = uiutil.get_combo_entry(self.widget("controller-model")) if model: kwargs["model"] = model return vmmAddHardware.change_config_helper(self.vm.define_controller, kwargs, self.vm, self.err, devobj=devobj) def config_watchdog_apply(self, devobj): kwargs = {} if self.edited(EDIT_WATCHDOG_MODEL): kwargs["model"] = uiutil.get_combo_entry( self.widget("watchdog-model")) if self.edited(EDIT_WATCHDOG_ACTION): kwargs["action"] = uiutil.get_combo_entry( self.widget("watchdog-action")) return vmmAddHardware.change_config_helper(self.vm.define_watchdog, kwargs, self.vm, self.err, devobj=devobj) def config_filesystem_apply(self, devobj): kwargs = {} if self.edited(EDIT_FS): if self.fsDetails.validate_page_filesystem() is False: return False kwargs["newdev"] = self.fsDetails.get_dev() return vmmAddHardware.change_config_helper(self.vm.define_filesystem, kwargs, self.vm, self.err, devobj=devobj) def config_hostdev_apply(self, devobj): kwargs = {} if self.edited(EDIT_HOSTDEV_ROMBAR): kwargs["rom_bar"] = self.widget("hostdev-rombar").get_active() return vmmAddHardware.change_config_helper(self.vm.define_hostdev, kwargs, self.vm, self.err, devobj=devobj) # Device removal def remove_device(self, devobj): logging.debug("Removing device: %s", devobj) if not self.err.chkbox_helper(self.config.get_confirm_removedev, self.config.set_confirm_removedev, text1=(_("Are you sure you want to remove this device?"))): return # Define the change try: self.vm.remove_device(devobj) except Exception, e: self.err.show_err(_("Error Removing Device: %s" % str(e))) return # Try to hot remove detach_err = False try: if self.vm.is_active(): self.vm.detach_device(devobj) except Exception, e: logging.debug("Device could not be hotUNplugged: %s", str(e)) detach_err = (str(e), "".join(traceback.format_exc())) if not detach_err: self.disable_apply() return self.err.show_err( _("Device could not be removed from the running machine"), details=(detach_err[0] + "\n\n" + detach_err[1]), text2=_("This change will take effect after the next guest " "shutdown."), buttons=Gtk.ButtonsType.OK, dialog_type=Gtk.MessageType.INFO) ######################## # Details page refresh # ######################## def refresh_resources(self, ignore): details = self.widget("details-pages") page = details.get_current_page() # If the dialog is visible, we want to make sure the XML is always # up to date try: if self.is_visible(): self.vm.ensure_latest_xml() except libvirt.libvirtError, e: if util.exception_is_libvirt_error(e, "VIR_ERR_NO_DOMAIN"): self.close() return raise # Stats page needs to be refreshed every tick 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 != DETAILS_PAGE_DETAILS: return # This function should only be called when the VM xml actually # changes (not everytime it is refreshed). This saves us from blindly # parsing the xml every tick # Add / remove new devices self.repopulate_hw_list() pagetype = self.get_hw_selection(HW_LIST_COL_TYPE) if pagetype is None: return if self.widget("config-apply").get_sensitive(): # Apply button sensitive means user is making changes, don't # erase them return self.hw_selected(page=pagetype) def refresh_overview_page(self): # Basic details self.widget("overview-name").set_text(self.vm.get_name()) self.widget("overview-uuid").set_text(self.vm.get_uuid()) desc = self.vm.get_description() or "" desc_widget = self.widget("overview-description") desc_widget.get_buffer().set_text(desc) title = self.vm.get_title() self.widget("overview-title").set_sensitive(self.vm.title_supported) self.widget("overview-title").set_text(title or "") # Hypervisor Details self.widget("overview-hv").set_text(self.vm.get_pretty_hv_type()) arch = self.vm.get_arch() or _("Unknown") emu = self.vm.get_emulator() or _("None") self.widget("overview-arch").set_text(arch) self.widget("overview-emulator").set_text(emu) # Firmware domcaps = self.vm.get_domain_capabilities() firmware = domcaps.label_for_firmware_path( self.vm.get_xmlobj().os.loader) if self.widget("overview-firmware").is_visible(): uiutil.set_combo_entry( self.widget("overview-firmware"), firmware) elif self.widget("overview-firmware-label").is_visible(): self.widget("overview-firmware-label").set_text(firmware) # Machine settings machtype = self.vm.get_machtype() if self.widget("machine-type").is_visible(): uiutil.set_combo_entry( self.widget("machine-type"), machtype) elif self.widget("machine-type-label").is_visible(): self.widget("machine-type-label").set_text(machtype) # Chipset chipset = _chipset_label_from_machine(machtype) if self.widget("overview-chipset").is_visible(): uiutil.set_combo_entry( self.widget("overview-chipset"), chipset) elif self.widget("overview-chipset-label").is_visible(): self.widget("overview-chipset-label").set_text(chipset) # User namespace idmap setting is_container = self.vm.is_container() self.widget("config-idmap-expander").set_visible(is_container) self.widget("uid-target").set_text('1000') self.widget("uid-count").set_text('10') self.widget("gid-target").set_text('1000') self.widget("gid-count").set_text('10') IdMap = self.vm.get_idmap() show_config = IdMap.uid_start is not None self.widget("config-idmap-checkbutton").set_active(show_config) self.widget("idmap-spin-grid").set_sensitive(show_config) if show_config: Name = ["uid-target", "uid-count", "gid-target", "gid-count"] for name in Name: IdMap_proper = getattr(IdMap, name.replace("-", "_")) self.widget(name).set_value(int(IdMap_proper)) def refresh_inspection_page(self): inspection_supported = self.config.support_inspection uiutil.set_grid_row_visible(self.widget("details-overview-error"), self.vm.inspection.error) if self.vm.inspection.error: msg = _("Error while inspecting the guest configuration") self.widget("details-overview-error").set_text(msg) # Operating System (ie. inspection data) self.widget("details-inspection-os").set_visible(inspection_supported) if inspection_supported: hostname = self.vm.inspection.hostname if not hostname: hostname = _("unknown") self.widget("inspection-hostname").set_text(hostname) product_name = self.vm.inspection.product_name if not product_name: product_name = _("unknown") self.widget("inspection-product-name").set_text(product_name) # Applications (also inspection data) self.widget("details-inspection-apps").set_visible(inspection_supported) if inspection_supported: apps = self.vm.inspection.applications or [] apps_list = self.widget("inspection-apps") apps_model = apps_list.get_model() apps_model.clear() for app in apps: name = "" if app["app_name"]: name = app["app_name"] if app["app_display_name"]: name = app["app_display_name"] version = "" if app["app_version"]: version = app["app_version"] if app["app_release"]: version += "-" + app["app_release"] summary = "" if app["app_summary"]: summary = app["app_summary"] apps_model.append([name, version, summary]) def refresh_stats_page(self): def _dsk_rx_tx_text(rx, tx, unit): return ('%(rx)d %(unit)s read ' '%(tx)d %(unit)s write' % {"rx": rx, "tx": tx, "unit": unit}) def _net_rx_tx_text(rx, tx, unit): return ('%(rx)d %(unit)s in ' '%(tx)d %(unit)s out' % {"rx": rx, "tx": tx, "unit": unit}) cpu_txt = _("Disabled") mem_txt = _("Disabled") dsk_txt = _("Disabled") net_txt = _("Disabled") if self.config.get_stats_enable_cpu_poll(): cpu_txt = "%d %%" % self.vm.guest_cpu_time_percentage() if self.config.get_stats_enable_memory_poll(): cur_vm_memory = self.vm.stats_memory() vm_memory = self.vm.maximum_memory() mem_txt = "%s of %s" % (util.pretty_mem(cur_vm_memory), util.pretty_mem(vm_memory)) if self.config.get_stats_enable_disk_poll(): dsk_txt = _dsk_rx_tx_text(self.vm.disk_read_rate(), self.vm.disk_write_rate(), "KiB/s") if self.config.get_stats_enable_net_poll(): net_txt = _net_rx_tx_text(self.vm.network_rx_rate(), self.vm.network_tx_rate(), "KiB/s") self.widget("overview-cpu-usage-text").set_text(cpu_txt) self.widget("overview-memory-usage-text").set_text(mem_txt) self.widget("overview-network-traffic-text").set_markup(net_txt) self.widget("overview-disk-usage-text").set_markup(dsk_txt) self.cpu_usage_graph.set_property("data_array", self.vm.guest_cpu_time_vector()) self.memory_usage_graph.set_property("data_array", self.vm.stats_memory_vector()) d1, d2 = self.vm.disk_io_vectors() self.disk_io_graph.set_property("data_array", d1 + d2) n1, n2 = self.vm.network_traffic_vectors() self.network_traffic_graph.set_property("data_array", n1 + n2) def refresh_config_cpu(self): # This bit needs to come first, since CPU values can be affected # by whether topology is enabled cpu = self.vm.get_cpu_config() show_top = bool(cpu.sockets or cpu.cores or cpu.threads) self.widget("cpu-topology-enable").set_active(show_top) conn = self.vm.conn host_active_count = conn.host_active_processor_count() maxvcpus = self.vm.vcpu_max_count() curvcpus = self.vm.vcpu_count() curadj = self.widget("config-vcpus") maxadj = self.widget("config-maxvcpus") curadj.set_value(int(curvcpus)) maxadj.set_value(int(maxvcpus)) self.widget("state-host-cpus").set_text(str(host_active_count)) # Warn about overcommit warn = bool(self.config_get_vcpus() > host_active_count) self.widget("config-vcpus-warn-box").set_visible(warn) # CPU model config sockets = cpu.sockets or 1 cores = cpu.cores or 1 threads = cpu.threads or 1 self.widget("cpu-sockets").set_value(sockets) self.widget("cpu-cores").set_value(cores) self.widget("cpu-threads").set_value(threads) if show_top: self.widget("cpu-topology-expander").set_expanded(True) model = cpu.model or None if not model: if cpu.mode == "host-model" or cpu.mode == "host-passthrough": model = cpu.mode if model: self.widget("cpu-model").get_child().set_text(model) else: uiutil.set_combo_entry( self.widget("cpu-model"), virtinst.CPU.SPECIAL_MODE_HV_DEFAULT, 2) # Warn about hyper-threading setting cpu_model = self.get_config_cpu_model() warn_ht = _warn_cpu_thread_topo(threads, cpu_model) self.widget("config-topology-warn-box").set_visible(warn_ht) is_host = (cpu.mode == "host-model") self.widget("cpu-copy-host").set_active(bool(is_host)) self.on_cpu_copy_host_clicked(self.widget("cpu-copy-host")) def refresh_config_memory(self): host_mem_widget = self.widget("state-host-memory") host_mem = self.vm.conn.host_memory_size() / 1024 vm_cur_mem = self.vm.get_memory() / 1024.0 vm_max_mem = self.vm.maximum_memory() / 1024.0 host_mem_widget.set_text("%d MiB" % (int(round(host_mem)))) curmem = self.widget("config-memory") maxmem = self.widget("config-maxmem") curmem.set_value(int(round(vm_cur_mem))) maxmem.set_value(int(round(vm_max_mem))) if not self.widget("config-memory").get_sensitive(): ignore, upper = maxmem.get_range() maxmem.set_range(curmem.get_value(), upper) @staticmethod def build_disk_sgio(vm, combo): ignore = vm model = Gtk.ListStore(str, str) combo.set_model(model) uiutil.init_combo_text_column(combo, 1) model.append([None, _("Hypervisor default")]) model.append(["filtered", "filtered"]) model.append(["unfiltered", "unfiltered"]) def refresh_disk_page(self): disk = self.get_hw_selection(HW_LIST_COL_DEVICE) if not disk: return path = disk.path source_pool = disk.source_pool devtype = disk.device ro = disk.read_only share = disk.shareable bus = disk.bus removable = disk.removable addr = disk.address.type cache = disk.driver_cache io = disk.driver_io driver_type = disk.driver_type or "" serial = disk.serial iotune_rbs = (disk.iotune_rbs or 0) / 1024 iotune_ris = (disk.iotune_ris or 0) iotune_tbs = (disk.iotune_tbs or 0) / 1024 iotune_tis = (disk.iotune_tis or 0) iotune_wbs = (disk.iotune_wbs or 0) / 1024 iotune_wis = (disk.iotune_wis or 0) show_format = (not self.is_customize_dialog or virtinst.VirtualDisk.path_definitely_exists( disk.conn, disk.path)) size = _("Unknown") if not path: size = "-" else: if source_pool: try: pool = self.conn.get_pool(source_pool) vol = pool.get_volume(path) except KeyError: vol = None else: vol = self.conn.get_vol_by_path(path) if vol: size = vol.get_pretty_capacity() elif not self.conn.is_remote(): ignore, val = virtinst.VirtualDisk.stat_local_path(path) if val != 0: if val > (1024 * 1024 * 1024): size = "%2.2f GiB" % (val / (1024.0 * 1024.0 * 1024.0)) else: size = "%2.2f MiB" % (val / (1024.0 * 1024.0)) is_cdrom = (devtype == virtinst.VirtualDisk.DEVICE_CDROM) is_floppy = (devtype == virtinst.VirtualDisk.DEVICE_FLOPPY) is_usb = (bus == "usb") can_set_removable = (is_usb and (self.conn.is_qemu() or self.conn.is_test_conn())) if removable is None: removable = False else: can_set_removable = True if addr == "spapr-vio": bus = "spapr-vscsi" pretty_name = _label_for_device(disk, self.vm) self.widget("disk-source-path").set_text(path or "-") self.widget("disk-target-type").set_text(pretty_name) self.widget("disk-readonly").set_active(ro) self.widget("disk-readonly").set_sensitive(not is_cdrom) self.widget("disk-shareable").set_active(share) self.widget("disk-removable").set_active(removable) uiutil.set_grid_row_visible(self.widget("disk-removable"), can_set_removable) is_lun = disk.device == virtinst.VirtualDisk.DEVICE_LUN uiutil.set_grid_row_visible(self.widget("disk-sgio"), is_lun) if is_lun: self.build_disk_sgio(self.vm, self.widget("disk-sgio")) uiutil.set_combo_entry(self.widget("disk-sgio"), disk.sgio) self.widget("disk-size").set_text(size) uiutil.set_combo_entry(self.widget("disk-cache"), cache) uiutil.set_combo_entry(self.widget("disk-io"), io) self.widget("disk-format").set_sensitive(show_format) self.widget("disk-format").get_child().set_text(driver_type) self.widget("disk-format-warn").hide() # skip virtio-scsi since we don't handle it correctly yet, need # to duplicate the addhw logic vmmAddHardware.populate_disk_bus_combo(self.vm, devtype, self.widget("disk-bus").get_model(), do_virtio_scsi=False) uiutil.set_combo_entry(self.widget("disk-bus"), bus) self.widget("disk-serial").set_text(serial or "") self.widget("disk-iotune-rbs").set_value(iotune_rbs) self.widget("disk-iotune-ris").set_value(iotune_ris) self.widget("disk-iotune-tbs").set_value(iotune_tbs) self.widget("disk-iotune-tis").set_value(iotune_tis) self.widget("disk-iotune-wbs").set_value(iotune_wbs) self.widget("disk-iotune-wis").set_value(iotune_wis) button = self.widget("config-cdrom-connect") if is_cdrom or is_floppy: if not path: # source device not connected button.set_label(Gtk.STOCK_CONNECT) else: button.set_label(Gtk.STOCK_DISCONNECT) button.show() else: button.hide() def refresh_network_page(self): net = self.get_hw_selection(HW_LIST_COL_DEVICE) if not net: return vmmAddHardware.populate_network_model_combo( self.vm, self.widget("network-model")) uiutil.set_combo_entry(self.widget("network-model"), net.model) macaddr = net.macaddr or "" if self.widget("network-mac-label").is_visible(): self.widget("network-mac-label").set_text(macaddr) else: self.widget("network-mac-entry").set_text(macaddr) self.netlist.set_dev(net) def refresh_input_page(self): inp = self.get_hw_selection(HW_LIST_COL_DEVICE) if not inp: return dev = vmmAddHardware.label_for_input_device(inp.type, inp.bus) mode = None if inp.type == "tablet": mode = _("Absolute Movement") elif inp.type == "mouse": mode = _("Relative Movement") self.widget("input-dev-type").set_text(dev) self.widget("input-dev-mode").set_text(mode or "") uiutil.set_grid_row_visible(self.widget("input-dev-mode"), bool(mode)) tooltip = _remove_tooltip sensitive = True if ((inp.type == "mouse" and inp.bus in ("xen", "ps2")) or (inp.type == "keyboard" and inp.bus in ("xen", "ps2"))): sensitive = False tooltip = _("Hypervisor does not support removing this device") self.widget("config-remove").set_sensitive(sensitive) self.widget("config-remove").set_tooltip_text(tooltip) def refresh_graphics_page(self): gfx = self.get_hw_selection(HW_LIST_COL_DEVICE) if not gfx: return title = self.gfxdetails.set_dev(gfx) self.widget("graphics-title").set_markup("%s" % title) def refresh_sound_page(self): sound = self.get_hw_selection(HW_LIST_COL_DEVICE) if not sound: return uiutil.set_combo_entry(self.widget("sound-model"), sound.model) def refresh_smartcard_page(self): sc = self.get_hw_selection(HW_LIST_COL_DEVICE) if not sc: return uiutil.set_combo_entry(self.widget("smartcard-mode"), sc.mode) def refresh_redir_page(self): rd = self.get_hw_selection(HW_LIST_COL_DEVICE) if not rd: return address = None if rd.type == 'tcp': address = _("%s:%s") % (rd.host, rd.service) self.widget("redir-title").set_markup(_label_for_device(rd, self.vm)) self.widget("redir-type").set_text(rd.pretty_type(rd.type)) self.widget("redir-address").set_text(address or "") uiutil.set_grid_row_visible( self.widget("redir-address"), bool(address)) def refresh_tpm_page(self): tpmdev = self.get_hw_selection(HW_LIST_COL_DEVICE) if not tpmdev: return def show_ui(param, val=None): widgetname = "tpm-" + param.replace("_", "-") doshow = tpmdev.supports_property(param) if not val and doshow: val = getattr(tpmdev, param) uiutil.set_grid_row_visible(self.widget(widgetname), doshow) self.widget(widgetname).set_text(val or "-") dev_type = tpmdev.type self.widget("tpm-dev-type").set_text( virtinst.VirtualTPMDevice.get_pretty_type(dev_type)) # Device type specific properties, only show if apply to the cur dev show_ui("device_path") def refresh_panic_page(self): dev = self.get_hw_selection(HW_LIST_COL_DEVICE) if not dev: return def show_ui(param, val=None): widgetname = "panic-" + param.replace("_", "-") if not val: val = getattr(dev, param) if not val: propername = param.upper() + "_DEFAULT" val = getattr(virtinst.VirtualPanicDevice, propername, "-").upper() uiutil.set_grid_row_visible(self.widget(widgetname), True) self.widget(widgetname).set_text(val or "-") ptyp = virtinst.VirtualPanicDevice.get_pretty_type(dev.type) show_ui("type", ptyp) show_ui("iobase") def refresh_rng_page(self): dev = self.get_hw_selection(HW_LIST_COL_DEVICE) values = { "rng-bind-host" : "bind_host", "rng-bind-service" : "bind_service", "rng-connect-host" : "connect_host", "rng-connect-service" : "connect_service", "rng-type" : "type", "rng-device" : "device", "rng-backend-type" : "backend_type", "rng-rate-bytes" : "rate_bytes", "rng-rate-period" : "rate_period" } rewriter = { "rng-type" : lambda x: VirtualRNGDevice.get_pretty_type(x), "rng-backend-type" : lambda x: VirtualRNGDevice.get_pretty_backend_type(x), } def set_visible(widget, v): uiutil.set_grid_row_visible(self.widget(widget), v) is_egd = dev.type == VirtualRNGDevice.TYPE_EGD udp = dev.backend_type == VirtualRNGDevice.BACKEND_TYPE_UDP bind = VirtualRNGDevice.BACKEND_MODE_BIND in dev.backend_mode() set_visible("rng-device", not is_egd) set_visible("rng-mode", is_egd and not udp) set_visible("rng-backend-type", is_egd) set_visible("rng-connect-host", is_egd and (udp or not bind)) set_visible("rng-connect-service", is_egd and (udp or not bind)) set_visible("rng-bind-host", is_egd and (udp or bind)) set_visible("rng-bind-service", is_egd and (udp or bind)) for k, prop in values.items(): val = "-" if dev.supports_property(prop): val = getattr(dev, prop) or "-" r = rewriter.get(k) if r: val = r(val) self.widget(k).set_text(val) if is_egd and not udp: mode = VirtualRNGDevice.get_pretty_mode(dev.backend_mode()[0]) self.widget("rng-mode").set_text(mode) def refresh_char_page(self): chardev = self.get_hw_selection(HW_LIST_COL_DEVICE) if not chardev: return show_target_type = not (chardev.virtual_device_type in ["serial", "parallel"]) show_target_name = chardev.virtual_device_type == "channel" def show_ui(param, val=None): widgetname = "char-" + param.replace("_", "-") doshow = chardev.supports_property(param, ro=True) # Exception: don't show target type for serial/parallel if (param == "target_type" and not show_target_type): doshow = False if (param == "target_name" and not show_target_name): doshow = False if not val and doshow: val = getattr(chardev, param) uiutil.set_grid_row_visible(self.widget(widgetname), doshow) self.widget(widgetname).set_text(val or "-") def build_host_str(base): if (not chardev.supports_property(base + "_host") or not chardev.supports_property(base + "_port")): return "" host = getattr(chardev, base + "_host") or "" port = getattr(chardev, base + "_port") or "" ret = str(host) if port: ret += ":%s" % str(port) return ret char_type = chardev.virtual_device_type.capitalize() target_port = chardev.target_port dev_type = chardev.type or "pty" primary = hasattr(chardev, "virtmanager_console_dup") typelabel = "" if char_type == "serial": typelabel = _("Serial Device") elif char_type == "parallel": typelabel = _("Parallel Device") elif char_type == "console": typelabel = _("Console Device") elif char_type == "channel": typelabel = _("Channel Device") else: typelabel = _("%s Device") % char_type.capitalize() if (target_port is not None and chardev.virtual_device_type == "console"): typelabel += " %s" % (int(target_port) + 1) if target_port is not None and not show_target_type: typelabel += " %s" % (int(target_port) + 1) if primary: typelabel += " (%s)" % _("Primary Console") typelabel = "%s" % typelabel self.widget("char-type").set_markup(typelabel) self.widget("char-dev-type").set_text(dev_type) # Device type specific properties, only show if apply to the cur dev show_ui("source_host", build_host_str("source")) show_ui("bind_host", build_host_str("bind")) show_ui("source_path") show_ui("target_type") show_ui("target_name") def refresh_hostdev_page(self): hostdev = self.get_hw_selection(HW_LIST_COL_DEVICE) if not hostdev: return rom_bar = hostdev.rom_bar if rom_bar is None: rom_bar = True devtype = hostdev.type if hostdev.type == 'usb': devtype = 'usb_device' nodedev = None for trydev in self.vm.conn.filter_nodedevs(devtype, None): if trydev.xmlobj.compare_to_hostdev(hostdev): nodedev = trydev.xmlobj pretty_name = None if nodedev: pretty_name = nodedev.pretty_name() if not pretty_name: pretty_name = hostdev.pretty_name() uiutil.set_grid_row_visible( self.widget("hostdev-rombar"), hostdev.type == "pci") devlabel = "Physical %s Device" % hostdev.type.upper() self.widget("hostdev-title").set_markup(devlabel) self.widget("hostdev-source").set_text(pretty_name) self.widget("hostdev-rombar").set_active(rom_bar) def refresh_video_page(self): vid = self.get_hw_selection(HW_LIST_COL_DEVICE) if not vid: return no_default = not self.is_customize_dialog vmmAddHardware.populate_video_combo(self.vm, self.widget("video-model"), no_default=no_default) model = vid.model ram = vid.vram heads = vid.heads try: ramlabel = ram and "%d MiB" % (int(ram) / 1024) or "-" except: ramlabel = "-" self.widget("video-ram").set_text(ramlabel) self.widget("video-heads").set_text(heads and str(heads) or "-") uiutil.set_combo_entry(self.widget("video-model"), model) def refresh_watchdog_page(self): watch = self.get_hw_selection(HW_LIST_COL_DEVICE) if not watch: return model = watch.model action = watch.action uiutil.set_combo_entry(self.widget("watchdog-model"), model) uiutil.set_combo_entry(self.widget("watchdog-action"), action) def refresh_controller_page(self): dev = self.get_hw_selection(HW_LIST_COL_DEVICE) if not dev: return type_label = dev.pretty_desc() model_label = dev.model if not model_label: model_label = _("Default") self.widget("controller-type").set_text(type_label) combo = self.widget("controller-model") uiutil.set_grid_row_visible(combo, True) vmmAddHardware.populate_controller_model_combo(combo, dev.type, self.widget("config-remove"), False) uiutil.set_combo_entry(self.widget("controller-model"), dev.model or "Default") def refresh_filesystem_page(self): dev = self.get_hw_selection(HW_LIST_COL_DEVICE) if not dev: return self.fsDetails.set_dev(dev) self.fsDetails.update_fs_rows() def refresh_boot_page(self): # Refresh autostart try: # Older libvirt versions return None if not supported autoval = self.vm.get_autostart() except libvirt.libvirtError: autoval = None # Autostart autostart_chk = self.widget("config-autostart") enable_autostart = (autoval is not None) autostart_chk.set_sensitive(enable_autostart) autostart_chk.set_active(enable_autostart and autoval or False) show_kernel = not self.vm.is_container() show_init = self.vm.is_container() show_boot = (not self.vm.is_container() and not self.vm.is_xenpv()) uiutil.set_grid_row_visible( self.widget("boot-order-frame"), show_boot) uiutil.set_grid_row_visible( self.widget("boot-kernel-expander"), show_kernel) uiutil.set_grid_row_visible( self.widget("boot-init-frame"), show_init) # Kernel/initrd boot kernel, initrd, dtb, args = self.vm.get_boot_kernel_info() expand = bool(kernel or dtb or initrd or args) def keep_text(wname, guestval): # If the user unsets kernel/initrd by unchecking the # 'enable kernel boot' box, we keep the previous values cached # in the text fields to allow easy switching back and forth. guestval = guestval or "" if self.get_text(wname) and not guestval: return self.widget(wname).set_text(guestval) keep_text("boot-kernel", kernel) keep_text("boot-initrd", initrd) keep_text("boot-dtb", dtb) keep_text("boot-kernel-args", args) if expand: # Only 'expand' if requested, so a refresh doesn't # magically unexpand the UI the user just touched self.widget("boot-kernel-expander").set_expanded(True) self.widget("boot-kernel-enable").set_active(expand) self.widget("boot-kernel-enable").toggled() # Only show dtb if it's supported arch = self.vm.get_arch() or "" show_dtb = (self.get_text("boot-dtb") or self.vm.get_hv_type() == "test" or "arm" in arch or "microblaze" in arch or "ppc" in arch) self.widget("boot-dtb-label").set_visible(show_dtb) self.widget("boot-dtb-box").set_visible(show_dtb) # populate init, initargs = self.vm.get_init() self.widget("boot-init-path").set_text(init or "") self.widget("boot-init-args").set_text(initargs or "") # Boot menu populate menu = self.vm.get_boot_menu() or False self.widget("boot-menu").set_active(menu) self.repopulate_boot_order() ############################ # Hardware list population # ############################ def populate_hw_list(self): hw_list_model = self.widget("hw-list").get_model() hw_list_model.clear() def add_hw_list_option(title, page_id, icon_name): hw_list_model.append([title, icon_name, Gtk.IconSize.LARGE_TOOLBAR, page_id, title]) add_hw_list_option(_("Overview"), HW_LIST_TYPE_GENERAL, "computer") if not self.is_customize_dialog: if self.config.support_inspection: add_hw_list_option(_("OS information"), HW_LIST_TYPE_INSPECTION, "computer") add_hw_list_option("Performance", HW_LIST_TYPE_STATS, "utilities-system-monitor") add_hw_list_option("Processor", HW_LIST_TYPE_CPU, "device_cpu") add_hw_list_option("Memory", HW_LIST_TYPE_MEMORY, "device_mem") add_hw_list_option("Boot Options", HW_LIST_TYPE_BOOT, "system-run") self.repopulate_hw_list() def repopulate_hw_list(self): hw_list = self.widget("hw-list") hw_list_model = hw_list.get_model() currentDevices = [] def dev_cmp(origdev, newdev): if isinstance(origdev, str): return False if origdev == newdev: return True if not origdev.get_root_xpath(): return False return origdev.get_root_xpath() == newdev.get_root_xpath() def add_hw_list_option(idx, name, page_id, info, icon_name): hw_list_model.insert(idx, [name, icon_name, Gtk.IconSize.LARGE_TOOLBAR, page_id, info]) def update_hwlist(hwtype, dev): """ See if passed hw is already in list, and if so, update info. If not in list, add it! """ label = _label_for_device(dev, self.vm) icon = _icon_for_device(dev) currentDevices.append(dev) insertAt = 0 for row in hw_list_model: rowdev = row[HW_LIST_COL_DEVICE] if dev_cmp(rowdev, dev): # Update existing HW info row[HW_LIST_COL_DEVICE] = dev row[HW_LIST_COL_LABEL] = label row[HW_LIST_COL_ICON_NAME] = icon return if row[HW_LIST_COL_TYPE] <= hwtype: insertAt += 1 # Add the new HW row add_hw_list_option(insertAt, label, hwtype, dev, icon) for dev in self.vm.get_disk_devices(): update_hwlist(HW_LIST_TYPE_DISK, dev) for dev in self.vm.get_network_devices(): update_hwlist(HW_LIST_TYPE_NIC, dev) for dev in self.vm.get_input_devices(): update_hwlist(HW_LIST_TYPE_INPUT, dev) for dev in self.vm.get_graphics_devices(): update_hwlist(HW_LIST_TYPE_GRAPHICS, dev) for dev in self.vm.get_sound_devices(): update_hwlist(HW_LIST_TYPE_SOUND, dev) for dev in self.vm.get_char_devices(): update_hwlist(HW_LIST_TYPE_CHAR, dev) for dev in self.vm.get_hostdev_devices(): update_hwlist(HW_LIST_TYPE_HOSTDEV, dev) for dev in self.vm.get_redirdev_devices(): update_hwlist(HW_LIST_TYPE_REDIRDEV, dev) for dev in self.vm.get_video_devices(): update_hwlist(HW_LIST_TYPE_VIDEO, dev) for dev in self.vm.get_watchdog_devices(): update_hwlist(HW_LIST_TYPE_WATCHDOG, dev) for dev in self.vm.get_controller_devices(): # skip USB2 ICH9 companion controllers if dev.model in ["ich9-uhci1", "ich9-uhci2", "ich9-uhci3"]: continue update_hwlist(HW_LIST_TYPE_CONTROLLER, dev) for dev in self.vm.get_filesystem_devices(): update_hwlist(HW_LIST_TYPE_FILESYSTEM, dev) for dev in self.vm.get_smartcard_devices(): update_hwlist(HW_LIST_TYPE_SMARTCARD, dev) for dev in self.vm.get_tpm_devices(): update_hwlist(HW_LIST_TYPE_TPM, dev) for dev in self.vm.get_rng_devices(): update_hwlist(HW_LIST_TYPE_RNG, dev) for dev in self.vm.get_panic_devices(): update_hwlist(HW_LIST_TYPE_PANIC, dev) devs = range(len(hw_list_model)) devs.reverse() for i in devs: _iter = hw_list_model.iter_nth_child(None, i) olddev = hw_list_model[i][HW_LIST_COL_DEVICE] # Existing device, don't remove it if type(olddev) is str or olddev in currentDevices: continue hw_list_model.remove(_iter) def _make_boot_rows(self): if not self.vm.can_use_device_boot_order(): return [ ["hd", "Hard Disk", "drive-harddisk", False, True], ["cdrom", "CDROM", "media-optical", False, True], ["network", "Network (PXE)", "network-idle", False, True], ["fd", "Floppy", "media-floppy", False, True], ] ret = [] for dev in self.vm.get_bootable_devices(): icon = _icon_for_device(dev) label = _label_for_device(dev, self.vm) ret.append([dev.vmmidstr, label, icon, False, True]) if not ret: ret.append([None, _("No bootable devices"), None, False, False]) return ret def repopulate_boot_order(self): boot_list = self.widget("config-boot-list") boot_model = boot_list.get_model() boot_model.clear() boot_rows = self._make_boot_rows() boot_order = self.vm.get_boot_order() for key in boot_order: for row in boot_rows[:]: if key != row[BOOT_KEY]: continue row[BOOT_ACTIVE] = True boot_model.append(row) boot_rows.remove(row) break for row in boot_rows: boot_model.append(row)