Skip to content

Commit

Permalink
Adding support for falling back to lspci-vv/lspci-n if they are avail…
Browse files Browse the repository at this point in the history
…able when operating in log mode.

Signed-off-by: Rob Dobson <rob.dobson@citrix.com>
  • Loading branch information
rdobson committed Jul 16, 2014
1 parent 3d08a04 commit 055417b
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 23 deletions.
55 changes: 40 additions & 15 deletions hwinfo/pci/__init__.py
Expand Up @@ -2,65 +2,90 @@

class PCIDevice(object):

NONE_VALUE = 'unknown'

def __init__(self, record):
self.rec = record

def lookup_value(self, k):
if k in self.rec:
return self.rec[k]
else:
return "Unknown"
return None

def _fmt(self, value, wrap=None):
if not value:
return self.NONE_VALUE
else:
if wrap:
return "%s%s%s" % (wrap, value, wrap)
else:
return value

def get_device_name(self):
wrap = None
name = self.lookup_value('pci_device_name')

# Fall back to using pci_device_string if it exists.
if not name:
name = self.lookup_value('pci_device_string')
wrap = '-'

if name == 'Device':
# If the input has come from lspci, this is the value for
# not being able to find a key in the pciids db.
return '[Device %s]' % self.get_device_id()
else:
return name
return self._fmt(name, wrap)


def get_device_id(self):
return self.lookup_value('pci_device_id')
return self._fmt(self.lookup_value('pci_device_id'))

def get_vendor_name(self):
return self.lookup_value('pci_vendor_name')
return self._fmt(self.lookup_value('pci_vendor_name'))

def get_vendor_id(self):
return self.lookup_value('pci_vendor_id')
return self._fmt(self.lookup_value('pci_vendor_id'))

def get_subdevice_name(self):
name = self.lookup_value('pci_subdevice_name')
wrap = None

# Fall back to using pci_device_string if it exists.
if not name:
name = self.lookup_value('pci_device_sub_string')
wrap = '-'

if name == 'Device':
# If the input has come from lspci, this is the value for
# not being able to find a key in the pciids db.
return '[Device %s]' % self.get_subdevice_id()
else:
return name
return self._fmt(name, wrap)

def get_subdevice_id(self):
return self.lookup_value('pci_subdevice_id')
return self._fmt(self.lookup_value('pci_subdevice_id'))

def get_subvendor_name(self):
return self.lookup_value('pci_subvendor_name')
return self._fmt(self.lookup_value('pci_subvendor_name'))

def get_subvendor_id(self):
return self.lookup_value('pci_subvendor_id')
return self._fmt(self.lookup_value('pci_subvendor_id'))

def get_pci_id(self):
return "%s:%s %s:%s" % (
self.lookup_value('pci_vendor_id'),
self.lookup_value('pci_device_id'),
self.lookup_value('pci_subvendor_id'),
self.lookup_value('pci_subdevice_id'),
self._fmt(self.lookup_value('pci_vendor_id')),
self._fmt(self.lookup_value('pci_device_id')),
self._fmt(self.lookup_value('pci_subvendor_id')),
self._fmt(self.lookup_value('pci_subdevice_id')),
)

def get_pci_class(self):
return self.lookup_value('pci_device_class')
return self._fmt(self.lookup_value('pci_device_class'))

def is_subdevice(self):
return self.lookup_value('pci_subvendor_id') and self.lookup_value('pci_subdevice_id')
return self.lookup_value('pci_subvendor_id') and self.lookup_value('pci_subdevice_id') or self.lookup_value('pci_device_sub_string')

def get_info(self):

Expand Down
14 changes: 7 additions & 7 deletions hwinfo/pci/lspci.py
Expand Up @@ -5,11 +5,15 @@
class ParserException(Exception):
pass

LABEL_REGEX = r'[\w+\ \.\-\/\[\]\(\)]+'
CODE_REGEX = r'[0-9a-fA-F]{4}'
BUSID_REGEX = r'[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F]'

class LspciVVParser(CommandParser):
"""Parser object for the output of lspci -vv"""

ITEM_REGEXS = [
r'(?P<pci_device_bus_id>([0-9][0-9]:[0-9][0-9]\.[0-9]))\ (?P<pci_device_class_name>[\w\ ]*):\ (?P<pci_device_string>(.*))\n',
r'(?P<pci_device_bus_id>(' + BUSID_REGEX + r'))\ (?P<pci_device_class_name>' + LABEL_REGEX + r'):\ (?P<pci_device_string>(.*))\n',
r'Product\ Name:\ (?P<pci_device_vpd_product_name>(.)*)\n',
r'Subsystem:\ (?P<pci_device_sub_string>(.)*)\n',
]
Expand All @@ -27,7 +31,7 @@ class LspciNParser(CommandParser):

