List, display info about, and enable removing VM host devices.

This commit is contained in:
Cole Robinson 2009-01-22 17:00:38 -05:00
parent 05cbb24d97
commit 37f436300f
3 changed files with 967 additions and 641 deletions

View File

@ -57,6 +57,7 @@ HW_LIST_TYPE_INPUT = 5
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 @@ class vmmDetails(gobject.GObject):
"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 @@ class vmmDetails(gobject.GObject):
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 @@ class vmmDetails(gobject.GObject):
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 @@ class vmmDetails(gobject.GObject):
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 @@ class vmmDetails(gobject.GObject):
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 @@ class vmmDetails(gobject.GObject):
currentGraphics = {}
currentSounds = {}
currentChars = {}
currentHostdevs = {}
def update_hwlist(hwtype, info):
"""Return (true if we updated an entry,
@ -1590,7 +1610,6 @@ class vmmDetails(gobject.GObject):
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 @@ class vmmDetails(gobject.GObject):
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 @@ class vmmDetails(gobject.GObject):
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

View File

@ -805,6 +805,101 @@ class vmmDomain(gobject.GObject):
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()
srclabel = 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
devstr = " %s:%s" % (dehex(vendor), dehex(product))
srclabel += devstr
hwlabel += devstr
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 and addrdom:
# PCI by bus:slot:function
devstr = " %s:%s:%s.%s" % \
(dehex(addrdom), dehex(addrbus),
dehex(addrslt), dehex(addrfun))
srclabel += devstr
hwlabel += devstr
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 +978,51 @@ class vmmDomain(gobject.GObject):
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)

File diff suppressed because it is too large Load Diff