[Date Prev][Date Next] [Thread Prev][Thread Next]
[Thread Index]
[Date Index]
[Author Index]
[et-mgmt-tools] [PATCH] virt-manager: List VM 'hostdev' devices
- From: Cole Robinson <crobinso redhat com>
- To: Fedora/Linux Management Tools <et-mgmt-tools redhat com>
- Subject: [et-mgmt-tools] [PATCH] virt-manager: List VM 'hostdev' devices
- Date: Tue, 20 Jan 2009 17:41:48 -0500
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"><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>
[Date Prev][Date Next] [Thread Prev][Thread Next]
[Thread Index]
[Date Index]
[Author Index]