Hardware
banshee/hardware-collection/hardware/ibm/https/hmc/
lpars
The state and the RMC connection of each logical partition and Virtual I/O Server an IBM HMC manages, by the HMC REST API.
by banshee0 installations1 version
Published on October 27, 2026. First version.
// The logical partitions of the IBM Power servers a Hardware Management Console (HMC) manages, by its REST
// API: the state of each partition (AIX, Linux, IBM i) and of each Virtual I/O Server, and the state of its
// RMC connection to the HMC. Every partition is watched; one stopped on purpose is left out by the selection
// of the probe. 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('partition', 'Partitions');
output('state', 'text', array('label' => 'State', 'per' => 'partition'));
output('rmc', 'text', array('label' => 'RMC connection', 'per' => 'partition'));
watch.expect('not-running', 'Partition not running', 'state', array('critical' => array('running')));
watch.state('rmc-inactive', 'RMC connection inactive', 'rmc', array(
'help' => 'Raises when the HMC has lost the RMC connection to a partition, through which it makes dynamic changes (DLPAR) to a running partition.',
'warning' => array('inactive'),
'enabled' => false,
));
watch.missing('missing', 'Partition 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: <PartitionState kb="ROO" kxe="false">running</PartitionState>.
function child_text($element, $name) {
$children = list.get($element, $name, array());
if (list.count($children) == 0) {
return '';
}
return list.get($children[0], '#text', '');
}
// The partitions (/rest/api/uom/LogicalPartition) and the Virtual I/O Servers (/rest/api/uom/VirtualIOServer)
// of every managed system, two feeds of one form: <entry><id>UUID</id><content type="..."><LogicalPartition:
// LogicalPartition ...> (<VirtualIOServer:VirtualIOServer ...>) <PartitionName>lpar1</PartitionName>
// <PartitionState>running</PartitionState> <ResourceMonitoringControlState>active</...>.
// Two partitions of one name (on two systems) each keep their own, the second carrying its UUID.
$read = 0;
$seen = array();
$feeds = array('LogicalPartition', 'VirtualIOServer');
foreach ($feeds as $kind) {
$entries = hmc_entries($address, '/rest/api/uom/' . $kind, $user, $password);
foreach ($entries as $entry) {
$partition = $entry['content'][0][$kind . ':' . $kind][0];
$name = child_text($partition, 'PartitionName');
if ($name == '' || list.has($seen, $name)) {
$name = $name . ' #' . $entry['id'][0];
}
$seen[$name] = true;
value('state', child_text($partition, 'PartitionState'), $name);
$rmc = child_text($partition, 'ResourceMonitoringControlState');
if ($rmc != '') {
value('rmc', $rmc, $name);
}
$read = $read + 1;
}
}
if ($read == 0) {
fail('misconfigured', 'The HMC lists no partition', 'The account sees only the partitions its resource role is set on.');
}