Added dialog for viewing host status & resource config

This commit is contained in:
Daniel P. Berrange 2007-03-27 19:52:00 -04:00
parent d06e495a04
commit 25a92026c4
8 changed files with 1868 additions and 36 deletions

View File

@ -36,6 +36,8 @@ class vmmConnection(gobject.GObject):
[str, str]),
"vm-removed": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
[str, str]),
"resources-sampled": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
[]),
"disconnected": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, [str])
}
@ -56,6 +58,7 @@ class vmmConnection(gobject.GObject):
self.nets = {}
self.vms = {}
self.activeUUIDs = []
self.record = []
def is_read_only(self):
return self.readOnly
@ -63,7 +66,7 @@ class vmmConnection(gobject.GObject):
def get_type(self):
return self.vmm.getType()
def get_name(self):
def get_hostname(self):
hostname = "localhost"
try:
(host, aliases, ipaddrs) = gethostbyaddr(gethostname())
@ -71,28 +74,27 @@ class vmmConnection(gobject.GObject):
except:
logging.warning("Unable to resolve local hostname for machine")
if self.get_type()[0:3] == "Xen" and self.uri == "xen" or self.uri == "Xen" or self.uri is None:
return hostname
if self.get_type() == "QEMU" and ( self.uri == "qemu:///session" or self.uri == "qemu://system"):
return hostname
try:
urlbits = urlparse(self.uri)
return urlbits.netloc
except:
return hostname
def get_name(self):
if self.get_type()[0:3] == "Xen":
if self.uri == "xen" or self.uri == "Xen" or self.uri is None:
return "Xen: " + hostname
else:
try:
urlbits = urlparse(self.uri)
return "Xen: " + urlbits.netloc
except:
return self.uri
return "Xen: " + self.get_hostname()
elif self.get_type() == "QEMU":
if self.uri == "qemu:///session":
return "QEMU session: " + hostname
elif self.uri == "qemu:///system":
return "QEMU system: " + hostname
return "QEMU session: " + self.get_hostname()
else:
try:
urlbits = urlparse(self.uri)
return "QEMU system: " + urlbits.netloc
except:
return self.uri
else:
return self.uri
return "QEMU system: " + self.get_hostname()
def get_uri(self):
return self.uri
@ -129,9 +131,20 @@ class vmmConnection(gobject.GObject):
return handle_id
def pretty_host_memory_size(self):
mem = self.host_memory_size()
if mem > (1024*1024):
return "%2.2f GB" % (mem/(1024.0*1024.0))
else:
return "%2.2f MB" % (mem/1024.0)
def host_memory_size(self):
return self.hostinfo[1]*1024
def host_architecture(self):
return self.hostinfo[0]
def host_active_processor_count(self):
return self.hostinfo[2]
@ -169,7 +182,7 @@ class vmmConnection(gobject.GObject):
# Now we can clear the list of actives from the last time through
self.activeUUIDs = []
newActiveIDs = self.vmm.listDomainsID()
newInactiveNames = []
try:
@ -283,8 +296,109 @@ class vmmConnection(gobject.GObject):
for uuid in updateVMs.keys():
self.vms[uuid].tick(now)
if not noStatsUpdate:
self.recalculate_stats(now)
return 1
def recalculate_stats(self, now):
expected = self.config.get_stats_history_length()
current = len(self.record)
if current > expected:
del self.record[expected:current]
mem = 0
cpuTime = 0
for uuid in self.vms:
vm = self.vms[uuid]
if vm.get_id() != -1:
cpuTime = cpuTime + vm.get_cputime()
mem = mem + vm.get_memory()
pcentCpuTime = 0
if len(self.record) > 0:
prevTimestamp = self.record[0]["timestamp"]
pcentCpuTime = (cpuTime) * 100.0 / ((now - prevTimestamp)*1000.0*1000.0*1000.0*self.host_active_processor_count())
# Due to timing diffs between getting wall time & getting
# the domain's time, its possible to go a tiny bit over
# 100% utilization. This freaks out users of the data, so
# we hard limit it.
if pcentCpuTime > 100.0:
pcentCpuTime = 100.0
# Enforce >= 0 just in case
if pcentCpuTime < 0.0:
pcentCpuTime = 0.0
pcentMem = mem * 100.0 / self.host_memory_size()
newStats = {
"timestamp": now,
"memory": mem,
"memoryPercent": pcentMem,
"cpuTime": cpuTime,
"cpuTimePercent": pcentCpuTime
}
self.record.insert(0, newStats)
self.emit("resources-sampled")
def cpu_time_vector(self):
vector = []
stats = self.record
for i in range(self.config.get_stats_history_length()+1):
if i < len(stats):
vector.append(stats[i]["cpuTimePercent"]/100.0)
else:
vector.append(0)
return vector
def cpu_time_vector_limit(self, limit):
cpudata = self.cpu_time_vector()
if len(cpudata) > limit:
cpudata = cpudata[0:limit]
return cpudata
def cpu_time_percentage(self):
if len(self.record) == 0:
return 0
return self.record[0]["cpuTimePercent"]
def current_memory(self):
if len(self.record) == 0:
return 0
return self.record[0]["memory"]
def pretty_current_memory(self):
mem = self.current_memory()
if mem > (1024*1024):
return "%2.2f GB" % (mem/(1024.0*1024.0))
else:
return "%2.2f MB" % (mem/1024.0)
def current_memory(self):
if len(self.record) == 0:
return 0
return self.record[0]["memory"]
def current_memory_percentage(self):
if len(self.record) == 0:
return 0
return self.record[0]["memoryPercent"]
def current_memory_vector(self):
vector = []
stats = self.record
for i in range(self.config.get_stats_history_length()+1):
if i < len(stats):
vector.append(stats[i]["memoryPercent"]/100.0)
else:
vector.append(0)
return vector
def uuidstr(self, rawuuid):
hex = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']
uuid = []

