Setup tests
This commit is contained in:
parent
bdc2cbeb8f
commit
bab2d26ad0
46 changed files with 11717 additions and 117 deletions
|
@ -1,18 +1,18 @@
|
|||
from netbox_agent.logging import logging # NOQA
|
||||
from netbox_agent.vendors.dell import DellHost
|
||||
import netbox_agent.dmidecode as dmidecode
|
||||
from netbox_agent.config import config
|
||||
from netbox_agent.logging import logging # NOQA
|
||||
from netbox_agent.vendors.dell import DellHost
|
||||
from netbox_agent.vendors.hp import HPHost
|
||||
from netbox_agent.vendors.qct import QCTHost
|
||||
from netbox_agent.vendors.supermicro import SupermicroHost
|
||||
|
||||
MANUFACTURERS = {
|
||||
'Dell Inc.': DellHost,
|
||||
'HP': HPHost,
|
||||
'HPE': HPHost,
|
||||
'Supermicro': SupermicroHost,
|
||||
'Quanta Cloud Technology Inc.': QCTHost,
|
||||
}
|
||||
'Dell Inc.': DellHost,
|
||||
'HP': HPHost,
|
||||
'HPE': HPHost,
|
||||
'Supermicro': SupermicroHost,
|
||||
'Quanta Cloud Technology Inc.': QCTHost,
|
||||
}
|
||||
|
||||
|
||||
def run(config):
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import logging
|
||||
import pynetbox
|
||||
import jsonargparse
|
||||
import sys
|
||||
|
||||
import jsonargparse
|
||||
import pynetbox
|
||||
|
||||
|
||||
def get_config():
|
||||
p = jsonargparse.ArgumentParser(
|
||||
|
@ -13,6 +14,8 @@ def get_config():
|
|||
],
|
||||
prog='netbox_agent',
|
||||
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)
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import logging
|
||||
import re as _re
|
||||
import subprocess as _subprocess
|
||||
import sys
|
||||
|
||||
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$')
|
||||
_in_block_re = _re.compile('^\\t\\t(.+)$')
|
||||
|
@ -11,7 +11,7 @@ _record_re = _re.compile('\\t(.+):\\s+(.+)$')
|
|||
_record2_re = _re.compile('\\t(.+):$')
|
||||
|
||||
_type2str = {
|
||||
0: 'BIOS',
|
||||
0: 'BIOS',
|
||||
1: 'System',
|
||||
2: 'Baseboard',
|
||||
3: 'Chassis',
|
||||
|
@ -60,19 +60,22 @@ for type_id, type_str in _type2str.items():
|
|||
_str2type[type_str] = type_id
|
||||
|
||||
|
||||
def parse():
|
||||
def parse(output=None):
|
||||
"""
|
||||
parse the full output of the dmidecode
|
||||
command and return a dic containing the parsed information
|
||||
"""
|
||||
buffer = _execute_cmd()
|
||||
if output:
|
||||
buffer = output
|
||||
else:
|
||||
buffer = _execute_cmd()
|
||||
if isinstance(buffer, bytes):
|
||||
buffer = buffer.decode('utf-8')
|
||||
_data = _parse(buffer)
|
||||
return _data
|
||||
|
||||
|
||||
def get_by_type(type_id):
|
||||
def get_by_type(data, type_id):
|
||||
"""
|
||||
filter the output of dmidecode per type
|
||||
0 BIOS
|
||||
|
@ -124,7 +127,6 @@ def get_by_type(type_id):
|
|||
if type_id is None:
|
||||
return None
|
||||
|
||||
data = parse()
|
||||
result = []
|
||||
for entry in data.values():
|
||||
if entry['DMIType'] == type_id:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import re
|
||||
from shutil import which
|
||||
import subprocess
|
||||
from shutil import which
|
||||
|
||||
# Originally from https://github.com/opencoff/useful-scripts/blob/master/linktest.py
|
||||
|
||||
|
@ -9,7 +9,7 @@ field_map = {
|
|||
'Supported ports': 'ports',
|
||||
'Supported link modes': 'sup_link_modes',
|
||||
'Supports auto-negotiation': 'sup_autoneg',
|
||||
'Advertised link modes': 'adv_link_modes',
|
||||
'Advertised link modes': 'adv_link_modes',
|
||||
'Advertised auto-negotiation': 'adv_autoneg',
|
||||
'Speed': 'speed',
|
||||
'Duplex': 'duplex',
|
||||
|
@ -31,6 +31,7 @@ class Ethtool():
|
|||
There is several bindings to have something proper, but it requires
|
||||
compilation and other requirements.
|
||||
"""
|
||||
|
||||
def __init__(self, interface, *args, **kwargs):
|
||||
self.interface = interface
|
||||
|
||||
|
@ -54,7 +55,7 @@ class Ethtool():
|
|||
if field not in field_map:
|
||||
continue
|
||||
field = field_map[field]
|
||||
output = line[r+1:].strip()
|
||||
output = line[r + 1:].strip()
|
||||
fields[field] = output
|
||||
else:
|
||||
if len(field) > 0 and \
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import logging
|
||||
import pynetbox
|
||||
import re
|
||||
|
||||
from netbox_agent.config import netbox_instance as nb, config
|
||||
from netbox_agent.misc import is_tool, get_vendor
|
||||
import pynetbox
|
||||
|
||||
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.omreport import OmreportRaid
|
||||
from netbox_agent.raid.storcli import StorcliRaid
|
||||
from netbox_agent.lshw import LSHW
|
||||
|
||||
INVENTORY_TAG = {
|
||||
'cpu': {'name': 'hw:cpu', 'slug': 'hw-cpu'},
|
||||
|
@ -16,7 +18,7 @@ INVENTORY_TAG = {
|
|||
'memory': {'name': 'hw:memory', 'slug': 'hw-memory'},
|
||||
'motherboard': {'name': 'hw:motherboard', 'slug': 'hw-motherboard'},
|
||||
'raid_card': {'name': 'hw:raid_card', 'slug': 'hw-raid-card'},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Inventory():
|
||||
|
@ -132,8 +134,8 @@ class Inventory():
|
|||
|
||||
motherboards = self.get_hw_motherboards()
|
||||
nb_motherboards = self.get_netbox_inventory(
|
||||
device_id=self.device_id,
|
||||
tag=INVENTORY_TAG['motherboard']['slug'])
|
||||
device_id=self.device_id,
|
||||
tag=INVENTORY_TAG['motherboard']['slug'])
|
||||
|
||||
for nb_motherboard in nb_motherboards:
|
||||
if nb_motherboard.serial not in [x['serial'] for x in motherboards]:
|
||||
|
@ -169,8 +171,8 @@ class Inventory():
|
|||
|
||||
def do_netbox_interfaces(self):
|
||||
nb_interfaces = self.get_netbox_inventory(
|
||||
device_id=self.device_id,
|
||||
tag=INVENTORY_TAG['interface']['slug'])
|
||||
device_id=self.device_id,
|
||||
tag=INVENTORY_TAG['interface']['slug'])
|
||||
interfaces = self.lshw.interfaces
|
||||
|
||||
# delete interfaces that are in netbox but not locally
|
||||
|
@ -269,9 +271,9 @@ class Inventory():
|
|||
"""
|
||||
|
||||
nb_raid_cards = self.get_netbox_inventory(
|
||||
device_id=self.device_id,
|
||||
tag=[INVENTORY_TAG['raid_card']['slug']]
|
||||
)
|
||||
device_id=self.device_id,
|
||||
tag=[INVENTORY_TAG['raid_card']['slug']]
|
||||
)
|
||||
raid_cards = self.get_raid_cards()
|
||||
|
||||
# delete cards that are in netbox but not locally
|
||||
|
@ -296,7 +298,7 @@ class Inventory():
|
|||
|
||||
non_raid_disks = [
|
||||
'MR9361-8i',
|
||||
]
|
||||
]
|
||||
|
||||
if size is None and logicalname is None or \
|
||||
'virtual' in product.lower() or 'logical' in product.lower() or \
|
||||
|
@ -321,7 +323,7 @@ class Inventory():
|
|||
|
||||
d = {}
|
||||
d["name"] = ""
|
||||
d['Size'] = '{} GB'.format(int(size/1024/1024/1024))
|
||||
d['Size'] = '{} GB'.format(int(size / 1024 / 1024 / 1024))
|
||||
d['logicalname'] = logicalname
|
||||
d['description'] = description
|
||||
d['SN'] = serial
|
||||
|
@ -378,8 +380,8 @@ class Inventory():
|
|||
|
||||
def do_netbox_disks(self):
|
||||
nb_disks = self.get_netbox_inventory(
|
||||
device_id=self.device_id,
|
||||
tag=INVENTORY_TAG['disk']['slug'])
|
||||
device_id=self.device_id,
|
||||
tag=INVENTORY_TAG['disk']['slug'])
|
||||
disks = self.get_hw_disks()
|
||||
|
||||
# delete disks that are in netbox but not locally
|
||||
|
@ -421,9 +423,9 @@ class Inventory():
|
|||
def do_netbox_memories(self):
|
||||
memories = self.lshw.memories
|
||||
nb_memories = self.get_netbox_inventory(
|
||||
device_id=self.device_id,
|
||||
tag=INVENTORY_TAG['memory']['slug']
|
||||
)
|
||||
device_id=self.device_id,
|
||||
tag=INVENTORY_TAG['memory']['slug']
|
||||
)
|
||||
|
||||
for nb_memory in nb_memories:
|
||||
if nb_memory.serial not in [x['serial'] for x in memories]:
|
||||
|
|
|
@ -33,6 +33,7 @@ class IPMI():
|
|||
: O=OEM
|
||||
Bad Password Threshold : Not Available
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.ret, self.output = subprocess.getstatusoutput('ipmitool lan print')
|
||||
if self.ret != 0:
|
||||
|
|
|
@ -2,8 +2,11 @@ import subprocess
|
|||
|
||||
|
||||
class LLDP():
|
||||
def __init__(self):
|
||||
self.output = subprocess.getoutput('lldpctl -f keyvalue')
|
||||
def __init__(self, output=None):
|
||||
if output:
|
||||
self.output = output
|
||||
else:
|
||||
self.output = subprocess.getoutput('lldpctl -f keyvalue')
|
||||
self.data = self.parse()
|
||||
|
||||
def parse(self):
|
||||
|
@ -49,6 +52,8 @@ class LLDP():
|
|||
# lldp.eth0.port.descr=GigabitEthernet1/0/1
|
||||
if self.data['lldp'].get(interface) is None:
|
||||
return None
|
||||
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):
|
||||
|
|
|
@ -17,6 +17,7 @@ class LocationBase():
|
|||
There's also a support for an external driver file outside of this project in case
|
||||
the logic isn't supported here.
|
||||
"""
|
||||
|
||||
def __init__(self, driver, driver_value, driver_file, regex, *args, **kwargs):
|
||||
self.driver = driver
|
||||
self.driver_value = driver_value
|
||||
|
|
|
@ -2,7 +2,6 @@ import logging
|
|||
|
||||
from netbox_agent.config import config
|
||||
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
if config.log_level == 'debug':
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import subprocess
|
||||
import json
|
||||
import logging
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from netbox_agent.misc import is_tool
|
||||
|
|
|
@ -24,7 +24,7 @@ def get_vendor(name):
|
|||
'MD': 'Toshiba',
|
||||
'MG': 'Toshiba',
|
||||
'WD': 'WDC'
|
||||
}
|
||||
}
|
||||
for key, value in vendors.items():
|
||||
if name.upper().startswith(key):
|
||||
return value
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
from itertools import chain
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from itertools import chain
|
||||
|
||||
from netaddr import IPAddress, IPNetwork
|
||||
import netifaces
|
||||
from netaddr import IPAddress, IPNetwork
|
||||
|
||||
from netbox_agent.config import netbox_instance as nb, config
|
||||
from netbox_agent.config import config
|
||||
from netbox_agent.config import netbox_instance as nb
|
||||
from netbox_agent.ethtool import Ethtool
|
||||
from netbox_agent.ipmi import IPMI
|
||||
from netbox_agent.lldp import LLDP
|
||||
|
@ -95,7 +96,7 @@ class Network():
|
|||
x['addr'],
|
||||
IPAddress(x['netmask']).netmask_bits()
|
||||
) for x in ip_addr
|
||||
] if ip_addr else None, # FIXME: handle IPv6 addresses
|
||||
] if ip_addr else None, # FIXME: handle IPv6 addresses
|
||||
'ethtool': Ethtool(interface).parse(),
|
||||
'vlan': vlan,
|
||||
'bonding': bonding,
|
||||
|
@ -242,7 +243,7 @@ class Network():
|
|||
interface = nb.dcim.interfaces.get(
|
||||
device_id=self.device.id,
|
||||
mgmt_only=True,
|
||||
)
|
||||
)
|
||||
nic = {
|
||||
'name': 'IPMI',
|
||||
'mac': mac,
|
||||
|
|
|
@ -34,13 +34,13 @@ class PowerSupply():
|
|||
'allocated_draw': None,
|
||||
'maximum_draw': max_power,
|
||||
'device': self.device_id,
|
||||
})
|
||||
})
|
||||
return power_supply
|
||||
|
||||
def get_netbox_power_supply(self):
|
||||
return nb.dcim.power_ports.filter(
|
||||
device_id=self.device_id
|
||||
)
|
||||
)
|
||||
|
||||
def create_or_update_power_supply(self):
|
||||
nb_psus = self.get_netbox_power_supply()
|
||||
|
@ -78,10 +78,10 @@ class PowerSupply():
|
|||
if psu['name'] not in [x.name for x in nb_psus]:
|
||||
logging.info('Creating PSU {name} ({description}), {maximum_draw}W'.format(
|
||||
**psu
|
||||
))
|
||||
))
|
||||
nb_psu = nb.dcim.power_ports.create(
|
||||
**psu
|
||||
)
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import re
|
||||
import subprocess
|
||||
|
||||
from netbox_agent.raid.base import Raid, RaidController
|
||||
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]+)')
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import re
|
||||
import subprocess
|
||||
import xml.etree.ElementTree as ET # NOQA
|
||||
import xml.etree.ElementTree as ET # NOQA
|
||||
|
||||
from netbox_agent.misc import get_vendor
|
||||
from netbox_agent.raid.base import Raid, RaidController
|
||||
|
@ -43,7 +43,7 @@ class OmreportController(RaidController):
|
|||
ret = []
|
||||
output = subprocess.getoutput(
|
||||
'omreport storage controller controller={} -fmt xml'.format(self.controller_index)
|
||||
)
|
||||
)
|
||||
root = ET.fromstring(output)
|
||||
et_array_disks = root.find('ArrayDisks')
|
||||
if et_array_disks is not None:
|
||||
|
@ -54,7 +54,7 @@ class OmreportController(RaidController):
|
|||
'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__,
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import subprocess
|
||||
import json
|
||||
import subprocess
|
||||
|
||||
from netbox_agent.misc import get_vendor
|
||||
from netbox_agent.raid.base import Raid, RaidController
|
||||
|
@ -47,7 +47,7 @@ class StorcliController(RaidController):
|
|||
'Size': size,
|
||||
'Type': media_type,
|
||||
'_src': self.__class__.__name__,
|
||||
})
|
||||
})
|
||||
return ret
|
||||
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import logging
|
||||
from pprint import pprint
|
||||
import socket
|
||||
import subprocess
|
||||
from pprint import pprint
|
||||
|
||||
from netbox_agent.config import netbox_instance as nb, config
|
||||
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.location import Datacenter, Rack
|
||||
from netbox_agent.network import Network
|
||||
from netbox_agent.power import PowerSupply
|
||||
|
||||
|
@ -36,10 +37,10 @@ class ServerBase():
|
|||
else:
|
||||
self.dmi = dmidecode.parse()
|
||||
|
||||
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.baseboard = dmidecode.get_by_type(self.dmi, 'Baseboard')
|
||||
self.bios = dmidecode.get_by_type(self.dmi, 'BIOS')
|
||||
self.chassis = dmidecode.get_by_type(self.dmi, 'Chassis')
|
||||
self.system = dmidecode.get_by_type(self.dmi, 'System')
|
||||
|
||||
self.network = None
|
||||
|
||||
|
@ -229,7 +230,7 @@ class ServerBase():
|
|||
# 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)
|
||||
|
||||
|
|
2
netbox_agent/vendors/dell.py
vendored
2
netbox_agent/vendors/dell.py
vendored
|
@ -1,8 +1,8 @@
|
|||
import logging
|
||||
import subprocess
|
||||
|
||||
from netbox_agent.server import ServerBase
|
||||
from netbox_agent.misc import is_tool
|
||||
from netbox_agent.server import ServerBase
|
||||
|
||||
|
||||
class DellHost(ServerBase):
|
||||
|
|
4
netbox_agent/vendors/hp.py
vendored
4
netbox_agent/vendors/hp.py
vendored
|
@ -27,14 +27,14 @@ class HPHost(ServerBase):
|
|||
'Enclosure Name': locator[0].strip(),
|
||||
'Server Bay': locator[3].strip(),
|
||||
'Enclosure Serial': locator[4].strip(),
|
||||
}
|
||||
}
|
||||
return locator[0]
|
||||
|
||||
def get_blade_slot(self):
|
||||
if self.is_blade():
|
||||
return 'Bay {}'.format(
|
||||
int(self.hp_rack_locator['Server Bay'].strip())
|
||||
)
|
||||
)
|
||||
return None
|
||||
|
||||
def get_chassis(self):
|
||||
|
|
1
netbox_agent/vendors/supermicro.py
vendored
1
netbox_agent/vendors/supermicro.py
vendored
|
@ -2,6 +2,7 @@
|
|||
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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue