wip: use lshw for inventory, do cpu/disks/memory/network/motherboard; update supermicro fatwin support.
This commit is contained in:
parent
d39b692985
commit
c312396326
4 changed files with 263 additions and 47 deletions
|
|
@ -6,11 +6,13 @@ from netbox_agent.config import netbox_instance as nb, config
|
||||||
from netbox_agent.misc import is_tool
|
from netbox_agent.misc import is_tool
|
||||||
from netbox_agent.raid.hp import HPRaid
|
from netbox_agent.raid.hp import HPRaid
|
||||||
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'},
|
||||||
'memory': {'name': 'hw:memory', 'slug': 'hw-memory'},
|
|
||||||
'disk': {'name': 'hw:disk', 'slug': 'hw-disk'},
|
'disk': {'name': 'hw:disk', 'slug': 'hw-disk'},
|
||||||
|
'memory': {'name': 'hw:memory', 'slug': 'hw-memory'},
|
||||||
|
'network':{'name': 'hw:network', 'slug':'hw-network'},
|
||||||
'raid_card': {'name': 'hw:raid_card', 'slug': 'hw-raid-card'},
|
'raid_card': {'name': 'hw:raid_card', 'slug': 'hw-raid-card'},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,6 +48,25 @@ class Inventory():
|
||||||
self.raid = None
|
self.raid = None
|
||||||
self.disks = []
|
self.disks = []
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
self.lshw = LSHW()
|
||||||
|
|
||||||
|
def find_or_create_manufacturer(self, name):
|
||||||
|
if name is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
manufacturer = nb.dcim.manufacturers.get(
|
||||||
|
name=name,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not manufacturer:
|
||||||
|
logging.info('Creating missing manufacturer {name}'.format(name=name))
|
||||||
|
manufacturer = nb.dcim.manufacturers.create(
|
||||||
|
name=name,
|
||||||
|
slug=name.replace(' ','-').lower(),
|
||||||
|
)
|
||||||
|
logging.info('Creating missing manufacturer {name}'.format(name=name))
|
||||||
|
=======
|
||||||
def create_netbox_tags():
|
def create_netbox_tags():
|
||||||
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(
|
||||||
|
|
@ -61,37 +82,34 @@ class Inventory():
|
||||||
def get_cpus(self):
|
def get_cpus(self):
|
||||||
model = None
|
model = None
|
||||||
nb = None
|
nb = None
|
||||||
|
>>>>>>> d39b692985797707ab33ee927e9d45bef0a73638
|
||||||
|
|
||||||
output = subprocess.getoutput('lscpu')
|
return manufacturer
|
||||||
model_re = re.search(r'Model name: (.*)', output)
|
|
||||||
if len(model_re.groups()) > 0:
|
|
||||||
model = model_re.groups()[0].strip()
|
|
||||||
socket_re = re.search(r'Socket\(s\): (.*)', output)
|
|
||||||
if len(socket_re.groups()) > 0:
|
|
||||||
nb = int(socket_re.groups()[0].strip())
|
|
||||||
return nb, model
|
|
||||||
|
|
||||||
def create_netbox_cpus(self):
|
def create_netbox_cpus(self):
|
||||||
nb_cpus, model = self.get_cpus()
|
for cpu in self.lshw.get_hw_linux('cpu'):
|
||||||
for i in range(nb_cpus):
|
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,
|
||||||
tags=[INVENTORY_TAG['cpu']['name']],
|
manufacturer = manufacturer.id,
|
||||||
name=model,
|
|
||||||
discovered=True,
|
discovered=True,
|
||||||
description='CPU',
|
tags=[INVENTORY_TAG['cpu']['name']],
|
||||||
|
name=cpu['product'],
|
||||||
|
description='{}'.format(cpu['location']),
|
||||||
|
asset_tag=cpu['location']
|
||||||
)
|
)
|
||||||
logging.info('Creating CPU model {model}'.format(model=model))
|
|
||||||
|
logging.info('Creating CPU model {}'.format(cpu['product']))
|
||||||
|
|
||||||
def update_netbox_cpus(self):
|
def update_netbox_cpus(self):
|
||||||
cpus_number, model = self.get_cpus()
|
cpus = self.lshw.get_hw_linux('cpu')
|
||||||
nb_cpus = nb.dcim.inventory_items.filter(
|
nb_cpus = nb.dcim.inventory_items.filter(
|
||||||
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 cpus_number != 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()
|
||||||
|
|
@ -118,20 +136,6 @@ class Inventory():
|
||||||
)
|
)
|
||||||
return raid_cards
|
return raid_cards
|
||||||
|
|
||||||
def find_or_create_manufacturer(self, name):
|
|
||||||
if name is None:
|
|
||||||
return None
|
|
||||||
manufacturer = nb.dcim.manufacturers.get(
|
|
||||||
name=name,
|
|
||||||
)
|
|
||||||
if not manufacturer:
|
|
||||||
manufacturer = nb.dcim.manufacturers.create(
|
|
||||||
name=name,
|
|
||||||
slug=name.lower(),
|
|
||||||
)
|
|
||||||
logging.info('Creating missing manufacturer {name}'.format(name=name))
|
|
||||||
return manufacturer
|
|
||||||
|
|
||||||
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()
|
||||||
|
|
@ -186,10 +190,41 @@ class Inventory():
|
||||||
self.create_netbox_raid_card(raid_card)
|
self.create_netbox_raid_card(raid_card)
|
||||||
|
|
||||||
def get_disks(self):
|
def get_disks(self):
|
||||||
ret = []
|
disks = []
|
||||||
|
|
||||||
|
for disk in self.lshw.get_hw_linux("storage"):
|
||||||
|
print(disk)
|
||||||
|
d = {}
|
||||||
|
d["name"] = ""
|
||||||
|
d['Size'] = '{} GB'.format(int(disk['size']/1024/1024/1024))
|
||||||
|
d['Model'] = disk['product']
|
||||||
|
d['logicalname'] = disk['logicalname']
|
||||||
|
d['description'] = disk['description']
|
||||||
|
|
||||||
|
if 'serial' in disk:
|
||||||
|
d['SN'] = disk['serial']
|
||||||
|
|
||||||
|
if 'vendor' in disk:
|
||||||
|
d['vendor'] = disk['vendor']
|
||||||
|
|
||||||
|
if disk['product'].startswith('ST'):
|
||||||
|
d['vendor'] = 'Seagate'
|
||||||
|
|
||||||
|
if disk['product'].startswith('Crucial'):
|
||||||
|
d['vendor'] = 'Crucial'
|
||||||
|
|
||||||
|
if disk['product'].startswith('INTEL'):
|
||||||
|
d['vendor'] = 'Intel'
|
||||||
|
|
||||||
|
if disk['product'].startswith('Samsung'):
|
||||||
|
d['vendor'] = 'Samsung'
|
||||||
|
|
||||||
|
disks.append(d)
|
||||||
|
|
||||||
for raid_card in self.get_raid_cards():
|
for raid_card in self.get_raid_cards():
|
||||||
ret += raid_card.get_physical_disks()
|
disks += raid_card.get_physical_disks()
|
||||||
return ret
|
|
||||||
|
return disks
|
||||||
|
|
||||||
def get_netbox_disks(self):
|
def get_netbox_disks(self):
|
||||||
disks = nb.dcim.inventory_items.filter(
|
disks = nb.dcim.inventory_items.filter(
|
||||||
|
|
@ -200,12 +235,24 @@ class Inventory():
|
||||||
|
|
||||||
def create_netbox_disks(self):
|
def create_netbox_disks(self):
|
||||||
for disk in self.get_disks():
|
for disk in self.get_disks():
|
||||||
|
m_id = 0
|
||||||
|
if "vendor" in disk:
|
||||||
|
manufacturer = self.find_or_create_manufacturer(disk["vendor"])
|
||||||
|
m_id = manufacturer.id
|
||||||
|
|
||||||
|
print("inserting disk")
|
||||||
|
print(disk)
|
||||||
|
|
||||||
_ = nb.dcim.inventory_items.create(
|
_ = nb.dcim.inventory_items.create(
|
||||||
device=self.device_id,
|
device=self.device_id,
|
||||||
discovered=True,
|
discovered=True,
|
||||||
tags=[INVENTORY_TAG['disk']['name']],
|
tags=[INVENTORY_TAG['disk']['name']],
|
||||||
name='{} ({})'.format(disk['Model'], disk['Size']),
|
name='{} - {} ({})'.format(disk.get('description', 'Unknown'), disk.get('logicalname', 'Unknown'), disk.get('Size', 0)),
|
||||||
serial=disk['SN'],
|
serial=disk['SN'],
|
||||||
|
part_id=disk['Model'],
|
||||||
|
description='{}'.format(disk.get('logicalname', 'Unknown')),
|
||||||
|
# asset_tag=disk['logicalname'],
|
||||||
|
manufacturer=m_id
|
||||||
)
|
)
|
||||||
logging.info('Creating Disk {model} {serial}'.format(
|
logging.info('Creating Disk {model} {serial}'.format(
|
||||||
model=disk['Model'],
|
model=disk['Model'],
|
||||||
|
|
@ -298,7 +345,7 @@ class Inventory():
|
||||||
for memory in self.get_memory():
|
for memory in self.get_memory():
|
||||||
self.create_netbox_memory(memory)
|
self.create_netbox_memory(memory)
|
||||||
|
|
||||||
def update_netbox_memory(self):
|
def update_netbox_memories(self):
|
||||||
memories = self.get_memory()
|
memories = self.get_memory()
|
||||||
nb_memories = self.get_netbox_memory()
|
nb_memories = self.get_netbox_memory()
|
||||||
|
|
||||||
|
|
@ -316,7 +363,7 @@ class Inventory():
|
||||||
if config.inventory is None:
|
if config.inventory is None:
|
||||||
return False
|
return False
|
||||||
self.create_netbox_cpus()
|
self.create_netbox_cpus()
|
||||||
self.create_netbox_memory()
|
self.create_netbox_memories()
|
||||||
self.create_netbox_raid_cards()
|
self.create_netbox_raid_cards()
|
||||||
self.create_netbox_disks()
|
self.create_netbox_disks()
|
||||||
return True
|
return True
|
||||||
|
|
@ -325,7 +372,7 @@ class Inventory():
|
||||||
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
|
||||||
self.update_netbox_cpus()
|
self.update_netbox_cpus()
|
||||||
self.update_netbox_memory()
|
self.update_netbox_memories()
|
||||||
self.update_netbox_raid_cards()
|
self.update_netbox_raid_cards()
|
||||||
self.update_netbox_disks()
|
self.update_netbox_disks()
|
||||||
return True
|
return True
|
||||||
|
|
|
||||||
145
netbox_agent/lshw.py
Normal file
145
netbox_agent/lshw.py
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import getpass
|
||||||
|
import json
|
||||||
|
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
class LSHW():
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
self.hw_info = json.loads(subprocess.check_output(["lshw", "-quiet", "-json"],encoding='utf8'))
|
||||||
|
|
||||||
|
self.info = {}
|
||||||
|
self.memories = []
|
||||||
|
self.interfaces = []
|
||||||
|
self.cpus = []
|
||||||
|
self.power = []
|
||||||
|
self.disks = []
|
||||||
|
self.vendor = self.hw_info["vendor"]
|
||||||
|
self.product = self.hw_info["product"]
|
||||||
|
self.chassis_serial = self.hw_info["serial"]
|
||||||
|
self.motherboard_serial = self.hw_info["children"][0]["serial"]
|
||||||
|
self.motherboard = self.hw_info["children"][0]["product"]
|
||||||
|
|
||||||
|
for k in self.hw_info["children"]:
|
||||||
|
if k["class"] == "power":
|
||||||
|
# self.power[k["id"]] = k
|
||||||
|
self.power.append(k)
|
||||||
|
|
||||||
|
if "children" in k:
|
||||||
|
for j in k["children"]:
|
||||||
|
if j["class"] == "generic":
|
||||||
|
continue
|
||||||
|
|
||||||
|
if j["class"] == "storage":
|
||||||
|
self.find_storage(j)
|
||||||
|
|
||||||
|
if j["class"] == "memory":
|
||||||
|
self.find_memories(j)
|
||||||
|
|
||||||
|
if j["class"] == "processor":
|
||||||
|
self.find_cpus(j)
|
||||||
|
|
||||||
|
if j["class"] == "bridge":
|
||||||
|
self.walk_bridge(j)
|
||||||
|
|
||||||
|
def get_hw_linux(self, hwclass):
|
||||||
|
if hwclass == "cpu":
|
||||||
|
return self.cpus
|
||||||
|
if hwclass == "network":
|
||||||
|
return self.interfaces
|
||||||
|
if hwclass == 'storage':
|
||||||
|
return self.disks
|
||||||
|
if hwclass == 'memory':
|
||||||
|
return self.memories
|
||||||
|
|
||||||
|
def find_network(self, obj):
|
||||||
|
d = {}
|
||||||
|
d["name"] = obj["logicalname"]
|
||||||
|
d["macaddress"] = obj["serial"]
|
||||||
|
d["serial"] = obj["serial"]
|
||||||
|
d["product"] = obj["product"]
|
||||||
|
d["vendor"] = obj["vendor"]
|
||||||
|
d["description"] = obj["description"]
|
||||||
|
|
||||||
|
self.interfaces.append(d)
|
||||||
|
|
||||||
|
def find_storage(self, obj):
|
||||||
|
if "children" in obj:
|
||||||
|
for device in obj["children"]:
|
||||||
|
d = {}
|
||||||
|
d["logicalname"] = device["logicalname"]
|
||||||
|
d["product"] = device["product"]
|
||||||
|
d["serial"] = device["serial"]
|
||||||
|
d["version"] = device["version"]
|
||||||
|
d["size"] = device["size"]
|
||||||
|
d["description"] = device["description"]
|
||||||
|
|
||||||
|
self.disks.append(d)
|
||||||
|
|
||||||
|
elif "nvme" in obj["configuration"]["driver"]:
|
||||||
|
nvme = json.loads(subprocess.check_output(["nvme", '-list', '-o', 'json'],encoding='utf8'))
|
||||||
|
|
||||||
|
d = {}
|
||||||
|
d["vendor"] = obj["vendor"]
|
||||||
|
d["version"] = obj["version"]
|
||||||
|
d["product"] = obj["product"]
|
||||||
|
|
||||||
|
d['description'] = "NVME Disk"
|
||||||
|
d['product'] = nvme["Devices"][0]["ModelNumber"]
|
||||||
|
d['size'] = nvme["Devices"][0]["PhysicalSize"]
|
||||||
|
d['serial'] = nvme["Devices"][0]["SerialNumber"]
|
||||||
|
d['logicalname'] = nvme["Devices"][0]["DevicePath"]
|
||||||
|
|
||||||
|
self.disks.append(d)
|
||||||
|
|
||||||
|
def find_cpus(self, obj):
|
||||||
|
pprint(obj)
|
||||||
|
c = {}
|
||||||
|
c["product"] = obj["product"]
|
||||||
|
c["vendor"] = obj["vendor"]
|
||||||
|
c["description"] = obj["description"]
|
||||||
|
c["location"] = obj["slot"]
|
||||||
|
|
||||||
|
self.cpus.append(c)
|
||||||
|
|
||||||
|
def find_memories(self, obj):
|
||||||
|
if "children" not in obj:
|
||||||
|
print("not a DIMM memory.")
|
||||||
|
return
|
||||||
|
|
||||||
|
for dimm in obj["children"]:
|
||||||
|
if "empty" in dimm["description"]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
d = {}
|
||||||
|
d["slot"] = dimm["slot"]
|
||||||
|
d["description"] = dimm["description"]
|
||||||
|
d["id"] = dimm["id"]
|
||||||
|
d["serial"] = dimm["serial"]
|
||||||
|
d["vendor"] = dimm["vendor"]
|
||||||
|
d["product"] = dimm["product"]
|
||||||
|
d["size"] = dimm["size"] / 2 ** 20 / 1024
|
||||||
|
|
||||||
|
self.memories.append(d)
|
||||||
|
|
||||||
|
def walk_bridge(self, obj):
|
||||||
|
if "children" not in obj:
|
||||||
|
return
|
||||||
|
|
||||||
|
for bus in obj["children"]:
|
||||||
|
if bus["class"] == "storage":
|
||||||
|
self.find_storage(bus)
|
||||||
|
|
||||||
|
if "children" in bus:
|
||||||
|
for b in bus["children"]:
|
||||||
|
if b["class"] == "storage":
|
||||||
|
self.find_storage(b)
|
||||||
|
if b["class"] == "network":
|
||||||
|
self.find_network(b)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
pass
|
||||||
|
|
@ -15,8 +15,11 @@ class ServerBase():
|
||||||
self.dmi = dmi
|
self.dmi = dmi
|
||||||
else:
|
else:
|
||||||
self.dmi = dmidecode.parse()
|
self.dmi = dmidecode.parse()
|
||||||
self.system = self.dmi.get_by_type('System')
|
|
||||||
|
self.baseboard = self.dmi.get_by_type('Baseboard')
|
||||||
self.bios = self.dmi.get_by_type('BIOS')
|
self.bios = self.dmi.get_by_type('BIOS')
|
||||||
|
self.chassis = self.dmi.get_by_type('Chassis')
|
||||||
|
self.system = self.dmi.get_by_type('System')
|
||||||
|
|
||||||
self.network = None
|
self.network = None
|
||||||
|
|
||||||
|
|
|
||||||
37
netbox_agent/vendors/supermicro.py
vendored
37
netbox_agent/vendors/supermicro.py
vendored
|
|
@ -1,6 +1,18 @@
|
||||||
from netbox_agent.location import Slot
|
from netbox_agent.location import Slot
|
||||||
from netbox_agent.server import ServerBase
|
from netbox_agent.server import ServerBase
|
||||||
|
|
||||||
|
"""
|
||||||
|
Supermicro DMI can be messed up. They depend on the vendor
|
||||||
|
to set the correct values. The endusers cannot
|
||||||
|
change them without buying a license from Supermicro.
|
||||||
|
|
||||||
|
There are 3 serial numbers in the system
|
||||||
|
|
||||||
|
1) System - this is used for the chassis information.
|
||||||
|
2) Baseboard - this is used for the blade.
|
||||||
|
3) Chassis - this is ignored.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
class SupermicroHost(ServerBase):
|
class SupermicroHost(ServerBase):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
@ -8,8 +20,8 @@ class SupermicroHost(ServerBase):
|
||||||
self.manufacturer = 'Supermicro'
|
self.manufacturer = 'Supermicro'
|
||||||
|
|
||||||
def is_blade(self):
|
def is_blade(self):
|
||||||
blade = self.get_product_name().startswith('SBI')
|
blade = self.system[0]['Product Name'].startswith('SBI')
|
||||||
blade |= self.get_product_name().startswith('SYS')
|
blade |= self.system[0]['Product Name'].startswith('SYS')
|
||||||
return blade
|
return blade
|
||||||
|
|
||||||
def get_blade_slot(self):
|
def get_blade_slot(self):
|
||||||
|
|
@ -21,17 +33,26 @@ class SupermicroHost(ServerBase):
|
||||||
# No supermicro on hands
|
# No supermicro on hands
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_chassis_name(self):
|
def get_service_tag(self):
|
||||||
if not self.is_blade():
|
return self.baseboard[0]['Serial Number'].strip()
|
||||||
return None
|
|
||||||
return 'Chassis {}'.format(self.get_service_tag())
|
def get_product_name(self):
|
||||||
|
if self.is_blade():
|
||||||
|
return self.baseboard[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.dmi.get_by_type('Chassis')[0]['Version']
|
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.dmi.get_by_type('Chassis')[0]['Serial Number']
|
return self.system[0]['Serial Number'].strip()
|
||||||
return self.get_service_tag()
|
return self.get_service_tag()
|
||||||
|
|
||||||
|
def get_chassis_name(self):
|
||||||
|
if not self.is_blade():
|
||||||
|
return None
|
||||||
|
return 'Chassis {}'.format(self.get_chassis_service_tag())
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
editor.link_modal.header
Reference in a new issue