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.

Virtualization

banshee/virtualization-collection/virtualization/proxmox/https/

nodes

The state, processor, memory and load of each node of a Proxmox VE cluster by its API.

  • virtualization
  • proxmox
  • pve
  • node
  • https
  • api

by banshee1 installation2 versions

v2Latest

Published on October 28, 2026. One naming convention across collections; documentation completed.

Keen 1.0.0 · 77 lines
// The nodes of a Proxmox VE cluster, or a single Proxmox VE server, by its API behind an API token (GET
// /api2/json/nodes, then GET /api2/json/nodes/<node>/status for each node online): the state of each node,
// its processor and memory in use, and its load.
// The address is any node of the cluster, the host's: https://<host:address>:<port>/api2/json/.
need('protocol:http');
$address = need('host:address');
$token = need('protocol:http:token');
$port = variable('proxmox:port', 'integer', array(
    'label'   => 'API port',
    'help'    => 'The port of the Proxmox VE API: 8006, unless a proxy serves it on another.',
    'default' => 8006,
));

per('node', 'Nodes');
output('state', 'text', array('label' => 'State', 'per' => 'node'));
output('cpu-busy', 'number', array('label' => 'Overall processor usage', 'unit' => '%', 'per' => 'node'));
output('memory-used-percent', 'number', array('label' => 'Used memory', 'unit' => '%', 'per' => 'node'));
output('load-1', 'number', array('label' => 'Load, 1 minute', 'per' => 'node'));

watch.expect('not-online', 'Node not online', 'state', array('critical' => array('online')));
watch.above('cpu-high', 'Processor saturated', 'cpu-busy', array('warning' => 85, 'critical' => 95, 'for' => '3 runs'));
watch.above('memory-high', 'Memory saturated', 'memory-used-percent', array('warning' => 90, 'critical' => 95, 'for' => '3 runs'));
watch.above('load-high', 'High load', 'load-1', array('enabled' => false));
watch.missing('missing', 'Node missing', 'state', array('enabled' => false));

// One GET, the token in the header Authorization: PVEAPIToken=<user>@<realm>!<token id>=<secret>. Proxmox VE
// answers {"data": ...}. The answer is not secret, its body having carried none.
function api_get($base, $path, $token) {
    $answer = http.get($base . $path, headers=array('Authorization' => 'PVEAPIToken=' . $token));
    if ($answer['status'] == 401) {
        fail('misconfigured', 'Proxmox VE refuses the API token, code 401');
    }
    if ($answer['status'] == 403) {
        fail('misconfigured', 'The API token may not read ' . $path . ', code 403');
    }
    if ($answer['status'] != 200) {
        fail('unreachable', 'Proxmox VE answers ' . $path . ' with code ' . $answer['status']);
    }
    $page = json.read($answer['body']);
    return $page['data'];
}

$base = 'https://' . $address . ':' . $port . '/api2/json';

// [{"node": "pve1", "status": "online", "cpu": 0.0832, "maxcpu": 16, "mem": 27596369920, "maxmem":
// 67350208512, "uptime": 2312890}]. The state is online, offline or unknown; a node that is not online has no
// figures. cpu is a fraction of all its processors.
$nodes = api_get($base, '/nodes', $token);
if (list.count($nodes) == 0) {
    fail('misconfigured', 'Proxmox VE lists no node');
}
foreach ($nodes as $node) {
    $name = $node['node'];
    value('state', $node['status'], $name);
    if ($node['status'] != 'online') {
        continue;
    }
    if (list.has($node, 'cpu')) {
        value('cpu-busy', number.round(100 * $node['cpu'], 1), $name);
    }
    if (list.get($node, 'maxmem', 0) > 0) {
        value('memory-used-percent', number.round(100 * $node['mem'] / $node['maxmem'], 1), $name);
    }
    // {"loadavg": ["0.52", "0.61", "0.58"], ...}, as texts. A node gone between the two readings answers
    // another code: its load is not returned, its state having said online a moment before.
    $answer = http.get($base . '/nodes/' . url.encode($name) . '/status', headers=array('Authorization' => 'PVEAPIToken=' . $token));
    if ($answer['status'] == 403) {
        fail('misconfigured', 'The API token may not read the status of ' . $name . ', code 403');
    }
    if ($answer['status'] == 200) {
        $status = json.read($answer['body']);
        $load = list.get($status['data'], 'loadavg', array());
        if (list.count($load) > 0) {
            value('load-1', $load[0] + 0, $name);
        }
    }
}