2019-08-07 10:32:23 +02:00
|
|
|
import logging
|
2019-08-04 15:14:36 +02:00
|
|
|
import socket
|
2019-09-12 11:15:12 +02:00
|
|
|
import subprocess
|
2020-07-01 18:51:57 +02:00
|
|
|
import sys
|
2020-02-02 20:08:56 +01:00
|
|
|
from pprint import pprint
|
2019-08-04 00:00:22 +02:00
|
|
|
|
|
|
|
import netbox_agent.dmidecode as dmidecode
|
2020-02-02 20:08:56 +01:00
|
|
|
from netbox_agent.config import config
|
|
|
|
from netbox_agent.config import netbox_instance as nb
|
2019-08-26 16:54:48 +02:00
|
|
|
from netbox_agent.inventory import Inventory
|
2020-07-01 18:54:58 +02:00
|
|
|
from netbox_agent.location import Datacenter, Rack, Tenant
|
|
|
|
from netbox_agent.misc import create_netbox_tags, get_device_role, get_device_type
|
2020-04-19 12:28:49 +02:00
|
|
|
from netbox_agent.network import ServerNetwork
|
2019-09-05 13:47:10 +02:00
|
|
|
from netbox_agent.power import PowerSupply
|
2019-08-04 00:00:22 +02:00
|
|
|
|
2019-08-04 15:14:36 +02:00
|
|
|
|
2019-08-02 18:39:05 +02:00
|
|
|
class ServerBase():
|
|
|
|
def __init__(self, dmi=None):
|
|
|
|
if dmi:
|
|
|
|
self.dmi = dmi
|
|
|
|
else:
|
2019-08-03 15:46:21 +02:00
|
|
|
self.dmi = dmidecode.parse()
|
2019-09-05 15:13:36 +02:00
|
|
|
|
2020-02-02 20:08:56 +01:00
|
|
|
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')
|
2019-08-02 18:39:05 +02:00
|
|
|
|
2019-08-09 12:08:11 +02:00
|
|
|
self.network = None
|
2019-08-02 18:39:05 +02:00
|
|
|
|
2020-07-01 18:54:58 +02:00
|
|
|
self.tags = list(set(config.device.tags.split(','))) if config.device.tags else []
|
|
|
|
if self.tags and len(self.tags):
|
|
|
|
create_netbox_tags(self.tags)
|
|
|
|
|
|
|
|
def get_tenant(self):
|
|
|
|
tenant = Tenant()
|
|
|
|
return tenant.get()
|
|
|
|
|
|
|
|
def get_netbox_tenant(self):
|
|
|
|
tenant = nb.tenancy.tenants.get(
|
|
|
|
slug=self.get_tenant()
|
|
|
|
)
|
|
|
|
return tenant
|
|
|
|
|
2019-08-04 01:11:53 +02:00
|
|
|
def get_datacenter(self):
|
|
|
|
dc = Datacenter()
|
2019-08-04 19:33:09 +02:00
|
|
|
return dc.get()
|
2019-08-04 01:11:53 +02:00
|
|
|
|
|
|
|
def get_netbox_datacenter(self):
|
2020-07-01 18:51:57 +02:00
|
|
|
dc = self.get_datacenter()
|
|
|
|
if dc is None:
|
|
|
|
logging.error("Specificing a datacenter (Site) is mandatory in Netbox")
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
nb_dc = nb.dcim.sites.get(
|
|
|
|
slug=dc,
|
2019-08-04 01:11:53 +02:00
|
|
|
)
|
2020-07-01 18:51:57 +02:00
|
|
|
if nb_dc is None:
|
|
|
|
logging.error("Site (slug: {}) has not been found".format(dc))
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
return nb_dc
|
2019-08-04 01:11:53 +02:00
|
|
|
|
2019-09-03 13:16:37 +02:00
|
|
|
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
|
2019-09-12 17:47:41 +02:00
|
|
|
if dc and server.site and server.site.slug != nb_dc.slug:
|
2019-09-03 13:16:37 +02:00
|
|
|
logging.info('Datacenter location has changed from {} to {}, updating'.format(
|
|
|
|
server.site.slug,
|
|
|
|
nb_dc.slug,
|
|
|
|
))
|
|
|
|
update = True
|
|
|
|
server.site = nb_dc.id
|
|
|
|
|
2019-09-12 17:47:41 +02:00
|
|
|
if rack and server.rack and server.rack.id != nb_rack.id:
|
2019-09-03 13:16:37 +02:00
|
|
|
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
|
|
|
|
|
2019-08-05 11:54:06 +02:00
|
|
|
def get_rack(self):
|
|
|
|
rack = Rack()
|
|
|
|
return rack.get()
|
|
|
|
|
|
|
|
def get_netbox_rack(self):
|
2020-07-01 18:51:57 +02:00
|
|
|
rack = self.get_rack()
|
|
|
|
datacenter = self.get_netbox_datacenter()
|
|
|
|
if not rack:
|
|
|
|
return None
|
|
|
|
if rack and not datacenter:
|
|
|
|
logging.error("Can't get rack if no datacenter is configured or found")
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
return nb.dcim.racks.get(
|
|
|
|
name=rack,
|
|
|
|
site_id=datacenter.id,
|
2019-08-05 11:54:06 +02:00
|
|
|
)
|
|
|
|
|
2019-08-02 18:39:05 +02:00
|
|
|
def get_product_name(self):
|
2019-08-04 00:00:22 +02:00
|
|
|
"""
|
2019-08-02 18:39:05 +02:00
|
|
|
Return the Chassis Name from dmidecode info
|
2019-08-04 00:00:22 +02:00
|
|
|
"""
|
2019-09-10 14:46:04 +02:00
|
|
|
return self.system[0]['Product Name'].strip()
|
2019-08-02 18:39:05 +02:00
|
|
|
|
|
|
|
def get_service_tag(self):
|
2019-08-04 00:00:22 +02:00
|
|
|
"""
|
2019-08-02 18:39:05 +02:00
|
|
|
Return the Service Tag from dmidecode info
|
2019-08-04 00:00:22 +02:00
|
|
|
"""
|
2019-08-14 11:17:24 +02:00
|
|
|
return self.system[0]['Serial Number'].strip()
|
2019-08-02 18:39:05 +02:00
|
|
|
|
2019-08-05 16:03:35 +02:00
|
|
|
def get_hostname(self):
|
2019-09-12 11:15:12 +02:00
|
|
|
if config.hostname_cmd is None:
|
|
|
|
return '{}'.format(socket.gethostname())
|
|
|
|
return subprocess.getoutput(config.hostname_cmd)
|
2019-08-05 16:03:35 +02:00
|
|
|
|
2019-08-02 18:39:05 +02:00
|
|
|
def is_blade(self):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def get_blade_slot(self):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def get_chassis(self):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2019-08-08 14:37:02 +02:00
|
|
|
def get_chassis_name(self):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2019-08-02 18:39:05 +02:00
|
|
|
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
|
|
|
|
|
2019-09-05 13:47:10 +02:00
|
|
|
def get_power_consumption(self):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2020-07-01 18:54:58 +02:00
|
|
|
def _netbox_create_chassis(self, datacenter, tenant, rack):
|
2019-10-23 12:14:50 +02:00
|
|
|
device_type = get_device_type(self.get_chassis())
|
2020-07-01 18:54:58 +02:00
|
|
|
device_role = get_device_role(config.device.chassis_role)
|
2019-08-06 17:59:46 +02:00
|
|
|
serial = self.get_chassis_service_tag()
|
2019-08-07 10:32:23 +02:00
|
|
|
logging.info('Creating chassis blade (serial: {serial})'.format(
|
2019-08-06 17:59:46 +02:00
|
|
|
serial=serial))
|
2019-08-03 15:46:21 +02:00
|
|
|
new_chassis = nb.dcim.devices.create(
|
2019-08-08 14:37:02 +02:00
|
|
|
name=self.get_chassis_name(),
|
2019-08-03 15:46:21 +02:00
|
|
|
device_type=device_type.id,
|
2019-08-06 17:59:46 +02:00
|
|
|
serial=serial,
|
2019-08-03 15:46:21 +02:00
|
|
|
device_role=device_role.id,
|
2019-08-04 01:11:53 +02:00
|
|
|
site=datacenter.id if datacenter else None,
|
2020-07-01 18:54:58 +02:00
|
|
|
tenant=tenant.id if tenant else None,
|
2019-08-14 11:17:24 +02:00
|
|
|
rack=rack.id if rack else None,
|
2020-07-01 18:54:58 +02:00
|
|
|
tags=self.tags,
|
2019-08-03 15:46:21 +02:00
|
|
|
)
|
|
|
|
return new_chassis
|
|
|
|
|
2020-07-01 18:54:58 +02:00
|
|
|
def _netbox_create_blade(self, chassis, datacenter, tenant, rack):
|
|
|
|
device_role = get_device_role(config.device.blade_role)
|
2019-10-23 12:14:50 +02:00
|
|
|
device_type = get_device_type(self.get_product_name())
|
2019-08-06 17:59:46 +02:00
|
|
|
serial = self.get_service_tag()
|
|
|
|
hostname = self.get_hostname()
|
2019-08-07 10:32:23 +02:00
|
|
|
logging.info(
|
2019-08-06 17:59:46 +02:00
|
|
|
'Creating blade (serial: {serial}) {hostname} on chassis {chassis_serial}'.format(
|
|
|
|
serial=serial, hostname=hostname, chassis_serial=chassis.serial
|
|
|
|
))
|
2019-08-03 15:46:21 +02:00
|
|
|
new_blade = nb.dcim.devices.create(
|
2019-08-06 17:59:46 +02:00
|
|
|
name=hostname,
|
|
|
|
serial=serial,
|
2019-08-03 15:46:21 +02:00
|
|
|
device_role=device_role.id,
|
|
|
|
device_type=device_type.id,
|
|
|
|
parent_device=chassis.id,
|
2019-08-04 01:11:53 +02:00
|
|
|
site=datacenter.id if datacenter else None,
|
2020-07-01 18:54:58 +02:00
|
|
|
tenant=tenant.id if tenant else None,
|
2019-08-14 11:17:24 +02:00
|
|
|
rack=rack.id if rack else None,
|
2020-07-01 18:54:58 +02:00
|
|
|
tags=self.tags,
|
2019-08-03 15:46:21 +02:00
|
|
|
)
|
|
|
|
return new_blade
|
|
|
|
|
2020-07-01 18:54:58 +02:00
|
|
|
def _netbox_create_server(self, datacenter, tenant, rack):
|
|
|
|
device_role = get_device_role(config.device.server_role)
|
2019-10-23 12:14:50 +02:00
|
|
|
device_type = get_device_type(self.get_product_name())
|
2019-08-03 15:57:06 +02:00
|
|
|
if not device_type:
|
|
|
|
raise Exception('Chassis "{}" doesn\'t exist'.format(self.get_chassis()))
|
2019-08-06 17:59:46 +02:00
|
|
|
serial = self.get_service_tag()
|
|
|
|
hostname = self.get_hostname()
|
2019-08-07 10:32:23 +02:00
|
|
|
logging.info('Creating server (serial: {serial}) {hostname}'.format(
|
2019-08-06 17:59:46 +02:00
|
|
|
serial=serial, hostname=hostname))
|
2019-08-03 15:57:06 +02:00
|
|
|
new_server = nb.dcim.devices.create(
|
2019-08-06 17:59:46 +02:00
|
|
|
name=hostname,
|
|
|
|
serial=serial,
|
2019-08-03 15:57:06 +02:00
|
|
|
device_role=device_role.id,
|
|
|
|
device_type=device_type.id,
|
2019-08-04 01:11:53 +02:00
|
|
|
site=datacenter.id if datacenter else None,
|
2020-07-01 18:54:58 +02:00
|
|
|
tenant=tenant.id if tenant else None,
|
2019-08-14 11:17:24 +02:00
|
|
|
rack=rack.id if rack else None,
|
2020-07-01 18:54:58 +02:00
|
|
|
tags=self.tags,
|
2019-08-03 15:57:06 +02:00
|
|
|
)
|
|
|
|
return new_server
|
|
|
|
|
2019-08-04 15:14:36 +02:00
|
|
|
def get_netbox_server(self):
|
|
|
|
return nb.dcim.devices.get(serial=self.get_service_tag())
|
|
|
|
|
2020-04-19 12:28:49 +02:00
|
|
|
def _netbox_set_or_update_blade_slot(self, server, chassis, datacenter):
|
|
|
|
# before everything check if right chassis
|
|
|
|
actual_device_bay = server.parent_device.device_bay if server.parent_device else None
|
|
|
|
actual_chassis = actual_device_bay.device if actual_device_bay else None
|
|
|
|
slot = self.get_blade_slot()
|
|
|
|
if actual_chassis and \
|
|
|
|
actual_chassis.serial == chassis.serial and \
|
|
|
|
actual_device_bay.name == slot:
|
|
|
|
return
|
2019-08-04 15:14:36 +02:00
|
|
|
|
2020-04-19 12:28:49 +02:00
|
|
|
real_device_bays = nb.dcim.device_bays.filter(
|
|
|
|
device_id=chassis.id,
|
|
|
|
name=slot,
|
2019-08-08 14:37:02 +02:00
|
|
|
)
|
2020-04-19 12:28:49 +02:00
|
|
|
if len(real_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
|
|
|
|
))
|
|
|
|
# reset actual device bay if set
|
|
|
|
if actual_device_bay:
|
|
|
|
actual_device_bay.installed_device = None
|
|
|
|
actual_device_bay.save()
|
|
|
|
# setup new device bay
|
|
|
|
real_device_bay = real_device_bays[0]
|
|
|
|
real_device_bay.installed_device = server
|
|
|
|
real_device_bay.save()
|
|
|
|
else:
|
|
|
|
logging.error('Could not find slot {slot} for chassis'.format(
|
|
|
|
slot=slot
|
|
|
|
))
|
2020-01-25 12:03:30 +01:00
|
|
|
|
2020-04-19 12:28:49 +02:00
|
|
|
def netbox_create_or_update(self, config):
|
|
|
|
"""
|
|
|
|
Netbox method to create or update info about our server/blade
|
2020-01-25 12:03:30 +01:00
|
|
|
|
2020-04-19 12:28:49 +02:00
|
|
|
Handle:
|
|
|
|
* new chassis for a blade
|
|
|
|
* new slot for a blade
|
|
|
|
* hostname update
|
|
|
|
* Network infos
|
|
|
|
* Inventory management
|
|
|
|
* PSU management
|
|
|
|
"""
|
|
|
|
datacenter = self.get_netbox_datacenter()
|
|
|
|
rack = self.get_netbox_rack()
|
2020-07-01 18:54:58 +02:00
|
|
|
tenant = self.get_netbox_tenant()
|
2019-08-08 14:37:02 +02:00
|
|
|
|
2020-04-19 12:28:49 +02:00
|
|
|
if self.is_blade():
|
2019-08-08 14:37:02 +02:00
|
|
|
chassis = nb.dcim.devices.get(
|
|
|
|
serial=self.get_chassis_service_tag()
|
|
|
|
)
|
2020-04-19 12:28:49 +02:00
|
|
|
# Chassis does not exist
|
2019-08-08 14:37:02 +02:00
|
|
|
if not chassis:
|
2020-07-01 18:54:58 +02:00
|
|
|
chassis = self._netbox_create_chassis(datacenter, tenant, rack)
|
2019-08-08 14:37:02 +02:00
|
|
|
|
2020-04-19 12:28:49 +02:00
|
|
|
server = nb.dcim.devices.get(serial=self.get_service_tag())
|
|
|
|
if not server:
|
2020-07-01 18:54:58 +02:00
|
|
|
server = self._netbox_create_blade(chassis, datacenter, tenant, rack)
|
2019-08-08 14:37:02 +02:00
|
|
|
|
2020-04-19 12:28:49 +02:00
|
|
|
# Set slot for blade
|
|
|
|
self._netbox_set_or_update_blade_slot(server, chassis, datacenter)
|
|
|
|
else:
|
|
|
|
server = nb.dcim.devices.get(serial=self.get_service_tag())
|
|
|
|
if not server:
|
2020-07-01 18:54:58 +02:00
|
|
|
self._netbox_create_server(datacenter, tenant, rack)
|
2019-08-05 16:03:35 +02:00
|
|
|
|
2019-08-07 10:32:23 +02:00
|
|
|
logging.debug('Updating Server...')
|
2020-04-19 12:28:49 +02:00
|
|
|
# check network cards
|
|
|
|
if config.register or config.update_all or config.update_network:
|
|
|
|
self.network = ServerNetwork(server=self)
|
|
|
|
self.network.create_or_update_netbox_network_cards()
|
|
|
|
# update inventory if feature is enabled
|
|
|
|
if config.inventory and (config.register or config.update_all or config.update_inventory):
|
|
|
|
self.inventory = Inventory(server=self)
|
|
|
|
self.inventory.create_or_update()
|
|
|
|
# update psu
|
|
|
|
if config.register or config.update_all or config.update_psu:
|
|
|
|
self.power = PowerSupply(server=self)
|
|
|
|
self.power.create_or_update_power_supply()
|
|
|
|
self.power.report_power_consumption()
|
2019-09-03 13:16:37 +02:00
|
|
|
|
|
|
|
update = 0
|
2019-08-05 12:02:42 +02:00
|
|
|
# for every other specs
|
|
|
|
# check hostname
|
2019-08-05 16:03:35 +02:00
|
|
|
if server.name != self.get_hostname():
|
2019-09-03 13:16:37 +02:00
|
|
|
update += 1
|
2019-09-12 11:15:12 +02:00
|
|
|
server.name = self.get_hostname()
|
2019-09-03 13:16:37 +02:00
|
|
|
|
2020-07-01 18:54:58 +02:00
|
|
|
if sorted(set(server.tags)) != sorted(set(self.tags)):
|
|
|
|
server.tags = self.tags
|
|
|
|
update += 1
|
|
|
|
|
2019-09-03 13:16:37 +02:00
|
|
|
if config.update_all or config.update_location:
|
|
|
|
ret, server = self.update_netbox_location(server)
|
|
|
|
update += ret
|
|
|
|
|
2019-08-05 16:03:35 +02:00
|
|
|
if update:
|
|
|
|
server.save()
|
2019-08-07 10:32:23 +02:00
|
|
|
logging.debug('Finished updating Server!')
|
2019-08-04 15:14:36 +02:00
|
|
|
|
2019-08-04 00:00:22 +02:00
|
|
|
def print_debug(self):
|
2020-04-19 12:28:49 +02:00
|
|
|
self.network = ServerNetwork(server=self)
|
2019-08-04 00:00:22 +02:00
|
|
|
print('Datacenter:', self.get_datacenter())
|
|
|
|
print('Netbox Datacenter:', self.get_netbox_datacenter())
|
2019-08-05 11:54:06 +02:00
|
|
|
print('Rack:', self.get_rack())
|
|
|
|
print('Netbox Rack:', self.get_netbox_rack())
|
2019-08-04 00:00:22 +02:00
|
|
|
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())
|
2019-08-04 14:37:51 +02:00
|
|
|
print('NIC:',)
|
|
|
|
pprint(self.network.get_network_cards())
|
|
|
|
pass
|