#ff:0d.1 0880: 8086:0ee3 (rev 04)
ITEM_REGEXS = [
r'(?P<pci_device_bus_id>([0-9a-f][0-9a-f]:[0-9a-f][0-9a-f]\.[0-9a-f]))\ (?P<pci_device_class>[0-9a-f]{4}):\ (?P<pci_vendor_id>[0-9a-f]{4}):(?P<pci_device_id>[0-9a-f]{4})',
r'(?P<pci_device_bus_id>(' + BUSID_REGEX + r'))\ (?P<pci_device_class>' + CODE_REGEX + r'):\ (?P<pci_vendor_id>' + CODE_REGEX + r'):(?P<pci_device_id>' + CODE_REGEX + r')',
]

ITEM_SEPERATOR = "\n"
Expand All @@ -39,17 +43,13 @@ class LspciNParser(CommandParser):
'pci_device_class',
]


LABEL_REGEX = r'[\w+\ \.\-\/\[\]\(\)]+'
CODE_REGEX = r'[0-9a-fA-F]{4}'

class LspciNNMMParser(CommandParser):
"""Parser object for the output of lspci -nnmm"""

#02:00.1 "Ethernet controller [0200]" "Broadcom Corporation [14e4]" "NetXtreme II BCM5716 Gigabit Ethernet [163b]" -r20 "Dell [1028]" "Device [02a3]"

ITEM_REGEXS = [
r'(?P<pci_device_bus_id>([0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F]))\ "(?P<pci_device_class_name>' + LABEL_REGEX + r')\ \[(?P<pci_device_class>' + CODE_REGEX + r')\]"' \
r'(?P<pci_device_bus_id>(' + BUSID_REGEX + r'))\ "(?P<pci_device_class_name>' + LABEL_REGEX + r')\ \[(?P<pci_device_class>' + CODE_REGEX + r')\]"' \
+ r'\ "(?P<pci_vendor_name>' + LABEL_REGEX + r')\ \[(?P<pci_vendor_id>' + CODE_REGEX + r')\]"\ "(?P<pci_device_name>' + LABEL_REGEX + r')\ \[(?P<pci_device_id>' + CODE_REGEX + r')\]"' \
+ r'\ .*\"((?P<pci_subvendor_name>' + LABEL_REGEX + r')\ \[(?P<pci_subvendor_id>' + CODE_REGEX + r')\])*"\ "((?P<pci_subdevice_name>' + LABEL_REGEX + r')\ \[(?P<pci_subdevice_id>' + CODE_REGEX + r')\])*',
]
Expand Down
7 changes: 7 additions & 0 deletions hwinfo/pci/tests/test_lspci.py
Expand Up @@ -62,6 +62,13 @@ def setUp(self):
def test_parse_all_devices(self):
recs = self.parser.parse_items()
self.assertEqual(len(recs), 58)
found = False
for rec in recs:
print rec
if rec['pci_device_bus_id'] == '02:00.0':
self.assertEqual(rec['pci_device_class_name'], 'VGA compatible controller')
found = True
self.assertEqual(found, True)

class TestSingleDeviceNParse(unittest.TestCase):

Expand Down
38 changes: 37 additions & 1 deletion hwinfo/tools/inspector.py
Expand Up @@ -77,18 +77,40 @@ def get_cpu_info(self):
parser = cpuinfo.CPUInfoParser(data)
return parser.parse_items()

class FileNotFound(Exception):
pass

def search_for_file(dirname, filename):
for root, _, files in os.walk(dirname):
if filename in files:
return os.path.join(root, filename)
raise Exception("Could not find '%s' in directory '%s'" % (filename, dirname))
raise FileNotFound("Could not find '%s' in directory '%s'" % (filename, dirname))

def read_from_file(filename):
fh = open(filename, 'r')
data = fh.read()
fh.close()
return data

def parse_data(parser, data):
p = parser(data)
return p.parse_items()

def combine_recs(rec_list, key):
"""Use a common key to combine a list of recs"""
final_recs = {}
for rec in rec_list:
rec_key = rec[key]
if rec_key in final_recs:
for k, v in rec.iteritems():
if k in final_recs[rec_key] and final_recs[rec_key][k] != v:
raise Exception("Mis-match for key '%s'" % k)
final_recs[rec_key][k] = v
else:
final_recs[rec_key] = rec
return final_recs.values()


