Skip to content

Commit 82211e4

Browse files
committed
Merge pull request #10 from rdobson/lspcivv
Add ability to fall back to using lspci-vv/lspci-n to grab info for the CLI
2 parents 3b901a4 + 055417b commit 82211e4

File tree

5 files changed

+220
-43
lines changed

5 files changed

+220
-43
lines changed

hwinfo/pci/__init__.py

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,65 +2,90 @@
22

33
class PCIDevice(object):
44

5+
NONE_VALUE = 'unknown'
6+
57
def __init__(self, record):
68
self.rec = record
79

810
def lookup_value(self, k):
911
if k in self.rec:
1012
return self.rec[k]
1113
else:
12-
return "Unknown"
14+
return None
15+
16+
def _fmt(self, value, wrap=None):
17+
if not value:
18+
return self.NONE_VALUE
19+
else:
20+
if wrap:
21+
return "%s%s%s" % (wrap, value, wrap)
22+
else:
23+
return value
1324

1425
def get_device_name(self):
26+
wrap = None
1527
name = self.lookup_value('pci_device_name')
28+
29+
# Fall back to using pci_device_string if it exists.
30+
if not name:
31+
name = self.lookup_value('pci_device_string')
32+
wrap = '-'
33+
1634
if name == 'Device':
1735
# If the input has come from lspci, this is the value for
1836
# not being able to find a key in the pciids db.
1937
return '[Device %s]' % self.get_device_id()
2038
else:
21-
return name
39+
return self._fmt(name, wrap)
2240

2341

2442
def get_device_id(self):
25-
return self.lookup_value('pci_device_id')
43+
return self._fmt(self.lookup_value('pci_device_id'))
2644

2745
def get_vendor_name(self):
28-
return self.lookup_value('pci_vendor_name')
46+
return self._fmt(self.lookup_value('pci_vendor_name'))
2947

3048
def get_vendor_id(self):
31-
return self.lookup_value('pci_vendor_id')
49+
return self._fmt(self.lookup_value('pci_vendor_id'))
3250

3351
def get_subdevice_name(self):
3452
name = self.lookup_value('pci_subdevice_name')
53+
wrap = None
54+
55+
# Fall back to using pci_device_string if it exists.
56+
if not name:
57+
name = self.lookup_value('pci_device_sub_string')
58+
wrap = '-'
59+
3560
if name == 'Device':
3661
# If the input has come from lspci, this is the value for
3762
# not being able to find a key in the pciids db.
3863
return '[Device %s]' % self.get_subdevice_id()
3964
else:
40-
return name
65+
return self._fmt(name, wrap)
4166

4267
def get_subdevice_id(self):
43-
return self.lookup_value('pci_subdevice_id')
68+
return self._fmt(self.lookup_value('pci_subdevice_id'))
4469

4570
def get_subvendor_name(self):
46-
return self.lookup_value('pci_subvendor_name')
71+
return self._fmt(self.lookup_value('pci_subvendor_name'))
4772

4873
def get_subvendor_id(self):
49-
return self.lookup_value('pci_subvendor_id')
74+
return self._fmt(self.lookup_value('pci_subvendor_id'))
5075

5176
def get_pci_id(self):
5277
return "%s:%s %s:%s" % (
53-
self.lookup_value('pci_vendor_id'),
54-
self.lookup_value('pci_device_id'),
55-
self.lookup_value('pci_subvendor_id'),
56-
self.lookup_value('pci_subdevice_id'),
78+
self._fmt(self.lookup_value('pci_vendor_id')),
79+
self._fmt(self.lookup_value('pci_device_id')),
80+
self._fmt(self.lookup_value('pci_subvendor_id')),
81+
self._fmt(self.lookup_value('pci_subdevice_id')),
5782
)
5883

5984
def get_pci_class(self):
60-
return self.lookup_value('pci_device_class')
85+
return self._fmt(self.lookup_value('pci_device_class'))
6186

