2013-03-18 01:06:52 +04:00
#
2013-10-28 00:59:46 +04:00
# Copyright 2009, 2013 Red Hat, Inc.
2013-03-18 01:06:52 +04:00
#
2018-04-04 16:35:41 +03:00
# This work is licensed under the GNU GPLv2 or later.
2018-03-20 22:00:02 +03:00
# See the COPYING file in the top-level directory.
2013-03-18 01:06:52 +04:00
import logging
2017-02-21 16:00:53 +03:00
import os
2013-03-18 01:06:52 +04:00
2017-02-21 16:00:53 +03:00
from . xmlbuilder import XMLBuilder , XMLProperty , XMLChildProperty
2013-03-18 01:06:52 +04:00
2013-04-13 22:34:52 +04:00
2014-09-20 21:52:52 +04:00
def _compare_int ( nodedev_val , hostdev_val ) :
def _intify ( val ) :
try :
if " 0x " in str ( val ) :
return int ( val or ' 0x00 ' , 16 )
else :
return int ( val )
2017-07-24 11:26:48 +03:00
except Exception :
2014-09-20 21:52:52 +04:00
return - 1
2014-09-16 19:44:21 +04:00
2014-09-20 21:52:52 +04:00
nodedev_val = _intify ( nodedev_val )
hostdev_val = _intify ( hostdev_val )
return ( nodedev_val == hostdev_val or hostdev_val == - 1 )
2014-09-16 19:44:21 +04:00
2017-02-21 16:00:53 +03:00
class DevNode ( XMLBuilder ) :
2018-03-21 17:53:34 +03:00
XML_NAME = " devnode "
2017-02-21 16:00:53 +03:00
node_type = XMLProperty ( " ./@type " )
path = XMLProperty ( " . " )
2013-09-23 02:13:24 +04:00
class NodeDevice ( XMLBuilder ) :
CAPABILITY_TYPE_NET = " net "
CAPABILITY_TYPE_PCI = " pci "
CAPABILITY_TYPE_USBDEV = " usb_device "
CAPABILITY_TYPE_STORAGE = " storage "
CAPABILITY_TYPE_SCSIBUS = " scsi_host "
CAPABILITY_TYPE_SCSIDEV = " scsi "
2017-02-21 16:00:54 +03:00
CAPABILITY_TYPE_DRM = " drm "
2013-09-23 02:13:24 +04:00
@staticmethod
2014-09-20 19:30:24 +04:00
def lookupNodedevFromString ( conn , idstring ) :
2013-09-23 02:13:24 +04:00
"""
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
2013-05-14 00:45:51 +04:00
2018-02-14 15:17:31 +03:00
: param conn : libvirt . virConnect instance to perform the lookup on
: param idstring : libvirt node device name to lookup , or address
2014-09-20 19:30:24 +04:00
of the form :
- 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 )
2013-03-18 01:06:52 +04:00
2018-02-14 15:17:31 +03:00
: returns : NodeDevice instance
2013-09-23 02:13:24 +04:00
"""
2013-10-06 18:08:04 +04:00
if not conn . check_support ( conn . SUPPORT_CONN_NODEDEV ) :
2013-09-23 02:13:24 +04:00
raise ValueError ( _ ( " Connection does not support host device "
" enumeration. " ) )
2014-09-20 19:30:24 +04:00
# First try and see if this is a libvirt nodedev name
for nodedev in conn . fetch_all_nodedevs ( ) :
if nodedev . name == idstring :
return nodedev
2013-09-23 02:13:24 +04:00
try :
2014-09-20 19:30:24 +04:00
return _AddressStringToNodedev ( conn , idstring )
2017-03-30 17:43:44 +03:00
except Exception :
2014-09-20 19:30:24 +04:00
logging . debug ( " Error looking up nodedev from idstring= %s " ,
idstring , exc_info = True )
2017-03-29 21:09:35 +03:00
raise
2013-09-23 02:13:24 +04:00
@staticmethod
def parse ( conn , xml ) :
tmpdev = NodeDevice ( conn , parsexml = xml , allow_node_instantiate = True )
cls = _typeToDeviceClass ( tmpdev . device_type )
return cls ( conn , parsexml = xml , allow_node_instantiate = True )
def __init__ ( self , * args , * * kwargs ) :
instantiate = kwargs . pop ( " allow_node_instantiate " , False )
if self . __class__ is NodeDevice and not instantiate :
raise RuntimeError ( " Can not instantiate NodeDevice directly " )
2013-09-28 02:06:39 +04:00
2013-09-23 02:13:24 +04:00
XMLBuilder . __init__ ( self , * args , * * kwargs )
2018-03-21 17:53:34 +03:00
XML_NAME = " device "
2013-09-23 02:13:24 +04:00
2015-03-27 01:04:23 +03:00
# Libvirt can generate bogus 'system' XML:
# https://bugzilla.redhat.com/show_bug.cgi?id=1184131
_XML_SANITIZE = True
2013-09-23 02:13:24 +04:00
name = XMLProperty ( " ./name " )
parent = XMLProperty ( " ./parent " )
device_type = XMLProperty ( " ./capability/@type " )
2017-02-21 16:00:53 +03:00
devnodes = XMLChildProperty ( DevNode )
def get_devnode ( self , parent = " by-path " ) :
for d in self . devnodes :
paths = d . path . split ( os . sep )
if len ( paths ) > 2 and paths [ - 2 ] == parent :
return d
if len ( self . devnodes ) > 0 :
return self . devnodes [ 0 ]
return None
2013-03-18 01:06:52 +04:00
2019-06-05 22:11:31 +03:00
def compare_to_hostdev ( self , hostdev ) :
2019-06-05 22:29:10 +03:00
if self . device_type == " pci " :
2019-06-05 22:39:09 +03:00
if hostdev . type != " pci " :
2019-06-05 22:29:10 +03:00
return False
return ( _compare_int ( self . domain , hostdev . domain ) and
_compare_int ( self . bus , hostdev . bus ) and
_compare_int ( self . slot , hostdev . slot ) and
_compare_int ( self . function , hostdev . function ) )
2019-06-05 22:39:09 +03:00
if self . device_type == " usb_device " :
if hostdev . type != " usb " :
return False
return ( _compare_int ( self . product_id , hostdev . product ) and
_compare_int ( self . vendor_id , hostdev . vendor ) and
_compare_int ( self . bus , hostdev . bus ) and
_compare_int ( self . device , hostdev . device ) )
2019-06-05 22:11:31 +03:00
return False
2019-06-05 22:39:09 +03:00
def _usb_pretty_name ( self ) :
# Hypervisor may return a rather sparse structure, missing
# some ol all stringular descriptions of the device altogether.
# Do our best to help user identify the device.
# Certain devices pad their vendor with trailing spaces,
# such as "LENOVO ". It does not look well.
product = str ( self . _product_name ) . strip ( )
vendor = str ( self . _vendor_name ) . strip ( )
if product == " " :
product = str ( self . product_id )
if vendor == " " :
# No stringular descriptions altogether
vendor = str ( self . vendor_id )
devstr = " %s : %s " % ( vendor , product )
else :
# Only the vendor is known
devstr = " %s %s " % ( vendor , product )
else :
if vendor == " " :
# Sometimes vendor is left out empty, but product is
# already descriptive enough or contains the vendor string:
# "Lenovo USB Laser Mouse"
devstr = product
else :
# We know everything. Perfect.
devstr = " %s %s " % ( vendor , product )
busstr = " %.3d : %.3d " % ( int ( self . bus ) , int ( self . device ) )
desc = " %s %s " % ( busstr , devstr )
return desc
2014-01-12 23:52:15 +04:00
def pretty_name ( self ) :
2013-03-18 01:06:52 +04:00
"""
Use device information to attempt to print a human readable device
name .
2018-02-14 15:17:31 +03:00
: returns : Device description string
2013-03-18 01:06:52 +04:00
"""
2019-06-05 22:11:31 +03:00
if self . device_type == " net " :
if self . interface :
2019-06-05 23:00:25 +03:00
return _ ( " Interface %s " ) % self . interface
return self . name
2019-06-05 22:29:10 +03:00
if self . device_type == " pci " :
devstr = " %.4X : %.2X : %.2X : %X " % ( int ( self . domain ) ,
int ( self . bus ) ,
int ( self . slot ) ,
int ( self . function ) )
2019-06-05 23:00:25 +03:00
return " %s %s %s " % ( devstr ,
self . _vendor_name , self . _product_name )
2019-06-05 22:39:09 +03:00
if self . device_type == " usb_device " :
2019-06-05 23:00:25 +03:00
return self . _usb_pretty_name ( )
2019-06-05 22:39:09 +03:00
2019-06-05 23:00:25 +03:00
return self . name
2013-03-18 01:06:52 +04:00
2014-09-16 19:44:21 +04:00
2019-06-05 22:29:10 +03:00
########################
# XML helper functions #
########################
def is_pci_sriov ( self ) :
return self . _capability_type == " virt_functions "
def is_pci_bridge ( self ) :
return self . _capability_type == " pci-bridge "
2019-06-05 22:39:09 +03:00
def is_usb_linux_root_hub ( self ) :
return ( self . vendor_id == " 0x1d6b " and
self . product_id in [ " 0x0001 " , " 0x0002 " , " 0x0003 " ] )
2019-06-05 22:29:10 +03:00
2019-06-05 22:11:31 +03:00
##################
# XML properties #
##################
2013-04-13 22:34:52 +04:00
2019-06-05 22:11:31 +03:00
# type='net' options
2013-09-23 02:13:24 +04:00
interface = XMLProperty ( " ./capability/interface " )
2013-03-18 01:06:52 +04:00
2019-06-05 22:29:10 +03:00
# type='pci' options
2013-09-23 02:13:24 +04:00
domain = XMLProperty ( " ./capability/domain " )
bus = XMLProperty ( " ./capability/bus " )
slot = XMLProperty ( " ./capability/slot " )
function = XMLProperty ( " ./capability/function " )
2019-06-05 22:29:10 +03:00
_product_name = XMLProperty ( " ./capability/product " )
_vendor_name = XMLProperty ( " ./capability/vendor " )
_capability_type = XMLProperty ( " ./capability/capability/@type " )
2013-03-18 01:06:52 +04:00
2019-06-05 22:39:09 +03:00
# type='usb' options
2013-09-23 02:13:24 +04:00
device = XMLProperty ( " ./capability/device " )
product_id = XMLProperty ( " ./capability/product/@id " )
vendor_id = XMLProperty ( " ./capability/vendor/@id " )
2013-03-18 01:06:52 +04:00
2019-06-05 22:46:26 +03:00
# type='scsi' options
host = XMLProperty ( " ./capability/host " )
target = XMLProperty ( " ./capability/target " )
lun = XMLProperty ( " ./capability/lun " )
2019-06-05 23:00:25 +03:00
# type='storage' options
2013-09-23 02:13:24 +04:00
block = XMLProperty ( " ./capability/block " )
drive_type = XMLProperty ( " ./capability/drive_type " )
media_label = XMLProperty (
" ./capability/capability[@type= ' removable ' ]/media_label " )
2019-06-05 23:00:25 +03:00
media_available = XMLProperty (
2013-09-23 02:13:24 +04:00
" ./capability/capability[@type= ' removable ' ]/media_available " ,
is_int = True )
2013-03-18 01:06:52 +04:00
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
class SCSIBus ( NodeDevice ) :
2013-09-23 02:13:24 +04:00
host = XMLProperty ( " ./capability/host " )
2013-03-18 01:06:52 +04:00
2013-09-23 02:13:24 +04:00
vport_ops = XMLProperty (
" ./capability/capability[@type= ' vport_ops ' ] " , is_bool = True )
2013-04-13 22:34:52 +04:00
2013-09-23 02:13:24 +04:00
fc_host = XMLProperty (
" ./capability/capability[@type= ' fc_host ' ] " , is_bool = True )
wwnn = XMLProperty ( " ./capability/capability[@type= ' fc_host ' ]/wwnn " )
wwpn = XMLProperty ( " ./capability/capability[@type= ' fc_host ' ]/wwpn " )
2013-03-18 01:06:52 +04:00
2013-04-13 22:34:52 +04:00
2017-02-21 16:00:54 +03:00
class DRMDevice ( NodeDevice ) :
drm_type = XMLProperty ( " ./capability/type " )
def drm_pretty_name ( self , conn ) :
parent = NodeDevice . lookupNodedevFromString ( conn , self . parent )
return " %s ( %s ) " % ( parent . pretty_name ( ) , self . drm_type )
2014-09-20 20:58:51 +04:00
def _AddressStringToHostdev ( conn , addrstr ) :
2018-03-20 19:27:37 +03:00
from . devices import DeviceHostdev
2018-03-20 19:18:35 +03:00
hostdev = DeviceHostdev ( conn )
2013-03-18 01:06:52 +04:00
try :
# Determine addrstr type
2018-02-14 03:04:08 +03:00
if addrstr . count ( " : " ) in [ 1 , 2 ] and " . " in addrstr :
2013-03-18 01:06:52 +04:00
addrstr , func = addrstr . split ( " . " , 1 )
addrstr , slot = addrstr . rsplit ( " : " , 1 )
domain = " 0 "
2018-02-14 03:04:08 +03:00
if " : " in addrstr :
2013-03-18 01:06:52 +04:00
domain , bus = addrstr . split ( " : " , 1 )
else :
bus = addrstr
2014-09-20 20:58:51 +04:00
hostdev . type = " pci "
hostdev . domain = " 0x %.4X " % int ( domain , 16 )
hostdev . function = " 0x %.2X " % int ( func , 16 )
hostdev . slot = " 0x %.2X " % int ( slot , 16 )
hostdev . bus = " 0x %.2X " % int ( bus , 16 )
2013-03-18 01:06:52 +04:00
2018-02-14 03:04:08 +03:00
elif " : " in addrstr :
2013-03-18 01:06:52 +04:00
vendor , product = addrstr . split ( " : " )
2014-09-20 20:58:51 +04:00
hostdev . type = " usb "
hostdev . vendor = " 0x %.4X " % int ( vendor , 16 )
hostdev . product = " 0x %.4X " % int ( product , 16 )
2013-03-18 01:06:52 +04:00
2018-02-14 03:04:08 +03:00
elif " . " in addrstr :
2014-09-20 20:58:51 +04:00
bus , device = addrstr . split ( " . " , 1 )
hostdev . type = " usb "
hostdev . bus = bus
hostdev . device = device
2013-09-28 02:06:39 +04:00
else :
2014-03-22 19:21:19 +04:00
raise RuntimeError ( " Unknown address type " )
2017-07-24 11:26:48 +03:00
except Exception :
2014-09-20 19:30:24 +04:00
logging . debug ( " Error parsing node device string. " , exc_info = True )
2014-03-22 19:21:19 +04:00
raise
2013-03-18 01:06:52 +04:00
2014-09-20 20:58:51 +04:00
return hostdev
2013-03-18 01:06:52 +04:00
2013-04-13 22:34:52 +04:00
2014-09-20 19:30:24 +04:00
def _AddressStringToNodedev ( conn , addrstr ) :
2014-09-20 20:58:51 +04:00
hostdev = _AddressStringToHostdev ( conn , addrstr )
2013-03-18 01:06:52 +04:00
# Iterate over node devices and compare
2013-04-24 14:05:08 +04:00
count = 0
nodedev = None
2014-09-20 19:30:24 +04:00
for xmlobj in conn . fetch_all_nodedevs ( ) :
2014-09-20 20:58:51 +04:00
if xmlobj . compare_to_hostdev ( hostdev ) :
2014-09-20 19:30:24 +04:00
nodedev = xmlobj
2013-04-24 14:05:08 +04:00
count + = 1
if count == 1 :
2013-09-28 02:06:39 +04:00
return nodedev
2013-04-24 14:05:08 +04:00
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 )
2013-03-18 01:06:52 +04:00
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
def _typeToDeviceClass ( t ) :
2019-06-05 23:00:25 +03:00
if t == NodeDevice . CAPABILITY_TYPE_SCSIBUS :
2013-03-18 01:06:52 +04:00
return SCSIBus
2017-02-21 16:00:54 +03:00
elif t == NodeDevice . CAPABILITY_TYPE_DRM :
return DRMDevice
2013-03-18 01:06:52 +04:00
else :
return NodeDevice