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/

cluster

The quorum of a Proxmox VE cluster and the state of each of its nodes by its API.

  • virtualization
  • proxmox
  • pve
  • cluster
  • quorum
  • corosync
  • https
  • api

by banshee1 installation1 version

v1Latest

Published on October 27, 2026. First version.

Keen 1.0.0 · 72 lines
// The cluster of Proxmox VE, by its API behind an API token (GET /api2/json/cluster/status): whether it has
// quorum, how many of its nodes are online, and whether each node is. A single Proxmox VE server, without
// cluster, reads "standalone".
// 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('quorum', 'text', array('label' => 'Quorum'));
output('online', 'number', array('label' => 'Nodes online'));
output('nodes', 'number', array('label' => 'Nodes'));
output('state', 'text', array('label' => 'State', 'per' => 'node'));

watch.state('no-quorum', 'Cluster without quorum', 'quorum', array('critical' => array('no')));
watch.state('offline', 'Node offline', 'state', array('critical' => array('offline')));
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';

// [{"type": "cluster", "name": "prod", "quorate": 1, "nodes": 3, "version": 7}, {"type": "node", "name":
// "pve1", "online": 1, "local": 1, "nodeid": 1, "ip": "192.0.2.11"}, ...]. A server without cluster gives its
// own node only.
$entries = api_get($base, '/cluster/status', $token);
$quorum = 'standalone';
$online = 0;
$count = 0;
foreach ($entries as $entry) {
    if ($entry['type'] == 'cluster') {
        $quorum = 'no';
        if (list.get($entry, 'quorate', 0) == 1) {
            $quorum = 'yes';
        }
    }
    if ($entry['type'] == 'node') {
        $count = $count + 1;
        $state = 'offline';
        if (list.get($entry, 'online', 0) == 1) {
            $state = 'online';
            $online = $online + 1;
        }
        value('state', $state, $entry['name']);
    }
}
if ($count == 0) {
    fail('misconfigured', 'Proxmox VE lists no node in its cluster status');
}
value('quorum', $quorum);
value('online', $online);
value('nodes', $count);