wip - should change the name of manufacturers, maybe other classes than Server etc
This commit is contained in:
parent
a598c3a68a
commit
94e0437427
8 changed files with 277 additions and 0 deletions
6
netbox_agent/__init__.py
Normal file
6
netbox_agent/__init__.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
from pkg_resources import get_distribution, DistributionNotFound
|
||||||
|
|
||||||
|
try:
|
||||||
|
__version__ = get_distribution(__name__).version
|
||||||
|
except DistributionNotFound:
|
||||||
|
pass
|
0
netbox_agent/dell/__init__.py
Normal file
0
netbox_agent/dell/__init__.py
Normal file
86
netbox_agent/dell/dell.py
Normal file
86
netbox_agent/dell/dell.py
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
import socket
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
from netbox_agent.server import ServerBase
|
||||||
|
from netbox_agent.config import netbox_instance as nb
|
||||||
|
|
||||||
|
class DellHost(ServerBase):
|
||||||
|
def is_blade(self):
|
||||||
|
return self.get_product_name().startswith('PowerEdge M')
|
||||||
|
|
||||||
|
def get_blade_slot(self):
|
||||||
|
'''
|
||||||
|
Return blade slot
|
||||||
|
dmidecode output is:
|
||||||
|
` Location In Chassis: Slot 03`
|
||||||
|
'''
|
||||||
|
if self.is_blade():
|
||||||
|
return int(self.dmi.get('base board')[0].get('Location In Chassis').split()[1])
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_chassis(self):
|
||||||
|
if self.is_blade():
|
||||||
|
return self.dmi.get('chassis')[0]['Version']
|
||||||
|
return self.get_product_name()
|
||||||
|
|
||||||
|
def get_chassis_service_tag(self):
|
||||||
|
if self.is_blade():
|
||||||
|
return self.dmi.get('chassis')[0]['Serial Number']
|
||||||
|
return self.get_service_tag
|
||||||
|
|
||||||
|
def netbox_create(self):
|
||||||
|
if self.is_blade():
|
||||||
|
# let's find the bblade
|
||||||
|
blade = nb.dcim.devices.get(serial=self.get_service_tag())
|
||||||
|
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
|
||||||
|
if not chassis:
|
||||||
|
device_type = nb.dcim.device_types.get(
|
||||||
|
model=self.get_chassis(),
|
||||||
|
)
|
||||||
|
device_role = nb.dcim.device_roles.get(
|
||||||
|
name='Server Chassis',
|
||||||
|
)
|
||||||
|
datacenter = nb.dcim.sites.get(
|
||||||
|
name='DC3'
|
||||||
|
)
|
||||||
|
new_chassis = nb.dcim.devices.create(
|
||||||
|
name=''.format(),
|
||||||
|
device_type=device_type.id,
|
||||||
|
serial=self.get_chassis_service_tag(),
|
||||||
|
device_role=device_role.id,
|
||||||
|
site=datacenter.id,
|
||||||
|
)
|
||||||
|
chassis = new_chassis
|
||||||
|
|
||||||
|
device_role = nb.dcim.device_roles.get(
|
||||||
|
name='Blade',
|
||||||
|
)
|
||||||
|
device_type = nb.dcim.device_types.get(
|
||||||
|
model=self.get_product_name(),
|
||||||
|
)
|
||||||
|
|
||||||
|
new_blade = nb.dcim.devices.create(
|
||||||
|
name='{}'.format(socket.gethostname()),
|
||||||
|
serial=self.get_service_tag(),
|
||||||
|
device_role=device_role.id,
|
||||||
|
device_type=device_type.id,
|
||||||
|
parent_device=chassis.id,
|
||||||
|
site='1',
|
||||||
|
)
|
||||||
|
blade = new_blade
|
||||||
|
|
||||||
|
# Find the slot and update it with our blade
|
||||||
|
device_bays = nb.dcim.device_bays.filter(
|
||||||
|
device_id=chassis.id,
|
||||||
|
name='Blade {}'.format(self.get_blade_slot()),
|
||||||
|
)
|
||||||
|
if len(device_bays) > 0:
|
||||||
|
device_bay = device_bays[0]
|
||||||
|
device_bay.installed_device = blade
|
||||||
|
device_bay.save()
|
||||||
|
else:
|
||||||
|
# FIXME : handle pizza box
|
96
netbox_agent/dmidecode.py
Normal file
96
netbox_agent/dmidecode.py
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
class Dmidecode():
|
||||||
|
def __init__(self):
|
||||||
|
self.types = {
|
||||||
|
0: 'bios',
|
||||||
|
1: 'system',
|
||||||
|
2: 'base board',
|
||||||
|
3: 'chassis',
|
||||||
|
4: 'processor',
|
||||||
|
7: 'cache',
|
||||||
|
8: 'port connector',
|
||||||
|
9: 'system slot',
|
||||||
|
10: 'on board device',
|
||||||
|
11: 'OEM strings',
|
||||||
|
#13: 'bios language',
|
||||||
|
15: 'system event log',
|
||||||
|
16: 'physical memory array',
|
||||||
|
17: 'memory device',
|
||||||
|
19: 'memory array mapped address',
|
||||||
|
24: 'hardware security',
|
||||||
|
25: 'system power controls',
|
||||||
|
27: 'cooling device',
|
||||||
|
32: 'system boot',
|
||||||
|
41: 'onboard device',
|
||||||
|
}
|
||||||
|
self.content = self._get_output()
|
||||||
|
self.info = self.parse_dmi()
|
||||||
|
|
||||||
|
def parse_dmi(self):
|
||||||
|
"""
|
||||||
|
Parse the whole dmidecode output.
|
||||||
|
Returns a list of tuples of (type int, value dict).
|
||||||
|
"""
|
||||||
|
self.info = []
|
||||||
|
lines = iter(self.content.strip().splitlines())
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
line = next(lines)
|
||||||
|
except StopIteration:
|
||||||
|
break
|
||||||
|
|
||||||
|
if line.startswith('Handle 0x'):
|
||||||
|
typ = int(line.split(',', 2)[1].strip()[len('DMI type'):])
|
||||||
|
if typ in self.types:
|
||||||
|
self.info.append(
|
||||||
|
(self.types[typ], self._parse_handle_section(lines))
|
||||||
|
)
|
||||||
|
return self.info
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_handle_section(self, lines):
|
||||||
|
"""
|
||||||
|
Parse a section of dmidecode output
|
||||||
|
* 1st line contains address, type and size
|
||||||
|
* 2nd line is title
|
||||||
|
* line started with one tab is one option and its value
|
||||||
|
* line started with two tabs is a member of list
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'_title': next(lines).rstrip(),
|
||||||
|
}
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
line = line.rstrip()
|
||||||
|
if line.startswith('\t\t'):
|
||||||
|
if isinstance(data[k], list):
|
||||||
|
data[k].append(line.lstrip())
|
||||||
|
elif line.startswith('\t'):
|
||||||
|
k, v = [i.strip() for i in line.lstrip().split(':', 1)]
|
||||||
|
if v:
|
||||||
|
data[k] = v
|
||||||
|
else:
|
||||||
|
data[k] = []
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def _get_output(self):
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(
|
||||||
|
'PATH=$PATH:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin '
|
||||||
|
'sudo dmidecode', shell=True)
|
||||||
|
except Exception as e:
|
||||||
|
print(e, file=sys.stderr)
|
||||||
|
if str(e).find("command not found") == -1:
|
||||||
|
print("please install dmidecode", file=sys.stderr)
|
||||||
|
print("e.g. sudo apt install dmidecode",file=sys.stderr)
|
||||||
|
|
||||||
|
sys.exit(1)
|
||||||
|
return output.decode()
|
||||||
|
|
||||||
|
def get(self, i):
|
||||||
|
return [v for j, v in self.info if j == i]
|
0
netbox_agent/hp/__init__.py
Normal file
0
netbox_agent/hp/__init__.py
Normal file
4
netbox_agent/hp/hp.py
Normal file
4
netbox_agent/hp/hp.py
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
from netbox_agent.server import ServerBase
|
||||||
|
|
||||||
|
class HPHost():
|
||||||
|
pass
|
22
netbox_agent/main.py
Normal file
22
netbox_agent/main.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
from netbox_agent.dmidecode import Dmidecode
|
||||||
|
from netbox_agent.dell.dell import DellHost
|
||||||
|
from netbox_agent.hp.hp import HPHost
|
||||||
|
|
||||||
|
MANUFACTURERS = {
|
||||||
|
'Dell Inc.': DellHost,
|
||||||
|
'HP': HPHost,
|
||||||
|
'HPE': HPHost,
|
||||||
|
}
|
||||||
|
|
||||||
|
def main():
|
||||||
|
dmi = Dmidecode()
|
||||||
|
manufacturer = dmi.get('chassis')[0].get('Manufacturer')
|
||||||
|
server = MANUFACTURERS[manufacturer](dmi)
|
||||||
|
print(server.get_chassis())
|
||||||
|
print(server.get_service_tag())
|
||||||
|
print(server.get_chassis_service_tag())
|
||||||
|
server.netbox_create()
|
||||||
|
print(server.get_network_cards())
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
63
netbox_agent/server.py
Normal file
63
netbox_agent/server.py
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
from netbox_agent.dmidecode import Dmidecode
|
||||||
|
|
||||||
|
# Regex to match base interface name
|
||||||
|
# Doesn't match vlan interfaces and other loopback etc
|
||||||
|
INTERFACE_REGEX = re.compile('^(eth[0-9]+|ens[0-9]+|enp[0-9]+s[0-9]f[0-9])$')
|
||||||
|
|
||||||
|
class ServerBase():
|
||||||
|
def __init__(self, dmi=None):
|
||||||
|
if dmi:
|
||||||
|
self.dmi = dmi
|
||||||
|
else:
|
||||||
|
self.dmi = Dmidecode()
|
||||||
|
self.system = self.dmi.get('system')
|
||||||
|
self.bios = self.dmi.get('bios')
|
||||||
|
|
||||||
|
self.network_cards = []
|
||||||
|
|
||||||
|
def get_product_name(self):
|
||||||
|
'''
|
||||||
|
Return the Chassis Name from dmidecode info
|
||||||
|
'''
|
||||||
|
return self.system[0]['Product Name']
|
||||||
|
|
||||||
|
def get_service_tag(self):
|
||||||
|
'''
|
||||||
|
Return the Service Tag from dmidecode info
|
||||||
|
'''
|
||||||
|
return self.system[0]['Serial Number']
|
||||||
|
|
||||||
|
def is_blade(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_blade_slot(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_chassis(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_chassis_service_tag(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_bios_version(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_bios_version_attr(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_bios_release_date(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_network_cards(self):
|
||||||
|
nics = []
|
||||||
|
for interface in os.listdir('/sys/class/net/'):
|
||||||
|
if re.match(INTERFACE_REGEX, interface):
|
||||||
|
nic = {
|
||||||
|
'name': interface,
|
||||||
|
'mac': open('/sys/class/net/{}/address'.format(interface), 'r').read().strip(),
|
||||||
|
'ip': None, #FIXME
|
||||||
|
}
|
||||||
|
nics.append(nic)
|
||||||
|
return nics
|
Loading…
Reference in a new issue