[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[et-mgmt-tools] [PATCH] virt-manager: List VM 'hostdev' devices



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 redhat com>
# 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">&lt;b&gt;Physical Host Device&lt;/b&gt;</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>

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]