banshee is under active development: collectors will keep changing, and a version you publish will likely need adjustments. For stable use, wait for version 1.0.0.

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.

  • hardware
  • ibm
  • power
  • hmc
  • lpar
  • partition
  • vios
  • aix
  • ibm-i
  • https

by banshee0 installations1 version

v1Latest

Published on October 27, 2026. First version.

Keen 1.0.0 · 116 lines
// 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.');
}