Merge heads

This commit is contained in:
Daniel P. Berrange 2006-11-13 10:31:29 -05:00
commit 5c0221bd1b
7 changed files with 267 additions and 104 deletions

View File

@ -360,12 +360,12 @@
<child>
<widget class="GtkComboBox" id="vm-view">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="items" translatable="yes">All virtual machines
Active virtual machines
Inactive virtual machines</property>
<property name="add_tearoffs">False</property>
<property name="focus_on_click">True</property>
<signal name="changed" handler="on_vm_view_changed" last_modification_time="Tue, 10 Oct 2006 20:44:44 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
@ -423,7 +423,6 @@ Inactive virtual machines</property>
<child>
<widget class="GtkButton" id="vm-delete">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_default">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-delete</property>

View File

@ -146,6 +146,7 @@ def opt_show_cb(option, opt_str, value, parser):
# Run me!
def main():
optParser = OptionParser()
optParser.add_option("--profile", dest="profile", help="Generate runtime performance profile stats", metavar="FILE")
optParser.set_defaults(uuid=None)
optParser.add_option("-c", "--connect", dest="uri",
help="Connect to hypervisor at URI", metavar="URI")
@ -200,7 +201,13 @@ def main():
# Finally start the app for real
show_engine(engine, options.show, options.uri, options.uuid)
gtk.main()
if options.profile != None:
import hotshot
prof = hotshot.Profile(options.profile)
prof.runcall(gtk.main)
prof.close()
else:
gtk.main()
if __name__ == "__main__":
main()

View File