class HostFromLogs(Host):

def __init__(self, dirname):
Expand All @@ -107,6 +129,20 @@ def get_dmidecode_data(self):
def get_cpuinfo_data(self):
return self._load_from_file('cpuinfo')

def get_pci_devices(self):
try:
devs = super(HostFromLogs, self).get_pci_devices()
return devs
except FileNotFound:
# Fall back to looking for the file lspci-vv.out
print "***lspci-nnm.out found. Falling back to looking for lspci-vv.out and lspci-n.out.***"
lspci_vv_recs = parse_data(LspciVVParser, self._load_from_file('lspci-vv.out'))
lspci_n_recs = parse_data(LspciNParser, self._load_from_file('lspci-n.out'))
all_recs = lspci_vv_recs + lspci_n_recs
recs = combine_recs(all_recs, 'pci_device_bus_id')
return [PCIDevice(rec) for rec in recs]


def pci_filter(devices, types):
res = []
for device in devices:
Expand Down
113 changes: 113 additions & 0 deletions hwinfo/tools/tests/test_inspector.py
Expand Up @@ -142,4 +142,117 @@ def test_rec_to_table(self, mock_pt_cls):
]
mock_table.add_row.assert_has_calls(expected_calls, any_order=True)

class CombineRecsTests(unittest.TestCase):


def _validate_rec(self, rec, key, value):
self.assertEqual(rec[key], value)

def test_combine_two_recs(self):
recs = [
{
'name':'rec1',
'valuea': '10',
'valueb': '11',
'valuec': '12',
},
{
'name': 'rec1',
'valued': '5',
'valuee': '8',
},
]

combined_recs = inspector.combine_recs(recs, 'name')
self.assertEqual(len(combined_recs), 1)
rec = combined_recs[0]
self._validate_rec(rec, 'valuea', '10')
self._validate_rec(rec, 'valueb', '11')
self._validate_rec(rec, 'valuec', '12')
self._validate_rec(rec, 'valued', '5')
self._validate_rec(rec, 'valuee', '8')

def test_combine_three_recs(self):
recs = [
{
'name':'rec1',
'valuea': '10',
'valueb': '11',
'valuec': '12',
},
{
'name': 'rec1',
'valued': '5',
'valuee': '8',
},
{
'name': 'rec1',
'valuef': '1',
'valueg': '2',
},
]

combined_recs = inspector.combine_recs(recs, 'name')
self.assertEqual(len(combined_recs), 1)
rec = combined_recs[0]
self._validate_rec(rec, 'valuea', '10')
self._validate_rec(rec, 'valueb', '11')
self._validate_rec(rec, 'valuec', '12')
self._validate_rec(rec, 'valued', '5')
self._validate_rec(rec, 'valuee', '8')
self._validate_rec(rec, 'valuef', '1')
self._validate_rec(rec, 'valueg', '2')

def test_combine_three_recs_to_two(self):
recs = [
{
'name':'rec1',
'valuea': '10',
'valueb': '11',
'valuec': '12',
},
{
'name': 'rec2',
'valued': '5',
'valuee': '8',
},
{
'name': 'rec1',
'valuef': '1',
'valueg': '2',
},
]

combined_recs = inspector.combine_recs(recs, 'name')
self.assertEqual(len(combined_recs), 2)
for rec in combined_recs:
if rec['name'] == 'rec1':
self._validate_rec(rec, 'valuea', '10')
self._validate_rec(rec, 'valueb', '11')
self._validate_rec(rec, 'valuec', '12')
self._validate_rec(rec, 'valuef', '1')
self._validate_rec(rec, 'valueg', '2')
elif rec['name'] == 'rec2':
self._validate_rec(rec, 'valued', '5')
self._validate_rec(rec, 'valuee', '8')
else:
raise Exception("Unexpected rec: %s" % rec)

def test_colliding_values(self):
recs = [
{
'name':'rec1',
'valuea': '10',
'valueb': '11',
'valuec': '12',
},
{
'name': 'rec1',
'valuea': '5',
'valuee': '8',
},
]
with self.assertRaises(Exception) as context:
combined_recs = inspector.combine_recs(recs, 'name')
self.assertEqual(context.exception.message, "Mis-match for key 'valuea'")

0 comments on commit 055417b

Please sign in to comment.