Skip to content

Commit

Permalink
Improving the CLI and adding a couple of host specific tests.
Browse files Browse the repository at this point in the history
Signed-off-by: Rob Dobson <[email protected]>
  • Loading branch information
rdobson committed Jul 14, 2014
1 parent 0c1e4be commit 1a05bbb
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 24 deletions.
14 changes: 14 additions & 0 deletions hwinfo/pci/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,17 @@ def get_info(self):
return "%s %s (%s %s)" % (self.get_subvendor_name(), self.get_subdevice_name(), self.get_vendor_name(), self.get_device_name())
else:
return "%s %s" % (self.get_vendor_name(), self.get_device_name())

def get_rec(self):
rec = {}
rec['vendor_name'] = self.get_vendor_name()
rec['device_name'] = self.get_device_name()
rec['vendor_id'] = self.get_vendor_id()
rec['device_id'] = self.get_device_id()
rec['class'] = self.get_pci_class()
rec['subvendor_name'] = self.get_subvendor_name()
rec['subdevice_name'] = self.get_subdevice_name()
rec['subvendor_id'] = self.get_subvendor_id()
rec['subdevice_id'] = self.get_subdevice_id()

return rec
4 changes: 2 additions & 2 deletions hwinfo/pci/lspci.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class LspciNParser(CommandParser):
]


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

class LspciNNMMParser(CommandParser):
Expand All @@ -51,7 +51,7 @@ class LspciNNMMParser(CommandParser):
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_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')\])*',
+ 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')\])*',
]

ITEM_SEPERATOR = "\n"
55 changes: 55 additions & 0 deletions hwinfo/pci/tests/test_lspci.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,61 @@ class IntelUSBControllerDeviceParse(TestSingleDeviceNNMMParse):
'pci_subdevice_id': '02a3',
}

class EmulexNicDeviceParse(TestSingleDeviceNNMMParse):

SAMPLE_DATA = '0c:00.0 "Ethernet controller [0200]" "Emulex Corporation [19a2]" "OneConnect 10Gb NIC (be3) [0710]" -r02 "Emulex Corporation [10df]" "Device [e70b]"'

DEVICE_REC = {
'pci_device_bus_id': '0c:00.0',
'pci_device_class': '0200',
'pci_device_class_name': 'Ethernet controller',
'pci_vendor_name': 'Emulex Corporation',
'pci_vendor_id': '19a2',
'pci_device_id': '0710',
'pci_device_name': 'OneConnect 10Gb NIC (be3)',
'pci_subvendor_name': 'Emulex Corporation',
'pci_subvendor_id': '10df',
'pci_subdevice_name': 'Device',
'pci_subdevice_id': 'e70b',
}

class LsiSASDeviceParse(TestSingleDeviceNNMMParse):

SAMPLE_DATA = '06:00.0 "Serial Attached SCSI controller [0107]" "LSI Logic / Symbios Logic [1000]" "SAS2004 PCI-Express Fusion-MPT SAS-2 [Spitfire] [0070]" -r03 "IBM [1014]" "Device [03f8]"'

DEVICE_REC = {
'pci_device_bus_id': '06:00.0',
'pci_device_class': '0107',
'pci_device_class_name': 'Serial Attached SCSI controller',
'pci_vendor_name': 'LSI Logic / Symbios Logic',
'pci_vendor_id': '1000',
'pci_device_id': '0070',
'pci_device_name': 'SAS2004 PCI-Express Fusion-MPT SAS-2 [Spitfire]',
'pci_subvendor_name': 'IBM',
'pci_subvendor_id': '1014',
'pci_subdevice_name': 'Device',
'pci_subdevice_id': '03f8',
}

class BroadcomNetDeviceParse(TestSingleDeviceNNMMParse):

SAMPLE_DATA = '01:00.0 "Ethernet controller [0200]" "Broadcom Corporation [14e4]" "NetXtreme BCM5720 Gigabit Ethernet PCIe [165f]" "Dell [1028]" "Device [1f5b]"'

DEVICE_REC = {
'pci_device_bus_id': '01:00.0',
'pci_device_class': '0200',
'pci_device_class_name': 'Ethernet controller',
'pci_vendor_name': 'Broadcom Corporation',
'pci_vendor_id': '14e4',
'pci_device_id': '165f',
'pci_device_name': 'NetXtreme BCM5720 Gigabit Ethernet PCIe',
'pci_subvendor_name': 'Dell',
'pci_subvendor_id': '1028',
'pci_subdevice_name': 'Device',
'pci_subdevice_id': '1f5b',
}


class TestMultiDeviceNNMMParse(unittest.TestCase):

SAMPLE_FILE = '%s/lspci-nnmm' % DATA_DIR
Expand Down
137 changes: 116 additions & 21 deletions hwinfo/tools/inspector.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
#!/usr/bin/env python