@ -22,6 +22,7 @@ import libvirt
import logging
import os
from time import time
import logging
from socket import gethostbyaddr, gethostname
from virtManager.domain import vmmDomain
@ -50,7 +51,6 @@ class vmmConnection(gobject.GObject):
self.vmm = libvirt.open(openURI)
self.vms = {}
self.tick()
def is_read_only(self):
return self.readOnly
@ -76,6 +76,9 @@ class vmmConnection(gobject.GObject):
self.vmm = None
self.emit("disconnected", self.uri)
def list_vm_uuids(self):
return self.vms.keys()
def get_host_info(self):
return self.hostinfo
@ -107,46 +110,118 @@ class vmmConnection(gobject.GObject):
if self.vmm == None:
return
ids = {}
oldActiveIDs = {}
oldInactiveNames = {}
for uuid in self.vms.keys():
vm = self.vms[uuid]
ids[vm.get_id()] = vm
if vm.get_id() == -1:
oldInactiveNames[vm.get_name()] = vm
else:
oldActiveIDs[vm.get_id()] = vm
doms = self.vmm.listDomainsID()
newVms = {}
if doms != None:
for id in doms:
if ids.has_key(id):
# Existing VM, so just keep handle
vm = ids[id]
del ids[id]
newActiveIDs = self.vmm.listDomainsID()
newInactiveNames = []
try:
newInactiveNames = self.vmm.listDefinedDomains()
except:
logging.warn("Unable to list inactive domains")
newUUIDs = {}
oldUUIDs = {}
curUUIDs = {}
maybeNewUUIDs = {}
# NB in these first 2 loops, we go to great pains to
# avoid actually instantiating a new VM object so that
# the common case of 'no new/old VMs' avoids hitting
# XenD too much & thus slowing stuff down.
# Filter out active domains which haven't changed
if newActiveIDs != None:
for id in newActiveIDs:
if oldActiveIDs.has_key(id):
# No change, copy across existing VM object
vm = oldActiveIDs[id]
#print "Existing active " + str(vm.get_name()) + " " + vm.get_uuid()
curUUIDs[vm.get_uuid()] = vm
else:
# New VM so create wrapper object
# May be a new VM, we have no choice but
# to create the wrapper so we can see
# if its a previously inactive domain.
vm = self.vmm.lookupByID(id)
uuid = self.uuidstr(vm.UUID())
maybeNewUUIDs[uuid] = vm
#print "Maybe new active " + str(maybeNewUUIDs[uuid].get_name()) + " " + uuid
# Filter out inactive domains which haven't changed
if newInactiveNames != None:
for name in newInactiveNames:
if oldInactiveNames.has_key(name):
# No change, copy across existing VM object
vm = oldInactiveNames[name]
#print "Existing inactive " + str(vm.get_name()) + " " + vm.get_uuid()
curUUIDs[vm.get_uuid()] = vm
else:
# May be a new VM, we have no choice but
# to create the wrapper so we can see
# if its a previously inactive domain.
try:
vm = self.vmm.lookupByID(id)
vm = self.vmm.lookupByName(name)
uuid = self.uuidstr(vm.UUID())
newVms[uuid] = vm
maybeNewUUIDs[uuid] = vm
except libvirt.libvirtError:
logging.debug("Couldn't fetch domain id " + str(id) + "; it probably went away")
#print "Maybe new inactive " + str(maybeNewUUIDs[uuid].get_name()) + " " + uuid
# Any left in ids hash are old removed VMs
for id in ids:
vm = ids[id]
del self.vms[vm.get_uuid()]
self.emit("vm-removed", self.uri, vm.get_uuid())
# At this point, maybeNewUUIDs has domains which are
# either completely new, or changed state.
# Any in newVms hash are added
for uuid in newVms.keys():
vm = newVms[uuid]
self.vms[uuid] = vmmDomain(self.config, self, vm, uuid)
# Filter out VMs which merely changed state, leaving
# only new domains
for uuid in maybeNewUUIDs.keys():
rawvm = maybeNewUUIDs[uuid]
if not(self.vms.has_key(uuid)):
#print "Completely new VM " + str(vm)
vm = vmmDomain(self.config, self, rawvm, uuid)
newUUIDs[vm.get_uuid()] = vm
curUUIDs[uuid] = vm
else:
vm = self.vms[uuid]
vm.set_handle(rawvm)
curUUIDs[uuid] = vm
#print "Mere state change " + str(vm)
# Finalize list of domains which went away altogether
for uuid in self.vms.keys():
vm = self.vms[uuid]
if not(curUUIDs.has_key(uuid)):
#print "Completly old VM " + str(vm)
oldUUIDs[uuid] = vm
else:
#print "Mere state change " + str(vm)
pass
# We have our new master list
self.vms = curUUIDs
# Inform everyone what changed
for uuid in oldUUIDs:
vm = oldUUIDs[uuid]
#print "Remove " + vm.get_name() + " " + uuid
self.emit("vm-removed", self.uri, uuid)
for uuid in newUUIDs:
vm = newUUIDs[uuid]
#print "Add " + vm.get_name() + " " + uuid
self.emit("vm-added", self.uri, uuid)
# Finally, we sample each domain
now = time()
self.hostinfo = self.vmm.getInfo()
updateVMs = self.vms
if noStatsUpdate:
updateVMs = newVms
updateVMs = newUUIDs
for uuid in updateVMs.keys():
self.vms[uuid].tick(now)

View File

@ -154,9 +154,6 @@ class vmmConsole(gobject.GObject):
return 1
return 0
def control_vm_run(self, src):
return 0
def _vnc_disconnected(self, src):
self.activate_auth_page()
@ -293,7 +290,15 @@ class vmmConsole(gobject.GObject):
else:
fcdialog.hide()
fcdialog.destroy()
def control_vm_run(self, src):
status = self.vm.status()
if status != libvirt.VIR_DOMAIN_SHUTOFF:
pass
else:
self.vm.startup()
def control_vm_shutdown(self, src):
status = self.vm.status()
if not(status in [ libvirt.VIR_DOMAIN_SHUTDOWN, libvirt.VIR_DOMAIN_SHUTOFF, libvirt.VIR_DOMAIN_CRASHED ]):

View File

@ -175,7 +175,11 @@ class vmmDetails(gobject.GObject):
self.window.get_widget("config-cpus-apply").set_sensitive(False)
def control_vm_run(self, src):
return 0
status = self.vm.status()
if status != libvirt.VIR_DOMAIN_SHUTOFF:
pass
else:
self.vm.startup()
def control_vm_shutdown(self, src):
status = self.vm.status()

