Compare commits

...

210 commits
v0.5.0 ... main

Author SHA1 Message Date
4242832396 Merge pull request 'Fix disk size inventory' (#310) from ribetm/disk_size into main
Reviewed-on: #310
2024-10-21 13:35:29 +02:00
13b84b4da1
feat: Run formatters 2024-10-21 12:55:54 +02:00
837860e31a
feat: Add nix tooling 2024-10-21 12:55:40 +02:00
c1a7f661a3 Merge pull request 'Virtualmachine corrections' (#279) from obeone/virtualmachine_corrections into master
Reviewed-on: #279
2024-10-21 12:32:30 +02:00
Grégoire Compagnon
7d268ea0e8
Return 0 if everything ok as excepted in a shell 2024-10-21 12:30:09 +02:00
Grégoire Compagnon
9d496c6854
Check if it's a VM before running lldp related actions 2024-10-21 12:30:09 +02:00
Grégoire Compagnon
6ef23eae4d
Add missing prtint debug 2024-10-21 12:30:09 +02:00
12ceea413c Merge pull request 'Replaced deprecated module pkg_resources' (#311) from ribetm/py312 into master
Reviewed-on: #311
2024-10-21 10:18:18 +02:00
7a968deee9 Merge pull request 'Make lshw scrpping more resilient' (#292) from sinavir/fix_gpu_without_vendor into master
Reviewed-on: #292
2024-10-21 10:15:27 +02:00
d55cbd62fc Merge pull request 'Fix not working tag with VM' (#293) from sinavir/fix_vm_tags into master
Reviewed-on: #293
2024-10-21 10:15:11 +02:00
59ce76fc29 Merge pull request 'feat(network): Batch requests when filtering on interfaces' (#297) from Tom-Hubrecht/master into master
Reviewed-on: #297
2024-10-21 10:01:35 +02:00
Mathis Ribet
c3d3e6857a
Replaced deprecated module pkg_resources
Removed in py3.12
2024-10-17 20:52:28 +02:00
Mathis Ribet
56627c1aa9
Fix disk size 2024-10-17 20:50:23 +02:00
CllaudiaB
8dde35dd31
Merge pull request #306 from CllaudiaB/netifaces2
netifaces2
2024-10-17 11:01:22 +02:00
clbu
de88ca85b9
fix(network): use netifaces2 2024-10-17 10:46:05 +02:00
CllaudiaB
514627aa72
Merge pull request #305 from CllaudiaB/version
Support Netbox 3
2024-10-17 10:37:09 +02:00
CllaudiaB
4b54a0a3db
fix: typo
Co-authored-by: n1nj4- <39305378+n1nj444@users.noreply.github.com>
2024-10-17 10:26:41 +02:00
clbu
e44fd2fe78
fix(network): retrieve switch interface using id instead of name 2024-10-16 10:44:08 +02:00
clbu
1429fedb9d
fix(network): cable
https://github.com/netbox-community/netbox/issues/9102
2024-10-10 23:15:55 +02:00
clbu
818c835711
fix(requirements): update dependencies version 2024-10-09 15:45:07 +02:00
clbu
40af19e801
fix(readme): required Netbox version 2024-10-09 15:32:25 +02:00
clbu
1d69f4e2f0
python version compatible with dependencies 2024-10-09 15:12:39 +02:00
clbu
a7104b6b94
updating dependencies 2024-08-07 12:29:19 +02:00
clbu
ee41fb4fc2
replace device_role with role 2024-08-07 12:24:49 +02:00
Tom Hubrecht
e04d0c6d59 feat(network): Batch requests when filtering on interfaces
This avoids an issue where the requested URI is too long when many
interfaces are present
2024-05-07 13:54:27 +02:00
sinavir
c9a57de843 fix: vm tags 2024-03-28 12:07:17 +01:00
sinavir
f512e7a0a9 fix: replace list.push by list.append 2024-03-27 18:49:05 +01:00
sinavir
116334be2f fix: make lshw props finding more resilient 2024-03-27 17:12:48 +01:00
Oleg Zagrebelsky
ba4cdb217b
fix lldp.py (#280)
Co-authored-by: kszd487 <kszd487@ukc02dd0ylp3ywui.lan>
2023-08-28 18:54:53 +02:00
renovate[bot]
7ab7bbb9e1
chore(deps): update dependency distro to v1.8.0 (#244)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-18 17:24:00 +02:00
renovate[bot]
b6a3acd6b4
chore(deps): update dependency netifaces to v0.11.0 (#188)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-18 17:22:58 +02:00
renovate[bot]
bb05e12f6e
chore(deps): update dependency packaging to v23 (#261)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-18 17:22:25 +02:00
renovate[bot]
fa51ca31ca
chore(deps): update dependency python-slugify to v8 (#263)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-18 17:13:35 +02:00
renovate[bot]
0fdb56e01d
chore(deps): update dependency pyyaml to v6 (#196)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-18 15:44:16 +02:00
Cyril Levis
19a158ec82
Merge pull request #271 from AlexDaichendt/fix-227-lshw 2023-04-08 12:42:53 +02:00
Alexander Daichendt
08360bafbb fix: lshw crashes due to duplicate logical name 2023-04-08 11:17:23 +02:00
Cyril Levis
e0d734d0ca
Merge pull request #268 from Varriount/make-ipmi-optional 2023-02-28 09:24:33 +01:00
Clay Sweetser
dc224e209b Make IPMI information gathering optional. 2023-02-27 19:06:14 -05:00
Cyril Levis
7259c7602d
Merge pull request #266 from Varriount/patch-2
Simplify `is_vm` function
2023-02-27 14:37:10 +01:00
Clay Sweetser
8cba98ec43
Simplify is_vm function 2023-02-24 19:15:23 -05:00
Cyril Levis
ab96965767
Merge pull request #264 from Farfaday/feat/manage_mtu
feat: Retrieve and manage MTU for the interfaces
2023-02-15 11:55:02 +01:00
François Blondel
221ac16e87 feat: Retrieve and manage MTU for the interfaces 2023-02-14 23:32:26 +01:00
Cyril Levis
9204ae2187
Merge pull request #262 from Solvik/fix/cle/hp-inventory-fix-3
fix: allow the inventory of several hp controller of the same model
2023-01-16 15:06:54 +01:00
Cyril Levis
528ecc09b0
fix: allow the inventory of several hp controller of the same model 2023-01-13 17:12:25 +01:00
Cyril Levis
117b39350e
Merge pull request #260 from Solvik/fix/cle/hp-inventory-fix-2
fix: prevent crash if disk fields attrs missing
2022-12-14 09:02:16 +01:00
Cyril Levis
4cf054278f
fix: prevent crash if disk fields attrs missing 2022-12-14 09:00:37 +01:00
Cyril Levis
b82dc80fe3
Merge pull request #259 from Solvik/fix/cle/hp-inventory-fix-wait
disk inventory fix and hp improvement
2022-12-13 17:28:57 +01:00
Cyril Levis
49b269efa6
chore: fetch more data as custom_fields for hp inventory 2022-12-13 17:27:35 +01:00
Cyril Levis
cedb6818a3
fix: handle big list of hp disk
we cant use p.wait() if too much data.
2022-12-13 17:26:45 +01:00
Cyril Levis
a9af96bba2
fix: get disk serial number 2022-12-13 17:26:14 +01:00
Cyril Levis
282f914665
Merge pull request #256 from Solvik/fix/cle/hpe-controller
fix: enable disk inventory for HPE
2022-12-09 14:22:07 +01:00
Cyril Levis
ca2a69b66f
fix: enable disk inventory for HPE 2022-12-09 14:19:26 +01:00
Cyril Levis
778814b0a1
Merge pull request #254 from Solvik/fix/cle/hp-controller
fix: add space in ignore_patterns
2022-12-02 12:30:40 +01:00
Cyril Levis
e0685e7167
fix: add space in ignore_patterns
to prevent false positive like `/data/cache/temp` for eg
2022-12-02 12:28:47 +01:00
Cyril Levis
ba0be73d3c
Merge pull request #253 from Solvik/fix/cle/storcli-inventory
fix: deadlock storcli inventory
2022-11-30 09:52:03 +01:00
Cyril Levis
bf65da0c58
fix: deadlock storcli inventory
When there is a lot of data return by storcli due to a lot of disk, the inventory was in deadlock
Its a known problem, see:
https://docs.python.org/3/library/subprocess.html#subprocess.Popen.wait
2022-11-30 09:49:51 +01:00
Cyril Levis
5b0df6ca05
Merge pull request #251 from Solvik/fix/dvdrom-inventory
fix: dvdrom inventory crash
2022-11-19 09:38:51 +01:00
Cyril Levis
2d8ec831e6
fix: dvd rom inventory crash
dvdrom dont have serial number, we fallback on a 'unknown' value now to prevent crash
2022-11-17 11:00:48 +01:00
Cyril Levis
fde6211f5b
Merge pull request #250 from Solvik/fix/hp-raid-ignore-errors-3
refactor hp raid errors handling
2022-11-16 15:48:28 +01:00
Cyril Levis
e96a50379b
chore: refactor hp raid ignore errors handling 2022-11-16 15:46:57 +01:00
Cyril Levis
633b6d3851
fix: ignore more cache errors 2022-11-16 14:43:35 +01:00
Cyril Levis
04c1f83e74
Merge pull request #249 from Solvik/fix/hp-raid-ignore-errors
fix: ignore some more hp controllers errors while parsing
2022-11-16 10:46:06 +01:00
Cyril Levis
9b06584fed
fix: ignore some more hp controllers errors while parsing 2022-11-16 10:45:25 +01:00
Cyril Levis
f7cdd92fa3
Merge pull request #232 from KivraChristoffer/fix-#226
This will solve issue #226
2022-11-10 15:53:46 +01:00
Cyril Levis
53769db3e4
Merge pull request #246 from ypid/fix/various
fix: TypeError: Object of type Record is not JSON serializable; fix: disk is also virtual if `product` is None
2022-11-10 15:52:39 +01:00
Cyril Levis
bce02a5e7c
Merge branch 'master' into fix/various 2022-11-10 15:51:32 +01:00
Cyril Levis
368d793704
Merge pull request #248 from Solvik/fix/cle/inventory-crash-2
fix: HP inventory crash is some warnings
2022-11-10 15:47:39 +01:00
Cyril Levis
776f951e3b
fix: hp inventory crash is controller return some warning
about the cache to be re-enable for example

```
DEBUG:urllib3.connectionpool:https://netbox.local:443 "GET /api/dcim/inventory-items/?device_id=9&tag=hw-disk&limit=0 HTTP/1.1" 200 52
('A cache backup failure has occurred. Please execute the "reenablecache" '
 'command')
Traceback (most recent call last):
  File "/usr/bin/netbox_agent", line 33, in <module>
    sys.exit(load_entry_point('netbox-agent==0.7.1', 'console_scripts', 'netbox_agent')())
  File "/opt/netbox-agent/lib/python3.6/site-packages/netbox_agent/cli.py", line 50, in main
    return run(config)
  File "/opt/netbox-agent/lib/python3.6/site-packages/netbox_agent/cli.py", line 43, in run
    server.netbox_create_or_update(config)
  File "/opt/netbox-agent/lib/python3.6/site-packages/netbox_agent/server.py", line 417, in netbox_create_or_update
    self.inventory.create_or_update()
  File "/opt/netbox-agent/lib/python3.6/site-packages/netbox_agent/inventory.py", line 547, in create_or_update
    self.do_netbox_disks()
  File "/opt/netbox-agent/lib/python3.6/site-packages/netbox_agent/inventory.py", line 417, in do_netbox_disks
    disks = self.get_hw_disks()
  File "/opt/netbox-agent/lib/python3.6/site-packages/netbox_agent/inventory.py", line 332, in get_hw_disks
    for raid_card in self.get_raid_cards(filter_cards=True):
  File "/opt/netbox-agent/lib/python3.6/site-packages/netbox_agent/inventory.py", line 242, in get_raid_cards
    self.raid = raid_class()
  File "/opt/netbox-agent/lib/python3.6/site-packages/netbox_agent/raid/hp.py", line 180, in __init__
    self.convert_to_dict()
  File "/opt/netbox-agent/lib/python3.6/site-packages/netbox_agent/raid/hp.py", line 185, in convert_to_dict
    controllers = _parse_ctrl_output(lines)
  File "/opt/netbox-agent/lib/python3.6/site-packages/netbox_agent/raid/hp.py", line 34, in _parse_ctrl_output
    attr, val = line.split(': ', 1)
```
2022-11-10 15:47:16 +01:00
Cyril Levis
84f1f00ffe
Merge pull request #247 from Solvik/fix/cle/inventory-crash-1
fix: disk inventory crash some values are None
2022-11-10 15:46:24 +01:00
Cyril Levis
8c12fa8e86
fix: crash if product or description None 2022-11-10 15:19:31 +01:00
Robin Schneider
5d0f0bf2fa
chore: Fix typo 2022-11-03 16:51:43 +01:00
Robin Schneider
4bd4b6bb94
fix: disk is also virtual if product is None
Example on QEMU/KVM:

```json
{'logicalname': '/dev/vda', 'product': None, 'serial': None, 'version': None, 'size': 10737418240, 'description': 'Virtual I/O device', 'type': 'Virtual I/O device'}
```
2022-11-03 16:51:43 +01:00
Robin Schneider
d286fde999
chore: Remove unused API parameter: device_platform 2022-11-03 16:51:42 +01:00
Robin Schneider
dfe937d54e
fix: TypeError: Object of type Record is not JSON serializable 2022-11-03 16:51:33 +01:00
Cyril Levis
042a6fcf35
Merge pull request #239 from Solvik/fix/inventory-disk-issue
Inventory disk issue
2022-08-25 08:43:36 +02:00
Cyril Levis
f4d7796094
fix: dvd-ram should be consider as virtual to be ignore 2022-08-23 09:00:31 +02:00
Cyril Levis
0b9087fa41
fix: inventory disk issue if size None
```
Traceback (most recent call last):
  File "/usr/bin/netbox_agent", line 33, in <module>
    sys.exit(load_entry_point('netbox-agent==0.7.1', 'console_scripts', 'netbox_agent')())
  File "/opt/netbox-agent/lib/python3.6/site-packages/netbox_agent/cli.py", line 50, in main
    return run(config)
  File "/opt/netbox-agent/lib/python3.6/site-packages/netbox_agent/cli.py", line 43, in run
    server.netbox_create_or_update(config)
  File "/opt/netbox-agent/lib/python3.6/site-packages/netbox_agent/server.py", line 417, in netbox_create_or_update
    self.inventory.create_or_update()
  File "/opt/netbox-agent/lib/python3.6/site-packages/netbox_agent/inventory.py", line 550, in create_or_update
    self.do_netbox_disks()
  File "/opt/netbox-agent/lib/python3.6/site-packages/netbox_agent/inventory.py", line 420, in do_netbox_disks
    disks = self.get_hw_disks()
  File "/opt/netbox-agent/lib/python3.6/site-packages/netbox_agent/inventory.py", line 344, in get_hw_disks
    size = int(disk.get('size', 0))
TypeError: int() argument must be a string, a bytes-like object or a number, not 'NoneType'
```
2022-08-23 08:41:59 +02:00
Sylvain Rabot
79a08359ae
Add Amazon EC2 to virtual machines (#236) 2022-08-08 15:37:13 +02:00
bds-congnguyen
29d2f23986
Update requirements.txt (#233)
* Update requirements.txt

Fix `ModuleNotFoundError: No module named 'distro'` cause linux_distribution return None
2022-08-08 15:26:58 +02:00
ChrNic
838ffd8e41 using dynamic ref to NIC 2022-07-21 08:32:24 +02:00
DeathRabbit679
ea66becd3d
Use platform id instead of record (#231) 2022-07-18 21:08:03 +01:00
illes
0cff7d3477
Fix module platform has no attribute linux_distribution (#224) 2022-07-18 21:06:33 +01:00
illes
be770a947a
Fix for missing mgmt-ip in LLDP output (#225) 2022-06-20 13:13:31 +02:00
Cyril Levis
6181800cb3
Merge pull request #220 from cyrinux/fix/hp-raid
fix: hp raid, prevent us to miss some errors
2022-04-06 09:25:56 +02:00
Cyril Levis
2f23844dfd
fix: hp raid, prevent us to miss some errors when matching only returncode 1
Signed-off-by: Cyril Levis <git@levis.name>
2022-04-04 14:39:19 +02:00
Cyril Levis
5f0aae6c01
Merge pull request #218 from KivraChristoffer/Feature/fix-HP-raid
Bug: if HP raidcard have no discs
2022-04-04 09:26:51 +02:00
ChrNic
6717b43cc9 Solves if raidcard have no discs 2022-04-04 09:05:42 +02:00
Cyril Levis
05bfc69f26
Merge pull request #214 from morihaya/readme-patch
Fix netbox_agent.yml to netbox_agent.yaml
2022-04-01 08:58:23 +02:00
Cyril Levis
d06931852f
Merge pull request #216 from cyrinux/feature/platform
Added platform to the config with auto-detect as fallback
2022-04-01 08:57:24 +02:00
Cyril Levis
46354865d3
chore: update Help
Signed-off-by: Cyril Levis <git@levis.name>
2022-03-31 21:21:19 +02:00
Cyril Levis
3dbeb5b9de
chore: cleanup virtual_machines get_device_platform
Signed-off-by: Cyril Levis <git@levis.name>
2022-03-31 21:04:32 +02:00
Cyril Levis
dfb6b234ba
chore: cleanup, add device_platform in summary
Signed-off-by: Cyril Levis <git@levis.name>
2022-03-31 20:58:50 +02:00
Cyril Levis
9e797c376e
fix: always get_device_platform
Signed-off-by: Cyril Levis <git@levis.name>
2022-03-31 19:54:34 +02:00
Cyril Levis
cf2cc54da4
feat: auto-detect device platform
this platform module still works for python3.6 we use.

Signed-off-by: Cyril Levis <git@levis.name>
2022-03-31 17:33:35 +02:00
Fredrik Bergström
2a4f24f00a not failing if platform is not set 2022-03-31 11:14:26 +02:00
Fredrik Bergström
a7b965a8b5 Added platform to the config 2022-03-30 13:27:13 +02:00
Yukiya Hayashi A.k.a morihaya
ddf2e4d1cc
Fix netbox_agent.yml to netbox_agent.yaml
Unify the notation of netbox_agent.yaml in the README
2022-03-30 00:17:27 +09:00
Cyril Levis
7bf955d601
Merge pull request #213 from cyrinux/feat/add_tags_replace_params
fix(#212): prevent server tags to be override by default
2022-03-28 15:30:57 +02:00
Cyril Levis
dd4d935fb2
fix(#212): prevent server tags to be override.
Add a param `--replace-tags` to override all tags, default behavior is to append new tags.
2022-03-28 15:17:27 +02:00
Cyril Levis
ce83364ff9
Merge pull request #211 from cyrinux/feat/add_qemu_support
feat: add QEMU support
2022-03-12 10:42:28 +01:00
Cyril Levis
75f14fa895
feat: add QEMU support 2022-03-12 10:40:34 +01:00
Christophe Simon
bb08813700
Merge pull request #209 from Solvik/fix/chriss/raid_cmd_returncod_hp_jbod
Fixed HP raid parser when disks are in JBOD
2022-03-11 17:05:38 +01:00
Christophe Simon
ad951b9288 Fixed HP raid parser when disks are in JBOD
The function parsing the RAID logical volumes in the HP module did not
manage the case where disks were set in JBOD mode, thus having no RAID
array set.

This patch fixes this by checking if arrays are defined on pdisks before
parsing the logical disks.

Also added returncode read when executing the RAID related commands to
raise a more precise error.
2022-03-11 15:55:07 +01:00
Christophe Simon
b53dfa9b59
Merge pull request #208 from Solvik/fix/chriss/virtual_network_nic_mac_addr
Fixed virtual network cards creation
2022-03-08 17:31:26 +01:00
Christophe Simon
3b8917aaf3 Fixed virtual network cards creation
This patch fixes a mismatch between the way network interfaces are
looked for from network cards, and the way they are created:

- If a network card has a NAC address, it used in the search criteria to
  find the network interface
- When creating virtual interfaces, the MAC address is not specified in
  the creation parameters

This raises an exception when a virtual card has a MAC address:

- The virtual interface has been created without MAC address
- But the network card has one, so the interface is looked for using it
- The network interface is not found, so it's created
- An error is risen because an interface with the same name already
  exists

This patch sets the MAC address on the interface if it exists, no matter if
it is virtual or not.
2022-03-08 17:27:06 +01:00
Cyril Levis
18773a2dff
Merge pull request #204 from Solvik/fix/chriss/unnamed_network_interfaces
Unnamed network interfaces.
2022-03-08 10:56:21 +01:00
Christophe Simon
84a9aca141 Unnamed network interfaces.
Some interfaces do not have device (logical) name (eth0, for instance), such
as not connected network mezzanine cards in blade servers.

In such situations, the card will be named `unknown[0-9]`.
2022-03-07 17:18:35 +01:00
Christophe Simon
a7c6ae68e2
Merge pull request #177 from Solvik/176-netbox-2.9
support netbox >=2.9
2022-03-07 16:03:05 +01:00
Christophe Simon
0f210e16f2
Merge pull request #202 from Solvik/feature/chriss/disk_extended_information
Added disks extended attributes
2022-03-07 15:57:50 +01:00
Christophe Simon
e789619b34 Added disks extended attributes
This patch brings some of the physical and virtual drive attributes as
`custom_fields` to the disks inventory.

The goal is to have this information present to ease disks maintenance
when a drive becomes unavailable and its attributes can't be read anymore
from the RAID controller.

It also helps to standardize the extended disk attributes across the
different manufacturers.

As the disk physical identifers were not available under the correct
format (hexadecimal format using the `xml` output as opposed as `X:Y:Z` format
using the default `list` format), the command line parser has been
refactored to read the `list` format, rather than `xml` one in the
`omreport` raid controller parser.

As the custom fields have to be created prior being able to register
the disks extended attributes, this feature is only activated using the
`--process-virtual-drives` command line parameter, or by setting
`process_virtual_drives` to `true` in the configuration file.

The custom fields to create as `DCIM > inventory item` `Text` are described
below.

    NAME            LABEL                      DESCRIPTION
    mount_point     Mount point                Device mount point(s)
    pd_identifier   Physical disk identifier   Physical disk identifier in the RAID controller
    vd_array        Virtual drive array        Virtual drive array the disk is member of
    vd_consistency  Virtual drive consistency  Virtual disk array consistency
    vd_device       Virtual drive device       Virtual drive system device
    vd_raid_type    Virtual drive RAID         Virtual drive array RAID type
    vd_size         Virtual drive size         Virtual drive array size

In the current implementation, the disks attributes ore not updated: if
a disk with the correct serial number is found, it's sufficient to
consider it as up to date.

To force the reprocessing of the disks extended attributes, the
`--force-disk-refresh` command line option can be used: it removes all
existing disks to before populating them with the correct parsing.
Unless this option is specified, the extended attributes won't be
modified unless a disk is replaced.

It is possible to dump the physical/virtual disks map on the filesystem under
the JSON notation to ease or automate disks management. The file path has to
be provided using the `--dump-disks-map` command line parameter.
2022-03-02 15:53:38 +01:00
Christophe Simon
af9df9ab4b Fixed PSU power consumption calculation
When PSUs were found, the voltage value was never set, raising an
exception when trying to multiply current * voltage (float * None).

This patch reads per PSU voltage (with 230V as default) and define power
consumption from it.
2022-02-23 16:06:32 +01:00
Cyril LEVIS
b092079820
chore: add rpmenv config for rpm packaging as a virtualenv
https://github.com/kevinconway/rpmvenv
2022-02-23 14:50:45 +01:00
Cyril LEVIS
021c5db7d3
fix: add MANIFEST.in for packaging 2022-02-23 11:27:35 +01:00
Christophe Simon
1fc0aee929 Bumped version number
Those changes deserve a major release :)
2022-02-22 17:58:01 +01:00
Cyril Levis
7cfcd685f3
Merge pull request #201 from dailymotion/feat/cle/upstream_add_custom_fields_support
feat: add custom_fields support
2022-02-22 17:48:05 +01:00
Cyril LEVIS
d1ee380ffb
feat: add custom_fields suppport
```bash
$ netbox_agent -du --device.custom_fields="last_run=$(date),last_run_ts=$(date +'%s')"
```

obviously, custom_fields need to be create manually in netbox admin
2022-02-22 17:45:25 +01:00
Christophe Simon
305d4d41ec Various changes
- Added an option to specify an SSL CA certificates file to talk to the
  Netbox API
- Enhanced GPU expansion bays inventory: internal GPU (VGA) goes into
  the blade, external GPU (3D) goes into the expansion bay
- Unified the way expansion bays are managed (GPU and drive exansion
  bays)
- Started to refactor `network` module to make it more readable
- Dependencies in `setup.py` now reads its requirements from
  `requirements.txt` to avoid double maintenance
2022-02-22 14:51:58 +01:00
Christophe Simon
34c1619ce8 Merge remote-tracking branch 'origin/master' into 176-netbox-2.9 2022-02-21 18:09:25 +01:00
Christophe Simon
fcb038f4b6
Merge pull request #199 from dailymotion/fix/chriss/purge_remaining_devices
Added option to purge remaining devices
2022-02-21 11:36:39 +01:00
Christophe Simon
5d9602402b
Merge branch 'master' into fix/chriss/purge_remaining_devices 2022-02-15 10:34:50 +01:00
Christophe Simon
71c603620b
Merge pull request #2 from dailymotion/feature/chriss/external_drive_bay
Manage blade expansions as independent devices
2022-02-15 10:33:56 +01:00
Christophe Simon
2f09cf8d42 Manage blade expansions as independent devices
This patch adds the ability to detect and manage GPU and Disk expansion
bays, and either add their internal components into the device
corresponding to the blade server, or into a dedicated device.

It takes advantage of the work made by @cyrinux on GPU bays management, and
applies the same principle to the external disk bays, but harmonize the
inventory management:

- If no argument is specified on the command line, the GPU cards, RAID
  controllers and their attached disks are added in the blade device,
  and the device corresponding to an expansion device is deleted.
- If the `--expansion-as-device` option is specified on the command
  line, a dedicated device corresponding to the expansion bay is
  created, and the GPUs, RAID card and attached disks are removed from
  the blade device and added to the expansion device.
2022-02-14 14:37:02 +01:00
Christophe Simon
58775c0950 Added option to purge remaining devices
This patch adds an option to clear remaining devices in the context of a
new device replacing an old one with the same name but with a different
hardware.

The serial leading to the bug is described below:

- A first server is registered under name `A` with serial `X`
- A second server is registered under name `B` with serial `Y`
- The server with serial `X` is decomissionned, but not removed
- The server with serial `Y` is reinstalled with name `A`

In this case, a device with serial `X` is well found, and netbox agent
tries to update its name. But this raises an error because of the unique
constraint on name, as another device already holds this name.

The proposed solution to handle this situation is to delete any device
with same `name` but different `serial` before handling a device.

As this is not necessarily the expected behavior, it can be enabled by
using the `--purge-old-devices` to avoid breaking existing inventory.
2022-02-08 18:46:30 +01:00
Cyrinux
8a46af19b8
Fix a crash when missing rack id (#172) 2021-07-20 21:55:47 +02:00
ramnes
bdc450ef6a Make flake8 and isort happy 2021-07-09 11:10:43 +02:00
Cyril Levis
794f9787f0 NVME inventory fix.
The inventory crash with default nvme-cli version 1.8.1 on Centos7.
2021-07-09 11:00:41 +02:00
Solvik Blum
86527af1c0 lint 2021-05-18 13:59:04 +02:00
Solvik Blum
b738f4bfef fix last few bugs due to pynetbox 6 upgrade 2021-05-14 10:00:50 +02:00
Solvik Blum
aaea0a2477 fix ipv6 cleanup on interface with only one ipv6 and not ipv4 2021-05-12 20:11:20 +02:00
Solvik Blum
9eafcbf215 this MR will drop compatibility with versions < 2.9 2021-05-12 19:41:35 +02:00
Solvik Blum
95d7f98389 add new dependencies 2021-05-12 19:41:35 +02:00
Solvik Blum
576eb07dd7 make inventory compatible 2021-05-12 19:41:01 +02:00
Solvik Blum
337e272eea fix ipv6 deletion with regex 2021-05-12 19:41:01 +02:00
Solvik Blum
a1af028df6 last commit is based on pynetbox >= 6.1 2021-05-12 19:41:01 +02:00
Solvik Blum
58c18fc2da add more compatibility, now to netbox 2.10 2021-05-12 19:41:01 +02:00
Solvik Blum
3639662961 ignore tag update for Netbox version >= 2.8 2021-05-12 19:41:01 +02:00
Solvik Blum
1d98d3c8e9 fix ip association 2021-05-12 19:41:01 +02:00
Solvik Blum
dc582b5de6 change .interface foreignkey with .assigned_object 2021-05-12 19:41:01 +02:00
renovate[bot]
51efa8edba
Update dependency jsonargparse to v3 (#179)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-12 15:50:28 +02:00
Solvik
c4eb8f34ac
add 25G compatibility (#185) 2021-05-12 15:50:16 +02:00
renovate[bot]
7147670255
Update dependency pyyaml to v5.4.1 (#181)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-03-26 16:00:40 +01:00
Mark David
9e934af835
Remove an invalid character (#182)
Invalid character that causes some errors to throw if you're copying and pasting the config
2021-02-15 10:54:47 +01:00
Laurent Marchaud
2a1196b52d
Add support for Supermicro TwinPro devices (#180) 2020-12-18 17:23:37 +01:00
Solvik
77a84b365f
fix ugly supermicro blade support (#178) 2020-12-15 11:32:11 +01:00
Cyrinux
0f2cb531ae
Fix inventory crash on nvme binary too old or absent (#170)
* When nvme binary absent or too old and no json output, this crash the inventory, i prefer just pass nvme inventory and continue

* log if nvme-cli is not installed
2020-10-14 12:40:19 +02:00
Cyrinux
00653628c6
Truncate GPU product name to 50 characteres. (#169)
Example: Hi1710 [iBMC Intelligent Management system chip w/VGA support]
This product is too long, the api want max_length = 50 ...
2020-10-14 12:39:57 +02:00
Cyrinux
7999244096
Check if lldpctl present, and log debug if no lldpctl output. (#171) 2020-10-14 12:39:40 +02:00
Cyrinux
137728be1f
Add "ProLiant BL460c Gen10 Graphics Exp" support and GPU expansion bay (#165)
* Add "ProLiant BL460c Gen10 Graphics Exp"

* Add GPU expansion support for HP_ProLiant_BL460c_Gen10_Graphics_Exp

* Add ProLiant BL460c Graphics Expansion Blade support in README

* Dont crash if vendor other than HP

* Typo
2020-09-18 12:29:17 +02:00
Cyrinux
0fe17c9687
Add GPU inventory support (#164)
* Add GPU support

* Some update in the doc
2020-09-18 12:29:05 +02:00
Anton A. Grishin
28955612be
fix allocated_draw for inactive PSU (#168)
Co-authored-by: Anton Grishyn <grishin@maxpay.com>
2020-09-07 14:20:32 +02:00
Solvik Blum
fc930b9ef4 bump version 2020-08-24 13:45:18 +02:00
Cyrinux
e20e6a7dee
Fix travis (#162) 2020-08-24 12:52:25 +02:00
Cyrinux
e95a66d93a
Add HP Moonshot 1500 support (#161)
* Add HP Moonshot 1500 support
2020-08-20 18:03:36 +02:00
Solvik
feab0f1a58
Merge pull request #158 from Solvik/157
fix agent crash on server creation if not blade
2020-08-05 15:16:45 +02:00
Solvik
0a903596dc
Merge pull request #148 from Solvik/renovate/pynetbox-5.x
Update dependency pynetbox to v5.0.5
2020-07-29 00:54:36 +02:00
Renovate Bot
3aea4fbc3b
Update dependency pynetbox to v5.0.5 2020-07-28 22:50:26 +00:00
Solvik
54526d3d0b
Merge pull request #146 from Solvik/renovate/netaddr-0.x
Update dependency netaddr to v0.8.0
2020-07-29 00:49:54 +02:00
Solvik
a866e20548
Merge pull request #156 from Solvik/renovate/jsonargparse-2.x
Update dependency jsonargparse to v2.32.2
2020-07-29 00:48:33 +02:00
Solvik Blum
f8c0732c2b fix agent crash on server creation if not blade 2020-07-29 00:45:24 +02:00
Solvik
a59690469b
Merge pull request #155 from Solvik/supermicro
Fix Supermicro vendor class
2020-07-29 00:26:52 +02:00
Solvik
da5e8ad2e7
Update server.py 2020-07-29 00:21:08 +02:00
Renovate Bot
df18f6ea00
Update dependency netaddr to v0.8.0 2020-07-28 16:54:48 +00:00
Renovate Bot
4f0db3478f
Update dependency jsonargparse to v2.32.2 2020-07-28 16:54:34 +00:00
Solvik Blum
d1a0ec2324 fix unused import and add QCT test 2020-07-28 18:51:15 +02:00
Solvik Blum
e6472c623d update README.md to change the datacenter example to use a lowercase slug 2020-07-28 18:46:38 +02:00
Solvik
1685ecab16 Update issue templates 2020-07-19 11:32:32 +02:00
Solvik Blum
18e3480574 fix supermicro dmidecode parsing 2020-07-13 21:04:45 +02:00
Solvik Blum
839d9a908c better handling supermicro products (blade or not blade) 2020-07-11 15:56:10 +02:00
Solvik
875ad5c92e
Merge pull request #153 from Solvik/renovate/jsonargparse-2.x
Update dependency jsonargparse to v2.31.2
2020-07-11 15:53:49 +02:00
Solvik
ebee42bb48
Update README.md 2020-07-11 15:51:26 +02:00
Solvik Blum
70146b8614 fix not passing tenant when tenants exist in Netbox 2020-07-11 15:33:34 +02:00
Solvik
f936aeb717
Adding the --netbox.ssl_verify option (#152) 2020-07-11 15:32:00 +02:00
Solvik Blum
9611400b22 move ssl_verify under 'netbox' config 2020-07-11 15:28:04 +02:00
Solvik Blum
53cb29a6d4 fix import order 2020-07-11 15:14:33 +02:00
strus38
70e5d407f5 Fix indentation 2020-07-08 22:19:39 +02:00
Renovate Bot
67a564855b
Update dependency jsonargparse to v2.31.2 2020-07-08 16:06:09 +00:00
strus38
0894e645a8 Fix indentation 2020-07-08 10:02:45 +02:00
strus38
95d2ae420e Fix indentation 2020-07-08 09:47:48 +02:00
strus38
275e1850c6 Adding the --no_ssl_verify option to support Netbox connection with https and untrusted certificates 2020-07-08 09:36:13 +02:00
Ryan Jones
9aedb46530
small fix to add recognition of RHEV- Virtual machines (#150) 2020-07-07 20:08:35 +02:00
ThomasADavis
a60c0cd70c
Add tags,device roles,tenants.. (#110)
* Adds support for:

* sets the tenant for IP addresses and Devices.
* setting device tags
* setting the blade, chassis, and server roles.

Co-authored-by: Thomas Davis <tdavis@nersc.gov>
Co-authored-by: Solvik <solvik@solvik.fr>
2020-07-01 18:54:58 +02:00
Solvik
bd5037996b
make the agent work if only a datacenter is specified (#143) 2020-07-01 18:51:57 +02:00
renovate[bot]
665531cb87
Update dependency jsonargparse to v2.31.1 (#137)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-07-01 18:50:34 +02:00
renovate[bot]
21ddf3f312
Update dependency pynetbox to v5 (#140)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-07-01 18:50:13 +02:00
renovate[bot]
f654187074
Update dependency netaddr to v0.7.20 (#136)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-06-29 09:18:00 +02:00
Cyrinux
a896d118d7
* Loop on nvme disk (#138)
* Remove vendor and product (not present, make crash)
* Use firmware version for version
2020-06-23 21:01:32 +02:00
Solvik Blum
a71d992a51 prepare for release 2020-06-17 17:58:18 +02:00
renovate[bot]
c6a1e48956
Update dependency jsonargparse to v2.31.0 (#125)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-06-17 17:57:41 +02:00
Solvik
b331bcb934
Better usage infos (#134) 2020-06-17 17:54:10 +02:00
Solvik
feec16c4fe
fix latest flake8 error (#135) 2020-06-15 15:46:09 +02:00
Solvik Blum
d227f35964 add long_description_content_type to setup.py to make twine works 2020-06-14 18:46:27 +02:00
Solvik Blum
b94f246196 some power fixes 2020-06-14 18:29:28 +02:00
Cyrinux
754a284fd1
Add openvpn TUN and TAP interfaces support, change type to Virtual and remove mac address (#132)
* Add Tun and Tap support, change type to Virtual and remove mac address

* Simplify

* Resimplify
2020-06-14 18:18:13 +02:00
Cyrinux
683e6cacb1
Fix HP raid controller parsing (#131)
* Some messages about the cache for example, with indentation level 0 break the parsing.
I ignore line indentation if indentation level and line dont match REGEXP_CONTROLLER_HP.

```bash

DC1|server-01:~# hpacucli ctrl all show detail

Smart Array P244br in Slot 0 (Embedded)
A cache backup failure has occurred. Please execute the "reenablecache" command
to enable the cache. Your controller may require a reboot after the operation
to complete the cache recovery process.

   Bus Interface: PCI
   Slot: 0
   Serial Number: PDZVU0WLM241FP
   Cache Serial Number: PDZVU0WLM241FP
   RAID 6 (ADG) Status: Enabled
   Controller Status: OK
   Hardware Revision: B
   Firmware Version: 7.00-0
   ...
```

* Remove whitespace
2020-06-10 15:24:43 +02:00
Cyrinux
41f0b04d00
Add missing N/A fallback on DIMM memory (#129) 2020-06-09 09:05:35 +02:00
Cyrinux
9dedbea47a
Some fixes that prevent crash on register and update (#128) 2020-06-08 14:16:50 +02:00
Solvik
28fc87c9e2
handle netbox 2.8 deprecation of _choices method (#127) 2020-05-23 13:23:51 +02:00
Solvik Blum
29d2bff805 fix flake8 errors 2020-05-22 13:23:28 +02:00
Solvik Blum
bd5b45fc6d bump version 2020-05-04 13:23:33 +02:00
renovate[bot]
26413fe0e8
Update dependency jsonargparse to v2.27.0 (#124)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-05-01 19:39:26 +02:00
Solvik
f06da32fc3
Virtual Machine feature (#122)
Add ability to create Virtual Machine in netbox with an auto-detecting feature (currently VirtualBox, Xen, Hyper-V, VMWare, GCP)

One caveat, I made the choice not to report the disk as Netbox model allow only one size but VM often have multiples.

Also, lot of code refactoring to be able to use create_or_update function style, fixing a lot of corner case
2020-04-19 12:28:49 +02:00
renovate[bot]
996c10d95b
Update dependency jsonargparse to v2.25.3 (#109)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-04-19 12:20:11 +02:00
renovate[bot]
ace8f69eab
Update dependency pynetbox to v4.3.1 (#113)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-04-19 12:19:43 +02:00
Solvik
312f472e9c
fix ethtool class in case ethtool works but returns nothing (#123) 2020-04-19 12:19:28 +02:00
renovate[bot]
a42cf2eb48
Update dependency pyyaml to v5.3.1 (#121)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-04-08 14:23:43 +02:00
ThomasADavis
dc4f90d490
handle sockets with no cpu installed. should fix #117 (#118) 2020-02-21 09:13:02 +01:00
Solvik
5590f39131
Fix agent, wasn't working anymore (#115)
Bad rebase made dmidecode calls fail
2020-02-16 20:24:40 +01:00
Solvik
bb7c8c8f94
Update README.md 2020-02-04 19:27:40 +01:00
ThomasADavis
a5bc16d3b1
Adds a generic vendor for unrecognizable systems (#105)
Co-authored-by: Solvik <solvik@solvik.fr>
2020-02-03 00:38:23 +01:00
ramnes
bab2d26ad0 Setup tests 2020-02-02 20:24:01 +01:00
68 changed files with 18662 additions and 1466 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use nix

25
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,25 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Configuration file**
Paste here your netbox agent configuration file
**Environment:**
- OS: [e.g. Ubuntu 18.04]
- Netbox agent version [e.g. master, v0.6.0]
**Additional context**
Add any other context about the problem here.

5
.gitignore vendored
View file

@ -179,3 +179,8 @@ dmypy.json
.pyre/ .pyre/
# End of https://www.gitignore.io/api/emacs,python # End of https://www.gitignore.io/api/emacs,python
netbox-docker
/.vscode
.direnv
.pre-commit-config.yaml

View file

@ -2,20 +2,23 @@ sudo: false
dist: xenial dist: xenial
language: python language: python
services:
- docker
matrix: matrix:
include: include:
- python: 3.5 - python: 3.5
env: TOXENV=py35 env: TOXENV=pytest
- python: 3.6 - python: 3.6
env: TOXENV=py36 env: TOXENV=pytest
- python: 3.7 - python: 3.7
env: TOXENV=py37 env: TOXENV=pytest
- python: pypy3 - python: pypy3
env: TOXENV=pypy3 env: TOXENV=pytest
- python: 3.5 - python: 3.5
env: TOXENV=pep8 env: TOXENV=flake8
- python: 3.7 - python: 3.7
env: TOXENV=pep8 env: TOXENV=flake8
cache: cache:
directories: directories:
@ -23,7 +26,7 @@ cache:
install: install:
- pip install tox - pip install tox
- if [[ $TOXENV == py* ]]; then pip install coveralls; fi - if [[ $TOXENV == pytest ]]; then pip install coveralls; fi
script: script:
- tox - tox
@ -32,4 +35,4 @@ notifications:
email: false email: false
after_success: after_success:
- if [[ $TOXENV == py* ]]; then coveralls; fi - if [[ $TOXENV == pytest ]]; then coveralls; fi

1
MANIFEST.in Normal file
View file

@ -0,0 +1 @@
include requirements.txt

181
README.md
View file

@ -1,5 +1,4 @@
# Netbox agent # Netbox agent [![Build Status](https://travis-ci.com/Solvik/netbox-agent.svg?branch=master)](https://travis-ci.com/Solvik/netbox-agent)
This project aims to create hardware automatically into [Netbox](https://github.com/netbox-community/netbox) based on standard tools (dmidecode, lldpd, parsing /sys/, etc). This project aims to create hardware automatically into [Netbox](https://github.com/netbox-community/netbox) based on standard tools (dmidecode, lldpd, parsing /sys/, etc).
@ -7,21 +6,22 @@ The goal is to generate an existing infrastructure on Netbox and have the abilit
# Features # Features
* Create servers, chassis and blade through standard tools (`dmidecode`) * Create virtual machines, servers, chassis and blade through standard tools (`dmidecode`)
* Create physical, bonding and vlan network interfaces with IPs (IPv4 & IPv6) * Create physical, bonding and vlan network interfaces with IPs (IPv4 & IPv6)
* Create IPMI interface if found * Create IPMI interface if found
* Create or get existing VLAN and associate it to interfaces * Create or get existing VLAN and associate it to interfaces
* Generic ability to guess datacenters and rack location through drivers (`cmd` and `file` and custom ones) * Generic ability to guess datacenters and rack location through drivers (`cmd` and `file` and custom ones)
* Update existing `Device` and `Interface` * Update existing `Device` and `Interface`
* Handle blade moving (new slot, new chassis) * Handle blade moving (new slot, new chassis)
* Handle blade GPU expansions
* Automatic cabling (server's interface to switch's interface) using lldp * Automatic cabling (server's interface to switch's interface) using lldp
* Local inventory using `Inventory Item` for CPU, RAM, RAID cards, physical disks (behind raid cards) * Local inventory using `Inventory Item` for CPU, GPU, RAM, RAID cards, physical disks (behind raid cards)
* PSUs creation and power consumption reporting (based on vendor's tools) * PSUs creation and power consumption reporting (based on vendor's tools)
# Requirements # Requirements
- Netbox >= 2.6 - Netbox >= 3.7
- Python >= 3.4 - Python >= 3.7
- [pynetbox](https://github.com/digitalocean/pynetbox/) - [pynetbox](https://github.com/digitalocean/pynetbox/)
- [python3-netaddr](https://github.com/drkjam/netaddr) - [python3-netaddr](https://github.com/drkjam/netaddr)
- [python3-netifaces](https://github.com/al45tair/netifaces) - [python3-netifaces](https://github.com/al45tair/netifaces)
@ -33,35 +33,112 @@ The goal is to generate an existing infrastructure on Netbox and have the abilit
- lldpd - lldpd
- lshw - lshw
# Known limitations ## Inventory requirement
- hpassacli
- storcli
- omreport
* The project is only compatible with Linux. # Installation
Since it uses `ethtool` and parses `/sys/` directory, it's not compatible with *BSD distributions.
* Netbox `>=2.6.0,<=2.6.2` has a caching problem ; if the cache lifetime is too high, the script can get stale data after modification. ```
We advise to set `CACHE_TIME` to `0`. # pip3 install netbox-agent
```
# Usage
The agent can be run from a shell and get its configuration from either the configuration file or environment variables.
Configuration values are overridden based on the following precedence: command line arguments (might include config file) > environment variables > default config file > defaults.
```
# netbox_agent -c /etc/netbox_agent.yaml --register
INFO:root:Creating chassis blade (serial: QTFCQ574502EF)
INFO:root:Creating blade (serial: QTFCQ574502D2) myserver on chassis QTFCQ574502EF
INFO:root:Setting device (QTFCQ574502D2) new slot on Slot 9 (Chassis QTFCQ574502EF)..
INFO:root:Interface a8:1e:84:f2:9e:6a not found, creating..
INFO:root:Creating NIC enp1s0f1 (a8:1e:84:f2:9e:6a) on myserver
INFO:root:Interface 02:42:7a:89:cf:a4 not found, creating..
INFO:root:Creating NIC br-07ea1e4a2f0e (02:42:7a:89:cf:a4) on myserver
INFO:root:Create new IP 172.19.0.1/16 on br-07ea1e4a2f0e
INFO:root:Interface a8:1e:84:f2:9e:69 not found, creating..
INFO:root:Creating NIC enp1s0f0 (a8:1e:84:f2:9e:69) on myserver
INFO:root:Create new IP 42.42.42.42/24 on enp1s0f0
INFO:root:Create new IP fe80::aa1e:84ff:fef2:9e69/64 on enp1s0f0
INFO:root:Interface a8:1e:84:cd:9d:d6 not found, creating..
INFO:root:Creating NIC IPMI (a8:1e:84:cd:9d:d6) on myserver
INFO:root:Create new IP 10.191.122.10/24 on IPMI
```
If you need, you can update only specific informations like:
* Network
* Inventory
* Location
* PSUs
```
# ip a add 42.42.42.43/24 dev enp1s0f1
# netbox_agent -c /etc/netbox_agent.yaml --update-network
INFO:root:Create new IP 42.42.42.43/24 on enp1s0f1
# netbox_agent --update-inventory
INFO:root:Creating Disk Samsung SSD 850 S2RBNX0K101698D
```
# Configuration # Configuration
``` ```
# Netbox configuration
netbox: netbox:
url: 'http://netbox.internal.company.com' url: 'http://netbox.internal.company.com'
token: supersecrettoken token: supersecrettoken
# uncomment to disable ssl verification
# ssl_verify: false
# uncomment to use the system's CA certificates
# ssl_ca_certs_file: /etc/ssl/certs/ca-certificates.crt
# Network configuration
network: network:
# Regex to ignore interfaces
ignore_interfaces: "(dummy.*|docker.*)" ignore_interfaces: "(dummy.*|docker.*)"
# Regex to ignore IP addresses
ignore_ips: (127\.0\.0\..*) ignore_ips: (127\.0\.0\..*)
# enable auto-cabling # enable auto-cabling by parsing LLDP answers
lldp: true lldp: true
#
# You can use these to change the Netbox roles.
# These are the defaults.
#
#device:
# chassis_role: "Server Chassis"
# blade_role: "Blade"
# server_role: "Server"
# tags: server, blade, ,just a comma,delimited,list
# custom_fields: field1=value1,field2=value2#
#
# Can use this to set the tenant
#
#tenant:
# driver: "file:/tmp/tenant"
# regex: "(.*)"
## Enable virtual machine support
# virtual:
# # not mandatory, can be guessed
# enabled: True
# # see https://netbox.company.com/virtualization/clusters/
# cluster_name: my_vm_cluster
# Enable datacenter location feature in Netbox
datacenter_location: datacenter_location:
driver: "cmd:cat /etc/qualification | tr [a-z] [A-Z]" driver: "cmd:cat /etc/qualification | tr [A-Z] [a-z]"
regex: "DATACENTER: (?P<datacenter>[A-Za-z0-9]+)" regex: "datacenter: (?P<datacenter>[A-Za-z0-9]+)"
# driver: 'cmd:lldpctl' # driver: 'cmd:lldpctl'
# regex: 'SysName: .*\.([A-Za-z0-9]+)' # regex: 'SysName: .*\.([A-Za-z0-9]+)'
# #
# driver: "file:/tmp/datacenter" # driver: "file:/tmp/datacenter"
# regex: "(.*)" # regex: "(.*)"
# Enable rack location feature in Netbox
rack_location: rack_location:
# driver: 'cmd:lldpctl' # driver: 'cmd:lldpctl'
# match SysName: sw-dist-a1.dc42 # match SysName: sw-dist-a1.dc42
@ -70,6 +147,7 @@ rack_location:
# driver: "file:/tmp/datacenter" # driver: "file:/tmp/datacenter"
# regex: "(.*)" # regex: "(.*)"
# Enable local inventory reporting
inventory: true inventory: true
``` ```
@ -84,6 +162,36 @@ The `get_blade_slot` method return the name of the `Device Bay`.
Certain vendors don't report the blade slot in `dmidecode`, so we can use the `slot_location` regex feature of the configuration file. Certain vendors don't report the blade slot in `dmidecode`, so we can use the `slot_location` regex feature of the configuration file.
Some blade servers can be equipped with additional hardware using expansion blades, next to the processing blade, such as GPU expansion, or drives bay expansion. By default, the hardware from the expnasion is associated with the blade server itself, but it's possible to register the expansion as its own device using the `--expansion-as-device` command line parameter, or by setting `expansion_as_device` to `true` in the configuration file.
## Drives attributes processing
It is possible to process drives extended attributes such as the drive's physical or logical identifier, logical drive RAID type, size, consistency and so on.
Those attributes as set as `custom_fields` in Netbox, and need to be registered properly before being able to specify them during the inventory phase.
As the custom fields have to be created prior being able to register the disks extended attributes, this feature is only activated using the `--process-virtual-drives` command line parameter, or by setting `process_virtual_drives` to `true` in the configuration file.
The custom fields to create as `DCIM > inventory item` `Text` are described below.
```
NAME LABEL DESCRIPTION
mount_point Mount point Device mount point(s)
pd_identifier Physical disk identifier Physical disk identifier in the RAID controller
vd_array Virtual drive array Virtual drive array the disk is member of
vd_consistency Virtual drive consistency Virtual disk array consistency
vd_device Virtual drive device Virtual drive system device
vd_raid_type Virtual drive RAID Virtual drive array RAID type
vd_size Virtual drive size Virtual drive array size
```
In the current implementation, the disks attributes ore not updated: if a disk with the correct serial number is found, it's sufficient to consider it as up to date.
To force the reprocessing of the disks extended attributes, the `--force-disk-refresh` command line option can be used: it removes all existing disks to before populating them with the correct parsing. Unless this option is specified, the extended attributes won't be modified unless a disk is replaced.
It is possible to dump the physical/virtual disks map on the filesystem under the JSON notation to ease or automate disks management. The file path has to be provided using the `--dump-disks-map` command line parameter.
## Anycast IP ## Anycast IP
The default behavior of the agent is to assign an interface to an IP. The default behavior of the agent is to assign an interface to an IP.
@ -95,7 +203,15 @@ In order to handle this case, user need to set Netbox IP's mode to `Anycast` so
Tested on: Tested on:
## Dell Inc. ## Virtual Machines
* Hyper-V
* VMWare
* VirtualBox
* AWS
* GCP
## [Dell Inc.](https://github.com/Solvik/netbox-agent/blob/master/netbox_agent/vendors/dell.py)
### Blades ### Blades
@ -111,7 +227,7 @@ Tested on:
* DSS7500 * DSS7500
## HP / HPE ## [HP / HPE](https://github.com/Solvik/netbox-agent/blob/master/netbox_agent/vendors/hp.py)
### Blades ### Blades
@ -119,6 +235,8 @@ Tested on:
* HP ProLiant BL460c Gen8 * HP ProLiant BL460c Gen8
* HP ProLiant BL460c Gen9 * HP ProLiant BL460c Gen9
* HP ProLiant BL460c Gen10 * HP ProLiant BL460c Gen10
* HP ProLiant BL460c Gen10 Graphics Exp its expansion HP ProLiant BL460c Graphics Expansion Blade
* HP Moonshot 1500 Enclosure (your `DeviceType` should have slots batch create with `Bay c[1-45n1]`) with HP ProLiant m750, m710x, m510 Server Cartridge
### Pizzas ### Pizzas
@ -127,18 +245,18 @@ Tested on:
* ProLiant SL4540 Gen9 * ProLiant SL4540 Gen9
* ProLiant XL450 Gen10 * ProLiant XL450 Gen10
## Supermicro ## [Supermicro](https://github.com/Solvik/netbox-agent/blob/master/netbox_agent/vendors/supermicro.py)
### Blades ### Blades
Feel free to send me a dmidecode output for Supermicro's blade! * SBI-* and SBA-* should be supported, but I need dmidecode output example to support automatic blade location
### Pizzas ### Pizzas
* SSG-6028R * SSG-6028R
* SYS-6018R * SYS-6018R
## QCT ## [QCT](https://github.com/Solvik/netbox-agent/blob/master/netbox_agent/vendors/qct.py)
### Blades ### Blades
@ -148,6 +266,27 @@ Feel free to send me a dmidecode output for Supermicro's blade!
* Nothing ATM, feel free to send me a dmidecode or make a PR! * Nothing ATM, feel free to send me a dmidecode or make a PR!
# TODO # Known limitations
- [ ] `CustomFields` support with firmware versions for Device (BIOS), RAID Cards and disks * The project is only compatible with Linux.
Since it uses `ethtool` and parses `/sys/` directory, it's not compatible with *BSD distributions.
* Netbox `>=2.6.0,<=2.6.2` has a caching problem ; if the cache lifetime is too high, the script can get stale data after modification.
We advise to set `CACHE_TIME` to `0`.
# Developing
If you want to run the agent while adding features or just for debugging purposes
```
# git clone https://github.com/Solvik/netbox-agent.git
# cd netbox-agent
# python3 -m netbox_agent.cli --register
```
On a personal note, I use the docker image from [netbox-community/netbox-docker](https://github.com/netbox-community/netbox-docker)
```
# git clone https://github.com/netbox-community/netbox-docker
# cd netbox-docker
# docker-compose pull
# docker-compose up
```

55
default.nix Normal file
View file

@ -0,0 +1,55 @@
{
sources ? import ./npins,
pkgs ? import sources.nixpkgs { },
}:
let
checks = (import sources.git-hooks).run {
src = ./.;
hooks =
{
commitizen.enable = true;
}
// (pkgs.lib.genAttrs
[
"black"
"isort"
"ruff"
]
(hook: {
enable = true;
stages = [ "pre-push" ];
})
);
};
python3 = pkgs.python3.override {
packageOverrides = self: _: {
netifaces2 = self.callPackage ./nix/netifaces2.nix { };
};
};
in
{
devShell = pkgs.mkShell {
name = "netbox-agent.dev";
packages = [
(python3.withPackages (ps: [
ps.pynetbox
ps.netaddr
ps.netifaces2
ps.pyyaml
ps.jsonargparse
ps.python-slugify
ps.packaging
ps.distro
]))
] ++ checks.enabledPackages;
shellHook = ''
${checks.shellHook}
'';
};
}

View file

@ -1,8 +1,7 @@
-r requirements.txt
pytest pytest
pytest-cov pytest-cov
mypy flake8
flake8==3.7.9 flake8-isort
pep8-naming==0.9.1 tox
flake8-quotes==2.1.1
flake8-import-order==0.18.1
Sphinx

View file

@ -1,6 +1,8 @@
netbox: netbox:
url: 'http://netbox.internal.company.com' url: 'http://netbox.internal.company.com'
token: supersecrettoken token: supersecrettoken
# uncomment to disable ssl verification
# ssl_verify: false
network: network:
ignore_interfaces: "(dummy.*|docker.*)" ignore_interfaces: "(dummy.*|docker.*)"
@ -8,9 +10,26 @@ network:
# enable auto-cabling # enable auto-cabling
lldp: true lldp: true
#
# You can use these to change the roles.
#
#device:
# chassis_role: "Server Chassis"
# blade_role: "Blade"
# server_role: "Server"
# tags: server, blade, ,just a comma,delimited,list
# custom_fields: field1=value1,field2=value2
#
#
# Use this to set the tenant
#
#tenant:
# driver: "file:/tmp/tenant"
# regex: "(.*)"
datacenter_location: datacenter_location:
driver: "cmd:cat /etc/qualification | tr [a-z] [A-Z]" driver: "cmd:cat /etc/qualification | tr [A-Z] [a-z]"
regex: "DATACENTER: (?P<datacenter>[A-Za-z0-9]+)" regex: "datacenter: (?P<datacenter>[A-Za-z0-9]+)"
# driver: 'cmd:lldpctl' # driver: 'cmd:lldpctl'
# regex: 'SysName: .*\.([A-Za-z0-9]+)' # regex: 'SysName: .*\.([A-Za-z0-9]+)'
# #

View file

@ -1,6 +1,7 @@
from pkg_resources import DistributionNotFound, get_distribution from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as _get_version
try: try:
__version__ = get_distribution(__name__).version __version__ = _get_version(__name__)
except DistributionNotFound: except PackageNotFoundError:
pass pass

View file

@ -1,37 +1,63 @@
from netbox_agent.logging import logging # NOQA from packaging import version
from netbox_agent.vendors.dell import DellHost
import netbox_agent.dmidecode as dmidecode import netbox_agent.dmidecode as dmidecode
from netbox_agent.config import config from netbox_agent.config import config
from netbox_agent.config import netbox_instance as nb
from netbox_agent.logging import logging # NOQA
from netbox_agent.vendors.dell import DellHost
from netbox_agent.vendors.generic import GenericHost
from netbox_agent.vendors.hp import HPHost from netbox_agent.vendors.hp import HPHost
from netbox_agent.vendors.qct import QCTHost from netbox_agent.vendors.qct import QCTHost
from netbox_agent.vendors.supermicro import SupermicroHost from netbox_agent.vendors.supermicro import SupermicroHost
from netbox_agent.virtualmachine import VirtualMachine, is_vm
MANUFACTURERS = { MANUFACTURERS = {
'Dell Inc.': DellHost, "Dell Inc.": DellHost,
'HP': HPHost, "HP": HPHost,
'HPE': HPHost, "HPE": HPHost,
'Supermicro': SupermicroHost, "Supermicro": SupermicroHost,
'Quanta Cloud Technology Inc.': QCTHost, "Quanta Cloud Technology Inc.": QCTHost,
} "Generic": GenericHost,
}
def run(config): def run(config):
manufacturer = dmidecode.get_by_type('Chassis')[0].get('Manufacturer') dmi = dmidecode.parse()
server = MANUFACTURERS[manufacturer](dmi=dmidecode)
if config.virtual.enabled or is_vm(dmi):
if not config.virtual.cluster_name:
raise Exception(
"virtual.cluster_name parameter is mandatory because it's a VM"
)
server = VirtualMachine(dmi=dmi)
else:
manufacturer = dmidecode.get_by_type(dmi, "Chassis")[0].get("Manufacturer")
try:
server = MANUFACTURERS[manufacturer](dmi=dmi)
except KeyError:
server = GenericHost(dmi=dmi)
if version.parse(nb.version) < version.parse("3.7"):
print("netbox-agent is not compatible with Netbox prior to version 3.7")
return False
if (
config.register
or config.update_all
or config.update_network
or config.update_location
or config.update_inventory
or config.update_psu
):
server.netbox_create_or_update(config)
if config.debug: if config.debug:
server.print_debug() server.print_debug()
if config.register:
server.netbox_create(config)
if config.update_all or config.update_network or config.update_location or \
config.update_inventory or config.update_psu:
server.netbox_update(config)
return True return True
def main(): def main():
return run(config) return 0 if run(config) else 1
if __name__ == '__main__': if __name__ == "__main__":
main() main()

View file

@ -1,68 +1,186 @@
import logging import logging
import pynetbox
import jsonargparse
import sys import sys
import jsonargparse
import pynetbox
import requests
import urllib3
def get_config(): def get_config():
p = jsonargparse.ArgumentParser( p = jsonargparse.ArgumentParser(
default_config_files=[ default_config_files=[
'/etc/netbox_agent.yaml', "/etc/netbox_agent.yaml",
'~/.config/netbox_agent.yaml', "~/.config/netbox_agent.yaml",
'~/.netbox_agent.yaml', "~/.netbox_agent.yaml",
], ],
prog='netbox_agent', prog="netbox_agent",
description="Netbox agent to run on your infrastructure's servers", description="Netbox agent to run on your infrastructure's servers",
env_prefix="NETBOX_AGENT_",
default_env=True,
) )
p.add_argument('-c', '--config', action=jsonargparse.ActionConfigFile) p.add_argument("-c", "--config", action=jsonargparse.ActionConfigFile)
p.add_argument('-r', '--register', action='store_true', help='Register server to Netbox') p.add_argument(
p.add_argument('-u', '--update-all', action='store_true', help='Update all infos in Netbox') "-r", "--register", action="store_true", help="Register server to Netbox"
p.add_argument('-d', '--debug', action='store_true', help='Print debug infos') )
p.add_argument('--update-network', action='store_true', help='Update network') p.add_argument(
p.add_argument('--update-inventory', action='store_true', help='Update inventory') "-u", "--update-all", action="store_true", help="Update all infos in Netbox"
p.add_argument('--update-location', action='store_true', help='Update location') )
p.add_argument('--update-psu', action='store_true', help='Update PSU') p.add_argument("-d", "--debug", action="store_true", help="Print debug infos")
p.add_argument("--update-network", action="store_true", help="Update network")
p.add_argument("--update-inventory", action="store_true", help="Update inventory")
p.add_argument("--update-location", action="store_true", help="Update location")
p.add_argument("--update-psu", action="store_true", help="Update PSU")
p.add_argument(
"--purge-old-devices",
action="store_true",
help="Purge existing (old ?) devices having same name but different serial",
)
p.add_argument(
"--expansion-as-device",
action="store_true",
help="Manage blade expansions as external devices",
)
p.add_argument('--log_level', default='debug') p.add_argument("--log_level", default="debug")
p.add_argument('--netbox.url', help='Netbox URL') p.add_argument("--netbox.ssl_ca_certs_file", help="SSL CA certificates file")
p.add_argument('--netbox.token', help='Netbox API Token') p.add_argument("--netbox.url", help="Netbox URL")
p.add_argument('--hostname_cmd', default=None, p.add_argument("--netbox.token", help="Netbox API Token")
help="Command to output hostname, used as Device's name in netbox") p.add_argument(
p.add_argument('--datacenter_location.driver', "--netbox.ssl_verify",
help='Datacenter location driver, ie: cmd, file') default=True,
p.add_argument('--datacenter_location.driver_file', action="store_true",
help='Datacenter location custom driver file path') help="Disable SSL verification",
p.add_argument('--datacenter_location.regex', )
help='Datacenter location regex to extract Netbox DC slug') p.add_argument(
p.add_argument('--rack_location.driver', help='Rack location driver, ie: cmd, file') "--virtual.enabled", action="store_true", help="Is a virtual machine or not"
p.add_argument('--rack_location.driver_file', help='Rack location custom driver file path') )
p.add_argument('--rack_location.regex', help='Rack location regex to extract Rack name') p.add_argument("--virtual.cluster_name", help="Cluster name of VM")
p.add_argument('--slot_location.driver', help='Slot location driver, ie: cmd, file') p.add_argument(
p.add_argument('--slot_location.driver_file', help='Slot location custom driver file path') "--hostname_cmd",
p.add_argument('--slot_location.regex', help='Slot location regex to extract slot name') default=None,
p.add_argument('--network.ignore_interfaces', default=r'(dummy.*|docker.*)', help="Command to output hostname, used as Device's name in netbox",
help='Regex to ignore interfaces') )
p.add_argument('--network.ignore_ips', default=r'^(127\.0\.0\..*|fe80.*|::1.*)', p.add_argument(
help='Regex to ignore IPs') "--device.platform",
p.add_argument('--network.lldp', help='Enable auto-cabling feature through LLDP infos') default=None,
p.add_argument('--inventory', action='store_true', help="Override device platform. Here we use OS distribution.",
help='Enable HW inventory (CPU, Memory, RAID Cards, Disks) feature') )
p.add_argument("--device.tags", default=r"", help="tags to use for a host")
p.add_argument(
"--preserve-tags",
action="store_true",
help="Append new unique tags, preserve those already present",
)
p.add_argument(
"--device.custom_fields",
default=r"",
help="custom_fields to use for a host, eg: field1=v1,field2=v2",
)
p.add_argument(
"--device.blade_role", default=r"Blade", help="role to use for a blade server"
)
p.add_argument(
"--device.chassis_role",
default=r"Server Chassis",
help="role to use for a chassis",
)
p.add_argument(
"--device.server_role", default=r"Server", help="role to use for a server"
)
p.add_argument("--tenant.driver", help="tenant driver, ie cmd, file")
p.add_argument("--tenant.driver_file", help="tenant driver custom driver file path")
p.add_argument("--tenant.regex", help="tenant regex to extract Netbox tenant slug")
p.add_argument(
"--datacenter_location.driver", help="Datacenter location driver, ie: cmd, file"
)
p.add_argument(
"--datacenter_location.driver_file",
help="Datacenter location custom driver file path",
)
p.add_argument(
"--datacenter_location.regex",
help="Datacenter location regex to extract Netbox DC slug",
)
p.add_argument("--rack_location.driver", help="Rack location driver, ie: cmd, file")
p.add_argument(
"--rack_location.driver_file", help="Rack location custom driver file path"
)
p.add_argument(
"--rack_location.regex", help="Rack location regex to extract Rack name"
)
p.add_argument("--slot_location.driver", help="Slot location driver, ie: cmd, file")
p.add_argument(
"--slot_location.driver_file", help="Slot location custom driver file path"
)
p.add_argument(
"--slot_location.regex", help="Slot location regex to extract slot name"
)
p.add_argument(
"--network.ignore_interfaces",
default=r"(dummy.*|docker.*)",
help="Regex to ignore interfaces",
)
p.add_argument(
"--network.ignore_ips",
default=r"^(127\.0\.0\..*|fe80.*|::1.*)",
help="Regex to ignore IPs",
)
p.add_argument(
"--network.ipmi", default=True, help="Enable gathering IPMI information"
)
p.add_argument(
"--network.lldp", help="Enable auto-cabling feature through LLDP infos"
)
p.add_argument(
"--inventory",
action="store_true",
help="Enable HW inventory (CPU, Memory, RAID Cards, Disks) feature",
)
p.add_argument(
"--process-virtual-drives",
action="store_true",
help="Process virtual drives information from RAID "
"controllers to fill disk custom_fields",
)
p.add_argument(
"--force-disk-refresh",
action="store_true",
help="Forces disks detection reprocessing",
)
p.add_argument(
"--dump-disks-map", help="File path to dump physical/virtual disks map"
)
options = p.parse_args() options = p.parse_args()
return options return options
config = get_config()
def get_netbox_instance(): def get_netbox_instance():
config = get_config()
if config.netbox.url is None or config.netbox.token is None: if config.netbox.url is None or config.netbox.token is None:
logging.error('Netbox URL and token are mandatory') logging.error("Netbox URL and token are mandatory")
sys.exit(1) sys.exit(1)
return pynetbox.api(
nb = pynetbox.api(
url=get_config().netbox.url, url=get_config().netbox.url,
token=get_config().netbox.token, token=get_config().netbox.token,
) )
ca_certs_file = config.netbox.ssl_ca_certs_file
if ca_certs_file is not None:
session = requests.Session()
session.verify = ca_certs_file
nb.http_session = session
elif config.netbox.ssl_verify is False:
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
session = requests.Session()
session.verify = False
nb.http_session = session
return nb
config = get_config()
netbox_instance = get_netbox_instance() netbox_instance = get_netbox_instance()

View file

@ -1,78 +1,83 @@
import logging
import re as _re import re as _re
import subprocess as _subprocess import subprocess as _subprocess
import sys import sys
from netbox_agent.misc import is_tool from netbox_agent.misc import is_tool
import logging
_handle_re = _re.compile('^Handle\\s+(.+),\\s+DMI\\s+type\\s+(\\d+),\\s+(\\d+)\\s+bytes$') _handle_re = _re.compile(
_in_block_re = _re.compile('^\\t\\t(.+)$') "^Handle\\s+(.+),\\s+DMI\\s+type\\s+(\\d+),\\s+(\\d+)\\s+bytes$"
_record_re = _re.compile('\\t(.+):\\s+(.+)$') )
_record2_re = _re.compile('\\t(.+):$') _in_block_re = _re.compile("^\\t\\t(.+)$")
_record_re = _re.compile("\\t(.+):\\s+(.+)$")
_record2_re = _re.compile("\\t(.+):$")
_type2str = { _type2str = {
0: 'BIOS', 0: "BIOS",
1: 'System', 1: "System",
2: 'Baseboard', 2: "Baseboard",
3: 'Chassis', 3: "Chassis",
4: 'Processor', 4: "Processor",
5: 'Memory Controller', 5: "Memory Controller",
6: 'Memory Module', 6: "Memory Module",
7: 'Cache', 7: "Cache",
8: 'Port Connector', 8: "Port Connector",
9: 'System Slots', 9: "System Slots",
10: ' On Board Devices', 10: " On Board Devices",
11: ' OEM Strings', 11: " OEM Strings",
12: ' System Configuration Options', 12: " System Configuration Options",
13: ' BIOS Language', 13: " BIOS Language",
14: ' Group Associations', 14: " Group Associations",
15: ' System Event Log', 15: " System Event Log",
16: ' Physical Memory Array', 16: " Physical Memory Array",
17: ' Memory Device', 17: " Memory Device",
18: ' 32-bit Memory Error', 18: " 32-bit Memory Error",
19: ' Memory Array Mapped Address', 19: " Memory Array Mapped Address",
20: ' Memory Device Mapped Address', 20: " Memory Device Mapped Address",
21: ' Built-in Pointing Device', 21: " Built-in Pointing Device",
22: ' Portable Battery', 22: " Portable Battery",
23: ' System Reset', 23: " System Reset",
24: ' Hardware Security', 24: " Hardware Security",
25: ' System Power Controls', 25: " System Power Controls",
26: ' Voltage Probe', 26: " Voltage Probe",
27: ' Cooling Device', 27: " Cooling Device",
28: ' Temperature Probe', 28: " Temperature Probe",
29: ' Electrical Current Probe', 29: " Electrical Current Probe",
30: ' Out-of-band Remote Access', 30: " Out-of-band Remote Access",
31: ' Boot Integrity Services', 31: " Boot Integrity Services",
32: ' System Boot', 32: " System Boot",
33: ' 64-bit Memory Error', 33: " 64-bit Memory Error",
34: ' Management Device', 34: " Management Device",
35: ' Management Device Component', 35: " Management Device Component",
36: ' Management Device Threshold Data', 36: " Management Device Threshold Data",
37: ' Memory Channel', 37: " Memory Channel",
38: ' IPMI Device', 38: " IPMI Device",
39: ' Power Supply', 39: " Power Supply",
40: ' Additional Information', 40: " Additional Information",
41: ' Onboard Devices Extended Information', 41: " Onboard Devices Extended Information",
42: ' Management Controller Host Interface' 42: " Management Controller Host Interface",
} }
_str2type = {} _str2type = {}
for type_id, type_str in _type2str.items(): for type_id, type_str in _type2str.items():
_str2type[type_str] = type_id _str2type[type_str] = type_id
def parse(): def parse(output=None):
""" """
parse the full output of the dmidecode parse the full output of the dmidecode
command and return a dic containing the parsed information command and return a dic containing the parsed information
""" """
if output:
buffer = output
else:
buffer = _execute_cmd() buffer = _execute_cmd()
if isinstance(buffer, bytes): if isinstance(buffer, bytes):
buffer = buffer.decode('utf-8') buffer = buffer.decode("utf-8")
_data = _parse(buffer) _data = _parse(buffer)
return _data return _data
def get_by_type(type_id): def get_by_type(data, type_id):
""" """
filter the output of dmidecode per type filter the output of dmidecode per type
0 BIOS 0 BIOS
@ -124,27 +129,33 @@ def get_by_type(type_id):
if type_id is None: if type_id is None:
return None return None
data = parse()
result = [] result = []
for entry in data.values(): for entry in data.values():
if entry['DMIType'] == type_id: if entry["DMIType"] == type_id:
result.append(entry) result.append(entry)
return result return result
def _execute_cmd(): def _execute_cmd():
if not is_tool('dmidecode'): if not is_tool("dmidecode"):
logging.error('Dmidecode does not seem to be present on your system. Add it your path or ' logging.error(
'check the compatibility of this project with your distro.') "Dmidecode does not seem to be present on your system. Add it your path or "
"check the compatibility of this project with your distro."
)
sys.exit(1) sys.exit(1)
return _subprocess.check_output(['dmidecode', ], stderr=_subprocess.PIPE) return _subprocess.check_output(
[
"dmidecode",
],
stderr=_subprocess.PIPE,
)
def _parse(buffer): def _parse(buffer):
output_data = {} output_data = {}
# Each record is separated by double newlines # Each record is separated by double newlines
split_output = buffer.split('\n\n') split_output = buffer.split("\n\n")
for record in split_output: for record in split_output:
record_element = record.splitlines() record_element = record.splitlines()
@ -162,21 +173,21 @@ def _parse(buffer):
dmi_handle = handle_data[0] dmi_handle = handle_data[0]
output_data[dmi_handle] = {} output_data[dmi_handle] = {}
output_data[dmi_handle]['DMIType'] = int(handle_data[1]) output_data[dmi_handle]["DMIType"] = int(handle_data[1])
output_data[dmi_handle]['DMISize'] = int(handle_data[2]) output_data[dmi_handle]["DMISize"] = int(handle_data[2])
# Okay, we know 2nd line == name # Okay, we know 2nd line == name
output_data[dmi_handle]['DMIName'] = record_element[1] output_data[dmi_handle]["DMIName"] = record_element[1]
in_block_elemet = '' in_block_elemet = ""
in_block_list = '' in_block_list = ""
# Loop over the rest of the record, gathering values # Loop over the rest of the record, gathering values
for i in range(2, len(record_element), 1): for i in range(2, len(record_element), 1):
if i >= len(record_element): if i >= len(record_element):
break break
# Check whether we are inside a \t\t block # Check whether we are inside a \t\t block
if in_block_elemet != '': if in_block_elemet != "":
in_block_data = _in_block_re.findall(record_element[i]) in_block_data = _in_block_re.findall(record_element[i])
if in_block_data: if in_block_data:
@ -190,7 +201,7 @@ def _parse(buffer):
else: else:
# We are out of the \t\t block; reset it again, and let # We are out of the \t\t block; reset it again, and let
# the parsing continue # the parsing continue
in_block_elemet = '' in_block_elemet = ""
record_data = _record_re.findall(record_element[i]) record_data = _record_re.findall(record_element[i])
@ -206,7 +217,7 @@ def _parse(buffer):
# This is an array of data - let the loop know we are inside # This is an array of data - let the loop know we are inside
# an array block # an array block
in_block_elemet = record_data2[0] in_block_elemet = record_data2[0]
in_block_list = '' in_block_list = ""
continue continue

View file

@ -2,7 +2,7 @@ import re
def get(value, regex): def get(value, regex):
for line in open(value, 'r'): for line in open(value, "r"):
r = re.search(regex, line) r = re.search(regex, line)
if r and len(r.groups()) > 0: if r and len(r.groups()) > 0:
return r.groups()[0] return r.groups()[0]

View file

@ -1,21 +1,21 @@
import re import re
from shutil import which
import subprocess import subprocess
from shutil import which
# Originally from https://github.com/opencoff/useful-scripts/blob/master/linktest.py # Originally from https://github.com/opencoff/useful-scripts/blob/master/linktest.py
# mapping fields from ethtool output to simple names # mapping fields from ethtool output to simple names
field_map = { field_map = {
'Supported ports': 'ports', "Supported ports": "ports",
'Supported link modes': 'sup_link_modes', "Supported link modes": "sup_link_modes",
'Supports auto-negotiation': 'sup_autoneg', "Supports auto-negotiation": "sup_autoneg",
'Advertised link modes': 'adv_link_modes', "Advertised link modes": "adv_link_modes",
'Advertised auto-negotiation': 'adv_autoneg', "Advertised auto-negotiation": "adv_autoneg",
'Speed': 'speed', "Speed": "speed",
'Duplex': 'duplex', "Duplex": "duplex",
'Port': 'port', "Port": "port",
'Auto-negotiation': 'autoneg', "Auto-negotiation": "autoneg",
'Link detected': 'link', "Link detected": "link",
} }
@ -25,12 +25,13 @@ def merge_two_dicts(x, y):
return z return z
class Ethtool(): class Ethtool:
""" """
This class aims to parse ethtool output This class aims to parse ethtool output
There is several bindings to have something proper, but it requires There is several bindings to have something proper, but it requires
compilation and other requirements. compilation and other requirements.
""" """
def __init__(self, interface, *args, **kwargs): def __init__(self, interface, *args, **kwargs):
self.interface = interface self.interface = interface
@ -39,39 +40,40 @@ class Ethtool():
parse ethtool output parse ethtool output
""" """
output = subprocess.getoutput('ethtool {}'.format(self.interface)) output = subprocess.getoutput("ethtool {}".format(self.interface))
fields = {} fields = {}
field = '' field = ""
fields['speed'] = '-' fields["speed"] = "-"
fields['link'] = '-' fields["link"] = "-"
fields['duplex'] = '-' fields["duplex"] = "-"
for line in output.split('\n')[1:]: for line in output.split("\n")[1:]:
line = line.rstrip() line = line.rstrip()
r = line.find(':') r = line.find(":")
if r > 0: if r > 0:
field = line[:r].strip() field = line[:r].strip()
if field not in field_map: if field not in field_map:
continue continue
field = field_map[field] field = field_map[field]
output = line[r+1:].strip() output = line[r + 1 :].strip()
fields[field] = output fields[field] = output
else: else:
if len(field) > 0 and \ if len(field) > 0 and field in field_map:
field in field_map: fields[field] += " " + line.strip()
fields[field] += ' ' + line.strip()
return fields return fields
def _parse_ethtool_module_output(self): def _parse_ethtool_module_output(self):
status, output = subprocess.getstatusoutput('ethtool -m {}'.format(self.interface)) status, output = subprocess.getstatusoutput(
if status != 0: "ethtool -m {}".format(self.interface)
return {} )
r = re.search(r'Identifier.*\((\w+)\)', output) if status == 0:
r = re.search(r"Identifier.*\((\w+)\)", output)
if r and len(r.groups()) > 0: if r and len(r.groups()) > 0:
return {'form_factor': r.groups()[0]} return {"form_factor": r.groups()[0]}
return {}
def parse(self): def parse(self):
if which('ethtool') is None: if which("ethtool") is None:
return None return None
output = self._parse_ethtool_output() output = self._parse_ethtool_output()
output.update(self._parse_ethtool_module_output()) output.update(self._parse_ethtool_module_output())

View file

@ -1,25 +1,31 @@
import json
import logging import logging
import pynetbox
import re import re
import sys
import traceback
from netbox_agent.config import netbox_instance as nb, config import pynetbox
from netbox_agent.misc import is_tool, get_vendor
from netbox_agent.config import config
from netbox_agent.config import netbox_instance as nb
from netbox_agent.lshw import LSHW
from netbox_agent.misc import get_vendor, is_tool
from netbox_agent.raid.hp import HPRaid from netbox_agent.raid.hp import HPRaid
from netbox_agent.raid.omreport import OmreportRaid from netbox_agent.raid.omreport import OmreportRaid
from netbox_agent.raid.storcli import StorcliRaid from netbox_agent.raid.storcli import StorcliRaid
from netbox_agent.lshw import LSHW
INVENTORY_TAG = { INVENTORY_TAG = {
'cpu': {'name': 'hw:cpu', 'slug': 'hw-cpu'}, "cpu": {"name": "hw:cpu", "slug": "hw-cpu"},
'disk': {'name': 'hw:disk', 'slug': 'hw-disk'}, "gpu": {"name": "hw:gpu", "slug": "hw-gpu"},
'interface': {'name': 'hw:interface', 'slug': 'hw-interface'}, "disk": {"name": "hw:disk", "slug": "hw-disk"},
'memory': {'name': 'hw:memory', 'slug': 'hw-memory'}, "interface": {"name": "hw:interface", "slug": "hw-interface"},
'motherboard': {'name': 'hw:motherboard', 'slug': 'hw-motherboard'}, "memory": {"name": "hw:memory", "slug": "hw-memory"},
'raid_card': {'name': 'hw:raid_card', 'slug': 'hw-raid-card'}, "motherboard": {"name": "hw:motherboard", "slug": "hw-motherboard"},
} "raid_card": {"name": "hw:raid_card", "slug": "hw-raid-card"},
}
class Inventory(): class Inventory:
""" """
Better Inventory items coming, see: Better Inventory items coming, see:
- https://github.com/netbox-community/netbox/issues/3087 - https://github.com/netbox-community/netbox/issues/3087
@ -30,6 +36,7 @@ class Inventory():
* cpu * cpu
* raid cards * raid cards
* disks * disks
* gpus
methods that: methods that:
* get local item * get local item
@ -42,10 +49,11 @@ class Inventory():
- no scan of NVMe devices - no scan of NVMe devices
""" """
def __init__(self, server): def __init__(self, server, update_expansion=False):
self.create_netbox_tags() self.create_netbox_tags()
self.server = server self.server = server
netbox_server = self.server.get_netbox_server() self.update_expansion = update_expansion
netbox_server = self.server.get_netbox_server(update_expansion)
self.device_id = netbox_server.id if netbox_server else None self.device_id = netbox_server.id if netbox_server else None
self.raid = None self.raid = None
@ -54,16 +62,17 @@ class Inventory():
self.lshw = LSHW() self.lshw = LSHW()
def create_netbox_tags(self): def create_netbox_tags(self):
ret = []
for key, tag in INVENTORY_TAG.items(): for key, tag in INVENTORY_TAG.items():
nb_tag = nb.extras.tags.get( nb_tag = nb.extras.tags.get(name=tag["name"])
name=tag['name']
)
if not nb_tag: if not nb_tag:
nb_tag = nb.extras.tags.create( nb_tag = nb.extras.tags.create(
name=tag['name'], name=tag["name"],
slug=tag['slug'], slug=tag["slug"],
comments=tag['name'], comments=tag["name"],
) )
ret.append(nb_tag)
return ret
def find_or_create_manufacturer(self, name): def find_or_create_manufacturer(self, name):
if name is None: if name is None:
@ -73,29 +82,28 @@ class Inventory():
name=name, name=name,
) )
if not manufacturer: if not manufacturer:
logging.info('Creating missing manufacturer {name}'.format(name=name)) logging.info("Creating missing manufacturer {name}".format(name=name))
manufacturer = nb.dcim.manufacturers.create( manufacturer = nb.dcim.manufacturers.create(
name=name, name=name,
slug=re.sub('[^A-Za-z0-9]+', '-', name).lower(), slug=re.sub("[^A-Za-z0-9]+", "-", name).lower(),
) )
logging.info('Creating missing manufacturer {name}'.format(name=name)) logging.info("Creating missing manufacturer {name}".format(name=name))
return manufacturer return manufacturer
def get_netbox_inventory(self, device_id, tag): def get_netbox_inventory(self, device_id, tag):
try: try:
items = nb.dcim.inventory_items.filter( items = nb.dcim.inventory_items.filter(device_id=device_id, tag=tag)
device_id=device_id,
tag=tag
)
except pynetbox.core.query.RequestError: except pynetbox.core.query.RequestError:
logging.info('Tag {tag} is missing, returning empty array.'.format(tag=tag)) logging.info("Tag {tag} is missing, returning empty array.".format(tag=tag))
items = [] items = []
return items return list(items)
def create_netbox_inventory_item(self, device_id, tags, vendor, name, serial, description): def create_netbox_inventory_item(
self, device_id, tags, vendor, name, serial, description
):
manufacturer = self.find_or_create_manufacturer(vendor) manufacturer = self.find_or_create_manufacturer(vendor)
_ = nb.dcim.inventory_items.create( _ = nb.dcim.inventory_items.create(
@ -103,26 +111,25 @@ class Inventory():
manufacturer=manufacturer.id, manufacturer=manufacturer.id,
discovered=True, discovered=True,
tags=tags, tags=tags,
name='{}'.format(name), name="{}".format(name),
serial='{}'.format(serial), serial="{}".format(serial),
description=description description=description,
) )
logging.info('Creating inventory item {} {}/{} {} '.format( logging.info(
vendor, "Creating inventory item {} {}/{} {} ".format(
name, vendor, name, serial, description
serial, )
description)
) )
def get_hw_motherboards(self): def get_hw_motherboards(self):
motherboards = [] motherboards = []
m = {} m = {}
m['serial'] = self.lshw.motherboard_serial m["serial"] = self.lshw.motherboard_serial
m['vendor'] = self.lshw.vendor m["vendor"] = self.lshw.vendor
m['name'] = '{} {}'.format(self.lshw.vendor, self.lshw.motherboard) m["name"] = "{} {}".format(self.lshw.vendor, self.lshw.motherboard)
m['description'] = '{} Motherboard'.format(self.lshw.motherboard) m["description"] = "{} Motherboard".format(self.lshw.motherboard)
motherboards.append(m) motherboards.append(m)
@ -132,27 +139,29 @@ class Inventory():
motherboards = self.get_hw_motherboards() motherboards = self.get_hw_motherboards()
nb_motherboards = self.get_netbox_inventory( nb_motherboards = self.get_netbox_inventory(
device_id=self.device_id, device_id=self.device_id, tag=INVENTORY_TAG["motherboard"]["slug"]
tag=INVENTORY_TAG['motherboard']['slug']) )
for nb_motherboard in nb_motherboards: for nb_motherboard in nb_motherboards:
if nb_motherboard.serial not in [x['serial'] for x in motherboards]: if nb_motherboard.serial not in [x["serial"] for x in motherboards]:
logging.info('Deleting unknown motherboard {vendor} {motherboard}/{serial}'.format( logging.info(
"Deleting unknown motherboard {motherboard}/{serial}".format(
motherboard=self.lshw.motherboard, motherboard=self.lshw.motherboard,
serial=nb_motherboard.serial, serial=nb_motherboard.serial,
)) )
)
nb_motherboard.delete() nb_motherboard.delete()
# create interfaces that are not in netbox # create interfaces that are not in netbox
for motherboard in motherboards: for motherboard in motherboards:
if motherboard.get('serial') not in [x.serial for x in nb_motherboards]: if motherboard.get("serial") not in [x.serial for x in nb_motherboards]:
self.create_netbox_inventory_item( self.create_netbox_inventory_item(
device_id=self.device_id, device_id=self.device_id,
tags=[INVENTORY_TAG['motherboard']['name']], tags=[{"name": INVENTORY_TAG["motherboard"]["name"]}],
vendor='{}'.format(motherboard.get('vendor', 'N/A')), vendor="{}".format(motherboard.get("vendor", "N/A")),
serial='{}'.format(motherboard.get('serial', 'No SN')), serial="{}".format(motherboard.get("serial", "No SN")),
name='{}'.format(motherboard.get('name')), name="{}".format(motherboard.get("name")),
description='{}'.format(motherboard.get('description')) description="{}".format(motherboard.get("description")),
) )
def create_netbox_interface(self, iface): def create_netbox_interface(self, iface):
@ -161,84 +170,93 @@ class Inventory():
device=self.device_id, device=self.device_id,
manufacturer=manufacturer.id, manufacturer=manufacturer.id,
discovered=True, discovered=True,
tags=[INVENTORY_TAG['interface']['name']], tags=[{"name": INVENTORY_TAG["interface"]["name"]}],
name="{}".format(iface['product']), name="{}".format(iface["product"]),
serial='{}'.format(iface['serial']), serial="{}".format(iface["serial"]),
description='{} {}'.format(iface['description'], iface['name']) description="{} {}".format(iface["description"], iface["name"]),
) )
def do_netbox_interfaces(self): def do_netbox_interfaces(self):
nb_interfaces = self.get_netbox_inventory( nb_interfaces = self.get_netbox_inventory(
device_id=self.device_id, device_id=self.device_id, tag=INVENTORY_TAG["interface"]["slug"]
tag=INVENTORY_TAG['interface']['slug']) )
interfaces = self.lshw.interfaces interfaces = self.lshw.interfaces
# delete interfaces that are in netbox but not locally # delete interfaces that are in netbox but not locally
# use the serial_number has the comparison element # use the serial_number has the comparison element
for nb_interface in nb_interfaces: for nb_interface in nb_interfaces:
if nb_interface.serial not in [x['serial'] for x in interfaces]: if nb_interface.serial not in [x["serial"] for x in interfaces]:
logging.info('Deleting unknown interface {serial}'.format( logging.info(
"Deleting unknown interface {serial}".format(
serial=nb_interface.serial, serial=nb_interface.serial,
)) )
)
nb_interface.delete() nb_interface.delete()
# create interfaces that are not in netbox # create interfaces that are not in netbox
for iface in interfaces: for iface in interfaces:
if iface.get('serial') not in [x.serial for x in nb_interfaces]: if iface.get("serial") not in [x.serial for x in nb_interfaces]:
self.create_netbox_interface(iface) self.create_netbox_interface(iface)
def create_netbox_cpus(self): def create_netbox_cpus(self):
for cpu in self.lshw.get_hw_linux('cpu'): for cpu in self.lshw.get_hw_linux("cpu"):
manufacturer = self.find_or_create_manufacturer(cpu["vendor"]) manufacturer = self.find_or_create_manufacturer(cpu["vendor"])
_ = nb.dcim.inventory_items.create( _ = nb.dcim.inventory_items.create(
device=self.device_id, device=self.device_id,
manufacturer=manufacturer.id, manufacturer=manufacturer.id,
discovered=True, discovered=True,
tags=[INVENTORY_TAG['cpu']['name']], tags=[{"name": INVENTORY_TAG["cpu"]["name"]}],
name=cpu['product'], name=cpu["product"],
description='CPU {}'.format(cpu['location']), description="CPU {}".format(cpu["location"]),
# asset_tag=cpu['location'] # asset_tag=cpu['location']
) )
logging.info('Creating CPU model {}'.format(cpu['product'])) logging.info("Creating CPU model {}".format(cpu["product"]))
def do_netbox_cpus(self): def do_netbox_cpus(self):
cpus = self.lshw.get_hw_linux('cpu') cpus = self.lshw.get_hw_linux("cpu")
nb_cpus = self.get_netbox_inventory( nb_cpus = self.get_netbox_inventory(
device_id=self.device_id, device_id=self.device_id,
tag=INVENTORY_TAG['cpu']['slug'], tag=INVENTORY_TAG["cpu"]["slug"],
) )
if not len(nb_cpus) or \ if not len(nb_cpus) or len(nb_cpus) and len(cpus) != len(nb_cpus):
len(nb_cpus) and len(cpus) != len(nb_cpus):
for x in nb_cpus: for x in nb_cpus:
x.delete() x.delete()
self.create_netbox_cpus() self.create_netbox_cpus()
def get_raid_cards(self): def get_raid_cards(self, filter_cards=False):
raid_class = None raid_class = None
if self.server.manufacturer == 'Dell': if self.server.manufacturer in ("Dell", "Huawei"):
if is_tool('omreport'): if is_tool("omreport"):
raid_class = OmreportRaid raid_class = OmreportRaid
if is_tool('storcli'): if is_tool("storcli"):
raid_class = StorcliRaid raid_class = StorcliRaid
elif self.server.manufacturer == 'HP': elif self.server.manufacturer in ("HP", "HPE"):
if is_tool('ssacli'): if is_tool("ssacli"):
raid_class = HPRaid raid_class = HPRaid
if not raid_class: if not raid_class:
return [] return []
self.raid = raid_class() self.raid = raid_class()
controllers = self.raid.get_controllers()
if len(self.raid.get_controllers()): if (
return controllers filter_cards
and config.expansion_as_device
and self.server.own_expansion_slot()
):
return [
c
for c in self.raid.get_controllers()
if c.is_external() is self.update_expansion
]
else:
return self.raid.get_controllers()
def create_netbox_raid_card(self, raid_card): def create_netbox_raid_card(self, raid_card):
manufacturer = self.find_or_create_manufacturer( manufacturer = self.find_or_create_manufacturer(raid_card.get_manufacturer())
raid_card.get_manufacturer()
)
name = raid_card.get_product_name() name = raid_card.get_product_name()
serial = raid_card.get_serial_number() serial = raid_card.get_serial_number()
@ -246,15 +264,17 @@ class Inventory():
device=self.device_id, device=self.device_id,
discovered=True, discovered=True,
manufacturer=manufacturer.id if manufacturer else None, manufacturer=manufacturer.id if manufacturer else None,
tags=[INVENTORY_TAG['raid_card']['name']], tags=[{"name": INVENTORY_TAG["raid_card"]["name"]}],
name='{}'.format(name), name="{}".format(name),
serial='{}'.format(serial), serial="{}".format(serial),
description='RAID Card', description="RAID Card",
) )
logging.info('Creating RAID Card {name} (SN: {serial})'.format( logging.info(
"Creating RAID Card {name} (SN: {serial})".format(
name=name, name=name,
serial=serial, serial=serial,
)) )
)
return nb_raid_card return nb_raid_card
def do_netbox_raid_cards(self): def do_netbox_raid_cards(self):
@ -269,18 +289,19 @@ class Inventory():
""" """
nb_raid_cards = self.get_netbox_inventory( nb_raid_cards = self.get_netbox_inventory(
device_id=self.device_id, device_id=self.device_id, tag=[INVENTORY_TAG["raid_card"]["slug"]]
tag=[INVENTORY_TAG['raid_card']['slug']]
) )
raid_cards = self.get_raid_cards() raid_cards = self.get_raid_cards(filter_cards=True)
# delete cards that are in netbox but not locally # delete cards that are in netbox but not locally
# use the serial_number has the comparison element # use the serial_number has the comparison element
for nb_raid_card in nb_raid_cards: for nb_raid_card in nb_raid_cards:
if nb_raid_card.serial not in [x.get_serial_number() for x in raid_cards]: if nb_raid_card.serial not in [x.get_serial_number() for x in raid_cards]:
logging.info('Deleting unknown locally RAID Card {serial}'.format( logging.info(
"Deleting unknown locally RAID Card {serial}".format(
serial=nb_raid_card.serial, serial=nb_raid_card.serial,
)) )
)
nb_raid_card.delete() nb_raid_card.delete()
# create card that are not in netbox # create card that are not in netbox
@ -288,56 +309,70 @@ class Inventory():
if raid_card.get_serial_number() not in [x.serial for x in nb_raid_cards]: if raid_card.get_serial_number() not in [x.serial for x in nb_raid_cards]:
self.create_netbox_raid_card(raid_card) self.create_netbox_raid_card(raid_card)
def is_virtual_disk(self, disk): def is_virtual_disk(self, disk, raid_devices):
logicalname = disk.get('logicalname') disk_type = disk.get("type")
description = disk.get('description') logicalname = disk.get("logicalname")
size = disk.get('size') description = disk.get("description")
product = disk.get('product') size = disk.get("size")
product = disk.get("product")
if (
logicalname in raid_devices
or disk_type is None
or product is None
or description is None
):
return True
non_raid_disks = [ non_raid_disks = [
'MR9361-8i', "MR9361-8i",
] ]
if size is None and logicalname is None or \ if (
'virtual' in product.lower() or 'logical' in product.lower() or \ logicalname in raid_devices
product in non_raid_disks or \ or product in non_raid_disks
description == 'SCSI Enclosure' or \ or "virtual" in product.lower()
'volume' in description.lower(): or "logical" in product.lower()
or "volume" in description.lower()
or "dvd-ram" in description.lower()
or description == "SCSI Enclosure"
or (size is None and logicalname is None)
):
return True return True
return False return False
def get_hw_disks(self): def get_hw_disks(self):
disks = [] disks = []
for raid_card in self.get_raid_cards(filter_cards=True):
disks.extend(raid_card.get_physical_disks())
raid_devices = [
d.get("custom_fields", {}).get("vd_device")
for d in disks
if d.get("custom_fields", {}).get("vd_device")
]
for disk in self.lshw.get_hw_linux("storage"): for disk in self.lshw.get_hw_linux("storage"):
if self.is_virtual_disk(disk): if self.is_virtual_disk(disk, raid_devices):
continue continue
size = round(int(disk.get("size", 0)) / 1073741824, 1)
logicalname = disk.get('logicalname') d = {
description = disk.get('description') "name": "",
size = disk.get('size', 0) "Size": "{} GB".format(size),
product = disk.get('product') "logicalname": disk.get("logicalname"),
serial = disk.get('serial') "description": disk.get("description"),
"SN": disk.get("serial"),
d = {} "Model": disk.get("product"),
d["name"] = "" "Type": disk.get("type"),
d['Size'] = '{} GB'.format(int(size/1024/1024/1024)) }
d['logicalname'] = logicalname if disk.get("vendor"):
d['description'] = description d["Vendor"] = disk["vendor"]
d['SN'] = serial
d['Model'] = product
if disk.get('vendor'):
d['Vendor'] = disk['vendor']
else: else:
d['Vendor'] = get_vendor(disk['product']) d["Vendor"] = get_vendor(disk["product"])
disks.append(d) disks.append(d)
for raid_card in self.get_raid_cards():
disks += raid_card.get_physical_disks()
# remove duplicate serials # remove duplicate serials
seen = set() seen = set()
uniq = [x for x in disks if x['SN'] not in seen and not seen.add(x['SN'])] uniq = [x for x in disks if x["SN"] not in seen and not seen.add(x["SN"])]
return uniq return uniq
def create_netbox_disk(self, disk): def create_netbox_disk(self, disk):
@ -345,115 +380,184 @@ class Inventory():
if "Vendor" in disk: if "Vendor" in disk:
manufacturer = self.find_or_create_manufacturer(disk["Vendor"]) manufacturer = self.find_or_create_manufacturer(disk["Vendor"])
logicalname = disk.get('logicalname') name = "{} ({})".format(disk["Model"], disk["Size"])
desc = disk.get('description') description = disk["Type"]
# nonraid disk sn = disk.get("SN", "unknown")
if logicalname and desc:
if type(logicalname) is list:
logicalname = logicalname[0]
name = '{} - {} ({})'.format(
desc,
logicalname,
disk.get('Size', 0))
description = 'Device {}'.format(disk.get('logicalname', 'Unknown'))
else:
name = '{} ({})'.format(disk['Model'], disk['Size'])
description = '{}'.format(disk['Type'])
_ = nb.dcim.inventory_items.create( parms = {
device=self.device_id, "device": self.device_id,
discovered=True, "discovered": True,
tags=[INVENTORY_TAG['disk']['name']], "tags": [{"name": INVENTORY_TAG["disk"]["name"]}],
name=name, "name": name,
serial=disk['SN'], "serial": sn,
part_id=disk['Model'], "part_id": disk["Model"],
description=description, "description": description,
manufacturer=manufacturer.id if manufacturer else None "manufacturer": getattr(manufacturer, "id", None),
}
if config.process_virtual_drives:
parms["custom_fields"] = disk.get("custom_fields", {})
_ = nb.dcim.inventory_items.create(**parms)
logging.info(
"Creating Disk {model} {serial}".format(
model=disk["Model"],
serial=sn,
)
) )
logging.info('Creating Disk {model} {serial}'.format( def dump_disks_map(self, disks):
model=disk['Model'], disk_map = [d["custom_fields"] for d in disks if "custom_fields" in d]
serial=disk['SN'], if config.dump_disks_map == "-":
)) f = sys.stdout
else:
f = open(config.dump_disks_map, "w")
f.write(json.dumps(disk_map, separators=(",", ":"), indent=4, sort_keys=True))
if config.dump_disks_map != "-":
f.close()
def do_netbox_disks(self): def do_netbox_disks(self):
nb_disks = self.get_netbox_inventory( nb_disks = self.get_netbox_inventory(
device_id=self.device_id, device_id=self.device_id, tag=INVENTORY_TAG["disk"]["slug"]
tag=INVENTORY_TAG['disk']['slug']) )
disks = self.get_hw_disks() disks = self.get_hw_disks()
if config.dump_disks_map:
try:
self.dump_disks_map(disks)
except Exception as e:
logging.error("Failed to dump disks map: {}".format(e))
logging.debug(traceback.format_exc())
disk_serials = [d["SN"] for d in disks if "SN" in d]
# delete disks that are in netbox but not locally # delete disks that are in netbox but not locally
# use the serial_number has the comparison element # use the serial_number has the comparison element
for nb_disk in nb_disks: for nb_disk in nb_disks:
if nb_disk.serial not in [x['SN'] for x in disks if x.get('SN')]: if nb_disk.serial not in disk_serials or config.force_disk_refresh:
logging.info('Deleting unknown locally Disk {serial}'.format( logging.info(
"Deleting unknown locally Disk {serial}".format(
serial=nb_disk.serial, serial=nb_disk.serial,
)) )
)
nb_disk.delete() nb_disk.delete()
if config.force_disk_refresh:
nb_disks = self.get_netbox_inventory(
device_id=self.device_id, tag=INVENTORY_TAG["disk"]["slug"]
)
# create disks that are not in netbox # create disks that are not in netbox
for disk in disks: for disk in disks:
if disk.get('SN') not in [x.serial for x in nb_disks]: if disk.get("SN") not in [d.serial for d in nb_disks]:
self.create_netbox_disk(disk) self.create_netbox_disk(disk)
def create_netbox_memory(self, memory): def create_netbox_memory(self, memory):
manufacturer = self.find_or_create_manufacturer(memory['vendor']) manufacturer = self.find_or_create_manufacturer(memory["vendor"])
name = 'Slot {} ({}GB)'.format(memory['slot'], memory['size']) name = "Slot {} ({}GB)".format(memory["slot"], memory["size"])
nb_memory = nb.dcim.inventory_items.create( nb_memory = nb.dcim.inventory_items.create(
device=self.device_id, device=self.device_id,
discovered=True, discovered=True,
manufacturer=manufacturer.id, manufacturer=manufacturer.id,
tags=[INVENTORY_TAG['memory']['name']], tags=[{"name": INVENTORY_TAG["memory"]["name"]}],
name=name, name=name,
part_id=memory['product'], part_id=memory["product"],
serial=memory['serial'], serial=memory["serial"],
description=memory['description'], description=memory["description"],
) )
logging.info('Creating Memory {location} {type} {size}GB'.format( logging.info(
location=memory['slot'], "Creating Memory {location} {type} {size}GB".format(
type=memory['product'], location=memory["slot"],
size=memory['size'], type=memory["product"],
)) size=memory["size"],
)
)
return nb_memory return nb_memory
def do_netbox_memories(self): def do_netbox_memories(self):
memories = self.lshw.memories memories = self.lshw.memories
nb_memories = self.get_netbox_inventory( nb_memories = self.get_netbox_inventory(
device_id=self.device_id, device_id=self.device_id, tag=INVENTORY_TAG["memory"]["slug"]
tag=INVENTORY_TAG['memory']['slug']
) )
for nb_memory in nb_memories: for nb_memory in nb_memories:
if nb_memory.serial not in [x['serial'] for x in memories]: if nb_memory.serial not in [x["serial"] for x in memories]:
logging.info('Deleting unknown locally Memory {serial}'.format( logging.info(
"Deleting unknown locally Memory {serial}".format(
serial=nb_memory.serial, serial=nb_memory.serial,
)) )
)
nb_memory.delete() nb_memory.delete()
for memory in memories: for memory in memories:
if memory.get('serial') not in [x.serial for x in nb_memories]: if memory.get("serial") not in [x.serial for x in nb_memories]:
self.create_netbox_memory(memory) self.create_netbox_memory(memory)
def create(self): def create_netbox_gpus(self, gpus):
if config.inventory is None: for gpu in gpus:
return False if "product" in gpu and len(gpu["product"]) > 50:
self.do_netbox_cpus() gpu["product"] = gpu["product"][:48] + ".."
self.do_netbox_memories()
self.do_netbox_raid_cards()
self.do_netbox_disks()
self.do_netbox_interfaces()
self.do_netbox_motherboard()
return True
def update(self): manufacturer = self.find_or_create_manufacturer(gpu["vendor"])
_ = nb.dcim.inventory_items.create(
device=self.device_id,
manufacturer=manufacturer.id,
discovered=True,
tags=[{"name": INVENTORY_TAG["gpu"]["name"]}],
name=gpu["product"],
description=gpu["description"],
)
logging.info("Creating GPU model {}".format(gpu["product"]))
def is_external_gpu(self, gpu):
is_3d_gpu = gpu["description"].startswith("3D")
return (
self.server.is_blade()
and self.server.own_gpu_expansion_slot()
and is_3d_gpu
)
def do_netbox_gpus(self):
gpus = []
gpu_models = {}
for gpu in self.lshw.get_hw_linux("gpu"):
# Filters GPU if an expansion bay is detected:
# The internal (VGA) GPU only goes into the blade inventory,
# the external (3D) GPU goes into the expansion blade.
if (
config.expansion_as_device
and self.update_expansion ^ self.is_external_gpu(gpu)
):
continue
gpus.append(gpu)
gpu_models.setdefault(gpu["product"], 0)
gpu_models[gpu["product"]] += 1
nb_gpus = self.get_netbox_inventory(
device_id=self.device_id,
tag=INVENTORY_TAG["gpu"]["slug"],
)
nb_gpu_models = {}
for gpu in nb_gpus:
nb_gpu_models.setdefault(str(gpu), 0)
nb_gpu_models[str(gpu)] += 1
up_to_date = set(gpu_models) == set(nb_gpu_models)
if not gpus or not up_to_date:
for x in nb_gpus:
x.delete()
if gpus and not up_to_date:
self.create_netbox_gpus(gpus)
def create_or_update(self):
if config.inventory is None or config.update_inventory is None: if config.inventory is None or config.update_inventory is None:
return False return False
if self.update_expansion is False:
self.do_netbox_cpus() self.do_netbox_cpus()
self.do_netbox_memories() self.do_netbox_memories()
self.do_netbox_raid_cards()
self.do_netbox_disks()
self.do_netbox_interfaces() self.do_netbox_interfaces()
self.do_netbox_motherboard() self.do_netbox_motherboard()
self.do_netbox_gpus()
self.do_netbox_disks()
self.do_netbox_raid_cards()
return True return True

View file

@ -1,8 +1,10 @@
import logging import logging
import subprocess import subprocess
from netaddr import IPNetwork
class IPMI():
class IPMI:
""" """
Parse IPMI output Parse IPMI output
ie: ie:
@ -33,17 +35,43 @@ class IPMI():
: O=OEM : O=OEM
Bad Password Threshold : Not Available Bad Password Threshold : Not Available
""" """
def __init__(self): def __init__(self):
self.ret, self.output = subprocess.getstatusoutput('ipmitool lan print') self.ret, self.output = subprocess.getstatusoutput("ipmitool lan print")
if self.ret != 0: if self.ret != 0:
logging.error('Cannot get ipmi info: {}'.format(self.output)) logging.error("Cannot get ipmi info: {}".format(self.output))
def parse(self): def parse(self):
ret = {} _ipmi = {}
if self.ret != 0: if self.ret != 0:
return ret return _ipmi
for line in self.output.splitlines(): for line in self.output.splitlines():
key = line.split(':')[0].strip() key = line.split(":")[0].strip()
value = ':'.join(line.split(':')[1:]).strip() if key not in [
ret[key] = value "802.1q VLAN ID",
"IP Address",
"Subnet Mask",
"MAC Address",
]:
continue
value = ":".join(line.split(":")[1:]).strip()
_ipmi[key] = value
ret = {}
ret["name"] = "IPMI"
ret["mtu"] = 1500
ret["bonding"] = False
ret["mac"] = _ipmi["MAC Address"]
ret["vlan"] = (
int(_ipmi["802.1q VLAN ID"])
if _ipmi["802.1q VLAN ID"] != "Disabled"
else None
)
ip = _ipmi["IP Address"]
netmask = _ipmi["Subnet Mask"]
address = str(IPNetwork("{}/{}".format(ip, netmask)))
ret["ip"] = [address]
ret["ipmi"] = True
return ret return ret

View file

@ -1,9 +1,17 @@
import logging
import subprocess import subprocess
from netbox_agent.misc import is_tool
class LLDP():
def __init__(self): class LLDP:
self.output = subprocess.getoutput('lldpctl -f keyvalue') def __init__(self, output=None):
if not is_tool("lldpctl"):
logging.debug("lldpd package seems to be missing or daemon not running.")
if output:
self.output = output
else:
self.output = subprocess.getoutput("lldpctl -f keyvalue")
self.data = self.parse() self.data = self.parse()
def parse(self): def parse(self):
@ -11,7 +19,7 @@ class LLDP():
vlans = {} vlans = {}
vid = None vid = None
for entry in self.output.splitlines(): for entry in self.output.splitlines():
if '=' not in entry: if "=" not in entry:
continue continue
path, value = entry.strip().split("=", 1) path, value = entry.strip().split("=", 1)
split_path = path.split(".") split_path = path.split(".")
@ -23,36 +31,41 @@ class LLDP():
vlans[interface] = {} vlans[interface] = {}
for path_component in path_components: for path_component in path_components:
current_dict[path_component] = current_dict.get(path_component, {}) if not isinstance(current_dict.get(path_component), dict):
current_dict = current_dict[path_component] current_dict[path_component] = {}
if 'vlan-id' in path: current_dict = current_dict.get(path_component)
if "vlan-id" in path:
vid = value vid = value
vlans[interface][value] = vlans[interface].get(vid, {}) vlans[interface][value] = vlans[interface].get(vid, {})
elif path.endswith('vlan'): elif path.endswith("vlan"):
vid = value.replace('vlan-', '') vid = value.replace("vlan-", "")
vlans[interface][vid] = vlans[interface].get(vid, {}) vlans[interface][vid] = vlans[interface].get(vid, {})
elif 'pvid' in path: elif "pvid" in path:
vlans[interface][vid]['pvid'] = True vlans[interface][vid]["pvid"] = True
if 'vlan' not in path: if "vlan" not in path:
current_dict[final] = value current_dict[final] = value
for interface, vlan in vlans.items(): for interface, vlan in vlans.items():
output_dict['lldp'][interface]['vlan'] = vlan output_dict["lldp"][interface]["vlan"] = vlan
if not output_dict:
logging.debug("No LLDP output, please check your network config.")
return output_dict return output_dict
def get_switch_ip(self, interface): def get_switch_ip(self, interface):
# lldp.eth0.chassis.mgmt-ip=100.66.7.222 # lldp.eth0.chassis.mgmt-ip=100.66.7.222
if self.data['lldp'].get(interface) is None: if self.data["lldp"].get(interface) is None:
return None return None
return self.data['lldp'][interface]['chassis']['mgmt-ip'] return self.data["lldp"][interface]["chassis"].get("mgmt-ip")
def get_switch_port(self, interface): def get_switch_port(self, interface):
# lldp.eth0.port.descr=GigabitEthernet1/0/1 # lldp.eth0.port.descr=GigabitEthernet1/0/1
if self.data['lldp'].get(interface) is None: if self.data["lldp"].get(interface) is None:
return None return None
return self.data['lldp'][interface]['port']['descr'] if self.data["lldp"][interface]["port"].get("ifname"):
return self.data["lldp"][interface]["port"]["ifname"]
return self.data["lldp"][interface]["port"]["descr"]
def get_switch_vlan(self, interface): def get_switch_vlan(self, interface):
# lldp.eth0.vlan.vlan-id=296 # lldp.eth0.vlan.vlan-id=296
if self.data['lldp'].get(interface) is None: if self.data["lldp"].get(interface) is None:
return None return None
return self.data['lldp'][interface]['vlan'] return self.data["lldp"][interface]["vlan"]

View file

@ -4,7 +4,7 @@ import importlib.machinery
from netbox_agent.config import config from netbox_agent.config import config
class LocationBase(): class LocationBase:
""" """
This class is used to guess the location in order to push the information This class is used to guess the location in order to push the information
in Netbox for a `Device` in Netbox for a `Device`
@ -17,6 +17,7 @@ class LocationBase():
There's also a support for an external driver file outside of this project in case There's also a support for an external driver file outside of this project in case
the logic isn't supported here. the logic isn't supported here.
""" """
def __init__(self, driver, driver_value, driver_file, regex, *args, **kwargs): def __init__(self, driver, driver_value, driver_file, regex, *args, **kwargs):
self.driver = driver self.driver = driver
self.driver_value = driver_value self.driver_value = driver_value
@ -26,15 +27,19 @@ class LocationBase():
if self.driver_file: if self.driver_file:
try: try:
# FIXME: Works with Python 3.3+, support older version? # FIXME: Works with Python 3.3+, support older version?
loader = importlib.machinery.SourceFileLoader('driver_file', self.driver_file) loader = importlib.machinery.SourceFileLoader(
"driver_file", self.driver_file
)
self.driver = loader.load_module() self.driver = loader.load_module()
except ImportError: except ImportError:
raise ImportError("Couldn't import {} as a module".format(self.driver_file)) raise ImportError(
"Couldn't import {} as a module".format(self.driver_file)
)
else: else:
if self.driver: if self.driver:
try: try:
self.driver = importlib.import_module( self.driver = importlib.import_module(
'netbox_agent.drivers.{}'.format(self.driver) "netbox_agent.drivers.{}".format(self.driver)
) )
except ImportError: except ImportError:
raise ImportError("Driver {} doesn't exists".format(self.driver)) raise ImportError("Driver {} doesn't exists".format(self.driver))
@ -42,19 +47,40 @@ class LocationBase():
def get(self): def get(self):
if self.driver is None: if self.driver is None:
return None return None
if not hasattr(self.driver, 'get'): if not hasattr(self.driver, "get"):
raise Exception( raise Exception(
"Your driver {} doesn't have a get() function, please fix it".format(self.driver) "Your driver {} doesn't have a get() function, please fix it".format(
self.driver
) )
return getattr(self.driver, 'get')(self.driver_value, self.regex) )
return getattr(self.driver, "get")(self.driver_value, self.regex)
class Tenant(LocationBase):
def __init__(self):
driver = config.tenant.driver.split(":")[0] if config.tenant.driver else None
driver_value = (
":".join(config.tenant.driver.split(":")[1:])
if config.tenant.driver
else None
)
driver_file = config.tenant.driver_file
regex = config.tenant.regex
super().__init__(driver, driver_value, driver_file, regex)
class Datacenter(LocationBase): class Datacenter(LocationBase):
def __init__(self): def __init__(self):
driver = config.datacenter_location.driver.split(':')[0] if \ driver = (
config.datacenter_location.driver else None config.datacenter_location.driver.split(":")[0]
driver_value = ':'.join(config.datacenter_location.driver.split(':')[1:]) if \ if config.datacenter_location.driver
config.datacenter_location.driver else None else None
)
driver_value = (
":".join(config.datacenter_location.driver.split(":")[1:])
if config.datacenter_location.driver
else None
)
driver_file = config.datacenter_location.driver_file driver_file = config.datacenter_location.driver_file
regex = config.datacenter_location.regex regex = config.datacenter_location.regex
super().__init__(driver, driver_value, driver_file, regex) super().__init__(driver, driver_value, driver_file, regex)
@ -62,10 +88,16 @@ class Datacenter(LocationBase):
class Rack(LocationBase): class Rack(LocationBase):
def __init__(self): def __init__(self):
driver = config.rack_location.driver.split(':')[0] if \ driver = (
config.rack_location.driver else None config.rack_location.driver.split(":")[0]
driver_value = ':'.join(config.rack_location.driver.split(':')[1:]) if \ if config.rack_location.driver
config.rack_location.driver else None else None
)
driver_value = (
":".join(config.rack_location.driver.split(":")[1:])
if config.rack_location.driver
else None
)
driver_file = config.rack_location.driver_file driver_file = config.rack_location.driver_file
regex = config.rack_location.regex regex = config.rack_location.regex
super().__init__(driver, driver_value, driver_file, regex) super().__init__(driver, driver_value, driver_file, regex)
@ -73,10 +105,16 @@ class Rack(LocationBase):
class Slot(LocationBase): class Slot(LocationBase):
def __init__(self): def __init__(self):
driver = config.slot_location.driver.split(':')[0] if \ driver = (
config.slot_location.driver else None config.slot_location.driver.split(":")[0]
driver_value = ':'.join(config.slot_location.driver.split(':')[1:]) if \ if config.slot_location.driver
config.slot_location.driver else None else None
)
driver_value = (
":".join(config.slot_location.driver.split(":")[1:])
if config.slot_location.driver
else None
)
driver_file = config.slot_location.driver_file driver_file = config.slot_location.driver_file
regex = config.slot_location.regex regex = config.slot_location.regex
super().__init__(driver, driver_value, driver_file, regex) super().__init__(driver, driver_value, driver_file, regex)

View file

@ -2,10 +2,8 @@ import logging
from netbox_agent.config import config from netbox_agent.config import config
logger = logging.getLogger() logger = logging.getLogger()
if config.log_level.lower() == "debug":
if config.log_level == 'debug':
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
else: else:
logger.setLevel(logging.INFO) logger.setLevel(logging.INFO)

View file

@ -1,27 +1,32 @@
import subprocess
import json import json
import logging import logging
import subprocess
import sys import sys
from netbox_agent.misc import is_tool from netbox_agent.misc import is_tool
class LSHW(): class LSHW:
def __init__(self): def __init__(self):
if not is_tool('lshw'): if not is_tool("lshw"):
logging.error('lshw does not seem to be installed') logging.error("lshw does not seem to be installed")
sys.exit(1) sys.exit(1)
data = subprocess.getoutput( data = subprocess.getoutput("lshw -quiet -json")
'lshw -quiet -json' json_data = json.loads(data)
) # Starting from version 02.18, `lshw -json` wraps its result in a list
self.hw_info = json.loads(data) # rather than returning directly a dictionary
if isinstance(json_data, list):
self.hw_info = json_data[0]
else:
self.hw_info = json_data
self.info = {} self.info = {}
self.memories = [] self.memories = []
self.interfaces = [] self.interfaces = []
self.cpus = [] self.cpus = []
self.power = [] self.power = []
self.disks = [] self.disks = []
self.gpus = []
self.vendor = self.hw_info["vendor"] self.vendor = self.hw_info["vendor"]
self.product = self.hw_info["product"] self.product = self.hw_info["product"]
self.chassis_serial = self.hw_info["serial"] self.chassis_serial = self.hw_info["serial"]
@ -53,63 +58,93 @@ class LSHW():
def get_hw_linux(self, hwclass): def get_hw_linux(self, hwclass):
if hwclass == "cpu": if hwclass == "cpu":
return self.cpus return self.cpus
if hwclass == "gpu":
return self.gpus
if hwclass == "network": if hwclass == "network":
return self.interfaces return self.interfaces
if hwclass == 'storage': if hwclass == "storage":
return self.disks return self.disks
if hwclass == 'memory': if hwclass == "memory":
return self.memories return self.memories
def find_network(self, obj): def find_network(self, obj):
d = {} # Some interfaces do not have device (logical) name (eth0, for
d["name"] = obj["logicalname"] # instance), such as not connected network mezzanine cards in blade
d["macaddress"] = obj["serial"] # servers. In such situations, the card will be named `unknown[0-9]`.
d["serial"] = obj["serial"] unkn_intfs = []
d["product"] = obj["product"] for i in self.interfaces:
d["vendor"] = obj["vendor"] # newer versions of lshw can return a list of names, see issue #227
d["description"] = obj["description"] if not isinstance(i["name"], list):
if i["name"].startswith("unknown"):
unkn_intfs.append(i)
else:
for j in i["name"]:
if j.startswith("unknown"):
unkn_intfs.append(j)
self.interfaces.append(d) unkn_name = "unknown{}".format(len(unkn_intfs))
self.interfaces.append(
{
"name": obj.get("logicalname", unkn_name),
"macaddress": obj.get("serial", ""),
"serial": obj.get("serial", ""),
"product": obj.get("product", "Unknown NIC"),
"vendor": obj.get("vendor", "Unknown"),
"description": obj.get("description", ""),
}
)
def find_storage(self, obj): def find_storage(self, obj):
if "children" in obj: if "children" in obj:
for device in obj["children"]: for device in obj["children"]:
d = {} self.disks.append(
d["logicalname"] = device.get("logicalname") {
d["product"] = device.get("product") "logicalname": device.get("logicalname"),
d["serial"] = device.get("serial") "product": device.get("product"),
d["version"] = device.get("version") "serial": device.get("serial"),
d["size"] = device.get("size") "version": device.get("version"),
d["description"] = device.get("description") "size": device.get("size"),
"description": device.get("description"),
self.disks.append(d) "type": device.get("description"),
}
)
elif "nvme" in obj["configuration"]["driver"]: elif "nvme" in obj["configuration"]["driver"]:
if not is_tool("nvme"):
logging.error("nvme-cli >= 1.0 does not seem to be installed")
return
try:
nvme = json.loads( nvme = json.loads(
subprocess.check_output(["nvme", '-list', '-o', 'json'], subprocess.check_output(
encoding='utf8')) # noqa: E128 ["nvme", "-list", "-o", "json"], encoding="utf8"
)
d = {} )
d["vendor"] = obj["vendor"] for device in nvme["Devices"]:
d["version"] = obj["version"] d = {
d["product"] = obj["product"] "logicalname": device["DevicePath"],
"product": device["ModelNumber"],
d['description'] = "NVME Disk" "serial": device["SerialNumber"],
d['product'] = nvme["Devices"][0]["ModelNumber"] "version": device["Firmware"],
d['size'] = nvme["Devices"][0]["PhysicalSize"] "description": "NVME",
d['serial'] = nvme["Devices"][0]["SerialNumber"] "type": "NVME",
d['logicalname'] = nvme["Devices"][0]["DevicePath"] }
if "UsedSize" in device:
d["size"] = device["UsedSize"]
if "UsedBytes" in device:
d["size"] = device["UsedBytes"]
self.disks.append(d) self.disks.append(d)
except Exception:
pass
def find_cpus(self, obj): def find_cpus(self, obj):
c = {} if "product" in obj:
c["product"] = obj["product"] self.cpus.append(
c["vendor"] = obj["vendor"] {
c["description"] = obj["description"] "product": obj.get("product", "Unknown CPU"),
c["location"] = obj["slot"] "vendor": obj.get("vendor", "Unknown vendor"),
"description": obj.get("description", ""),
self.cpus.append(c) "location": obj.get("slot", ""),
}
)
def find_memories(self, obj): def find_memories(self, obj):
if "children" not in obj: if "children" not in obj:
@ -120,16 +155,26 @@ class LSHW():
if "empty" in dimm["description"]: if "empty" in dimm["description"]:
continue continue
d = {} self.memories.append(
d["slot"] = dimm.get("slot") {
d["description"] = dimm.get("description") "slot": dimm.get("slot"),
d["id"] = dimm.get("id") "description": dimm.get("description"),
d["serial"] = dimm.get("serial", 'N/A') "id": dimm.get("id"),
d["vendor"] = dimm.get("vendor", 'N/A') "serial": dimm.get("serial", "N/A"),
d["product"] = dimm.get("product") "vendor": dimm.get("vendor", "N/A"),
d["size"] = dimm.get("size", 0) / 2 ** 20 / 1024 "product": dimm.get("product", "N/A"),
"size": dimm.get("size", 0) / 2**20 / 1024,
}
)
self.memories.append(d) def find_gpus(self, obj):
if "product" in obj:
infos = {
"product": obj.get("product", "Unknown GPU"),
"vendor": obj.get("vendor", "Unknown"),
"description": obj.get("description", ""),
}
self.gpus.append(infos)
def walk_bridge(self, obj): def walk_bridge(self, obj):
if "children" not in obj: if "children" not in obj:
@ -138,6 +183,8 @@ class LSHW():
for bus in obj["children"]: for bus in obj["children"]:
if bus["class"] == "storage": if bus["class"] == "storage":
self.find_storage(bus) self.find_storage(bus)
if bus["class"] == "display":
self.find_gpus(bus)
if "children" in bus: if "children" in bus:
for b in bus["children"]: for b in bus["children"]:
@ -145,6 +192,8 @@ class LSHW():
self.find_storage(b) self.find_storage(b)
if b["class"] == "network": if b["class"] == "network":
self.find_network(b) self.find_network(b)
if b["class"] == "display":
self.find_gpus(b)
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -1,31 +1,113 @@
import re
import socket
import subprocess
from shutil import which from shutil import which
from slugify import slugify
from netbox_agent.config import netbox_instance as nb
def is_tool(name): def is_tool(name):
'''Check whether `name` is on PATH and marked as executable.''' """Check whether `name` is on PATH and marked as executable."""
return which(name) is not None return which(name) is not None
def get_device_role(role):
device_role = nb.dcim.device_roles.get(name=role)
if device_role is None:
raise Exception('DeviceRole "{}" does not exist, please create it'.format(role))
return device_role
def get_device_type(type):
device_type = nb.dcim.device_types.get(model=type)
if device_type is None:
raise Exception('DeviceType "{}" does not exist, please create it'.format(type))
return device_type
def get_device_platform(device_platform):
if device_platform is None:
try:
# Python 3.8+ moved linux_distribution() to distro
try:
import distro
linux_distribution = " ".join(distro.linux_distribution())
except ImportError:
import platform
linux_distribution = " ".join(platform.linux_distribution())
if not linux_distribution:
return None
except (ModuleNotFoundError, NameError, AttributeError):
return None
else:
linux_distribution = device_platform
device_platform = nb.dcim.platforms.get(name=linux_distribution)
if device_platform is None:
device_platform = nb.dcim.platforms.create(
name=linux_distribution, slug=slugify(linux_distribution)
)
return device_platform
def get_vendor(name): def get_vendor(name):
vendors = { vendors = {
'PERC': 'Dell', "PERC": "Dell",
'SANDISK': 'SanDisk', "SANDISK": "SanDisk",
'DELL': 'Dell', "DELL": "Dell",
'ST': 'Seagate', "ST": "Seagate",
'CRUCIAL': 'Crucial', "CRUCIAL": "Crucial",
'MICRON': 'Micron', "MICRON": "Micron",
'INTEL': 'Intel', "INTEL": "Intel",
'SAMSUNG': 'Samsung', "SAMSUNG": "Samsung",
'EH0': 'HP', "EH0": "HP",
'HGST': 'HGST', "HGST": "HGST",
'HUH': 'HGST', "HUH": "HGST",
'MB': 'Toshiba', "MB": "Toshiba",
'MC': 'Toshiba', "MC": "Toshiba",
'MD': 'Toshiba', "MD": "Toshiba",
'MG': 'Toshiba', "MG": "Toshiba",
'WD': 'WDC' "WD": "WDC",
} }
for key, value in vendors.items(): for key, value in vendors.items():
if name.upper().startswith(key): if name.upper().startswith(key):
return value return value
return name return name
def get_hostname(config):
if config.hostname_cmd is None:
return "{}".format(socket.gethostname())
return subprocess.getoutput(config.hostname_cmd)
def create_netbox_tags(tags):
ret = []
for tag in tags:
nb_tag = nb.extras.tags.get(name=tag)
if not nb_tag:
nb_tag = nb.extras.tags.create(
name=tag,
slug=slugify(tag),
)
ret.append(nb_tag)
return ret
def get_mount_points():
mount_points = {}
output = subprocess.getoutput("mount")
for r in output.split("\n"):
if not r.startswith("/dev/"):
continue
mount_info = r.split()
device = mount_info[0]
device = re.sub(r"\d+$", "", device)
mp = mount_info[2]
mount_points.setdefault(device, []).append(mp)
return mount_points

File diff suppressed because it is too large Load diff

View file

@ -1,58 +1,68 @@
import logging import logging
import netbox_agent.dmidecode as dmidecode
from netbox_agent.config import netbox_instance as nb from netbox_agent.config import netbox_instance as nb
PSU_DMI_TYPE = 39 PSU_DMI_TYPE = 39
class PowerSupply(): class PowerSupply:
def __init__(self, server=None): def __init__(self, server=None):
self.server = server self.server = server
self.netbox_server = self.server.get_netbox_server() self.netbox_server = self.server.get_netbox_server()
if self.server.is_blade(): if self.server.is_blade():
self.device_id = self.netbox_server.parent_device.id if self.netbox_server else None self.device_id = (
self.netbox_server.parent_device.id if self.netbox_server else None
)
else: else:
self.device_id = self.netbox_server.id if self.netbox_server else None self.device_id = self.netbox_server.id if self.netbox_server else None
def get_power_supply(self): def get_power_supply(self):
power_supply = [] power_supply = []
for psu in self.server.dmi.get_by_type(PSU_DMI_TYPE): for psu in dmidecode.get_by_type(self.server.dmi, PSU_DMI_TYPE):
if 'Present' not in psu['Status'] or psu['Status'] == 'Not Present': if "Present" not in psu["Status"] or psu["Status"] == "Not Present":
continue continue
try: try:
max_power = int(psu.get('Max Power Capacity').split()[0]) max_power = int(psu.get("Max Power Capacity").split()[0])
except ValueError: except ValueError:
max_power = None max_power = None
desc = '{} - {}'.format( desc = "{} - {}".format(
psu.get('Manufacturer', 'No Manufacturer').strip(), psu.get("Manufacturer", "No Manufacturer").strip(),
psu.get('Name', 'No name').strip(), psu.get("Name", "No name").strip(),
)
sn = psu.get("Serial Number", "").strip()
# Let's assume that if no serial and no power reported we skip it
if sn == "" and max_power is None:
continue
if sn == "":
sn = "N/A"
power_supply.append(
{
"name": sn,
"description": desc,
"allocated_draw": None,
"maximum_draw": max_power,
"device": self.device_id,
}
) )
power_supply.append({
'name': psu.get('Serial Number', 'No S/N').strip(),
'description': desc,
'allocated_draw': None,
'maximum_draw': max_power,
'device': self.device_id,
})
return power_supply return power_supply
def get_netbox_power_supply(self): def get_netbox_power_supply(self):
return nb.dcim.power_ports.filter( return nb.dcim.power_ports.filter(device_id=self.device_id)
device_id=self.device_id
)
def create_or_update_power_supply(self): def create_or_update_power_supply(self):
nb_psus = self.get_netbox_power_supply() nb_psus = list(self.get_netbox_power_supply())
psus = self.get_power_supply() psus = self.get_power_supply()
# Delete unknown PSU # Delete unknown PSU
delete = False delete = False
for nb_psu in nb_psus: for nb_psu in nb_psus:
if nb_psu.name not in [x['name'] for x in psus]: if nb_psu.name not in [x["name"] for x in psus]:
logging.info('Deleting unknown locally PSU {name}'.format( logging.info(
name=nb_psu.name "Deleting unknown locally PSU {name}".format(name=nb_psu.name)
)) )
nb_psu.delete() nb_psu.delete()
delete = True delete = True
@ -61,27 +71,23 @@ class PowerSupply():
# sync existing Netbox PSU with local infos # sync existing Netbox PSU with local infos
for nb_psu in nb_psus: for nb_psu in nb_psus:
local_psu = next( local_psu = next(item for item in psus if item["name"] == nb_psu.name)
item for item in psus if item['name'] == nb_psu.name
)
update = False update = False
if nb_psu.description != local_psu['description']: if nb_psu.description != local_psu["description"]:
update = True update = True
nb_psu.description = local_psu['description'] nb_psu.description = local_psu["description"]
if nb_psu.maximum_draw != local_psu['maximum_draw']: if nb_psu.maximum_draw != local_psu["maximum_draw"]:
update = True update = True
nb_psu.maximum_draw = local_psu['maximum_draw'] nb_psu.maximum_draw = local_psu["maximum_draw"]
if update: if update:
nb_psu.save() nb_psu.save()
for psu in psus: for psu in psus:
if psu['name'] not in [x.name for x in nb_psus]: if psu["name"] not in [x.name for x in nb_psus]:
logging.info('Creating PSU {name} ({description}), {maximum_draw}W'.format( logging.info(
**psu "Creating PSU {name} ({description}), {maximum_draw}W".format(**psu)
))
nb_psu = nb.dcim.power_ports.create(
**psu
) )
nb_psu = nb.dcim.power_ports.create(**psu)
return True return True
@ -89,7 +95,7 @@ class PowerSupply():
try: try:
psu_cons = self.server.get_power_consumption() psu_cons = self.server.get_power_consumption()
except NotImplementedError: except NotImplementedError:
logging.error('Cannot report power consumption for this vendor') logging.error("Cannot report power consumption for this vendor")
return False return False
nb_psus = self.get_netbox_power_supply() nb_psus = self.get_netbox_power_supply()
@ -97,22 +103,27 @@ class PowerSupply():
return False return False
# find power feeds for rack or dc # find power feeds for rack or dc
voltage = None
pwr_feeds = None pwr_feeds = None
if self.netbox_server.rack: if self.netbox_server.rack:
pwr_feeds = nb.dcim.power_feeds.filter( pwr_feeds = nb.dcim.power_feeds.filter(rack=self.netbox_server.rack.id)
rack=self.netbox_server.rack.id
) if pwr_feeds:
if pwr_feeds is None or not len(pwr_feeds): voltage = [p["voltage"] for p in pwr_feeds]
logging.info('Could not find power feeds for Rack, defaulting value to 230') else:
voltage = 230 logging.info("Could not find power feeds for Rack, defaulting value to 230")
voltage = [230 for _ in nb_psus]
for i, nb_psu in enumerate(nb_psus): for i, nb_psu in enumerate(nb_psus):
nb_psu.allocated_draw = float(psu_cons[i]) * voltage nb_psu.allocated_draw = int(float(psu_cons[i]) * voltage[i])
if nb_psu.allocated_draw < 1:
logging.info("PSU is not connected or in standby mode")
continue
nb_psu.save() nb_psu.save()
logging.info('Updated power consumption for PSU {}: {}W'.format( logging.info(
"Updated power consumption for PSU {}: {}W".format(
nb_psu.name, nb_psu.name,
nb_psu.allocated_draw, nb_psu.allocated_draw,
)) )
)
return True return True

View file

@ -1,4 +1,4 @@
class RaidController(): class RaidController:
def get_product_name(self): def get_product_name(self):
raise NotImplementedError raise NotImplementedError
@ -15,7 +15,10 @@ class RaidController():
def get_physical_disks(self): def get_physical_disks(self):
raise NotImplementedError raise NotImplementedError
def is_external(self):
return False
class Raid():
class Raid:
def get_controllers(self): def get_controllers(self):
raise NotImplementedError raise NotImplementedError

View file

@ -1,166 +1,240 @@
import logging
import re import re
import subprocess import subprocess
from netbox_agent.raid.base import Raid, RaidController
from netbox_agent.misc import get_vendor from netbox_agent.misc import get_vendor
from netbox_agent.raid.base import Raid, RaidController
REGEXP_CONTROLLER_HP = re.compile(r'Smart Array ([a-zA-Z0-9- ]+) in Slot ([0-9]+)') REGEXP_CONTROLLER_HP = re.compile(r"Smart Array ([a-zA-Z0-9- ]+) in Slot ([0-9]+)")
def _get_indentation(string): class HPRaidControllerError(Exception):
"""Return the number of spaces before the current line.""" pass
return len(string) - len(string.lstrip(' '))
def _get_key_value(string): def ssacli(sub_command):
"""Return the (key, value) as a tuple from a string.""" command = ["ssacli"]
# Normally all properties look like this: command.extend(sub_command.split())
# Unique Identifier: 600508B1001CE4ACF473EE9C826230FF p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# Disk Name: /dev/sda stdout, stderr = p.communicate()
# Mount Points: None stdout = stdout.decode("utf-8")
key = '' if p.returncode != 0:
value = '' mesg = "Failed to execute command '{}':\n{}".format(" ".join(command), stdout)
try: raise HPRaidControllerError(mesg)
key, value = string.split(':')
except ValueError: if "does not have any physical" in stdout:
# This handles the case when the property of a logical drive return list()
# returned is as follows. Here we cannot split by ':' because
# the disk id has colon in it. So if this is about disk,
# then strip it accordingly.
# Mirror Group 0: physicaldrive 6I:1:5
string = string.lstrip(' ')
if string.startswith('physicaldrive'):
fields = string.split(' ')
key = fields[0]
value = fields[1]
else: else:
# TODO(rameshg87): Check if this ever occurs. lines = stdout.split("\n")
return None, None lines = list(filter(None, lines))
return lines
return key.lstrip(' ').rstrip(' '), value.lstrip(' ').rstrip(' ')
def _get_dict(lines, start_index, indentation): def _test_if_valid_line(line):
"""Recursive function for parsing hpssacli/ssacli output.""" ignore_patterns = [
"Note:",
"Error:",
"is not loaded",
"README",
" failure",
" cache",
]
for pattern in ignore_patterns:
if not line or pattern in line:
return None
return line
info = {}
current_item = None
i = start_index def _parse_ctrl_output(lines):
while i < len(lines): controllers = {}
current_line = lines[i] current_ctrl = None
if current_line.startswith('Note:'):
i = i + 1 for line in lines:
line = line.strip()
line = _test_if_valid_line(line)
if line is None:
continue
ctrl = REGEXP_CONTROLLER_HP.search(line)
if ctrl is not None:
slot = ctrl.group(2)
current_ctrl = "{} - Slot {}".format(ctrl.group(1), slot)
controllers[current_ctrl] = {"Slot": slot}
if "Embedded" not in line:
controllers[current_ctrl]["External"] = True
continue
if ": " not in line:
continue continue
current_line_indentation = _get_indentation(current_line) attr, val = line.split(": ", 1)
if current_line_indentation == indentation: attr = attr.strip()
current_item = current_line.lstrip(' ') val = val.strip()
controllers[current_ctrl][attr] = val
return controllers
info[current_item] = {}
i = i + 1 def _parse_pd_output(lines):
drives = {}
current_array = None
current_drv = None
for line in lines:
line = line.strip()
line = _test_if_valid_line(line)
if line is None:
continue continue
# Parses the Array the drives are in
if line.startswith("Array"):
current_array = line.split(None, 1)[1]
# Detects new physical drive
if line.startswith("physicaldrive"):
current_drv = line.split(None, 1)[1]
drives[current_drv] = {}
if current_array is not None:
drives[current_drv]["Array"] = current_array
continue
if ": " not in line:
continue
attr, val = line.split(": ", 1)
attr = attr.strip()
val = val.strip()
drives.setdefault(current_drv, {})[attr] = val
return drives
if i >= len(lines) - 1:
key, value = _get_key_value(current_line)
# If this is some unparsable information, then
# just skip it.
if key:
info[current_item][key] = value
return info, i
next_line = lines[i + 1] def _parse_ld_output(lines):
next_line_indentation = _get_indentation(next_line) drives = {}
current_array = None
current_drv = None
if current_line_indentation == next_line_indentation: for line in lines:
key, value = _get_key_value(current_line) line = line.strip()
if key: line = _test_if_valid_line(line)
info[current_item][key] = value if line is None:
i = i + 1 continue
elif next_line_indentation > current_line_indentation: # Parses the Array the drives are in
ret_dict, j = _get_dict(lines, i, current_line_indentation) if line.startswith("Array"):
info[current_item].update(ret_dict) current_array = line.split(None, 1)[1]
i = j + 1 drives[current_array] = {}
elif next_line_indentation < current_line_indentation: # Detects new physical drive
key, value = _get_key_value(current_line) if line.startswith("Logical Drive"):
if key: current_drv = line.split(": ", 1)[1]
info[current_item][key] = value drives.setdefault(current_array, {})["LogicalDrive"] = current_drv
return info, i continue
if ": " not in line:
return info, i continue
attr, val = line.split(": ", 1)
drives.setdefault(current_array, {})[attr] = val
return drives
class HPRaidController(RaidController): class HPRaidController(RaidController):
def __init__(self, controller_name, data): def __init__(self, controller_name, data):
self.controller_name = controller_name self.controller_name = controller_name
self.data = data self.data = data
self.pdrives = self._get_physical_disks()
arrays = [d["Array"] for d in self.pdrives.values() if d.get("Array")]
if arrays:
self.ldrives = self._get_logical_drives()
self._get_virtual_drives_map()
def get_product_name(self): def get_product_name(self):
return self.controller_name return self.controller_name
def get_manufacturer(self): def get_manufacturer(self):
return 'HP' return "HP"
def get_serial_number(self): def get_serial_number(self):
return self.data['Serial Number'] return self.data["Serial Number"]
def get_firmware_version(self): def get_firmware_version(self):
return self.data['Firmware Version'] return self.data["Firmware Version"]
def get_physical_disks(self): def is_external(self):
ret = [] return self.data.get("External", False)
output = subprocess.getoutput(
'ssacli ctrl slot={slot} pd all show detail'.format(slot=self.data['Slot'])
)
lines = output.split('\n')
lines = list(filter(None, lines))
j = -1
while j < len(lines):
info_dict, j = _get_dict(lines, j + 1, 0)
key = next(iter(info_dict)) def _get_physical_disks(self):
for array, physical_disk in info_dict[key].items(): lines = ssacli("ctrl slot={} pd all show detail".format(self.data["Slot"]))
for _, pd_attr in physical_disk.items(): pdrives = _parse_pd_output(lines)
model = pd_attr.get('Model', '').strip() ret = {}
for name, attrs in pdrives.items():
array = attrs.get("Array", "")
model = attrs.get("Model", "").strip()
vendor = None vendor = None
if model.startswith('HP'): if model.startswith("HP"):
vendor = 'HP' vendor = "HP"
elif len(model.split()) > 1: elif len(model.split()) > 1:
vendor = get_vendor(model.split()[1]) vendor = get_vendor(model.split()[1])
else: else:
vendor = get_vendor(model) vendor = get_vendor(model)
ret.append({ ret[name] = {
'Model': model, "Array": array,
'Vendor': vendor, "Model": model,
'SN': pd_attr.get('Serial Number', '').strip(), "Vendor": vendor,
'Size': pd_attr.get('Size', '').strip(), "SN": attrs.get("Serial Number", "").strip(),
'Type': 'SSD' if pd_attr.get('Interface Type') == 'Solid State SATA' "Size": attrs.get("Size", "").strip(),
else 'HDD', "Type": (
'_src': self.__class__.__name__, "SSD"
}) if attrs.get("Interface Type") == "Solid State SATA"
else "HDD"
),
"_src": self.__class__.__name__,
"custom_fields": {
"pd_identifier": name,
"mount_point": attrs.get("Mount Points", "").strip(),
"vd_device": attrs.get("Disk Name", "").strip(),
"vd_size": attrs.get("Size", "").strip(),
},
}
return ret return ret
def _get_logical_drives(self):
lines = ssacli("ctrl slot={} ld all show detail".format(self.data["Slot"]))
ldrives = _parse_ld_output(lines)
ret = {}
for array, attrs in ldrives.items():
ret[array] = {
"vd_array": array,
"vd_size": attrs.get("Size", "").strip(),
"vd_consistency": attrs.get("Status", "").strip(),
"vd_raid_type": "RAID {}".format(
attrs.get("Fault Tolerance", "N/A").strip()
),
"vd_device": attrs.get("LogicalDrive", "").strip(),
"mount_point": attrs.get("Mount Points", "").strip(),
}
return ret
def _get_virtual_drives_map(self):
for name, attrs in self.pdrives.items():
array = attrs["Array"]
ld = self.ldrives.get(array)
if ld is None:
logging.error(
"Failed to find array information for physical drive {}."
" Ignoring.".format(name)
)
continue
attrs["custom_fields"].update(ld)
def get_physical_disks(self):
return list(self.pdrives.values())
class HPRaid(Raid): class HPRaid(Raid):
def __init__(self): def __init__(self):
self.output = subprocess.getoutput('ssacli ctrl all show detail') self.output = subprocess.getoutput("ssacli ctrl all show detail")
self.controllers = [] self.controllers = []
self.convert_to_dict() self.convert_to_dict()
def convert_to_dict(self): def convert_to_dict(self):
lines = self.output.split('\n') lines = self.output.split("\n")
lines = list(filter(None, lines)) lines = list(filter(None, lines))
j = -1 controllers = _parse_ctrl_output(lines)
while j < len(lines): for controller, attrs in controllers.items():
info_dict, j = _get_dict(lines, j + 1, 0) self.controllers.append(HPRaidController(controller, attrs))
if len(info_dict.keys()):
_product_name = list(info_dict.keys())[0]
product_name = REGEXP_CONTROLLER_HP.search(_product_name)
if product_name:
self.controllers.append(
HPRaidController(product_name.group(1), info_dict[_product_name])
)
def get_controllers(self): def get_controllers(self):
return self.controllers return self.controllers

View file

@ -1,25 +1,43 @@
import logging
import re import re
import subprocess import subprocess
import xml.etree.ElementTree as ET # NOQA
from netbox_agent.misc import get_vendor from netbox_agent.misc import get_mount_points, get_vendor
from netbox_agent.raid.base import Raid, RaidController from netbox_agent.raid.base import Raid, RaidController
# Inspiration from https://github.com/asciiphil/perc-status/blob/master/perc-status
class OmreportControllerError(Exception):
pass
def get_field(obj, fieldname): def omreport(sub_command):
f = obj.find(fieldname) command = ["omreport"]
if f is None: command.extend(sub_command.split())
return None p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if f.attrib['type'] in ['u32', 'u64']: p.wait()
if re.search('Mask$', fieldname): stdout = p.stdout.read().decode("utf-8")
return int(f.text, 2) if p.returncode != 0:
else: mesg = "Failed to execute command '{}':\n{}".format(" ".join(command), stdout)
return int(f.text) raise OmreportControllerError(mesg)
if f.attrib['type'] == 'astring':
return f.text res = {}
return f.text section_re = re.compile("^[A-Z]")
current_section = None
current_obj = None
for line in stdout.split("\n"):
if ": " in line:
attr, value = line.split(": ", 1)
attr = attr.strip()
value = value.strip()
if attr == "ID":
obj = {}
res.setdefault(current_section, []).append(obj)
current_obj = obj
current_obj[attr] = value
elif section_re.search(line) is not None:
current_section = line.strip()
return res
class OmreportController(RaidController): class OmreportController(RaidController):
@ -28,50 +46,82 @@ class OmreportController(RaidController):
self.controller_index = controller_index self.controller_index = controller_index
def get_product_name(self): def get_product_name(self):
return get_field(self.data, 'Name') return self.data["Name"]
def get_manufacturer(self): def get_manufacturer(self):
return None return None
def get_serial_number(self): def get_serial_number(self):
return get_field(self.data, 'DeviceSerialNumber') return self.data.get("DeviceSerialNumber")
def get_firmware_version(self): def get_firmware_version(self):
return get_field(self.data, 'Firmware Version') return self.data.get("Firmware Version")
def _get_physical_disks(self):
pds = {}
res = omreport("storage pdisk controller={}".format(self.controller_index))
for pdisk in [d for d in list(res.values())[0]]:
disk_id = pdisk["ID"]
size = re.sub("B .*$", "B", pdisk["Capacity"])
pds[disk_id] = {
"Vendor": get_vendor(pdisk["Vendor ID"]),
"Model": pdisk["Product ID"],
"SN": pdisk["Serial No."],
"Size": size,
"Type": pdisk["Media"],
"_src": self.__class__.__name__,
}
return pds
def _get_virtual_drives_map(self):
pds = {}
res = omreport("storage vdisk controller={}".format(self.controller_index))
for vdisk in [d for d in list(res.values())[0]]:
vdisk_id = vdisk["ID"]
device = vdisk["Device Name"]
mount_points = get_mount_points()
mp = mount_points.get(device, "n/a")
size = re.sub("B .*$", "B", vdisk["Size"])
vd = {
"vd_array": vdisk_id,
"vd_size": size,
"vd_consistency": vdisk["State"],
"vd_raid_type": vdisk["Layout"],
"vd_device": vdisk["Device Name"],
"mount_point": ", ".join(sorted(mp)),
}
drives_res = omreport(
"storage pdisk controller={} vdisk={}".format(
self.controller_index, vdisk_id
)
)
for pdisk in [d for d in list(drives_res.values())[0]]:
pds[pdisk["ID"]] = vd
return pds
def get_physical_disks(self): def get_physical_disks(self):
ret = [] pds = self._get_physical_disks()
output = subprocess.getoutput( vds = self._get_virtual_drives_map()
'omreport storage controller controller={} -fmt xml'.format(self.controller_index) for pd_identifier, vd in vds.items():
if pd_identifier not in pds:
logging.error(
"Physical drive {} listed in virtual drive {} not "
"found in drives list".format(pd_identifier, vd["vd_array"])
) )
root = ET.fromstring(output) continue
et_array_disks = root.find('ArrayDisks') pds[pd_identifier].setdefault("custom_fields", {}).update(vd)
if et_array_disks is not None: pds[pd_identifier]["custom_fields"]["pd_identifier"] = pd_identifier
for obj in et_array_disks.findall('DCStorageObject'): return list(pds.values())
ret.append({
'Vendor': get_vendor(get_field(obj, 'Vendor')),
'Model': get_field(obj, 'ProductID'),
'SN': get_field(obj, 'DeviceSerialNumber'),
'Size': '{:.0f}GB'.format(
int(get_field(obj, 'Length')) / 1024 / 1024 / 1024
),
'Type': 'HDD' if int(get_field(obj, 'MediaType')) == 1 else 'SSD',
'_src': self.__class__.__name__,
})
return ret
class OmreportRaid(Raid): class OmreportRaid(Raid):
def __init__(self): def __init__(self):
output = subprocess.getoutput('omreport storage controller -fmt xml')
controller_xml = ET.fromstring(output)
self.controllers = [] self.controllers = []
res = omreport("storage controller")
for obj in controller_xml.find('Controllers').findall('DCStorageObject'): for controller in res["Controller"]:
ctrl_index = get_field(obj, 'ControllerNum') ctrl_index = controller["ID"]
self.controllers.append( self.controllers.append(OmreportController(ctrl_index, controller))
OmreportController(ctrl_index, obj)
)
def get_controllers(self): def get_controllers(self):
return self.controllers return self.controllers

View file

@ -1,73 +1,153 @@
import subprocess
import json import json
import logging
import os
import re
import subprocess
from netbox_agent.misc import get_vendor from netbox_agent.config import config
from netbox_agent.misc import get_mount_points, get_vendor
from netbox_agent.raid.base import Raid, RaidController from netbox_agent.raid.base import Raid, RaidController
class StorcliControllerError(Exception):
pass
def storecli(sub_command):
command = ["storcli"]
command.extend(sub_command.split())
command.append("J")
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, stderr = p.communicate()
if stderr:
mesg = "Failed to execute command '{}':\n{}".format(" ".join(command), stdout)
raise StorcliControllerError(mesg)
stdout = stdout.decode("utf-8")
data = json.loads(stdout)
controllers = dict(
[
(c["Command Status"]["Controller"], c["Response Data"])
for c in data["Controllers"]
if c["Command Status"]["Status"] == "Success"
]
)
if not controllers:
logging.error(
"Failed to execute command '{}'. "
"Ignoring data.".format(" ".join(command))
)
return {}
return controllers
class StorcliController(RaidController): class StorcliController(RaidController):
def __init__(self, controller_index, data): def __init__(self, controller_index, data):
self.data = data self.data = data
self.controller_index = controller_index self.controller_index = controller_index
def get_product_name(self): def get_product_name(self):
return self.data['Product Name'] return self.data["Product Name"]
def get_manufacturer(self): def get_manufacturer(self):
return None return None
def get_serial_number(self): def get_serial_number(self):
return self.data['Serial Number'] return self.data["Serial Number"]
def get_firmware_version(self): def get_firmware_version(self):
return self.data['FW Package Build'] return self.data["FW Package Build"]
def _get_physical_disks(self):
pds = {}
cmd = "/c{}/eall/sall show all".format(self.controller_index)
controllers = storecli(cmd)
pd_info = controllers[self.controller_index]
pd_re = re.compile(r"^Drive (/c\d+/e\d+/s\d+)$")
for section, attrs in pd_info.items():
reg = pd_re.search(section)
if reg is None:
continue
pd_name = reg.group(1)
pd_attr = attrs[0]
pd_identifier = pd_attr["EID:Slt"]
size = pd_attr.get("Size", "").strip()
media_type = pd_attr.get("Med", "").strip()
pd_details = pd_info["{} - Detailed Information".format(section)]
pd_dev_attr = pd_details["{} Device attributes".format(section)]
model = pd_dev_attr.get("Model Number", "").strip()
pd = {
"Model": model,
"Vendor": get_vendor(model),
"SN": pd_dev_attr.get("SN", "").strip(),
"Size": size,
"Type": media_type,
"_src": self.__class__.__name__,
}
if config.process_virtual_drives:
pd.setdefault("custom_fields", {})["pd_identifier"] = pd_name
pds[pd_identifier] = pd
return pds
def _get_virtual_drives_map(self):
vds = {}
cmd = "/c{}/vall show all".format(self.controller_index)
controllers = storecli(cmd)
vd_info = controllers[self.controller_index]
mount_points = get_mount_points()
for vd_identifier, vd_attrs in vd_info.items():
if not vd_identifier.startswith("/c{}/v".format(self.controller_index)):
continue
volume = vd_identifier.split("/")[-1].lstrip("v")
vd_attr = vd_attrs[0]
vd_pd_identifier = "PDs for VD {}".format(volume)
vd_pds = vd_info[vd_pd_identifier]
vd_prop_identifier = "VD{} Properties".format(volume)
vd_properties = vd_info[vd_prop_identifier]
for pd in vd_pds:
pd_identifier = pd["EID:Slt"]
wwn = vd_properties["SCSI NAA Id"]
wwn_path = "/dev/disk/by-id/wwn-0x{}".format(wwn)
device = os.path.realpath(wwn_path)
mp = mount_points.get(device, "n/a")
vds[pd_identifier] = {
"vd_array": vd_identifier,
"vd_size": vd_attr["Size"],
"vd_consistency": vd_attr["Consist"],
"vd_raid_type": vd_attr["TYPE"],
"vd_device": device,
"mount_point": ", ".join(sorted(mp)),
}
return vds
def get_physical_disks(self): def get_physical_disks(self):
ret = [] # Parses physical disks information
output = subprocess.getoutput( pds = self._get_physical_disks()
'storcli /c{}/eall/sall show all J'.format(self.controller_index)
)
drive_infos = json.loads(output)['Controllers'][self.controller_index]['Response Data']
for physical_drive in self.data['PD LIST']: # Parses virtual drives information and maps them to physical disks
enclosure = physical_drive.get('EID:Slt').split(':')[0] vds = self._get_virtual_drives_map()
slot = physical_drive.get('EID:Slt').split(':')[1] for pd_identifier, vd in vds.items():
size = physical_drive.get('Size').strip() if pd_identifier not in pds:
media_type = physical_drive.get('Med').strip() logging.error(
drive_identifier = 'Drive /c{}/e{}/s{}'.format( "Physical drive {} listed in virtual drive {} not "
str(self.controller_index), str(enclosure), str(slot) "found in drives list".format(pd_identifier, vd["vd_array"])
) )
drive_attr = drive_infos['{} - Detailed Information'.format(drive_identifier)][ continue
'{} Device attributes'.format(drive_identifier)] pds[pd_identifier].setdefault("custom_fields", {}).update(vd)
model = drive_attr.get('Model Number', '').strip()
ret.append({ return list(pds.values())
'Model': model,
'Vendor': get_vendor(model),
'SN': drive_attr.get('SN', '').strip(),
'Size': size,
'Type': media_type,
'_src': self.__class__.__name__,
})
return ret
class StorcliRaid(Raid): class StorcliRaid(Raid):
def __init__(self): def __init__(self):
self.output = subprocess.getoutput('storcli /call show J')
self.data = json.loads(self.output)
self.controllers = [] self.controllers = []
controllers = storecli("/call show")
if len([ for controller_id, controller_data in controllers.items():
x for x in self.data['Controllers'] self.controllers.append(StorcliController(controller_id, controller_data))
if x['Command Status']['Status'] == 'Success'
]) > 0:
for controller in self.data['Controllers']:
self.controllers.append(
StorcliController(
controller['Command Status']['Controller'],
controller['Response Data']
)
)
def get_controllers(self): def get_controllers(self):
return self.controllers return self.controllers

View file

@ -1,78 +1,111 @@
import logging import logging
from pprint import pprint
import socket import socket
import subprocess import subprocess
import sys
from pprint import pprint
from netbox_agent.config import netbox_instance as nb, config
import netbox_agent.dmidecode as dmidecode import netbox_agent.dmidecode as dmidecode
from netbox_agent.location import Datacenter, Rack from netbox_agent.config import config
from netbox_agent.config import netbox_instance as nb
from netbox_agent.inventory import Inventory from netbox_agent.inventory import Inventory
from netbox_agent.network import Network from netbox_agent.location import Datacenter, Rack, Tenant
from netbox_agent.misc import (
create_netbox_tags,
get_device_platform,
get_device_role,
get_device_type,
)
from netbox_agent.network import ServerNetwork
from netbox_agent.power import PowerSupply from netbox_agent.power import PowerSupply
def get_device_role(role): class ServerBase:
device_role = nb.dcim.device_roles.get(
name=role
)
if device_role is None:
raise Exception('DeviceRole "{}" does not exist, please create it'.format(role))
return device_role
def get_device_type(type):
device_type = nb.dcim.device_types.get(
model=type
)
if device_type is None:
raise Exception('DeviceType "{}" does not exist, please create it'.format(type))
return device_type
class ServerBase():
def __init__(self, dmi=None): def __init__(self, dmi=None):
if dmi: if dmi:
self.dmi = dmi self.dmi = dmi
else: else:
self.dmi = dmidecode.parse() self.dmi = dmidecode.parse()
self.baseboard = self.dmi.get_by_type('Baseboard') self.baseboard = dmidecode.get_by_type(self.dmi, "Baseboard")
self.bios = self.dmi.get_by_type('BIOS') self.bios = dmidecode.get_by_type(self.dmi, "BIOS")
self.chassis = self.dmi.get_by_type('Chassis') self.chassis = dmidecode.get_by_type(self.dmi, "Chassis")
self.system = self.dmi.get_by_type('System') self.system = dmidecode.get_by_type(self.dmi, "System")
self.device_platform = get_device_platform(config.device.platform)
self.network = None self.network = None
self.tags = (
list(set([x.strip() for x in config.device.tags.split(",") if x.strip()]))
if config.device.tags
else []
)
self.nb_tags = list(create_netbox_tags(self.tags))
config_cf = set(
[f.strip() for f in config.device.custom_fields.split(",") if f.strip()]
)
self.custom_fields = {}
self.custom_fields.update(
dict(
[
(k.strip(), v.strip())
for k, v in [f.split("=", 1) for f in config_cf]
]
)
)
def get_tenant(self):
tenant = Tenant()
return tenant.get()
def get_netbox_tenant(self):
tenant = self.get_tenant()
if tenant is None:
return None
nb_tenant = nb.tenancy.tenants.get(slug=self.get_tenant())
return nb_tenant
def get_datacenter(self): def get_datacenter(self):
dc = Datacenter() dc = Datacenter()
return dc.get() return dc.get()
def get_netbox_datacenter(self): def get_netbox_datacenter(self):
datacenter = nb.dcim.sites.get( dc = self.get_datacenter()
slug=self.get_datacenter() if dc is None:
logging.error("Specifying a datacenter (Site) is mandatory in Netbox")
sys.exit(1)
nb_dc = nb.dcim.sites.get(
slug=dc,
) )
return datacenter if nb_dc is None:
logging.error("Site (slug: {}) has not been found".format(dc))
sys.exit(1)
return nb_dc
def update_netbox_location(self, server): def update_netbox_location(self, server):
dc = self.get_datacenter() dc = self.get_datacenter()
rack = self.get_rack()
nb_rack = self.get_netbox_rack() nb_rack = self.get_netbox_rack()
nb_dc = self.get_netbox_datacenter() nb_dc = self.get_netbox_datacenter()
update = False update = False
if dc and server.site and server.site.slug != nb_dc.slug: if dc and server.site and server.site.slug != nb_dc.slug:
logging.info('Datacenter location has changed from {} to {}, updating'.format( logging.info(
"Datacenter location has changed from {} to {}, updating".format(
server.site.slug, server.site.slug,
nb_dc.slug, nb_dc.slug,
)) )
)
update = True update = True
server.site = nb_dc.id server.site = nb_dc.id
if rack and server.rack and server.rack.id != nb_rack.id: if server.rack and nb_rack and server.rack.id != nb_rack.id:
logging.info('Rack location has changed from {} to {}, updating'.format( logging.info(
"Rack location has changed from {} to {}, updating".format(
server.rack, server.rack,
nb_rack, nb_rack,
)) )
)
update = True update = True
server.rack = nb_rack server.rack = nb_rack
if nb_rack is None: if nb_rack is None:
@ -80,32 +113,59 @@ class ServerBase():
server.position = None server.position = None
return update, server return update, server
def update_netbox_expansion_location(self, server, expansion):
update = False
if expansion.tenant != server.tenant:
expansion.tenant = server.tenant
update = True
if expansion.site != server.site:
expansion.site = server.site
update = True
if expansion.rack != server.rack:
expansion.rack = server.rack
update = True
return update
def get_rack(self): def get_rack(self):
rack = Rack() rack = Rack()
return rack.get() return rack.get()
def get_netbox_rack(self): def get_netbox_rack(self):
rack = nb.dcim.racks.get( rack = self.get_rack()
name=self.get_rack(), datacenter = self.get_netbox_datacenter()
site_id=self.get_netbox_datacenter().id, if not rack:
return None
if rack and not datacenter:
logging.error("Can't get rack if no datacenter is configured or found")
sys.exit(1)
return nb.dcim.racks.get(
name=rack,
site_id=datacenter.id,
) )
return rack
def get_product_name(self): def get_product_name(self):
""" """
Return the Chassis Name from dmidecode info Return the Chassis Name from dmidecode info
""" """
return self.system[0]['Product Name'].strip() return self.system[0]["Product Name"].strip()
def get_service_tag(self): def get_service_tag(self):
""" """
Return the Service Tag from dmidecode info Return the Service Tag from dmidecode info
""" """
return self.system[0]['Serial Number'].strip() return self.system[0]["Serial Number"].strip()
def get_expansion_service_tag(self):
"""
Return the virtual Service Tag from dmidecode info host
with 'expansion'
"""
return self.system[0]["Serial Number"].strip() + " expansion"
def get_hostname(self): def get_hostname(self):
if config.hostname_cmd is None: if config.hostname_cmd is None:
return '{}'.format(socket.gethostname()) return "{}".format(socket.gethostname())
return subprocess.getoutput(config.hostname_cmd) return subprocess.getoutput(config.hostname_cmd)
def is_blade(self): def is_blade(self):
@ -135,225 +195,352 @@ class ServerBase():
def get_power_consumption(self): def get_power_consumption(self):
raise NotImplementedError raise NotImplementedError
def _netbox_create_blade_chassis(self, datacenter, rack): def get_expansion_product(self):
raise NotImplementedError
def _netbox_create_chassis(self, datacenter, tenant, rack):
device_type = get_device_type(self.get_chassis()) device_type = get_device_type(self.get_chassis())
device_role = get_device_role('Server Chassis') device_role = get_device_role(config.device.chassis_role)
serial = self.get_chassis_service_tag() serial = self.get_chassis_service_tag()
logging.info('Creating chassis blade (serial: {serial})'.format( logging.info("Creating chassis blade (serial: {serial})".format(serial=serial))
serial=serial))
new_chassis = nb.dcim.devices.create( new_chassis = nb.dcim.devices.create(
name=self.get_chassis_name(), name=self.get_chassis_name(),
device_type=device_type.id, device_type=device_type.id,
serial=serial, serial=serial,
device_role=device_role.id, role=device_role.id,
site=datacenter.id if datacenter else None, site=datacenter.id if datacenter else None,
tenant=tenant.id if tenant else None,
rack=rack.id if rack else None, rack=rack.id if rack else None,
tags=[{"name": x} for x in self.tags],
custom_fields=self.custom_fields,
) )
return new_chassis return new_chassis
def _netbox_create_blade(self, chassis, datacenter, rack): def _netbox_create_blade(self, chassis, datacenter, tenant, rack):
device_role = get_device_role('Blade') device_role = get_device_role(config.device.blade_role)
device_type = get_device_type(self.get_product_name()) device_type = get_device_type(self.get_product_name())
serial = self.get_service_tag() serial = self.get_service_tag()
hostname = self.get_hostname() hostname = self.get_hostname()
logging.info( logging.info(
'Creating blade (serial: {serial}) {hostname} on chassis {chassis_serial}'.format( "Creating blade (serial: {serial}) {hostname} on chassis {chassis_serial}".format(
serial=serial, hostname=hostname, chassis_serial=chassis.serial serial=serial, hostname=hostname, chassis_serial=chassis.serial
)) )
)
new_blade = nb.dcim.devices.create( new_blade = nb.dcim.devices.create(
name=hostname, name=hostname,
serial=serial, serial=serial,
device_role=device_role.id, role=device_role.id,
device_type=device_type.id, device_type=device_type.id,
parent_device=chassis.id, parent_device=chassis.id,
site=datacenter.id if datacenter else None, site=datacenter.id if datacenter else None,
tenant=tenant.id if tenant else None,
rack=rack.id if rack else None, rack=rack.id if rack else None,
tags=[{"name": x} for x in self.tags],
custom_fields=self.custom_fields,
) )
return new_blade return new_blade
def _netbox_set_blade_slot(self, chassis, server): def _netbox_create_blade_expansion(self, chassis, datacenter, tenant, rack):
slot = self.get_blade_slot() device_role = get_device_role(config.device.blade_role)
# Find the slot and update it with our blade device_type = get_device_type(self.get_expansion_product())
device_bays = nb.dcim.device_bays.filter( serial = self.get_expansion_service_tag()
device_id=chassis.id, hostname = self.get_hostname() + " expansion"
name=slot,
)
if len(device_bays) > 0:
logging.info( logging.info(
'Setting device ({serial}) new slot on {slot} ' "Creating expansion (serial: {serial}) {hostname} on chassis {chassis_serial}".format(
'(Chassis {chassis_serial})..'.format( serial=serial, hostname=hostname, chassis_serial=chassis.serial
serial=server.serial, slot=slot, chassis_serial=chassis.serial )
)) )
device_bay = device_bays[0] new_blade = nb.dcim.devices.create(
device_bay.installed_device = server name=hostname,
device_bay.save() serial=serial,
else: role=device_role.id,
logging.error('Could not find slot {slot} for chassis'.format( device_type=device_type.id,
slot=slot parent_device=chassis.id,
)) site=datacenter.id if datacenter else None,
tenant=tenant.id if tenant else None,
rack=rack.id if rack else None,
tags=[{"name": x} for x in self.tags],
)
return new_blade
def _netbox_create_server(self, datacenter, rack): def _netbox_deduplicate_server(self):
device_role = get_device_role('Server') serial = self.get_service_tag()
hostname = self.get_hostname()
server = nb.dcim.devices.get(name=hostname)
if server and server.serial != serial:
server.delete()
def _netbox_create_server(self, datacenter, tenant, rack):
device_role = get_device_role(config.device.server_role)
device_type = get_device_type(self.get_product_name()) device_type = get_device_type(self.get_product_name())
if not device_type: if not device_type:
raise Exception('Chassis "{}" doesn\'t exist'.format(self.get_chassis())) raise Exception('Chassis "{}" doesn\'t exist'.format(self.get_chassis()))
serial = self.get_service_tag() serial = self.get_service_tag()
hostname = self.get_hostname() hostname = self.get_hostname()
logging.info('Creating server (serial: {serial}) {hostname}'.format( logging.info(
serial=serial, hostname=hostname)) "Creating server (serial: {serial}) {hostname}".format(
serial=serial, hostname=hostname
)
)
new_server = nb.dcim.devices.create( new_server = nb.dcim.devices.create(
name=hostname, name=hostname,
serial=serial, serial=serial,
device_role=device_role.id, role=device_role.id,
device_type=device_type.id, device_type=device_type.id,
platform=self.device_platform.id,
site=datacenter.id if datacenter else None, site=datacenter.id if datacenter else None,
tenant=tenant.id if tenant else None,
rack=rack.id if rack else None, rack=rack.id if rack else None,
tags=[{"name": x} for x in self.tags],
) )
return new_server return new_server
def get_netbox_server(self): def get_netbox_server(self, expansion=False):
if expansion is False:
return nb.dcim.devices.get(serial=self.get_service_tag()) return nb.dcim.devices.get(serial=self.get_service_tag())
def netbox_create(self, config):
logging.debug('Creating Server..')
datacenter = self.get_netbox_datacenter()
rack = self.get_netbox_rack()
if self.is_blade():
# let's find the blade
serial = self.get_service_tag()
blade = nb.dcim.devices.get(serial=serial)
chassis = nb.dcim.devices.get(serial=self.get_chassis_service_tag())
# if it doesn't exist, create it
if not blade:
# check if the chassis exist before
# if it doesn't exist, create it
chassis = nb.dcim.devices.get(
serial=self.get_chassis_service_tag()
)
if not chassis:
chassis = self._netbox_create_blade_chassis(datacenter, rack)
blade = self._netbox_create_blade(chassis, datacenter, rack)
# Set slot for blade
self._netbox_set_blade_slot(chassis, blade)
else: else:
server = nb.dcim.devices.get(serial=self.get_service_tag()) return nb.dcim.devices.get(serial=self.get_expansion_service_tag())
if not server:
self._netbox_create_server(datacenter, rack)
self.network = Network(server=self) def _netbox_set_or_update_blade_slot(self, server, chassis, datacenter):
self.network.create_netbox_network_cards() # before everything check if right chassis
actual_device_bay = (
self.power = PowerSupply(server=self) server.parent_device.device_bay if server.parent_device else None
self.power.create_or_update_power_supply()
if config.inventory:
self.inventory = Inventory(server=self)
self.inventory.create()
logging.debug('Server created!')
def _netbox_update_chassis_for_blade(self, server, datacenter):
chassis = server.parent_device.device_bay.device
device_bay = nb.dcim.device_bays.get(
server.parent_device.device_bay.id
) )
actual_chassis = actual_device_bay.device if actual_device_bay else None
slot = self.get_blade_slot()
if (
actual_chassis
and actual_chassis.serial == chassis.serial
and actual_device_bay.name == slot
):
return
parent_chassis = nb.dcim.devices.get( real_device_bays = nb.dcim.device_bays.filter(
chassis.id device_id=chassis.id,
name=slot,
) )
real_device_bays = nb.dcim.device_bays.filter(
netbox_chassis_serial = parent_chassis.serial device_id=chassis.id,
move_device_bay = False name=slot,
# check chassis serial with dmidecode
if netbox_chassis_serial != self.get_chassis_service_tag():
move_device_bay = True
# try to find the new netbox chassis
chassis = nb.dcim.devices.get(
serial=self.get_chassis_service_tag()
) )
if not chassis: if real_device_bays:
chassis = self._netbox_create_blade_chassis(datacenter) logging.info(
if move_device_bay or device_bay.name != self.get_blade_slot(): "Setting device ({serial}) new slot on {slot} "
logging.info('Device ({serial}) seems to have moved, reseting old slot..'.format( "(Chassis {chassis_serial})..".format(
serial=server.serial)) serial=server.serial, slot=slot, chassis_serial=chassis.serial
device_bay.installed_device = None )
device_bay.save() )
# reset actual device bay if set
if actual_device_bay:
# Forces the evaluation of the installed_device attribute to
# workaround a bug probably due to lazy loading optimization
# that prevents the value change detection
actual_device_bay.installed_device
actual_device_bay.installed_device = None
actual_device_bay.save()
# setup new device bay
real_device_bay = next(real_device_bays)
real_device_bay.installed_device = server
real_device_bay.save()
else:
logging.error("Could not find slot {slot} for chassis".format(slot=slot))
# Set slot for blade def _netbox_set_or_update_blade_expansion_slot(
self._netbox_set_blade_slot(chassis, server) self, expansion, chassis, datacenter
):
# before everything check if right chassis
actual_device_bay = (
expansion.parent_device.device_bay if expansion.parent_device else None
)
actual_chassis = actual_device_bay.device if actual_device_bay else None
slot = self.get_blade_expansion_slot()
if (
actual_chassis
and actual_chassis.serial == chassis.serial
and actual_device_bay.name == slot
):
return
def netbox_update(self, config): real_device_bays = nb.dcim.device_bays.filter(
device_id=chassis.id,
name=slot,
)
if not real_device_bays:
logging.error(
"Could not find slot {slot} expansion for chassis".format(slot=slot)
)
return
logging.info(
"Setting device expansion ({serial}) new slot on {slot} "
"(Chassis {chassis_serial})..".format(
serial=expansion.serial, slot=slot, chassis_serial=chassis.serial
)
)
# reset actual device bay if set
if actual_device_bay:
# Forces the evaluation of the installed_device attribute to
# workaround a bug probably due to lazy loading optimization
# that prevents the value change detection
actual_device_bay.installed_device
actual_device_bay.installed_device = None
actual_device_bay.save()
# setup new device bay
real_device_bay = next(real_device_bays)
real_device_bay.installed_device = expansion
real_device_bay.save()
def netbox_create_or_update(self, config):
""" """
Netbox method to update info about our server/blade Netbox method to create or update info about our server/blade
Handle: Handle:
* new chasis for a blade * new chassis for a blade
* new slot for a bblade * new slot for a blade
* hostname update * hostname update
* new network infos * Network infos
* Inventory management
* PSU management
""" """
logging.debug('Updating Server...') datacenter = self.get_netbox_datacenter()
rack = self.get_netbox_rack()
tenant = self.get_netbox_tenant()
if config.purge_old_devices:
self._netbox_deduplicate_server()
if self.is_blade():
chassis = nb.dcim.devices.get(serial=self.get_chassis_service_tag())
# Chassis does not exist
if not chassis:
chassis = self._netbox_create_chassis(datacenter, tenant, rack)
server = nb.dcim.devices.get(serial=self.get_service_tag()) server = nb.dcim.devices.get(serial=self.get_service_tag())
if not server: if not server:
raise Exception("The server (Serial: {}) isn't yet registered in Netbox, register" server = self._netbox_create_blade(chassis, datacenter, tenant, rack)
'it before updating it'.format(self.get_service_tag()))
update = 0
if self.is_blade():
datacenter = self.get_netbox_datacenter()
# if it's already linked to a chassis
if server.parent_device:
self._netbox_update_chassis_for_blade(server, datacenter)
else:
logging.info('Blade is not in a chassis, fixing...')
chassis = nb.dcim.devices.get(
serial=self.get_chassis_service_tag()
)
if not chassis:
chassis = self._netbox_create_blade_chassis(datacenter)
# Set slot for blade
self._netbox_set_blade_slot(chassis, server)
# Set slot for blade
self._netbox_set_or_update_blade_slot(server, chassis, datacenter)
else:
server = nb.dcim.devices.get(serial=self.get_service_tag())
if not server:
server = self._netbox_create_server(datacenter, tenant, rack)
logging.debug("Updating Server...")
# check network cards
if config.register or config.update_all or config.update_network:
self.network = ServerNetwork(server=self)
self.network.create_or_update_netbox_network_cards()
update_inventory = config.inventory and (
config.register or config.update_all or config.update_inventory
)
# update inventory if feature is enabled
self.inventory = Inventory(server=self)
if update_inventory:
self.inventory.create_or_update()
# update psu
if config.register or config.update_all or config.update_psu:
self.power = PowerSupply(server=self)
self.power.create_or_update_power_supply()
self.power.report_power_consumption()
expansion = nb.dcim.devices.get(serial=self.get_expansion_service_tag())
if self.own_expansion_slot() and config.expansion_as_device:
logging.debug("Update Server expansion...")
if not expansion:
expansion = self._netbox_create_blade_expansion(
chassis, datacenter, tenant, rack
)
# set slot for blade expansion
self._netbox_set_or_update_blade_expansion_slot(
expansion, chassis, datacenter
)
if update_inventory:
# Updates expansion inventory
inventory = Inventory(server=self, update_expansion=True)
inventory.create_or_update()
elif self.own_expansion_slot() and expansion:
expansion.delete()
expansion = None
update = 0
# for every other specs # for every other specs
# check hostname # check hostname
if server.name != self.get_hostname(): if server.name != self.get_hostname():
update += 1
server.name = self.get_hostname() server.name = self.get_hostname()
update += 1
server_tags = sorted(set([x.name for x in server.tags]))
tags = sorted(set(self.tags))
if server_tags != tags:
new_tags_ids = [x.id for x in self.nb_tags]
if not config.preserve_tags:
server.tags = new_tags_ids
else:
server_tags_ids = [x.id for x in server.tags]
server.tags = sorted(set(new_tags_ids + server_tags_ids))
update += 1
if server.custom_fields != self.custom_fields:
server.custom_fields = self.custom_fields
update += 1
if config.update_all or config.update_location: if config.update_all or config.update_location:
ret, server = self.update_netbox_location(server) ret, server = self.update_netbox_location(server)
update += ret update += ret
# check network cards if server.platform != self.device_platform:
if config.update_all or config.update_network: server.platform = self.device_platform
self.network = Network(server=self) update += 1
self.network.update_netbox_network_cards()
# update inventory
if config.update_all or config.update_inventory:
self.inventory = Inventory(server=self)
self.inventory.update()
# update psu
if config.update_all or config.update_psu:
self.power = PowerSupply(server=self)
self.power.create_or_update_power_supply()
self.power.report_power_consumption()
if update: if update:
server.save() server.save()
logging.debug('Finished updating Server!')
if expansion:
update = 0
expansion_name = server.name + " expansion"
if expansion.name != expansion_name:
expansion.name = expansion_name
update += 1
if self.update_netbox_expansion_location(server, expansion):
update += 1
if update:
expansion.save()
logging.debug("Finished updating Server!")
def print_debug(self): def print_debug(self):
self.network = Network(server=self) self.network = ServerNetwork(server=self)
print('Datacenter:', self.get_datacenter()) print("Datacenter:", self.get_datacenter())
print('Netbox Datacenter:', self.get_netbox_datacenter()) print("Netbox Datacenter:", self.get_netbox_datacenter())
print('Rack:', self.get_rack()) print("Rack:", self.get_rack())
print('Netbox Rack:', self.get_netbox_rack()) print("Netbox Rack:", self.get_netbox_rack())
print('Is blade:', self.is_blade()) print("Is blade:", self.is_blade())
print('Product Name:', self.get_product_name()) print("Got expansion:", self.own_expansion_slot())
print('Chassis:', self.get_chassis()) print("Product Name:", self.get_product_name())
print('Chassis service tag:', self.get_chassis_service_tag()) print("Platform:", self.device_platform)
print('Service tag:', self.get_service_tag()) print("Chassis:", self.get_chassis())
print('NIC:',) print("Chassis service tag:", self.get_chassis_service_tag())
print("Service tag:", self.get_service_tag())
print(
"NIC:",
)
pprint(self.network.get_network_cards()) pprint(self.network.get_network_cards())
pass pass
def own_expansion_slot(self):
"""
Indicates if the device hosts an expansion card
"""
return False
def own_gpu_expansion_slot(self):
"""
Indicates if the device hosts a GPU expansion card
"""
return False
def own_drive_expansion_slot(self):
"""
Indicates if the device hosts a drive expansion bay
"""
return False

View file

@ -1,17 +1,17 @@
import logging import logging
import subprocess import subprocess
from netbox_agent.server import ServerBase
from netbox_agent.misc import is_tool from netbox_agent.misc import is_tool
from netbox_agent.server import ServerBase
class DellHost(ServerBase): class DellHost(ServerBase):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(DellHost, self).__init__(*args, **kwargs) super(DellHost, self).__init__(*args, **kwargs)
self.manufacturer = 'Dell' self.manufacturer = "Dell"
def is_blade(self): def is_blade(self):
return self.get_product_name().startswith('PowerEdge M') return self.get_product_name().startswith("PowerEdge M")
def get_blade_slot(self): def get_blade_slot(self):
""" """
@ -20,50 +20,69 @@ class DellHost(ServerBase):
` Location In Chassis: Slot 03` ` Location In Chassis: Slot 03`
""" """
if self.is_blade(): if self.is_blade():
return self.dmi.get_by_type('Baseboard')[0].get('Location In Chassis').strip() return self.baseboard[0].get("Location In Chassis").strip()
return None return None
def get_chassis_name(self): def get_chassis_name(self):
if not self.is_blade(): if not self.is_blade():
return None return None
return 'Chassis {}'.format(self.get_service_tag()) return "Chassis {}".format(self.get_service_tag())
def get_chassis(self): def get_chassis(self):
if self.is_blade(): if self.is_blade():
return self.dmi.get_by_type('Chassis')[0]['Version'].strip() return self.chassis[0]["Version"].strip()
return self.get_product_name() return self.get_product_name()
def get_chassis_service_tag(self): def get_chassis_service_tag(self):
if self.is_blade(): if self.is_blade():
return self.dmi.get_by_type('Chassis')[0]['Serial Number'].strip() return self.chassis[0]["Serial Number"].strip()
return self.get_service_tag() return self.get_service_tag()
def get_power_consumption(self): def get_power_consumption(self):
''' """
Parse omreport output like this Parse omreport output like this
Amperage Amperage
PS1 Current 1 : 1.8 A PS1 Current 1 : 1.8 A
PS2 Current 2 : 1.4 A PS2 Current 2 : 1.4 A
''' """
value = [] value = []
if not is_tool('omreport'): if not is_tool("omreport"):
logging.error('omreport does not seem to be installed, please debug') logging.error("omreport does not seem to be installed, please debug")
return value return value
data = subprocess.getoutput('omreport chassis pwrmonitoring') data = subprocess.getoutput("omreport chassis pwrmonitoring")
amperage = False amperage = False
for line in data.splitlines(): for line in data.splitlines():
if line.startswith('Amperage'): if line.startswith("Amperage"):
amperage = True amperage = True
continue continue
if amperage: if amperage:
if line.startswith('PS'): if line.startswith("PS"):
amp_value = line.split(':')[1].split()[0] amp_value = line.split(":")[1].split()[0]
value.append(amp_value) value.append(amp_value)
else: else:
break break
return value return value
def get_expansion_product(self):
"""
Get the extension slot that is on a pair slot number
next to the compute slot that is on an odd slot number
"""
raise NotImplementedError
def is_expansion_slot(self, server):
"""
Return True if its an extension slot
"""
raise NotImplementedError
def get_blade_expansion_slot(self):
"""
Expansion slot are always the compute bay number + 1
"""
raise NotImplementedError

25
netbox_agent/vendors/generic.py vendored Normal file
View file

@ -0,0 +1,25 @@
import netbox_agent.dmidecode as dmidecode
from netbox_agent.server import ServerBase
class GenericHost(ServerBase):
def __init__(self, *args, **kwargs):
super(GenericHost, self).__init__(*args, **kwargs)
self.manufacturer = dmidecode.get_by_type(self.dmi, "Baseboard")[0].get(
"Manufacturer"
)
def is_blade(self):
return False
def get_blade_slot(self):
return None
def get_chassis_name(self):
return None
def get_chassis(self):
return self.get_product_name()
def get_chassis_service_tag(self):
return self.get_service_tag()

View file

@ -1,15 +1,22 @@
import netbox_agent.dmidecode as dmidecode
from netbox_agent.inventory import Inventory
from netbox_agent.server import ServerBase from netbox_agent.server import ServerBase
class HPHost(ServerBase): class HPHost(ServerBase):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(HPHost, self).__init__(*args, **kwargs) super(HPHost, self).__init__(*args, **kwargs)
self.manufacturer = "HP"
self.product = self.get_product_name()
if self.is_blade(): if self.is_blade():
self.hp_rack_locator = self._find_rack_locator() self.hp_rack_locator = self._find_rack_locator()
self.manufacturer = 'HP'
def is_blade(self): def is_blade(self):
return self.get_product_name().startswith('ProLiant BL') blade = self.product.startswith("ProLiant BL")
blade |= self.product.startswith("ProLiant m") and self.product.endswith(
"Server Cartridge"
)
return blade
def _find_rack_locator(self): def _find_rack_locator(self):
""" """
@ -19,35 +26,102 @@ class HPHost(ServerBase):
""" """
# FIXME: make a dmidecode function get_by_dminame() ? # FIXME: make a dmidecode function get_by_dminame() ?
if self.is_blade(): if self.is_blade():
locator = self.dmi.get_by_type(204) locator = dmidecode.get_by_type(self.dmi, 204)
if self.get_product_name() == 'ProLiant BL460c Gen10': if self.product.startswith("ProLiant BL460c Gen10"):
locator = locator[0]['Strings'] locator = locator[0]["Strings"]
return { return {
'Enclosure Model': locator[2].strip(), "Enclosure Model": locator[2].strip(),
'Enclosure Name': locator[0].strip(), "Enclosure Name": locator[0].strip(),
'Server Bay': locator[3].strip(), "Server Bay": locator[3].strip(),
'Enclosure Serial': locator[4].strip(), "Enclosure Serial": locator[4].strip(),
} }
# HP ProLiant m750, m710x, m510 Server Cartridge
if self.product.startswith("ProLiant m") and self.product.endswith(
"Server Cartridge"
):
locator = dmidecode.get_by_type(self.dmi, 2)
chassis = dmidecode.get_by_type(self.dmi, 3)
return {
"Enclosure Model": "Moonshot 1500 Chassis",
"Enclosure Name": "Unknown",
"Server Bay": locator[0]["Location In Chassis"].strip(),
"Enclosure Serial": chassis[0]["Serial Number"].strip(),
}
return locator[0] return locator[0]
def get_blade_slot(self): def get_blade_slot(self):
if self.is_blade(): if self.is_blade():
return 'Bay {}'.format( return "Bay {}".format(str(self.hp_rack_locator["Server Bay"].strip()))
int(self.hp_rack_locator['Server Bay'].strip())
)
return None return None
def get_chassis(self): def get_chassis(self):
if self.is_blade(): if self.is_blade():
return self.hp_rack_locator['Enclosure Model'].strip() return self.hp_rack_locator["Enclosure Model"].strip()
return self.get_product_name() return self.get_product_name()
def get_chassis_name(self): def get_chassis_name(self):
if not self.is_blade(): if not self.is_blade():
return None return None
return self.hp_rack_locator['Enclosure Name'].strip() return self.hp_rack_locator["Enclosure Name"].strip()
def get_chassis_service_tag(self): def get_chassis_service_tag(self):
if self.is_blade(): if self.is_blade():
return self.hp_rack_locator['Enclosure Serial'].strip() return self.hp_rack_locator["Enclosure Serial"].strip()
return self.get_service_tag() return self.get_service_tag()
def get_blade_expansion_slot(self):
"""
Expansion slot are always the compute bay number + 1
"""
if (
self.is_blade()
and self.own_gpu_expansion_slot()
or self.own_disk_expansion_slot()
or True
):
return "Bay {}".format(
str(int(self.hp_rack_locator["Server Bay"].strip()) + 1)
)
return None
def get_expansion_product(self):
"""
Get the extension slot that is on a pair slot number
next to the compute slot that is on an odd slot number
I only know on model of slot GPU extension card that.
"""
if self.own_gpu_expansion_slot():
return "ProLiant BL460c Graphics Expansion Blade"
elif self.own_disk_expansion_slot():
return "ProLiant BL460c Disk Expansion Blade"
return None
def own_expansion_slot(self):
"""
Indicates if the device hosts an expension card
"""
return self.own_gpu_expansion_slot() or self.own_disk_expansion_slot()
def own_gpu_expansion_slot(self):
"""
Indicates if the device hosts a GPU expansion card based
on the product name
"""
return self.get_product_name().endswith("Graphics Exp")
def own_disk_expansion_slot(self):
"""
Indicates if the device hosts a drive expansion card based
on raid card attributes.
"""
# Uses already parsed inventory if available
# parses it otherwise
inventory = getattr(self, "inventory", None)
if inventory is None:
inventory = Inventory(self)
for raid_card in inventory.get_raid_cards():
if self.is_blade() and raid_card.is_external():
return True
return False

View file

@ -4,29 +4,29 @@ from netbox_agent.server import ServerBase
class QCTHost(ServerBase): class QCTHost(ServerBase):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(QCTHost, self).__init__(*args, **kwargs) super(QCTHost, self).__init__(*args, **kwargs)
self.manufacturer = 'QCT' self.manufacturer = "QCT"
def is_blade(self): def is_blade(self):
return 'Location In Chassis' in self.dmi.get_by_type('Baseboard')[0].keys() return "Location In Chassis" in self.baseboard[0].keys()
def get_blade_slot(self): def get_blade_slot(self):
if self.is_blade(): if self.is_blade():
return 'Slot {}'.format( return "Slot {}".format(
self.dmi.get_by_type('Baseboard')[0].get('Location In Chassis').strip() self.baseboard[0].get("Location In Chassis").strip()
) )
return None return None
def get_chassis_name(self): def get_chassis_name(self):
if not self.is_blade(): if not self.is_blade():
return None return None
return 'Chassis {}'.format(self.get_service_tag()) return "Chassis {}".format(self.get_service_tag())
def get_chassis(self): def get_chassis(self):
if self.is_blade(): if self.is_blade():
return self.dmi.get_by_type('Chassis')[0]['Version'].strip() return self.chassis[0]["Version"].strip()
return self.get_product_name() return self.get_product_name()
def get_chassis_service_tag(self): def get_chassis_service_tag(self):
if self.is_blade(): if self.is_blade():
return self.dmi.get_by_type('Chassis')[0]['Serial Number'].strip() return self.chassis[0]["Serial Number"].strip()
return self.get_service_tag() return self.get_service_tag()

View file

@ -1,8 +1,9 @@
from netbox_agent.location import Slot from netbox_agent.location import Slot
from netbox_agent.server import ServerBase from netbox_agent.server import ServerBase
"""
class SupermicroHost(ServerBase):
"""
Supermicro DMI can be messed up. They depend on the vendor Supermicro DMI can be messed up. They depend on the vendor
to set the correct values. The endusers cannot to set the correct values. The endusers cannot
change them without buying a license from Supermicro. change them without buying a license from Supermicro.
@ -13,17 +14,26 @@ from netbox_agent.server import ServerBase
2) Baseboard - this is used for the blade. 2) Baseboard - this is used for the blade.
3) Chassis - this is ignored. 3) Chassis - this is ignored.
""" """
class SupermicroHost(ServerBase):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(SupermicroHost, self).__init__(*args, **kwargs) super(SupermicroHost, self).__init__(*args, **kwargs)
self.manufacturer = 'Supermicro' self.manufacturer = "Supermicro"
def is_blade(self): def is_blade(self):
blade = self.system[0]['Product Name'].startswith('SBI') product_name = self.system[0]["Product Name"].strip()
blade |= self.system[0]['Product Name'].startswith('SYS') # Blades
blade = product_name.startswith("SBI")
blade |= product_name.startswith("SBA")
# Twin
blade |= "TR-" in product_name
# TwinPro
blade |= "TP-" in product_name
# BigTwin
blade |= "BT-" in product_name
# Microcloud
blade |= product_name.startswith("SYS-5039")
blade |= product_name.startswith("SYS-5038")
return blade return blade
def get_blade_slot(self): def get_blade_slot(self):
@ -36,24 +46,34 @@ class SupermicroHost(ServerBase):
return None return None
def get_service_tag(self): def get_service_tag(self):
return self.baseboard[0]['Serial Number'].strip() if self.is_blade():
return self.baseboard[0]["Serial Number"].strip()
return self.system[0]["Serial Number"].strip()
def get_product_name(self): def get_product_name(self):
if self.is_blade(): if self.is_blade():
return self.baseboard[0]['Product Name'].strip() return self.baseboard[0]["Product Name"].strip()
return self.system[0]['Product Name'].strip() return self.system[0]["Product Name"].strip()
def get_chassis(self): def get_chassis(self):
if self.is_blade(): if self.is_blade():
return self.system[0]['Product Name'].strip() return self.system[0]["Product Name"].strip()
return self.get_product_name() return self.get_product_name()
def get_chassis_service_tag(self): def get_chassis_service_tag(self):
if self.is_blade(): if self.is_blade():
return self.system[0]['Serial Number'].strip() return self.system[0]["Serial Number"].strip()
return self.get_service_tag() return self.get_service_tag()
def get_chassis_name(self): def get_chassis_name(self):
if not self.is_blade(): if not self.is_blade():
return None return None
return 'Chassis {}'.format(self.get_chassis_service_tag()) return "Chassis {}".format(self.get_chassis_service_tag())
def get_expansion_product(self):
"""
Get the extension slot that is on a pair slot number
next to the compute slot that is on an odd slot number
I only know on model of slot GPU extension card that.
"""
raise NotImplementedError

View file

@ -0,0 +1,151 @@
import os
from pprint import pprint
import netbox_agent.dmidecode as dmidecode
from netbox_agent.config import config
from netbox_agent.config import netbox_instance as nb
from netbox_agent.location import Tenant
from netbox_agent.logging import logging # NOQA
from netbox_agent.misc import create_netbox_tags, get_device_platform, get_hostname
from netbox_agent.network import VirtualNetwork
def is_vm(dmi):
bios = dmidecode.get_by_type(dmi, "BIOS")[0]
system = dmidecode.get_by_type(dmi, "System")[0]
return (
"Hyper-V" in bios["Version"]
or "Xen" in bios["Version"]
or "Google Compute Engine" in system["Product Name"]
) or (
(
"Amazon EC2" in system["Manufacturer"]
and not system["Product Name"].endswith(".metal")
)
or "RHEV Hypervisor" in system["Product Name"]
or "QEMU" in system["Manufacturer"]
or "VirtualBox" in bios["Version"]
or "VMware" in system["Manufacturer"]
)
class VirtualMachine(object):
def __init__(self, dmi=None):
if dmi:
self.dmi = dmi
else:
self.dmi = dmidecode.parse()
self.network = None
self.device_platform = get_device_platform(config.device.platform)
self.tags = (
list(set(config.device.tags.split(","))) if config.device.tags else []
)
self.nb_tags = create_netbox_tags(self.tags)
def get_memory(self):
mem_bytes = os.sysconf("SC_PAGE_SIZE") * os.sysconf(
"SC_PHYS_PAGES"
) # e.g. 4015976448
mem_gib = mem_bytes / (1024.0**2) # e.g. 3.74
return int(mem_gib)
def get_vcpus(self):
return os.cpu_count()
def get_netbox_vm(self):
hostname = get_hostname(config)
vm = nb.virtualization.virtual_machines.get(name=hostname)
return vm
def get_netbox_cluster(self, name):
cluster = nb.virtualization.clusters.get(
name=name,
)
return cluster
def get_netbox_datacenter(self, name):
cluster = self.get_netbox_cluster()
if cluster.datacenter:
return cluster.datacenter
return None
def get_tenant(self):
tenant = Tenant()
return tenant.get()
def get_netbox_tenant(self):
tenant = self.get_tenant()
if tenant is None:
return None
nb_tenant = nb.tenancy.tenants.get(slug=self.get_tenant())
return nb_tenant
def netbox_create_or_update(self, config):
logging.debug("It's a virtual machine")
created = False
updated = 0
hostname = get_hostname(config)
vm = self.get_netbox_vm()
vcpus = self.get_vcpus()
memory = self.get_memory()
tenant = self.get_netbox_tenant()
if not vm:
logging.debug("Creating Virtual machine..")
cluster = self.get_netbox_cluster(config.virtual.cluster_name)
vm = nb.virtualization.virtual_machines.create(
name=hostname,
cluster=cluster.id,
platform=self.device_platform.id,
vcpus=vcpus,
memory=memory,
tenant=tenant.id if tenant else None,
tags=[{"name": x} for x in self.tags],
)
created = True
self.network = VirtualNetwork(server=self)
self.network.create_or_update_netbox_network_cards()
if not created:
if vm.vcpus != vcpus:
vm.vcpus = vcpus
updated += 1
if vm.memory != memory:
vm.memory = memory
updated += 1
vm_tags = sorted(set([x.name for x in vm.tags]))
tags = sorted(set(self.tags))
if vm_tags != tags:
new_tags_ids = [x.id for x in self.nb_tags]
if not config.preserve_tags:
vm.tags = new_tags_ids
else:
vm_tags_ids = [x.id for x in vm.tags]
vm.tags = sorted(set(new_tags_ids + vm_tags_ids))
updated += 1
if vm.platform != self.device_platform:
vm.platform = self.device_platform
updated += 1
if updated:
vm.save()
def print_debug(self):
self.network = VirtualNetwork(server=self)
print("Cluster:", self.get_netbox_cluster(config.virtual.cluster_name))
print("Platform:", self.device_platform)
print("VM:", self.get_netbox_vm())
print("vCPU:", self.get_vcpus())
print("Memory:", f"{self.get_memory()} MB")
print(
"NIC:",
)
pprint(self.network.get_network_cards())
pass

46
nix/netifaces2.nix Normal file
View file

@ -0,0 +1,46 @@
{
lib,
buildPythonPackage,
fetchFromGitHub,
cargo,
rustPlatform,
rustc,
typing-extensions,
}:
buildPythonPackage rec {
pname = "netifaces-2";
version = "0.0.22";
pyproject = true;
src = fetchFromGitHub {
owner = "SamuelYvon";
repo = "netifaces-2";
rev = "V${version}";
hash = "sha256-XO3HWq8FOVzvpbK8mIBOup6hFMnhDpqOK/5bPziPZQ8=";
};
cargoDeps = rustPlatform.fetchCargoTarball {
inherit src;
name = "${pname}-${version}";
hash = "sha256-uoUa6DSBuIV3RrE7svT1TVLxPHdx8BFu/C6mbpRmor0=";
};
build-system = [
cargo
rustPlatform.cargoSetupHook
rustPlatform.maturinBuildHook
rustc
];
dependencies = [ typing-extensions ];
pythonImportsCheck = [ "netifaces" ];
meta = {
description = "Netifaces reborn";
homepage = "https://github.com/SamuelYvon/netifaces-2.git";
license = lib.licenses.mit;
maintainers = with lib.maintainers; [ ];
};
}

80
npins/default.nix Normal file
View file

@ -0,0 +1,80 @@
# Generated by npins. Do not modify; will be overwritten regularly
let
data = builtins.fromJSON (builtins.readFile ./sources.json);
version = data.version;
mkSource =
spec:
assert spec ? type;
let
path =
if spec.type == "Git" then
mkGitSource spec
else if spec.type == "GitRelease" then
mkGitSource spec
else if spec.type == "PyPi" then
mkPyPiSource spec
else if spec.type == "Channel" then
mkChannelSource spec
else
builtins.throw "Unknown source type ${spec.type}";
in
spec // { outPath = path; };
mkGitSource =
{
repository,
revision,
url ? null,
hash,
branch ? null,
...
}:
assert repository ? type;
# At the moment, either it is a plain git repository (which has an url), or it is a GitHub/GitLab repository
# In the latter case, there we will always be an url to the tarball
if url != null then
(builtins.fetchTarball {
inherit url;
sha256 = hash; # FIXME: check nix version & use SRI hashes
})
else
assert repository.type == "Git";
let
urlToName =
url: rev:
let
matched = builtins.match "^.*/([^/]*)(\\.git)?$" repository.url;
short = builtins.substring 0 7 rev;
appendShort = if (builtins.match "[a-f0-9]*" rev) != null then "-${short}" else "";
in
"${if matched == null then "source" else builtins.head matched}${appendShort}";
name = urlToName repository.url revision;
in
builtins.fetchGit {
url = repository.url;
rev = revision;
inherit name;
# hash = hash;
};
mkPyPiSource =
{ url, hash, ... }:
builtins.fetchurl {
inherit url;
sha256 = hash;
};
mkChannelSource =
{ url, hash, ... }:
builtins.fetchTarball {
inherit url;
sha256 = hash;
};
in
if version == 3 then
builtins.mapAttrs (_: mkSource) data.pins
else
throw "Unsupported format version ${toString version} in sources.json. Try running `npins upgrade`"

23
npins/sources.json Normal file
View file

@ -0,0 +1,23 @@
{
"pins": {
"git-hooks": {
"type": "Git",
"repository": {
"type": "GitHub",
"owner": "cachix",
"repo": "git-hooks.nix"
},
"branch": "master",
"revision": "3c3e88f0f544d6bb54329832616af7eb971b6be6",
"url": "https://github.com/cachix/git-hooks.nix/archive/3c3e88f0f544d6bb54329832616af7eb971b6be6.tar.gz",
"hash": "04pwjz423iq2nkazkys905gvsm5j39722ngavrnx42b8msr5k555"
},
"nixpkgs": {
"type": "Channel",
"name": "nixpkgs-unstable",
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-24.11pre694416.ccc0c2126893/nixexprs.tar.xz",
"hash": "0cn1z4wzps8nfqxzr6l5mbn81adcqy2cy2ic70z13fhzicmxfsbx"
}
},
"version": 3
}

2
pyproject.toml Normal file
View file

@ -0,0 +1,2 @@
[tool.isort]
profile = "black"

View file

@ -1,5 +1,8 @@
pynetbox==4.2.4 pynetbox==7.3.4
netaddr==0.7.19 netaddr==1.3.0
netifaces==0.10.9 netifaces2==0.0.22
pyyaml==5.3 pyyaml==6.0.1
jsonargparse==2.22.2 jsonargparse==4.32.0
python-slugify==8.0.4
packaging==23.2
distro==1.9.0

27
rpmenv.json Normal file
View file

@ -0,0 +1,27 @@
{
"extensions": {
"enabled": ["python_venv", "blocks"]
},
"core": {
"group": "Application/System",
"license": "Apache2",
"name": "netbox-agent",
"summary": "NetBox agent for server",
"url": "https://github.com/Solvik/netbox-agent",
"version": "0.7.0",
"requires": ["lshw"]
},
"python_venv": {
"python": "python3.6",
"requirements": ["requirements.txt"],
"name": "netbox-agent",
"path": "/opt/"
},
"blocks": {
"post": ["ln -sf /opt/netbox-agent/bin/netbox_agent /usr/bin/netbox_agent"],
"desc": [
"This project aims to create hardware automatically into Netbox based on standard tools (dmidecode, lldpd, parsing /sys/, etc).",
"The goal is to generate an existing infrastructure on Netbox and have the ability to update it regularly by executing the agent."
]
}
}

16
setup.cfg Normal file
View file

@ -0,0 +1,16 @@
[tool:pytest]
testpaths = tests
python_files = *.py
addopts = -vv --showlocals --cov-report term-missing --cov netbox_agent --no-cov-on-fail
[flake8]
ignore = E125,E129,W503,W504
exclude = .venv/,.git/,.tox/,netbox-docker/
max-line-length = 99
[isort]
line_length = 99
indent=' '
multi_line_output = 0
skip = .venv/,.git/,tests/conftest.py,ipython_config.py
known_first_party = netbox_agent,tests

View file

@ -1,33 +1,38 @@
from setuptools import setup, find_packages import os
from setuptools import find_packages, setup
def get_requirements():
reqs_path = os.path.join(os.path.dirname(__file__), "requirements.txt")
with open(reqs_path, "r") as f:
reqs = [r.strip() for r in f if r.strip()]
return reqs
setup( setup(
name='netbox_agent', name="netbox_agent",
version='0.5.0', version="0.7.1",
description='NetBox agent for server', description="NetBox agent for server",
long_description=open('README.md', encoding="utf-8").read(), long_description=open("README.md", encoding="utf-8").read(),
url='https://github.com/solvik/netbox_agent', long_description_content_type="text/markdown",
author='Solvik Blum', url="https://github.com/solvik/netbox_agent",
author_email='solvik@solvik.fr', author="Solvik Blum",
license='Apache2', author_email="solvik@solvik.fr",
license="Apache2",
include_package_data=True, include_package_data=True,
packages=find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"]), packages=find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"]),
use_scm_version=True, use_scm_version=True,
install_requires=[ install_requires=get_requirements(),
'pynetbox==4.2.4',
'netaddr==0.7.19',
'netifaces==0.10.9',
'pyyaml==5.3',
'jsonargparse==2.22.2',
],
zip_safe=False, zip_safe=False,
keywords=['netbox'], keywords=["netbox"],
classifiers=[ classifiers=[
'Intended Audience :: Developers', "Intended Audience :: Developers",
'Development Status :: 5 - Production/Stable', "Development Status :: 5 - Production/Stable",
'Programming Language :: Python :: 3', "Programming Language :: Python :: 3",
'Programming Language :: Python :: 3.6', "Programming Language :: Python :: 3.6",
], ],
entry_points={ entry_points={
'console_scripts': ['netbox_agent=netbox_agent.cli:main'], "console_scripts": ["netbox_agent=netbox_agent.cli:main"],
} },
) )

1
shell.nix Normal file
View file

@ -0,0 +1 @@
(import ./. { }).devShell

22
tests.sh Executable file
View file

@ -0,0 +1,22 @@
set -x
git clone https://github.com/netbox-community/netbox-docker.git
cd netbox-docker
docker-compose pull
docker-compose up -d
while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' http://$(docker-compose port nginx 8080))" != "200" ]]
do
sleep 5
done
export NETBOX_AGENT__NETBOX__URL="http://$(docker-compose port nginx 8080)"
export NETBOX_AGENT__NETBOX__TOKEN=0123456789abcdef0123456789abcdef01234567
cd -
pytest
cd netbox-docker
docker-compose down
cd -
set +x

35
tests/conftest.py Normal file
View file

@ -0,0 +1,35 @@
import os
import pytest
def get_fixture_paths(path):
if not os.path.isdir(path):
return [path]
fixture_paths = []
for p in os.listdir(path):
p = os.path.join(path, p)
if os.path.isfile(p):
fixture_paths.append(p)
return fixture_paths
def parametrize_with_fixtures(
path, base_path="tests/fixtures", argname="fixture", only_filenames=None
):
path = os.path.join(base_path, path)
fixture_paths = get_fixture_paths(path)
argvalues = []
for path in fixture_paths:
with open(path, "r") as f:
content = "".join(f.readlines())
filename = os.path.basename(path)
if only_filenames and filename not in only_filenames:
continue
param = pytest.param(content, id=filename)
argvalues.append(param)
def _decorator(test_function):
return pytest.mark.parametrize(argname, argvalues)(test_function)
return _decorator

868
tests/fixtures/dmidecode/Dell_DSS7500 vendored Normal file
View file

@ -0,0 +1,868 @@
# dmidecode 2.12
SMBIOS 2.8 present.
57 structures occupying 3093 bytes.
Table at 0x7AF09000.
Handle 0xDA00, DMI type 218, 11 bytes
OEM-specific Type
Header and Data:
DA 0B 00 DA B2 00 17 20 0E 10 03
Handle 0x0000, DMI type 0, 24 bytes
BIOS Information
Vendor: Dell Inc.
Version: 2.3.4
Release Date: 11/10/2016
Address: 0xF0000
Runtime Size: 64 kB
ROM Size: 16384 kB
Characteristics:
ISA is supported
PCI is supported
PNP is supported
BIOS is upgradeable
BIOS shadowing is allowed
Boot from CD is supported
Selectable boot is supported
EDD is supported
Japanese floppy for Toshiba 1.2 MB is supported (int 13h)
5.25"/360 kB floppy services are supported (int 13h)
5.25"/1.2 MB floppy services are supported (int 13h)
3.5"/720 kB floppy services are supported (int 13h)
8042 keyboard services are supported (int 9h)
Serial services are supported (int 14h)
CGA/mono video services are supported (int 10h)
ACPI is supported
USB legacy is supported
BIOS boot specification is supported
Function key-initiated network boot is supported
Targeted content distribution is supported
UEFI is supported
BIOS Revision: 2.3
Handle 0x0100, DMI type 1, 27 bytes
System Information
Manufacturer: Dell Inc.
Product Name: DSS7500
Version: Not Specified
Serial Number: 4242-SERVICE_TAG
UUID: 4C4C4544-0048-5110-804A-C6C04F594A32
Wake-up Type: Power Switch
SKU Number: SKU=NotProvided;ModelName=DSS7500
Family: Not Specified
Handle 0x0200, DMI type 2, 8 bytes
Base Board Information
Manufacturer: Dell Inc.
Product Name: 0X89R8
Version: A04
Serial Number: 4242
Handle 0x0300, DMI type 3, 22 bytes
Chassis Information
Manufacturer: Dell Inc.
Type: Rack Mount Chassis
Lock: Present
Version: Not Specified
Serial Number: 4242
Asset Tag: Not Specified
Boot-up State: Safe
Power Supply State: Safe
Thermal State: Safe
Security Status: Unknown
OEM Information: 0x00000000
Height: 4 U
Number Of Power Cords: Unspecified
Contained Elements: 0
SKU Number: Not Specified
Handle 0x0400, DMI type 4, 42 bytes
Processor Information
Socket Designation: CPU1
Type: Central Processor
Family: Xeon
Manufacturer: Intel
ID: F1 06 04 00 FF FB EB BF
Signature: Type 0, Family 6, Model 79, Stepping 1
Flags:
FPU (Floating-point unit on-chip)
VME (Virtual mode extension)
DE (Debugging extension)
PSE (Page size extension)
TSC (Time stamp counter)
MSR (Model specific registers)
PAE (Physical address extension)
MCE (Machine check exception)
CX8 (CMPXCHG8 instruction supported)
APIC (On-chip APIC hardware supported)
SEP (Fast system call)
MTRR (Memory type range registers)
PGE (Page global enable)
MCA (Machine check architecture)
CMOV (Conditional move instruction supported)
PAT (Page attribute table)
PSE-36 (36-bit page size extension)
CLFSH (CLFLUSH instruction supported)
DS (Debug store)
ACPI (ACPI supported)
MMX (MMX technology supported)
FXSR (FXSAVE and FXSTOR instructions supported)
SSE (Streaming SIMD extensions)
SSE2 (Streaming SIMD extensions 2)
SS (Self-snoop)
HTT (Multi-threading)
TM (Thermal monitor supported)
PBE (Pending break enabled)
Version: Intel(R) Xeon(R) CPU E5-2630 v4 @ 2.20GHz
Voltage: 1.3 V
External Clock: 8000 MHz
Max Speed: 4000 MHz
Current Speed: 2200 MHz
Status: Populated, Enabled
Upgrade: <OUT OF SPEC>
L1 Cache Handle: 0x0700
L2 Cache Handle: 0x0701
L3 Cache Handle: 0x0702
Serial Number: 4242
Asset Tag: Not Specified
Part Number: Not Specified
Core Count: 10
Core Enabled: 10
Thread Count: 10
Characteristics:
64-bit capable
Multi-Core
Hardware Thread
Execute Protection
Enhanced Virtualization
Power/Performance Control
Handle 0x0401, DMI type 4, 42 bytes
Processor Information
Socket Designation: CPU2
Type: Central Processor
Family: Xeon
Manufacturer: Intel
ID: F1 06 04 00 FF FB EB BF
Signature: Type 0, Family 6, Model 79, Stepping 1
Flags:
FPU (Floating-point unit on-chip)
VME (Virtual mode extension)
DE (Debugging extension)
PSE (Page size extension)
TSC (Time stamp counter)
MSR (Model specific registers)
PAE (Physical address extension)
MCE (Machine check exception)
CX8 (CMPXCHG8 instruction supported)
APIC (On-chip APIC hardware supported)
SEP (Fast system call)
MTRR (Memory type range registers)
PGE (Page global enable)
MCA (Machine check architecture)
CMOV (Conditional move instruction supported)
PAT (Page attribute table)
PSE-36 (36-bit page size extension)
CLFSH (CLFLUSH instruction supported)
DS (Debug store)
ACPI (ACPI supported)
MMX (MMX technology supported)
FXSR (FXSAVE and FXSTOR instructions supported)
SSE (Streaming SIMD extensions)
SSE2 (Streaming SIMD extensions 2)
SS (Self-snoop)
HTT (Multi-threading)
TM (Thermal monitor supported)
PBE (Pending break enabled)
Version: Intel(R) Xeon(R) CPU E5-2630 v4 @ 2.20GHz
Voltage: 1.3 V
External Clock: 8000 MHz
Max Speed: 4000 MHz
Current Speed: 2200 MHz
Status: Populated, Enabled
Upgrade: <OUT OF SPEC>
L1 Cache Handle: 0x0703
L2 Cache Handle: 0x0704
L3 Cache Handle: 0x0705
Serial Number: 4242
Asset Tag: Not Specified
Part Number: Not Specified
Core Count: 10
Core Enabled: 10
Thread Count: 10
Characteristics:
64-bit capable
Multi-Core
Hardware Thread
Execute Protection
Enhanced Virtualization
Power/Performance Control
Handle 0x0700, DMI type 7, 19 bytes
Cache Information
Socket Designation: Not Specified
Configuration: Enabled, Not Socketed, Level 1
Operational Mode: Write Back
Location: Internal
Installed Size: 640 kB
Maximum Size: 640 kB
Supported SRAM Types:
Unknown
Installed SRAM Type: Unknown
Speed: Unknown
Error Correction Type: Parity
System Type: Unified
Associativity: 8-way Set-associative
Handle 0x0701, DMI type 7, 19 bytes
Cache Information
Socket Designation: Not Specified
Configuration: Enabled, Not Socketed, Level 2
Operational Mode: Write Back
Location: Internal
Installed Size: 2560 kB
Maximum Size: 2560 kB
Supported SRAM Types:
Unknown
Installed SRAM Type: Unknown
Speed: Unknown
Error Correction Type: Single-bit ECC
System Type: Unified
Associativity: 8-way Set-associative
Handle 0x0702, DMI type 7, 19 bytes
Cache Information
Socket Designation: Not Specified
Configuration: Enabled, Not Socketed, Level 3
Operational Mode: Write Back
Location: Internal
Installed Size: 25600 kB
Maximum Size: 25600 kB
Supported SRAM Types:
Unknown
Installed SRAM Type: Unknown
Speed: Unknown
Error Correction Type: Single-bit ECC
System Type: Unified
Associativity: 20-way Set-associative
Handle 0x0703, DMI type 7, 19 bytes
Cache Information
Socket Designation: Not Specified
Configuration: Enabled, Not Socketed, Level 1
Operational Mode: Write Back
Location: Internal
Installed Size: 640 kB
Maximum Size: 640 kB
Supported SRAM Types:
Unknown
Installed SRAM Type: Unknown
Speed: Unknown
Error Correction Type: Parity
System Type: Unified
Associativity: 8-way Set-associative
Handle 0x0704, DMI type 7, 19 bytes
Cache Information
Socket Designation: Not Specified
Configuration: Enabled, Not Socketed, Level 2
Operational Mode: Write Back
Location: Internal
Installed Size: 2560 kB
Maximum Size: 2560 kB
Supported SRAM Types:
Unknown
Installed SRAM Type: Unknown
Speed: Unknown
Error Correction Type: Single-bit ECC
System Type: Unified
Associativity: 8-way Set-associative
Handle 0x0705, DMI type 7, 19 bytes
Cache Information
Socket Designation: Not Specified
Configuration: Enabled, Not Socketed, Level 3
Operational Mode: Write Back
Location: Internal
Installed Size: 25600 kB
Maximum Size: 25600 kB
Supported SRAM Types:
Unknown
Installed SRAM Type: Unknown
Speed: Unknown
Error Correction Type: Single-bit ECC
System Type: Unified
Associativity: 20-way Set-associative
Handle 0x0800, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: Not Specified
Internal Connector Type: None
External Reference Designator: Back USB port 2
External Connector Type: Access Bus (USB)
Port Type: USB
Handle 0x0801, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: Internal USB port 1
Internal Connector Type: Access Bus (USB)
External Reference Designator: Not Specified
External Connector Type: None
Port Type: USB
Handle 0x0802, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: Not Specified
Internal Connector Type: None
External Reference Designator: Front USB port 2
External Connector Type: Access Bus (USB)
Port Type: USB
Handle 0x0803, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: Not Specified
Internal Connector Type: None
External Reference Designator: Back USB port 1
External Connector Type: Access Bus (USB)
Port Type: USB
Handle 0x0804, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: Not Specified
Internal Connector Type: None
External Reference Designator: Front USB port 1
External Connector Type: Access Bus (USB)
Port Type: USB
Handle 0x0805, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: Not Specified
Internal Connector Type: None
External Reference Designator: Video port 1
External Connector Type: DB-15 female
Port Type: Video Port
Handle 0x0806, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: Not Specified
Internal Connector Type: None
External Reference Designator: Serial port 1
External Connector Type: DB-9 male
Port Type: Serial Port 16550A Compatible
Handle 0x0900, DMI type 9, 17 bytes
System Slot Information
Designation: PCIe Slot 1
Type: x8 PCI Express 3
Current Usage: Available
Length: Long
Characteristics:
3.3 V is provided
PME signal is supported
Handle 0x0901, DMI type 9, 17 bytes
System Slot Information
Designation: PCIe Slot 2
Type: x8 PCI Express 3 x16
Current Usage: In Use
Length: Long
Characteristics:
3.3 V is provided
PME signal is supported
Bus Address: 0000:04:00.0
Handle 0x0902, DMI type 9, 17 bytes
System Slot Information
Designation: PCIe Slot 3
Type: x8 PCI Express 3
Current Usage: Available
Length: Long
Characteristics:
3.3 V is provided
PME signal is supported
Handle 0x0903, DMI type 9, 17 bytes
System Slot Information
Designation: PCIe Slot 4
Type: x8 PCI Express 3
Current Usage: In Use
Length: Long
Characteristics:
3.3 V is provided
PME signal is supported
Bus Address: 0000:06:00.0
Handle 0x0B00, DMI type 11, 5 bytes
OEM Strings
String 1: Dell System
String 2: 5[0000]
String 3: 1[06B6]
String 4: 7[0721]
String 5: 8[Dell Inc.]
String 6: 9[DSS7500]
String 7: 10[2.3.4]
String 8: 11[01.00.00]
String 9: 12[www.dell.com]
String 10: 14[1]
String 11: 17[8F27911A0C407F63]
String 12: 17[8F28041BFDD8E7E6]
String 13: 18[0]
String 14: 19[1]
String 15: 19[1]
Handle 0x0C00, DMI type 12, 5 bytes
System Configuration Options
Option 1: NVRAM_CLR: Clear user settable NVRAM areas and set defaults
Option 2: PWRD_EN: Close to enable password
Handle 0x0D00, DMI type 13, 22 bytes
BIOS Language Information
Language Description Format: Long
Installable Languages: 1
en|US|iso8859-1
Currently Installed Language: en|US|iso8859-1
Handle 0x1000, DMI type 16, 23 bytes
Physical Memory Array
Location: System Board Or Motherboard
Use: System Memory
Error Correction Type: Multi-bit ECC
Maximum Capacity: 1536 GB
Error Information Handle: Not Provided
Number Of Devices: 12
Handle 0x1100, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x1000
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: 1
Locator: A1
Bank Locator: Not Specified
Type: <OUT OF SPEC>
Type Detail: Synchronous Registered (Buffered)
Speed: 2400 MHz
Manufacturer: 002C00B3002C
Serial Number: 4242
Asset Tag: 00171230
Part Number: 18ASF2G72PDZ-2G3B1
Rank: 2
Configured Clock Speed: 2133 MHz
Minimum voltage: 1.200 V
Maximum voltage: 1.200 V
Configured voltage: 1.200 V
Handle 0x1101, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x1000
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: 1
Locator: A2
Bank Locator: Not Specified
Type: <OUT OF SPEC>
Type Detail: Synchronous Registered (Buffered)
Speed: 2400 MHz
Manufacturer: 002C00B3002C
Serial Number: 4242
Asset Tag: 00171230
Part Number: 18ASF2G72PDZ-2G3B1
Rank: 2
Configured Clock Speed: 2133 MHz
Minimum voltage: 1.200 V
Maximum voltage: 1.200 V
Configured voltage: 1.200 V
Handle 0x1102, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x1000
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: 1
Locator: A3
Bank Locator: Not Specified
Type: <OUT OF SPEC>
Type Detail: Synchronous Registered (Buffered)
Speed: 2400 MHz
Manufacturer: 002C00B3002C
Serial Number: 4242
Asset Tag: 00171230
Part Number: 18ASF2G72PDZ-2G3B1
Rank: 2
Configured Clock Speed: 2133 MHz
Minimum voltage: 1.200 V
Maximum voltage: 1.200 V
Configured voltage: 1.200 V
Handle 0x1103, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x1000
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: 1
Locator: A4
Bank Locator: Not Specified
Type: <OUT OF SPEC>
Type Detail: Synchronous Registered (Buffered)
Speed: 2400 MHz
Manufacturer: 002C00B3002C
Serial Number: 4242
Asset Tag: 00171230
Part Number: 18ASF2G72PDZ-2G3B1
Rank: 2
Configured Clock Speed: 2133 MHz
Minimum voltage: 1.200 V
Maximum voltage: 1.200 V
Configured voltage: 1.200 V
Handle 0x1104, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x1000
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: 2
Locator: A5
Bank Locator: Not Specified
Type: <OUT OF SPEC>
Type Detail: Synchronous Registered (Buffered)
Speed: 2400 MHz
Manufacturer: 002C00B3002C
Serial Number: 4242
Asset Tag: 00171230
Part Number: 18ASF2G72PDZ-2G3B1
Rank: 2
Configured Clock Speed: 2133 MHz
Minimum voltage: 1.200 V
Maximum voltage: 1.200 V
Configured voltage: 1.200 V
Handle 0x1105, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x1000
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: 2
Locator: A6
Bank Locator: Not Specified
Type: <OUT OF SPEC>
Type Detail: Synchronous Registered (Buffered)
Speed: 2400 MHz
Manufacturer: 002C00B3002C
Serial Number: 4242
Asset Tag: 00171230
Part Number: 18ASF2G72PDZ-2G3B1
Rank: 2
Configured Clock Speed: 2133 MHz
Minimum voltage: 1.200 V
Maximum voltage: 1.200 V
Configured voltage: 1.200 V
Handle 0x1106, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x1000
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: 2
Locator: A7
Bank Locator: Not Specified
Type: <OUT OF SPEC>
Type Detail: Synchronous Registered (Buffered)
Speed: 2400 MHz
Manufacturer: 002C00B3002C
Serial Number: 4242
Asset Tag: 00171230
Part Number: 18ASF2G72PDZ-2G3B1
Rank: 2
Configured Clock Speed: 2133 MHz
Minimum voltage: 1.200 V
Maximum voltage: 1.200 V
Configured voltage: 1.200 V
Handle 0x1107, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x1000
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: 2
Locator: A8
Bank Locator: Not Specified
Type: <OUT OF SPEC>
Type Detail: Synchronous Registered (Buffered)
Speed: 2400 MHz
Manufacturer: 002C00B3002C
Serial Number: 4242
Asset Tag: 00171230
Part Number: 18ASF2G72PDZ-2G3B1
Rank: 2
Configured Clock Speed: 2133 MHz
Minimum voltage: 1.200 V
Maximum voltage: 1.200 V
Configured voltage: 1.200 V
Handle 0x1108, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x1000
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: 3
Locator: B1
Bank Locator: Not Specified
Type: <OUT OF SPEC>
Type Detail: Synchronous Registered (Buffered)
Speed: 2400 MHz
Manufacturer: 002C00B3002C
Serial Number: 4242
Asset Tag: 00171230
Part Number: 18ASF2G72PDZ-2G3B1
Rank: 2
Configured Clock Speed: 2133 MHz
Minimum voltage: 1.200 V
Maximum voltage: 1.200 V
Configured voltage: 1.200 V
Handle 0x1109, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x1000
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: 3
Locator: B2
Bank Locator: Not Specified
Type: <OUT OF SPEC>
Type Detail: Synchronous Registered (Buffered)
Speed: 2400 MHz
Manufacturer: 002C00B3002C
Serial Number: 4242
Asset Tag: 00171230
Part Number: 18ASF2G72PDZ-2G3B1
Rank: 2
Configured Clock Speed: 2133 MHz
Minimum voltage: 1.200 V
Maximum voltage: 1.200 V
Configured voltage: 1.200 V
Handle 0x110A, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x1000
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: 3
Locator: B3
Bank Locator: Not Specified
Type: <OUT OF SPEC>
Type Detail: Synchronous Registered (Buffered)
Speed: 2400 MHz
Manufacturer: 002C00B3002C
Serial Number: 4242
Asset Tag: 00171230
Part Number: 18ASF2G72PDZ-2G3B1
Rank: 2
Configured Clock Speed: 2133 MHz
Minimum voltage: 1.200 V
Maximum voltage: 1.200 V
Configured voltage: 1.200 V
Handle 0x110B, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x1000
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: 3
Locator: B4
Bank Locator: Not Specified
Type: <OUT OF SPEC>
Type Detail: Synchronous Registered (Buffered)
Speed: 2400 MHz
Manufacturer: 002C00B3002C
Serial Number: 4242
Asset Tag: 00171230
Part Number: 18ASF2G72PDZ-2G3B1
Rank: 2
Configured Clock Speed: 2133 MHz
Minimum voltage: 1.200 V
Maximum voltage: 1.200 V
Configured voltage: 1.200 V
Handle 0x1300, DMI type 19, 31 bytes
Memory Array Mapped Address
Starting Address: 0x00000000000
Ending Address: 0x0007FFFFFFF
Range Size: 2 GB
Physical Array Handle: 0x1000
Partition Width: 2
Handle 0x1301, DMI type 19, 31 bytes
Memory Array Mapped Address
Starting Address: 0x00100000000
Ending Address: 0x0307FFFFFFF
Range Size: 190 GB
Physical Array Handle: 0x1000
Partition Width: 2
Handle 0x2000, DMI type 32, 11 bytes
System Boot Information
Status: No errors detected
Handle 0x2600, DMI type 38, 18 bytes
IPMI Device Information
Interface Type: KCS (Keyboard Control Style)
Specification Version: 2.0
I2C Slave Address: 0x10
NV Storage Device: Not Present
Base Address: 0x0000000000000CA8 (I/O)
Register Spacing: 32-bit Boundaries
Interrupt Polarity: Active High
Interrupt Trigger Mode: Edge
Interrupt Number: a
Handle 0x2700, DMI type 39, 22 bytes
System Power Supply
Location: Not Specified
Name: PWR SPLY,1600W,RDNT,DELTA
Manufacturer: DELL
Serial Number: 4242
Asset Tag: Not Specified
Model Part Number: 0685W7A00
Revision: Not Specified
Max Power Capacity: 1600 W
Status: Present, Unknown
Type: Unknown
Input Voltage Range Switching: Unknown
Plugged: Yes
Hot Replaceable: Yes
Handle 0x2701, DMI type 39, 22 bytes
System Power Supply
Location: Not Specified
Name: PWR SPLY,1600W,RDNT,DELTA
Manufacturer: DELL
Serial Number: 4242
Asset Tag: Not Specified
Model Part Number: 0685W7A00
Revision: Not Specified
Max Power Capacity: 1600 W
Status: Present, Unknown
Type: Unknown
Input Voltage Range Switching: Unknown
Plugged: Yes
Hot Replaceable: Yes
Handle 0x2900, DMI type 41, 11 bytes
Onboard Device
Reference Designation: Embedded Video
Type: Video
Status: Enabled
Type Instance: 1
Bus Address: 0000:0b:00.0
Handle 0x2901, DMI type 41, 11 bytes
Onboard Device
Reference Designation: Embedded EHCI USB Controller 1
Type: Other
Status: Enabled
Type Instance: 1
Bus Address: 0000:00:1d.0
Handle 0x2902, DMI type 41, 11 bytes
Onboard Device
Reference Designation: Embedded EHCI USB Controller 2
Type: Other
Status: Enabled
Type Instance: 2
Bus Address: 0000:00:1a.0
Handle 0xB100, DMI type 177, 12 bytes
OEM-specific Type
Header and Data:
B1 0C 00 B1 00 02 00 00 00 00 00 00
Handle 0xD000, DMI type 208, 16 bytes
OEM-specific Type
Header and Data:
D0 10 00 D0 02 00 FE 00 B6 06 00 00 00 01 00 00
Handle 0xD200, DMI type 210, 12 bytes
OEM-specific Type
Header and Data:
D2 0C 00 D2 F8 02 03 03 06 80 04 05
Handle 0xD800, DMI type 216, 9 bytes
OEM-specific Type
Header and Data:
D8 09 00 D8 01 02 00 F0 03
Strings:
Matrox
{* Content TBD *}
Handle 0xDE00, DMI type 222, 16 bytes
OEM-specific Type
Header and Data:
DE 10 00 DE 00 40 FF FF 00 00 00 00 00 01 00 00
Handle 0xE100, DMI type 225, 13 bytes
OEM-specific Type
Header and Data:
E1 0D 00 E1 01 01 00 04 00 02 01 04 00
Strings:
CPU.Socket.1
CPU.Socket.2
Handle 0xE101, DMI type 225, 53 bytes
OEM-specific Type
Header and Data:
E1 35 01 E1 01 01 00 11 00 02 01 11 00 03 02 11
00 04 03 11 00 05 04 11 00 06 05 11 00 07 06 11
00 08 07 11 00 09 08 11 00 0A 09 11 00 0B 0A 11
00 0C 0B 11 00
Strings:
DIMM.Socket.A1
DIMM.Socket.A2
DIMM.Socket.A3
DIMM.Socket.A4
DIMM.Socket.A5
DIMM.Socket.A6
DIMM.Socket.A7
DIMM.Socket.A8
DIMM.Socket.B1
DIMM.Socket.B2
DIMM.Socket.B3
DIMM.Socket.B4
Handle 0xFEFF, DMI type 127, 4 bytes
End Of Table

File diff suppressed because it is too large Load diff

1766
tests/fixtures/dmidecode/HP_BL460c_Gen10 vendored Normal file

File diff suppressed because it is too large Load diff

1709
tests/fixtures/dmidecode/HP_BL460c_Gen9 vendored Normal file

File diff suppressed because it is too large Load diff

1751
tests/fixtures/dmidecode/HP_DL380p_Gen8 vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,921 @@
# dmidecode 3.1
Getting SMBIOS data from sysfs.
SMBIOS 2.8 present.
93 structures occupying 4311 bytes.
Table at 0x766AD000.
Handle 0x0000, DMI type 7, 19 bytes
Cache Information
Socket Designation: L1-Cache
Configuration: Enabled, Not Socketed, Level 1
Operational Mode: Write Back
Location: Internal
Installed Size: 256 kB
Maximum Size: 256 kB
Supported SRAM Types:
Synchronous
Installed SRAM Type: Synchronous
Speed: Unknown
Error Correction Type: Single-bit ECC
System Type: Unified
Associativity: 8-way Set-associative
Handle 0x0001, DMI type 7, 19 bytes
Cache Information
Socket Designation: L2-Cache
Configuration: Enabled, Not Socketed, Level 2
Operational Mode: Varies With Memory Address
Location: Internal
Installed Size: 1024 kB
Maximum Size: 1024 kB
Supported SRAM Types:
Synchronous
Installed SRAM Type: Synchronous
Speed: Unknown
Error Correction Type: Single-bit ECC
System Type: Unified
Associativity: 4-way Set-associative
Handle 0x0002, DMI type 7, 19 bytes
Cache Information
Socket Designation: L3-Cache
Configuration: Enabled, Not Socketed, Level 3
Operational Mode: Varies With Memory Address
Location: Internal
Installed Size: 8192 kB
Maximum Size: 8192 kB
Supported SRAM Types:
Synchronous
Installed SRAM Type: Synchronous
Speed: Unknown
Error Correction Type: Single-bit ECC
System Type: Unified
Associativity: 16-way Set-associative
Handle 0x0003, DMI type 4, 48 bytes
Processor Information
Socket Designation: Proc 1
Type: Central Processor
Family: Xeon
Manufacturer: Intel(R) Corporation
ID: E3 06 05 00 FF FB EB BF
Signature: Type 0, Family 6, Model 94, Stepping 3
Flags:
FPU (Floating-point unit on-chip)
VME (Virtual mode extension)
DE (Debugging extension)
PSE (Page size extension)
TSC (Time stamp counter)
MSR (Model specific registers)
PAE (Physical address extension)
MCE (Machine check exception)
CX8 (CMPXCHG8 instruction supported)
APIC (On-chip APIC hardware supported)
SEP (Fast system call)
MTRR (Memory type range registers)
PGE (Page global enable)
MCA (Machine check architecture)
CMOV (Conditional move instruction supported)
PAT (Page attribute table)
PSE-36 (36-bit page size extension)
CLFSH (CLFLUSH instruction supported)
DS (Debug store)
ACPI (ACPI supported)
MMX (MMX technology supported)
FXSR (FXSAVE and FXSTOR instructions supported)
SSE (Streaming SIMD extensions)
SSE2 (Streaming SIMD extensions 2)
SS (Self-snoop)
HTT (Multi-threading)
TM (Thermal monitor supported)
PBE (Pending break enabled)
Version: Intel(R) Xeon(R) CPU E3-1585L v5 @ 3.00GHz
Voltage: 1.0 V
External Clock: 100 MHz
Max Speed: 3700 MHz
Current Speed: 3000 MHz
Status: Populated, Enabled
Upgrade: Other
L1 Cache Handle: 0x0000
L2 Cache Handle: 0x0001
L3 Cache Handle: 0x0002
Serial Number: To Be Filled By O.E.M.
Asset Tag: To Be Filled By O.E.M.
Part Number: To Be Filled By O.E.M.
Core Count: 4
Core Enabled: 4
Thread Count: 8
Characteristics:
64-bit capable
Multi-Core
Hardware Thread
Execute Protection
Enhanced Virtualization
Power/Performance Control
Handle 0x0004, DMI type 0, 24 bytes
BIOS Information
Vendor: HP
Version: H07
Release Date: 05/23/2016
Address: 0xF0000
Runtime Size: 64 kB
ROM Size: 16 MB
Characteristics:
PCI is supported
PNP is supported
BIOS is upgradeable
BIOS shadowing is allowed
ESCD support is available
Boot from CD is supported
Selectable boot is supported
EDD is supported
5.25"/360 kB floppy services are supported (int 13h)
5.25"/1.2 MB floppy services are supported (int 13h)
3.5"/720 kB floppy services are supported (int 13h)
Print screen service is supported (int 5h)
8042 keyboard services are supported (int 9h)
Serial services are supported (int 14h)
Printer services are supported (int 17h)
CGA/mono video services are supported (int 10h)
ACPI is supported
USB legacy is supported
BIOS boot specification is supported
Function key-initiated network boot is supported
Targeted content distribution is supported
UEFI is supported
BIOS Revision: 1.0
Firmware Revision: 2.40
Handle 0x0005, DMI type 1, 27 bytes
System Information
Manufacturer: HP
Product Name: ProLiant m710x Server Cartridge
Version: Not Specified
Serial Number: CN66480BLA
UUID: CF6D6DE0-A5AB-512C-BCD4-5CF40EC490C1
Wake-up Type: Power Switch
SKU Number: 833105-B21
Family: ProLiant
Handle 0x0006, DMI type 16, 23 bytes
Physical Memory Array
Location: System Board Or Motherboard
Use: System Memory
Error Correction Type: Multi-bit ECC
Maximum Capacity: 64 GB
Error Information Handle: Not Provided
Number Of Devices: 4
Handle 0x0007, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x0006
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: None
Locator: PROC 1 DIMM 1
Bank Locator: Not Specified
Type: DDR4
Type Detail: Synchronous
Speed: 2400 MT/s
Manufacturer: HP
Serial Number: Not Specified
Asset Tag: Not Specified
Part Number: 853289-091
Rank: 2
Configured Clock Speed: 2133 MT/s
Minimum Voltage: 1.2 V
Maximum Voltage: 1.2 V
Configured Voltage: 1.2 V
Handle 0x0008, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x0006
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: 1
Locator: PROC 1 DIMM 2
Bank Locator: Not Specified
Type: DDR4
Type Detail: Synchronous
Speed: 2400 MT/s
Manufacturer: HP
Serial Number: Not Specified
Asset Tag: Not Specified
Part Number: 853289-091
Rank: 2
Configured Clock Speed: 2133 MT/s
Minimum Voltage: 1.2 V
Maximum Voltage: 1.2 V
Configured Voltage: 1.2 V
Handle 0x0009, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x0006
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: 2
Locator: PROC 1 DIMM 3
Bank Locator: Not Specified
Type: DDR4
Type Detail: Synchronous
Speed: 2400 MT/s
Manufacturer: HP
Serial Number: Not Specified
Asset Tag: Not Specified
Part Number: 853289-091
Rank: 2
Configured Clock Speed: 2133 MT/s
Minimum Voltage: 1.2 V
Maximum Voltage: 1.2 V
Configured Voltage: 1.2 V
Handle 0x000A, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x0006
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: 3
Locator: PROC 1 DIMM 4
Bank Locator: Not Specified
Type: DDR4
Type Detail: Synchronous
Speed: 2400 MT/s
Manufacturer: HP
Serial Number: Not Specified
Asset Tag: Not Specified
Part Number: 853289-091
Rank: 2
Configured Clock Speed: 2133 MT/s
Minimum Voltage: 1.2 V
Maximum Voltage: 1.2 V
Configured Voltage: 1.2 V
Handle 0x000B, DMI type 19, 31 bytes
Memory Array Mapped Address
Starting Address: 0x00000000000
Ending Address: 0x0007E7FFFFF
Range Size: 2024 MB
Physical Array Handle: 0x0006
Partition Width: 1
Handle 0x000C, DMI type 19, 31 bytes
Memory Array Mapped Address
Starting Address: 0x0000000100000000k
Ending Address: 0x00000010817FFFFFk
Range Size: 63512 MB
Physical Array Handle: 0x0006
Partition Width: 1
Handle 0x000D, DMI type 211, 7 bytes
OEM-specific Type
Header and Data:
D3 07 0D 00 03 00 00
Handle 0x000E, DMI type 2, 17 bytes
Base Board Information
Manufacturer: HP
Product Name: ProLiant m710x Server Cartridge
Version: Not Specified
Serial Number: CN66480ANY
Asset Tag:
Features:
Board is a hosting board
Board is removable
Board is replaceable
Location In Chassis: c2n1
Chassis Handle: 0x0000
Type: Motherboard
Contained Object Handles: 0
Handle 0x000F, DMI type 3, 21 bytes
Chassis Information
Manufacturer: HP
Type: Multi-system
Lock: Not Present
Version: Not Specified
Serial Number: CZ3702MD5K
Asset Tag:
Boot-up State: Safe
Power Supply State: Safe
Thermal State: Safe
Security Status: Unknown
OEM Information: 0x00000000
Height: 5 U
Number Of Power Cords: Unspecified
Contained Elements: 0
Handle 0x0010, DMI type 11, 5 bytes
OEM Strings
String 1: PSF:
String 2: Product ID: 833105-B21
String 3: CPN: HP Moonshot 1500 Chassis
String 4: OEM String:
Handle 0x0011, DMI type 38, 18 bytes
IPMI Device Information
Interface Type: KCS (Keyboard Control Style)
Specification Version: 2.0
I2C Slave Address: 0x10
NV Storage Device: Not Present
Base Address: 0x0000000000000CA2 (I/O)
Register Spacing: Successive Byte Boundaries
Handle 0x0012, DMI type 193, 9 bytes
OEM-specific Type
Header and Data:
C1 09 12 00 00 01 00 02 03
Strings:
0/0000
Handle 0x0013, DMI type 194, 5 bytes
OEM-specific Type
Header and Data:
C2 05 13 00 10
Handle 0x0014, DMI type 195, 7 bytes
OEM-specific Type
Header and Data:
C3 07 14 00 01 12 01
Strings:
$0E110855
Handle 0x0015, DMI type 197, 16 bytes
OEM-specific Type
Header and Data:
C5 10 15 00 03 00 00 01 FF 01 2D 00 00 00 00 00
Handle 0x0016, DMI type 198, 14 bytes
OEM-specific Type
Header and Data:
C6 0E 16 00 17 00 00 00 00 00 01 0A FF FF
Handle 0x0017, DMI type 199, 28 bytes
OEM-specific Type
Header and Data:
C7 1C 17 00 2C 00 00 00 15 20 01 07 E2 06 05 00
88 00 00 00 16 20 16 03 E3 06 05 00
Handle 0x0018, DMI type 201, 16 bytes
OEM-specific Type
Header and Data:
C9 10 18 00 10 02 00 00 40 0D 01 00 0E 00 00 80
Handle 0x0019, DMI type 215, 6 bytes
OEM-specific Type
Header and Data:
D7 06 19 00 00 05
Handle 0x001A, DMI type 216, 23 bytes
OEM-specific Type
Header and Data:
D8 17 1A 00 01 00 01 02 07 01 00 05 17 E0 07 00
00 00 00 00 00 00 00
Strings:
System ROM
v1.00 (05/23/2016)
Handle 0x001B, DMI type 216, 23 bytes
OEM-specific Type
Header and Data:
D8 17 1B 00 04 00 01 02 04 00 00 00 00 00 00 00
00 00 00 00 00 00 00
Strings:
Power Management Controller Firmware
0.0
Handle 0x001C, DMI type 216, 23 bytes
OEM-specific Type
Header and Data:
D8 17 1C 00 05 00 01 02 02 60 00 00 00 00 00 00
00 00 00 00 00 00 00
Strings:
Power Management Controller FW Bootloader
6.0
Handle 0x001D, DMI type 216, 23 bytes
OEM-specific Type
Header and Data:
D8 17 1D 00 08 00 01 00 01 09 09 00 00 00 00 00
00 00 00 00 00 00 00
Strings:
System Programmable Logic Device
Handle 0x001E, DMI type 216, 23 bytes
OEM-specific Type
Header and Data:
D8 17 1E 00 09 00 01 00 03 04 00 00 00 03 00 96
00 0A 00 00 00 01 00
Strings:
Server Platform Services (SPS) Firmware
Handle 0x001F, DMI type 219, 32 bytes
HP ProLiant Information
Power Features: 0x000000df
Omega Features: 0x00000000
Misc. Features: 0x00009001
iCRU: Yes
UEFI: No
Handle 0x0020, DMI type 223, 11 bytes
OEM-specific Type
Header and Data:
DF 0B 20 00 66 46 70 00 00 00 00
Handle 0x0021, DMI type 224, 10 bytes
OEM-specific Type
Header and Data:
E0 0A 21 00 00 00 02 03 FE FF
Handle 0x0022, DMI type 226, 21 bytes
OEM-specific Type
Header and Data:
E2 15 22 00 38 33 33 31 30 35 43 4E 36 36 34 38
30 41 4E 59 01
Strings:
CN66480ANY
Handle 0x0023, DMI type 227, 22 bytes
OEM-specific Type
Header and Data:
E3 16 23 00 03 00 07 00 00 A0 01 00 FF FF FF FF
00 00 00 00 00 00
Handle 0x0024, DMI type 227, 22 bytes
OEM-specific Type
Header and Data:
E3 16 24 00 03 00 08 00 00 A2 01 00 FF FF FF FF
00 00 00 00 00 00
Handle 0x0025, DMI type 227, 22 bytes
OEM-specific Type
Header and Data:
E3 16 25 00 03 00 09 00 00 A4 01 00 FF FF FF FF
01 00 00 00 00 00
Handle 0x0026, DMI type 227, 22 bytes
OEM-specific Type
Header and Data:
E3 16 26 00 03 00 0A 00 00 A6 01 00 FF FF FF FF
01 00 00 00 00 00
Handle 0x0027, DMI type 228, 14 bytes
OEM-specific Type
Header and Data:
E4 0E 27 00 00 00 00 00 00 00 00 FF 00 00
Handle 0x0028, DMI type 228, 14 bytes
OEM-specific Type
Header and Data:
E4 0E 28 00 01 00 00 00 00 00 00 FF 00 00
Handle 0x0029, DMI type 228, 14 bytes
OEM-specific Type
Header and Data:
E4 0E 29 00 02 00 00 00 00 00 00 FF 00 00
Handle 0x002A, DMI type 228, 14 bytes
OEM-specific Type
Header and Data:
E4 0E 2A 00 03 00 00 00 00 00 00 FF 00 00
Handle 0x002B, DMI type 228, 14 bytes
OEM-specific Type
Header and Data:
E4 0E 2B 00 04 00 00 00 00 00 00 FF 00 00
Handle 0x002C, DMI type 228, 14 bytes
OEM-specific Type
Header and Data:
E4 0E 2C 00 05 00 00 00 00 00 00 FF 00 00
Handle 0x002D, DMI type 228, 14 bytes
OEM-specific Type
Header and Data:
E4 0E 2D 00 06 00 00 00 00 00 00 FF 00 00
Handle 0x002E, DMI type 228, 14 bytes
OEM-specific Type
Header and Data:
E4 0E 2E 00 07 00 00 00 00 00 00 FF 00 00
Handle 0x002F, DMI type 228, 14 bytes
OEM-specific Type
Header and Data:
E4 0E 2F 00 08 06 00 00 00 00 00 FF 01 00
Handle 0x0030, DMI type 229, 100 bytes
OEM-specific Type
Header and Data:
E5 64 30 00 24 4F 43 53 00 B0 F1 77 00 00 00 00
00 40 00 00 24 4F 43 42 00 50 F0 77 00 00 00 00
00 60 01 00 24 48 44 44 00 30 F0 77 00 00 00 00
00 20 00 00 24 57 48 45 00 20 EF 77 00 00 00 00
00 10 00 00 24 53 4D 56 98 2E F2 77 00 00 00 00
08 00 00 00 24 5A 58 54 00 10 EF 77 00 00 00 00
3B 00 00 00
Handle 0x0031, DMI type 232, 14 bytes
OEM-specific Type
Header and Data:
E8 0E 31 00 07 00 11 00 00 00 B0 04 B0 04
Handle 0x0032, DMI type 232, 14 bytes
OEM-specific Type
Header and Data:
E8 0E 32 00 08 00 11 00 00 00 B0 04 B0 04
Handle 0x0033, DMI type 232, 14 bytes
OEM-specific Type
Header and Data:
E8 0E 33 00 09 00 11 00 00 00 B0 04 B0 04
Handle 0x0034, DMI type 232, 14 bytes
OEM-specific Type
Header and Data:
E8 0E 34 00 0A 00 11 00 00 00 B0 04 B0 04
Handle 0x0035, DMI type 237, 9 bytes
OEM-specific Type
Header and Data:
ED 09 35 00 07 00 01 02 03
Strings:
Hynix
HMA82GS7AFR8N-UH
31F9E4B8
Handle 0x0036, DMI type 237, 9 bytes
OEM-specific Type
Header and Data:
ED 09 36 00 08 00 01 02 03
Strings:
Hynix
HMA82GS7AFR8N-UH
31F9E4B6
Handle 0x0037, DMI type 237, 9 bytes
OEM-specific Type
Header and Data:
ED 09 37 00 09 00 01 02 03
Strings:
Hynix
HMA82GS7AFR8N-UH
31F9E96B
Handle 0x0038, DMI type 237, 9 bytes
OEM-specific Type
Header and Data:
ED 09 38 00 0A 00 01 02 03
Strings:
Hynix
HMA82GS7AFR8N-UH
31F9E4B9
Handle 0x0039, DMI type 9, 17 bytes
System Slot Information
Designation: PCI-E Slot 2
Type: x4 PCI Express 2
Current Usage: Available
Length: Other
ID: 2
Characteristics:
3.3 V is provided
PME signal is supported
Bus Address: 0000:02:00.0
Handle 0x003A, DMI type 9, 17 bytes
System Slot Information
Designation: PCI-E Slot 1
Type: x1 PCI Express 2
Current Usage: Available
Length: Other
ID: 1
Characteristics:
3.3 V is provided
PME signal is supported
Bus Address: 0000:05:00.0
Handle 0x003B, DMI type 9, 17 bytes
System Slot Information
Designation: PCI-E Slot 5
Type: x2 PCI Express 2
Current Usage: Available
Length: Other
ID: 5
Characteristics:
3.3 V is provided
PME signal is supported
Bus Address: 0000:08:00.0
Handle 0x003C, DMI type 9, 17 bytes
System Slot Information
Designation: PCI-E Slot 0
Type: x1 PCI Express 3 x4
Current Usage: Available
Length: Unknown
ID: 0
Characteristics:
3.3 V is provided
PME signal is supported
Bus Address: 0000:fe:00.0
Handle 0x003D, DMI type 9, 17 bytes
System Slot Information
Designation: PCI-E Slot 0
Type: x1 PCI Express 3 x4
Current Usage: Available
Length: Unknown
ID: 0
Characteristics:
3.3 V is provided
PME signal is supported
Bus Address: 0000:fe:00.0
Handle 0x003E, DMI type 9, 17 bytes
System Slot Information
Designation: PCI-E Slot 4
Type: x4 PCI Express 2
Current Usage: Available
Length: Unknown
ID: 4
Characteristics:
3.3 V is provided
PME signal is supported
Bus Address: 0000:0b:00.0
Handle 0x003F, DMI type 9, 17 bytes
System Slot Information
Designation: PCI-E Slot 3
Type: x4 PCI Express 2
Current Usage: In Use
Length: Other
ID: 3
Characteristics:
3.3 V is provided
PME signal is supported
Bus Address: 0000:0e:00.0
Handle 0x0040, DMI type 9, 17 bytes
System Slot Information
Designation: PCI-E Slot 1
Type: x4 PCI Express 3 x4
Current Usage: Available
Length: Unknown
ID: 1
Characteristics:
3.3 V is provided
PME signal is supported
Bus Address: 0000:fe:00.0
Handle 0x0041, DMI type 9, 17 bytes
System Slot Information
Designation: PCI-E Slot 1
Type: x4 PCI Express 3 x4
Current Usage: Available
Length: Unknown
ID: 1
Characteristics:
3.3 V is provided
PME signal is supported
Bus Address: 0000:fe:00.0
Handle 0x0042, DMI type 233, 41 bytes
HP BIOS PXE NIC PCI and MAC Information
NIC 1: PCI device 11:00.0, MAC address 00:FD:45:50:6C:01
Handle 0x0043, DMI type 233, 41 bytes
HP BIOS PXE NIC PCI and MAC Information
NIC 2: PCI device 11:00.0, MAC address 00:FD:45:50:6C:02
Handle 0x0044, DMI type 32, 11 bytes
System Boot Information
Status: No errors detected
Handle 0x0045, DMI type 196, 15 bytes
OEM-specific Type
Header and Data:
C4 0F 45 00 00 00 00 00 00 00 02 01 00 01 02
Handle 0x0046, DMI type 41, 11 bytes
Onboard Device
Reference Designation: Embedded LOM 1 Port 1
Type: Ethernet
Status: Enabled
Type Instance: 1
Bus Address: 0000:11:00.0
Handle 0x0047, DMI type 41, 11 bytes
Onboard Device
Reference Designation: Embedded LOM 1 Port 2
Type: Ethernet
Status: Enabled
Type Instance: 2
Bus Address: 0000:11:00.0
Handle 0x0048, DMI type 41, 11 bytes
Onboard Device
Reference Designation: Embedded SATA Controller #1
Type: SATA Controller
Status: Enabled
Type Instance: 1
Bus Address: 0000:00:17.0
Handle 0x0049, DMI type 202, 13 bytes
OEM-specific Type
Header and Data:
CA 0D 49 00 07 00 FF 01 01 01 00 00 00
Handle 0x004A, DMI type 202, 13 bytes
OEM-specific Type
Header and Data:
CA 0D 4A 00 08 00 FF 02 01 02 00 00 00
Handle 0x004B, DMI type 202, 13 bytes
OEM-specific Type
Header and Data:
CA 0D 4B 00 09 00 FF 03 01 03 00 00 00
Handle 0x004C, DMI type 202, 13 bytes
OEM-specific Type
Header and Data:
CA 0D 4C 00 0A 00 FF 04 01 04 00 00 00
Handle 0x004D, DMI type 203, 34 bytes
OEM-specific Type
Header and Data:
CB 22 4D 00 39 00 FE FF FF FF FF FF FF FF FF FF
FF FF FE FF 00 00 09 0A 02 01 FF FF 01 02 03 04
FE FF
Strings:
PciRoot(0x0)/Pci(0x1D,0x0)/Pci(0x0,0x0)
PCI.Slot.2.1
Empty slot 2
Slot 2
Handle 0x004E, DMI type 203, 34 bytes
OEM-specific Type
Header and Data:
CB 22 4E 00 3A 00 FE FF FF FF FF FF FF FF FF FF
FF FF FE FF 00 00 09 0A 01 01 FF FF 01 02 03 04
FE FF
Strings:
PciRoot(0x0)/Pci(0x1D,0x5)/Pci(0x0,0x0)
PCI.Slot.1.1
Empty slot 1
Slot 1
Handle 0x004F, DMI type 203, 34 bytes
OEM-specific Type
Header and Data:
CB 22 4F 00 3B 00 FE FF FF FF FF FF FF FF FF FF
FF FF FE FF 00 00 09 0A 05 01 FF FF 01 02 03 04
FE FF
Strings:
PciRoot(0x0)/Pci(0x1C,0x0)/Pci(0x0,0x0)
PCI.Slot.5.1
Empty slot 5
Slot 5
Handle 0x0050, DMI type 203, 34 bytes
OEM-specific Type
Header and Data:
CB 22 50 00 3C 00 FE FF FF FF FF FF FF FF FF FF
FF FF FE FF 00 00 09 0A 01 01 FF FF 01 02 03 04
FE FF
Strings:
PciRoot(0x0)/Pci(0x1C,0x2)/Pci(0x0,0x0)
PCI.Slot.1.1
Unknown
Slot 0
Handle 0x0051, DMI type 203, 34 bytes
OEM-specific Type
Header and Data:
CB 22 51 00 3D 00 FE FF FF FF FF FF FF FF FF FF
FF FF FE FF 00 00 09 0A 01 01 FF FF 01 02 03 04
FE FF
Strings:
PciRoot(0x0)/Pci(0x1C,0x2)/Pci(0x0,0x0)
PCI.Slot.1.1
Unknown
Slot 0
Handle 0x0052, DMI type 203, 34 bytes
OEM-specific Type
Header and Data:
CB 22 52 00 3E 00 FE FF FF FF FF FF FF FF FF FF
FF FF FE FF 00 00 09 0A 04 01 FF FF 01 02 03 04
FE FF
Strings:
PciRoot(0x0)/Pci(0x1C,0x4)/Pci(0x0,0x0)
PCI.Slot.4.1
Empty slot 4
Slot 4
Handle 0x0053, DMI type 203, 34 bytes
OEM-specific Type
Header and Data:
CB 22 53 00 3F 00 FE FF 79 11 0F 01 79 11 01 00
01 08 FE FF 00 00 10 0A 03 01 FF FF 01 02 03 04
FE FF
Strings:
PciRoot(0x0)/Pci(0x1B,0x0)/Pci(0x0,0x0)
NVMe.Slot.3.1
NVM Express Controller
Slot 3
Handle 0x0054, DMI type 203, 34 bytes
OEM-specific Type
Header and Data:
CB 22 54 00 40 00 FE FF FF FF FF FF FF FF FF FF
FF FF FE FF 00 00 09 0A 01 01 FF FF 01 02 03 04
FE FF
Strings:
PciRoot(0x0)/Pci(0x1C,0x2)/Pci(0x0,0x0)
PCI.Slot.1.1
Empty slot 1
Slot 0
Handle 0x0055, DMI type 203, 34 bytes
OEM-specific Type
Header and Data:
CB 22 55 00 41 00 FE FF FF FF FF FF FF FF FF FF
FF FF FE FF 00 00 09 0A 01 01 FF FF 01 02 03 04
FE FF
Strings:
PciRoot(0x0)/Pci(0x1C,0x2)/Pci(0x0,0x0)
PCI.Slot.1.1
Empty slot 1
Slot 0
Handle 0x0056, DMI type 203, 34 bytes
OEM-specific Type
Header and Data:
CB 22 56 00 46 00 FE FF B3 15 07 10 90 15 04 22
02 00 FE FF 00 00 04 01 01 01 FF FF 01 02 03 04
FE FF
Strings:
PciRoot(0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Ctrl(0x1)
NIC.LOM.1.1
Port 1 - Mellanox Network Adapter
Embedded LOM 1
Handle 0x0057, DMI type 203, 34 bytes
OEM-specific Type
Header and Data:
CB 22 57 00 47 00 FE FF B3 15 07 10 90 15 04 22
02 00 FE FF 00 00 04 01 01 02 FF FF 01 02 03 04
FE FF
Strings:
PciRoot(0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Ctrl(0x2)
NIC.LOM.1.2
Port 2 - Mellanox Network Adapter
Embedded LOM 1
Handle 0x0058, DMI type 203, 34 bytes
OEM-specific Type
Header and Data:
CB 22 58 00 48 00 FE FF 86 80 02 A1 3C 10 65 81
01 06 FE FF 00 00 06 08 01 01 FF FF 01 02 03 04
FE FF
Strings:
PciRoot(0x0)/Pci(0x17,0x0)
SATA.Emb.1.1
Embedded SATA Controller #1
Embedded SATA Controller #1
Handle 0x0059, DMI type 234, 16 bytes
OEM-specific Type
Header and Data:
EA 10 59 00 FE FF C0 00 01 A0 00 00 00 00 00 00
Handle 0x005A, DMI type 234, 12 bytes
OEM-specific Type
Header and Data:
EA 0C 5A 00 27 00 60 03 01 01 00 00
Handle 0x005B, DMI type 240, 39 bytes
OEM-specific Type
Header and Data:
F0 27 5B 00 56 00 18 1E 24 00 01 08 48 0D 00 00
00 00 00 0F 00 00 00 00 00 00 00 0B 00 00 00 00
00 00 00 02 00 00 00
Strings:
02.36.70.00
Handle 0xFEFF, DMI type 127, 4 bytes
End Of Table

1364
tests/fixtures/dmidecode/HP_SL4540_Gen8 vendored Normal file

File diff suppressed because it is too large Load diff

526
tests/fixtures/dmidecode/QCT_X10E-9N vendored Normal file
View file

@ -0,0 +1,526 @@
# dmidecode 3.0
Getting SMBIOS data from sysfs.
SMBIOS 3.0 present.
37 structures occupying 2615 bytes.
Table at 0x8F9C5000.
Handle 0x0000, DMI type 0, 24 bytes
BIOS Information
Vendor: American Megatrends Inc.
Version: S3E_3B09.02
Release Date: 02/23/2018
Address: 0xF0000
Runtime Size: 64 kB
ROM Size: 8192 kB
Characteristics:
PCI is supported
BIOS is upgradeable
BIOS shadowing is allowed
Boot from CD is supported
Selectable boot is supported
BIOS ROM is socketed
EDD is supported
Print screen service is supported (int 5h)
Serial services are supported (int 14h)
Printer services are supported (int 17h)
ACPI is supported
USB legacy is supported
BIOS boot specification is supported
Targeted content distribution is supported
UEFI is supported
BIOS Revision: 5.11
Firmware Revision: 3.20
Handle 0x0001, DMI type 1, 27 bytes
System Information
Manufacturer: Quanta Cloud Technology Inc.
Product Name: QuantaMicro X10E-9N
Version: N/A
Serial Number: QTFCQ57140285
UUID: E1A3D1C8-0809-E711-B48A-A81E84729878
Wake-up Type: Power Switch
SKU Number: S3E
Family: Default string
Handle 0x0002, DMI type 2, 15 bytes
Base Board Information
Manufacturer: Quanta Cloud Technology Inc.
Product Name: S3E-MB
Version: 31S3EMB0010
Serial Number: CQ571000415
Asset Tag: N/A
Features:
Board is a hosting board
Board is replaceable
Location In Chassis: 4
Chassis Handle: 0x0003
Type: Motherboard
Contained Object Handles: 0
Handle 0x0003, DMI type 3, 22 bytes
Chassis Information
Manufacturer: Quanta Cloud Technology Inc.
Type: Rack Mount Chassis
Lock: Not Present
Version: 32S3ECAST00
Serial Number: QTFCQ571402FD
Asset Tag: N/A
Boot-up State: Safe
Power Supply State: Safe
Thermal State: Safe
Security Status: None
OEM Information: 0x00000000
Height: 3 U
Number Of Power Cords: 2
Contained Elements: 0
SKU Number: Default string
Handle 0x0021, DMI type 11, 5 bytes
OEM Strings
String 1: Default string
String 2: Default string
String 3: Default string
String 4: Default string
String 5: Default string
Handle 0x0025, DMI type 38, 18 bytes
IPMI Device Information
Interface Type: KCS (Keyboard Control Style)
Specification Version: 2.0
I2C Slave Address: 0x10
NV Storage Device: Not Present
Base Address: 0x0000000000000CA2 (I/O)
Register Spacing: Successive Byte Boundaries
Handle 0x0034, DMI type 7, 19 bytes
Cache Information
Socket Designation: L1 Cache
Configuration: Enabled, Not Socketed, Level 1
Operational Mode: Write Back
Location: Internal
Installed Size: 256 kB
Maximum Size: 256 kB
Supported SRAM Types:
Synchronous
Installed SRAM Type: Synchronous
Speed: Unknown
Error Correction Type: Parity
System Type: Other
Associativity: 8-way Set-associative
Handle 0x0035, DMI type 7, 19 bytes
Cache Information
Socket Designation: L2 Cache
Configuration: Enabled, Not Socketed, Level 2
Operational Mode: Write Back
Location: Internal
Installed Size: 1024 kB
Maximum Size: 1024 kB
Supported SRAM Types:
Synchronous
Installed SRAM Type: Synchronous
Speed: Unknown
Error Correction Type: Single-bit ECC
System Type: Unified
Associativity: 4-way Set-associative
Handle 0x0036, DMI type 7, 19 bytes
Cache Information
Socket Designation: L3 Cache
Configuration: Enabled, Not Socketed, Level 3
Operational Mode: Write Back
Location: Internal
Installed Size: 8192 kB
Maximum Size: 8192 kB
Supported SRAM Types:
Synchronous
Installed SRAM Type: Synchronous
Speed: Unknown
Error Correction Type: Multi-bit ECC
System Type: Unified
Associativity: 16-way Set-associative
Handle 0x0037, DMI type 4, 48 bytes
Processor Information
Socket Designation: CPU
Type: Central Processor
Family: Xeon
Manufacturer: Intel(R) Corporation
ID: E9 06 09 00 FF FB EB BF
Signature: Type 0, Family 6, Model 158, Stepping 9
Flags:
FPU (Floating-point unit on-chip)
VME (Virtual mode extension)
DE (Debugging extension)
PSE (Page size extension)
TSC (Time stamp counter)
MSR (Model specific registers)
PAE (Physical address extension)
MCE (Machine check exception)
CX8 (CMPXCHG8 instruction supported)
APIC (On-chip APIC hardware supported)
SEP (Fast system call)
MTRR (Memory type range registers)
PGE (Page global enable)
MCA (Machine check architecture)
CMOV (Conditional move instruction supported)
PAT (Page attribute table)
PSE-36 (36-bit page size extension)
CLFSH (CLFLUSH instruction supported)
DS (Debug store)
ACPI (ACPI supported)
MMX (MMX technology supported)
FXSR (FXSAVE and FXSTOR instructions supported)
SSE (Streaming SIMD extensions)
SSE2 (Streaming SIMD extensions 2)
SS (Self-snoop)
HTT (Multi-threading)
TM (Thermal monitor supported)
PBE (Pending break enabled)
Version: Intel(R) Xeon(R) CPU E3-1240 v6 @ 3.70GHz
Voltage: 1.0 V
External Clock: 100 MHz
Max Speed: 4100 MHz
Current Speed: 3700 MHz
Status: Populated, Enabled
Upgrade: Other
L1 Cache Handle: 0x0034
L2 Cache Handle: 0x0035
L3 Cache Handle: 0x0036
Serial Number: To Be Filled By O.E.M.
Asset Tag: To Be Filled By O.E.M.
Part Number: To Be Filled By O.E.M.
Core Count: 4
Core Enabled: 4
Thread Count: 8
Characteristics:
64-bit capable
Multi-Core
Hardware Thread
Execute Protection
Enhanced Virtualization
Power/Performance Control
Handle 0x0038, DMI type 16, 23 bytes
Physical Memory Array
Location: System Board Or Motherboard
Use: System Memory
Error Correction Type: Single-bit ECC
Maximum Capacity: 64 GB
Error Information Handle: Not Provided
Number Of Devices: 4
Handle 0x0039, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x0038
Error Information Handle: Not Provided
Total Width: Unknown
Data Width: Unknown
Size: No Module Installed
Form Factor: Unknown
Set: None
Locator: DIMM A1
Bank Locator: CHANNEL A
Type: Unknown
Type Detail: None
Speed: Unknown
Manufacturer: Not Specified
Serial Number: Not Specified
Asset Tag: Not Specified
Part Number: Not Specified
Rank: Unknown
Configured Clock Speed: Unknown
Minimum Voltage: Unknown
Maximum Voltage: Unknown
Configured Voltage: Unknown
Handle 0x003A, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x0038
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: None
Locator: DIMM A0
Bank Locator: CHANNEL A
Type: DDR4
Type Detail: Synchronous
Speed: 2133 MHz
Manufacturer: Samsung
Serial Number: 328CBA1D
Asset Tag: 9876543210
Part Number: M391A2K43BB1-CPB
Rank: 2
Configured Clock Speed: 2133 MHz
Minimum Voltage: 1.2 V
Maximum Voltage: 1.2 V
Configured Voltage: 1.2 V
Handle 0x003B, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x0038
Error Information Handle: Not Provided
Total Width: Unknown
Data Width: Unknown
Size: No Module Installed
Form Factor: Unknown
Set: None
Locator: DIMM B1
Bank Locator: CHANNEL B
Type: Unknown
Type Detail: None
Speed: Unknown
Manufacturer: Not Specified
Serial Number: Not Specified
Asset Tag: Not Specified
Part Number: Not Specified
Rank: Unknown
Configured Clock Speed: Unknown
Minimum Voltage: Unknown
Maximum Voltage: Unknown
Configured Voltage: Unknown
Handle 0x003C, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x0038
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: None
Locator: DIMM B0
Bank Locator: CHANNEL B
Type: DDR4
Type Detail: Synchronous
Speed: 2133 MHz
Manufacturer: Samsung
Serial Number: 328CB9DF
Asset Tag: 9876543210
Part Number: M391A2K43BB1-CPB
Rank: 2
Configured Clock Speed: 2133 MHz
Minimum Voltage: 1.2 V
Maximum Voltage: 1.2 V
Configured Voltage: 1.2 V
Handle 0x003D, DMI type 19, 31 bytes
Memory Array Mapped Address
Starting Address: 0x00000000000
Ending Address: 0x007FFFFFFFF
Range Size: 32 GB
Physical Array Handle: 0x0038
Partition Width: 2
Handle 0x003E, DMI type 221, 26 bytes
OEM-specific Type
Header and Data:
DD 1A 3E 00 03 01 00 04 01 00 08 00 02 00 00 00
00 84 00 03 00 01 03 00 00 00
Strings:
Reference Code - CPU
uCode Version
TXT ACM version
Handle 0x003F, DMI type 221, 68 bytes
OEM-specific Type
Header and Data:
DD 44 3F 00 09 01 00 04 01 00 08 00 02 03 FF FF
FF FF FF 04 00 FF FF FF 31 00 05 00 FF FF FF 31
00 06 00 FF FF FF FF FF 07 00 3E 00 00 00 00 08
00 34 00 00 00 00 09 00 3E 00 00 00 00 0A 00 34
00 00 00 00
Strings:
Reference Code - SKL PCH
PCH-CRID Status
Disabled
PCH-CRID Original Value
PCH-CRID New Value
OPROM - RST - RAID
SKL PCH H Bx Hsio Version
SKL PCH H Dx Hsio Version
SKL PCH LP Bx Hsio Version
SKL PCH LP Cx Hsio Version
Handle 0x0040, DMI type 221, 54 bytes
OEM-specific Type
Header and Data:
DD 36 40 00 07 01 00 04 01 00 08 00 02 00 04 01
00 08 00 03 00 04 01 00 00 00 04 05 FF FF FF FF
FF 06 00 FF FF FF 05 00 07 00 FF FF FF 05 00 08
00 FF FF FF 00 00
Strings:
Reference Code - SA - System Agent
Reference Code - MRC
SA - PCIe Version
SA-CRID Status
Disabled
SA-CRID Original Value
SA-CRID New Value
OPROM - VBIOS
Handle 0x0041, DMI type 221, 96 bytes
OEM-specific Type
Header and Data:
DD 60 41 00 0D 01 00 00 00 00 00 00 02 00 FF FF
FF FF FF 03 04 FF FF FF FF FF 05 06 FF FF FF FF
FF 07 08 FF FF FF FF FF 09 00 00 00 00 00 00 0A
00 FF FF FF FF FF 0B 00 FF FF 00 00 00 0C 00 FF
FF FF FF FF 0D 00 FF FF FF FF FF 0E 00 FF FF FF
FF FF 0F 00 FF FF FF FF FF 10 11 01 02 02 03 00
Strings:
Lan Phy Version
Sensor Firmware Version
Debug Mode Status
Disabled
Performance Mode Status
Disabled
Debug Use USB(Disabled:Serial)
Disabled
ICC Overclocking Version
UNDI Version
EC FW Version
GOP Version
BIOS Guard Version
Base EC FW Version
EC-EC Protocol Version
Royal Park Version
BP1.2.2.0_RP03
Handle 0x0043, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J1:XDP CONN
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x0044, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: JP2:CPLD JTAG
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x0045, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J7:HDD0
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: SATA
Handle 0x0046, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: Not Specified
Internal Connector Type: None
External Reference Designator: J15:USB CONN_1
External Connector Type: Access Bus (USB)
Port Type: USB
Handle 0x0047, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: Not Specified
Internal Connector Type: None
External Reference Designator: J16:USB CONN_0
External Connector Type: Access Bus (USB)
Port Type: USB
Handle 0x0048, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: JP10:SMBUS_HOST
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x0049, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J20:TPM CONN
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x004A, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: JP13:SERIAL_A
Internal Connector Type: DB-9 male
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Serial Port XT/AT Compatible
Handle 0x004B, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: Not Specified
Internal Connector Type: None
External Reference Designator: J22:VGA_Conn
External Connector Type: DB-15 female
Port Type: Video Port
Handle 0x004C, DMI type 9, 17 bytes
System Slot Information
Designation: Mezz Slot(PCIex8 x4)
Type: x12 Proprietary
Current Usage: In Use
Length: Short
Characteristics:
3.3 V is provided
PME signal is supported
SMBus signal is supported
Bus Address: 0000:00:01.0
Handle 0x004D, DMI type 9, 17 bytes
System Slot Information
Designation: OCP Mezz Slot(PCIex4)
Type: x4 Proprietary
Current Usage: Available
Length: Short
Characteristics:
3.3 V is provided
PME signal is supported
Bus Address: 0000:00:1d.0
Handle 0x004E, DMI type 41, 11 bytes
Onboard Device
Reference Designation: Intel(R) C236 chipset SATA Controller
Type: SATA Controller
Status: Enabled
Type Instance: 1
Bus Address: 0000:00:17.0
Handle 0x004F, DMI type 41, 11 bytes
Onboard Device
Reference Designation: onboard VGA
Type: Video
Status: Enabled
Type Instance: 1
Bus Address: 0000:03:00.0
Handle 0x0050, DMI type 136, 6 bytes
OEM-specific Type
Header and Data:
88 06 50 00 00 00
Handle 0x0051, DMI type 14, 17 bytes
Group Associations
Name: Firmware Version Info
Items: 4
0x003E (<OUT OF SPEC>)
0x003F (<OUT OF SPEC>)
0x0040 (<OUT OF SPEC>)
0x0041 (<OUT OF SPEC>)
Handle 0x0052, DMI type 13, 22 bytes
BIOS Language Information
Language Description Format: Long
Installable Languages: 1
en|US|iso8859-1
Currently Installed Language: en|US|iso8859-1
Handle 0x0053, DMI type 127, 4 bytes
End Of Table

1242
tests/fixtures/dmidecode/SM_SSG-6028R vendored Normal file

File diff suppressed because it is too large Load diff

957
tests/fixtures/dmidecode/SM_SYS-6018R vendored Normal file
View file

@ -0,0 +1,957 @@
# dmidecode 3.0
Getting SMBIOS data from sysfs.
SMBIOS 3.0.0 present.
Table at 0x000EC9B0.
Handle 0x0000, DMI type 0, 24 bytes
BIOS Information
Vendor: American Megatrends Inc.
Version: 2.0b
Release Date: 04/14/2017
Address: 0xF0000
Runtime Size: 64 kB
ROM Size: 16384 kB
Characteristics:
PCI is supported
BIOS is upgradeable
BIOS shadowing is allowed
Boot from CD is supported
Selectable boot is supported
BIOS ROM is socketed
EDD is supported
5.25"/1.2 MB floppy services are supported (int 13h)
3.5"/720 kB floppy services are supported (int 13h)
3.5"/2.88 MB floppy services are supported (int 13h)
Print screen service is supported (int 5h)
8042 keyboard services are supported (int 9h)
Serial services are supported (int 14h)
Printer services are supported (int 17h)
ACPI is supported
USB legacy is supported
BIOS boot specification is supported
Targeted content distribution is supported
UEFI is supported
BIOS Revision: 5.6
Handle 0x0001, DMI type 1, 27 bytes
System Information
Manufacturer: Supermicro
Product Name: SYS-6018R-TDTPR
Version: 0123456789
Serial Number: A177950X7709591
UUID: 00000000-0000-0000-0000-0CC47A4B18C0
Wake-up Type: Power Switch
SKU Number: Default string
Family: Default string
Handle 0x0002, DMI type 2, 15 bytes
Base Board Information
Manufacturer: Supermicro
Product Name: X10DRD-LTP
Version: 1.00
Serial Number: ZM152S007866
Asset Tag: Default string
Features:
Board is a hosting board
Board is replaceable
Location In Chassis: Default string
Chassis Handle: 0x0003
Type: Motherboard
Contained Object Handles: 0
Handle 0x0003, DMI type 3, 22 bytes
Chassis Information
Manufacturer: Supermicro
Type: Other
Lock: Not Present
Version: 0123456789
Serial Number: C8150LG15NH0008
Asset Tag: Default string
Boot-up State: Safe
Power Supply State: Safe
Thermal State: Safe
Security Status: None
OEM Information: 0x00000000
Height: Unspecified
Number Of Power Cords: 1
Contained Elements: 0
SKU Number: Default string
Handle 0x0004, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J1A1
Internal Connector Type: None
External Reference Designator: PS2Mouse
External Connector Type: PS/2
Port Type: Mouse Port
Handle 0x0005, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J1A1
Internal Connector Type: None
External Reference Designator: Keyboard
External Connector Type: PS/2
Port Type: Keyboard Port
Handle 0x0006, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J2A1
Internal Connector Type: None
External Reference Designator: TV Out
External Connector Type: Mini Centronics Type-14
Port Type: Other
Handle 0x0007, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J2A2A
Internal Connector Type: None
External Reference Designator: COM A
External Connector Type: DB-9 male
Port Type: Serial Port 16550A Compatible
Handle 0x0008, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J2A2B
Internal Connector Type: None
External Reference Designator: Video
External Connector Type: DB-15 female
Port Type: Video Port
Handle 0x0009, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J3A1
Internal Connector Type: None
External Reference Designator: USB1
External Connector Type: Access Bus (USB)
Port Type: USB
Handle 0x000A, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J3A1
Internal Connector Type: None
External Reference Designator: USB2
External Connector Type: Access Bus (USB)
Port Type: USB
Handle 0x000B, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J3A1
Internal Connector Type: None
External Reference Designator: USB3
External Connector Type: Access Bus (USB)
Port Type: USB
Handle 0x000C, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J9A1 - TPM HDR
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x000D, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J9C1 - PCIE DOCKING CONN
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x000E, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J2B3 - CPU FAN
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x000F, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J6C2 - EXT HDMI
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x0010, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J3C1 - GMCH FAN
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x0011, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J1D1 - ITP
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x0012, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J9E2 - MDC INTPSR
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x0013, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J9E4 - MDC INTPSR
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x0014, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J9E3 - LPC HOT DOCKING
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x0015, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J9E1 - SCAN MATRIX
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x0016, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J9G1 - LPC SIDE BAND
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x0017, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J8F1 - UNIFIED
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x0018, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J6F1 - LVDS
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x0019, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J2F1 - LAI FAN
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x001A, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J2G1 - GFX VID
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x001B, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: J1G6 - AC JACK
Internal Connector Type: Other
External Reference Designator: Not Specified
External Connector Type: None
Port Type: Other
Handle 0x001C, DMI type 9, 17 bytes
System Slot Information
Designation: CPU1 SLOT4 PCI-E 3.0 X8
Type: x8 PCI Express 3 x8
Current Usage: Available
Length: Short
ID: 4
Characteristics:
3.3 V is provided
Opening is shared
PME signal is supported
Bus Address: 0000:05:00.0
Handle 0x001D, DMI type 9, 17 bytes
System Slot Information
Designation: CPU1 SLOT5 PCI-E 3.0 X8
Type: x8 PCI Express 3 x8
Current Usage: Available
Length: Short
ID: 5
Characteristics:
3.3 V is provided
Opening is shared
PME signal is supported
Bus Address: 0000:03:00.0
Handle 0x001E, DMI type 9, 17 bytes
System Slot Information
Designation: CPU1 SLOT6 PCI-E 3.0 X8
Type: x8 PCI Express 3 x8
Current Usage: In Use
Length: Short
ID: 6
Characteristics:
3.3 V is provided
Opening is shared
PME signal is supported
Bus Address: 0000:04:00.0
Handle 0x001F, DMI type 9, 17 bytes
System Slot Information
Designation: CPU2 SLOT7 PCI-E 3.0 X8
Type: x8 PCI Express 3 x8
Current Usage: Available
Length: Short
ID: 7
Characteristics:
3.3 V is provided
Opening is shared
PME signal is supported
Bus Address: 0000:ff:00.0
Handle 0x0020, DMI type 11, 5 bytes
OEM Strings
String 1: Intel Haswell/Wellsburg/Grantley
String 2: Supermicro motherboard-X10 Series
Handle 0x0021, DMI type 32, 20 bytes
System Boot Information
Status: No errors detected
Handle 0x0022, DMI type 39, 22 bytes
System Power Supply
Power Unit Group: 1
Location: PSU1
Name: PWS-504P-1R
Manufacturer: SUPERMICRO
Serial Number: P5041CG52ST0022
Asset Tag: N/A
Model Part Number: PWS-504P-1R
Revision: 1.4
Max Power Capacity: 500 W
Status: Present, OK
Type: Switching
Input Voltage Range Switching: Auto-switch
Plugged: Yes
Hot Replaceable: Yes
Handle 0x0023, DMI type 39, 22 bytes
System Power Supply
Power Unit Group: 2
Location: PSU2
Name: PWS-504P-1R
Manufacturer: SUPERMICRO
Serial Number: P5041CG52ST0021
Asset Tag: N/A
Model Part Number: PWS-504P-1R
Revision: 1.4
Max Power Capacity: 500 W
Status: Present, OK
Type: Switching
Input Voltage Range Switching: Auto-switch
Plugged: Yes
Hot Replaceable: Yes
Handle 0x0024, DMI type 41, 11 bytes
Onboard Device
Reference Designation: ASPEED Video AST2400
Type: Video
Status: Enabled
Type Instance: 1
Bus Address: 0000:09:00.0
Handle 0x0025, DMI type 41, 11 bytes
Onboard Device
Reference Designation: Intel Ethernet 82599ES/EN SFP+ #1
Type: Ethernet
Status: Enabled
Type Instance: 1
Bus Address: 0000:06:00.0
Handle 0x0026, DMI type 41, 11 bytes
Onboard Device
Reference Designation: Intel Ethernet 82599ES/EN SFP+ #2
Type: Ethernet
Status: Enabled
Type Instance: 2
Bus Address: 0000:06:00.1
Handle 0x0027, DMI type 38, 18 bytes
IPMI Device Information
Interface Type: KCS (Keyboard Control Style)
Specification Version: 2.0
I2C Slave Address: 0x10
NV Storage Device: Not Present
Base Address: 0x0000000000000CA2 (I/O)
Register Spacing: Successive Byte Boundaries
Handle 0x002A, DMI type 16, 23 bytes
Physical Memory Array
Location: System Board Or Motherboard
Use: System Memory
Error Correction Type: Multi-bit ECC
Maximum Capacity: 256 GB
Error Information Handle: Not Provided
Number Of Devices: 4
Handle 0x002B, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x002A
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: None
Locator: P1-DIMMA1
Bank Locator: P0_Node0_Channel0_Dimm0
Type: DDR4
Type Detail: Synchronous
Speed: 2400 MHz
Manufacturer: SK Hynix
Serial Number: 2AA5B8E9
Asset Tag: P1-DIMMA1_AssetTag (date:17/39)
Part Number: HMA42GR7AFR4N-UH
Rank: 2
Configured Clock Speed: 2133 MHz
Minimum Voltage: Unknown
Maximum Voltage: Unknown
Configured Voltage: Unknown
Handle 0x002C, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x002A
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: None
Locator: P1-DIMMB1
Bank Locator: P0_Node0_Channel1_Dimm0
Type: DDR4
Type Detail: Synchronous
Speed: 2400 MHz
Manufacturer: SK Hynix
Serial Number: 2AA5B923
Asset Tag: P1-DIMMB1_AssetTag (date:17/39)
Part Number: HMA42GR7AFR4N-UH
Rank: 2
Configured Clock Speed: 2133 MHz
Minimum Voltage: Unknown
Maximum Voltage: Unknown
Configured Voltage: Unknown
Handle 0x002D, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x002A
Error Information Handle: Not Provided
Total Width: Unknown
Data Width: Unknown
Size: No Module Installed
Form Factor: DIMM
Set: None
Locator: P1-DIMMC1
Bank Locator: P0_Node0_Channel2_Dimm0
Type: DDR4
Type Detail: Synchronous
Speed: Unknown
Manufacturer: NO DIMM
Serial Number: NO DIMM
Asset Tag: NO DIMM
Part Number: NO DIMM
Rank: Unknown
Configured Clock Speed: Unknown
Minimum Voltage: Unknown
Maximum Voltage: Unknown
Configured Voltage: Unknown
Handle 0x002E, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x002A
Error Information Handle: Not Provided
Total Width: Unknown
Data Width: Unknown
Size: No Module Installed
Form Factor: DIMM
Set: None
Locator: P1-DIMMD1
Bank Locator: P0_Node0_Channel3_Dimm0
Type: DDR4
Type Detail: Synchronous
Speed: Unknown
Manufacturer: NO DIMM
Serial Number: NO DIMM
Asset Tag: NO DIMM
Part Number: NO DIMM
Rank: Unknown
Configured Clock Speed: Unknown
Minimum Voltage: Unknown
Maximum Voltage: Unknown
Configured Voltage: Unknown
Handle 0x002F, DMI type 16, 23 bytes
Physical Memory Array
Location: System Board Or Motherboard
Use: System Memory
Error Correction Type: Multi-bit ECC
Maximum Capacity: 256 GB
Error Information Handle: Not Provided
Number Of Devices: 4
Handle 0x0030, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x002F
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: None
Locator: P2-DIMME1
Bank Locator: P1_Node1_Channel0_Dimm0
Type: DDR4
Type Detail: Synchronous
Speed: 2400 MHz
Manufacturer: SK Hynix
Serial Number: 2AA5B88C
Asset Tag: P2-DIMME1_AssetTag (date:17/39)
Part Number: HMA42GR7AFR4N-UH
Rank: 2
Configured Clock Speed: 2133 MHz
Minimum Voltage: Unknown
Maximum Voltage: Unknown
Configured Voltage: Unknown
Handle 0x0031, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x002F
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: None
Locator: P2-DIMMF1
Bank Locator: P1_Node1_Channel1_Dimm0
Type: DDR4
Type Detail: Synchronous
Speed: 2400 MHz
Manufacturer: SK Hynix
Serial Number: 2AA5B86B
Asset Tag: P2-DIMMF1_AssetTag (date:17/39)
Part Number: HMA42GR7AFR4N-UH
Rank: 2
Configured Clock Speed: 2133 MHz
Minimum Voltage: Unknown
Maximum Voltage: Unknown
Configured Voltage: Unknown
Handle 0x0032, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x002F
Error Information Handle: Not Provided
Total Width: Unknown
Data Width: Unknown
Size: No Module Installed
Form Factor: DIMM
Set: None
Locator: P2-DIMMG1
Bank Locator: P1_Node1_Channel2_Dimm0
Type: DDR4
Type Detail: Synchronous
Speed: Unknown
Manufacturer: NO DIMM
Serial Number: NO DIMM
Asset Tag: NO DIMM
Part Number: NO DIMM
Rank: Unknown
Configured Clock Speed: Unknown
Minimum Voltage: Unknown
Maximum Voltage: Unknown
Configured Voltage: Unknown
Handle 0x0033, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x002F
Error Information Handle: Not Provided
Total Width: Unknown
Data Width: Unknown
Size: No Module Installed
Form Factor: DIMM
Set: None
Locator: P2-DIMMH1
Bank Locator: P1_Node1_Channel3_Dimm0
Type: DDR4
Type Detail: Synchronous
Speed: Unknown
Manufacturer: NO DIMM
Serial Number: NO DIMM
Asset Tag: NO DIMM
Part Number: NO DIMM
Rank: Unknown
Configured Clock Speed: Unknown
Minimum Voltage: Unknown
Maximum Voltage: Unknown
Configured Voltage: Unknown
Handle 0x0034, DMI type 19, 31 bytes
Memory Array Mapped Address
Starting Address: 0x00000000000
Ending Address: 0x007FFFFFFFF
Range Size: 32 GB
Physical Array Handle: 0x002A
Partition Width: 2
Handle 0x0035, DMI type 20, 35 bytes
Memory Device Mapped Address
Starting Address: 0x00000000000
Ending Address: 0x003FFFFFFFF
Range Size: 16 GB
Physical Device Handle: 0x002B
Memory Array Mapped Address Handle: 0x0034
Partition Row Position: 1
Handle 0x0036, DMI type 20, 35 bytes
Memory Device Mapped Address
Starting Address: 0x00400000000
Ending Address: 0x007FFFFFFFF
Range Size: 16 GB
Physical Device Handle: 0x002C
Memory Array Mapped Address Handle: 0x0034
Partition Row Position: 1
Handle 0x0037, DMI type 19, 31 bytes
Memory Array Mapped Address
Starting Address: 0x00800000000
Ending Address: 0x00FFFFFFFFF
Range Size: 32 GB
Physical Array Handle: 0x002F
Partition Width: 2
Handle 0x0038, DMI type 20, 35 bytes
Memory Device Mapped Address
Starting Address: 0x00800000000
Ending Address: 0x00BFFFFFFFF
Range Size: 16 GB
Physical Device Handle: 0x0030
Memory Array Mapped Address Handle: 0x0037
Partition Row Position: 1
Handle 0x0039, DMI type 20, 35 bytes
Memory Device Mapped Address
Starting Address: 0x00C00000000
Ending Address: 0x00FFFFFFFFF
Range Size: 16 GB
Physical Device Handle: 0x0031
Memory Array Mapped Address Handle: 0x0037
Partition Row Position: 1
Handle 0x003A, DMI type 15, 73 bytes
System Event Log
Area Length: 65535 bytes
Header Start Offset: 0x0000
Header Length: 16 bytes
Data Start Offset: 0x0010
Access Method: Memory-mapped physical 32-bit address
Access Address: 0xFF540000
Status: Valid, Not Full
Change Token: 0x00000001
Header Format: Type 1
Supported Log Type Descriptors: 25
Descriptor 1: Single-bit ECC memory error
Data Format 1: Multiple-event handle
Descriptor 2: Multi-bit ECC memory error
Data Format 2: Multiple-event handle
Descriptor 3: Parity memory error
Data Format 3: None
Descriptor 4: Bus timeout
Data Format 4: None
Descriptor 5: I/O channel block
Data Format 5: None
Descriptor 6: Software NMI
Data Format 6: None
Descriptor 7: POST memory resize
Data Format 7: None
Descriptor 8: POST error
Data Format 8: POST results bitmap
Descriptor 9: PCI parity error
Data Format 9: Multiple-event handle
Descriptor 10: PCI system error
Data Format 10: Multiple-event handle
Descriptor 11: CPU failure
Data Format 11: None
Descriptor 12: EISA failsafe timer timeout
Data Format 12: None
Descriptor 13: Correctable memory log disabled
Data Format 13: None
Descriptor 14: Logging disabled
Data Format 14: None
Descriptor 15: System limit exceeded
Data Format 15: None
Descriptor 16: Asynchronous hardware timer expired
Data Format 16: None
Descriptor 17: System configuration information
Data Format 17: None
Descriptor 18: Hard disk information
Data Format 18: None
Descriptor 19: System reconfigured
Data Format 19: None
Descriptor 20: Uncorrectable CPU-complex error
Data Format 20: None
Descriptor 21: Log area reset/cleared
Data Format 21: None
Descriptor 22: System boot
Data Format 22: None
Descriptor 23: End of log
Data Format 23: None
Descriptor 24: OEM-specific
Data Format 24: OEM-specific
Descriptor 25: OEM-specific
Data Format 25: OEM-specific
Handle 0x003B, DMI type 7, 19 bytes
Cache Information
Socket Designation: CPU Internal L1
Configuration: Enabled, Not Socketed, Level 1
Operational Mode: Write Back
Location: Internal
Installed Size: 640 kB
Maximum Size: 640 kB
Supported SRAM Types:
Unknown
Installed SRAM Type: Unknown
Speed: Unknown
Error Correction Type: Parity
System Type: Other
Associativity: 8-way Set-associative
Handle 0x003C, DMI type 7, 19 bytes
Cache Information
Socket Designation: CPU Internal L2
Configuration: Enabled, Not Socketed, Level 2
Operational Mode: Write Back
Location: Internal
Installed Size: 2560 kB
Maximum Size: 2560 kB
Supported SRAM Types:
Unknown
Installed SRAM Type: Unknown
Speed: Unknown
Error Correction Type: Single-bit ECC
System Type: Unified
Associativity: 8-way Set-associative
Handle 0x003D, DMI type 7, 19 bytes
Cache Information
Socket Designation: CPU Internal L3
Configuration: Enabled, Not Socketed, Level 3
Operational Mode: Write Back
Location: Internal
Installed Size: 25600 kB
Maximum Size: 25600 kB
Supported SRAM Types:
Unknown
Installed SRAM Type: Unknown
Speed: Unknown
Error Correction Type: Single-bit ECC
System Type: Unified
Associativity: 20-way Set-associative
Handle 0x003E, DMI type 4, 42 bytes
Processor Information
Socket Designation: CPU1
Type: Central Processor
Family: Xeon
Manufacturer: Intel
ID: F1 06 04 00 FF FB EB BF
Signature: Type 0, Family 6, Model 79, Stepping 1
Flags:
FPU (Floating-point unit on-chip)
VME (Virtual mode extension)
DE (Debugging extension)
PSE (Page size extension)
TSC (Time stamp counter)
MSR (Model specific registers)
PAE (Physical address extension)
MCE (Machine check exception)
CX8 (CMPXCHG8 instruction supported)
APIC (On-chip APIC hardware supported)
SEP (Fast system call)
MTRR (Memory type range registers)
PGE (Page global enable)
MCA (Machine check architecture)
CMOV (Conditional move instruction supported)
PAT (Page attribute table)
PSE-36 (36-bit page size extension)
CLFSH (CLFLUSH instruction supported)
DS (Debug store)
ACPI (ACPI supported)
MMX (MMX technology supported)
FXSR (FXSAVE and FXSTOR instructions supported)
SSE (Streaming SIMD extensions)
SSE2 (Streaming SIMD extensions 2)
SS (Self-snoop)
HTT (Multi-threading)
TM (Thermal monitor supported)
PBE (Pending break enabled)
Version: Intel(R) Xeon(R) CPU E5-2630 v4 @ 2.20GHz
Voltage: 1.8 V
External Clock: 100 MHz
Max Speed: 4000 MHz
Current Speed: 2200 MHz
Status: Populated, Enabled
Upgrade: Socket LGA2011-3
L1 Cache Handle: 0x003B
L2 Cache Handle: 0x003C
L3 Cache Handle: 0x003D
Serial Number: Not Specified
Asset Tag: Not Specified
Part Number: Not Specified
Core Count: 10
Core Enabled: 10
Thread Count: 20
Characteristics:
64-bit capable
Multi-Core
Hardware Thread
Execute Protection
Enhanced Virtualization
Power/Performance Control
Handle 0x003F, DMI type 7, 19 bytes
Cache Information
Socket Designation: CPU Internal L1
Configuration: Enabled, Not Socketed, Level 1
Operational Mode: Write Back
Location: Internal
Installed Size: 640 kB
Maximum Size: 640 kB
Supported SRAM Types:
Unknown
Installed SRAM Type: Unknown
Speed: Unknown
Error Correction Type: Parity
System Type: Other
Associativity: 8-way Set-associative
Handle 0x0040, DMI type 7, 19 bytes
Cache Information
Socket Designation: CPU Internal L2
Configuration: Enabled, Not Socketed, Level 2
Operational Mode: Write Back
Location: Internal
Installed Size: 2560 kB
Maximum Size: 2560 kB
Supported SRAM Types:
Unknown
Installed SRAM Type: Unknown
Speed: Unknown
Error Correction Type: Single-bit ECC
System Type: Unified
Associativity: 8-way Set-associative
Handle 0x0041, DMI type 7, 19 bytes
Cache Information
Socket Designation: CPU Internal L3
Configuration: Enabled, Not Socketed, Level 3
Operational Mode: Write Back
Location: Internal
Installed Size: 25600 kB
Maximum Size: 25600 kB
Supported SRAM Types:
Unknown
Installed SRAM Type: Unknown
Speed: Unknown
Error Correction Type: Single-bit ECC
System Type: Unified
Associativity: 20-way Set-associative
Handle 0x0042, DMI type 4, 42 bytes
Processor Information
Socket Designation: CPU2
Type: Central Processor
Family: Xeon
Manufacturer: Intel
ID: F1 06 04 00 FF FB EB BF
Signature: Type 0, Family 6, Model 79, Stepping 1
Flags:
FPU (Floating-point unit on-chip)
VME (Virtual mode extension)
DE (Debugging extension)
PSE (Page size extension)
TSC (Time stamp counter)
MSR (Model specific registers)
PAE (Physical address extension)
MCE (Machine check exception)
CX8 (CMPXCHG8 instruction supported)
APIC (On-chip APIC hardware supported)
SEP (Fast system call)
MTRR (Memory type range registers)
PGE (Page global enable)
MCA (Machine check architecture)
CMOV (Conditional move instruction supported)
PAT (Page attribute table)
PSE-36 (36-bit page size extension)
CLFSH (CLFLUSH instruction supported)
DS (Debug store)
ACPI (ACPI supported)
MMX (MMX technology supported)
FXSR (FXSAVE and FXSTOR instructions supported)
SSE (Streaming SIMD extensions)
SSE2 (Streaming SIMD extensions 2)
SS (Self-snoop)
HTT (Multi-threading)
TM (Thermal monitor supported)
PBE (Pending break enabled)
Version: Intel(R) Xeon(R) CPU E5-2630 v4 @ 2.20GHz
Voltage: 1.8 V
External Clock: 100 MHz
Max Speed: 4000 MHz
Current Speed: 2200 MHz
Status: Populated, Enabled
Upgrade: Socket LGA2011-3
L1 Cache Handle: 0x003F
L2 Cache Handle: 0x0040
L3 Cache Handle: 0x0041
Serial Number: Not Specified
Asset Tag: Not Specified
Part Number: Not Specified
Core Count: 10
Core Enabled: 10
Thread Count: 20
Characteristics:
64-bit capable
Multi-Core
Hardware Thread
Execute Protection
Enhanced Virtualization
Power/Performance Control
Handle 0x0043, DMI type 40, 27 bytes
Additional Information 1
Handle 0x0044, DMI type 40, 27 bytes
Additional Information 1
Handle 0x0045, DMI type 40, 27 bytes
Additional Information 1
Handle 0x0046, DMI type 40, 27 bytes
Additional Information 1
Handle 0x0047, DMI type 127, 4 bytes
End Of Table

View file

@ -0,0 +1,536 @@
# dmidecode 3.1
Getting SMBIOS data from sysfs.
SMBIOS 3.0 present.
32 structures occupying 2366 bytes.
Table at 0x6FB76000.
Handle 0x0000, DMI type 0, 24 bytes
BIOS Information
Vendor: American Megatrends Inc.
Version: 2.2
Release Date: 05/16/2018
Address: 0xF0000
Runtime Size: 64 kB
ROM Size: 16 MB
Characteristics:
PCI is supported
BIOS is upgradeable
BIOS shadowing is allowed
Boot from CD is supported
Selectable boot is supported
BIOS ROM is socketed
EDD is supported
5.25"/1.2 MB floppy services are supported (int 13h)
3.5"/720 kB floppy services are supported (int 13h)
3.5"/2.88 MB floppy services are supported (int 13h)
Print screen service is supported (int 5h)
Serial services are supported (int 14h)
Printer services are supported (int 17h)
ACPI is supported
USB legacy is supported
BIOS boot specification is supported
Targeted content distribution is supported
UEFI is supported
BIOS Revision: 5.11
Handle 0x0001, DMI type 1, 27 bytes
System Information
Manufacturer: Supermicro
Product Name: SYS-5039MS-H12TRF-OS012
Version: 0123456789
Serial Number: E235735X6B01665
UUID: 00000000-0000-0000-0000-0CC47AE14338
Wake-up Type: Power Switch
SKU Number: To be filled by O.E.M.
Family: To be filled by O.E.M.
Handle 0x0002, DMI type 2, 15 bytes
Base Board Information
Manufacturer: Supermicro
Product Name: X11SSE-F
Version: 1.01
Serial Number: ZM169S040205
Asset Tag: To be filled by O.E.M.
Features:
Board is a hosting board
Board is replaceable
Location In Chassis: To be filled by O.E.M.
Chassis Handle: 0x0003
Type: Motherboard
Contained Object Handles: 0
Handle 0x0003, DMI type 3, 22 bytes
Chassis Information
Manufacturer: Supermicro
Type: Other
Lock: Not Present
Version: 0123456789
Serial Number: C9390AF40A20098
Asset Tag: To be filled by O.E.M.
Boot-up State: Safe
Power Supply State: Safe
Thermal State: Safe
Security Status: None
OEM Information: 0x00000000
Height: Unspecified
Number Of Power Cords: 1
Contained Elements: 0
SKU Number: To be filled by O.E.M.
Handle 0x0004, DMI type 9, 17 bytes
System Slot Information
Designation: CPU MICRO-LP PCI-E 3.0 X8
Type: x8 PCI Express 3 x8
Current Usage: In Use
Length: Short
ID: 1
Characteristics:
3.3 V is provided
Opening is shared
PME signal is supported
Bus Address: 0000:01:00.0
Handle 0x0005, DMI type 11, 5 bytes
OEM Strings
String 1: Intel Skylake-S/Skylake-H/Greenlow
String 2: Supermicro motherboard-X11 Series
Handle 0x0006, DMI type 32, 20 bytes
System Boot Information
Status: No errors detected
Handle 0x0007, DMI type 39, 22 bytes
System Power Supply
Power Unit Group: 1
Location: PSU1
Name: PWS-2K04F-1R
Manufacturer: SUPERMICRO
Serial Number: P2K4FCG37KT0851
Asset Tag: N/A
Model Part Number: PWS-2K04F-1R
Revision: 1.0
Max Power Capacity: 2000 W
Status: Present, OK
Type: Switching
Input Voltage Range Switching: Auto-switch
Plugged: Yes
Hot Replaceable: No
Handle 0x0008, DMI type 39, 22 bytes
System Power Supply
Power Unit Group: 2
Location: PSU2
Name: PWS-2K04F-1R
Manufacturer: SUPERMICRO
Serial Number: P2K4FCG37KT0852
Asset Tag: N/A
Model Part Number: PWS-2K04F-1R
Revision: 1.0
Max Power Capacity: 2000 W
Status: Present, OK
Type: Switching
Input Voltage Range Switching: Auto-switch
Plugged: Yes
Hot Replaceable: No
Handle 0x0009, DMI type 41, 11 bytes
Onboard Device
Reference Designation: ASPEED Video AST2400
Type: Video
Status: Enabled
Type Instance: 1
Bus Address: 0000:04:00.0
Handle 0x000A, DMI type 38, 18 bytes
IPMI Device Information
Interface Type: KCS (Keyboard Control Style)
Specification Version: 2.0
I2C Slave Address: 0x10
NV Storage Device: Not Present
Base Address: 0x0000000000000CA2 (I/O)
Register Spacing: Successive Byte Boundaries
Handle 0x000B, DMI type 7, 19 bytes
Cache Information
Socket Designation: L1 Cache
Configuration: Enabled, Not Socketed, Level 1
Operational Mode: Write Back
Location: Internal
Installed Size: 128 kB
Maximum Size: 128 kB
Supported SRAM Types:
Synchronous
Installed SRAM Type: Synchronous
Speed: Unknown
Error Correction Type: Parity
System Type: Data
Associativity: 8-way Set-associative
Handle 0x000C, DMI type 7, 19 bytes
Cache Information
Socket Designation: L1 Cache
Configuration: Enabled, Not Socketed, Level 1
Operational Mode: Write Back
Location: Internal
Installed Size: 128 kB
Maximum Size: 128 kB
Supported SRAM Types:
Synchronous
Installed SRAM Type: Synchronous
Speed: Unknown
Error Correction Type: Parity
System Type: Instruction
Associativity: 8-way Set-associative
Handle 0x000D, DMI type 7, 19 bytes
Cache Information
Socket Designation: L2 Cache
Configuration: Enabled, Not Socketed, Level 2
Operational Mode: Write Back
Location: Internal
Installed Size: 1024 kB
Maximum Size: 1024 kB
Supported SRAM Types:
Synchronous
Installed SRAM Type: Synchronous
Speed: Unknown
Error Correction Type: Single-bit ECC
System Type: Unified
Associativity: 4-way Set-associative
Handle 0x000E, DMI type 7, 19 bytes
Cache Information
Socket Designation: L3 Cache
Configuration: Enabled, Not Socketed, Level 3
Operational Mode: Write Back
Location: Internal
Installed Size: 8192 kB
Maximum Size: 8192 kB
Supported SRAM Types:
Synchronous
Installed SRAM Type: Synchronous
Speed: Unknown
Error Correction Type: Multi-bit ECC
System Type: Unified
Associativity: 16-way Set-associative
Handle 0x000F, DMI type 4, 48 bytes
Processor Information
Socket Designation: CPU
Type: Central Processor
Family: Xeon
Manufacturer: Intel(R) Corporation
ID: E3 06 05 00 FF FB EB BF
Signature: Type 0, Family 6, Model 94, Stepping 3
Flags:
FPU (Floating-point unit on-chip)
VME (Virtual mode extension)
DE (Debugging extension)
PSE (Page size extension)
TSC (Time stamp counter)
MSR (Model specific registers)
PAE (Physical address extension)
MCE (Machine check exception)
CX8 (CMPXCHG8 instruction supported)
APIC (On-chip APIC hardware supported)
SEP (Fast system call)
MTRR (Memory type range registers)
PGE (Page global enable)
MCA (Machine check architecture)
CMOV (Conditional move instruction supported)
PAT (Page attribute table)
PSE-36 (36-bit page size extension)
CLFSH (CLFLUSH instruction supported)
DS (Debug store)
ACPI (ACPI supported)
MMX (MMX technology supported)
FXSR (FXSAVE and FXSTOR instructions supported)
SSE (Streaming SIMD extensions)
SSE2 (Streaming SIMD extensions 2)
SS (Self-snoop)
HTT (Multi-threading)
TM (Thermal monitor supported)
PBE (Pending break enabled)
Version: Intel(R) Xeon(R) CPU E3-1245 v5 @ 3.50GHz
Voltage: 1.1 V
External Clock: 100 MHz
Max Speed: 3900 MHz
Current Speed: 3500 MHz
Status: Populated, Enabled
Upgrade: Other
L1 Cache Handle: 0x000C
L2 Cache Handle: 0x000D
L3 Cache Handle: 0x000E
Serial Number: To Be Filled By O.E.M.
Asset Tag: To Be Filled By O.E.M.
Part Number: To Be Filled By O.E.M.
Core Count: 4
Core Enabled: 4
Thread Count: 8
Characteristics:
64-bit capable
Multi-Core
Hardware Thread
Execute Protection
Enhanced Virtualization
Power/Performance Control
Handle 0x0010, DMI type 16, 23 bytes
Physical Memory Array
Location: System Board Or Motherboard
Use: System Memory
Error Correction Type: Single-bit ECC
Maximum Capacity: 64 GB
Error Information Handle: Not Provided
Number Of Devices: 4
Handle 0x0011, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x0010
Error Information Handle: Not Provided
Total Width: Unknown
Data Width: Unknown
Size: No Module Installed
Form Factor: Unknown
Set: None
Locator: DIMMA1
Bank Locator: P0_Node0_Channel0_Dimm0
Type: Unknown
Type Detail: None
Speed: Unknown
Manufacturer: Not Specified
Serial Number: Not Specified
Asset Tag: Not Specified
Part Number: Not Specified
Rank: Unknown
Configured Clock Speed: Unknown
Minimum Voltage: Unknown
Maximum Voltage: Unknown
Configured Voltage: Unknown
Handle 0x0012, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x0010
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: None
Locator: DIMMA2
Bank Locator: P0_Node0_Channel0_Dimm1
Type: DDR4
Type Detail: Synchronous
Speed: 2400 MT/s
Manufacturer: Micron
Serial Number: 13DA8BB7
Asset Tag: DIMMA2_AssetTag(16/36)
Part Number: 18ADF2G72AZ-2G3B1
Rank: 2
Configured Clock Speed: 2133 MT/s
Minimum Voltage: 1.2 V
Maximum Voltage: 1.2 V
Configured Voltage: 1.2 V
Handle 0x0013, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x0010
Error Information Handle: Not Provided
Total Width: Unknown
Data Width: Unknown
Size: No Module Installed
Form Factor: Unknown
Set: None
Locator: DIMMB1
Bank Locator: P0_Node0_Channel1_Dimm0
Type: Unknown
Type Detail: None
Speed: Unknown
Manufacturer: Not Specified
Serial Number: Not Specified
Asset Tag: Not Specified
Part Number: Not Specified
Rank: Unknown
Configured Clock Speed: Unknown
Minimum Voltage: Unknown
Maximum Voltage: Unknown
Configured Voltage: Unknown
Handle 0x0014, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x0010
Error Information Handle: Not Provided
Total Width: 72 bits
Data Width: 64 bits
Size: 16384 MB
Form Factor: DIMM
Set: None
Locator: DIMMB2
Bank Locator: P0_Node0_Channel1_Dimm1
Type: DDR4
Type Detail: Synchronous
Speed: 2400 MT/s
Manufacturer: Micron
Serial Number: 13DA8C08
Asset Tag: DIMMB2_AssetTag(16/36)
Part Number: 18ADF2G72AZ-2G3B1
Rank: 2
Configured Clock Speed: 2133 MT/s
Minimum Voltage: 1.2 V
Maximum Voltage: 1.2 V
Configured Voltage: 1.2 V
Handle 0x0015, DMI type 19, 31 bytes
Memory Array Mapped Address
Starting Address: 0x00000000000
Ending Address: 0x007FFFFFFFF
Range Size: 32 GB
Physical Array Handle: 0x0010
Partition Width: 2
Handle 0x0016, DMI type 15, 73 bytes
System Event Log
Area Length: 65535 bytes
Header Start Offset: 0x0000
Header Length: 16 bytes
Data Start Offset: 0x0010
Access Method: Memory-mapped physical 32-bit address
Access Address: 0xFF610000
Status: Valid, Not Full
Change Token: 0x00000001
Header Format: Type 1
Supported Log Type Descriptors: 25
Descriptor 1: Single-bit ECC memory error
Data Format 1: Multiple-event handle
Descriptor 2: Multi-bit ECC memory error
Data Format 2: Multiple-event handle
Descriptor 3: Parity memory error
Data Format 3: None
Descriptor 4: Bus timeout
Data Format 4: None
Descriptor 5: I/O channel block
Data Format 5: None
Descriptor 6: Software NMI
Data Format 6: None
Descriptor 7: POST memory resize
Data Format 7: None
Descriptor 8: POST error
Data Format 8: POST results bitmap
Descriptor 9: PCI parity error
Data Format 9: Multiple-event handle
Descriptor 10: PCI system error
Data Format 10: Multiple-event handle
Descriptor 11: CPU failure
Data Format 11: None
Descriptor 12: EISA failsafe timer timeout
Data Format 12: None
Descriptor 13: Correctable memory log disabled
Data Format 13: None
Descriptor 14: Logging disabled
Data Format 14: None
Descriptor 15: System limit exceeded
Data Format 15: None
Descriptor 16: Asynchronous hardware timer expired
Data Format 16: None
Descriptor 17: System configuration information
Data Format 17: None
Descriptor 18: Hard disk information
Data Format 18: None
Descriptor 19: System reconfigured
Data Format 19: None
Descriptor 20: Uncorrectable CPU-complex error
Data Format 20: None
Descriptor 21: Log area reset/cleared
Data Format 21: None
Descriptor 22: System boot
Data Format 22: None
Descriptor 23: End of log
Data Format 23: None
Descriptor 24: OEM-specific
Data Format 24: OEM-specific
Descriptor 25: OEM-specific
Data Format 25: OEM-specific
Handle 0x0017, DMI type 20, 35 bytes
Memory Device Mapped Address
Starting Address: 0x00000000000
Ending Address: 0x003FFFFFFFF
Range Size: 16 GB
Physical Device Handle: 0x0012
Memory Array Mapped Address Handle: 0x0015
Partition Row Position: Unknown
Interleave Position: 1
Interleaved Data Depth: 2
Handle 0x0018, DMI type 20, 35 bytes
Memory Device Mapped Address
Starting Address: 0x00400000000
Ending Address: 0x007FFFFFFFF
Range Size: 16 GB
Physical Device Handle: 0x0014
Memory Array Mapped Address Handle: 0x0015
Partition Row Position: Unknown
Interleave Position: 2
Interleaved Data Depth: 2
Handle 0x0019, DMI type 221, 26 bytes
OEM-specific Type
Header and Data:
DD 1A 19 00 03 01 00 04 01 00 08 00 02 00 00 00
00 C6 00 03 00 00 05 00 00 00
Strings:
Reference Code - CPU
uCode Version
TXT ACM version
Handle 0x001A, DMI type 221, 68 bytes
OEM-specific Type
Header and Data:
DD 44 1A 00 09 01 00 04 01 00 08 00 02 03 FF FF
FF FF FF 04 00 FF FF FF 31 00 05 00 FF FF FF 31
00 06 00 FF FF FF FF FF 07 00 3E 00 00 00 00 08
00 34 00 00 00 00 09 00 3E 00 00 00 00 0A 00 34
00 00 00 00
Strings:
Reference Code - SKL PCH
PCH-CRID Status
Disabled
PCH-CRID Original Value
PCH-CRID New Value
OPROM - RST - RAID
SKL PCH H Bx Hsio Version
SKL PCH H Dx Hsio Version
SKL PCH LP Bx Hsio Version
SKL PCH LP Cx Hsio Version
Handle 0x001B, DMI type 221, 54 bytes
OEM-specific Type
Header and Data:
DD 36 1B 00 07 01 00 04 01 00 08 00 02 00 02 01
00 00 00 03 00 04 01 00 00 00 04 05 FF FF FF FF
FF 06 00 FF FF FF 07 00 07 00 FF FF FF 07 00 08
00 FF FF FF 00 00
Strings:
Reference Code - SA - System Agent
Reference Code - MRC
SA - PCIe Version
SA-CRID Status
Disabled
SA-CRID Original Value
SA-CRID New Value
OPROM - VBIOS
Handle 0x001C, DMI type 40, 27 bytes
Additional Information 1
Handle 0x001D, DMI type 40, 27 bytes
Additional Information 1
Handle 0x001E, DMI type 136, 6 bytes
OEM-specific Type
Header and Data:
88 06 1E 00 00 00
Handle 0x001F, DMI type 127, 4 bytes
End Of Table

571
tests/fixtures/dmidecode/unknown.txt vendored Normal file
View file

@ -0,0 +1,571 @@
# dmidecode 3.1
Getting SMBIOS data from sysfs.
SMBIOS 2.8 present.
44 structures occupying 2055 bytes.
Table at 0x000E4E00.
Handle 0x0000, DMI type 0, 24 bytes
BIOS Information
Vendor: Online Labs
Version: 00.00.00.0007
Release Date: 03/04/2016
Address: 0xE0000
Runtime Size: 128 kB
ROM Size: 6144 kB
Characteristics:
PCI is supported
BIOS is upgradeable
BIOS shadowing is allowed
Boot from CD is supported
Selectable boot is supported
EDD is supported
Japanese floppy for NEC 9800 1.2 MB is supported (int 13h)
Japanese floppy for Toshiba 1.2 MB is supported (int 13h)
5.25"/360 kB floppy services are supported (int 13h)
5.25"/1.2 MB floppy services are supported (int 13h)
3.5"/720 kB floppy services are supported (int 13h)
3.5"/2.88 MB floppy services are supported (int 13h)
8042 keyboard services are supported (int 9h)
CGA/mono video services are supported (int 10h)
ACPI is supported
USB legacy is supported
BIOS boot specification is supported
Targeted content distribution is supported
UEFI is supported
BIOS Revision: 0.0
Handle 0x0001, DMI type 1, 27 bytes
System Information
Manufacturer: Online Labs
Product Name: SR
Version: (^_^)
Serial Number: 42
UUID: 12345678-1234-5678-90AB-CDDEEFAABBCC
Wake-up Type: Power Switch
SKU Number: (^_^)
Family: Avoton
Handle 0x0002, DMI type 2, 15 bytes
Base Board Information
Manufacturer: Online Labs
Product Name: SR
Version: 42
Serial Number: 42
Asset Tag: 42
Features:
Board is a hosting board
Board is replaceable
Location In Chassis: <BAD INDEX>
Chassis Handle: 0x0003
Type: Motherboard
Contained Object Handles: 0
Handle 0x0003, DMI type 3, 22 bytes
Chassis Information
Manufacturer: Chassis Manufacturer
Type: Desktop
Lock: Not Present
Version: Chassis Version
Serial Number: Chassis Serial Number
Asset Tag: Chassis Asset Tag
Boot-up State: Safe
Power Supply State: Safe
Thermal State: Safe
Security Status: None
OEM Information: 0x00000000
Height: Unspecified
Number Of Power Cords: 1
Contained Elements: 0
SKU Number: SKU Number
Handle 0x0004, DMI type 4, 42 bytes
Processor Information
Socket Designation: CPU0
Type: Central Processor
Family: Atom
Manufacturer: Intel(R) Corporation
ID: D8 06 04 00 FF FB EB BF
Signature: Type 0, Family 6, Model 77, Stepping 8
Flags:
FPU (Floating-point unit on-chip)
VME (Virtual mode extension)
DE (Debugging extension)
PSE (Page size extension)
TSC (Time stamp counter)
MSR (Model specific registers)
PAE (Physical address extension)
MCE (Machine check exception)
CX8 (CMPXCHG8 instruction supported)
APIC (On-chip APIC hardware supported)
SEP (Fast system call)
MTRR (Memory type range registers)
PGE (Page global enable)
MCA (Machine check architecture)
CMOV (Conditional move instruction supported)
PAT (Page attribute table)
PSE-36 (36-bit page size extension)
CLFSH (CLFLUSH instruction supported)
DS (Debug store)
ACPI (ACPI supported)
MMX (MMX technology supported)
FXSR (FXSAVE and FXSTOR instructions supported)
SSE (Streaming SIMD extensions)
SSE2 (Streaming SIMD extensions 2)
SS (Self-snoop)
HTT (Multi-threading)
TM (Thermal monitor supported)
PBE (Pending break enabled)
Version: Intel(R) Atom(TM) CPU C2750 @ 2.40GHz
Voltage: 1.6 V
External Clock: 100 MHz
Max Speed: 2400 MHz
Current Speed: 2400 MHz
Status: Populated, Enabled
Upgrade: Socket LGA775
L1 Cache Handle: 0x0005
L2 Cache Handle: 0x0006
L3 Cache Handle: Not Provided
Serial Number: Not Specified
Asset Tag: UNKNOWN
Part Number: Not Specified
Core Count: 8
Core Enabled: 8
Thread Count: 8
Characteristics:
64-bit capable
Handle 0x0005, DMI type 7, 19 bytes
Cache Information
Socket Designation: L1-Cache
Configuration: Enabled, Not Socketed, Level 1
Operational Mode: Write Back
Location: Internal
Installed Size: 448 kB
Maximum Size: 448 kB
Supported SRAM Types:
Synchronous
Installed SRAM Type: Synchronous
Speed: Unknown
Error Correction Type: Single-bit ECC
System Type: Instruction
Associativity: 8-way Set-associative
Handle 0x0006, DMI type 7, 19 bytes
Cache Information
Socket Designation: L2-Cache
Configuration: Enabled, Not Socketed, Level 2
Operational Mode: Write Back
Location: Internal
Installed Size: 4096 kB
Maximum Size: 4096 kB
Supported SRAM Types:
Synchronous
Installed SRAM Type: Synchronous
Speed: Unknown
Error Correction Type: Single-bit ECC
System Type: Unified
Associativity: 16-way Set-associative
Handle 0x0007, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: EC5
Internal Connector Type: None
External Reference Designator: USB
External Connector Type: Access Bus (USB)
Port Type: USB
Handle 0x0008, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: EC6
Internal Connector Type: None
External Reference Designator: USB
External Connector Type: Access Bus (USB)
Port Type: USB
Handle 0x0009, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: LAN1
Internal Connector Type: None
External Reference Designator: 1G Network
External Connector Type: RJ-45
Port Type: Network Port
Handle 0x000A, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: LAN1
Internal Connector Type: None
External Reference Designator: 1G Network
External Connector Type: RJ-45
Port Type: Network Port
Handle 0x000B, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: 10GLAN1
Internal Connector Type: None
External Reference Designator: 10G Network
External Connector Type: RJ-45
Port Type: Network Port
Handle 0x000C, DMI type 8, 9 bytes
Port Connector Information
Internal Reference Designator: 10GLAN2
Internal Connector Type: None
External Reference Designator: 10G Network
External Connector Type: RJ-45
Port Type: Network Port
Handle 0x000D, DMI type 9, 17 bytes
System Slot Information
Designation: PCI_E_1
Type: x8 PCI Express x8
Current Usage: Available
Length: Other
ID: 1
Characteristics:
PME signal is supported
Hot-plug devices are supported
Bus Address: 0000:00:01.0
Handle 0x000E, DMI type 9, 17 bytes
System Slot Information
Designation: PCI_E_2
Type: x4 PCI Express x4
Current Usage: Available
Length: Other
ID: 2
Characteristics:
PME signal is supported
Hot-plug devices are supported
Bus Address: 0000:00:02.0
Handle 0x000F, DMI type 9, 17 bytes
System Slot Information
Designation: PCI_E_3
Type: x4 PCI Express x4
Current Usage: Available
Length: Other
ID: 3
Characteristics:
PME signal is supported
Hot-plug devices are supported
Bus Address: 0000:04:08.0
Handle 0x0010, DMI type 9, 17 bytes
System Slot Information
Designation: PCI_E_4
Type: x16 PCI Express x16
Current Usage: Available
Length: Other
ID: 4
Characteristics:
PME signal is supported
Hot-plug devices are supported
Bus Address: 0000:04:09.0
Handle 0x0011, DMI type 11, 5 bytes
OEM Strings
String 1: OemString1
String 2: OemString2
String 3: OemString3
Handle 0x0012, DMI type 12, 5 bytes
System Configuration Options
Option 1: ConfigOptions1
Option 2: ConfigOptions2
Option 3: ConfigOptions3
Handle 0x0013, DMI type 13, 22 bytes
BIOS Language Information
Language Description Format: Long
Installable Languages: 4
en|US|iso8859-1,0
fr|CA|iso8859-1,0
zh|TW|unicode,0
ja|JP|unicode,0
Currently Installed Language: en|US|iso8859-1,0
Handle 0x0014, DMI type 15, 29 bytes
System Event Log
Area Length: 0 bytes
Header Start Offset: 0x0000
Header Length: 8192 bytes
Data Start Offset: 0x2000
Access Method: General-purpose non-volatile data functions
Access Address: 0x0000
Status: Valid, Not Full
Change Token: 0x12345678
Header Format: OEM-specific
Supported Log Type Descriptors: 3
Descriptor 1: POST memory resize
Data Format 1: None
Descriptor 2: POST error
Data Format 2: POST results bitmap
Descriptor 3: Log area reset/cleared
Data Format 3: None
Handle 0x0015, DMI type 16, 23 bytes
Physical Memory Array
Location: System Board Or Motherboard
Use: System Memory
Error Correction Type: None
Maximum Capacity: 64 GB
Error Information Handle: No Error
Number Of Devices: 4
Handle 0x0016, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x0015
Error Information Handle: No Error
Total Width: Unknown
Data Width: Unknown
Size: 8192 MB
Form Factor: DIMM
Set: None
Locator: DIMM0
Bank Locator: BANK 0
Type: DDR3
Type Detail: Synchronous Unbuffered (Unregistered)
Speed: 1600 MT/s
Manufacturer: Micron
Serial Number: 23115200
Asset Tag: 01
Part Number: DVM64453C DATARAM
Rank: 2
Configured Clock Speed: 1600 MT/s
Minimum Voltage: Unknown
Maximum Voltage: Unknown
Configured Voltage: Unknown
Handle 0x0017, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x0015
Error Information Handle: No Error
Total Width: Unknown
Data Width: Unknown
Size: 8192 MB
Form Factor: DIMM
Set: None
Locator: DIMM0
Bank Locator: BANK 1
Type: DDR3
Type Detail: Synchronous Unbuffered (Unregistered)
Speed: 1600 MT/s
Manufacturer: Micron
Serial Number: 00001639
Asset Tag: 02
Part Number: DVM64453C DATARAM
Rank: 2
Configured Clock Speed: 1600 MT/s
Minimum Voltage: Unknown
Maximum Voltage: Unknown
Configured Voltage: Unknown
Handle 0x0018, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x0015
Error Information Handle: No Error
Total Width: Unknown
Data Width: Unknown
Size: 8192 MB
Form Factor: DIMM
Set: None
Locator: DIMM1
Bank Locator: BANK 0
Type: DDR3
Type Detail: Synchronous Unbuffered (Unregistered)
Speed: 1600 MT/s
Manufacturer: Micron
Serial Number: 23115200
Asset Tag: 03
Part Number: DVM64453C DATARAM
Rank: 2
Configured Clock Speed: 1600 MT/s
Minimum Voltage: Unknown
Maximum Voltage: Unknown
Configured Voltage: Unknown
Handle 0x0019, DMI type 17, 40 bytes
Memory Device
Array Handle: 0x0015
Error Information Handle: No Error
Total Width: Unknown
Data Width: Unknown
Size: 8192 MB
Form Factor: DIMM
Set: None
Locator: DIMM1
Bank Locator: BANK 1
Type: DDR3
Type Detail: Synchronous Unbuffered (Unregistered)
Speed: 1600 MT/s
Manufacturer: Micron
Serial Number: 23115200
Asset Tag: 04
Part Number: DVM64453C DATARAM
Rank: 2
Configured Clock Speed: 1600 MT/s
Minimum Voltage: Unknown
Maximum Voltage: Unknown
Configured Voltage: Unknown
Handle 0x001A, DMI type 19, 31 bytes
Memory Array Mapped Address
Starting Address: 0x00000000000
Ending Address: 0x007FFFFFFFF
Range Size: 32 GB
Physical Array Handle: 0x0015
Partition Width: 4
Handle 0x001B, DMI type 20, 35 bytes
Memory Device Mapped Address
Starting Address: 0x00000000000
Ending Address: 0x001FFFFFFFF
Range Size: 8 GB
Physical Device Handle: 0x0016
Memory Array Mapped Address Handle: 0x0015
Partition Row Position: Unknown
Handle 0x001C, DMI type 20, 35 bytes
Memory Device Mapped Address
Starting Address: 0x00200000000
Ending Address: 0x003FFFFFFFF
Range Size: 8 GB
Physical Device Handle: 0x0017
Memory Array Mapped Address Handle: 0x0015
Partition Row Position: Unknown
Handle 0x001D, DMI type 20, 35 bytes
Memory Device Mapped Address
Starting Address: 0x00400000000
Ending Address: 0x005FFFFFFFF
Range Size: 8 GB
Physical Device Handle: 0x0018
Memory Array Mapped Address Handle: 0x0015
Partition Row Position: Unknown
Handle 0x001E, DMI type 20, 35 bytes
Memory Device Mapped Address
Starting Address: 0x00600000000
Ending Address: 0x007FFFFFFFF
Range Size: 8 GB
Physical Device Handle: 0x0019
Memory Array Mapped Address Handle: 0x0015
Partition Row Position: Unknown
Handle 0x001F, DMI type 21, 7 bytes
Built-in Pointing Device
Type: Touch Pad
Interface: PS/2
Buttons: 4
Handle 0x0020, DMI type 22, 26 bytes
Portable Battery
Location: Fake
Manufacturer: -Virtual Battery 0-
Manufacture Date: 08/08/2010
Serial Number: Battery 0
Name: CRB Battery 0
Chemistry: Zinc Air
Design Capacity: Unknown
Design Voltage: Unknown
SBDS Version: LithiumPolymer
Maximum Error: Unknown
OEM-specific Information: 0x00000000
Handle 0x0021, DMI type 26, 24 bytes
Voltage Probe
Description: Voltage Probe Description
Location: Unknown
Status: Unknown
Maximum Value: Unknown
Minimum Value: Unknown
Resolution: Unknown
Tolerance: Unknown
Accuracy: Unknown
OEM-specific Information: 0x00008000
Nominal Value: 0.000 V
Handle 0x0022, DMI type 27, 15 bytes
Cooling Device
Temperature Probe Handle: 0x0023
Type: Fan
Status: OK
OEM-specific Information: 0x00000000
Nominal Speed: 8192 rpm
Description: Cooling Device Description
Handle 0x0023, DMI type 28, 24 bytes
Temperature Probe
Description: Temperature Probe Description
Location: Unknown
Status: Unknown
Maximum Value: Unknown
Minimum Value: Unknown
Resolution: Unknown
Tolerance: Unknown
Accuracy: Unknown
OEM-specific Information: 0x00008000
Nominal Value: 0.0 deg C
Handle 0x0024, DMI type 32, 11 bytes
System Boot Information
Status: No errors detected
Handle 0x0025, DMI type 39, 22 bytes
System Power Supply
Location: OEM Define 0
Name: OEM Define 1
Manufacturer: OEM Define 2
Serial Number: OEM Define 3
Asset Tag: OEM Define 4
Model Part Number: OEM Define 5
Revision: OEM Define 6
Max Power Capacity: 75 W
Status: Not Present
Type: Regulator
Input Voltage Range Switching: Auto-switch
Plugged: No
Hot Replaceable: No
Handle 0x0026, DMI type 40, 17 bytes
Additional Information 1
Referenced Handle: 0x000c
Referenced Offset: 0x05
String: PCIExpressx16
Value: 0xaa
Additional Information 2
Referenced Handle: 0x0000
Referenced Offset: 0x05
String: Compiler Version: VC 9.0
Value: 0x00
Handle 0x0027, DMI type 128, 8 bytes
OEM-specific Type
Header and Data:
80 08 27 00 55 AA 55 AA
Strings:
Oem Type 128 Test 1
Oem Type 128 Test 2
Handle 0x0028, DMI type 129, 8 bytes
OEM-specific Type
Header and Data:
81 08 28 00 01 01 02 01
Strings:
Insyde_ASF_001
Insyde_ASF_002
Handle 0x0029, DMI type 130, 20 bytes
OEM-specific Type
Header and Data:
82 14 29 00 24 41 4D 54 01 01 01 01 01 A5 1F 02
00 00 00 00
Handle 0x002A, DMI type 136, 6 bytes
OEM-specific Type
Header and Data:
88 06 2A 00 FF FF
Handle 0xFEFF, DMI type 127, 4 bytes
End Of Table

16
tests/fixtures/inventory/nvme.json vendored Normal file
View file

@ -0,0 +1,16 @@
{
"Devices" : [
{
"DevicePath" : "/dev/nvme0n1",
"Firmware" : "10604103",
"Index" : 0,
"ModelNumber" : "KXG60ZNV1T02 NVMe TOSHIBA 1024GB",
"ProductName" : "Non-Volatile memory controller: Toshiba America Info Systems Device 0x011a",
"SerialNumber" : "19FA109SK07N",
"UsedBytes" : 1024209543168,
"MaximumLBA" : 2000409264,
"PhysicalSize" : 1024209543168,
"SectorSize" : 512
}
]
}

56
tests/fixtures/lldp/cumulus.txt vendored Normal file
View file

@ -0,0 +1,56 @@
lldp.eno1.via=LLDP
lldp.eno1.rid=4
lldp.eno1.age=35 days, 08:24:00
lldp.eno1.chassis.mac=e4:f0:04:b6:47:32
lldp.eno1.chassis.name=toto-chassis.dc42
lldp.eno1.chassis.descr=Cumulus Linux version 3.7.3 running on Dell EMC S4100
lldp.eno1.chassis.ttl=120
lldp.eno1.chassis.mgmt-ip=10.160.20.18
lldp.eno1.chassis.mgmt-ip=fe80::e6f0:4ff:feb6:4732
lldp.eno1.chassis.Bridge.enabled=on
lldp.eno1.chassis.Router.enabled=on
lldp.eno1.port.ifname=swp46
lldp.eno1.port.descr=BXVWNR2:lom2
lldp.eno1.port.auto-negotiation.supported=yes
lldp.eno1.port.auto-negotiation.enabled=no
lldp.eno1.port.auto-negotiation.current=10GigBaseLR - R fiber over 1310 nm optics
lldp.eno1.lldp-med.device-type=Network Connectivity Device
lldp.eno1.lldp-med.Capabilities.available=yes
lldp.eno1.lldp-med.Policy.available=yes
lldp.eno1.lldp-med.Location.available=yes
lldp.eno1.lldp-med.MDI/PSE.available=yes
lldp.eno1.lldp-med.MDI/PD.available=yes
lldp.eno1.lldp-med.Inventory.available=yes
lldp.eno1.lldp-med.inventory.software=3.7.3
lldp.eno1.lldp-med.inventory.firmware=5.6.5
lldp.eno1.lldp-med.inventory.serial=To be filled by O.E.M.
lldp.eno1.lldp-med.inventory.manufacturer=Dell EMC
lldp.eno1.lldp-med.inventory.model=S4100
lldp.eno2d1.via=LLDP
lldp.eno2d1.rid=3
lldp.eno2d1.age=35 days, 08:30:52
lldp.eno2d1.chassis.mac=e4:f0:04:b6:4e:32
lldp.eno1.chassis.name=toto-chassis.dc42
lldp.eno2d1.chassis.descr=Cumulus Linux version 3.7.5 running on Dell EMC S4100
lldp.eno2d1.chassis.ttl=120
lldp.eno2d1.chassis.mgmt-ip=10.160.20.17
lldp.eno2d1.chassis.mgmt-ip=fe80::e6f0:4ff:feb6:4e32
lldp.eno2d1.chassis.Bridge.enabled=on
lldp.eno2d1.chassis.Router.enabled=on
lldp.eno2d1.port.ifname=swp46
lldp.eno2d1.port.descr=BXVWNR2:lom1
lldp.eno2d1.port.auto-negotiation.supported=yes
lldp.eno2d1.port.auto-negotiation.enabled=no
lldp.eno2d1.port.auto-negotiation.current=10GigBaseLR - R fiber over 1310 nm optics
lldp.eno2d1.lldp-med.device-type=Network Connectivity Device
lldp.eno2d1.lldp-med.Capabilities.available=yes
lldp.eno2d1.lldp-med.Policy.available=yes
lldp.eno2d1.lldp-med.Location.available=yes
lldp.eno2d1.lldp-med.MDI/PSE.available=yes
lldp.eno2d1.lldp-med.MDI/PD.available=yes
lldp.eno2d1.lldp-med.Inventory.available=yes
lldp.eno2d1.lldp-med.inventory.software=3.7.5
lldp.eno2d1.lldp-med.inventory.firmware=5.6.5
lldp.eno2d1.lldp-med.inventory.serial=To be filled by O.E.M.
lldp.eno2d1.lldp-med.inventory.manufacturer=Dell EMC
lldp.eno2d1.lldp-med.inventory.model=S4100

8
tests/fixtures/lldp/dedibox1.txt vendored Normal file
View file

@ -0,0 +1,8 @@
lldp.enp1s0f0.via=LLDP
lldp.enp1s0f0.rid=1
lldp.enp1s0f0.age=145 days, 00:00:39
lldp.enp1s0f0.chassis.mac=00:07:cb:0b:6c:d7
lldp.enp1s0f0.chassis.name=s120-f3-2.itx4
lldp.enp1s0f0.chassis.mgmt-ip=10.48.16.15
lldp.enp1s0f0.port.ifname=RJ-9
lldp.enp1s0f0.port.ttl=120

25
tests/fixtures/lldp/dedibox2.txt vendored Normal file
View file

@ -0,0 +1,25 @@
lldp.eno1.via=LLDP
lldp.eno1.rid=1
lldp.eno1.age=1 day, 06:17:13
lldp.eno1.chassis.mac=38:20:56:67:90:80
lldp.eno1.chassis.name=s35-b12.dc3
lldp.eno1.chassis.descr=Cisco IOS Software, C2960X Software (C2960X-UNIVERSALK9-M), Version 15.0(2a)EX5, RELEASE SOFTWARE (fc3)
Technical Support: http://www.cisco.com/techsupport
Copyright (c) 1986-2015 by Cisco Systems, Inc.
Compiled Mon 16-Feb-15 08:16 by prod_rel_team
lldp.eno1.chassis.mgmt-ip=10.43.31.35
lldp.eno1.chassis.Bridge.enabled=on
lldp.eno1.chassis.Router.enabled=off
lldp.eno1.port.ifname=Gi1/0/29
lldp.eno1.port.descr=GigabitEthernet1/0/29
lldp.eno1.port.auto-negotiation.supported=yes
lldp.eno1.port.auto-negotiation.enabled=yes
lldp.eno1.port.auto-negotiation.10Base-T.hd=yes
lldp.eno1.port.auto-negotiation.10Base-T.fd=yes
lldp.eno1.port.auto-negotiation.100Base-TX.hd=yes
lldp.eno1.port.auto-negotiation.100Base-TX.fd=yes
lldp.eno1.port.auto-negotiation.1000Base-T.hd=no
lldp.eno1.port.auto-negotiation.1000Base-T.fd=yes
lldp.eno1.port.auto-negotiation.current=1000BaseTFD - Four-pair Category 5 UTP, full duplex mode
lldp.eno1.vlan.vlan-id=140
lldp.eno1.vlan.pvid=yes

38
tests/fixtures/lldp/qfx.txt vendored Normal file
View file

@ -0,0 +1,38 @@
lldp.eth0.via=LLDP
lldp.eth0.rid=1
lldp.eth0.age=163 days, 23:03:53
lldp.eth0.chassis.mac=40:a6:77:7a:72:00
lldp.eth0.chassis.name=sw-filer-f06.dc42
lldp.eth0.chassis.descr=Juniper Networks, Inc. qfx5100-48s-6q Ethernet Switch, kernel JUNOS 14.1X53-D43.7, Build date: 2017-04-28 02:22:48 UTC Copyright (c) 1996-2017 Juniper Networks, Inc.
lldp.eth0.chassis.mgmt-ip=10.192.192.116
lldp.eth0.chassis.Bridge.enabled=on
lldp.eth0.chassis.Router.enabled=on
lldp.eth0.port.local=512
lldp.eth0.port.descr=xe-0/0/1
lldp.eth0.port.mfs=1514
lldp.eth0.vlan.vlan-id=296
lldp.eth0.vlan.pvid=yes
lldp.eth0.vlan=vlan-296
lldp.eth0.unknown-tlvs.unknown-tlv.oui=00,90,69
lldp.eth0.unknown-tlvs.unknown-tlv.subtype=1
lldp.eth0.unknown-tlvs.unknown-tlv.len=12
lldp.eth0.unknown-tlvs.unknown-tlv=56,46,33,37,31,35,30,33,30,31,36,34
lldp.eth1.via=LLDP
lldp.eth1.rid=2
lldp.eth1.age=163 days, 23:03:51
lldp.eth1.chassis.mac=40:a6:77:7c:fb:20
lldp.eth1.chassis.name=sw-filer-f05.dc42
lldp.eth1.chassis.descr=Juniper Networks, Inc. qfx5100-48s-6q Ethernet Switch, kernel JUNOS 17.3R3-S3.3, Build date: 2019-01-10 19:17:42 UTC Copyright (c) 1996-2019 Juniper Networks, Inc.
lldp.eth1.chassis.mgmt-ip=10.192.192.115
lldp.eth1.chassis.Bridge.enabled=on
lldp.eth1.chassis.Router.enabled=on
lldp.eth1.port.local=512
lldp.eth1.port.descr=xe-0/0/1
lldp.eth1.port.mfs=1514
lldp.eth1.vlan.vlan-id=296
lldp.eth1.vlan.pvid=yes
lldp.eth1.vlan=vlan-296
lldp.eth1.unknown-tlvs.unknown-tlv.oui=00,90,69
lldp.eth1.unknown-tlvs.unknown-tlv.subtype=1
lldp.eth1.unknown-tlvs.unknown-tlv.len=12
lldp.eth1.unknown-tlvs.unknown-tlv=56,46,33,37,31,35,30,33,30,30,34,35

16
tests/fixtures/netbox_agent.conf1.ok vendored Normal file
View file

@ -0,0 +1,16 @@
netbox:
url: 'https://netbox.company.com'
token: xx
datacenter_location:
driver: "cmd:cat /etc/qualification | tr [A-Z] [a-z]"
regex: "datacenter: (?P<datacenter>[A-Za-z0-9]+)"
rack_location:
driver: 'cmd:lldpctl'
regex: 'SysName:[ ]+[A-Za-z]+-[A-Za-z]+-([A-Za-z0-9]+)'
network:
ignore_interfaces: "(dummy.*|docker.*)"
ignore_ips: (127\.0\.0\..*)
lldp: true

24
tests/network.py Normal file
View file

@ -0,0 +1,24 @@
from netbox_agent.lldp import LLDP
from tests.conftest import parametrize_with_fixtures
@parametrize_with_fixtures(
"lldp/",
only_filenames=[
"dedibox1.txt",
],
)
def test_lldp_parse_with_port_desc(fixture):
lldp = LLDP(fixture)
assert lldp.get_switch_port("enp1s0f0") == "RJ-9"
@parametrize_with_fixtures(
"lldp/",
only_filenames=[
"qfx.txt",
],
)
def test_lldp_parse_without_ifname(fixture):
lldp = LLDP(fixture)
assert lldp.get_switch_port("eth0") == "xe-0/0/1"

91
tests/server.py Normal file
View file

@ -0,0 +1,91 @@
from netbox_agent.dmidecode import parse
from netbox_agent.server import ServerBase
from netbox_agent.vendors.hp import HPHost
from netbox_agent.vendors.qct import QCTHost
from netbox_agent.vendors.supermicro import SupermicroHost
from tests.conftest import parametrize_with_fixtures
@parametrize_with_fixtures("dmidecode/")
def test_init(fixture):
dmi = parse(fixture)
server = ServerBase(dmi)
assert server
@parametrize_with_fixtures(
"dmidecode/",
only_filenames=[
"HP_SL4540_Gen8",
"HP_BL460c_Gen9",
"HP_DL380p_Gen8",
"HP_SL4540_Gen8" "HP_ProLiant_BL460c_Gen10_Graphics_Exp",
],
)
def test_hp_service_tag(fixture):
dmi = parse(fixture)
server = HPHost(dmi)
assert server.get_service_tag() == "4242"
@parametrize_with_fixtures("dmidecode/", only_filenames=["HP_ProLiant_m710x"])
def test_moonshot_blade(fixture):
dmi = parse(fixture)
server = HPHost(dmi)
assert server.get_service_tag() == "CN66480BLA"
assert server.get_chassis_service_tag() == "CZ3702MD5K"
assert server.is_blade() is True
assert server.own_expansion_slot() is False
@parametrize_with_fixtures("dmidecode/", only_filenames=["SYS-5039MS-H12TRF-OS012.txt"])
def test_supermicro_blade(fixture):
dmi = parse(fixture)
server = SupermicroHost(dmi)
assert server.get_service_tag() == "E235735X6B01665"
assert server.get_chassis_service_tag() == "C9390AF40A20098"
assert server.get_chassis() == "SYS-5039MS-H12TRF-OS012"
assert server.is_blade() is True
@parametrize_with_fixtures("dmidecode/", only_filenames=["SM_SYS-6018R"])
def test_supermicro_pizza(fixture):
dmi = parse(fixture)
server = SupermicroHost(dmi)
assert server.get_service_tag() == "A177950X7709591"
assert server.get_chassis() == "SYS-6018R-TDTPR"
assert server.is_blade() is False
@parametrize_with_fixtures("dmidecode/", only_filenames=["QCT_X10E-9N"])
def test_qct_x10(fixture):
dmi = parse(fixture)
server = QCTHost(dmi)
assert server.get_service_tag() == "QTFCQ57140285"
@parametrize_with_fixtures("dmidecode/", only_filenames=["unknown.txt"])
def test_generic_host_service_tag(fixture):
dmi = parse(fixture)
server = ServerBase(dmi)
assert server.get_service_tag() == "42"
@parametrize_with_fixtures("dmidecode/", only_filenames=["unknown.txt"])
def test_generic_host_product_name(fixture):
dmi = parse(fixture)
server = ServerBase(dmi)
assert server.get_product_name() == "SR"
@parametrize_with_fixtures(
"dmidecode/", only_filenames=["HP_ProLiant_BL460c_Gen10_Graphics_Exp"]
)
def test_hp_blade_with_gpu_expansion(fixture):
dmi = parse(fixture)
server = HPHost(dmi)
assert server.get_service_tag() == "4242"
assert server.get_chassis_service_tag() == "4343"
assert server.is_blade() is True
assert server.own_expansion_slot() is True
assert server.get_expansion_service_tag() == "4242 expansion"

40
tox.ini
View file

@ -2,38 +2,16 @@
# These are the default environments that will be run # These are the default environments that will be run
# when ``tox`` is run without arguments. # when ``tox`` is run without arguments.
envlist = envlist =
py35 pytest
py36 flake8
py37
coverage
mypy
pep8
docs
skip_missing_interpreters = True skip_missing_interpreters = True
[flake8]
# Use the more relaxed max line length permitted in PEP8.
max-line-length = 99
# Enforce the Google Python style for grouping and sorting imports:
# https://github.com/google/styleguide/blob/gh-pages/pyguide.md#313-imports-formatting
import-order-style = google
# Inform flake8-import-order plugin that `fact` should be treated as a local package name.
application-import-names = netbox_agent
[testenv] [testenv]
# This is required in order to get UTF-8 output inside of the subprocesses deps = -r{toxinidir}/dev-requirements.txt
# that our tests use. whitelist_externals = bash
setenv = LC_CTYPE = en_US.UTF-8
# Pass Display down to have it for the tests available
passenv = DISPLAY TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH
whitelist_externals=convert
deps =
-r{toxinidir}/requirements.txt
[testenv:pep8] [testenv:pytest]
deps = commands = bash tests.sh
flake8
pep8-naming [testenv:flake8]
commands = flake8 {toxinidir}/netbox_agent {toxinidir}/tests commands = flake8