6287
def is_subdevice(self):
63-
return self.lookup_value('pci_subvendor_id') and self.lookup_value('pci_subdevice_id')
88+
return self.lookup_value('pci_subvendor_id') and self.lookup_value('pci_subdevice_id') or self.lookup_value('pci_device_sub_string')
6489

6590
def get_info(self):
6691

hwinfo/pci/lspci.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55
class ParserException(Exception):
66
pass
77

8+
LABEL_REGEX = r'[\w+\ \.\-\/\[\]\(\)]+'
9+
CODE_REGEX = r'[0-9a-fA-F]{4}'
10+
BUSID_REGEX = r'[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F]'
11+
812
class LspciVVParser(CommandParser):
913
"""Parser object for the output of lspci -vv"""
1014

1115
ITEM_REGEXS = [
12-
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',
16+
r'(?P<pci_device_bus_id>(' + BUSID_REGEX + r'))\ (?P<pci_device_class_name>' + LABEL_REGEX + r'):\ (?P<pci_device_string>(.*))\n',
1317
r'Product\ Name:\ (?P<pci_device_vpd_product_name>(.)*)\n',
1418
r'Subsystem:\ (?P<pci_device_sub_string>(.)*)\n',
1519
]
@@ -27,7 +31,7 @@ class LspciNParser(CommandParser):
2731

2832
#ff:0d.1 0880: 8086:0ee3 (rev 04)
2933
ITEM_REGEXS = [
30-
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})',
34+
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')',
3135
]
3236

3337
ITEM_SEPERATOR = "\n"
@@ -39,17 +43,13 @@ class LspciNParser(CommandParser):
3943
'pci_device_class',
4044
]
4145

42-
43-
LABEL_REGEX = r'[\w+\ \.\-\/\[\]\(\)]+'
44-
CODE_REGEX = r'[0-9a-fA-F]{4}'
45-
4646
class LspciNNMMParser(CommandParser):
4747
"""Parser object for the output of lspci -nnmm"""
4848

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

5151
ITEM_REGEXS = [
52-
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')\]"' \
52+
r'(?P<pci_device_bus_id>(' + BUSID_REGEX + r'))\ "(?P<pci_device_class_name>' + LABEL_REGEX + r')\ \[(?P<pci_device_class>' + CODE_REGEX + r')\]"' \
5353
+ 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')\]"' \
5454
+ 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')\])*',
5555
]

hwinfo/pci/tests/test_lspci.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ def setUp(self):
6262
def test_parse_all_devices(self):
6363
recs = self.parser.parse_items()
6464
self.assertEqual(len(recs), 58)
65+
found = False
66+
for rec in recs:
67+
print rec
68+
if rec['pci_device_bus_id'] == '02:00.0':
69+
self.assertEqual(rec['pci_device_class_name'], 'VGA compatible controller')
70+
found = True
71+
self.assertEqual(found, True)
6572

6673
class TestSingleDeviceNParse(unittest.TestCase):
6774

hwinfo/tools/inspector.py

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import paramiko
66
import subprocess
77
import os
8+
import sys
89

910
from hwinfo.pci import PCIDevice
1011
from hwinfo.pci.lspci import *
@@ -76,18 +77,40 @@ def get_cpu_info(self):
7677
parser = cpuinfo.CPUInfoParser(data)
7778
return parser.parse_items()
7879

80+
class FileNotFound(Exception):
81+
pass
82+
7983
def search_for_file(dirname, filename):
8084
for root, _, files in os.walk(dirname):
8185
if filename in files:
8286
return os.path.join(root, filename)
83-
raise Exception("Could not find '%s' in directory '%s'" % (filename, dirname))
87+
raise FileNotFound("Could not find '%s' in directory '%s'" % (filename, dirname))
8488

8589
def read_from_file(filename):
8690
fh = open(filename, 'r')
8791
data = fh.read()
8892
fh.close()
8993
return data
9094

