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.

Storage

banshee/storage-collection/storage/netapp/https/ontap-9.6/

cluster

The state, fans, power supplies and temperature of each node of a NetApp ONTAP cluster by its REST API.

  • storage
  • netapp
  • ontap
  • cluster
  • node
  • https
  • api

by banshee1 installation1 version

v1Latest

Published on October 27, 2026. First version.

Keen 1.0.0 · 66 lines
// The nodes of a NetApp ONTAP cluster, by its REST API (GET /api/cluster/nodes), which ONTAP serves from 9.6:
// the state of each node, a takeover by its HA partner, and its failed fans, failed power supplies and
// temperature.
// The address of the API is the cluster's management address, the host's: https://<host:address>/api/.
need('protocol:http');
$address = need('host:address');
$user = need('protocol:http:user');
$password = need('protocol:http:password');

per('node', 'Nodes');
output('state', 'text', array('label' => 'State', 'per' => 'node'));
output('failed-fans', 'number', array('label' => 'Failed fans', 'per' => 'node'));
output('failed-power-supplies', 'number', array('label' => 'Failed power supplies', 'per' => 'node'));
output('temperature', 'text', array('label' => 'Temperature', 'per' => 'node'));

watch.state('down', 'Node down or taken over', 'state', array('critical' => array('down', 'taken_over', 'waiting_for_giveback')));
watch.expect('not-up', 'Node not up', 'state', array('warning' => array('up')));
watch.above('fans', 'Failed fans', 'failed-fans', array('warning' => 0));
watch.above('power-supplies', 'Failed power supplies', 'failed-power-supplies', array('critical' => 0));
watch.state('over-temperature', 'Over temperature', 'temperature', array('critical' => array('over')));
watch.missing('missing', 'Node missing', 'state', array('enabled' => false));

// One page of records at a time: ONTAP gives the next page's path under _links.next.href when it cuts the
// list, until the last. The password goes in Basic: the answer is not secret.
$path = '/api/cluster/nodes?fields=name,state,controller.failed_fan.count,controller.failed_power_supply.count,controller.over_temperature&max_records=1000';
$nodes = array();
while ($path != '') {
    $answer = http.get('https://' . $address . $path, user=$user, password=$password);
    if ($answer['status'] == 401 || $answer['status'] == 403) {
        fail('misconfigured', 'ONTAP refuses the account, code ' . $answer['status']);
    }
    if ($answer['status'] != 200) {
        fail('unreachable', 'The ONTAP API answers code ' . $answer['status'],
            'ONTAP serves its REST API from 9.6.');
    }
    $page = json.read($answer['body']);
    foreach ($page['records'] as $record) {
        $nodes = list.add($nodes, $record);
    }
    $path = '';
    $links = list.get($page, '_links', array());
    if (list.has($links, 'next')) {
        $path = $links['next']['href'];
    }
}
if (list.count($nodes) == 0) {
    fail('misconfigured', 'The ONTAP API lists no node');
}

// {"name": "cluster1-01", "state": "up", "controller": {"failed_fan": {"count": 0}, "failed_power_supply":
// {"count": 0}, "over_temperature": "normal"}}. The states of a node: up, booting, down, taken_over,
// waiting_for_giveback, degraded, unknown. A node that cannot be reached may lack its controller.
foreach ($nodes as $node) {
    $name = $node['name'];
    value('state', $node['state'], $name);
    $controller = list.get($node, 'controller', array());
    if (list.has($controller, 'failed_fan')) {
        value('failed-fans', $controller['failed_fan']['count'], $name);
    }
    if (list.has($controller, 'failed_power_supply')) {
        value('failed-power-supplies', $controller['failed_power_supply']['count'], $name);
    }
    if (list.has($controller, 'over_temperature')) {
        value('temperature', $controller['over_temperature'], $name);
    }
}