Files
virt-manager/virtinst/NodeDeviceParser.py
Cole Robinson be2d9ddcb4 Add base conn object and bump minimum libvirt version to 0.6.0
This base connection object will be used to simplify the API in various
places, reduce libvirt API calls, and better share code between virtinst
and virt-manager. For now it just centralizes connection opening.

This also exposed various places where our handling for older libvirt
was busted, so raise our minimum host version to 0.6.0, the first
version that supports threaded client requests.
2013-07-05 16:42:19 -04:00

604 lines
18 KiB
Python

#
# Copyright 2009 Red Hat, Inc.
# Cole Robinson <crobinso@redhat.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA.
from virtinst import support
from virtinst import util
import libvirt
import logging
# class USBDevice
CAPABILITY_TYPE_SYSTEM = "system"
CAPABILITY_TYPE_NET = "net"
CAPABILITY_TYPE_PCI = "pci"
CAPABILITY_TYPE_USBDEV = "usb_device"
CAPABILITY_TYPE_USBBUS = "usb"
CAPABILITY_TYPE_STORAGE = "storage"
CAPABILITY_TYPE_SCSIBUS = "scsi_host"
CAPABILITY_TYPE_SCSIDEV = "scsi"
HOSTDEV_ADDR_TYPE_LIBVIRT = 0
HOSTDEV_ADDR_TYPE_PCI = 1
HOSTDEV_ADDR_TYPE_USB_BUSADDR = 2
HOSTDEV_ADDR_TYPE_USB_VENPRO = 3
class NodeDevice(object):
def __init__(self, node):
self.name = None
self.parent = None
self.device_type = None
self._parseNodeXML(node)
def pretty_name(self, child_dev=None):
"""
Use device information to attempt to print a human readable device
name.
@param child_dev: Child node device to display in description
@type child_dev: L{NodeDevice}
@returns: Device description string
@rtype C{str}
"""
ignore = child_dev
return self.name
def _parseNodeXML(self, node):
child = node.children
while child:
if child.name == "name":
self.name = child.content
elif child.name == "parent":
self.parent = child.content
elif child.name == "capability":
self.device_type = child.prop("type")
child = child.next
def _getCapabilityNode(self, node):
child = node.children
while child:
if child.name == "capability":
return child
child = child.next
return None
def _parseValueHelper(self, node, value_map):
if node.name in value_map:
setattr(self, value_map[node.name], node.content)
def _parseHelper(self, main_node, value_map):
node = main_node.children
while node:
self._parseValueHelper(node, value_map)
node = node.next
class SystemDevice(NodeDevice):
def __init__(self, node):
NodeDevice.__init__(self, node)
self.hw_vendor = None
self.hw_version = None
self.hw_serial = None
self.hw_uuid = None
self.fw_vendor = None
self.fw_version = None
self.fw_date = None
self.parseXML(self._getCapabilityNode(node))
def parseXML(self, node):
child = node.children
hardware_map = {"vendor": "hw_vendor",
"version": "hw_version",
"serial": "hw_serial",
"uuid": "hw_uuid"}
firmware_map = {"vendor": "fw_vendor",
"version": "fw_version",
"release_date": "fw_date"}
while child:
if child.name == "hardware":
self._parseHelper(child, hardware_map)
elif child.name == "firmware":
self._parseHelper(child, firmware_map)
child = child.next
def pretty_name(self, child_dev=None):
ignore = child_dev
desc = _("System")
if self.hw_vendor:
desc += ": %s" % self.hw_vendor
if self.hw_version:
desc += " %s" % self.hw_version
return desc
class NetDevice(NodeDevice):
def __init__(self, node):
NodeDevice.__init__(self, node)
self.interface = None
self.address = None
self.capability_type = None
self.parseXML(self._getCapabilityNode(node))
def parseXML(self, node):
value_map = {"interface" : "interface",
"address" : "address"}
child = node.children
while child:
if child.name == "capability":
self.capability_type = child.prop("type")
else:
self._parseValueHelper(child, value_map)
child = child.next
def pretty_name(self, child_dev=None):
ignore = child_dev
desc = self.name
if self.interface:
desc = _("Interface %s") % self.interface
return desc
class PCIDevice(NodeDevice):
def __init__(self, node):
NodeDevice.__init__(self, node)
self.domain = None
self.bus = None
self.slot = None
self.function = None
self.product_id = None
self.product_name = None
self.vendor_id = None
self.vendor_name = None
self.parseXML(self._getCapabilityNode(node))
def parseXML(self, node):
val_map = {"domain" : "domain",
"bus" : "bus",
"slot" : "slot",
"function" : "function"}
child = node.children
while child:
if child.name == "vendor":
self.vendor_name = child.content
self.vendor_id = child.prop("id")
elif child.name == "product":
self.product_name = child.content
self.product_id = child.prop("id")
else:
self._parseValueHelper(child, val_map)
child = child.next
def pretty_name(self, child_dev=None):
devstr = "%.2X:%.2X:%X" % (int(self.bus),
int(self.slot),
int(self.function))
if child_dev:
desc = "%s %s (%s)" % (devstr, child_dev.pretty_name(),
str(self.product_name))
else:
desc = "%s %s" % (devstr, str(self.product_name))
return desc
class USBDevice(NodeDevice):
def __init__(self, node):
NodeDevice.__init__(self, node)
self.bus = None
self.device = None
self.product_id = None
self.product_name = None
self.vendor_id = None
self.vendor_name = None
self.parseXML(self._getCapabilityNode(node))
def parseXML(self, node):
val_map = {"bus": "bus", "device": "device"}
child = node.children
while child:
if child.name == "vendor":
self.vendor_name = child.content
self.vendor_id = child.prop("id")
elif child.name == "product":
self.product_name = child.content
self.product_id = child.prop("id")
else:
self._parseValueHelper(child, val_map)
child = child.next
def pretty_name(self, child_dev=None):
ignore = child_dev
devstr = "%.3d:%.3d" % (int(self.bus), int(self.device))
desc = "%s %s %s" % (devstr, str(self.vendor_name),
str(self.product_name))
return desc
class StorageDevice(NodeDevice):
def __init__(self, node):
NodeDevice.__init__(self, node)
self.block = None
self.bus = None
self.drive_type = None
self.size = 0
self.model = None
self.vendor = None
self.removable = False
self.media_available = False
self.media_size = 0
self.media_label = None
self.hotpluggable = False
self.parseXML(self._getCapabilityNode(node))
def parseXML(self, node):
val_map = {"block" : "block",
"bus" : "bus",
"drive_type" : "drive_type",
"model" : "model",
"vendor" : "vendor"}
child = node.children
while child:
if child.name == "size":
self.size = int(child.content)
elif child.name == "capability":
captype = child.prop("type")
if captype == "hotpluggable":
self.hotpluggable = True
elif captype == "removable":
self.removable = True
rmchild = child.children
while rmchild:
if rmchild.name == "media_available":
self.media_available = bool(int(rmchild.content))
elif rmchild.name == "media_size":
self.media_size = int(rmchild.content)
elif rmchild.name == "media_label":
self.media_label = rmchild.content
rmchild = rmchild.next
else:
self._parseValueHelper(child, val_map)
child = child.next
def pretty_name(self, child_dev=None):
ignore = child_dev
desc = ""
if self.drive_type:
desc = self.drive_type
if self.block:
desc = ": ".join((desc, self.block))
elif self.model:
desc = ": ".join((desc, self.model))
else:
desc = ": ".join((desc, self.name))
return desc
class USBBus(NodeDevice):
def __init__(self, node):
NodeDevice.__init__(self, node)
self.number = None
self.classval = None
self.subclass = None
self.protocol = None
self.parseXML(self._getCapabilityNode(node))
def parseXML(self, node):
val_map = {"number" : "number",
"class" : "classval",
"subclass" : "subclass",
"protocol" : "protocol"}
self._parseHelper(node, val_map)
class SCSIDevice(NodeDevice):
def __init__(self, node):
NodeDevice.__init__(self, node)
self.host = None
self.bus = None
self.target = None
self.lun = None
self.disk = None
self.parseXML(self._getCapabilityNode(node))
def parseXML(self, node):
val_map = {"host" : "host",
"bus" : "bus",
"target": "target",
"lun" : "lun",
"type" : "type"}
self._parseHelper(node, val_map)
class SCSIBus(NodeDevice):
def __init__(self, node):
NodeDevice.__init__(self, node)
self.host = None
self.vport_ops = False
self.fc_host = False
self.wwnn = None
self.wwpn = None
self.parseXML(self._getCapabilityNode(node))
def parseXML(self, node):
val_map = {"host" : "host"}
child = node.children
while child:
if child.name == "capability":
captype = child.prop("type")
if captype == "vport_ops":
self.vport_ops = True
elif captype == "fc_host":
self.fc_host = True
fcchild = child.children
while fcchild:
if fcchild.name == "wwnn":
self.wwnn = fcchild.content
elif fcchild.name == "wwpn":
self.wwpn = fcchild.content
fcchild = fcchild.next
else:
self._parseValueHelper(child, val_map)
child = child.next
def is_nodedev_capable(conn):
"""
Check if the passed libvirt connection supports host device routines
@param conn: Connection to check
@rtype: C{bool}
"""
return support.check_conn_support(conn, support.SUPPORT_CONN_NODEDEV)
def is_pci_detach_capable(conn):
"""
Check if the passed libvirt connection support pci device Detach/Reset
@param conn: Connection to check
@rtype: C{bool}
"""
return support.check_conn_support(conn,
support.SUPPORT_NODEDEV_PCI_DETACH)
def _lookupNodeName(conn, name):
nodedev = conn.nodeDeviceLookupByName(name)
xml = nodedev.XMLDesc(0)
return parse(xml)
def lookupNodeName(conn, name):
"""
Convert the passed libvirt node device name to a NodeDevice
instance, with proper error reporting. If the name is name is not
found, we will attempt to parse the name as would be passed to
devAddressToNodeDev
@param conn: libvirt.virConnect instance to perform the lookup on
@param name: libvirt node device name to lookup, or address for
devAddressToNodedev
@rtype: L{NodeDevice} instance
"""
if not is_nodedev_capable(conn):
raise ValueError(_("Connection does not support host device "
"enumeration."))
try:
return (_lookupNodeName(conn, name),
HOSTDEV_ADDR_TYPE_LIBVIRT)
except libvirt.libvirtError, e:
ret = _isAddressStr(name)
if not ret:
raise e
return devAddressToNodedev(conn, name)
def _isAddressStr(addrstr):
cmp_func = None
addr_type = None
try:
# Determine addrstr type
if addrstr.count(":") in [1, 2] and addrstr.count("."):
devtype = CAPABILITY_TYPE_PCI
addrstr, func = addrstr.split(".", 1)
addrstr, slot = addrstr.rsplit(":", 1)
domain = "0"
if addrstr.count(":"):
domain, bus = addrstr.split(":", 1)
else:
bus = addrstr
func = int(func, 16)
slot = int(slot, 16)
domain = int(domain, 16)
bus = int(bus, 16)
def pci_cmp(nodedev):
return ((int(nodedev.domain) == domain) and
(int(nodedev.function) == func) and
(int(nodedev.bus) == bus) and
(int(nodedev.slot) == slot))
cmp_func = pci_cmp
addr_type = HOSTDEV_ADDR_TYPE_PCI
elif addrstr.count(":"):
devtype = CAPABILITY_TYPE_USBDEV
vendor, product = addrstr.split(":")
vendor = int(vendor, 16)
product = int(product, 16)
def usbprod_cmp(nodedev):
return ((int(nodedev.vendor_id, 16) == vendor) and
(int(nodedev.product_id, 16) == product))
cmp_func = usbprod_cmp
addr_type = HOSTDEV_ADDR_TYPE_USB_VENPRO
elif addrstr.count("."):
devtype = CAPABILITY_TYPE_USBDEV
bus, addr = addrstr.split(".", 1)
bus = int(bus)
addr = int(addr)
def usbaddr_cmp(nodedev):
return ((int(nodedev.bus) == bus) and
(int(nodedev.device) == addr))
cmp_func = usbaddr_cmp
addr_type = HOSTDEV_ADDR_TYPE_USB_BUSADDR
except:
logging.exception("Error parsing node device string.")
return None
return cmp_func, devtype, addr_type
def devAddressToNodedev(conn, addrstr):
"""
Look up the passed host device address string as a libvirt node device,
parse its xml, and return a NodeDevice instance.
@param conn: libvirt.virConnect instance to perform the lookup on
@param addrstr: host device string to parse and lookup
- bus.addr (ex. 001.003 for a usb device)
- vendor:product (ex. 0x1234:0x5678 for a usb device
- (domain:)bus:slot.func (ex. 00:10.0 for a pci device)
@param addrstr: C{str}
"""
if not is_nodedev_capable(conn):
raise ValueError(_("Connection does not support host device "
"enumeration."))
ret = _isAddressStr(addrstr)
if not ret:
raise ValueError(_("Could not determine format of '%s'") % addrstr)
cmp_func, devtype, addr_type = ret
# Iterate over node devices and compare
count = 0
nodedev = None
nodenames = conn.listDevices(devtype, 0)
for name in nodenames:
tmpnode = _lookupNodeName(conn, name)
if cmp_func(tmpnode):
nodedev = tmpnode
count += 1
if count == 1:
return nodedev, addr_type
elif count > 1:
raise ValueError(_("%s corresponds to multiple node devices") %
addrstr)
elif count < 1:
raise ValueError(_("Did not find a matching node device for '%s'") %
addrstr)
def parse(xml):
"""
Convert the passed libvirt node device xml into a NodeDevice object
@param xml: libvirt node device xml
@type xml: C{str}
@returns: L{NodeDevice} instance
"""
def _parse_func(root):
t = _findNodeType(root)
devclass = _typeToDeviceClass(t)
device = devclass(root)
return device
return util.parse_node_helper(xml, "device", _parse_func)
def _findNodeType(node):
child = node.children
while child:
if child.name == "capability":
return child.prop("type")
child = child.next
return None
def _typeToDeviceClass(t):
if t == CAPABILITY_TYPE_SYSTEM:
return SystemDevice
elif t == CAPABILITY_TYPE_NET:
return NetDevice
elif t == CAPABILITY_TYPE_PCI:
return PCIDevice
elif t == CAPABILITY_TYPE_USBDEV:
return USBDevice
elif t == CAPABILITY_TYPE_USBBUS:
return USBBus
elif t == CAPABILITY_TYPE_STORAGE:
return StorageDevice
elif t == CAPABILITY_TYPE_SCSIBUS:
return SCSIBus
elif t == CAPABILITY_TYPE_SCSIDEV:
return SCSIDevice
else:
return NodeDevice