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.raid.hp import HPRaid
|
||||
from netbox_agent.raid.storcli import StorcliRaid
|
||||
from netbox_agent.lshw import LSHW
|
||||
|
||||
INVENTORY_TAG = {
|
||||
'cpu': {'name': 'hw:cpu', 'slug': 'hw-cpu'},
|
||||
'memory': {'name': 'hw:memory', 'slug': 'hw-memory'},
|
||||
'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'},
|
||||
}
|
||||
|
||||
|
|
@ -46,6 +48,25 @@ class Inventory():
|
|||
self.raid = None
|
||||
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():
|
||||
for key, tag in INVENTORY_TAG.items():
|
||||
nb_tag = nb.extras.tags.get(
|
||||
|
|
@ -61,37 +82,34 @@ class Inventory():
|
|||
def get_cpus(self):
|
||||
model = None
|
||||
nb = None
|
||||
>>>>>>> d39b692985797707ab33ee927e9d45bef0a73638
|
||||
|
||||
output = subprocess.getoutput('lscpu')
|
||||
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
|
||||
return manufacturer
|
||||
|
||||
def create_netbox_cpus(self):
|
||||
nb_cpus, model = self.get_cpus()
|
||||
for i in range(nb_cpus):
|
||||
for cpu in self.lshw.get_hw_linux('cpu'):
|
||||
manufacturer = self.find_or_create_manufacturer(cpu["vendor"])
|
||||
_ = nb.dcim.inventory_items.create(
|
||||
device=self.device_id,
|
||||
tags=[INVENTORY_TAG['cpu']['name']],
|
||||
name=model,
|
||||
manufacturer = manufacturer.id,
|
||||
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):
|
||||
cpus_number, model = self.get_cpus()
|
||||
cpus = self.lshw.get_hw_linux('cpu')
|
||||
nb_cpus = nb.dcim.inventory_items.filter(
|
||||
device_id=self.device_id,
|
||||
tag=INVENTORY_TAG['cpu']['slug'],
|
||||
)
|
||||
|
||||
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:
|
||||
x.delete()
|
||||
self.create_netbox_cpus()
|
||||
|
|
@ -118,20 +136,6 @@ class Inventory():
|
|||
)
|
||||
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):
|
||||
manufacturer = self.find_or_create_manufacturer(
|
||||
raid_card.get_manufacturer()
|
||||
|
|
@ -186,10 +190,41 @@ class Inventory():
|
|||
self.create_netbox_raid_card(raid_card)
|
||||
|
||||
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():
|
||||
ret += raid_card.get_physical_disks()
|
||||
return ret
|
||||
disks += raid_card.get_physical_disks()
|
||||
|
||||
return disks
|
||||
|
||||
def get_netbox_disks(self):
|
||||
disks = nb.dcim.inventory_items.filter(
|
||||
|
|
@ -200,12 +235,24 @@ class Inventory():
|
|||
|
||||
def create_netbox_disks(self):
|
||||
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(
|
||||
device=self.device_id,
|
||||
discovered=True,
|
||||
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'],
|
||||
part_id=disk['Model'],
|
||||
description='{}'.format(disk.get('logicalname', 'Unknown')),
|
||||
# asset_tag=disk['logicalname'],
|
||||
manufacturer=m_id
|
||||
)
|
||||
logging.info('Creating Disk {model} {serial}'.format(
|
||||
model=disk['Model'],
|
||||
|
|
@ -298,7 +345,7 @@ class Inventory():
|
|||
for memory in self.get_memory():
|
||||
self.create_netbox_memory(memory)
|
||||
|
||||
def update_netbox_memory(self):
|
||||
def update_netbox_memories(self):
|
||||
memories = self.get_memory()
|
||||
nb_memories = self.get_netbox_memory()
|
||||
|
||||
|
|
@ -316,7 +363,7 @@ class Inventory():
|
|||
if config.inventory is None:
|
||||
return False
|
||||
self.create_netbox_cpus()
|
||||
self.create_netbox_memory()
|
||||
self.create_netbox_memories()
|
||||
self.create_netbox_raid_cards()
|
||||
self.create_netbox_disks()
|
||||
return True
|
||||
|
|
@ -325,7 +372,7 @@ class Inventory():
|
|||
if config.inventory is None or config.update_inventory is None:
|
||||
return False
|
||||
self.update_netbox_cpus()
|
||||
self.update_netbox_memory()
|
||||
self.update_netbox_memories()
|
||||
self.update_netbox_raid_cards()
|
||||
self.update_netbox_disks()
|
||||
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
|
||||
else:
|
||||
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.chassis = self.dmi.get_by_type('Chassis')
|
||||
self.system = self.dmi.get_by_type('System')
|
||||
|
||||
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.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):
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
|
@ -8,8 +20,8 @@ class SupermicroHost(ServerBase):
|
|||
self.manufacturer = 'Supermicro'
|
||||
|
||||
def is_blade(self):
|
||||
blade = self.get_product_name().startswith('SBI')
|
||||
blade |= self.get_product_name().startswith('SYS')
|
||||
blade = self.system[0]['Product Name'].startswith('SBI')
|
||||
blade |= self.system[0]['Product Name'].startswith('SYS')
|
||||
return blade
|
||||
|
||||
def get_blade_slot(self):
|
||||
|
|
@ -21,17 +33,26 @@ class SupermicroHost(ServerBase):
|
|||
# No supermicro on hands
|
||||
return None
|
||||
|
||||
def get_chassis_name(self):
|
||||
if not self.is_blade():
|
||||
return None
|
||||
return 'Chassis {}'.format(self.get_service_tag())
|
||||
def get_service_tag(self):
|
||||
return self.baseboard[0]['Serial Number'].strip()
|
||||
|
||||
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):
|
||||
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()
|
||||
|
||||
def get_chassis_service_tag(self):
|
||||
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()
|
||||
|
||||
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