virt-manager/virtManager/manager.py
Cole Robinson f107e39989 Switch to more traditional logging structure
Init a shared log instance in virtinst/logger.py, and use that
throughout the code base, so we aren't calling directly into
'logging'. This helps protect our logging output from being
cluttered with other library output, as happens with some
'requests' usage
2019-06-17 00:12:31 -04:00

1021 lines
35 KiB
Python

# Copyright (C) 2006-2008, 2013-2014 Red Hat, Inc.
# Copyright (C) 2006 Daniel P. Berrange <berrange@redhat.com>
#
# This work is licensed under the GNU GPLv2 or later.
# See the COPYING file in the top-level directory.
from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GdkPixbuf
from virtinst import log
from virtinst import xmlutil
from . import vmmenu
from . import uiutil
from .baseclass import vmmGObjectUI
from .connmanager import vmmConnectionManager
from .engine import vmmEngine
from .graphwidgets import CellRendererSparkline
# Number of data points for performance graphs
GRAPH_LEN = 40
# fields in the tree model data set
(ROW_HANDLE,
ROW_SORT_KEY,
ROW_MARKUP,
ROW_STATUS_ICON,
ROW_HINT,
ROW_IS_CONN,
ROW_IS_CONN_CONNECTED,
ROW_IS_VM,
ROW_IS_VM_RUNNING,
ROW_COLOR,
ROW_INSPECTION_OS_ICON) = range(11)
# Columns in the tree view
(COL_NAME,
COL_GUEST_CPU,
COL_HOST_CPU,
COL_MEM,
COL_DISK,
COL_NETWORK) = range(6)
def _style_get_prop(widget, propname):
value = GObject.Value()
value.init(GObject.TYPE_INT)
widget.style_get_property(propname, value)
return value.get_int()
def _cmp(a, b):
return ((a > b) - (a < b))
def _get_inspection_icon_pixbuf(vm, w, h):
# libguestfs gives us the PNG data as a string.
png_data = vm.inspection.icon
if png_data is None:
return None
try:
pb = GdkPixbuf.PixbufLoader()
pb.set_size(w, h)
pb.write(png_data)
pb.close()
return pb.get_pixbuf()
except Exception:
log.exception("Error loading inspection icon data")
vm.inspection.icon = None
return None
class vmmManager(vmmGObjectUI):
@classmethod
def get_instance(cls, parentobj):
try:
if not cls._instance:
cls._instance = vmmManager()
return cls._instance
except Exception as e:
if not parentobj:
raise
parentobj.err.show_err(
_("Error launching manager: %s") % str(e))
def __init__(self):
vmmGObjectUI.__init__(self, "manager.ui", "vmm-manager")
self._cleanup_on_app_close()
w, h = self.config.get_manager_window_size()
self.topwin.set_default_size(w or 550, h or 550)
self.prev_position = None
self._window_size = None
self.vmmenu = vmmenu.VMActionMenu(self, self.current_vm)
self.shutdownmenu = vmmenu.VMShutdownMenu(self, self.current_vm)
self.connmenu = Gtk.Menu()
self.connmenu_items = {}
self.builder.connect_signals({
"on_menu_view_guest_cpu_usage_activate":
self.toggle_stats_visible_guest_cpu,
"on_menu_view_host_cpu_usage_activate":
self.toggle_stats_visible_host_cpu,
"on_menu_view_memory_usage_activate":
self.toggle_stats_visible_memory_usage,
"on_menu_view_disk_io_activate":
self.toggle_stats_visible_disk,
"on_menu_view_network_traffic_activate":
self.toggle_stats_visible_network,
"on_vm_manager_delete_event": self.close,
"on_vmm_manager_configure_event": self.window_resized,
"on_menu_file_add_connection_activate": self.open_newconn,
"on_menu_new_vm_activate": self.new_vm,
"on_menu_file_quit_activate": self.exit_app,
"on_menu_file_close_activate": self.close,
"on_vmm_close_clicked": self.close,
"on_vm_open_clicked": self.show_vm,
"on_vm_run_clicked": self.start_vm,
"on_vm_new_clicked": self.new_vm,
"on_vm_shutdown_clicked": self.poweroff_vm,
"on_vm_pause_clicked": self.pause_vm_button,
"on_menu_edit_details_activate": self.show_vm,
"on_menu_edit_delete_activate": self.do_delete,
"on_menu_host_details_activate": self.show_host,
"on_vm_list_row_activated": self.row_activated,
"on_vm_list_button_press_event": self.popup_vm_menu_button,
"on_vm_list_key_press_event": self.popup_vm_menu_key,
"on_menu_edit_preferences_activate": self.show_preferences,
"on_menu_help_about_activate": self.show_about,
})
# There seem to be ref counting issues with calling
# list.get_column, so avoid it
self.diskcol = None
self.netcol = None
self.memcol = None
self.guestcpucol = None
self.hostcpucol = None
self.spacer_txt = None
self.init_vmlist()
self.init_stats()
self.init_toolbar()
self.init_context_menus()
self.update_current_selection()
self.widget("vm-list").get_selection().connect(
"changed", self.update_current_selection)
self.max_disk_rate = 10.0
self.max_net_rate = 10.0
# Initialize stat polling columns based on global polling
# preferences (we want signal handlers for this)
self.enable_polling(COL_GUEST_CPU)
self.enable_polling(COL_DISK)
self.enable_polling(COL_NETWORK)
self.enable_polling(COL_MEM)
connmanager = vmmConnectionManager.get_instance()
connmanager.connect("conn-added", self._conn_added)
connmanager.connect("conn-removed", self._conn_removed)
for conn in connmanager.conns.values():
self._conn_added(connmanager, conn)
##################
# Common methods #
##################
def show(self):
vis = self.is_visible()
self.topwin.present()
if vis:
return
log.debug("Showing manager")
if self.prev_position:
self.topwin.move(*self.prev_position)
self.prev_position = None
vmmEngine.get_instance().increment_window_counter()
def close(self, src_ignore=None, src2_ignore=None):
if not self.is_visible():
return
log.debug("Closing manager")
self.prev_position = self.topwin.get_position()
self.topwin.hide()
vmmEngine.get_instance().decrement_window_counter()
return 1
def _cleanup(self):
self.diskcol = None
self.guestcpucol = None
self.memcol = None
self.hostcpucol = None
self.netcol = None
self.shutdownmenu.destroy()
self.shutdownmenu = None
self.vmmenu.destroy()
self.vmmenu = None
self.connmenu.destroy()
self.connmenu = None
self.connmenu_items = None
if self._window_size:
self.config.set_manager_window_size(*self._window_size)
def set_startup_error(self, msg):
self.widget("vm-notebook").set_current_page(1)
self.widget("startup-error-label").set_text(msg)
################
# Init methods #
################
def init_stats(self):
self.add_gsettings_handle(
self.config.on_vmlist_guest_cpu_usage_visible_changed(
self.toggle_guest_cpu_usage_visible_widget))
self.add_gsettings_handle(
self.config.on_vmlist_host_cpu_usage_visible_changed(
self.toggle_host_cpu_usage_visible_widget))
self.add_gsettings_handle(
self.config.on_vmlist_memory_usage_visible_changed(
self.toggle_memory_usage_visible_widget))
self.add_gsettings_handle(
self.config.on_vmlist_disk_io_visible_changed(
self.toggle_disk_io_visible_widget))
self.add_gsettings_handle(
self.config.on_vmlist_network_traffic_visible_changed(
self.toggle_network_traffic_visible_widget))
# Register callbacks with the global stats enable/disable values
# that disable the associated vmlist widgets if reporting is disabled
self.add_gsettings_handle(
self.config.on_stats_enable_cpu_poll_changed(
self.enable_polling, COL_GUEST_CPU))
self.add_gsettings_handle(
self.config.on_stats_enable_disk_poll_changed(
self.enable_polling, COL_DISK))
self.add_gsettings_handle(
self.config.on_stats_enable_net_poll_changed(
self.enable_polling, COL_NETWORK))
self.add_gsettings_handle(
self.config.on_stats_enable_memory_poll_changed(
self.enable_polling, COL_MEM))
self.toggle_guest_cpu_usage_visible_widget()
self.toggle_host_cpu_usage_visible_widget()
self.toggle_memory_usage_visible_widget()
self.toggle_disk_io_visible_widget()
self.toggle_network_traffic_visible_widget()
def init_toolbar(self):
self.widget("vm-new").set_icon_name("vm_new")
self.widget("vm-open").set_icon_name("icon_console")
self.widget("vm-shutdown").set_icon_name("system-shutdown")
self.widget("vm-shutdown").set_menu(self.shutdownmenu)
tool = self.widget("vm-toolbar")
tool.set_property("icon-size", Gtk.IconSize.LARGE_TOOLBAR)
for c in tool.get_children():
c.set_homogeneous(False)
def init_context_menus(self):
def add_to_menu(idx, text, icon, cb):
if text[0:3] == 'gtk':
item = Gtk.ImageMenuItem.new_from_stock(text, None)
else:
item = Gtk.ImageMenuItem.new_with_mnemonic(text)
if icon:
item.set_image(icon)
if cb:
item.connect("activate", cb)
item.get_accessible().set_name("conn-%s" % idx)
self.connmenu.add(item)
self.connmenu_items[idx] = item
# Build connection context menu
add_to_menu("create", Gtk.STOCK_NEW, None, self.new_vm)
add_to_menu("connect", Gtk.STOCK_CONNECT, None, self.open_conn)
add_to_menu("disconnect", Gtk.STOCK_DISCONNECT, None,
self.close_conn)
self.connmenu.add(Gtk.SeparatorMenuItem())
add_to_menu("delete", Gtk.STOCK_DELETE, None, self.do_delete)
self.connmenu.add(Gtk.SeparatorMenuItem())
add_to_menu("details", _("D_etails"), None, self.show_host)
self.connmenu.show_all()
def init_vmlist(self):
vmlist = self.widget("vm-list")
self.widget("vm-notebook").set_show_tabs(False)
rowtypes = []
rowtypes.insert(ROW_HANDLE, object) # backing object
rowtypes.insert(ROW_SORT_KEY, str) # object name
rowtypes.insert(ROW_MARKUP, str) # row markup text
rowtypes.insert(ROW_STATUS_ICON, str) # status icon name
rowtypes.insert(ROW_HINT, str) # row tooltip
rowtypes.insert(ROW_IS_CONN, bool) # if object is a connection
rowtypes.insert(ROW_IS_CONN_CONNECTED, bool) # if conn is connected
rowtypes.insert(ROW_IS_VM, bool) # if row is VM
rowtypes.insert(ROW_IS_VM_RUNNING, bool) # if VM is running
rowtypes.insert(ROW_COLOR, str) # row markup color string
rowtypes.insert(ROW_INSPECTION_OS_ICON, GdkPixbuf.Pixbuf) # OS icon
model = Gtk.TreeStore(*rowtypes)
vmlist.set_model(model)
vmlist.set_tooltip_column(ROW_HINT)
vmlist.set_headers_visible(True)
vmlist.set_level_indentation(
-(_style_get_prop(vmlist, "expander-size") + 3))
nameCol = Gtk.TreeViewColumn(_("Name"))
nameCol.set_expand(True)
nameCol.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
nameCol.set_spacing(6)
nameCol.set_sort_column_id(COL_NAME)
vmlist.append_column(nameCol)
status_icon = Gtk.CellRendererPixbuf()
status_icon.set_property("stock-size", Gtk.IconSize.DND)
nameCol.pack_start(status_icon, False)
nameCol.add_attribute(status_icon, 'icon-name', ROW_STATUS_ICON)
nameCol.add_attribute(status_icon, 'visible', ROW_IS_VM)
inspection_os_icon = Gtk.CellRendererPixbuf()
nameCol.pack_start(inspection_os_icon, False)
nameCol.add_attribute(inspection_os_icon, 'pixbuf',
ROW_INSPECTION_OS_ICON)
nameCol.add_attribute(inspection_os_icon, 'visible', ROW_IS_VM)
name_txt = Gtk.CellRendererText()
nameCol.pack_start(name_txt, True)
nameCol.add_attribute(name_txt, 'markup', ROW_MARKUP)
nameCol.add_attribute(name_txt, 'foreground', ROW_COLOR)
self.spacer_txt = Gtk.CellRendererText()
self.spacer_txt.set_property("ypad", 4)
self.spacer_txt.set_property("visible", False)
nameCol.pack_end(self.spacer_txt, False)
def make_stats_column(title, colnum):
col = Gtk.TreeViewColumn(title)
col.set_min_width(140)
txt = Gtk.CellRendererText()
txt.set_property("ypad", 4)
col.pack_start(txt, True)
col.add_attribute(txt, 'visible', ROW_IS_CONN)
img = CellRendererSparkline()
img.set_property("xpad", 6)
img.set_property("ypad", 12)
img.set_property("reversed", True)
col.pack_start(img, True)
col.add_attribute(img, 'visible', ROW_IS_VM)
col.set_sort_column_id(colnum)
vmlist.append_column(col)
return col
self.guestcpucol = make_stats_column(_("CPU usage"), COL_GUEST_CPU)
self.hostcpucol = make_stats_column(_("Host CPU usage"), COL_HOST_CPU)
self.memcol = make_stats_column(_("Memory usage"), COL_MEM)
self.diskcol = make_stats_column(_("Disk I/O"), COL_DISK)
self.netcol = make_stats_column(_("Network I/O"), COL_NETWORK)
model.set_sort_func(COL_NAME, self.vmlist_name_sorter)
model.set_sort_func(COL_GUEST_CPU, self.vmlist_guest_cpu_usage_sorter)
model.set_sort_func(COL_HOST_CPU, self.vmlist_host_cpu_usage_sorter)
model.set_sort_func(COL_MEM, self.vmlist_memory_usage_sorter)
model.set_sort_func(COL_DISK, self.vmlist_disk_io_sorter)
model.set_sort_func(COL_NETWORK, self.vmlist_network_usage_sorter)
model.set_sort_column_id(COL_NAME, Gtk.SortType.ASCENDING)
##################
# Helper methods #
##################
@property
def model(self):
return self.widget("vm-list").get_model()
def current_row(self):
return uiutil.get_list_selected_row(self.widget("vm-list"))
def current_vm(self):
row = self.current_row()
if not row or row[ROW_IS_CONN]:
return None
return row[ROW_HANDLE]
def current_conn(self):
row = self.current_row()
if not row:
return None
handle = row[ROW_HANDLE]
if row[ROW_IS_CONN]:
return handle
return handle.conn
def get_row(self, conn_or_vm):
def _walk(model, rowiter, obj):
while rowiter:
row = model[rowiter]
if row[ROW_HANDLE] == obj:
return row
if model.iter_has_child(rowiter):
ret = _walk(model, model.iter_nth_child(rowiter, 0), obj)
if ret:
return ret
rowiter = model.iter_next(rowiter)
if not len(self.model):
return None
return _walk(self.model, self.model.get_iter_first(), conn_or_vm)
####################
# Action listeners #
####################
def window_resized(self, ignore, ignore2):
if not self.is_visible():
return
self._window_size = self.topwin.get_size()
def exit_app(self, src_ignore=None, src2_ignore=None):
vmmEngine.get_instance().exit_app()
def open_newconn(self, _src):
from .connect import vmmConnect
vmmConnect.get_instance(self).show(self.topwin)
def new_vm(self, _src):
from .create import vmmCreate
conn = self.current_conn()
vmmCreate.show_instance(self, conn and conn.get_uri() or None)
def show_about(self, _src):
from .about import vmmAbout
vmmAbout.show_instance(self)
def show_preferences(self, src_ignore):
from .preferences import vmmPreferences
vmmPreferences.show_instance(self)
def show_host(self, _src):
from .host import vmmHost
conn = self.current_conn()
vmmHost.show_instance(self, conn)
def show_vm(self, _src):
vmmenu.VMActionUI.show(self, self.current_vm())
def _conn_open_completed(self, _conn, ConnectError):
if ConnectError:
msg, details, title = ConnectError
self.err.show_err(msg, details, title)
def row_activated(self, _src, *args):
ignore = args
conn = self.current_conn()
vm = self.current_vm()
if conn is None:
return
if vm:
self.show_vm(_src)
elif conn.is_disconnected():
conn.connect_once("open-completed", self._conn_open_completed)
conn.open()
else:
self.show_host(_src)
def do_delete(self, ignore=None):
conn = self.current_conn()
vm = self.current_vm()
if vm is None:
self._do_delete_conn(conn)
else:
vmmenu.VMActionUI.delete(self, vm)
def _do_delete_conn(self, conn):
result = self.err.yes_no(_("This will remove the connection:\n\n%s\n\n"
"Are you sure?") % conn.get_uri())
if not result:
return
vmmConnectionManager.get_instance().remove_conn(conn.get_uri())
def set_pause_state(self, state):
src = self.widget("vm-pause")
try:
src.handler_block_by_func(self.pause_vm_button)
src.set_active(state)
finally:
src.handler_unblock_by_func(self.pause_vm_button)
def pause_vm_button(self, src):
do_pause = src.get_active()
# Set button state back to original value: just let the status
# update function fix things for us
self.set_pause_state(not do_pause)
if do_pause:
vmmenu.VMActionUI.suspend(self, self.current_vm())
else:
vmmenu.VMActionUI.resume(self, self.current_vm())
def start_vm(self, ignore):
vmmenu.VMActionUI.run(self, self.current_vm())
def poweroff_vm(self, _src):
vmmenu.VMActionUI.shutdown(self, self.current_vm())
def close_conn(self, ignore):
conn = self.current_conn()
if not conn.is_disconnected():
conn.close()
def open_conn(self, ignore=None):
conn = self.current_conn()
if conn.is_disconnected():
conn.open()
return True
####################################
# VM add/remove management methods #
####################################
def vm_added(self, conn, connkey):
vm = conn.get_vm(connkey)
if not vm:
return
vm_row = self._build_row(None, vm)
conn_row = self.get_row(conn)
self.model.append(conn_row.iter, vm_row)
vm.connect("state-changed", self.vm_changed)
vm.connect("resources-sampled", self.vm_row_updated)
vm.connect("inspection-changed", self.vm_inspection_changed)
# Expand a connection when adding a vm to it
self.widget("vm-list").expand_row(conn_row.path, False)
def vm_removed(self, conn, connkey):
parent = self.get_row(conn).iter
for rowidx in range(self.model.iter_n_children(parent)):
rowiter = self.model.iter_nth_child(parent, rowidx)
vm = self.model[rowiter][ROW_HANDLE]
if vm.get_connkey() == connkey:
self.model.remove(rowiter)
break
def _build_conn_hint(self, conn):
hint = conn.get_uri()
if conn.is_disconnected():
hint += " (%s)" % _("Double click to connect")
return hint
def _build_conn_markup(self, conn, name):
name = xmlutil.xml_escape(name)
text = name
if conn.is_disconnected():
text += " - " + _("Not Connected")
elif conn.is_connecting():
text += " - " + _("Connecting...")
markup = "<span size='smaller'>%s</span>" % text
return markup
def _build_conn_color(self, conn):
color = "#000000"
if conn.is_disconnected():
color = "#5b5b5b"
return color
def _build_vm_markup(self, name, status):
domtext = ("<span size='smaller' weight='bold'>%s</span>" %
xmlutil.xml_escape(name))
statetext = "<span size='smaller'>%s</span>" % status
return domtext + "\n" + statetext
def _build_row(self, conn, vm):
if conn:
name = conn.get_pretty_desc()
markup = self._build_conn_markup(conn, name)
status = ("<span size='smaller'>%s</span>" %
conn.get_state_text())
status_icon = None
hint = self._build_conn_hint(conn)
color = self._build_conn_color(conn)
os_icon = None
else:
name = vm.get_name_or_title()
status = vm.run_status()
markup = self._build_vm_markup(name, status)
status_icon = vm.run_status_icon_name()
hint = vm.get_description()
color = None
os_icon = _get_inspection_icon_pixbuf(vm, 16, 16)
row = []
row.insert(ROW_HANDLE, conn or vm)
row.insert(ROW_SORT_KEY, name)
row.insert(ROW_MARKUP, markup)
row.insert(ROW_STATUS_ICON, status_icon)
row.insert(ROW_HINT, xmlutil.xml_escape(hint))
row.insert(ROW_IS_CONN, bool(conn))
row.insert(ROW_IS_CONN_CONNECTED,
bool(conn) and not conn.is_disconnected())
row.insert(ROW_IS_VM, bool(vm))
row.insert(ROW_IS_VM_RUNNING, bool(vm) and vm.is_active())
row.insert(ROW_COLOR, color)
row.insert(ROW_INSPECTION_OS_ICON, os_icon)
return row
def _conn_added(self, _src, conn):
# Make sure error page isn't showing
self.widget("vm-notebook").set_current_page(0)
if self.get_row(conn):
return
conn_row = self._build_row(conn, None)
self.model.append(None, conn_row)
conn.connect("vm-added", self.vm_added)
conn.connect("vm-removed", self.vm_removed)
conn.connect("resources-sampled", self.conn_row_updated)
conn.connect("state-changed", self.conn_state_changed)
for vm in conn.list_vms():
self.vm_added(conn, vm.get_connkey())
def _conn_removed(self, _src, uri):
conn_row = None
for row in self.model:
if row[ROW_IS_CONN] and row[ROW_HANDLE].get_uri() == uri:
conn_row = row
break
if conn_row is None:
return
child = self.model.iter_children(conn_row.iter)
while child is not None:
self.model.remove(child)
child = self.model.iter_children(conn_row.iter)
self.model.remove(conn_row.iter)
#############################
# State/UI updating methods #
#############################
def vm_row_updated(self, vm):
row = self.get_row(vm)
if row is None:
return
self.model.row_changed(row.path, row.iter)
def vm_changed(self, vm):
row = self.get_row(vm)
if row is None:
return
try:
if vm == self.current_vm():
self.update_current_selection()
name = vm.get_name_or_title()
status = vm.run_status()
row[ROW_SORT_KEY] = name
row[ROW_STATUS_ICON] = vm.run_status_icon_name()
row[ROW_IS_VM_RUNNING] = vm.is_active()
row[ROW_MARKUP] = self._build_vm_markup(name, status)
desc = vm.get_description()
row[ROW_HINT] = xmlutil.xml_escape(desc)
except Exception as e:
if vm.conn.support.is_libvirt_error_no_domain(e):
return
raise
self.vm_row_updated(vm)
def vm_inspection_changed(self, vm):
row = self.get_row(vm)
if row is None:
return
new_icon = _get_inspection_icon_pixbuf(vm, 16, 16)
row[ROW_INSPECTION_OS_ICON] = new_icon
self.vm_row_updated(vm)
def set_initial_selection(self, uri):
"""
Select the passed URI in the UI. Called from engine.py via
cli --connect $URI
"""
sel = self.widget("vm-list").get_selection()
for row in self.model:
if not row[ROW_IS_CONN]:
continue
conn = row[ROW_HANDLE]
if conn.get_uri() == uri:
sel.select_iter(row.iter)
return
def conn_state_changed(self, conn):
row = self.get_row(conn)
row[ROW_SORT_KEY] = conn.get_pretty_desc()
row[ROW_MARKUP] = self._build_conn_markup(conn, row[ROW_SORT_KEY])
row[ROW_IS_CONN_CONNECTED] = not conn.is_disconnected()
row[ROW_COLOR] = self._build_conn_color(conn)
row[ROW_HINT] = self._build_conn_hint(conn)
if not conn.is_active():
child = self.model.iter_children(row.iter)
while child is not None:
self.model.remove(child)
child = self.model.iter_children(row.iter)
self.conn_row_updated(conn)
self.update_current_selection()
def conn_row_updated(self, conn):
row = self.get_row(conn)
self.max_disk_rate = max(self.max_disk_rate, conn.disk_io_max_rate())
self.max_net_rate = max(self.max_net_rate,
conn.network_traffic_max_rate())
self.model.row_changed(row.path, row.iter)
def change_run_text(self, can_restore):
if can_restore:
text = _("_Restore")
else:
text = _("_Run")
strip_text = text.replace("_", "")
self.vmmenu.change_run_text(text)
self.widget("vm-run").set_label(strip_text)
def update_current_selection(self, ignore=None):
vm = self.current_vm()
conn = self.current_conn()
show_open = bool(vm)
show_details = bool(vm)
host_details = bool(vm or conn)
can_delete = bool(vm or conn)
show_run = bool(vm and vm.is_runable())
is_paused = bool(vm and vm.is_paused())
if is_paused:
show_pause = bool(vm and vm.is_unpauseable())
else:
show_pause = bool(vm and vm.is_pauseable())
show_shutdown = bool(vm and vm.is_stoppable())
if vm and vm.managedsave_supported:
self.change_run_text(vm.has_managed_save())
self.widget("vm-open").set_sensitive(show_open)
self.widget("vm-run").set_sensitive(show_run)
self.widget("vm-shutdown").set_sensitive(show_shutdown)
self.widget("vm-shutdown").get_menu().update_widget_states(vm)
self.set_pause_state(is_paused)
self.widget("vm-pause").set_sensitive(show_pause)
if is_paused:
pauseTooltip = _("Resume the virtual machine")
else:
pauseTooltip = _("Pause the virtual machine")
self.widget("vm-pause").set_tooltip_text(pauseTooltip)
self.widget("menu_edit_delete").set_sensitive(can_delete)
self.widget("menu_edit_details").set_sensitive(show_details)
self.widget("menu_host_details").set_sensitive(host_details)
def popup_vm_menu_key(self, widget_ignore, event):
if Gdk.keyval_name(event.keyval) != "Menu":
return False
model, treeiter = self.widget("vm-list").get_selection().get_selected()
self.popup_vm_menu(model, treeiter, event)
return True
def popup_vm_menu_button(self, vmlist, event):
if event.button != 3:
return False
tup = vmlist.get_path_at_pos(int(event.x), int(event.y))
if tup is None:
return False
path = tup[0]
self.popup_vm_menu(self.model, self.model.get_iter(path), event)
return False
def popup_vm_menu(self, model, _iter, event):
if model.iter_parent(_iter) is not None:
# Popup the vm menu
vm = model[_iter][ROW_HANDLE]
self.vmmenu.update_widget_states(vm)
self.vmmenu.popup_at_pointer(event)
else:
# Pop up connection menu
conn = model[_iter][ROW_HANDLE]
disconn = conn.is_disconnected()
conning = conn.is_connecting()
self.connmenu_items["create"].set_sensitive(not disconn)
self.connmenu_items["disconnect"].set_sensitive(not (disconn or
conning))
self.connmenu_items["connect"].set_sensitive(disconn)
self.connmenu_items["delete"].set_sensitive(disconn)
self.connmenu.popup_at_pointer(event)
#################
# Stats methods #
#################
def vmlist_name_sorter(self, model, iter1, iter2, ignore):
key1 = str(model[iter1][ROW_SORT_KEY]).lower()
key2 = str(model[iter2][ROW_SORT_KEY]).lower()
return _cmp(key1, key2)
def vmlist_guest_cpu_usage_sorter(self, model, iter1, iter2, ignore):
obj1 = model[iter1][ROW_HANDLE]
obj2 = model[iter2][ROW_HANDLE]
return _cmp(obj1.guest_cpu_time_percentage(),
obj2.guest_cpu_time_percentage())
def vmlist_host_cpu_usage_sorter(self, model, iter1, iter2, ignore):
obj1 = model[iter1][ROW_HANDLE]
obj2 = model[iter2][ROW_HANDLE]
return _cmp(obj1.host_cpu_time_percentage(),
obj2.host_cpu_time_percentage())
def vmlist_memory_usage_sorter(self, model, iter1, iter2, ignore):
obj1 = model[iter1][ROW_HANDLE]
obj2 = model[iter2][ROW_HANDLE]
return _cmp(obj1.stats_memory(),
obj2.stats_memory())
def vmlist_disk_io_sorter(self, model, iter1, iter2, ignore):
obj1 = model[iter1][ROW_HANDLE]
obj2 = model[iter2][ROW_HANDLE]
return _cmp(obj1.disk_io_rate(), obj2.disk_io_rate())
def vmlist_network_usage_sorter(self, model, iter1, iter2, ignore):
obj1 = model[iter1][ROW_HANDLE]
obj2 = model[iter2][ROW_HANDLE]
return _cmp(obj1.network_traffic_rate(), obj2.network_traffic_rate())
def enable_polling(self, column):
# pylint: disable=redefined-variable-type
if column == COL_GUEST_CPU:
widgn = ["menu_view_stats_guest_cpu", "menu_view_stats_host_cpu"]
do_enable = self.config.get_stats_enable_cpu_poll()
if column == COL_DISK:
widgn = "menu_view_stats_disk"
do_enable = self.config.get_stats_enable_disk_poll()
elif column == COL_NETWORK:
widgn = "menu_view_stats_network"
do_enable = self.config.get_stats_enable_net_poll()
elif column == COL_MEM:
widgn = "menu_view_stats_memory"
do_enable = self.config.get_stats_enable_memory_poll()
for w in xmlutil.listify(widgn):
widget = self.widget(w)
tool_text = ""
if do_enable:
widget.set_sensitive(True)
else:
if widget.get_active():
widget.set_active(False)
widget.set_sensitive(False)
tool_text = _("Disabled in preferences dialog.")
widget.set_tooltip_text(tool_text)
def _toggle_graph_helper(self, do_show, col, datafunc, menu):
img = -1
for child in col.get_cells():
if isinstance(child, CellRendererSparkline):
img = child
datafunc = do_show and datafunc or None
col.set_cell_data_func(img, datafunc, None)
col.set_visible(do_show)
self.widget(menu).set_active(do_show)
any_visible = any([c.get_visible() for c in
[self.netcol, self.diskcol, self.memcol,
self.guestcpucol, self.hostcpucol]])
self.spacer_txt.set_property("visible", not any_visible)
def toggle_network_traffic_visible_widget(self):
self._toggle_graph_helper(
self.config.is_vmlist_network_traffic_visible(), self.netcol,
self.network_traffic_img, "menu_view_stats_network")
def toggle_disk_io_visible_widget(self):
self._toggle_graph_helper(
self.config.is_vmlist_disk_io_visible(), self.diskcol,
self.disk_io_img, "menu_view_stats_disk")
def toggle_memory_usage_visible_widget(self):
self._toggle_graph_helper(
self.config.is_vmlist_memory_usage_visible(), self.memcol,
self.memory_usage_img, "menu_view_stats_memory")
def toggle_guest_cpu_usage_visible_widget(self):
self._toggle_graph_helper(
self.config.is_vmlist_guest_cpu_usage_visible(), self.guestcpucol,
self.guest_cpu_usage_img, "menu_view_stats_guest_cpu")
def toggle_host_cpu_usage_visible_widget(self):
self._toggle_graph_helper(
self.config.is_vmlist_host_cpu_usage_visible(), self.hostcpucol,
self.host_cpu_usage_img, "menu_view_stats_host_cpu")
def toggle_stats_visible(self, src, stats_id):
visible = src.get_active()
set_stats = {
COL_GUEST_CPU: self.config.set_vmlist_guest_cpu_usage_visible,
COL_HOST_CPU: self.config.set_vmlist_host_cpu_usage_visible,
COL_MEM: self.config.set_vmlist_memory_usage_visible,
COL_DISK: self.config.set_vmlist_disk_io_visible,
COL_NETWORK: self.config.set_vmlist_network_traffic_visible,
}
set_stats[stats_id](visible)
def toggle_stats_visible_guest_cpu(self, src):
self.toggle_stats_visible(src, COL_GUEST_CPU)
def toggle_stats_visible_host_cpu(self, src):
self.toggle_stats_visible(src, COL_HOST_CPU)
def toggle_stats_visible_memory_usage(self, src):
self.toggle_stats_visible(src, COL_MEM)
def toggle_stats_visible_disk(self, src):
self.toggle_stats_visible(src, COL_DISK)
def toggle_stats_visible_network(self, src):
self.toggle_stats_visible(src, COL_NETWORK)
def guest_cpu_usage_img(self, column_ignore, cell, model, _iter, data):
obj = model[_iter][ROW_HANDLE]
if obj is None or not hasattr(obj, "conn"):
return
data = obj.guest_cpu_time_vector(GRAPH_LEN)
cell.set_property('data_array', data)
def host_cpu_usage_img(self, column_ignore, cell, model, _iter, data):
obj = model[_iter][ROW_HANDLE]
if obj is None or not hasattr(obj, "conn"):
return
data = obj.host_cpu_time_vector(GRAPH_LEN)
cell.set_property('data_array', data)
def memory_usage_img(self, column_ignore, cell, model, _iter, data):
obj = model[_iter][ROW_HANDLE]
if obj is None or not hasattr(obj, "conn"):
return
data = obj.stats_memory_vector(GRAPH_LEN)
cell.set_property('data_array', data)
def disk_io_img(self, column_ignore, cell, model, _iter, data):
obj = model[_iter][ROW_HANDLE]
if obj is None or not hasattr(obj, "conn"):
return
d1, d2 = obj.disk_io_vectors(GRAPH_LEN, self.max_disk_rate)
data = [(x + y) / 2 for x, y in zip(d1, d2)]
cell.set_property('data_array', data)
def network_traffic_img(self, column_ignore, cell, model, _iter, data):
obj = model[_iter][ROW_HANDLE]
if obj is None or not hasattr(obj, "conn"):
return
d1, d2 = obj.network_traffic_vectors(GRAPH_LEN, self.max_net_rate)
data = [(x + y) / 2 for x, y in zip(d1, d2)]
cell.set_property('data_array', data)