95+
def parse_data(parser, data):
96+
p = parser(data)
97+
return p.parse_items()
98+
99+
def combine_recs(rec_list, key):
100+
"""Use a common key to combine a list of recs"""
101+
final_recs = {}
102+
for rec in rec_list:
103+
rec_key = rec[key]
104+
if rec_key in final_recs:
105+
for k, v in rec.iteritems():
106+
if k in final_recs[rec_key] and final_recs[rec_key][k] != v:
107+
raise Exception("Mis-match for key '%s'" % k)
108+
final_recs[rec_key][k] = v
109+
else:
110+
final_recs[rec_key] = rec
111+
return final_recs.values()
112+
113+
91114
class HostFromLogs(Host):
92115

93116
def __init__(self, dirname):
@@ -106,6 +129,20 @@ def get_dmidecode_data(self):
106129
def get_cpuinfo_data(self):
107130
return self._load_from_file('cpuinfo')
108131

132+
def get_pci_devices(self):
133+
try:
134+
devs = super(HostFromLogs, self).get_pci_devices()
135+
return devs
136+
except FileNotFound:
137+
# Fall back to looking for the file lspci-vv.out
138+
print "***lspci-nnm.out found. Falling back to looking for lspci-vv.out and lspci-n.out.***"
139+
lspci_vv_recs = parse_data(LspciVVParser, self._load_from_file('lspci-vv.out'))
140+
lspci_n_recs = parse_data(LspciNParser, self._load_from_file('lspci-n.out'))
141+
all_recs = lspci_vv_recs + lspci_n_recs
142+
recs = combine_recs(all_recs, 'pci_device_bus_id')
143+
return [PCIDevice(rec) for rec in recs]
144+
145+
109146
def pci_filter(devices, types):
110147
res = []
111148
for device in devices:
@@ -167,6 +204,12 @@ def tabulate_cpu_recs(recs):
167204
]
168205
return tabulate_recs(recs, header)
169206

207+
def print_unit(title, content):
208+
print "%s" % title
209+
print ""
210+
print content
211+
print ""
212+
170213
def main():
171214
"""Entry Point"""
172215

@@ -184,6 +227,10 @@ def main():
184227
if args.logs:
185228
host = HostFromLogs(args.logs)
186229
else:
230+
if args.machine and not args.username or not args.password:
231+
print "Error: you must specify a username and password to query a remote machine."
232+
sys.exit(1)
233+
187234
host = Host(args.machine, args.username, args.password)
188235

189236
options = []
@@ -196,35 +243,20 @@ def main():
196243
options = filter_choices
197244

198245
if 'bios' in options:
199-
print "Bios Info:"
200-
print ""
201-
print rec_to_table(host.get_info())
202-
print ""
246+
print_unit("Bios Info:", rec_to_table(host.get_info()))
203247

204248
if 'cpu' in options:
205-
print "CPU Info:"
206-
print ""
207-
print tabulate_cpu_recs(host.get_cpu_info())
208-
print ""
249+
print_unit("CPU Info:", tabulate_cpu_recs(host.get_cpu_info()))
209250

210251
if 'nic' in options:
211252
devices = pci_filter_for_nics(host.get_pci_devices())
212-
print "Ethernet Controller Info:"
213-
print ""
214-
print tabulate_pci_recs([dev.get_rec() for dev in devices])
215-
print ""
253+
print_unit("Ethernet Controller Info:", tabulate_pci_recs([dev.get_rec() for dev in devices]))
216254

217255
if 'storage' in options:
218256
devices = pci_filter_for_storage(host.get_pci_devices())
219-
print "Storage Controller Info:"
220-
print ""
221-
print tabulate_pci_recs([dev.get_rec() for dev in devices])
222-
print ""
257+
print_unit("Storage Controller Info:", tabulate_pci_recs([dev.get_rec() for dev in devices]))
223258

224259
if 'gpu' in options:
225260
devices = pci_filter_for_gpu(host.get_pci_devices())
226261
if devices:
227-
print "GPU Info:"
228-
print ""
229-
print tabulate_pci_recs([dev.get_rec() for dev in devices])
230-
print ""
262+
print_unit("GPU Info:", tabulate_pci_recs([dev.get_rec() for dev in devices]))

0 commit comments

Comments
 (0)