Hardware
banshee/hardware-collection/hardware/ibm/https/hmc/
managed-systems
The state and the attention LED of each IBM Power server an HMC manages, by the HMC REST API.
by banshee0 installations1 version
Published on October 27, 2026. First version.
// The IBM Power servers a Hardware Management Console (HMC) manages, by its REST API: the state of each
// managed system as the HMC sees it, and its attention LED, which the server lights when it has a problem
// to service. The address is the HMC's, the host's: https://<host:address>/rest/api/.
need('protocol:http');
$address = need('host:address');
$user = need('protocol:http:user');
$password = need('protocol:http:password');
per('system', 'Managed systems');
output('state', 'text', array('label' => 'State', 'per' => 'system'));
output('attention-led', 'text', array('label' => 'Attention LED', 'per' => 'system'));
watch.expect('not-operating', 'System not operating', 'state', array(
'help' => 'Raises when the HMC gives a system any other state than the ones listed: powered off, in error, no connection to its service processor.',
'critical' => array('operating'),
));
watch.state('attention', 'Attention LED lit', 'attention-led', array(
'help' => 'The server lights its attention LED when it logs a problem to service; the serviceable events of the HMC say which.',
'warning' => array('true'),
));
watch.missing('missing', 'System missing', 'state', array('enabled' => false));
// Opens a session: PUT /rest/api/web/Logon, the account in the XML body IBM documents, each value passed by
// xml.escape. The HMC answers a LogonResponse whose X-API-Session is the session; the answer is secret, its
// body having carried the password. The session is kept in the cache, encrypted on the poller, so that one
// session serves every run: IBM warns that sessions opened and never closed wear the HMC's memory.
function logon($address, $user, $password) {
$body = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
. '<LogonRequest xmlns="http://www.ibm.com/xmlns/systems/power/firmware/web/mc/2012_10/" schemaVersion="V1_1_0">'
. '<Metadata><Atom/></Metadata>'
. '<UserID kb="CUR" kxe="false">' . xml.escape($user) . '</UserID>'
. '<Password kb="CUR" kxe="false">' . xml.escape($password) . '</Password>'
. '</LogonRequest>';
$answer = http.put('https://' . $address . '/rest/api/web/Logon', $body,
headers=array('Content-Type' => 'application/vnd.ibm.powervm.web+xml; type=LogonRequest'));
if ($answer['status'] == 401 || $answer['status'] == 403) {
fail('misconfigured', 'The HMC refuses the account, code ' . $answer['status']);
}
if ($answer['status'] != 200) {
fail('unreachable', 'The HMC answers the logon with code ' . $answer['status']);
}
$logon = xml.read($answer['body']);
$session = $logon['LogonResponse'][0]['X-API-Session'][0]['#text'];
cache.set('session', $session, 604800);
return $session;
}
// Returns the entries of an Atom feed of the HMC, read by its path (/rest/api/uom/...), with the session kept
// in the cache. A session the HMC no longer knows (expired, HMC restarted) is refused, 401 or 403: it is
// closed by DELETE /rest/api/web/Logon, whatever its answer, and replaced by a new one, once. The HMC answers
// 204, without a body, for a feed with no entry. Each success keeps the session in the cache seven more days.
function hmc_entries($address, $path, $user, $password) {
if (cache.has('session')) {
$session = cache.get('session');
} else {
$session = logon($address, $user, $password);
}
$answer = http.get('https://' . $address . $path, headers=array('X-API-Session' => $session));
if ($answer['status'] == 401 || $answer['status'] == 403) {
http.delete('https://' . $address . '/rest/api/web/Logon', headers=array('X-API-Session' => $session), errors=$err);
$session = logon($address, $user, $password);
$answer = http.get('https://' . $address . $path, headers=array('X-API-Session' => $session));
}
if ($answer['status'] == 401 || $answer['status'] == 403) {
fail('misconfigured', 'The account may not read ' . $path . ', code ' . $answer['status']);
}
if ($answer['status'] == 204) {
cache.set('session', $session, 604800);
return array();
}
if ($answer['status'] != 200) {
fail('unreachable', 'The HMC answers ' . $path . ' with code ' . $answer['status']);
}
cache.set('session', $session, 604800);
$feed = xml.read($answer['body']);
return list.get($feed['feed'][0], 'entry', array());
}
// Returns the text of the first child of that name, '' when the HMC does not give it. The HMC writes each
// value with its attributes: <State kb="ROR" kxe="false">operating</State>.
function child_text($element, $name) {
$children = list.get($element, $name, array());
if (list.count($children) == 0) {
return '';
}
return list.get($children[0], '#text', '');
}
// <entry><id>UUID</id><content type="..."><ManagedSystem:ManagedSystem ...> <State>operating</State>
// <SystemName>p10-a</SystemName> <PhysicalSystemAttentionLEDState>false</PhysicalSystemAttentionLEDState>.
// Two systems of one name each keep their own, the second carrying its UUID.
$systems = hmc_entries($address, '/rest/api/uom/ManagedSystem', $user, $password);
if (list.count($systems) == 0) {
fail('misconfigured', 'The HMC lists no managed system', 'The account sees only the systems its resource role is set on.');
}
$seen = array();
foreach ($systems as $entry) {
$system = $entry['content'][0]['ManagedSystem:ManagedSystem'][0];
$name = child_text($system, 'SystemName');
if ($name == '' || list.has($seen, $name)) {
$name = $name . ' #' . $entry['id'][0];
}
$seen[$name] = true;
value('state', child_text($system, 'State'), $name);
$led = child_text($system, 'PhysicalSystemAttentionLEDState');
if ($led != '') {
value('attention-led', $led, $name);
}
}