netbox-agent/netbox_agent/server.py
2020-01-25 12:03:30 +01:00

359 lines
12 KiB
Python

import logging
from pprint import pprint
import socket
import subprocess
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.inventory import Inventory
from netbox_agent.network import Network
from netbox_agent.power import PowerSupply
def get_device_role(role):
device_role = nb.dcim.device_roles.get(
name=role
)
if device_role is None:
raise Exception('DeviceRole "{}" does not exist, please create it'.format(role))
return device_role
def get_device_type(type):
device_type = nb.dcim.device_types.get(
model=type
)
if device_type is None:
raise Exception('DeviceType "{}" does not exist, please create it'.format(type))
return device_type
class ServerBase():
def __init__(self, dmi=None):
if dmi:
self.dmi = dmi
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.network = None
def get_datacenter(self):
dc = Datacenter()
return dc.get()
def get_netbox_datacenter(self):
datacenter = nb.dcim.sites.get(
slug=self.get_datacenter()
)
return datacenter
def update_netbox_location(self, server):
dc = self.get_datacenter()
rack = self.get_rack()
nb_rack = self.get_netbox_rack()
nb_dc = self.get_netbox_datacenter()
update = False
if dc and server.site and server.site.slug != nb_dc.slug:
logging.info('Datacenter location has changed from {} to {}, updating'.format(
server.site.slug,
nb_dc.slug,
))
update = True
server.site = nb_dc.id
if rack and server.rack and server.rack.id != nb_rack.id:
logging.info('Rack location has changed from {} to {}, updating'.format(
server.rack,
nb_rack,
))
update = True
server.rack = nb_rack
if nb_rack is None:
server.face = None
server.position = None
return update, server
def get_rack(self):
rack = Rack()
return rack.get()
def get_netbox_rack(self):
rack = nb.dcim.racks.get(
name=self.get_rack(),
site_id=self.get_netbox_datacenter().id,
)
return rack
def get_product_name(self):
"""
Return the Chassis Name from dmidecode info
"""
return self.system[0]['Product Name'].strip()
def get_service_tag(self):
"""
Return the Service Tag from dmidecode info
"""
return self.system[0]['Serial Number'].strip()
def get_hostname(self):
if config.hostname_cmd is None:
return '{}'.format(socket.gethostname())
return subprocess.getoutput(config.hostname_cmd)
def is_blade(self):
raise NotImplementedError
def get_blade_slot(self):
raise NotImplementedError
def get_chassis(self):
raise NotImplementedError
def get_chassis_name(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_power_consumption(self):
raise NotImplementedError
def _netbox_create_blade_chassis(self, datacenter, rack):
device_type = get_device_type(self.get_chassis())
device_role = get_device_role('Server Chassis')
serial = self.get_chassis_service_tag()
logging.info('Creating chassis blade (serial: {serial})'.format(
serial=serial))
new_chassis = nb.dcim.devices.create(
name=self.get_chassis_name(),
device_type=device_type.id,
serial=serial,
device_role=device_role.id,
site=datacenter.id if datacenter else None,
rack=rack.id if rack else None,
)
return new_chassis
def _netbox_create_blade(self, chassis, datacenter, rack):
device_role = get_device_role('Blade')
device_type = get_device_type(self.get_product_name())
serial = self.get_service_tag()
hostname = self.get_hostname()
logging.info(
'Creating blade (serial: {serial}) {hostname} on chassis {chassis_serial}'.format(
serial=serial, hostname=hostname, chassis_serial=chassis.serial
))
new_blade = nb.dcim.devices.create(
name=hostname,
serial=serial,
device_role=device_role.id,
device_type=device_type.id,
parent_device=chassis.id,
site=datacenter.id if datacenter else None,
rack=rack.id if rack else None,
)
return new_blade
def _netbox_set_blade_slot(self, chassis, server):
slot = self.get_blade_slot()
# Find the slot and update it with our blade
device_bays = nb.dcim.device_bays.filter(
device_id=chassis.id,
name=slot,
)
if len(device_bays) > 0:
logging.info(
'Setting device ({serial}) new slot on {slot} '
'(Chassis {chassis_serial})..'.format(
serial=server.serial, slot=slot, chassis_serial=chassis.serial
))
device_bay = device_bays[0]
device_bay.installed_device = server
device_bay.save()
else:
logging.error('Could not find slot {slot} for chassis'.format(
slot=slot
))
def _netbox_create_server(self, datacenter, rack):
device_role = get_device_role('Server')
device_type = get_device_type(self.get_product_name())
if not device_type:
raise Exception('Chassis "{}" doesn\'t exist'.format(self.get_chassis()))
serial = self.get_service_tag()
hostname = self.get_hostname()
logging.info('Creating server (serial: {serial}) {hostname}'.format(
serial=serial, hostname=hostname))
new_server = nb.dcim.devices.create(
name=hostname,
serial=serial,
device_role=device_role.id,
device_type=device_type.id,
site=datacenter.id if datacenter else None,
rack=rack.id if rack else None,
)
return new_server
def get_netbox_server(self):
return nb.dcim.devices.get(serial=self.get_service_tag())
def netbox_create(self, config):
logging.debug('Creating Server..')
datacenter = self.get_netbox_datacenter()
rack = self.get_netbox_rack()
if self.is_blade():
# let's find the blade
serial = self.get_service_tag()
blade = nb.dcim.devices.get(serial=serial)
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
chassis = nb.dcim.devices.get(
serial=self.get_chassis_service_tag()
)
if not chassis:
chassis = self._netbox_create_blade_chassis(datacenter, rack)
blade = self._netbox_create_blade(chassis, datacenter, rack)
# Set slot for blade
self._netbox_set_blade_slot(chassis, blade)
else:
server = nb.dcim.devices.get(serial=self.get_service_tag())
if not server:
self._netbox_create_server(datacenter, rack)
self.network = Network(server=self)
self.network.create_netbox_network_cards()
self.power = PowerSupply(server=self)
self.power.create_or_update_power_supply()
if config.inventory:
self.inventory = Inventory(server=self)
self.inventory.create()
logging.debug('Server created!')
def _netbox_update_chassis_for_blade(self, server, datacenter):
chassis = server.parent_device.device_bay.device
device_bay = nb.dcim.device_bays.get(
server.parent_device.device_bay.id
)
parent_chassis = nb.dcim.devices.get(
chassis.id
)
netbox_chassis_serial = parent_chassis.serial
move_device_bay = False
# check chassis serial with dmidecode
if netbox_chassis_serial != self.get_chassis_service_tag():
move_device_bay = True
# try to find the new netbox chassis
chassis = nb.dcim.devices.get(
serial=self.get_chassis_service_tag()
)
if not chassis:
chassis = self._netbox_create_blade_chassis(datacenter)
if move_device_bay or device_bay.name != self.get_blade_slot():
logging.info('Device ({serial}) seems to have moved, reseting old slot..'.format(
serial=server.serial))
device_bay.installed_device = None
device_bay.save()
# Set slot for blade
self._netbox_set_blade_slot(chassis, server)
def netbox_update(self, config):
"""
Netbox method to update info about our server/blade
Handle:
* new chasis for a blade
* new slot for a bblade
* hostname update
* new network infos
"""
logging.debug('Updating Server...')
server = nb.dcim.devices.get(serial=self.get_service_tag())
if not server:
raise Exception("The server (Serial: {}) isn't yet registered in Netbox, register"
'it before updating it'.format(self.get_service_tag()))
update = 0
if self.is_blade():
datacenter = self.get_netbox_datacenter()
# if it's already linked to a chassis
if server.parent_device:
self._netbox_update_chassis_for_blade(server, datacenter)
else:
logging.info('Blade is not in a chassis, fixing...')
chassis = nb.dcim.devices.get(
serial=self.get_chassis_service_tag()
)
if not chassis:
chassis = self._netbox_create_blade_chassis(datacenter)
# Set slot for blade
self._netbox_set_blade_slot(chassis, server)
# for every other specs
# check hostname
if server.name != self.get_hostname():
update += 1
server.name = self.get_hostname()
if config.update_all or config.update_location:
ret, server = self.update_netbox_location(server)
update += ret
# check network cards
if config.update_all or config.update_network:
self.network = Network(server=self)
self.network.update_netbox_network_cards()
# update inventory
if config.update_all or config.update_inventory:
self.inventory = Inventory(server=self)
self.inventory.update()
# update psu
if config.update_all or config.update_psu:
self.power = PowerSupply(server=self)
self.power.create_or_update_power_supply()
self.power.report_power_consumption()
if update:
server.save()
logging.debug('Finished updating Server!')
def print_debug(self):
self.network = Network(server=self)
print('Datacenter:', self.get_datacenter())
print('Netbox Datacenter:', self.get_netbox_datacenter())
print('Rack:', self.get_rack())
print('Netbox Rack:', self.get_netbox_rack())
print('Is blade:', self.is_blade())
print('Product Name:', self.get_product_name())
print('Chassis:', self.get_chassis())
print('Chassis service tag:', self.get_chassis_service_tag())
print('Service tag:', self.get_service_tag())
print('NIC:',)
pprint(self.network.get_network_cards())
pass