View File

@ -220,6 +220,11 @@ class vmmDomain(gobject.GObject):
return 0
return self.record[0]["currMemPercent"]
def get_cputime(self):
if len(self.record) == 0:
return 0
return self.record[0]["cpuTime"]
def get_memory_pretty(self):
mem = self.get_memory()
if mem > (1024*1024):

View File

@ -35,6 +35,7 @@ from virtManager.asyncjob import vmmAsyncJob
from virtManager.create import vmmCreate
from virtManager.serialcon import vmmSerialConsole
from virtManager.error import vmmErrorDialog
from virtManager.host import vmmHost
class vmmEngine:
def __init__(self, config):
@ -158,8 +159,9 @@ class vmmEngine:
for name in [ "windowDetails", "windowConsole", "windowSerialConsole" ]:
for window in conn[name].values():
ct += window.is_visible()
if conn["windowManager"]:
ct += conn["windowManager"].is_visible()
for name in [ "windowManager", "windowHost"]:
if conn[name] != None and conn[name].is_visible():
ct += 1
if self.windowCreate:
ct += self.windowCreate.is_visible()
return ct
@ -175,6 +177,8 @@ class vmmEngine:
self.show_about()
def _do_show_preferences(self, src):
self.show_preferences()
def _do_show_host(self, src, uri):
self.show_host(uri)
def _do_show_connect(self, src):
self.show_connect()
def _do_show_manager(self, src, uri):
@ -211,6 +215,15 @@ class vmmEngine:
self.windowPreferences.connect("action-show-help", self._do_show_help)
self.windowPreferences.show()
def show_host(self, uri):
con = self.get_connection(uri)
if self.connections[uri]["windowHost"] == None:
manager = vmmHost(self.get_config(), con)
manager.connect("action-show-help", self._do_show_help)
self.connections[uri]["windowHost"] = manager
self.connections[uri]["windowHost"].show()
def show_connect(self):
if self.windowConnect == None:
self.windowConnect = vmmConnect(self.get_config(), self)
@ -248,7 +261,7 @@ class vmmEngine:
def show_details_config(self, uri, uuid):
win = self.show_details(uri, uuid)
win.activate_config_page()
def show_details(self, uri, uuid):
con = self.get_connection(uri)
@ -277,6 +290,7 @@ class vmmEngine:
manager.connect("action-show-create", self._do_show_create)
manager.connect("action-show-help", self._do_show_help)
manager.connect("action-show-about", self._do_show_about)
manager.connect("action-show-host", self._do_show_host)
manager.connect("action-show-connect", self._do_show_connect)
self.connections[uri]["windowManager"] = manager
self.connections[uri]["windowManager"].show()
@ -295,6 +309,7 @@ class vmmEngine:
self.connections[uri] = {
"connection": vmmConnection(self.get_config(), uri, readOnly),
"windowManager": None,
"windowHost": None,
"windowDetails": {},
"windowConsole": {},
"windowSerialConsole": {},

150
src/virtManager/host.py Normal file
View File

@ -0,0 +1,150 @@
#
# Copyright (C) 2007 Red Hat, Inc.
# Copyright (C) 2007 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., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import gobject
import gtk
import gtk.glade
import libvirt
import sparkline
import logging
class vmmHost(gobject.GObject):
__gsignals__ = {
"action-show-help": (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, [str]),
}
def __init__(self, config, conn):
self.__gobject_init__()
self.window = gtk.glade.XML(config.get_glade_dir() + "/vmm-host.glade", "vmm-host", domain="virt-manager")
self.config = config
self.conn = conn
topwin = self.window.get_widget("vmm-host")
topwin.hide()
self.window.get_widget("overview-hostname").set_text(self.conn.get_hostname())
self.window.get_widget("overview-hypervisor").set_text(self.conn.get_type())
self.window.get_widget("overview-memory").set_text(self.conn.pretty_host_memory_size())
self.window.get_widget("overview-cpus").set_text(str(self.conn.host_active_processor_count()))
self.window.get_widget("overview-arch").set_text(self.conn.host_architecture())
netListModel = gtk.ListStore(str, str, gtk.gdk.Pixbuf)
self.window.get_widget("net-list").set_model(netListModel)
self.populate_networks(netListModel)
self.window.get_widget("net-list").get_selection().connect("changed", self.net_selected)
netCol = gtk.TreeViewColumn("Networks")
net_txt = gtk.CellRendererText()
net_img = gtk.CellRendererPixbuf()
netCol.pack_start(net_txt, True)
netCol.pack_start(net_img, False)
netCol.add_attribute(net_txt, 'text', 1)
netCol.add_attribute(net_img, 'pixbuf', 2)
self.window.get_widget("net-list").append_column(netCol)
self.window.get_widget("net-details").set_sensitive(False)
self.cpu_usage_graph = sparkline.Sparkline()
self.cpu_usage_graph.show()
self.window.get_widget("performance-table").attach(self.cpu_usage_graph, 1, 2, 0, 1)
self.memory_usage_graph = sparkline.Sparkline()
self.memory_usage_graph.show()
self.window.get_widget("performance-table").attach(self.memory_usage_graph, 1, 2, 1, 2)
self.window.get_widget("details-tabs").get_nth_page(2).hide()
self.window.signal_autoconnect({
"on_menu_file_close_activate": self.close,
"on_vmm_host_delete_event": self.close,
"on_menu_help_about_activate": self.show_help,
})
self.conn.connect("resources-sampled", self.refresh_resources)
def show(self):
dialog = self.window.get_widget("vmm-host")
dialog.present()
def is_visible(self):
if self.window.get_widget("vmm-host").flags() & gtk.VISIBLE:
return 1
return 0
def show_help(self, src):
# From the Details window, show the help document from the Details page
self.emit("action-show-help", "virt-manager-host-window")
def close(self,ignore1=None,ignore2=None):
self.window.get_widget("vmm-host").hide()
return 1
def refresh_resources(self, ignore=None):
self.window.get_widget("performance-cpu").set_text("%d %%" % self.conn.cpu_time_percentage())
vm_memory = self.conn.pretty_current_memory()
host_memory = self.conn.pretty_host_memory_size()
self.window.get_widget("performance-memory").set_text("%s of %s" % (vm_memory, host_memory))
cpu_vector = self.conn.cpu_time_vector()
cpu_vector.reverse()
self.cpu_usage_graph.set_property("data_array", cpu_vector)
memory_vector = self.conn.current_memory_vector()
memory_vector.reverse()
self.memory_usage_graph.set_property("data_array", memory_vector)
def net_selected(self, src):
active = src.get_selected()
if active[1] != None:
uuid = active[0].get_value(active[1], 0)
if uuid is None:
self.window.get_widget("net-details").set_sensitive(False)
else:
self.window.get_widget("net-details").set_sensitive(True)
net = self.conn.get_net(uuid)
self.window.get_widget("net-name").set_text(net.get_name())
self.window.get_widget("net-uuid").set_text(net.get_uuid())
self.window.get_widget("net-device").set_text(net.get_bridge_device())
ip4 = net.get_ip4_config()
self.window.get_widget("net-ip4-address").set_text(ip4[0])
self.window.get_widget("net-ip4-netmask").set_text(ip4[1])
self.window.get_widget("net-ip4-dhcp-start").set_text(ip4[2])
self.window.get_widget("net-ip4-dhcp-end").set_text(ip4[3])
if ip4[4] != None and ip4[4] != "":
self.window.get_widget("net-ip4-forwarding").set_text(_("NAT to physical device ") + ip4[4])
else:
self.window.get_widget("net-ip4-forwarding").set_text(_("Masquerade to default route"))
else:
self.window.get_widget("net-details").set_sensitive(False)
def populate_networks(self, model):
model.clear()
for uuid in self.conn.list_net_uuids():
net = self.conn.get_net(uuid)
model.append([uuid, net.get_name(), gtk.gdk.pixbuf_new_from_file(self.config.get_icon_dir() + "/icon_ethernet.png")])
#model.append([None, "Add network", gtk.gdk.pixbuf_new_from_file(self.config.get_icon_dir() + "/icon_addnew.png")])
gobject.type_register(vmmHost)

View File

@ -47,6 +47,8 @@ class vmmManager(gobject.GObject):
gobject.TYPE_NONE, (str,str)),
"action-show-about": (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, []),
"action-show-host": (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, [str]),
"action-show-preferences": (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, []),
"action-show-create": (gobject.SIGNAL_RUN_FIRST,
@ -95,7 +97,7 @@ class vmmManager(gobject.GObject):
self.vmmenu_icons["run"] = gtk.Image()
self.vmmenu_icons["run"].set_from_pixbuf(gtk.gdk.pixbuf_new_from_file_at_size(self.config.get_icon_dir() + "/icon_run.png", 18, 18))
self.vmmenu_icons["pause"] = gtk.Image()
self.vmmenu_icons["pause"].set_from_pixbuf(gtk.gdk.pixbuf_new_from_file_at_size(self.config.get_icon_dir() + "/icon_pause.png", 18, 18))
self.vmmenu_icons["pause"].set_from_pixbuf(gtk.gdk.pixbuf_new_from_file_at_size(self.config.get_icon_dir() + "/icon_pause.png", 18, 18))
self.vmmenu_icons["resume"] = gtk.Image()
self.vmmenu_icons["resume"].set_from_pixbuf(gtk.gdk.pixbuf_new_from_file_at_size(self.config.get_icon_dir() + "/icon_pause.png", 18, 18))
self.vmmenu_icons["shutdown"] = gtk.Image()
@ -170,6 +172,7 @@ class vmmManager(gobject.GObject):
"on_vm_new_clicked": self.show_vm_create,
"on_vm_delete_clicked": self.delete_vm,
"on_menu_edit_details_activate": self.show_vm_details,
"on_menu_host_details_activate": self.show_host,
"on_vm_view_changed": self.vm_view_changed,
"on_vm_list_row_activated": self.open_vm_console,
@ -495,6 +498,9 @@ class vmmManager(gobject.GObject):
def show_preferences(self, src):
self.emit("action-show-preferences")
def show_host(self, src):
self.emit("action-show-host", self.connection.get_uri())
def prepare_vmlist(self):
vmlist = self.window.get_widget("vm-list")

View File

@ -64,30 +64,54 @@ class vmmNetwork(gobject.GObject):
def get_uuid(self):
return self.uuid
def get_bridge_device(self):
return self.net.bridgeName()
def get_ip4_config(self):
doc = self._get_xml_doc()
try:
addr = self._get_xml_path(doc, "/network/ip/@address")
netmask = self._get_xml_path(doc, "/network/ip/@netmask")
dhcpstart = self._get_xml_path(doc, "/network/ip/dhcp/range[1]/@start")
dhcpend = self._get_xml_path(doc, "/network/ip/dhcp/range[1]/@end")
forward = self._get_xml_path(doc, "string(count(/network/forward))")
forwardDev = None
if forward != None:
forwardDev = self._get_xml_path(doc, "string(/network/forward/@dev)")
return [addr, netmask,dhcpstart,dhcpend,forwardDev]
finally:
doc.freeDoc()
def is_read_only(self):
if self.connection.is_read_only():
return True
return False
def get_xml_string(self, path):
def _get_xml_doc(self):
xml = self.net.XMLDesc(0)
doc = None
try:
doc = libxml2.parseDoc(xml)
except:
return None
return doc
def _get_xml_path(self, doc, path):
ctx = doc.xpathNewContext()
try:
ret = ctx.xpathEval(path)
tty = None
if len(ret) == 1:
tty = ret[0].content
str = None
if ret != None:
if type(ret) == list:
if len(ret) == 1:
str = ret[0].content
else:
str = ret
ctx.xpathFreeContext()
doc.freeDoc()
return tty
return str
except:
ctx.xpathFreeContext()
doc.freeDoc()
return None
gobject.type_register(vmmNetwork)

1509
src/vmm-host.glade Normal file

File diff suppressed because it is too large Load Diff

View File

@ -52,7 +52,7 @@
<accelerator key="n" modifiers="GDK_MOD1_MASK" signal="activate"/>
<child internal-child="image">
<widget class="GtkImage" id="image102">
<widget class="GtkImage" id="image106">
<property name="visible">True</property>
<property name="stock">gtk-new</property>
<property name="icon_size">1</property>
@ -75,7 +75,7 @@
<accelerator key="r" modifiers="GDK_MOD1_MASK" signal="activate"/>
<child internal-child="image">
<widget class="GtkImage" id="image103">
<widget class="GtkImage" id="image107">
<property name="visible">True</property>
<property name="stock">gtk-open</property>
<property name="icon_size">1</property>
@ -102,7 +102,7 @@
<signal name="activate" handler="on_menu_file_open_connection_activate" last_modification_time="Mon, 12 Jun 2006 20:34:47 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image104">
<widget class="GtkImage" id="image108">
<property name="visible">True</property>
<property name="stock">gtk-connect</property>
<property name="icon_size">1</property>
@ -152,6 +152,15 @@
<child>
<widget class="GtkMenu" id="menuitem5_menu">
<child>
<widget class="GtkMenuItem" id="menu_host_details">
<property name="visible">True</property>
<property name="label" translatable="yes">Host details...</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_menu_host_details_activate" last_modification_time="Tue, 27 Mar 2007 21:31:10 GMT"/>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="menu_edit_details">
<property name="visible">True</property>
@ -160,7 +169,7 @@
<signal name="activate" handler="on_menu_edit_details_activate" last_modification_time="Tue, 28 Mar 2006 17:06:34 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image105">
<widget class="GtkImage" id="image109">
<property name="visible">True</property>
<property name="stock">gtk-properties</property>
<property name="icon_size">1</property>
@ -181,7 +190,7 @@
<signal name="activate" handler="on_menu_edit_delete_activate" last_modification_time="Tue, 28 Mar 2006 17:06:34 GMT"/>
<child internal-child="image">
<widget class="GtkImage" id="image106">
<widget class="GtkImage" id="image110">
<property name="visible">True</property>
<property name="stock">gtk-delete</property>
<property name="icon_size">1</property>