From 782406b22216bdf82ab969cdfb393fe2bee9c37e Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Sat, 28 Feb 2009 19:27:20 -0500 Subject: [PATCH] Add domain api for invalidating and updating cached xml. Use this be a bit more intelligent with Details hardware list: only update list if domain xml has changed. Don't even check for new xml if the details window isn't present. --- src/virtManager/details.py | 176 ++++++++++++++++++++----------------- src/virtManager/domain.py | 45 +++++++--- 2 files changed, 128 insertions(+), 93 deletions(-) diff --git a/src/virtManager/details.py b/src/virtManager/details.py index 75cc0b7e8..7eb4daffb 100644 --- a/src/virtManager/details.py +++ b/src/virtManager/details.py @@ -186,7 +186,7 @@ class vmmDetails(gobject.GObject): self.addhw = None self.choose_cd = None - + self.cpu_usage_graph = sparkline.Sparkline() self.cpu_usage_graph.set_property("reversed", True) self.window.get_widget("graph-table").attach(self.cpu_usage_graph, 1, 2, 0, 1) @@ -202,7 +202,7 @@ class vmmDetails(gobject.GObject): self.disk_io_graph.set_property("rgb", map(lambda x: x/255.0, [0x82, 0x00, 0x3B, 0x29, 0x5C, 0x45])) self.window.get_widget("graph-table").attach(self.disk_io_graph, 1, 2, 2, 3) - + self.network_traffic_graph = sparkline.Sparkline() self.network_traffic_graph.set_property("reversed", True) self.network_traffic_graph.set_property("filled", False) @@ -331,6 +331,7 @@ class vmmDetails(gobject.GObject): self.vm.connect("status-changed", self.update_widget_states) self.vm.connect("resources-sampled", self.refresh_resources) + self.vm.connect("config-changed", self.refresh_vm_info) self.window.get_widget("hw-list").get_selection().connect("changed", self.hw_selected) self.update_widget_states(self.vm, self.vm.status()) @@ -339,7 +340,7 @@ class vmmDetails(gobject.GObject): self.pixbuf_memory = gtk.gdk.pixbuf_new_from_file(config.get_icon_dir() + "/icon_cpu.png") self.prepare_hw_list() self.hw_selected() - self.refresh_resources(self.vm) + self.refresh_vm_info() # Black magic todo with scrolled windows. Basically the behaviour we want @@ -560,8 +561,10 @@ class vmmDetails(gobject.GObject): return dialog.show_all() dialog.present() + self.engine.increment_window_counter() self.update_widget_states(self.vm, self.vm.status()) + self.vm.update_xml() def show_help(self, src): # From the Details window, show the help document from the Details page @@ -643,50 +646,47 @@ class vmmDetails(gobject.GObject): def hw_selected(self, src=None): - vmlist = self.window.get_widget("hw-list") - selection = vmlist.get_selection() - active = selection.get_selected() - if active[1] != None: - pagetype = active[0].get_value(active[1], HW_LIST_COL_TYPE) + pagetype = self.get_hw_selection(HW_LIST_COL_TYPE) + if not pagetype: self.window.get_widget("hw-panel").set_sensitive(True) - self.window.get_widget("hw-panel").show_all() - - pagenum = pagetype - if pagetype == HW_LIST_TYPE_GENERAL: - pass - elif pagetype == HW_LIST_TYPE_STATS: - self.refresh_stats_page() - elif pagetype == HW_LIST_TYPE_CPU: - self.window.get_widget("config-vcpus-apply").set_sensitive(False) - self.refresh_config_cpu() - elif pagetype == HW_LIST_TYPE_MEMORY: - self.window.get_widget("config-memory-apply").set_sensitive(False) - self.refresh_config_memory() - elif pagetype == HW_LIST_TYPE_BOOT: - self.refresh_boot_page() - self.window.get_widget("config-boot-options-apply").set_sensitive(False) - 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() - else: - pagenum = -1 - - self.window.get_widget("hw-panel").set_current_page(pagenum) - else: - self.window.get_widget("hw-panel").set_sensitive(False) - selection.select_path(0) + self.window.get_widget("hw-list").get_selection().select_path(0) self.window.get_widget("hw-panel").set_current_page(0) + return + + self.window.get_widget("hw-panel").set_sensitive(True) + self.window.get_widget("hw-panel").show_all() + + if pagetype == HW_LIST_TYPE_GENERAL: + pass + elif pagetype == HW_LIST_TYPE_STATS: + self.refresh_stats_page() + elif pagetype == HW_LIST_TYPE_CPU: + self.window.get_widget("config-vcpus-apply").set_sensitive(False) + self.refresh_config_cpu() + elif pagetype == HW_LIST_TYPE_MEMORY: + self.window.get_widget("config-memory-apply").set_sensitive(False) + self.refresh_config_memory() + elif pagetype == HW_LIST_TYPE_BOOT: + self.refresh_boot_page() + self.window.get_widget("config-boot-options-apply").set_sensitive(False) + 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() + else: + pagetype = -1 + + self.window.get_widget("hw-panel").set_current_page(pagetype) def control_vm_pause(self, src): if self.ignorePause: @@ -843,44 +843,58 @@ class vmmDetails(gobject.GObject): def switch_page(self, ignore1=None, ignore2=None, newpage=None): self.page_refresh(newpage) - def refresh_resources(self, ignore=None): + def refresh_resources(self, ignore): + details = self.window.get_widget("details-pages") + page = details.get_current_page() + + if self.is_visible(): + # Force an xml update, so we check for changed xml on every tick + self.vm.update_xml() + + if (page == PAGE_DETAILS and + self.get_hw_selection(HW_LIST_COL_TYPE) == HW_LIST_TYPE_STATS): + self.refresh_stats_page() + + def refresh_vm_info(self, ignore=None): details = self.window.get_widget("details-pages") self.page_refresh(details.get_current_page()) def page_refresh(self, page): - if page == PAGE_DETAILS: - # Add / remove new devices - self.repopulate_hw_list() + if page != PAGE_DETAILS: + return - # Now refresh desired page - hw_list = self.window.get_widget("hw-list") - selection = hw_list.get_selection() - active = selection.get_selected() - if active[1] != None: - pagetype = active[0].get_value(active[1], HW_LIST_COL_TYPE) - if pagetype == HW_LIST_TYPE_GENERAL: - # Nothing to refresh at this point - pass - 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_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() + # 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 + + pagetype = self.get_hw_selection(HW_LIST_COL_TYPE) + + # Add / remove new devices + self.repopulate_hw_list() + + if pagetype == HW_LIST_TYPE_GENERAL: + # Nothing to refresh at this point + pass + 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_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() def refresh_stats_page(self): def _rx_tx_text(rx, tx, unit): @@ -987,7 +1001,7 @@ class vmmDetails(gobject.GObject): else: perms = "Read/Write" if diskinfo[7] == True: - perms += ", Sharable" + perms += ", Shareable" self.window.get_widget("disk-permissions").set_text(perms) bus = diskinfo[8] or _("Unknown") self.window.get_widget("disk-bus").set_text(bus) @@ -1531,7 +1545,7 @@ class vmmDetails(gobject.GObject): return self.remove_device(info[0], info[1]) - self.refresh_resources() + self.vm.update_xml() def prepare_hw_list(self): hw_list_model = gtk.ListStore(str, str, int, gtk.gdk.Pixbuf, int, gobject.TYPE_PYOBJECT) @@ -1770,7 +1784,7 @@ class vmmDetails(gobject.GObject): self.addhw.show() def add_hardware_done(self, ignore=None): - self.refresh_resources() + self.vm.update_xml() def toggle_cdrom(self, src): info = self.get_hw_selection(HW_LIST_COL_DEVICE) diff --git a/src/virtManager/domain.py b/src/virtManager/domain.py index 8da638569..9975c0c50 100644 --- a/src/virtManager/domain.py +++ b/src/virtManager/domain.py @@ -33,6 +33,9 @@ class vmmDomain(gobject.GObject): "resources-sampled": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, []), + "config-changed": (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + []), } def __init__(self, config, connection, vm, uuid): @@ -49,14 +52,16 @@ class vmmDomain(gobject.GObject): "netRxRate" : 10.0, } - self._update_status() - self.xml = None + self._xml = None + self._valid_xml = False self._mem_stats = None self._cpu_stats = None self._network_traffic = None self._disk_io = None + self._update_status() + self.config.on_stats_enable_mem_poll_changed(self.toggle_sample_mem_stats) self.config.on_stats_enable_cpu_poll_changed(self.toggle_sample_cpu_stats) self.config.on_stats_enable_net_poll_changed(self.toggle_sample_network_traffic) @@ -67,14 +72,26 @@ class vmmDomain(gobject.GObject): self.toggle_sample_network_traffic() self.toggle_sample_disk_io() - def get_xml(self): - if self.xml is None: + # Get domain xml. If cached xml is invalid, update. + if self._xml is None or not self._valid_xml: self.update_xml() - return self.xml + return self._xml def update_xml(self): - self.xml = self.vm.XMLDesc(libvirt.VIR_DOMAIN_XML_SECURE) + # Force an xml update. Signal 'config-changed' if domain xml has + # changed since last refresh + + origxml = self._xml + self._xml = self.vm.XMLDesc(libvirt.VIR_DOMAIN_XML_SECURE) + self._valid_xml = True + + if origxml != self._xml: + self.emit("config-changed") + + def invalidate_xml(self): + # Mark cached xml as invalid + self._valid_xml = False def release_handle(self): del(self.vm) @@ -121,6 +138,7 @@ class vmmDomain(gobject.GObject): def is_hvm(self): os_type = util.get_xml_path(self.get_xml(), "/domain/os/type") + # FIXME: This should be static, not parse xml everytime # XXX libvirt bug - doesn't work for inactive guests #os_type = self.vm.OSType() logging.debug("OS Type: %s" % os_type) @@ -129,6 +147,7 @@ class vmmDomain(gobject.GObject): return False def get_type(self): + # FIXME: This should be static, not parse xml everytime return util.get_xml_path(self.get_xml(), "/domain/@type") def is_vcpu_hotplug_capable(self): @@ -261,8 +280,9 @@ class vmmDomain(gobject.GObject): if self.connection.get_state() != self.connection.STATE_ACTIVE: return - # Clear cached XML - self.xml = None + # Invalidate cached xml + self.invalidate_xml() + info = self.vm.info() expected = self.config.get_stats_history_length() current = len(self.record) @@ -588,7 +608,8 @@ class vmmDomain(gobject.GObject): return self._parse_device_xml(_parse_serial_consoles) def get_graphics_console(self): - self.xml = None + self.update_xml() + typ = util.get_xml_path(self.get_xml(), "/domain/devices/graphics/@type") port = None @@ -1085,7 +1106,7 @@ class vmmDomain(gobject.GObject): self.get_connection().define_domain(newxml) # Invalidate cached XML - self.xml = None + self.invalidate_xml() def remove_device(self, dev_type, dev_id_info): newxml = self._remove_xml_device(dev_type, dev_id_info) @@ -1094,7 +1115,7 @@ class vmmDomain(gobject.GObject): self.get_connection().define_domain(newxml) # Invalidate cached XML - self.xml = None + self.invalidate_xml() def _change_cdrom(self, newdev, dev_id_info): # If vm is shutoff, remove device, and redefine with media @@ -1234,7 +1255,7 @@ class vmmDomain(gobject.GObject): doc.freeDoc() # Invalidate cached xml - self.xml = None + self.invalidate_xml() def toggle_sample_cpu_stats(self, ignore1=None, ignore2=None, ignore3=None, ignore4=None):