Skip to content

Commit 055417b

Browse files
committed
Adding support for falling back to lspci-vv/lspci-n if they are available when operating in log mode.
Signed-off-by: Rob Dobson <rob.dobson@citrix.com>
1 parent 3d08a04 commit 055417b

5 files changed

Lines changed: 204 additions & 23 deletions

File tree

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: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,18 +77,40 @@ def get_cpu_info(self):
7777
parser = cpuinfo.CPUInfoParser(data)
7878
return parser.parse_items()
7979

80+
class FileNotFound(Exception):
81+
pass
82+
8083
def search_for_file(dirname, filename):
8184
for root, _, files in os.walk(dirname):
8285
if filename in files:
8386
return os.path.join(root, filename)
84-
raise Exception("Could not find '%s' in directory '%s'" % (filename, dirname))
87+
raise FileNotFound("Could not find '%s' in directory '%s'" % (filename, dirname))
8588

8689
def read_from_file(filename):
8790
fh = open(filename, 'r')
8891
data = fh.read()
8992
fh.close()
9093
return data
9194

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+
92114
class HostFromLogs(Host):
93115

94116
def __init__(self, dirname):
@@ -107,6 +129,20 @@ def get_dmidecode_data(self):
107129
def get_cpuinfo_data(self):
108130
return self._load_from_file('cpuinfo')
109131

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+
110146
def pci_filter(devices, types):
111147
res = []
112148
for device in devices:

hwinfo/tools/tests/test_inspector.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,117 @@ def test_rec_to_table(self, mock_pt_cls):
142142
]
143143
mock_table.add_row.assert_has_calls(expected_calls, any_order=True)
144144

145+
class CombineRecsTests(unittest.TestCase):
146+
147+
148+
def _validate_rec(self, rec, key, value):
149+
self.assertEqual(rec[key], value)
150+
151+
def test_combine_two_recs(self):
152+
recs = [
153+
{
154+
'name':'rec1',
155+
'valuea': '10',
156+
'valueb': '11',
157+
'valuec': '12',
158+
},
159+
{
160+
'name': 'rec1',
161+
'valued': '5',
162+
'valuee': '8',
163+
},
164+
]
165+
166+
combined_recs = inspector.combine_recs(recs, 'name')
167+
self.assertEqual(len(combined_recs), 1)
168+
rec = combined_recs[0]
169+
self._validate_rec(rec, 'valuea', '10')
170+
self._validate_rec(rec, 'valueb', '11')
171+
self._validate_rec(rec, 'valuec', '12')
172+
self._validate_rec(rec, 'valued', '5')
173+
self._validate_rec(rec, 'valuee', '8')
174+
175+
def test_combine_three_recs(self):
176+
recs = [
177+
{
178+
'name':'rec1',
179+
'valuea': '10',
180+
'valueb': '11',
181+
'valuec': '12',
182+
},
183+
{
184+
'name': 'rec1',
185+
'valued': '5',
186+
'valuee': '8',
187+
},
188+
{
189+
'name': 'rec1',
190+
'valuef': '1',
191+
'valueg': '2',
192+
},
193+
]
194+
195+
combined_recs = inspector.combine_recs(recs, 'name')
196+
self.assertEqual(len(combined_recs), 1)
197+
rec = combined_recs[0]
198+
self._validate_rec(rec, 'valuea', '10')
199+
self._validate_rec(rec, 'valueb', '11')
200+
self._validate_rec(rec, 'valuec', '12')
201+
self._validate_rec(rec, 'valued', '5')
202+
self._validate_rec(rec, 'valuee', '8')
203+
self._validate_rec(rec, 'valuef', '1')
204+
self._validate_rec(rec, 'valueg', '2')
205+
206+
def test_combine_three_recs_to_two(self):
207+
recs = [
208+
{
209+
'name':'rec1',
210+
'valuea': '10',
211+
'valueb': '11',
212+
'valuec': '12',
213+
},
214+
{
215+
'name': 'rec2',
216+
'valued': '5',
217+
'valuee': '8',
218+
},
219+
{
220+
'name': 'rec1',
221+
'valuef': '1',
222+
'valueg': '2',
223+
},
224+
]
225+
226+
combined_recs = inspector.combine_recs(recs, 'name')
227+
self.assertEqual(len(combined_recs), 2)
228+
for rec in combined_recs:
229+
if rec['name'] == 'rec1':
230+
self._validate_rec(rec, 'valuea', '10')
231+
self._validate_rec(rec, 'valueb', '11')
232+
self._validate_rec(rec, 'valuec', '12')
233+
self._validate_rec(rec, 'valuef', '1')
234+
self._validate_rec(rec, 'valueg', '2')
235+
elif rec['name'] == 'rec2':
236+
self._validate_rec(rec, 'valued', '5')
237+
self._validate_rec(rec, 'valuee', '8')
238+
else:
239+
raise Exception("Unexpected rec: %s" % rec)
240+
241+
def test_colliding_values(self):
242+
recs = [
243+
{
244+
'name':'rec1',
245+
'valuea': '10',
246+
'valueb': '11',
247+
'valuec': '12',
248+
},
249+
{
250+
'name': 'rec1',
251+
'valuea': '5',
252+
'valuee': '8',
253+
},
254+
]
255+
with self.assertRaises(Exception) as context:
256+
combined_recs = inspector.combine_recs(recs, 'name')
257+
self.assertEqual(context.exception.message, "Mis-match for key 'valuea'")
145258

0 commit comments

Comments
 (0)