Virtualization
banshee/virtualization-collection/virtualization/proxmox/https/
guests
The state, node and high availability of each virtual machine and container of a Proxmox VE cluster by its API.
by banshee1 installation1 version
Published on October 27, 2026. First version.
// The guests of a Proxmox VE cluster, or a single Proxmox VE server, by its API behind an API token (GET
// /api2/json/cluster/resources?type=vm): the state of each virtual machine and container, its name, the node
// it runs on, and its high-availability state when it has one. The watches of the expected state are not
// checked by default: only the administrator knows which guests must run and which must stay stopped, and
// says it by the selection of the probe, one probe for each.
// A guest is named by its id (VMID), unique in the cluster: its name is not, and Proxmox VE stops giving it
// when the guest's node is down, the moment the guest must still be seen under the same name.
// 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('guest', 'Guests');
output('state', 'text', array('label' => 'State', 'per' => 'guest'));
output('name', 'text', array('label' => 'Name', 'per' => 'guest'));
output('node', 'text', array('label' => 'Node', 'per' => 'guest'));
output('ha', 'text', array('label' => 'High availability', 'per' => 'guest'));
watch.expect('not-running', 'Guest not running', 'state', array(
'help' => 'For a selection of the guests that must run: critical when one is stopped or paused.',
'critical' => array('running'),
'enabled' => false,
));
watch.expect('not-stopped', 'Guest not stopped', 'state', array(
'help' => 'For a selection of the guests that must stay stopped: warning when one runs.',
'warning' => array('stopped'),
'enabled' => false,
));
watch.state('ha-error', 'High availability in error', 'ha', array(
'warning' => array('fence', 'recovery'),
'critical' => array('error'),
));
watch.missing('missing', 'Guest 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';
// [{"id": "qemu/101", "vmid": 101, "name": "web01", "type": "qemu", "node": "pve1", "status": "running",
// "template": 0, "hastate": "started", ...}]. The status is running or stopped, and unknown, without name,
// for the guest of a node down. A template, never started, reads "template". hastate is given for a guest
// under high availability only.
$guests = api_get($base, '/cluster/resources?type=vm', $token);
foreach ($guests as $guest) {
$id = '' . $guest['vmid'];
$state = list.get($guest, 'status', 'unknown');
if (list.get($guest, 'template', 0) == 1) {
$state = 'template';
}
value('state', $state, $id);
if (list.get($guest, 'name', '') != '') {
value('name', $guest['name'], $id);
}
value('node', $guest['node'], $id);
if (list.get($guest, 'hastate', '') != '') {
value('ha', $guest['hastate'], $id);
}
}