The attached patch adds support for listing, viewing details of, and removing VM 'hostdev' devices. libvirt currently supports usb and pci host devices, so I tried to tackle all the variants for each. Some example screeenshots: http://fedorapeople.org/~crobinso/virt-manager/vmm-show-hostdev1.png http://fedorapeople.org/~crobinso/virt-manager/vmm-show-hostdev2.png A couple particulars: - There is no icon for a 'hostdev' in the VM's hardware list. We've been stretching use of gtk stock icons pretty thin, and there really isn't anything available that makes sense. This will be addressed when we integrate custom icons (next release). - The parameter differences between PCI, USB by vendor/product, and USB by bus/device number are pretty large. Rather than have 10 different GUI fields, populating only what is appropriate and hiding the others, I just generate a single line listing for the device details section (ex. "Bus: 001, Device: 002". This is shown in the screenshots. It's a bit uglier this way, but much simpler, and conveys all the same information. Any comments appreciated. Thanks, Cole
# HG changeset patch # User Cole Robinson <crobinso@xxxxxxxxxx> # Date 1232490181 18000 # Node ID 2abd192af9aab074aed323a75cb79c47954f0de8 # Parent f3f6a05bf4ae3f1bba6d6fa4a339c530ecb56fa8 List, display info about, and enable removing VM host devices. diff -r f3f6a05bf4ae -r 2abd192af9aa src/virtManager/details.py --- a/src/virtManager/details.py Mon Jan 19 15:50:32 2009 -0500 +++ b/src/virtManager/details.py Tue Jan 20 17:23:01 2009 -0500 @@ -57,6 +57,7 @@ HW_LIST_TYPE_GRAPHICS = 6 HW_LIST_TYPE_SOUND = 7 HW_LIST_TYPE_CHAR = 8 +HW_LIST_TYPE_HOSTDEV = 9 # Console pages PAGE_UNAVAILABLE = 0 @@ -293,6 +294,7 @@ "on_config_graphics_remove_clicked": self.remove_xml_dev, "on_config_sound_remove_clicked": self.remove_xml_dev, "on_config_char_remove_clicked": self.remove_xml_dev, + "on_config_hostdev_remove_clicked": self.remove_xml_dev, "on_add_hardware_button_clicked": self.add_hardware, "on_details_menu_view_fullscreen_activate": self.toggle_fullscreen, @@ -610,6 +612,9 @@ 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: @@ -622,9 +627,8 @@ self.refresh_sound_page() elif pagetype == HW_LIST_TYPE_CHAR: self.refresh_char_page() - 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_HOSTDEV: + self.refresh_hostdev_page() else: pagenum = -1 @@ -822,6 +826,8 @@ 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_summary(self): def _rx_tx_text(rx, tx, unit): @@ -1037,6 +1043,19 @@ self.window.get_widget("char-target-port").set_text(charinfo[3]) self.window.get_widget("char-source-path").set_text(charinfo[5] or "-") + def refresh_hostdev_page(self): + hostdevinfo = self.get_hw_selection(HW_LIST_COL_DEVICE) + if not hostdevinfo: + return + + devlabel = "<b>Physical %s Device</b>" % hostdevinfo[4].upper() + + self.window.get_widget("hostdev-title").set_markup(devlabel) + self.window.get_widget("hostdev-type").set_text(hostdevinfo[4]) + self.window.get_widget("hostdev-mode").set_text(hostdevinfo[3]) + self.window.get_widget("hostdev-source").set_text(hostdevinfo[5]) + + def refresh_boot_page(self): # Refresh autostart try: @@ -1512,6 +1531,7 @@ currentGraphics = {} currentSounds = {} currentChars = {} + currentHostdevs = {} def update_hwlist(hwtype, info): """Return (true if we updated an entry, @@ -1590,7 +1610,6 @@ if missing: hw_list_model.insert(insertAt, [_("Sound: %s" % soundinfo[2]), gtk.STOCK_MEDIA_PLAY, gtk.ICON_SIZE_LARGE_TOOLBAR, None, HW_LIST_TYPE_SOUND, soundinfo]) - # Populate list of char devices for charinfo in self.vm.get_char_devices(): currentChars[charinfo[2]] = 1 @@ -1604,6 +1623,15 @@ l += " %s" % charinfo[3] # Don't show port for console hw_list_model.insert(insertAt, [l, gtk.STOCK_CONNECT, gtk.ICON_SIZE_LARGE_TOOLBAR, None, HW_LIST_TYPE_CHAR, charinfo]) + # Populate host devices + for hostdevinfo in self.vm.get_hostdev_devices(): + currentHostdevs[hostdevinfo[2]] = 1 + missing, insertAt = update_hwlist(HW_LIST_TYPE_HOSTDEV, + hostdevinfo) + + if missing: + hw_list_model.insert(insertAt, [hostdevinfo[2], None, gtk.ICON_SIZE_LARGE_TOOLBAR, None, HW_LIST_TYPE_HOSTDEV, hostdevinfo]) + # Now remove any no longer current devs devs = range(len(hw_list_model)) @@ -1631,6 +1659,9 @@ elif row[HW_LIST_COL_TYPE] == HW_LIST_TYPE_CHAR and not \ currentChars.has_key(row[HW_LIST_COL_DEVICE][2]): removeIt = True + elif row[HW_LIST_COL_TYPE] == HW_LIST_TYPE_HOSTDEV and not \ + currentHostdevs.has_key(row[HW_LIST_COL_DEVICE][2]): + removeIt = True if removeIt: # Re-select the first row, if we're viewing the device diff -r f3f6a05bf4ae -r 2abd192af9aa src/virtManager/domain.py --- a/src/virtManager/domain.py Mon Jan 19 15:50:32 2009 -0500 +++ b/src/virtManager/domain.py Tue Jan 20 17:23:01 2009 -0500 @@ -805,6 +805,99 @@ return self._parse_device_xml(_parse_char_devs) + def get_hostdev_devices(self): + def _parse_hostdev_devs(ctx): + hostdevs = [] + devs = ctx.xpathEval("/domain/devices/hostdev") + + for dev in devs: + vendor = None + product = None + addrbus = None + addrdev = None + unique = {} + + # String shown in the devices details section + srclabel = "" + # String shown in the VMs hardware list + hwlabel = "" + + def dehex(val): + if val.startswith("0x"): + val = val[2:] + return val + + def safeint(val, fmt="%.3d"): + try: + int(val) + except: + return str(val) + return fmt % int(val) + + def set_uniq(baseent, propname, node): + val = node.prop(propname) + if not unique.has_key(baseent): + unique[baseent] = {} + unique[baseent][propname] = val + return val + + mode = dev.prop("mode") + typ = dev.prop("type") + unique["type"] = typ + hwlabel = typ.upper() + + for node in dev.children: + if node.name == "source": + for child in node.children: + if child.name == "address": + addrbus = set_uniq("address", "bus", child) + + # For USB + addrdev = set_uniq("address", "device", child) + + # For PCI + addrdom = set_uniq("address", "domain", child) + addrslt = set_uniq("address", "slot", child) + addrfun = set_uniq("address", "function", child) + elif child.name == "vendor": + vendor = set_uniq("vendor", "id", child) + elif child.name == "product": + product = set_uniq("product", "id", child) + + if vendor and product: + # USB by vendor + product + srclabel += "Vendor: %s, Product: %s" % (vendor, product) + hwlabel += " %s:%s" % (vendor, product) + + 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: + # PCI by bus:slot:function + srclabel += "Bus: %s, Device: %s, Product: %s" % \ + (addrbus, addrslt, addrfun) + if addrdom: + srclabel += ", Domain: %s" % addrdom + hwlabel += " %s:%s:%s" % (dehex(addrbus), dehex(addrslt), + dehex(addrfun)) + + else: + # If we can't determine source info, skip these + # device since we have no way to determine uniqueness + continue + + # [device type, unique, hwlist label, hostdev mode, + # hostdev type, source desc label] + hostdevs.append(["hostdev", unique, hwlabel, mode, typ, + srclabel]) + + return hostdevs + return self._parse_device_xml(_parse_hostdev_devs) + + def _parse_device_xml(self, parse_function): doc = None ctx = None @@ -883,6 +976,51 @@ cons_ret = ctx.xpathEval("/domain/devices/console[target/@port='%s']" % dev_id_info) if cons_ret and len(cons_ret) > 0: ret.append(cons_ret[0]) + + elif dev_type == "hostdev": + # This whole process is a little funky, since we need a decent + # amount of info to determine which specific hostdev to remove + + xmlbase = "/domain/devices/hostdev[@type='%s' and " % \ + dev_id_info["type"] + xpath = "" + ret = [] + + addr = dev_id_info.get("address") + vend = dev_id_info.get("vendor") + prod = dev_id_info.get("product") + if addr: + bus = addr.get("bus") + dev = addr.get("device") + slot = addr.get("slot") + funct = addr.get("function") + dom = addr.get("domain") + + if bus and dev: + # USB by bus and dev + xpath = (xmlbase + "source/address/@bus='%s' and " + "source/address/@device='%s']" % + (bus, dev)) + elif bus and slot and funct and dom: + # PCI by bus,slot,funct,dom + xpath = (xmlbase + "source/address/@bus='%s' and " + "source/address/@slot='%s' and " + "source/address/@function='%s' and " + "source/address/@domain='%s']" % + (bus, slot, funct, dom)) + + elif vend.get("id") and prod.get("id"): + # USB by vendor and product + xpath = (xmlbase + "source/vendor/@id='%s' and " + "source/product/@id='%s']" % + (vend.get("id"), prod.get("id"))) + + if xpath: + # Log this, since we could hit issues with unexpected + # xml parameters in the future + logging.debug("Hostdev xpath string: %s" % xpath) + ret = ctx.xpathEval(xpath) + else: raise RuntimeError, _("Unknown device type '%s'" % dev_type) diff -r f3f6a05bf4ae -r 2abd192af9aa src/vmm-details.glade --- a/src/vmm-details.glade Mon Jan 19 15:50:32 2009 -0500 +++ b/src/vmm-details.glade Tue Jan 20 17:23:01 2009 -0500 @@ -412,6 +412,7 @@ </widget> <packing> <property name="expand">False</property> + <property name="homogeneous">False</property> </packing> </child> <child> @@ -3004,6 +3005,146 @@ <property name="tab_fill">False</property> </packing> </child> + <child> + <widget class="GtkVBox" id="vbox4"> + <property name="visible">True</property> + <child> + <widget class="GtkFrame" id="frame6"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <child> + <widget class="GtkAlignment" id="alignment5"> + <property name="visible">True</property> + <property name="top_padding">5</property> + <property name="left_padding">12</property> + <child> + <widget class="GtkTable" id="table50"> + <property name="visible">True</property> + <property name="n_rows">3</property> + <property name="n_columns">2</property> + <property name="column_spacing">8</property> + <property name="row_spacing">4</property> + <child> + <widget class="GtkLabel" id="hostdev-source"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">label</property> + </widget> + <packing> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="hostdev-mode"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">label</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="hostdev-type"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">label</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label9"> + <property name="visible">True</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Device Mode:</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label8"> + <property name="visible">True</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Device Type:</property> + </widget> + <packing> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkLabel" id="hostdev-title"> + <property name="visible">True</property> + <property name="label" translatable="no"><b>Physical Host Device</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">15</property> + </packing> + </child> + <child> + <widget class="GtkHButtonBox" id="hbuttonbox1"> + <property name="visible">True</property> + <property name="border_width">6</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + <child> + <widget class="GtkButton" id="config-hostdev-remove"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="label">gtk-remove</property> + <property name="use_stock">True</property> + <property name="response_id">0</property> + <signal name="clicked" handler="on_config_hostdev_remove_clicked"/> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="position">9</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label6"> + <property name="visible">True</property> + <property name="label">Hostdev</property> + </widget> + <packing> + <property name="type">tab</property> + <property name="position">9</property> + <property name="tab_fill">False</property> + </packing> + </child> </widget> <packing> <property name="resize">True</property>
_______________________________________________ et-mgmt-tools mailing list et-mgmt-tools@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/et-mgmt-tools