2013-03-18 01:06:52 +04:00
#
# 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.
2013-04-11 03:48:07 +04:00
from virtinst import support
from virtinst import util
2013-03-18 01:06:52 +04:00
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 "
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
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
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
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
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
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 ) :
2013-04-13 22:34:52 +04:00
value_map = { " interface " : " interface " ,
" address " : " address " }
2013-03-18 01:06:52 +04:00
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
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
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 ) :
2013-04-13 22:34:52 +04:00
val_map = { " domain " : " domain " ,
2013-03-18 01:06:52 +04:00
" bus " : " bus " ,
" slot " : " slot " ,
2013-04-13 22:34:52 +04:00
" function " : " function " }
2013-03-18 01:06:52 +04:00
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
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
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 ) :
2013-04-13 22:34:52 +04:00
val_map = { " bus " : " bus " , " device " : " device " }
2013-03-18 01:06:52 +04:00
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
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
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 ) :
2013-04-13 22:34:52 +04:00
val_map = { " block " : " block " ,
2013-03-18 01:06:52 +04:00
" 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
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
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 ) :
2013-04-13 22:34:52 +04:00
val_map = { " number " : " number " ,
2013-03-18 01:06:52 +04:00
" class " : " classval " ,
" subclass " : " subclass " ,
2013-04-13 22:34:52 +04:00
" protocol " : " protocol " }
2013-03-18 01:06:52 +04:00
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 ) :
2013-04-13 22:34:52 +04:00
val_map = { " host " : " host " ,
2013-03-18 01:06:52 +04:00
" bus " : " bus " ,
" target " : " target " ,
" lun " : " lun " ,
" type " : " type " }
self . _parseHelper ( node , val_map )
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
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 ) :
2013-04-13 22:34:52 +04:00
val_map = { " host " : " host " }
2013-03-18 01:06:52 +04:00
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
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
def is_nodedev_capable ( conn ) :
"""
Check if the passed libvirt connection supports host device routines
@param conn : Connection to check
@type conn : libvirt . virConnect
@rtype : C { bool }
"""
return support . check_conn_support ( conn , support . SUPPORT_CONN_NODEDEV )
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
def is_pci_detach_capable ( conn ) :
"""
Check if the passed libvirt connection support pci device Detach / Reset
@param conn : Connection to check
@type conn : libvirt . virConnect
@rtype : C { bool }
"""
return support . check_conn_support ( conn ,
support . SUPPORT_NODEDEV_PCI_DETACH )
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
def _lookupNodeName ( conn , name ) :
nodedev = conn . nodeDeviceLookupByName ( name )
xml = nodedev . XMLDesc ( 0 )
return parse ( xml )
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
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 )
except libvirt . libvirtError , e :
ret = _isAddressStr ( name )
if not ret :
raise e
return devAddressToNodedev ( conn , name )
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
def _isAddressStr ( addrstr ) :
cmp_func = 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
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
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
except :
logging . exception ( " Error parsing node device string. " )
return None
return cmp_func , devtype
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
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 = ret
# Iterate over node devices and compare
nodenames = conn . listDevices ( devtype , 0 )
for name in nodenames :
nodedev = _lookupNodeName ( conn , name )
if cmp_func ( nodedev ) :
return nodedev
raise ValueError ( _ ( " Did not find a matching node device for ' %s ' " ) %
addrstr )
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
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
2013-04-11 18:27:02 +04:00
return util . parse_node_helper ( xml , " device " , _parse_func )
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 _findNodeType ( node ) :
child = node . children
while child :
if child . name == " capability " :
return child . prop ( " type " )
child = child . next
return None
2013-04-13 22:34:52 +04:00
2013-03-18 01:06:52 +04:00
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