mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-01-11 05:17:59 +03:00
List, display info about, and enable removing VM host devices.
This commit is contained in:
parent
05cbb24d97
commit
37f436300f
@ -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
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user