Hardware
banshee/hardware-collection/hardware/redfish/https/
power
The health of each power supply and of their redundancy, and the power drawn, of a server by Redfish.
by banshee1 installation1 version
Published on October 27, 2026. First version.
// The power of a server, by the Redfish service of its management card (DMTF Redfish): the health of each
// power supply and of their redundancy, and the power the chassis draws. Any card that serves Redfish: Dell
// iDRAC, HPE iLO, Supermicro, Lenovo XCC, OpenBMC.
// A chassis is read by its Power resource, which the cards serve, deprecated since schema 2020.4; a chassis
// that serves only the PowerSubsystem of the later schemas is read by it. Power is preferred when both are
// served: it gives the power drawn in the same resource.
need('protocol:http');
$address = need('host:address');
$user = need('protocol:http:user');
$password = need('protocol:http:password');
per('component', 'Components');
output('health', 'text', array('label' => 'Health', 'per' => 'component'));
output('consumption', 'number', array('label' => 'Power drawn', 'unit' => 'W', 'per' => 'component'));
watch.state('unhealthy', 'Power supply or redundancy degraded', 'health', array('warning' => array('warning'), 'critical' => array('critical')));
watch.above('consumption', 'High power drawn', 'consumption', array(
'help' => 'Raises above the power set on the probe, in watts, for the chassis.',
'enabled' => false,
));
watch.missing('missing', 'Power supply missing', 'health', array('enabled' => false));
// Returns one Redfish resource, read by its path (/redfish/v1/...), the account in Basic. The password only
// goes in the Basic header: the answer is not secret.
function redfish_get($address, $path, $user, $password) {
$answer = http.get('https://' . $address . $path, headers=array('Accept' => 'application/json'), user=$user, password=$password);
if ($answer['status'] == 401 || $answer['status'] == 403) {
fail('misconfigured', 'The Redfish service refuses the account, code ' . $answer['status'], $path);
}
if ($answer['status'] != 200) {
fail('unreachable', 'The Redfish service answers code ' . $answer['status'], $path);
}
return json.read($answer['body']);
}
// Returns the health of a resource, in lower case (ok, warning, critical): the health it rolls up from what
// is under it when it gives one, its own otherwise, unknown when it gives neither.
function health_of($resource) {
$status = list.get($resource, 'Status', array());
$health = list.get($status, 'HealthRollup', '');
if ($health == '') {
$health = list.get($status, 'Health', '');
}
if ($health == '') {
return 'unknown';
}
return text.lower($health);
}
// Returns the instance name of each item: its name, or, when another item bears the same name (HPE iLO
// calls every power supply alike), its name followed by its id.
function instance_names($items) {
$counts = array();
foreach ($items as $item) {
$counts[$item['name']] = list.get($counts, $item['name'], 0) + 1;
}
$names = array();
foreach ($items as $key => $item) {
$names[$key] = $item['name'];
if ($item['name'] == '' || $counts[$item['name']] > 1) {
$names[$key] = text.trim($item['name'] . ' ' . $item['id']);
}
// A card that gives neither a name nor an id (both null, read as '').
if ($names[$key] == '') {
$names[$key] = 'unnamed ' . ($key + 1);
}
}
return $names;
}
// Each power supply and each redundancy group, as array('name', 'id', 'health'); the power drawn, by chassis.
$items = array();
$drawn = array();
$chassis_list = redfish_get($address, '/redfish/v1/Chassis', $user, $password);
foreach (list.get($chassis_list, 'Members', array()) as $member) {
$chassis = redfish_get($address, $member['@odata.id'], $user, $password);
$chassis_id = list.get($chassis, 'Id', $member['@odata.id']);
// Power: {"PowerSupplies": [{"MemberId": "0", "Name": "PS1", "Status": {"State": "Enabled", "Health":
// "OK"}}], "Redundancy": [{"MemberId": "0", "Name": "PSU Redundancy", "Mode": "N+m", "Status": {...}}],
// "PowerControl": [{"PowerConsumedWatts": 212}]}. A supply the card says absent is an empty bay.
if (list.has($chassis, 'Power')) {
$power = redfish_get($address, $chassis['Power']['@odata.id'], $user, $password);
foreach (list.get($power, 'PowerSupplies', array()) as $supply) {
if (list.get(list.get($supply, 'Status', array()), 'State', '') == 'Absent') {
continue;
}
$items = list.add($items, array('name' => list.get($supply, 'Name', 'Power supply'),
'id' => list.get($supply, 'MemberId', ''), 'health' => health_of($supply)));
}
foreach (list.get($power, 'Redundancy', array()) as $group) {
$items = list.add($items, array('name' => list.get($group, 'Name', 'Redundancy'),
'id' => list.get($group, 'MemberId', ''), 'health' => health_of($group)));
}
$watts = 0;
$measured = false;
foreach (list.get($power, 'PowerControl', array()) as $control) {
if (list.get($control, 'PowerConsumedWatts', '') != '') {
$watts = $watts + $control['PowerConsumedWatts'];
$measured = true;
}
}
if ($measured) {
$drawn['chassis ' . $chassis_id] = $watts;
}
} elseif (list.has($chassis, 'PowerSubsystem')) {
// PowerSubsystem: {"PowerSupplies": {"@odata.id": ".../PowerSupplies"}, "PowerSupplyRedundancy":
// [{"RedundancyType": "NPlusM", "Status": {...}}]}; each power supply a resource of its own. The power
// drawn is in the chassis' EnvironmentMetrics: {"PowerWatts": {"Reading": 212}}.
$subsystem = redfish_get($address, $chassis['PowerSubsystem']['@odata.id'], $user, $password);
if (list.has($subsystem, 'PowerSupplies')) {
$supplies = redfish_get($address, $subsystem['PowerSupplies']['@odata.id'], $user, $password);
foreach (list.get($supplies, 'Members', array()) as $link) {
$supply = redfish_get($address, $link['@odata.id'], $user, $password);
if (list.get(list.get($supply, 'Status', array()), 'State', '') == 'Absent') {
continue;
}
$items = list.add($items, array('name' => list.get($supply, 'Name', 'Power supply'),
'id' => list.get($supply, 'Id', ''), 'health' => health_of($supply)));
}
}
$number = 0;
foreach (list.get($subsystem, 'PowerSupplyRedundancy', array()) as $group) {
$number = $number + 1;
$items = list.add($items, array('name' => 'Redundancy ' . $number, 'id' => $chassis_id,
'health' => health_of($group)));
}
if (list.has($chassis, 'EnvironmentMetrics')) {
$metrics = redfish_get($address, $chassis['EnvironmentMetrics']['@odata.id'], $user, $password);
$reading = list.get(list.get($metrics, 'PowerWatts', array()), 'Reading', '');
if ($reading != '') {
$drawn['chassis ' . $chassis_id] = $reading;
}
}
}
}
if (list.count($items) == 0 && list.count($drawn) == 0) {
fail('misconfigured', 'The Redfish service serves no power supply and no power reading',
'No chassis has a Power or a PowerSubsystem resource; a blade reads its power from its enclosure.');
}
$names = instance_names($items);
foreach ($items as $key => $item) {
value('health', $item['health'], $names[$key]);
}
foreach ($drawn as $name => $watts) {
value('consumption', $watts, $name);
}