View File

@ -43,6 +43,15 @@ class vmmDomain(gobject.GObject):
self.lastStatus = None
self.record = []
def set_handle(self, vm):
self.vm = vm
def is_active(self):
if self.vm.ID() == -1:
return False
else:
return True
def get_connection(self):
return self.connection
@ -249,10 +258,17 @@ class vmmDomain(gobject.GObject):
self.vm.shutdown()
self._update_status()
def startup(self):
self.vm.create()
self._update_status()
def suspend(self):
self.vm.suspend()
self._update_status()
def delete(self):
self.vm.undefine()
def resume(self):
self.vm.resume()
self._update_status()

View File

@ -56,9 +56,6 @@ class vmmManager(gobject.GObject):
self.connection = connection
self.prepare_vmlist()
self.connection.connect("vm-added", self.vm_added)
self.connection.connect("vm-removed", self.vm_removed)
self.config.on_vmlist_domain_id_visible_changed(self.toggle_domain_id_visible_widget)
self.config.on_vmlist_status_visible_changed(self.toggle_status_visible_widget)
self.config.on_vmlist_cpu_usage_visible_changed(self.toggle_cpu_usage_visible_widget)
@ -87,7 +84,6 @@ class vmmManager(gobject.GObject):
self.window.get_widget("vm-new").set_sensitive(True)
self.window.get_widget("menu_file_restore_saved").set_sensitive(True)
self.window.get_widget("vm-view").set_sensitive(False)
self.window.get_widget("vm-view").set_active(0)
self.vmmenu = gtk.Menu()
@ -125,8 +121,10 @@ class vmmManager(gobject.GObject):
"on_vm_details_clicked": self.show_vm_details,
"on_vm_open_clicked": self.open_vm_console,
"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_vm_view_changed": self.vm_view_changed,
"on_vm_list_row_activated": self.open_vm_console,
"on_vm_list_button_press_event": self.popup_vm_menu,
@ -139,6 +137,9 @@ class vmmManager(gobject.GObject):
self.window.get_widget("vm-list").get_selection().connect("changed", self.vm_selected)
self.connection.connect("disconnected", self.close)
self.connection.connect("vm-added", self.vm_added)
self.connection.connect("vm-removed", self.vm_removed)
win = self.window.get_widget("vmm-manager")
win.set_title(win.get_title() + " (" + self.connection.get_name() + ")")
@ -167,6 +168,19 @@ class vmmManager(gobject.GObject):
def open_connection(self, src=None):
self.emit("action-show-connect")
def is_showing_active(self):
active = self.window.get_widget("vm-view").get_active()
if active in [0,1]:
return True
return False
def is_showing_inactive(self):
active = self.window.get_widget("vm-view").get_active()
if active in [0,2]:
return True
return False
def restore_saved(self, src=None):
# get filename
self.fcdialog = gtk.FileChooserDialog(_("Restore Virtual Machine"),
@ -185,7 +199,7 @@ class vmmManager(gobject.GObject):
[file_to_load],
_("Restoring Virtual Machine"))
progWin.run()
self.fcdialog.destroy()
if(self.domain_restore_error != ""):
self.error_msg = gtk.MessageDialog(self.window.get_widget("vmm-manager"),
@ -196,63 +210,97 @@ class vmmManager(gobject.GObject):
self.error_msg.run()
self.error_msg.destroy()
self.domain_restore_error = ""
def restore_saved_callback(self, file_to_load):
status = self.connection.restore(file_to_load)
if(status != 0):
self.domain_restore_error = _("Error restoring domain '%s'. Is the domain already running?") % file_to_load
def vm_view_changed(self, src):
vmlist = self.window.get_widget("vm-list")
model = vmlist.get_model()
model.clear()
uuids = self.connection.list_vm_uuids()
for vmuuid in uuids:
vm = self.connection.get_vm(vmuuid)
if vm.is_active():
if not(self.is_showing_active()):
continue
else:
if not(self.is_showing_inactive()):
continue
model.append([vm, vm.get_name()])
def vm_added(self, connection, uri, vmuuid):
vm = self.connection.get_vm(vmuuid)
vm.connect("status-changed", self.vm_status_changed)
vm.connect("resources-sampled", self.vm_resources_sampled)
vmlist = self.window.get_widget("vm-list")
model = vmlist.get_model()
dup = 0
for row in range(model.iter_n_children(None)):
vm = model.get_value(model.iter_nth_child(None, row), 0)
if vm == vmuuid:
dup = 1
if vm.is_active():
if not(self.is_showing_active()):
return
else:
if not(self.is_showing_inactive()):
return
vm = self.connection.get_vm(vmuuid)
model.append([vm, vm.get_name()])
if dup != 1:
model.append([vmuuid, vm.get_name()])
vm.connect("status-changed", self.vm_status_changed)
vm.connect("resources-sampled", self.vm_resources_sampled)
if self.config.get_console_popup() == 2 and range(model.iter_n_children(None)) > 1:
# user has requested consoles on all vms
(gtype, host, port) = vm.get_graphics_console()
if gtype == "vnc":
self.emit("action-show-console", uri, vmuuid)
else:
self.emit("action-show-terminal", uri, vmuuid)
if self.config.get_console_popup() == 2 and range(model.iter_n_children(None)) > 1:
# user has requested consoles on all vms
(gtype, host, port) = vm.get_graphics_console()
if gtype == "vnc":
self.emit("action-show-console", uri, vmuuid)
else:
self.emit("action-show-terminal", uri, vmuuid)
def vm_removed(self, connection, uri, vmuuid):
vmlist = self.window.get_widget("vm-list")
model = vmlist.get_model()
dup = 0
for row in range(model.iter_n_children(None)):
vm = model.get_value(model.iter_nth_child(None, row), 0)
if vm == vmuuid:
if vm.get_uuid() == vmuuid:
model.remove(model.iter_nth_child(None, row))
break
def vm_status_changed(self, domain, status):
self.vm_updated(domain.get_uuid())
def vm_status_changed(self, vm, status):
wanted = False
if vm.is_active():
if self.is_showing_active():
wanted = True
else:
if self.is_showing_inactive():
wanted = True
def vm_resources_sampled(self, domain):
self.vm_updated(domain.get_uuid())
vmlist = self.window.get_widget("vm-list")
model = vmlist.get_model()
def vm_updated(self, vmuuid):
missing = True
for row in range(model.iter_n_children(None)):
iter = model.iter_nth_child(None, row)
if model.get_value(iter, 0).get_uuid() == vm.get_uuid():
if wanted:
missing = False
else:
model.remove(model.iter_nth_child(None, row))
break
if missing and wanted:
model.append([vm, vm.get_name()])
def vm_resources_sampled(self, vm):
vmlist = self.window.get_widget("vm-list")
model = vmlist.get_model()
for row in range(model.iter_n_children(None)):
iter = model.iter_nth_child(None, row)
if model.get_value(iter, 0) == vmuuid:
if model.get_value(iter, 0).get_uuid() == vm.get_uuid():
model.row_changed(str(row), iter)
def current_vm(self):
@ -263,14 +311,28 @@ class vmmManager(gobject.GObject):
return active[0].get_value(active[1], 0)
return None
def current_vmuuid(self):
vm = self.current_vm()
if vm is None:
return None
return vm.get_uuid()
def delete_vm(self, src=None):
vm = self.current_vm()
if vm is None or vm.is_active():
return
vm.delete()
self.connection.tick(noStatsUpdate=True)
def show_vm_details(self,ignore):
self.emit("action-show-details", self.connection.get_uri(), self.current_vm())
self.emit("action-show-details", self.connection.get_uri(), self.current_vmuuid())
def show_vm_create(self,ignore):
self.emit("action-show-create", self.connection.get_uri())
def open_vm_console(self,ignore,ignore2=None,ignore3=None):
self.emit("action-show-console", self.connection.get_uri(), self.current_vm())
self.emit("action-show-console", self.connection.get_uri(), self.current_vmuuid())
def vm_selected(self, selection):
@ -281,12 +343,15 @@ class vmmManager(gobject.GObject):
self.window.get_widget("menu_edit_delete").set_sensitive(False)
self.window.get_widget("menu_edit_details").set_sensitive(False)
else:
#self.window.get_widget("vm-delete").set_sensitive(True)
self.window.get_widget("vm-delete").set_sensitive(False)
vm = self.current_vm()
if vm.is_active():
self.window.get_widget("vm-delete").set_sensitive(False)
self.window.get_widget("menu_edit_delete").set_sensitive(False)
else:
self.window.get_widget("vm-delete").set_sensitive(True)
self.window.get_widget("menu_edit_delete").set_sensitive(True)
self.window.get_widget("vm-details").set_sensitive(True)
self.window.get_widget("vm-open").set_sensitive(True)
#self.window.get_widget("menu_edit_delete").set_sensitive(True)
self.window.get_widget("menu_edit_delete").set_sensitive(False)
self.window.get_widget("menu_edit_details").set_sensitive(True)
def popup_vm_menu(self, widget, event):
@ -304,7 +369,7 @@ class vmmManager(gobject.GObject):
def prepare_vmlist(self):
vmlist = self.window.get_widget("vm-list")
model = gtk.ListStore(str, str)
model = gtk.ListStore(object, str)
vmlist.set_model(model)
idCol = gtk.TreeViewColumn(_("ID"))
@ -397,22 +462,22 @@ class vmmManager(gobject.GObject):
def vmlist_domain_id_sorter(self, model, iter1, iter2):
return cmp(self.connection.get_vm(model.get_value(iter1, 0)).get_id(), self.connection.get_vm(model.get_value(iter2, 0)).get_id())
return cmp(model.get_value(iter1, 0).get_id(), model.get_value(iter2, 0).get_id())
def vmlist_name_sorter(self, model, iter1, iter2):
return cmp(model.get_value(iter1, 1), model.get_value(iter2, 1))
def vmlist_cpu_usage_sorter(self, model, iter1, iter2):
return cmp(self.connection.get_vm(model.get_value(iter1, 0)).cpu_time(), self.connection.get_vm(model.get_value(iter2, 0)).cpu_time())
return cmp(model.get_value(iter1, 0).cpu_time(), model.get_value(iter2, 0).cpu_time())
def vmlist_memory_usage_sorter(self, model, iter1, iter2):
return cmp(self.connection.get_vm(model.get_value(iter1, 0)).current_memory(), self.connection.get_vm(model.get_value(iter2, 0)).current_memory())
return cmp(model.get_value(iter1, 0).current_memory(), model.get_value(iter2, 0).current_memory())
def vmlist_disk_usage_sorter(self, model, iter1, iter2):
return cmp(self.connection.get_vm(model.get_value(iter1, 0)).disk_usage(), self.connection.get_vm(model.get_value(iter2, 0)).disk_usage())
return cmp(model.get_value(iter1, 0).disk_usage(), model.get_value(iter2, 0).disk_usage())
def vmlist_network_usage_sorter(self, model, iter1, iter2):
return cmp(self.connection.get_vm(model.get_value(iter1, 0)).network_traffic(), self.connection.get_vm(model.get_value(iter2, 0)).network_traffic())
return cmp(model.get_value(iter1, 0).network_traffic(), model.get_value(iter2, 0).network_traffic())
def toggle_domain_id_visible_conf(self, menu):
self.config.set_vmlist_domain_id_visible(menu.get_active())
@ -479,26 +544,25 @@ class vmmManager(gobject.GObject):
def domain_id_text(self, column, cell, model, iter, data):
uuid = model.get_value(iter, 0)
cell.set_property('text', str(self.connection.get_vm(uuid).get_id()))
id = model.get_value(iter, 0).get_id()
if id >= 0:
cell.set_property('text', str(id))
else:
cell.set_property('text', "-")
def status_text(self, column, cell, model, iter, data):
uuid = model.get_value(iter, 0)
cell.set_property('text', self.connection.get_vm(uuid).run_status())
cell.set_property('text', model.get_value(iter, 0).run_status())
def status_icon(self, column, cell, model, iter, data):
uuid = model.get_value(iter, 0)
cell.set_property('pixbuf', self.connection.get_vm(uuid).run_status_icon())
cell.set_property('pixbuf', model.get_value(iter, 0).run_status_icon())
def cpu_usage_text(self, column, cell, model, iter, data):
uuid = model.get_value(iter, 0)
cell.set_property('text', "%2.2f %%" % self.connection.get_vm(uuid).cpu_time_percentage())
cell.set_property('text', "%2.2f %%" % model.get_value(iter, 0).cpu_time_percentage())
def cpu_usage_img(self, column, cell, model, iter, data):
uuid = model.get_value(iter, 0)
#cell.set_property('text', '')
#cell.set_property('value', self.connection.get_vm(uuid).cpu_time_percentage())
data = self.connection.get_vm(uuid).cpu_time_vector()
data = model.get_value(iter, 0).cpu_time_vector()
# Prevent histogram getting too wide if we're tracking lots
# of data - full data is viewable in details window
if len(data) > 40:
@ -507,43 +571,36 @@ class vmmManager(gobject.GObject):
cell.set_property('data_array', data)
def virtual_cpus_text(self, column, cell, model, iter, data):
uuid = model.get_value(iter, 0)
cell.set_property('text', str(self.connection.get_vm(uuid).vcpu_count()))
cell.set_property('text', str(model.get_value(iter, 0).vcpu_count()))
def memory_usage_text(self, column, cell, model, iter, data):
uuid = model.get_value(iter, 0)
current = self.connection.get_vm(uuid).current_memory()
currentPercent = self.connection.get_vm(uuid).current_memory_percentage()
current = model.get_value(iter, 0).current_memory()
currentPercent = model.get_value(iter, 0).current_memory_percentage()
cell.set_property('text', "%s (%2.2f%%)" % (self.pretty_mem(current) , currentPercent))
def memory_usage_img(self, column, cell, model, iter, data):
uuid = model.get_value(iter, 0)
currentPercent = self.connection.get_vm(uuid).current_memory_percentage()
currentPercent = model.get_value(iter, 0).current_memory_percentage()
cell.set_property('text', '')
cell.set_property('value', currentPercent)
def disk_usage_text(self, column, cell, model, iter, data):
uuid = model.get_value(iter, 0)
current = self.connection.get_vm(uuid).disk_usage()
currentPercent = self.connection.get_vm(uuid).disk_usage_percentage()
current = model.get_value(iter, 0).disk_usage()
currentPercent = model.get_value(iter, 0).disk_usage_percentage()
cell.set_property('text', "%s (%2.2f%%)" % (self.pretty_mem(current) , currentPercent))
def disk_usage_img(self, column, cell, model, iter, data):
uuid = model.get_value(iter, 0)
currentPercent = self.connection.get_vm(uuid).disk_usage_percentage()
currentPercent = model.get_value(iter, 0).disk_usage_percentage()
cell.set_property('text', '')
cell.set_property('value', currentPercent)
def network_traffic_text(self, column, cell, model, iter, data):
uuid = model.get_value(iter, 0)
current = self.connection.get_vm(uuid).network_traffic()
currentPercent = self.connection.get_vm(uuid).network_traffic_percentage()
current = model.get_value(iter, 0).network_traffic()
currentPercent = model.get_value(iter, 0).network_traffic_percentage()
cell.set_property('text', "%s (%2.2f%%)" % (self.pretty_mem(current) , currentPercent))
def network_traffic_img(self, column, cell, model, iter, data):
uuid = model.get_value(iter, 0)
currentPercent = self.connection.get_vm(uuid).network_traffic_percentage()
currentPercent = model.get_value(iter, 0).network_traffic_percentage()
cell.set_property('text', '')
cell.set_property('value', currentPercent)