from argparse import ArgumentParser
from prettytable import PrettyTable
import paramiko

from hwinfo.pci import PCIDevice
from hwinfo.pci.lspci import *
from hwinfo.host.dmidecode import *

def remote_command(host, username, password, cmd):
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(host, username=username, password=password, timeout=10)
cmdstr = ' '.join(cmd)
print "Executing '%s' on host '%s'" % (cmdstr, host)
#print "Executing '%s' on host '%s'" % (cmdstr, host)
_, stdout, stderr = client.exec_command(cmdstr)
output = stdout.readlines()
error = stderr.readlines()
Expand All @@ -25,43 +27,136 @@ def local_command(cmd):

class Host(object):

def __init__(self, host, username, password):
def __init__(self, host='localhost', username=None, password=None):
self.host = host
self.username = username
self.password = password

def exec_command(self, cmd):
assert not self.host
return local_command(cmd)
if self.host == 'localhost':
return local_command(cmd)
else:
return remote_command(self.host, self.username, self.password, cmd)

def get_pci_devices(self):
data = self.exec_command(['lspci', '-nnmm'])
parser = LspciNNMMParser(data)
devices = parser.parse_items()
return [PCIDevice(device) for device in devices]

class RemoteHost(Host):

def exec_command(self, cmd):
return remote_command(self.host, self.username, self.password, cmd)

def get_info(self):
data = self.exec_command(['dmidecode'])
parser = DmidecodeParser(data)
rec = parser.parse()
return rec

def pci_filter(devices, types):
res = []
for device in devices:
for t in types:
if device.get_pci_class().startswith(t):
res.append(device)
break
return res

def pci_filter_for_nics(devices):
nic_types = ['02']
return pci_filter(devices, nic_types)

def pci_filter_for_storage(devices):
storage_types = ['00', '01']
return pci_filter(devices, storage_types)

def pci_filter_for_gpu(devices):
gpu_types = ['03']
return pci_filter(devices, gpu_types)

def print_lines(lines):
max_len = 0
output = []
for line in lines:
output.append(line)
if len(line) > max_len:
max_len = len(line)
print ""
print "-" * max_len
print '\n'.join(output)
print "-" * max_len
print ""

def rec_to_table(rec):
table = PrettyTable(["Key", "Value"])
table.align['Key'] = 'l'
table.align['Value'] = 'l'
for k, v in rec.iteritems():
table.add_row([k, v])
return table

def tabulate_pci_recs(recs):
header = [
'vendor_name',
'vendor_id',
'device_name',
'device_id',
'subvendor_name',
'subvendor_id',
'subdevice_name',
'subdevice_id',
]
table = PrettyTable(header)
for rec in recs:
vls = [rec[k] for k in header]
table.add_row(vls)
return table

def main():
"""Entry Point"""

parser = ArgumentParser(prog="hwinfo")
parser.add_argument("cmd")
parser.add_argument("host")
parser.add_argument("username")
parser.add_argument("password")

args = parser.parse_args()

host = RemoteHost(args.host, args.username, args.password)

if args.cmd == 'list':
devices = host.get_pci_devices()
for device in devices:
print device.get_info()
filter_choices = ['bios', 'nic', 'storage', 'gpu']
parser.add_argument("-f", "--filter", choices=filter_choices)
parser.add_argument("-m", "--machine", default='localhost')
parser.add_argument("-u", "--username")
parser.add_argument("-p", "--password")

args = parser.parse_args()

host = Host(args.machine, args.username, args.password)

options = []

if args.filter:
filter_args = args.filter.split(',')
for arg in filter_args:
options.append(arg.strip())
else:
options = filter_choices

if 'bios' in options:
print "Bios Info:"
print ""
print rec_to_table(host.get_info())
print ""

if 'nic' in options:
devices = pci_filter_for_nics(host.get_pci_devices())
print "Ethernet Controller Info:"
print ""
print tabulate_pci_recs([dev.get_rec() for dev in devices])
print ""

if 'storage' in options:
devices = pci_filter_for_storage(host.get_pci_devices())
print "Storage Controller Info:"
print ""
print tabulate_pci_recs([dev.get_rec() for dev in devices])
print ""

if 'gpu' in options:
devices = pci_filter_for_gpu(host.get_pci_devices())
if devices:
print "GPU Info:"
print ""
print tabulate_pci_recs([dev.get_rec() for dev in devices])
print ""
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
},
install_requires = [
'paramiko',
'prettytable',
],
)
6 changes: 5 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ deps=
nose
coverage
mock
prettytable
paramiko

commands=
coverage
Expand All @@ -14,6 +16,8 @@ commands=

[testenv:lint]
basepython=python2.7
deps=pylint
deps=
pylint
prettytable
commands=
pylint -r n --rcfile=.pylint.rc hwinfo

0 comments on commit 1a05bbb

Please sign in to comment.