1# 2# Copyright 2009, 2013 Red Hat, Inc. 3# 4# This work is licensed under the GNU GPLv2 or later. 5# See the COPYING file in the top-level directory. 6 7import os 8 9from .logger import log 10from .xmlbuilder import XMLBuilder, XMLProperty, XMLChildProperty 11 12 13def _compare_int(nodedev_val, hostdev_val): 14 def _intify(val): 15 try: 16 if "0x" in str(val): 17 return int(val or '0x00', 16) 18 else: 19 return int(val) 20 except Exception: 21 return -1 22 23 nodedev_val = _intify(nodedev_val) 24 hostdev_val = _intify(hostdev_val) 25 return (nodedev_val == hostdev_val or hostdev_val == -1) 26 27 28class DevNode(XMLBuilder): 29 XML_NAME = "devnode" 30 31 node_type = XMLProperty("./@type") 32 path = XMLProperty(".") 33 34 35class NodeDevice(XMLBuilder): 36 CAPABILITY_TYPE_NET = "net" 37 CAPABILITY_TYPE_PCI = "pci" 38 CAPABILITY_TYPE_USBDEV = "usb_device" 39 CAPABILITY_TYPE_STORAGE = "storage" 40 CAPABILITY_TYPE_SCSIBUS = "scsi_host" 41 CAPABILITY_TYPE_SCSIDEV = "scsi" 42 CAPABILITY_TYPE_DRM = "drm" 43 44 @staticmethod 45 def lookupNodedevFromString(conn, idstring): 46 """ 47 Convert the passed libvirt node device name to a NodeDevice 48 instance, with proper error reporting. If the name is name is not 49 found, we will attempt to parse the name as would be passed to 50 devAddressToNodeDev 51 52 :param conn: libvirt.virConnect instance to perform the lookup on 53 :param idstring: libvirt node device name to lookup, or address 54 of the form: 55 - bus.addr (ex. 001.003 for a usb device) 56 - vendor:product (ex. 0x1234:0x5678 for a usb device 57 - (domain:)bus:slot.func (ex. 00:10.0 for a pci device) 58 59 :returns: NodeDevice instance 60 """ 61 # First try and see if this is a libvirt nodedev name 62 for nodedev in conn.fetch_all_nodedevs(): 63 if nodedev.name == idstring: 64 return nodedev 65 66 try: 67 return _AddressStringToNodedev(conn, idstring) 68 except Exception: 69 log.debug("Error looking up nodedev from idstring=%s", 70 idstring, exc_info=True) 71 raise 72 73 74 XML_NAME = "device" 75 76 # Libvirt can generate bogus 'system' XML: 77 # https://bugzilla.redhat.com/show_bug.cgi?id=1184131 78 _XML_SANITIZE = True 79 80 name = XMLProperty("./name") 81 parent = XMLProperty("./parent") 82 device_type = XMLProperty("./capability/@type") 83 84 def compare_to_hostdev(self, hostdev): 85 if self.device_type == "pci": 86 if hostdev.type != "pci": 87 return False 88 89 return (_compare_int(self.domain, hostdev.domain) and 90 _compare_int(self.bus, hostdev.bus) and 91 _compare_int(self.slot, hostdev.slot) and 92 _compare_int(self.function, hostdev.function)) 93 94 if self.device_type == "usb_device": 95 if hostdev.type != "usb": 96 return False 97 98 return (_compare_int(self.product_id, hostdev.product) and 99 _compare_int(self.vendor_id, hostdev.vendor) and 100 _compare_int(self.bus, hostdev.bus) and 101 _compare_int(self.device, hostdev.device)) 102 103 return False 104 105 106 ######################## 107 # XML helper functions # 108 ######################## 109 110 def is_pci_sriov(self): 111 return self._capability_type == "virt_functions" 112 def is_pci_bridge(self): 113 return self._capability_type == "pci-bridge" 114 115 def is_usb_linux_root_hub(self): 116 return (self.vendor_id == "0x1d6b" and 117 self.product_id in ["0x0001", "0x0002", "0x0003"]) 118 119 def is_drm_render(self): 120 return self.device_type == "drm" and self.drm_type == "render" 121 122 123 ################## 124 # XML properties # 125 ################## 126 127 # type='net' options 128 interface = XMLProperty("./capability/interface") 129 130 # type='pci' options 131 domain = XMLProperty("./capability/domain") 132 bus = XMLProperty("./capability/bus") 133 slot = XMLProperty("./capability/slot") 134 function = XMLProperty("./capability/function") 135 product_name = XMLProperty("./capability/product") 136 vendor_name = XMLProperty("./capability/vendor") 137 _capability_type = XMLProperty("./capability/capability/@type") 138 139 # type='usb' options 140 device = XMLProperty("./capability/device") 141 product_id = XMLProperty("./capability/product/@id") 142 vendor_id = XMLProperty("./capability/vendor/@id") 143 144 # type='scsi' options 145 host = XMLProperty("./capability/host") 146 target = XMLProperty("./capability/target") 147 lun = XMLProperty("./capability/lun") 148 149 # type='storage' options 150 block = XMLProperty("./capability/block") 151 drive_type = XMLProperty("./capability/drive_type") 152 153 media_label = XMLProperty( 154 "./capability/capability[@type='removable']/media_label") 155 media_available = XMLProperty( 156 "./capability/capability[@type='removable']/media_available", 157 is_int=True) 158 159 # type='drm' options 160 drm_type = XMLProperty("./capability/type") 161 devnodes = XMLChildProperty(DevNode) 162 163 def get_devnode(self, parent="by-path"): 164 for d in self.devnodes: 165 paths = d.path.split(os.sep) 166 if len(paths) > 2 and paths[-2] == parent: 167 return d 168 if len(self.devnodes) > 0: 169 return self.devnodes[0] 170 171 172def _AddressStringToHostdev(conn, addrstr): 173 from .devices import DeviceHostdev 174 hostdev = DeviceHostdev(conn) 175 176 try: 177 # Determine addrstr type 178 if addrstr.count(":") in [1, 2] and "." in addrstr: 179 addrstr, func = addrstr.split(".", 1) 180 addrstr, slot = addrstr.rsplit(":", 1) 181 domain = "0" 182 if ":" in addrstr: 183 domain, bus = addrstr.split(":", 1) 184 else: 185 bus = addrstr 186 187 hostdev.type = "pci" 188 hostdev.domain = "0x%.4X" % int(domain, 16) 189 hostdev.function = "0x%.2X" % int(func, 16) 190 hostdev.slot = "0x%.2X" % int(slot, 16) 191 hostdev.bus = "0x%.2X" % int(bus, 16) 192 193 elif ":" in addrstr: 194 vendor, product = addrstr.split(":") 195 196 hostdev.type = "usb" 197 hostdev.vendor = "0x%.4X" % int(vendor, 16) 198 hostdev.product = "0x%.4X" % int(product, 16) 199 200 elif "." in addrstr: 201 bus, device = addrstr.split(".", 1) 202 203 hostdev.type = "usb" 204 hostdev.bus = bus 205 hostdev.device = device 206 else: 207 raise RuntimeError("Unknown address type") 208 except Exception: 209 log.debug("Error parsing node device string.", exc_info=True) 210 raise 211 212 return hostdev 213 214 215def _AddressStringToNodedev(conn, addrstr): 216 hostdev = _AddressStringToHostdev(conn, addrstr) 217 218 # Iterate over node devices and compare 219 count = 0 220 nodedev = None 221 222 for xmlobj in conn.fetch_all_nodedevs(): 223 if xmlobj.compare_to_hostdev(hostdev): 224 nodedev = xmlobj 225 count += 1 226 227 if count == 1: 228 return nodedev 229 elif count > 1: 230 raise ValueError(_("%s corresponds to multiple node devices") % 231 addrstr) 232 elif count < 1: 233 raise ValueError(_("Did not find a matching node device for '%s'") % 234 addrstr) 235