Files
virt-manager/src/virtManager/details.py

3001 lines
111 KiB
Python
Raw Normal View History

#
# Copyright (C) 2006-2008 Red Hat, Inc.
# Copyright (C) 2006 Daniel P. Berrange <berrange@redhat.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA.
#
2006-09-25 18:41:47 -04:00
import logging
import traceback
import gtk
import libvirt
import virtManager.uihelpers as uihelpers
from virtManager.storagebrowse import vmmStorageBrowser
2010-12-08 17:26:19 -05:00
from virtManager.baseclass import vmmGObjectUI
from virtManager.addhardware import vmmAddHardware
from virtManager.choosecd import vmmChooseCD
from virtManager.console import vmmConsolePages
from virtManager.serialcon import vmmSerialConsole
from virtManager.graphwidgets import Sparkline
from virtManager import util as util
import virtinst
# Columns in hw list model
HW_LIST_COL_LABEL = 0
HW_LIST_COL_ICON_NAME = 1
HW_LIST_COL_ICON_SIZE = 2
HW_LIST_COL_TYPE = 3
HW_LIST_COL_DEVICE = 4
# Types for the hw list model: numbers specify what order they will be listed
HW_LIST_TYPE_GENERAL = 0
HW_LIST_TYPE_STATS = 1
HW_LIST_TYPE_CPU = 2
HW_LIST_TYPE_MEMORY = 3
HW_LIST_TYPE_BOOT = 4
HW_LIST_TYPE_DISK = 5
HW_LIST_TYPE_NIC = 6
HW_LIST_TYPE_INPUT = 7
HW_LIST_TYPE_GRAPHICS = 8
HW_LIST_TYPE_SOUND = 9
HW_LIST_TYPE_CHAR = 10
HW_LIST_TYPE_HOSTDEV = 11
HW_LIST_TYPE_VIDEO = 12
HW_LIST_TYPE_WATCHDOG = 13
HW_LIST_TYPE_CONTROLLER = 14
HW_LIST_TYPE_FILESYSTEM = 15
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]
# Boot device columns
BOOT_DEV_TYPE = 0
BOOT_LABEL = 1
BOOT_ICON = 2
BOOT_ACTIVE = 3
# Main tab pages
2008-05-07 11:14:20 -04:00
PAGE_CONSOLE = 0
PAGE_DETAILS = 1
PAGE_DYNAMIC_OFFSET = 2
2008-05-07 11:14:20 -04:00
def prettyify_disk_bus(bus):
if bus in ["ide", "scsi", "usb"]:
return bus.upper()
if bus in ["xen"]:
return bus.capitalize()
if bus == "virtio":
return "VirtIO"
return bus
def prettyify_disk(devtype, bus, idx):
busstr = prettyify_disk_bus(bus) or ""
if devtype == "floppy":
devstr = "Floppy"
busstr = ""
elif devtype == "cdrom":
devstr = "CDROM"
else:
devstr = devtype.capitalize()
if busstr:
ret = "%s %s" % (busstr, devstr)
else:
ret = devstr
return "%s %s" % (ret, idx)
def safeint(val, fmt="%.3d"):
try:
int(val)
except:
return str(val)
return fmt % int(val)
2010-03-04 15:29:51 -05:00
def prettyify_bytes(val):
if val > (1024 * 1024 * 1024):
return "%2.2f GB" % (val / (1024.0 * 1024.0 * 1024.0))
2010-03-03 18:43:48 -05:00
else:
return "%2.2f MB" % (val / (1024.0 * 1024.0))
2010-03-03 18:43:48 -05:00
def build_hostdev_label(hostdev):
# String shown in the devices details section
srclabel = ""
# String shown in the VMs hardware list
hwlabel = ""
typ = hostdev.type
vendor = hostdev.vendor
product = hostdev.product
addrbus = hostdev.bus
addrdev = hostdev.device
addrslt = hostdev.slot
addrfun = hostdev.function
addrdom = hostdev.domain
def dehex(val):
if val.startswith("0x"):
val = val[2:]
return val
hwlabel = typ.upper()
srclabel = typ.upper()
if vendor and product:
# USB by vendor + product
devstr = " %s:%s" % (dehex(vendor), dehex(product))
srclabel += devstr
hwlabel += devstr
elif addrbus and addrdev:
# USB by bus + dev
srclabel += (" Bus %s Device %s" %
(safeint(addrbus), safeint(addrdev)))
hwlabel += " %s:%s" % (safeint(addrbus), safeint(addrdev))
elif addrbus and addrslt and addrfun and addrdom:
# PCI by bus:slot:function
devstr = (" %s:%s:%s.%s" %
(dehex(addrdom), dehex(addrbus),
dehex(addrslt), dehex(addrfun)))
srclabel += devstr
hwlabel += devstr
return srclabel, hwlabel
def lookup_nodedev(vmmconn, hostdev):
def intify(val, do_hex=False):
try:
if do_hex:
return int(val or '0x00', 16)
else:
return int(val)
except:
return -1
def attrVal(node, attr):
if not hasattr(node, attr):
return None
return getattr(node, attr)
devtype = hostdev.type
vendor_id = hostdev.vendor or -1
product_id = hostdev.product or -1
device = intify(hostdev.device, True)
bus = intify(hostdev.bus, True)
domain = intify(hostdev.domain, True)
func = intify(hostdev.function, True)
slot = intify(hostdev.slot, True)
found_dev = None
# For USB we want a device, not a bus
if devtype == 'usb':
devtype = 'usb_device'
devs = vmmconn.get_nodedevs(devtype, None)
for dev in devs:
# Try to get info from {product|vendor}_id
if (attrVal(dev, "product_id") == product_id and
attrVal(dev, "vendor_id") == vendor_id):
found_dev = dev
break
else:
# Try to get info from bus/addr
dev_id = intify(attrVal(dev, "device"))
bus_id = intify(attrVal(dev, "bus"))
dom_id = intify(attrVal(dev, "domain"))
func_id = intify(attrVal(dev, "function"))
slot_id = intify(attrVal(dev, "slot"))
if ((dev_id == device and bus_id == bus) or
(dom_id == domain and func_id == func and
bus_id == bus and slot_id == slot)):
found_dev = dev
break
return found_dev
2010-12-08 17:26:19 -05:00
class vmmDetails(vmmGObjectUI):
def __init__(self, vm, parent=None):
2010-12-08 17:26:19 -05:00
vmmGObjectUI.__init__(self, "vmm-details.glade", "vmm-details")
self.vm = vm
self.conn = self.vm.get_connection()
self.is_customize_dialog = False
if parent:
self.is_customize_dialog = True
# Details window is being abused as a 'configure before install'
# dialog, set things as appropriate
self.topwin.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
self.topwin.set_transient_for(parent)
self.window.get_widget("customize-toolbar").show()
self.window.get_widget("details-toolbar").hide()
self.window.get_widget("details-menubar").hide()
pages = self.window.get_widget("details-pages")
pages.set_current_page(PAGE_DETAILS)
self.serial_tabs = []
self.last_console_page = PAGE_CONSOLE
self.addhw = None
self.media_choosers = {"cdrom": None, "floppy": None}
self.storage_browser = None
2009-11-02 11:21:50 -05:00
self.ignorePause = False
self.ignoreDetails = False
self._cpu_copy_host = False
2010-12-08 17:26:19 -05:00
self.console = vmmConsolePages(self.vm, self.window)
# Set default window size
w, h = self.vm.get_details_window_size()
self.topwin.set_default_size(w or 800, h or 600)
self.addhwmenu = None
self.keycombo_menu = None
2009-11-02 11:21:50 -05:00
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.window.signal_autoconnect({
"on_close_details_clicked": self.close,
"on_details_menu_close_activate": self.close,
"on_vmm_details_delete_event": self.close,
"on_vmm_details_configure_event": self.window_resized,
"on_details_menu_quit_activate": self.exit_app,
"on_control_vm_details_toggled": self.details_console_changed,
"on_control_vm_console_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.close,
"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_destroy_activate": self.control_vm_destroy,
2007-09-27 17:52:29 -04:00
"on_details_menu_pause_activate": self.control_vm_pause,
2009-07-26 15:54:14 -04:00
"on_details_menu_clone_activate": self.control_vm_clone,
"on_details_menu_migrate_activate": self.control_vm_migrate,
2008-05-07 11:40:29 -04:00
"on_details_menu_screenshot_activate": self.control_vm_screenshot,
"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_pages_switch_page": self.switch_page,
"on_overview_name_changed": self.config_enable_apply,
"on_overview_acpi_changed": self.config_acpi_changed,
"on_overview_apic_changed": self.config_apic_changed,
"on_overview_clock_changed": self.config_enable_apply,
"on_security_label_changed": self.security_label_changed,
"on_security_type_changed": self.security_type_changed,
"on_config_vcpus_changed": self.config_vcpus_changed,
"on_config_maxvcpus_changed": self.config_maxvcpus_changed,
"on_config_vcpupin_changed": self.config_vcpus_changed,
"on_config_vcpupin_generate_clicked": self.config_vcpupin_generate,
"on_cpu_model_changed": self.config_enable_apply,
"on_cpu_cores_changed": self.config_enable_apply,
"on_cpu_sockets_changed": self.config_enable_apply,
"on_cpu_threads_changed": self.config_enable_apply,
"on_cpu_copy_host_clicked": self.config_cpu_copy_host,
"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" : (self.config_boot_move, True),
"on_config_boot_movedown_clicked" : (self.config_boot_move,
False),
"on_config_autostart_changed": self.config_enable_apply,
2010-12-11 22:00:52 -05:00
"on_boot_menu_changed": self.config_enable_apply,
"on_boot_kernel_changed": self.config_enable_apply,
"on_boot_kernel_initrd_changed": self.config_enable_apply,
"on_boot_kernel_args_changed": self.config_enable_apply,
"on_boot_kernel_browse_clicked": self.browse_kernel,
"on_boot_kernel_initrd_browse_clicked": self.browse_initrd,
"on_boot_init_path_changed": self.config_enable_apply,
"on_disk_readonly_changed": self.config_enable_apply,
"on_disk_shareable_changed": self.config_enable_apply,
"on_disk_cache_combo_changed": self.config_enable_apply,
"on_disk_bus_combo_changed": self.config_enable_apply,
"on_disk_format_changed": self.config_enable_apply,
"on_network_source_combo_changed": self.config_enable_apply,
"on_network_bridge_changed": self.config_enable_apply,
"on_network-source-mode-combo_changed": self.config_enable_apply,
"on_network_model_combo_changed": self.config_enable_apply,
"on_vport_type_changed": self.config_enable_apply,
"on_vport_managerid_changed": self.config_enable_apply,
"on_vport_typeid_changed": self.config_enable_apply,
"on_vport_typeidversion_changed": self.config_enable_apply,
"on_vport_instanceid_changed": self.config_enable_apply,
"on_gfx_type_combo_changed": self.config_enable_apply,
"on_vnc_keymap_combo_changed": self.config_enable_apply,
"on_vnc_password_changed": self.config_enable_apply,
"on_sound_model_combo_changed": self.config_enable_apply,
"on_video_model_combo_changed": self.config_enable_apply,
"on_watchdog_model_combo_changed": self.config_enable_apply,
"on_watchdog_action_combo_changed": self.config_enable_apply,
"on_config_apply_clicked": self.config_apply,
2007-03-16 16:27:50 -04:00
"on_details_help_activate": self.show_help,
"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.toggle_fullscreen,
"on_details_menu_view_size_to_vm_activate": self.console.size_to_vm,
"on_details_menu_view_scale_always_toggled": self.console.set_scale_type,
"on_details_menu_view_scale_fullscreen_toggled": self.console.set_scale_type,
"on_details_menu_view_scale_never_toggled": self.console.set_scale_type,
"on_console_pages_switch_page": self.console.page_changed,
"on_console_auth_password_activate": self.console.auth_login,
"on_console_auth_login_clicked": self.console.auth_login,
})
2009-11-02 11:21:50 -05:00
# Deliberately keep all this after signal connection
self.vm.connect("status-changed", self.refresh_vm_state)
self.vm.connect("config-changed", self.refresh_vm_state)
self.vm.connect("resources-sampled", self.refresh_resources)
self.window.get_widget("hw-list").get_selection().connect(
"changed",
self.hw_selected)
self.window.get_widget("config-boot-list").get_selection().connect(
"changed",
self.config_bootdev_selected)
finish_img = gtk.image_new_from_stock(gtk.STOCK_ADD,
gtk.ICON_SIZE_BUTTON)
self.window.get_widget("add-hardware-button").set_image(finish_img)
self.populate_hw_list()
self.repopulate_boot_list()
self.hw_selected()
self.refresh_vm_state()
def cleanup(self):
self.close()
try:
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 = {}
for serial in self.serial_tabs:
self._close_serial_tab(serial)
self.console.cleanup()
self.console = None
self.vm = None
self.conn = None
self.addhwmenu = None
except:
logging.exception("Error cleaning up details")
vmmGObjectUI.cleanup(self)
def show(self):
2010-12-08 17:26:19 -05:00
vis = self.is_visible()
self.topwin.present()
2010-12-08 17:26:19 -05:00
if vis:
return
self.emit("details-opened")
self.refresh_vm_state()
def close(self, ignore1=None, ignore2=None):
fs = self.window.get_widget("details-menu-view-fullscreen")
if fs.get_active():
fs.set_active(False)
if not self.is_visible():
return
self.topwin.hide()
2010-12-22 02:13:11 +01:00
if self.console.viewer and self.console.viewer.get_widget() and \
self.console.viewer.get_widget().flags() & gtk.VISIBLE:
try:
2010-12-22 02:13:11 +01:00
self.console.close_viewer()
except:
2010-12-22 02:13:11 +01:00
logging.error("Failure when disconnecting from desktop server")
self.emit("details-closed")
return 1
def is_visible(self):
2010-12-08 17:26:19 -05:00
return bool(self.topwin.flags() & gtk.VISIBLE)
2009-11-02 11:21:50 -05:00
##########################
# Initialization helpers #
##########################
def init_menus(self):
# Shutdown button menu
uihelpers.build_shutdown_button_menu(
2009-11-02 11:21:50 -05:00
self.window.get_widget("control-shutdown"),
self.control_vm_shutdown,
self.control_vm_reboot,
self.control_vm_destroy,
self.control_vm_save)
2009-11-02 11:21:50 -05:00
icon_name = self.config.get_shutdown_icon_name()
for name in ["details-menu-shutdown",
"details-menu-reboot",
"details-menu-poweroff",
"details-menu-destroy"]:
image = gtk.image_new_from_icon_name(icon_name, gtk.ICON_SIZE_MENU)
self.window.get_widget(name).set_image(image)
# Add HW popup menu
self.addhwmenu = gtk.Menu()
addHW = gtk.ImageMenuItem(_("Add Hardware"))
addHWImg = gtk.Image()
addHWImg.set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU)
addHW.set_image(addHWImg)
addHW.show()
addHW.connect("activate", self.add_hardware)
self.addhwmenu.add(addHW)
2009-11-02 11:21:50 -05:00
# Serial list menu
smenu = gtk.Menu()
smenu.connect("show", self.populate_serial_menu)
self.window.get_widget("details-menu-view-serial-list").set_submenu(smenu)
# Don't allowing changing network/disks for Dom0
dom0 = self.vm.is_management_domain()
self.window.get_widget("add-hardware-button").set_sensitive(not dom0)
self.window.get_widget("hw-panel").set_show_tabs(False)
self.window.get_widget("details-pages").set_show_tabs(False)
self.window.get_widget("console-pages").set_show_tabs(False)
self.window.get_widget("details-menu-view-toolbar").set_active(self.config.get_details_show_toolbar())
# Keycombo menu (ctrl+alt+del etc.)
self.keycombo_menu = uihelpers.build_keycombo_menu(
self.console.send_key)
self.window.get_widget("details-menu-send-key").set_submenu(
self.keycombo_menu)
2009-11-02 11:21:50 -05:00
# XXX: Help docs useless/out of date
self.window.get_widget("help_menuitem").hide()
def init_graphs(self):
graph_table = self.window.get_widget("graph-table")
self.cpu_usage_graph = Sparkline()
self.cpu_usage_graph.set_property("reversed", True)
graph_table.attach(self.cpu_usage_graph, 1, 2, 0, 1)
self.memory_usage_graph = Sparkline()
self.memory_usage_graph.set_property("reversed", True)
graph_table.attach(self.memory_usage_graph, 1, 2, 1, 2)
self.disk_io_graph = Sparkline()
self.disk_io_graph.set_property("reversed", True)
self.disk_io_graph.set_property("filled", False)
self.disk_io_graph.set_property("num_sets", 2)
self.disk_io_graph.set_property("rgb", map(lambda x: x / 255.0,
2009-11-02 11:21:50 -05:00
[0x82, 0x00, 0x3B, 0x29, 0x5C, 0x45]))
graph_table.attach(self.disk_io_graph, 1, 2, 2, 3)
self.network_traffic_graph = Sparkline()
self.network_traffic_graph.set_property("reversed", True)
self.network_traffic_graph.set_property("filled", False)
self.network_traffic_graph.set_property("num_sets", 2)
self.network_traffic_graph.set_property("rgb",
map(lambda x: x / 255.0,
2009-11-02 11:21:50 -05:00
[0x82, 0x00, 0x3B,
0x29, 0x5C, 0x45]))
graph_table.attach(self.network_traffic_graph, 1, 2, 3, 4)
graph_table.show_all()
2009-11-02 11:21:50 -05:00
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.window.get_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.window.get_widget("hw-list").append_column(hwCol)
# Description text view
desc = self.window.get_widget("overview-description")
buf = gtk.TextBuffer()
buf.connect("changed", self.config_enable_apply)
desc.set_buffer(buf)
# Clock combo
clock_combo = self.window.get_widget("overview-clock-combo")
clock_model = gtk.ListStore(str)
clock_combo.set_model(clock_model)
text = gtk.CellRendererText()
clock_combo.pack_start(text, True)
clock_combo.add_attribute(text, 'text', 0)
clock_model.set_sort_column_id(0, gtk.SORT_ASCENDING)
for offset in ["localtime", "utc"]:
clock_model.append([offset])
2009-11-02 11:21:50 -05:00
# Security info tooltips
util.tooltip_wrapper(self.window.get_widget("security-static-info"),
2010-04-05 12:21:03 -04:00
_("Static SELinux security type tells libvirt to always start the guest process with the specified label. The administrator is responsible for making sure the images are labeled correctly on disk."))
2009-11-02 11:21:50 -05:00
util.tooltip_wrapper(self.window.get_widget("security-dynamic-info"),
_("The dynamic SELinux security type tells libvirt to automatically pick a unique label for the guest process and guest image, ensuring total isolation of the guest. (Default)"))
# VCPU Pinning list
generate_cpuset = self.window.get_widget("config-vcpupin-generate")
generate_warn = self.window.get_widget("config-vcpupin-generate-err")
if not self.conn.get_capabilities().host.topology:
generate_cpuset.set_sensitive(False)
generate_warn.show()
util.tooltip_wrapper(generate_warn,
_("Libvirt did not detect NUMA capabilities."))
# [ VCPU #, Currently running on Phys CPU #, CPU Pinning list ]
vcpu_list = self.window.get_widget("config-vcpu-list")
vcpu_model = gtk.ListStore(str, str, str)
vcpu_list.set_model(vcpu_model)
vcpuCol = gtk.TreeViewColumn(_("VCPU"))
physCol = gtk.TreeViewColumn(_("On CPU"))
pinCol = gtk.TreeViewColumn(_("Pinning"))
vcpu_list.append_column(vcpuCol)
vcpu_list.append_column(physCol)
vcpu_list.append_column(pinCol)
vcpu_text = gtk.CellRendererText()
vcpuCol.pack_start(vcpu_text, True)
vcpuCol.add_attribute(vcpu_text, 'text', 0)
vcpuCol.set_sort_column_id(0)
phys_text = gtk.CellRendererText()
physCol.pack_start(phys_text, True)
physCol.add_attribute(phys_text, 'text', 1)
physCol.set_sort_column_id(1)
pin_text = gtk.CellRendererText()
pin_text.set_property("editable", True)
pin_text.connect("edited", self.config_vcpu_pin)
pinCol.pack_start(pin_text, True)
pinCol.add_attribute(pin_text, 'text', 2)
# Boot device list
boot_list = self.window.get_widget("config-boot-list")
# model = [ XML boot type, display name, icon name, enabled ]
boot_list_model = gtk.ListStore(str, str, str, bool)
boot_list.set_model(boot_list_model)
2009-11-02 11:21:50 -05:00
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)
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
# CPU features
caps = self.vm.get_connection().get_capabilities()
cpu_values = None
cpu_names = []
all_features = []
try:
cpu_values = caps.get_cpu_values(self.vm.get_arch())
cpu_names = sorted(map(lambda c: c.model, cpu_values.cpus),
key=str.lower)
all_features = cpu_values.features
except:
logging.exception("Error populating CPU model list")
# [ feature name, enabled? ]
feat_list = self.window.get_widget("cpu-features")
feat_model = gtk.ListStore(str, bool)
feat_list.set_model(feat_model)
nameCol = gtk.TreeViewColumn()
chkCol = gtk.TreeViewColumn()
feat_list.append_column(nameCol)
feat_list.append_column(chkCol)
name_text = gtk.CellRendererText()
nameCol.pack_start(name_text, True)
nameCol.add_attribute(name_text, 'text', 0)
nameCol.set_sort_column_id(0)
feat_toggle = gtk.CellRendererToggle()
chkCol.pack_start(feat_toggle, True)
chkCol.add_attribute(feat_toggle, 'active', 1)
chkCol.set_sort_column_id(1)
def feature_changed(src, index, model):
model[index][1] = not src.get_active()
self.config_enable_apply()
feat_toggle.connect("toggled", feature_changed, feat_model)
for name in all_features:
feat_model.append([name, False])
# CPU model combo
cpu_model = self.window.get_widget("cpu-model")
model = gtk.ListStore(str, object)
cpu_model.set_model(model)
cpu_model.set_text_column(0)
model.set_sort_column_id(0, gtk.SORT_ASCENDING)
for name in cpu_names:
model.append([name, cpu_values.get_cpu(name)])
# Disk cache combo
disk_cache = self.window.get_widget("disk-cache-combo")
uihelpers.build_cache_combo(self.vm, disk_cache)
# Disk format combo
format_list = self.window.get_widget("disk-format")
uihelpers.build_storage_format_combo(self.vm, format_list)
# Disk bus combo
disk_bus = self.window.get_widget("disk-bus-combo")
uihelpers.build_disk_bus_combo(self.vm, disk_bus)
# Network source
net_source = self.window.get_widget("network-source-combo")
net_bridge = self.window.get_widget("network-bridge-box")
source_mode_box = self.window.get_widget("network-source-mode-box")
source_mode_label = self.window.get_widget("network-source-mode")
vport_expander = self.window.get_widget("vport-expander")
uihelpers.init_network_list(net_source, net_bridge, source_mode_box, source_mode_label, vport_expander)
# source mode
source_mode = self.window.get_widget("network-source-mode-combo")
uihelpers.build_source_mode_combo(self.vm, source_mode)
# Network model
net_model = self.window.get_widget("network-model-combo")
uihelpers.build_netmodel_combo(self.vm, net_model)
# Graphics type
gfx_type = self.window.get_widget("gfx-type-combo")
model = gtk.ListStore(str, str)
gfx_type.set_model(model)
text = gtk.CellRendererText()
gfx_type.pack_start(text, True)
gfx_type.add_attribute(text, 'text', 1)
model.append([virtinst.VirtualGraphics.TYPE_VNC,
"VNC"])
model.append([virtinst.VirtualGraphics.TYPE_SPICE,
2011-03-24 10:09:42 -04:00
"Spice"])
gfx_type.set_active(-1)
# Graphics keymap
gfx_keymap = self.window.get_widget("gfx-keymap-combo")
uihelpers.build_vnc_keymap_combo(self.vm, gfx_keymap,
no_default=no_default)
# Sound model
sound_dev = self.window.get_widget("sound-model-combo")
uihelpers.build_sound_combo(self.vm, sound_dev, no_default=no_default)
# Video model combo
video_dev = self.window.get_widget("video-model-combo")
uihelpers.build_video_combo(self.vm, video_dev, no_default=no_default)
# Watchdog model combo
combo = self.window.get_widget("watchdog-model-combo")
uihelpers.build_watchdogmodel_combo(self.vm, combo,
no_default=no_default)
# Watchdog action combo
combo = self.window.get_widget("watchdog-action-combo")
uihelpers.build_watchdogaction_combo(self.vm, combo,
no_default=no_default)
# Helper function to handle the combo/label pattern used for
# video model, sound model, network model, etc.
def set_combo_label(self, prefix, value, model_idx=0, label="",
comparefunc=None):
label = label or value
model_label = self.window.get_widget(prefix + "-label")
model_combo = self.window.get_widget(prefix + "-combo")
idx = -1
if comparefunc:
model_in_list, idx = comparefunc(model_combo.get_model(), value)
else:
model_list = map(lambda x: x[model_idx], model_combo.get_model())
model_in_list = (value in model_list)
if model_in_list:
idx = model_list.index(value)
model_label.set_property("visible", not model_in_list)
model_combo.set_property("visible", model_in_list)
model_label.set_text(label or "")
if model_in_list:
model_combo.set_active(idx)
else:
model_combo.set_active(-1)
# Helper for accessing value of combo/label pattern
def get_combo_value(self, widgetname, model_idx=0):
combo = self.window.get_widget(widgetname)
if combo.get_active() < 0:
return None
return combo.get_model()[combo.get_active()][model_idx]
def get_combo_label_value(self, prefix, model_idx=0):
comboname = prefix + "-combo"
label = self.window.get_widget(prefix + "-label")
value = None
if label.get_property("visible"):
value = label.get_text()
else:
value = self.get_combo_value(comboname, model_idx)
return value
##########################
# 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
self.addhwmenu.popup(None, None, None, 0, event.time)
def build_serial_list(self):
ret = []
def add_row(text, err, sensitive, do_radio, cb, serialidx):
ret.append([text, err, sensitive, do_radio, cb, serialidx])
devs = self.vm.get_serial_devs()
if len(devs) == 0:
add_row(_("No text console available"),
None, False, False, None, None)
def build_desc(dev):
if dev.virtual_device_type == "console":
return "Text Console %d" % (dev.vmmindex + 1)
return "Serial %d" % (dev.vmmindex + 1)
for dev in devs:
desc = build_desc(dev)
idx = dev.vmmindex
err = vmmSerialConsole.can_connect(self.vm, dev)
sensitive = not bool(err)
def cb(src):
return self.control_serial_tab(src, desc, idx)
add_row(desc, err, sensitive, True, cb, idx)
return ret
def current_serial_dev(self):
showing_serial = (self.last_console_page >= PAGE_DYNAMIC_OFFSET)
if not showing_serial:
return
serial_idx = self.last_console_page - PAGE_DYNAMIC_OFFSET
if len(self.serial_tabs) < serial_idx:
return
return self.serial_tabs[serial_idx]
def populate_serial_menu(self, src):
for ent in src:
src.remove(ent)
serial_page_dev = self.current_serial_dev()
showing_graphics = (self.last_console_page == PAGE_CONSOLE)
# Populate serial devices
group = None
itemlist = self.build_serial_list()
for msg, err, sensitive, do_radio, cb, ignore in itemlist:
if do_radio:
item = gtk.RadioMenuItem(group, msg)
if group is None:
group = item
else:
item = gtk.MenuItem(msg)
item.set_sensitive(sensitive)
if err and not sensitive:
util.tooltip_wrapper(item, err)
if cb:
item.connect("toggled", cb)
# Tab is already open, make sure marked as such
if (sensitive and
serial_page_dev and
serial_page_dev.name == msg):
item.set_active(True)
src.add(item)
src.add(gtk.SeparatorMenuItem())
# Populate graphical devices
devs = self.vm.get_graphics_devices()
if len(devs) == 0:
item = gtk.MenuItem(_("No graphical console available"))
item.set_sensitive(False)
src.add(item)
else:
dev = devs[0]
item = gtk.RadioMenuItem(group, _("Graphical Console %s") %
2011-03-24 10:09:42 -04:00
dev.pretty_type_simple(dev.type))
if group == None:
group = item
if showing_graphics:
item.set_active(True)
item.connect("toggled", self.control_serial_tab,
dev.virtual_device_type, dev.type)
src.add(item)
src.show_all()
def control_fullscreen(self, src):
menu = self.window.get_widget("details-menu-view-fullscreen")
if src.get_active() != menu.get_active():
menu.set_active(src.get_active())
def toggle_toolbar(self, src):
active = src.get_active()
self.config.set_details_show_toolbar(active)
if active and not \
self.window.get_widget("details-menu-view-fullscreen").get_active():
self.window.get_widget("toolbar-box").show()
else:
self.window.get_widget("toolbar-box").hide()
def get_boot_selection(self):
widget = self.window.get_widget("config-boot-list")
selection = widget.get_selection()
model, treepath = selection.get_selected()
if treepath == None:
return None
return model[treepath]
def set_hw_selection(self, page):
hwlist = self.window.get_widget("hw-list")
selection = hwlist.get_selection()
selection.select_path(str(page))
def get_hw_selection(self, field):
vmlist = self.window.get_widget("hw-list")
selection = vmlist.get_selection()
active = selection.get_selected()
if active[1] == None:
return None
return active[0].get_value(active[1], 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.window.get_widget("hw-list").get_selection().select_path(0)
return page
def hw_selected(self, ignore1=None, page=None, selected=True):
pagetype = self.force_get_hw_pagetype(page)
self.window.get_widget("config-remove").set_sensitive(True)
self.window.get_widget("hw-panel").set_sensitive(True)
self.window.get_widget("hw-panel").show()
try:
if pagetype == HW_LIST_TYPE_GENERAL:
self.refresh_overview_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()
else:
pagetype = -1
except Exception, e:
self.err.show_err(_("Error refreshing hardware page: %s") % str(e))
return
rem = pagetype in remove_pages
if selected:
self.window.get_widget("config-apply").set_sensitive(False)
self.window.get_widget("config-remove").set_property("visible", rem)
self.window.get_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 = False
if (src == self.window.get_widget("control-vm-details") or
src == self.window.get_widget("details-menu-view-details")):
is_details = True
pages = self.window.get_widget("details-pages")
if is_details:
pages.set_current_page(PAGE_DETAILS)
else:
pages.set_current_page(self.last_console_page)
def sync_details_console_view(self, is_details):
details = self.window.get_widget("control-vm-details")
details_menu = self.window.get_widget("details-menu-view-details")
console = self.window.get_widget("control-vm-console")
console_menu = self.window.get_widget("details-menu-view-console")
try:
self.ignoreDetails = True
details.set_active(is_details)
details_menu.set_active(is_details)
console.set_active(not is_details)
console_menu.set_active(not is_details)
finally:
self.ignoreDetails = False
def switch_page(self, ignore1=None, ignore2=None, newpage=None):
self.page_refresh(newpage)
self.sync_details_console_view(newpage == PAGE_DETAILS)
self.console.set_allow_fullscreen()
if newpage == PAGE_CONSOLE or newpage >= PAGE_DYNAMIC_OFFSET:
self.last_console_page = newpage
def change_run_text(self, can_restore):
if can_restore:
text = _("_Restore")
else:
text = _("_Run")
strip_text = text.replace("_", "")
self.window.get_widget("details-menu-run").get_child().set_label(text)
self.window.get_widget("control-run").set_label(strip_text)
def refresh_vm_state(self, ignore1=None, ignore2=None, ignore3=None):
vm = self.vm
status = self.vm.status()
self.toggle_toolbar(
self.window.get_widget("details-menu-view-toolbar"))
active = vm.is_active()
destroy = vm.is_destroyable()
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.hasSavedImage())
self.window.get_widget("details-menu-destroy").set_sensitive(destroy)
self.window.get_widget("control-run").set_sensitive(run)
self.window.get_widget("details-menu-run").set_sensitive(run)
self.window.get_widget("details-menu-migrate").set_sensitive(stop)
self.window.get_widget("control-shutdown").set_sensitive(stop)
self.window.get_widget("details-menu-shutdown").set_sensitive(stop)
self.window.get_widget("details-menu-save").set_sensitive(stop)
self.window.get_widget("control-pause").set_sensitive(stop)
self.window.get_widget("details-menu-pause").set_sensitive(stop)
self.set_pause_state(paused)
self.window.get_widget("overview-name").set_editable(not active)
self.window.get_widget("config-vcpus").set_sensitive(not ro)
self.window.get_widget("config-vcpupin").set_sensitive(not ro)
self.window.get_widget("config-memory").set_sensitive(not ro)
self.window.get_widget("config-maxmem").set_sensitive(not ro)
# Disable send key menu entries for offline VM
send_key = self.window.get_widget("details-menu-send-key")
for c in send_key.get_submenu().get_children():
c.set_sensitive(not (run or paused))
self.console.update_widget_states(vm, status)
if not run:
self.activate_default_console_page()
self.window.get_widget("overview-status-text").set_text(
self.vm.run_status())
self.window.get_widget("overview-status-icon").set_from_pixbuf(
self.vm.run_status_icon())
details = self.window.get_widget("details-pages")
self.page_refresh(details.get_current_page())
# This is safe to refresh, and is dependent on domain state
self._refresh_runtime_pinning()
#############################
# External action listeners #
#############################
def show_help(self, src_ignore):
self.emit("action-show-help", "virt-manager-details-window")
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):
if self.vm.get_graphics_devices() or not self.vm.get_serial_devs():
return
# Show serial console
devs = self.build_serial_list()
for name, ignore, sensitive, ignore, cb, serialidx in devs:
if not sensitive or not cb:
continue
self._show_serial_tab(name, serialidx)
break
def activate_default_page(self):
pages = self.window.get_widget("details-pages")
pages.set_current_page(PAGE_CONSOLE)
self.activate_default_console_page()
def activate_console_page(self):
pages = self.window.get_widget("details-pages")
pages.set_current_page(PAGE_CONSOLE)
def activate_performance_page(self):
self.window.get_widget("details-pages").set_current_page(PAGE_DETAILS)
self.set_hw_selection(HW_LIST_TYPE_STATS)
def activate_config_page(self):
self.window.get_widget("details-pages").set_current_page(PAGE_DETAILS)
def add_hardware(self, src_ignore):
try:
if self.addhw is None:
self.addhw = vmmAddHardware(self.vm)
2011-04-14 08:47:42 -04:00
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):
info = self.get_hw_selection(HW_LIST_COL_DEVICE)
if not info:
return
devtype = info.virtual_device_type
self.remove_device(devtype, info)
def set_pause_state(self, paused):
# Set pause widget states
try:
self.ignorePause = True
self.window.get_widget("control-pause").set_active(paused)
self.window.get_widget("details-menu-pause").set_active(paused)
finally:
self.ignorePause = False
def control_vm_pause(self, src):
if self.ignorePause:
return
# Let state handler listener change things if necc.
self.set_pause_state(not src.get_active())
if not self.vm.is_paused():
self.emit("action-suspend-domain",
self.vm.get_connection().get_uri(),
self.vm.get_uuid())
else:
self.emit("action-resume-domain",
self.vm.get_connection().get_uri(),
self.vm.get_uuid())
def control_vm_run(self, src_ignore):
self.emit("action-run-domain",
self.vm.get_connection().get_uri(), self.vm.get_uuid())
def control_vm_shutdown(self, src_ignore):
self.emit("action-shutdown-domain",
self.vm.get_connection().get_uri(), self.vm.get_uuid())
def control_vm_reboot(self, src_ignore):
self.emit("action-reboot-domain",
self.vm.get_connection().get_uri(), self.vm.get_uuid())
def control_vm_save(self, src_ignore):
self.emit("action-save-domain",
self.vm.get_connection().get_uri(), self.vm.get_uuid())
def control_vm_destroy(self, src_ignore):
self.emit("action-destroy-domain",
self.vm.get_connection().get_uri(), self.vm.get_uuid())
def control_vm_clone(self, src_ignore):
self.emit("action-clone-domain",
self.vm.get_connection().get_uri(), self.vm.get_uuid())
2009-07-26 15:54:14 -04:00
def control_vm_migrate(self, src_ignore):
self.emit("action-migrate-domain",
self.vm.get_connection().get_uri(), self.vm.get_uuid())
def control_vm_screenshot(self, src_ignore):
# If someone feels kind they could extend this code to allow
# user to choose what image format they'd like to save in....
path = util.browse_local(
self.topwin,
_("Save Virtual Machine Screenshot"),
self.vm.get_connection(),
_type=("png", "PNG files"),
dialog_type=gtk.FILE_CHOOSER_ACTION_SAVE,
browse_reason=self.config.CONFIG_DIR_SCREENSHOT)
if not path:
return
filename = path
if not(filename.endswith(".png")):
filename += ".png"
2010-12-22 02:13:11 +01:00
image = self.console.viewer.get_pixbuf()
# Save along with a little metadata about us & the domain
image.save(filename, 'png',
{'tEXt::Hypervisor URI': self.vm.get_connection().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()})
msg = gtk.MessageDialog(self.topwin,
gtk.DIALOG_MODAL,
gtk.MESSAGE_INFO,
gtk.BUTTONS_OK,
(_("The screenshot has been saved to:\n%s") %
filename))
msg.set_title(_("Screenshot saved"))
msg.run()
msg.destroy()
#########################
# Serial Console pieces #
#########################
2008-07-24 16:39:09 -04:00
def control_serial_tab(self, src_ignore, name, target_port):
pages = self.window.get_widget("details-pages")
is_graphics = (name == "graphics")
is_serial = not is_graphics
2009-10-21 10:09:09 -04:00
if is_graphics:
pages.set_current_page(PAGE_CONSOLE)
elif is_serial:
self._show_serial_tab(name, target_port)
2009-10-21 10:09:09 -04:00
def _show_serial_tab(self, name, target_port):
serial = None
for s in self.serial_tabs:
if s.name == name:
serial = s
break
if not serial:
serial = vmmSerialConsole(self.vm, target_port, name)
2011-04-11 17:48:21 -04:00
title = gtk.Label(name)
self.window.get_widget("details-pages").append_page(serial.box,
title)
self.serial_tabs.append(serial)
serial.open_console()
page_idx = self.serial_tabs.index(serial) + PAGE_DYNAMIC_OFFSET
self.window.get_widget("details-pages").set_current_page(page_idx)
def _close_serial_tab(self, serial):
if not serial in self.serial_tabs:
return
page_idx = self.serial_tabs.index(serial) + PAGE_DYNAMIC_OFFSET
self.window.get_widget("details-pages").remove_page(page_idx)
2011-04-11 17:48:21 -04:00
serial.cleanup()
self.serial_tabs.remove(serial)
2011-04-11 17:48:21 -04:00
############################
# Details/Hardware getters #
############################
def get_config_boot_devs(self):
boot_model = self.window.get_widget("config-boot-list").get_model()
devs = []
for row in boot_model:
if row[BOOT_ACTIVE]:
devs.append(row[BOOT_DEV_TYPE])
return devs
def get_config_cpu_model(self):
cpu_list = self.window.get_widget("cpu-model")
model = cpu_list.child.get_text()
for row in cpu_list.get_model():
if model == row[0]:
return model, row[1].vendor
return model, None
def get_config_cpu_features(self):
feature_list = self.window.get_widget("cpu-features")
ret = []
for row in feature_list.get_model():
if row[1]:
ret.append(row[0])
return ret
##############################
# Details/Hardware listeners #
##############################
def _spin_get_helper(self, wname):
widget = self.window.get_widget(wname)
adj = widget.get_adjustment()
txt = widget.get_text()
try:
ret = int(txt)
except:
ret = adj.value
return ret
def _browse_file(self, callback, is_media=False):
if is_media:
reason = self.config.CONFIG_DIR_MEDIA
else:
reason = self.config.CONFIG_DIR_IMAGE
if self.storage_browser == None:
self.storage_browser = vmmStorageBrowser(self.conn)
self.storage_browser.set_finish_cb(callback)
self.storage_browser.set_browse_reason(reason)
2011-04-14 08:47:42 -04:00
self.storage_browser.show(self.topwin, self.conn)
def browse_kernel(self, src_ignore):
def cb(ignore, path):
self.window.get_widget("boot-kernel").set_text(path)
self._browse_file(cb)
def browse_initrd(self, src_ignore):
def cb(ignore, path):
self.window.get_widget("boot-kernel-initrd").set_text(path)
self._browse_file(cb)
def config_enable_apply(self, ignore1=None, ignore2=None):
self.window.get_widget("config-apply").set_sensitive(True)
# Overview -> Machine settings
def config_acpi_changed(self, ignore):
widget = self.window.get_widget("overview-acpi")
incon = widget.get_inconsistent()
widget.set_inconsistent(False)
if incon:
widget.set_active(True)
self.config_enable_apply()
def config_apic_changed(self, ignore):
widget = self.window.get_widget("overview-apic")
incon = widget.get_inconsistent()
widget.set_inconsistent(False)
if incon:
widget.set_active(True)
self.config_enable_apply()
# Overview -> Security
def security_label_changed(self, label_ignore):
self.config_enable_apply()
def security_type_changed(self, button):
self.config_enable_apply()
self.window.get_widget("security-label").set_sensitive(
not button.get_active())
# Memory
def config_get_maxmem(self):
return self._spin_get_helper("config-maxmem")
def config_get_memory(self):
return self._spin_get_helper("config-memory")
def config_maxmem_changed(self, src_ignore):
self.config_enable_apply()
def config_memory_changed(self, src_ignore):
self.config_enable_apply()
maxadj = self.window.get_widget("config-maxmem").get_adjustment()
mem = self.config_get_memory()
if maxadj.value < mem:
maxadj.value = mem
maxadj.lower = mem
def generate_cpuset(self):
mem = int(self.vm.get_memory()) / 1024 / 1024
return virtinst.Guest.generate_cpuset(self.conn.vmm, mem)
# VCPUS
def config_get_vcpus(self):
return self._spin_get_helper("config-vcpus")
def config_get_maxvcpus(self):
return self._spin_get_helper("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: %s") % str(e))
self.window.get_widget("config-vcpupin").set_text("")
self.window.get_widget("config-vcpupin").set_text(pinstr)
def config_vcpus_changed(self, ignore):
self.config_enable_apply()
conn = self.vm.get_connection()
host_active_count = conn.host_active_processor_count()
cur = self.config_get_vcpus()
# Warn about overcommit
warn = bool(cur > host_active_count)
self.window.get_widget("config-vcpus-warn-box").set_property(
"visible", warn)
maxadj = self.window.get_widget("config-maxvcpus").get_adjustment()
maxval = self.config_get_maxvcpus()
if maxval < cur:
maxadj.value = cur
maxadj.lower = cur
def config_maxvcpus_changed(self, ignore):
self.config_enable_apply()
def config_cpu_copy_host(self, src_ignore):
# Update UI with output copied from host
try:
CPU = virtinst.CPU(self.vm.get_connection().vmm)
CPU.copy_host_cpu()
self._refresh_cpu_config(CPU)
self._cpu_copy_host = True
except Exception, e:
self.err.show_err(_("Error copying host CPU: %s") % str(e))
return
def config_cpu_topology_enable(self, src):
do_enable = src.get_active()
self.window.get_widget("cpu-topology-table").set_sensitive(do_enable)
self.config_enable_apply()
# Boot device / Autostart
def config_bootdev_selected(self, ignore):
boot_row = self.get_boot_selection()
boot_selection = boot_row and boot_row[BOOT_DEV_TYPE]
boot_devs = self.get_config_boot_devs()
up_widget = self.window.get_widget("config-boot-moveup")
down_widget = self.window.get_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):
boot_model = self.window.get_widget("config-boot-list").get_model()
boot_row = boot_model[index]
is_active = boot_row[BOOT_ACTIVE]
boot_row[BOOT_ACTIVE] = not is_active
self.repopulate_boot_list(self.get_config_boot_devs(),
boot_row[BOOT_DEV_TYPE])
self.config_enable_apply()
def config_boot_move(self, src_ignore, move_up):
boot_row = self.get_boot_selection()
if not boot_row:
return
boot_selection = boot_row[BOOT_DEV_TYPE]
boot_devs = self.get_config_boot_devs()
boot_idx = boot_devs.index(boot_selection)
if move_up:
new_idx = boot_idx - 1
else:
new_idx = boot_idx + 1
if new_idx < 0 or new_idx >= len(boot_devs):
# Somehow we got out of bounds
return
swap_dev = boot_devs[new_idx]
boot_devs[new_idx] = boot_selection
boot_devs[boot_idx] = swap_dev
self.repopulate_boot_list(boot_devs, boot_selection)
self.config_enable_apply()
# CDROM Eject/Connect
def toggle_storage_media(self, src_ignore):
disk = self.get_hw_selection(HW_LIST_COL_DEVICE)
if not disk:
return
dev_id_info = disk
curpath = disk.path
devtype = disk.device
if curpath:
# Disconnect cdrom
self.change_storage_media(dev_id_info, None)
return
def change_cdrom_wrapper(src_ignore, dev_id_info, newpath):
return self.change_storage_media(dev_id_info, newpath)
# Launch 'Choose CD' dialog
if self.media_choosers[devtype] is None:
2010-12-08 17:26:19 -05:00
ret = vmmChooseCD(dev_id_info,
self.vm.get_connection(),
devtype)
ret.connect("cdrom-chosen", change_cdrom_wrapper)
self.media_choosers[devtype] = ret
dialog = self.media_choosers[devtype]
dialog.dev_id_info = dev_id_info
2011-04-14 08:47:42 -04:00
dialog.show(self.topwin)
##################################################
# Details/Hardware config changes (apply button) #
##################################################
def config_apply(self, ignore):
pagetype = self.get_hw_selection(HW_LIST_COL_TYPE)
devobj = self.get_hw_selection(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)
else:
ret = False
except Exception, e:
self.err.show_err(_("Error apply changes: %s") % e)
if ret is not False:
self.window.get_widget("config-apply").set_sensitive(False)
# Overview section
def config_overview_apply(self):
# Overview details
name = self.window.get_widget("overview-name").get_text()
# Machine details
enable_acpi = self.window.get_widget("overview-acpi").get_active()
if self.window.get_widget("overview-acpi").get_inconsistent():
enable_acpi = None
enable_apic = self.window.get_widget("overview-apic").get_active()
if self.window.get_widget("overview-apic").get_inconsistent():
enable_apic = None
clock_combo = self.window.get_widget("overview-clock-combo")
if clock_combo.get_property("visible"):
clock = clock_combo.get_model()[clock_combo.get_active()][0]
else:
clock = self.window.get_widget("overview-clock-label").get_text()
# Security
if self.window.get_widget("security-dynamic").get_active():
setype = "dynamic"
else:
setype = "static"
selabel = self.window.get_widget("security-label").get_text()
semodel = None
if self.window.get_widget("security-type-box").get_property(
"sensitive"):
semodel = self.window.get_widget("security-model").get_text()
# Description
desc_widget = self.window.get_widget("overview-description")
desc = desc_widget.get_buffer().get_property("text") or ""
return self._change_config_helper([self.vm.define_name,
self.vm.define_acpi,
self.vm.define_apic,
self.vm.define_clock,
self.vm.define_seclabel,
self.vm.define_description],
[(name,),
(enable_acpi,),
(enable_apic,),
(clock,),
(semodel, setype, selabel),
(desc,)])
# CPUs
def config_vcpus_apply(self):
vcpus = self.config_get_vcpus()
maxv = self.config_get_maxvcpus()
cpuset = self.window.get_widget("config-vcpupin").get_text()
do_top = self.window.get_widget("cpu-topology-enable").get_active()
sockets = self.window.get_widget("cpu-sockets").get_value()
cores = self.window.get_widget("cpu-cores").get_value()
threads = self.window.get_widget("cpu-threads").get_value()
model, vendor = self.get_config_cpu_model()
features = self.get_config_cpu_features()
logging.info("Setting vcpus for %s to %s, cpuset is %s" %
(self.vm.get_name(), str(vcpus), cpuset))
if not do_top:
sockets = None
cores = None
threads = None
define_funcs = [self.vm.define_vcpus,
self.vm.define_cpuset,
self.vm.define_cpu,
self.vm.define_cpu_topology]
define_args = [(vcpus, maxv),
(cpuset,),
(model, vendor, self._cpu_copy_host, features),
(sockets, cores, threads)]
ret = self._change_config_helper(define_funcs, define_args,
[self.vm.hotplug_vcpus,
self.config_vcpu_pin_cpuset],
[(vcpus,), (cpuset,)])
if ret:
self._cpu_copy_host = False
def config_vcpu_pin(self, src_ignore, path, new_text):
vcpu_list = self.window.get_widget("config-vcpu-list")
vcpu_model = vcpu_list.get_model()
row = vcpu_model[path]
conn = self.vm.get_connection()
try:
vcpu_num = int(row[0])
pinlist = virtinst.Guest.cpuset_str_to_tuple(conn.vmm, new_text)
except Exception, e:
self.err.val_err(_("Error building pin list: %s") % str(e))
return
try:
self.vm.pin_vcpu(vcpu_num, pinlist)
except Exception, e:
self.err.show_err(_("Error pinning vcpus: %s") % str(e))
return
self._refresh_runtime_pinning()
def config_vcpu_pin_cpuset(self, cpuset):
conn = self.vm.get_connection()
vcpu_list = self.window.get_widget("config-vcpu-list")
vcpu_model = vcpu_list.get_model()
if self.vm.vcpu_pinning() == cpuset:
return
pinlist = virtinst.Guest.cpuset_str_to_tuple(conn.vmm, cpuset)
for row in vcpu_model:
vcpu_num = row[0]
self.vm.pin_vcpu(int(vcpu_num), pinlist)
# Memory
def config_memory_apply(self):
curmem = None
maxmem = self.config_get_maxmem()
if self.window.get_widget("config-memory").get_property("sensitive"):
curmem = self.config_get_memory()
if curmem:
curmem = int(curmem) * 1024
if maxmem:
maxmem = int(maxmem) * 1024
return self._change_config_helper(self.vm.define_both_mem,
(curmem, maxmem),
self.vm.hotplug_both_mem,
(curmem, maxmem))
# Boot device / Autostart
def config_boot_options_apply(self):
auto = self.window.get_widget("config-autostart")
if auto.get_property("sensitive"):
try:
self.vm.set_autostart(auto.get_active())
except Exception, e:
self.err.show_err((_("Error changing autostart value: %s") %
str(e)))
return False
bootdevs = self.get_config_boot_devs()
2010-12-11 22:00:52 -05:00
bootmenu = self.window.get_widget("boot-menu").get_active()
kernel = self.window.get_widget("boot-kernel").get_text()
initrd = self.window.get_widget("boot-kernel-initrd").get_text()
args = self.window.get_widget("boot-kernel-args").get_text()
funcs = [self.vm.set_boot_device,
self.vm.set_boot_menu,
self.vm.set_boot_kernel]
opts = [(bootdevs,),
(bootmenu,),
(kernel, initrd, args)]
2011-06-01 11:21:39 -04:00
if self.window.get_widget("boot-init-align").get_property("visible"):
init = self.window.get_widget("boot-init-path").get_text()
if not init:
return self.err.val_err(_("An init path must be specified"))
funcs.append(self.vm.set_boot_init)
opts.append((init,))
return self._change_config_helper(funcs, opts)
# CDROM
def change_storage_media(self, dev_id_info, newpath):
return self._change_config_helper(self.vm.define_storage_media,
(dev_id_info, newpath),
self.vm.hotplug_storage_media,
(dev_id_info, newpath))
# Disk options
def config_disk_apply(self, dev_id_info):
do_readonly = self.window.get_widget("disk-readonly").get_active()
do_shareable = self.window.get_widget("disk-shareable").get_active()
cache = self.get_combo_label_value("disk-cache")
fmt = self.window.get_widget("disk-format").child.get_text()
bus = self.get_combo_label_value("disk-bus")
return self._change_config_helper([self.vm.define_disk_readonly,
self.vm.define_disk_shareable,
self.vm.define_disk_cache,
self.vm.define_disk_driver_type,
self.vm.define_disk_bus],
[(dev_id_info, do_readonly),
(dev_id_info, do_shareable),
(dev_id_info, cache),
(dev_id_info, fmt),
(dev_id_info, bus)])
# Audio options
def config_sound_apply(self, dev_id_info):
model = self.get_combo_label_value("sound-model")
if model:
return self._change_config_helper(self.vm.define_sound_model,
(dev_id_info, model))
# Network options
def config_network_apply(self, dev_id_info):
net_list = self.window.get_widget("network-source-combo")
net_bridge = self.window.get_widget("network-bridge")
nettype, source = uihelpers.get_network_selection(net_list, net_bridge)
source_mode = None
if (nettype == "direct"):
source_mode = self.get_combo_label_value("network-source-mode")
model = self.get_combo_label_value("network-model")
vport_type = self.window.get_widget("vport-type").get_text()
vport_managerid = self.window.get_widget("vport-managerid").get_text()
vport_typeid = self.window.get_widget("vport-typeid").get_text()
vport_idver = self.window.get_widget("vport-typeidversion").get_text()
vport_instid = self.window.get_widget("vport-instanceid").get_text()
return self._change_config_helper([self.vm.define_network_model,
self.vm.define_virtualport,
self.vm.define_network_source],
[(dev_id_info, model),
(dev_id_info, vport_type,
vport_managerid,
vport_typeid,
vport_idver,
vport_instid),
(dev_id_info, nettype, source, source_mode)])
# Graphics options
def _do_change_spicevmc(self, gdev, newgtype):
has_multi_spice = (len(filter(
lambda dev: dev.type == dev.TYPE_SPICE,
self.vm.get_graphics_devices())) > 1)
has_spicevmc = bool(filter(
(lambda dev:
(dev.dev_type == dev.DEV_CHANNEL and
dev.char_type == dev.CHAR_SPICEVMC)),
self.vm.get_char_devices()))
fromspice = (gdev.type == "spice")
tospice = (newgtype == "spice")
if fromspice and tospice:
return False
if not fromspice and not tospice:
return False
if tospice and has_spicevmc:
return False
if fromspice and not has_spicevmc:
return False
if fromspice and has_multi_spice:
# Don't offer to remove if there are other spice displays
return False
msg = (_("You are switching graphics type to %(gtype)s, "
"would you like to %(action)s Spice agent channels?") %
{"gtype": newgtype,
"action": fromspice and "remove" or "add"})
return self.err.yes_no(msg)
def config_graphics_apply(self, dev_id_info):
gtype = self.get_combo_label_value("gfx-type")
passwd = self.window.get_widget("gfx-password").get_text() or None
keymap = self.get_combo_label_value("gfx-keymap")
change_spicevmc = self._do_change_spicevmc(dev_id_info, gtype)
return self._change_config_helper([self.vm.define_graphics_password,
self.vm.define_graphics_keymap,
self.vm.define_graphics_type],
[(dev_id_info, passwd),
(dev_id_info, keymap),
(dev_id_info, gtype,
change_spicevmc)],
[self.vm.hotplug_graphics_password],
[(dev_id_info, passwd)])
# Video options
def config_video_apply(self, dev_id_info):
model = self.get_combo_label_value("video-model")
if model:
return self._change_config_helper(self.vm.define_video_model,
(dev_id_info, model))
# Watchdog options
def config_watchdog_apply(self, dev_id_info):
model = self.get_combo_label_value("watchdog-model")
action = self.get_combo_label_value("watchdog-action")
if model or action:
return self._change_config_helper([self.vm.define_watchdog_model,
self.vm.define_watchdog_action],
[(dev_id_info, model),
(dev_id_info, action)])
# Device removal
def remove_device(self, dev_type, dev_id_info):
logging.debug("Removing device: %s %s" % (dev_type, dev_id_info))
if util.chkbox_helper(self, 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(dev_id_info)
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(dev_id_info)
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:
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 VM reboot."),
buttons=gtk.BUTTONS_OK,
dialog_type=gtk.MESSAGE_INFO)
# Generic config change helpers
def _change_config_helper(self,
define_funcs, define_funcs_args,
hotplug_funcs=None, hotplug_funcs_args=None):
"""
Requires at least a 'define' function and arglist to be specified
(a function where we change the inactive guest config).
Arguments can be a single arg or a list or appropriate arg type (e.g.
a list of functions for define_funcs)
"""
def listify(val):
if not val:
return []
if type(val) is not list:
return [val]
return val
define_funcs = listify(define_funcs)
define_funcs_args = listify(define_funcs_args)
hotplug_funcs = listify(hotplug_funcs)
hotplug_funcs_args = listify(hotplug_funcs_args)
hotplug_err = []
active = self.vm.is_active()
# Hotplug change
func = None
if active and hotplug_funcs:
for idx in range(len(hotplug_funcs)):
func = hotplug_funcs[idx]
args = hotplug_funcs_args[idx]
try:
func(*args)
except Exception, e:
logging.debug("Hotplug failed: func=%s: %s" % (func,
str(e)))
hotplug_err.append((str(e),
"".join(traceback.format_exc())))
# Persistent config change
try:
for idx in range(len(define_funcs)):
func = define_funcs[idx]
args = define_funcs_args[idx]
func(*args)
if define_funcs:
self.vm.redefine_cached()
except Exception, e:
self.err.show_err((_("Error changing VM configuration: %s") %
str(e)))
# If we fail, make sure we flush the cache
self.vm.refresh_xml()
return False
if (hotplug_err or
(active and not len(hotplug_funcs) == len(define_funcs))):
if len(define_funcs) > 1:
msg = _("Some changes may require a guest reboot "
"to take effect.")
else:
msg = _("These changes will take effect after "
"the next guest reboot.")
dtype = hotplug_err and gtk.MESSAGE_WARNING or gtk.MESSAGE_INFO
hotplug_msg = ""
for err1, tb in hotplug_err:
hotplug_msg += (err1 + "\n\n" + tb + "\n")
self.err.show_err(msg,
details=hotplug_msg,
buttons=gtk.BUTTONS_OK,
dialog_type=dtype)
return True
########################
# Details page refresh #
########################
def refresh_resources(self, ignore):
details = self.window.get_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
if self.is_visible():
self.vm.refresh_xml()
# Stats page needs to be refreshed every tick
if (page == PAGE_DETAILS and
self.get_hw_selection(HW_LIST_COL_TYPE) == HW_LIST_TYPE_STATS):
self.refresh_stats_page()
def page_refresh(self, page):
if page != PAGE_DETAILS:
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.window.get_widget("config-apply").get_property("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.window.get_widget("overview-name").set_text(self.vm.get_name())
self.window.get_widget("overview-uuid").set_text(self.vm.get_uuid())
desc = self.vm.get_description() or ""
desc_widget = self.window.get_widget("overview-description")
desc_widget.get_buffer().set_text(desc)
# Hypervisor Details
self.window.get_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.window.get_widget("overview-arch").set_text(arch)
self.window.get_widget("overview-emulator").set_text(emu)
# Machine settings
acpi = self.vm.get_acpi()
apic = self.vm.get_apic()
clock = self.vm.get_clock()
# Hack in a way to represent 'default' acpi/apic for customize dialog
self.window.get_widget("overview-acpi").set_active(bool(acpi))
self.window.get_widget("overview-acpi").set_inconsistent(
acpi is None and self.is_customize_dialog)
self.window.get_widget("overview-apic").set_active(bool(apic))
self.window.get_widget("overview-apic").set_inconsistent(
apic is None and self.is_customize_dialog)
if not clock:
clock = _("Same as host")
self.set_combo_label("overview-clock", clock)
# Security details
semodel, ignore, vmlabel = self.vm.get_seclabel()
caps = self.vm.get_connection().get_capabilities()
if caps.host.secmodel and caps.host.secmodel.model:
semodel = caps.host.secmodel.model
self.window.get_widget("security-model").set_text(semodel or _("None"))
if not semodel or semodel == "apparmor":
self.window.get_widget("security-type-box").hide()
self.window.get_widget("security-type-label").hide()
else:
self.window.get_widget("security-type-box").set_sensitive(bool(semodel))
if self.vm.get_seclabel()[1] == "static":
self.window.get_widget("security-static").set_active(True)
else:
self.window.get_widget("security-dynamic").set_active(True)
self.window.get_widget("security-label").set_text(vmlabel)
def refresh_stats_page(self):
def _dsk_rx_tx_text(rx, tx, unit):
return ('<span color="#82003B">%(rx)d %(unit)s read</span>\n'
'<span color="#295C45">%(tx)d %(unit)s write</span>' %
{"rx": rx, "tx": tx, "unit": unit})
def _net_rx_tx_text(rx, tx, unit):
return ('<span color="#82003B">%(rx)d %(unit)s in</span>\n'
'<span color="#295C45">%(tx)d %(unit)s out</span>' %
{"rx": rx, "tx": tx, "unit": unit})
cpu_txt = _("Disabled")
mem_txt = _("Disabled")
dsk_txt = _("Disabled")
net_txt = _("Disabled")
cpu_txt = "%d %%" % self.vm.cpu_time_percentage()
vm_memory = self.vm.stats_memory()
host_memory = self.vm.get_connection().host_memory_size()
mem_txt = "%d MB of %d MB" % (int(round(vm_memory / 1024.0)),
int(round(host_memory / 1024.0)))
if self.config.get_stats_enable_disk_poll():
dsk_txt = _dsk_rx_tx_text(self.vm.disk_read_rate(),
self.vm.disk_write_rate(), "KB/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(), "KB/s")
self.window.get_widget("overview-cpu-usage-text").set_text(cpu_txt)
self.window.get_widget("overview-memory-usage-text").set_text(mem_txt)
self.window.get_widget("overview-network-traffic-text").set_markup(net_txt)
self.window.get_widget("overview-disk-usage-text").set_markup(dsk_txt)
self.cpu_usage_graph.set_property("data_array",
self.vm.cpu_time_vector())
self.memory_usage_graph.set_property("data_array",
self.vm.stats_memory_vector())
self.disk_io_graph.set_property("data_array",
self.vm.disk_io_vector())
self.network_traffic_graph.set_property("data_array",
self.vm.network_traffic_vector())
def _refresh_cpu_count(self):
conn = self.vm.get_connection()
host_active_count = conn.host_active_processor_count()
maxvcpus = self.vm.vcpu_max_count()
curvcpus = self.vm.vcpu_count()
curadj = self.window.get_widget("config-vcpus").get_adjustment()
maxadj = self.window.get_widget("config-maxvcpus").get_adjustment()
curadj.value = int(curvcpus)
maxadj.value = int(maxvcpus)
self.window.get_widget("state-host-cpus").set_text(
str(host_active_count))
# Warn about overcommit
warn = bool(self.config_get_vcpus() > host_active_count)
self.window.get_widget("config-vcpus-warn-box").set_property(
"visible", warn)
def _refresh_cpu_pinning(self):
# Populate VCPU pinning
vcpupin = self.vm.vcpu_pinning()
self.window.get_widget("config-vcpupin").set_text(vcpupin)
def _refresh_runtime_pinning(self):
conn = self.vm.get_connection()
host_active_count = conn.host_active_processor_count()
vcpu_list = self.window.get_widget("config-vcpu-list")
vcpu_model = vcpu_list.get_model()
vcpu_model.clear()
reason = ""
if not self.vm.is_active():
reason = _("VCPU info only available for running domain.")
elif not self.vm.getvcpus_supported:
reason = _("Virtual machine does not support runtime VPCU info.")
else:
try:
vcpu_info, vcpu_pinning = self.vm.vcpu_info()
except Exception, e:
reason = _("Error getting VCPU info: %s") % str(e)
vcpu_list.set_sensitive(not bool(reason))
util.tooltip_wrapper(vcpu_list, reason or None)
if reason:
return
def build_cpuset_str(pin_info):
pinstr = ""
for i in range(host_active_count):
if i < len(pin_info) and pin_info[i]:
pinstr += (",%s" % str(i))
return pinstr.strip(",")
for idx in range(len(vcpu_info)):
2011-02-03 10:49:08 -05:00
vcpu = str(vcpu_info[idx][0])
vcpucur = str(vcpu_info[idx][3])
vcpupin = build_cpuset_str(vcpu_pinning[idx])
vcpu_model.append([vcpu, vcpucur, vcpupin])
def _refresh_cpu_config(self, cpu):
feature_ui = self.window.get_widget("cpu-features")
model = cpu.model or ""
show_top = bool(cpu.sockets or cpu.cores or cpu.threads)
sockets = cpu.sockets or 1
cores = cpu.cores or 1
threads = cpu.threads or 1
self.window.get_widget("cpu-topology-enable").set_active(show_top)
self.window.get_widget("cpu-model").child.set_text(model)
self.window.get_widget("cpu-sockets").set_value(sockets)
self.window.get_widget("cpu-cores").set_value(cores)
self.window.get_widget("cpu-threads").set_value(threads)
def has_feature(name):
for f in cpu.features:
if f.name == name:
return True
return False
for row in feature_ui.get_model():
row[1] = has_feature(row[0])
def refresh_config_cpu(self):
self._cpu_copy_host = False
cpu = self.vm.get_cpu_config()
self._refresh_cpu_count()
self._refresh_cpu_pinning()
self._refresh_runtime_pinning()
self._refresh_cpu_config(cpu)
def refresh_config_memory(self):
host_mem_widget = self.window.get_widget("state-host-memory")
host_mem = self.vm.get_connection().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 MB" % (int(round(host_mem))))
curmem = self.window.get_widget("config-memory").get_adjustment()
maxmem = self.window.get_widget("config-maxmem").get_adjustment()
curmem.value = int(round(vm_cur_mem))
maxmem.value = int(round(vm_max_mem))
if (not
self.window.get_widget("config-memory").get_property("sensitive")):
maxmem.lower = curmem.value
def refresh_disk_page(self):
disk = self.get_hw_selection(HW_LIST_COL_DEVICE)
if not disk:
return
path = disk.path
devtype = disk.device
ro = disk.read_only
share = disk.shareable
bus = disk.bus
idx = disk.disk_bus_index
cache = disk.driver_cache
driver_type = disk.driver_type or ""
2011-03-09 10:37:51 -05:00
show_format = (not self.is_customize_dialog or
disk.path_exists(disk.conn, disk.path))
2010-03-03 18:43:48 -05:00
size = _("Unknown")
if not path:
size = "-"
else:
vol = self.conn.get_vol_by_path(path)
if vol:
size = vol.get_pretty_capacity()
elif not self.conn.is_remote():
2010-03-04 15:36:29 -05:00
ignore, val = virtinst.VirtualDisk.stat_local_path(path)
if val != 0:
size = prettyify_bytes(val)
2010-03-03 18:43:48 -05:00
is_cdrom = (devtype == virtinst.VirtualDisk.DEVICE_CDROM)
is_floppy = (devtype == virtinst.VirtualDisk.DEVICE_FLOPPY)
pretty_name = prettyify_disk(devtype, bus, idx)
self.window.get_widget("disk-source-path").set_text(path or "-")
self.window.get_widget("disk-target-type").set_text(pretty_name)
self.window.get_widget("disk-readonly").set_active(ro)
self.window.get_widget("disk-readonly").set_sensitive(not is_cdrom)
self.window.get_widget("disk-shareable").set_active(share)
2010-03-03 18:43:48 -05:00
self.window.get_widget("disk-size").set_text(size)
self.set_combo_label("disk-cache", cache)
self.window.get_widget("disk-format").set_sensitive(show_format)
self.window.get_widget("disk-format").child.set_text(driver_type)
no_default = not self.is_customize_dialog
self.populate_disk_bus_combo(devtype, no_default)
self.set_combo_label("disk-bus", bus)
button = self.window.get_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
nettype = net.type
source = net.get_source()
source_mode = net.source_mode
model = net.model
netobj = None
if nettype == virtinst.VirtualNetworkInterface.TYPE_VIRTUAL:
name_dict = {}
for uuid in self.conn.list_net_uuids():
vnet = self.conn.get_net(uuid)
name = vnet.get_name()
name_dict[name] = vnet
if source and source in name_dict:
netobj = name_dict[source]
desc = uihelpers.pretty_network_desc(nettype, source, netobj)
self.window.get_widget("network-mac-address").set_text(net.macaddr)
uihelpers.populate_network_list(
self.window.get_widget("network-source-combo"),
self.conn)
self.window.get_widget("network-source-combo").set_active(-1)
self.window.get_widget("network-bridge").set_text("")
def compare_network(model, info):
for idx in range(len(model)):
row = model[idx]
if row[0] == info[0] and row[1] == info[1]:
return True, idx
if info[0] == virtinst.VirtualNetworkInterface.TYPE_BRIDGE:
idx = (len(model) - 1)
self.window.get_widget("network-bridge").set_text(str(info[1]))
return True, idx
return False, 0
self.set_combo_label("network-source",
(nettype, source), label=desc,
comparefunc=compare_network)
# source mode
uihelpers.populate_source_mode_combo(self.vm,
self.window.get_widget("network-source-mode-combo"))
self.set_combo_label("network-source-mode", source_mode)
# Virtualport config
show_vport = (nettype == "direct")
vport = net.virtualport
self.window.get_widget("vport-expander").set_property("visible",
show_vport)
self.window.get_widget("vport-type").set_text(vport.type or "")
self.window.get_widget("vport-managerid").set_text(
vport.managerid or "")
self.window.get_widget("vport-typeid").set_text(
vport.typeid or "")
self.window.get_widget("vport-typeidversion").set_text(
vport.typeidversion or "")
self.window.get_widget("vport-instanceid").set_text(
vport.instanceid or "")
uihelpers.populate_netmodel_combo(self.vm,
self.window.get_widget("network-model-combo"))
self.set_combo_label("network-model", model)
def refresh_input_page(self):
inp = self.get_hw_selection(HW_LIST_COL_DEVICE)
if not inp:
return
ident = "%s:%s" % (inp.type, inp.bus)
if ident == "tablet:usb":
dev = _("EvTouch USB Graphics Tablet")
elif ident == "mouse:usb":
dev = _("Generic USB Mouse")
elif ident == "mouse:xen":
dev = _("Xen Mouse")
elif ident == "mouse:ps2":
dev = _("PS/2 Mouse")
else:
dev = inp.bus + " " + inp.type
if inp.type == "tablet":
mode = _("Absolute Movement")
else:
mode = _("Relative Movement")
self.window.get_widget("input-dev-type").set_text(dev)
self.window.get_widget("input-dev-mode").set_text(mode)
# Can't remove primary Xen or PS/2 mice
if inp.type == "mouse" and inp.bus in ("xen", "ps2"):
self.window.get_widget("config-remove").set_sensitive(False)
else:
self.window.get_widget("config-remove").set_sensitive(True)
def refresh_graphics_page(self):
gfx = self.get_hw_selection(HW_LIST_COL_DEVICE)
if not gfx:
return
title = self.window.get_widget("graphics-title")
table = self.window.get_widget("graphics-table")
table.foreach(lambda w, ignore: w.hide(), ())
def set_title(text):
title.set_markup("<b>%s</b>" % text)
def show_row(widget_name, suffix=""):
base = "gfx-%s" % widget_name
self.window.get_widget(base + "-title").show()
self.window.get_widget(base + suffix).show()
def show_text(widget_name, text):
show_row(widget_name)
self.window.get_widget("gfx-" + widget_name).set_text(text)
def port_to_string(port):
if port is None:
return "-"
return (port == -1 and _("Automatically allocated") or str(port))
gtype = gfx.type
is_vnc = (gtype == "vnc")
is_sdl = (gtype == "sdl")
is_spice = (gtype == "spice")
is_other = not (True in [is_vnc, is_sdl, is_spice])
set_title(_("%(graphicstype)s Server") %
2011-03-24 10:09:42 -04:00
{"graphicstype" : gfx.pretty_type_simple(gtype)})
settype = ""
if is_vnc or is_spice:
port = port_to_string(gfx.port)
address = (gfx.listen or "127.0.0.1")
keymap = (gfx.keymap or None)
2011-01-07 15:14:33 +01:00
passwd = gfx.passwd or ""
2011-01-07 15:14:33 +01:00
show_text("password", passwd)
show_text("port", port)
show_text("address", address)
show_row("keymap", "-box")
self.set_combo_label("gfx-keymap", keymap)
settype = gtype
if is_spice:
tlsport = port_to_string(gfx.tlsPort)
show_text("tlsport", tlsport)
if is_sdl:
set_title(_("Local SDL Window"))
display = gfx.display or _("Unknown")
xauth = gfx.xauth or _("Unknown")
show_text("display", display)
show_text("xauth", xauth)
if is_other:
2011-03-24 10:09:42 -04:00
settype = gfx.pretty_type_simple(gtype)
if settype:
show_row("type", "-box")
self.set_combo_label("gfx-type", gtype, label=settype)
def refresh_sound_page(self):
sound = self.get_hw_selection(HW_LIST_COL_DEVICE)
if not sound:
return
self.set_combo_label("sound-model", sound.model)
def refresh_char_page(self):
chardev = self.get_hw_selection(HW_LIST_COL_DEVICE)
if not chardev:
return
show_target_type = not (chardev.dev_type in
[chardev.DEV_SERIAL, chardev.DEV_PARALLEL])
def show_ui(param, val=None):
widgetname = "char-" + param.replace("_", "-")
labelname = widgetname + "-label"
doshow = chardev.supports_property(param)
# Exception: don't show target type for serial/parallel
if (param == "target_type" and not show_target_type):
doshow = False
if not val and doshow:
val = getattr(chardev, param)
self.window.get_widget(widgetname).set_property("visible", doshow)
self.window.get_widget(labelname).set_property("visible", doshow)
self.window.get_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.char_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 not show_target_type:
typelabel += " %s" % (int(target_port) + 1)
if primary:
typelabel += " (%s)" % _("Primary Console")
typelabel = "<b>%s</b>" % typelabel
self.window.get_widget("char-type").set_markup(typelabel)
self.window.get_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
devtype = hostdev.type
pretty_name = None
nodedev = lookup_nodedev(self.vm.get_connection(), hostdev)
if nodedev:
pretty_name = nodedev.pretty_name()
if not pretty_name:
pretty_name = build_hostdev_label(hostdev)[0] or "-"
devlabel = "<b>Physical %s Device</b>" % devtype.upper()
self.window.get_widget("hostdev-title").set_markup(devlabel)
self.window.get_widget("hostdev-source").set_text(pretty_name)
def refresh_video_page(self):
vid = self.get_hw_selection(HW_LIST_COL_DEVICE)
if not vid:
return
model = vid.model_type
ram = vid.vram
heads = vid.heads
try:
ramlabel = ram and "%d MB" % (int(ram) / 1024) or "-"
except:
ramlabel = "-"
self.window.get_widget("video-ram").set_text(ramlabel)
self.window.get_widget("video-heads").set_text(heads and heads or "-")
self.set_combo_label("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
self.set_combo_label("watchdog-model", model)
self.set_combo_label("watchdog-action", action)
def refresh_controller_page(self):
dev = self.get_hw_selection(HW_LIST_COL_DEVICE)
if not dev:
return
type_label = virtinst.VirtualController.pretty_type(dev.type)
self.window.get_widget("controller-type").set_text(type_label)
def refresh_filesystem_page(self):
dev = self.get_hw_selection(HW_LIST_COL_DEVICE)
if not dev:
return
self.window.get_widget("fs-type").set_text(dev.type)
self.window.get_widget("fs-mode").set_text(dev.mode)
self.window.get_widget("fs-source").set_text(dev.source)
self.window.get_widget("fs-target").set_text(dev.target)
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
2010-12-11 22:00:52 -05:00
autostart_chk = self.window.get_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())
self.window.get_widget("boot-order-align").set_property("visible",
show_boot)
self.window.get_widget("boot-kernel-align").set_property("visible",
show_kernel)
self.window.get_widget("boot-init-align").set_property("visible",
show_init)
# Kernel/initrd boot
kernel, initrd, args = self.vm.get_boot_kernel_info()
expand = bool(kernel or initrd or args)
self.window.get_widget("boot-kernel").set_text(kernel or "")
self.window.get_widget("boot-kernel-initrd").set_text(initrd or "")
self.window.get_widget("boot-kernel-args").set_text(args or "")
if expand:
self.window.get_widget("boot-kernel-expander").set_expanded(True)
# <init> populate
init = self.vm.get_init()
self.window.get_widget("boot-init-path").set_text(init or "")
# Boot menu populate
menu = self.vm.get_boot_menu() or False
self.window.get_widget("boot-menu").set_active(menu)
self.repopulate_boot_list()
############################
# Hardware list population #
############################
def populate_disk_bus_combo(self, devtype, no_default):
buslist = self.window.get_widget("disk-bus-combo")
busmodel = buslist.get_model()
busmodel.clear()
buses = []
if devtype == virtinst.VirtualDisk.DEVICE_FLOPPY:
buses.append(["fdc", "Floppy"])
elif devtype == virtinst.VirtualDisk.DEVICE_CDROM:
buses.append(["ide", "IDE"])
buses.append(["scsi", "SCSI"])
else:
if self.vm.is_hvm():
buses.append(["ide", "IDE"])
buses.append(["scsi", "SCSI"])
buses.append(["usb", "USB"])
if self.vm.get_hv_type() == "kvm":
buses.append(["virtio", "Virtio"])
if self.vm.get_connection().is_xen():
buses.append(["xen", "Xen"])
for row in buses:
busmodel.append(row)
if not no_default:
busmodel.append([None, "default"])
def populate_hw_list(self):
hw_list_model = self.window.get_widget("hw-list").get_model()
hw_list_model.clear()
def add_hw_list_option(title, page_id, data, icon_name):
hw_list_model.append([title, icon_name,
gtk.ICON_SIZE_LARGE_TOOLBAR,
page_id, data])
add_hw_list_option("Overview", HW_LIST_TYPE_GENERAL, [], "computer")
if not self.is_customize_dialog:
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):
2007-04-12 18:54:33 -04:00
hw_list = self.window.get_widget("hw-list")
hw_list_model = hw_list.get_model()
currentDevices = []
def dev_cmp(origdev, newdev):
if not origdev:
return False
if origdev == newdev:
return True
if not origdev.get_xml_node_path():
return False
return origdev.get_xml_node_path() == newdev.get_xml_node_path()
def add_hw_list_option(idx, name, page_id, info, icon_name):
hw_list_model.insert(idx, [name, icon_name,
gtk.ICON_SIZE_LARGE_TOOLBAR,
page_id, info])
def update_hwlist(hwtype, info, name, icon_name):
"""
See if passed hw is already in list, and if so, update info.
If not in list, add it!
"""
currentDevices.append(info)
insertAt = 0
for row in hw_list_model:
rowdev = row[HW_LIST_COL_DEVICE]
if dev_cmp(rowdev, info):
# Update existing HW info
row[HW_LIST_COL_DEVICE] = info
row[HW_LIST_COL_LABEL] = name
row[HW_LIST_COL_ICON_NAME] = icon_name
return
if row[HW_LIST_COL_TYPE] <= hwtype:
insertAt += 1
# Add the new HW row
add_hw_list_option(insertAt, name, hwtype, info, icon_name)
# Populate list of disks
for disk in self.vm.get_disk_devices():
devtype = disk.device
bus = disk.bus
idx = disk.disk_bus_index
icon = "drive-harddisk"
if devtype == "cdrom":
icon = "media-optical"
elif devtype == "floppy":
icon = "media-floppy"
label = prettyify_disk(devtype, bus, idx)
update_hwlist(HW_LIST_TYPE_DISK, disk, label, icon)
# Populate list of NICs
for net in self.vm.get_network_devices():
mac = net.macaddr
update_hwlist(HW_LIST_TYPE_NIC, net,
"NIC %s" % mac[-9:], "network-idle")
# Populate list of input devices
for inp in self.vm.get_input_devices():
inptype = inp.type
icon = "input-mouse"
if inptype == "tablet":
label = _("Tablet")
icon = "input-tablet"
elif inptype == "mouse":
label = _("Mouse")
else:
label = _("Input")
update_hwlist(HW_LIST_TYPE_INPUT, inp, label, icon)
# Populate list of graphics devices
for gfx in self.vm.get_graphics_devices():
update_hwlist(HW_LIST_TYPE_GRAPHICS, gfx,
2011-03-24 10:09:42 -04:00
_("Display %s") % gfx.pretty_type_simple(gfx.type),
"video-display")
# Populate list of sound devices
for sound in self.vm.get_sound_devices():
update_hwlist(HW_LIST_TYPE_SOUND, sound,
_("Sound: %s" % sound.model), "audio-card")
# Populate list of char devices
for chardev in self.vm.get_char_devices():
devtype = chardev.virtual_device_type
port = chardev.target_port
label = devtype.capitalize()
if devtype not in ["console", "channel"]:
# Don't show port for console
label += " %s" % (int(port) + 1)
update_hwlist(HW_LIST_TYPE_CHAR, chardev, label,
"device_serial")
# Populate host devices
for hostdev in self.vm.get_hostdev_devices():
devtype = hostdev.type
label = build_hostdev_label(hostdev)[1]
if devtype == "usb":
icon = "device_usb"
else:
icon = "device_pci"
update_hwlist(HW_LIST_TYPE_HOSTDEV, hostdev, label, icon)
# Populate video devices
for vid in self.vm.get_video_devices():
update_hwlist(HW_LIST_TYPE_VIDEO, vid, _("Video"), "video-display")
# Populate watchdog devices
for watch in self.vm.get_watchdog_devices():
update_hwlist(HW_LIST_TYPE_WATCHDOG, watch, _("Watchdog"),
"device_pci")
# Populate controller devices
for cont in self.vm.get_controller_devices():
pretty_type = virtinst.VirtualController.pretty_type(cont.type)
update_hwlist(HW_LIST_TYPE_CONTROLLER, cont,
_("Controller %s") % pretty_type,
"device_pci")
# Populate filesystem devices
for fs in self.vm.get_filesystem_devices():
target = fs.target[:8]
update_hwlist(HW_LIST_TYPE_FILESYSTEM, fs,
_("Filesystem %s") % target,
gtk.STOCK_DIRECTORY)
2007-04-12 18:54:33 -04:00
devs = range(len(hw_list_model))
devs.reverse()
for i in devs:
2008-11-18 17:01:22 -05:00
_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 not olddev or olddev in currentDevices:
continue
hw_list_model.remove(_iter)
2007-04-12 18:54:33 -04:00
def repopulate_boot_list(self, bootdevs=None, dev_select=None):
boot_list = self.window.get_widget("config-boot-list")
boot_model = boot_list.get_model()
old_order = map(lambda x: x[BOOT_DEV_TYPE], boot_model)
boot_model.clear()
if bootdevs == None:
bootdevs = self.vm.get_boot_device()
boot_rows = {
"hd" : ["hd", "Hard Disk", "drive-harddisk", False],
"cdrom" : ["cdrom", "CDROM", "media-optical", False],
"network" : ["network", "Network (PXE)", "network-idle", False],
"fd" : ["fd", "Floppy", "media-floppy", False],
}
for dev in bootdevs:
foundrow = None
for key, row in boot_rows.items():
if key == dev:
foundrow = row
del(boot_rows[key])
break
if not foundrow:
# Some boot device listed that we don't know about.
foundrow = [dev, "Boot type '%s'" % dev,
"drive-harddisk", True]
foundrow[BOOT_ACTIVE] = True
boot_model.append(foundrow)
# Append all remaining boot_rows that aren't enabled
for dev in old_order:
if dev in boot_rows:
boot_model.append(boot_rows[dev])
del(boot_rows[dev])
for row in boot_rows.values():
boot_model.append(row)
boot_list.set_model(boot_model)
selection = boot_list.get_selection()
if dev_select:
idx = 0
for row in boot_model:
if row[BOOT_DEV_TYPE] == dev_select:
break
idx += 1
boot_list.get_selection().select_path(str(idx))
elif not selection.get_selected()[1]:
# Set a default selection
selection.select_path("0")
2010-12-08 17:26:19 -05:00
vmmGObjectUI.type_register(vmmDetails)
vmmDetails.signal_new(vmmDetails, "action-save-domain", [str, str])
vmmDetails.signal_new(vmmDetails, "action-destroy-domain", [str, str])
vmmDetails.signal_new(vmmDetails, "action-suspend-domain", [str, str])
vmmDetails.signal_new(vmmDetails, "action-resume-domain", [str, str])
vmmDetails.signal_new(vmmDetails, "action-run-domain", [str, str])
vmmDetails.signal_new(vmmDetails, "action-shutdown-domain", [str, str])
vmmDetails.signal_new(vmmDetails, "action-reboot-domain", [str, str])
vmmDetails.signal_new(vmmDetails, "action-show-help", [str])
vmmDetails.signal_new(vmmDetails, "action-exit-app", [])
vmmDetails.signal_new(vmmDetails, "action-view-manager", [])
vmmDetails.signal_new(vmmDetails, "action-migrate-domain", [str, str])
vmmDetails.signal_new(vmmDetails, "action-clone-domain", [str, str])
vmmDetails.signal_new(vmmDetails, "details-closed", [])
vmmDetails.signal_new(vmmDetails, "details-opened", [])