2019-09-05 13:47:10 +02:00
|
|
|
import logging
|
|
|
|
|
2020-02-16 20:24:40 +01:00
|
|
|
import netbox_agent.dmidecode as dmidecode
|
2019-09-05 13:47:10 +02:00
|
|
|
from netbox_agent.config import netbox_instance as nb
|
|
|
|
|
|
|
|
PSU_DMI_TYPE = 39
|
|
|
|
|
|
|
|
|
|
|
|
class PowerSupply():
|
|
|
|
def __init__(self, server=None):
|
|
|
|
self.server = server
|
|
|
|
self.netbox_server = self.server.get_netbox_server()
|
|
|
|
if self.server.is_blade():
|
|
|
|
self.device_id = self.netbox_server.parent_device.id if self.netbox_server else None
|
|
|
|
else:
|
|
|
|
self.device_id = self.netbox_server.id if self.netbox_server else None
|
|
|
|
|
|
|
|
def get_power_supply(self):
|
|
|
|
power_supply = []
|
2020-02-16 20:24:40 +01:00
|
|
|
for psu in dmidecode.get_by_type(self.server.dmi, PSU_DMI_TYPE):
|
2019-09-11 10:37:31 +02:00
|
|
|
if 'Present' not in psu['Status'] or psu['Status'] == 'Not Present':
|
2019-09-05 13:47:10 +02:00
|
|
|
continue
|
|
|
|
|
2019-09-10 14:48:16 +02:00
|
|
|
try:
|
|
|
|
max_power = int(psu.get('Max Power Capacity').split()[0])
|
|
|
|
except ValueError:
|
|
|
|
max_power = None
|
2019-09-05 13:47:10 +02:00
|
|
|
desc = '{} - {}'.format(
|
|
|
|
psu.get('Manufacturer', 'No Manufacturer').strip(),
|
|
|
|
psu.get('Name', 'No name').strip(),
|
|
|
|
)
|
2020-05-20 16:12:47 +02:00
|
|
|
|
|
|
|
sn = psu.get('Serial Number', '').strip()
|
|
|
|
# Let's assume that if no serial and no power reported we skip it
|
2020-06-15 15:46:09 +02:00
|
|
|
if sn == '' and max_power is None:
|
2020-05-20 16:12:47 +02:00
|
|
|
continue
|
|
|
|
if sn == '':
|
|
|
|
sn = 'N/A'
|
2019-09-05 13:47:10 +02:00
|
|
|
power_supply.append({
|
2020-05-20 16:12:47 +02:00
|
|
|
'name': sn,
|
2019-09-05 13:47:10 +02:00
|
|
|
'description': desc,
|
|
|
|
'allocated_draw': None,
|
2019-09-10 14:48:16 +02:00
|
|
|
'maximum_draw': max_power,
|
2019-09-05 13:47:10 +02:00
|
|
|
'device': self.device_id,
|
2020-02-02 20:08:56 +01:00
|
|
|
})
|
2019-09-05 13:47:10 +02:00
|
|
|
return power_supply
|
|
|
|
|
|
|
|
def get_netbox_power_supply(self):
|
|
|
|
return nb.dcim.power_ports.filter(
|
|
|
|
device_id=self.device_id
|
2020-02-02 20:08:56 +01:00
|
|
|
)
|
2019-09-05 13:47:10 +02:00
|
|
|
|
|
|
|
def create_or_update_power_supply(self):
|
2021-05-11 20:42:13 +02:00
|
|
|
nb_psus = list(self.get_netbox_power_supply())
|
2019-09-05 13:47:10 +02:00
|
|
|
psus = self.get_power_supply()
|
|
|
|
|
|
|
|
# Delete unknown PSU
|
|
|
|
delete = False
|
|
|
|
for nb_psu in nb_psus:
|
|
|
|
if nb_psu.name not in [x['name'] for x in psus]:
|
|
|
|
logging.info('Deleting unknown locally PSU {name}'.format(
|
|
|
|
name=nb_psu.name
|
|
|
|
))
|
|
|
|
nb_psu.delete()
|
|
|
|
delete = True
|
|
|
|
|
|
|
|
if delete:
|
|
|
|
nb_psus = self.get_netbox_power_supply()
|
|
|
|
|
|
|
|
# sync existing Netbox PSU with local infos
|
|
|
|
for nb_psu in nb_psus:
|
|
|
|
local_psu = next(
|
|
|
|
item for item in psus if item['name'] == nb_psu.name
|
|
|
|
)
|
|
|
|
update = False
|
|
|
|
if nb_psu.description != local_psu['description']:
|
|
|
|
update = True
|
|
|
|
nb_psu.description = local_psu['description']
|
|
|
|
if nb_psu.maximum_draw != local_psu['maximum_draw']:
|
|
|
|
update = True
|
|
|
|
nb_psu.maximum_draw = local_psu['maximum_draw']
|
|
|
|
if update:
|
|
|
|
nb_psu.save()
|
|
|
|
|
|
|
|
for psu in psus:
|
|
|
|
if psu['name'] not in [x.name for x in nb_psus]:
|
|
|
|
logging.info('Creating PSU {name} ({description}), {maximum_draw}W'.format(
|
|
|
|
**psu
|
2020-02-02 20:08:56 +01:00
|
|
|
))
|
2019-09-05 13:47:10 +02:00
|
|
|
nb_psu = nb.dcim.power_ports.create(
|
|
|
|
**psu
|
2020-02-02 20:08:56 +01:00
|
|
|
)
|
2019-09-05 13:47:10 +02:00
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
def report_power_consumption(self):
|
2019-09-11 10:37:31 +02:00
|
|
|
try:
|
|
|
|
psu_cons = self.server.get_power_consumption()
|
|
|
|
except NotImplementedError:
|
|
|
|
logging.error('Cannot report power consumption for this vendor')
|
|
|
|
return False
|
2019-09-05 13:47:10 +02:00
|
|
|
nb_psus = self.get_netbox_power_supply()
|
|
|
|
|
|
|
|
if not len(nb_psus) or not len(psu_cons):
|
|
|
|
return False
|
|
|
|
|
|
|
|
# find power feeds for rack or dc
|
|
|
|
pwr_feeds = None
|
|
|
|
if self.netbox_server.rack:
|
|
|
|
pwr_feeds = nb.dcim.power_feeds.filter(
|
|
|
|
rack=self.netbox_server.rack.id
|
|
|
|
)
|
2022-02-23 15:48:41 +01:00
|
|
|
|
|
|
|
if pwr_feeds:
|
|
|
|
voltage = [p['voltage'] for p in pwr_feeds]
|
|
|
|
else:
|
2019-09-05 13:47:10 +02:00
|
|
|
logging.info('Could not find power feeds for Rack, defaulting value to 230')
|
2022-02-25 18:43:09 +01:00
|
|
|
voltage = [230 for _ in nb_psus]
|
2019-09-05 13:47:10 +02:00
|
|
|
|
|
|
|
for i, nb_psu in enumerate(nb_psus):
|
2022-02-23 15:48:41 +01:00
|
|
|
nb_psu.allocated_draw = int(float(psu_cons[i]) * voltage[i])
|
2020-09-07 14:20:32 +02:00
|
|
|
if nb_psu.allocated_draw < 1:
|
|
|
|
logging.info('PSU is not connected or in standby mode')
|
|
|
|
continue
|
2019-09-05 13:47:10 +02:00
|
|
|
nb_psu.save()
|
|
|
|
logging.info('Updated power consumption for PSU {}: {}W'.format(
|
|
|
|
nb_psu.name,
|
|
|
|
nb_psu.allocated_draw,
|
|
|
|
))
|
|
|
|
|
|
|
|
return True
|