netbox-agent/netbox_agent/inventory.py
Thomas Davis 1c3ea69d9e Several minor fixes
raid_arry check should return an empty array, not NoneType

not all systems will have IPv6 defined, so try to get the information..  if not
just continue on.

Vendor Supermicro was incomplete.
2019-08-27 10:30:04 -07:00

328 lines
11 KiB
Python

import logging
import subprocess
import re
from netbox_agent.config import netbox_instance as nb, INVENTORY_ENABLED
from netbox_agent.misc import is_tool
from netbox_agent.raid.hp import HPRaid
from netbox_agent.raid.storcli import StorcliRaid
INVENTORY_TAG = {
'cpu': {'name': 'hw:cpu', 'slug': 'hw-cpu'},
'memory': {'name': 'hw:memory', 'slug': 'hw-memory'},
'disk': {'name': 'hw:disk', 'slug': 'hw-disk'},
'raid_card': {'name': 'hw:raid_card', 'slug': 'hw-raid-card'},
}
for key, tag in INVENTORY_TAG.items():
nb_tag = nb.extras.tags.get(
name=tag['name']
)
if not nb_tag:
nb_tag = nb.extras.tags.create(
name=tag['name'],
slug=tag['slug'],
comments=tag['name'],
)
class Inventory():
"""
Better Inventory items coming, see:
- https://github.com/netbox-community/netbox/issues/3087
- https://github.com/netbox-community/netbox/issues/3333
This class implements for:
* memory
* cpu
* raid cards
* disks
methods that:
* get local item
* get netbox item
* create netbox item
* update netbox item
Known issues:
- no scan of non-raid devices
- no scan of NVMe devices
"""
def __init__(self, server):
self.server = server
self.device_id = self.server.get_netbox_server().id
self.raid = None
self.disks = []
def get_cpus(self):
model = None
nb = None
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
def create_netbox_cpus(self):
nb_cpus, model = self.get_cpus()
for i in range(nb_cpus):
_ = nb.dcim.inventory_items.create(
device=self.device_id,
tags=[INVENTORY_TAG['cpu']['name']],
name=model,
discovered=True,
description='CPU',
)
logging.info('Creating CPU model {model}'.format(model=model))
def update_netbox_cpus(self):
cpus_number, model = self.get_cpus()
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):
for x in nb_cpus:
x.delete()
self.create_netbox_cpus()
def get_raid_cards(self):
if self.server.manufacturer == 'Dell':
if is_tool('storcli'):
self.raid = StorcliRaid()
elif self.server.manufacturer == 'HP':
if is_tool('ssacli'):
self.raid = HPRaid()
if not self.raid:
return []
controllers = self.raid.get_controllers()
if len(self.raid.get_controllers()):
return controllers
def get_netbox_raid_cards(self):
raid_cards = nb.dcim.inventory_items.filter(
device_id=self.device_id,
tag=INVENTORY_TAG['raid_card']['slug'],
)
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()
)
name = raid_card.get_product_name()
serial = raid_card.get_serial_number()
nb_raid_card = nb.dcim.inventory_items.create(
device=self.device_id,
discovered=True,
manufacturer=manufacturer.id if manufacturer else None,
tags=[INVENTORY_TAG['raid_card']['name']],
name='{}'.format(name),
serial='{}'.format(serial),
description='RAID Card',
)
logging.info('Creating RAID Card {name} (SN: {serial})'.format(
name=name,
serial=serial,
))
return nb_raid_card
def create_netbox_raid_cards(self):
for raid_card in self.get_raid_cards():
self.create_netbox_raid_card(raid_card)
def update_netbox_raid_cards(self):
"""
Update raid cards in netbobx
Since we only push:
* Name
* Manufacturer
* Serial
We only need to handle destroy and new cards
"""
nb_raid_cards = self.get_netbox_raid_cards()
raid_cards = self.get_raid_cards()
# delete cards that are in netbox but not locally
# use the serial_number has the comparison element
for nb_raid_card in nb_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(
serial=nb_raid_card.serial,
))
nb_raid_card.delete()
# create card that are not in netbox
for raid_card in 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)
def get_disks(self):
ret = []
for raid_card in self.get_raid_cards():
ret += raid_card.get_physical_disks()
return ret
def get_netbox_disks(self):
disks = nb.dcim.inventory_items.filter(
device_id=self.device_id,
tag=INVENTORY_TAG['disk']['slug'],
)
return disks
def create_netbox_disks(self):
for disk in self.get_disks():
_ = nb.dcim.inventory_items.create(
device=self.device_id,
discovered=True,
tags=[INVENTORY_TAG['disk']['name']],
name='{} ({})'.format(disk['Model'], disk['Size']),
serial=disk['SN'],
)
logging.info('Creating Disk {model} {serial}'.format(
model=disk['Model'],
serial=disk['SN'],
))
def update_netbox_disks(self):
nb_disks = self.get_netbox_disks()
disks = self.get_disks()
# delete disks that are in netbox but not locally
# use the serial_number has the comparison element
for nb_disk in nb_disks:
if nb_disk.serial not in [x['SN'] for x in disks]:
logging.info('Deleting unknown locally Disk {serial}'.format(
serial=nb_disk.serial,
))
nb_disk.delete()
# create disks that are not in netbox
for disk in disks:
if disk['SN'] not in [x.serial for x in nb_disks]:
nb_disk = nb.dcim.inventory_items.create(
device=self.device_id,
discovered=True,
tags=[INVENTORY_TAG['disk']['name']],
name='{} ({})'.format(disk['Model'], disk['Size']),
serial=disk['SN'],
description=disk.get('Type', ''),
)
logging.info('Creating Disk {model} {serial}'.format(
model=disk['Model'],
serial=disk['SN'],
))
def get_memory(self):
memories = []
for _, value in self.server.dmi.parse().items():
if value['DMIName'] == 'Memory Device' and \
value['Size'] != 'No Module Installed':
memories.append({
'Manufacturer': value['Manufacturer'].strip(),
'Size': value['Size'].strip(),
'PN': value['Part Number'].strip(),
'SN': value['Serial Number'].strip(),
'Locator': value['Locator'].strip(),
'Type': value['Type'].strip(),
})
return memories
def get_memory_total_size(self):
total_size = 0
for memory in self.get_memory():
total_size += int(memory['Size'].split()[0])
return total_size
def get_netbox_memory(self):
memories = nb.dcim.inventory_items.filter(
device_id=self.device_id,
tag=INVENTORY_TAG['memory']['slug'],
)
return memories
def create_netbox_memory(self, memory):
manufacturer = nb.dcim.manufacturers.get(
name=memory['Manufacturer']
)
if not manufacturer:
manufacturer = nb.dcim.manufacturers.create(
name=memory['Manufacturer'],
slug=memory['Manufacturer'].lower(),
)
nb_memory = nb.dcim.inventory_items.create(
device=self.device_id,
discovered=True,
manufacturer=manufacturer.id,
tags=[INVENTORY_TAG['memory']['name']],
name='{} ({} {})'.format(memory['Locator'], memory['Size'], memory['Type']),
part_id=memory['PN'],
serial=memory['SN'],
description='RAM',
)
logging.info('Creating Memory {type} {size}'.format(
type=memory['Type'],
size=memory['Size'],
))
return nb_memory
def create_netbox_memories(self):
for memory in self.get_memory():
self.create_netbox_memory(memory)
def update_netbox_memory(self):
memories = self.get_memory()
nb_memories = self.get_netbox_memory()
for nb_memory in nb_memories:
if nb_memory.serial not in [x['SN'] for x in memories]:
logging.info('Deleting unknown locally Memory {serial}'.format(
serial=nb_memory.serial,
))
nb_memory.delete()
for memory in memories:
if memory['SN'] not in [x.serial for x in nb_memories]:
self.create_netbox_memory(memory)
def create(self):
if not INVENTORY_ENABLED:
return False
self.create_netbox_cpus()
self.create_netbox_memory()
self.create_netbox_raid_cards()
self.create_netbox_disks()
return True
def update(self):
if not INVENTORY_ENABLED:
return False
self.update_netbox_cpus()
self.update_netbox_memory()
self.update_netbox_raid_cards()
self.update_netbox_disks()
return True