Skip to content

Proxmox ve rest api update - #805

Open
Garfieldttt wants to merge 21 commits into
zabbix:mainfrom
Garfieldttt:proxmox-ve-rest-api-update
Open

Proxmox ve rest api update#805
Garfieldttt wants to merge 21 commits into
zabbix:mainfrom
Garfieldttt:proxmox-ve-rest-api-update

Conversation

@Garfieldttt

Copy link
Copy Markdown
Contributor

No description provided.

Garfieldttt and others added 17 commits August 16, 2026 22:46
Updated the Zabbix template documentation for Proxmox VE REST API to include additional details on monitoring capabilities, requirements, and installation steps.
Updated the Zabbix template for Proxmox VE REST API with community additions, corrected URLs, and rebuilt the dashboard. Added new macros, discovery rules, and triggers for enhanced monitoring.
This Zabbix template enables full monitoring of a Proxmox VE environment via the official REST API. It includes metrics for hosts, clusters, VMs, containers, backup jobs, and more, with a rebuilt dashboard and corrected URLs.
Updated README to reflect community template modifications and added detailed instructions for rebuilding and API token creation.
This Zabbix template enables full monitoring of a Proxmox VE environment via the official REST API, collecting various metrics without requiring a Zabbix agent. It includes updated URLs, a rebuilt dashboard, and new discovery rules for enhanced functionality.
- guest stopped triggers used min(...,24h)=1 and could never fire
- task and backup alerts never cleared; bounded by new *.ALERT.WINDOW
  macros, with the Proxmox error text in the event name
- undefined {#NODE_ID}, missing =1 in the backup trigger, byte/bytes
  mismatch in the task status regex, unresolved macro in a trigger name
- dropped vm.balloonmin, it reads a field the API never returns
- disk temperature for ATA and NVMe, health as a dependent item,
  missing-disk trigger, dashboard cell
- README macro tables now match the template
… coverage

Adds nine endpoints that the template did not cover yet. All of them are read
with the permissions PVEAuditor already grants, except where noted.

  /nodes/{node}/services              PVE services, LLD with SubState
  /nodes/{node}/replication           ZFS replication jobs, LLD
  /nodes/{node}/disks/zfs             ZFS pools, LLD
  /nodes/{node}/certificates/info     certificate expiry, LLD
  /cluster/backup-info/not-backed-up  guests without a backup job
  /nodes/{node}/apt/repositories      broken repository files, warnings
  /nodes/{node}/apt/versions          pve-manager version, running kernel
  /nodes/{node}/subscription          subscription status
  /nodes/{node}/apt/update            pending updates, DISABLED by default

/apt/update is the only endpoint requiring Sys.Modify instead of Sys.Audit,
which PVEAuditor does not grant. Its items ship disabled and the repository
state is monitored through /apt/repositories instead.

Totals: 9 master items, 9 dependent items, 4 discovery rules, 18 item
prototypes, 10 triggers, 11 macros. No JavaScript preprocessing is used;
JSONPath, .length() and one calculated item are enough, since PVE already
returns last_sync and notafter as Unix epochs. Existing UUIDs are unchanged,
so importing with "Update existing" keeps the history.

Three corrections to existing objects:

- "PVE API not reachable" raised from Average to High. When the API is gone it
  is the cause of every other failure, so it should outrank them.
- "High CPU usage" had the threshold 90 hardcoded in the expression while the
  rest of the template uses macros. Now {$NODE.CPU.UTIL.MAX}.
- 14 numeric performance and capacity items had trends disabled and therefore
  kept no data beyond the history retention: pve.cpu.utilization, the three
  load average items, pve.memory.*, pve.rootfs.*, pve.swap.* and disk.wearout.
  Now 365d. Timestamp items keep trends off on purpose.

Field names and required privileges were taken from the official API schema
(pve-docs/api-viewer/apidoc.js) and verified against a PVE 9.2.10 node.
Zabbix validates the UUID version on import and rejects anything else with
"Invalid parameter ...: UUIDv4 is expected". The 50 UUIDs of the new objects
were derived with uuid5 and therefore carried version 5. The derivation stays
deterministic, the version and variant bits of a v4 are now stamped onto the
digest. All 254 UUIDs in the template are version 4 and unique.
zfs.health was a text item, so it could not be graphed or shown on a dashboard
and the trigger had to compare strings. It is now UNSIGNED with a value map:
0 ONLINE, 1 DEGRADED, 2 FAULTED, 3 OFFLINE, 4 REMOVED, 5 UNAVAIL, 6 SUSPENDED.
The mapping uses a chain of STR_REPLACE preprocessing steps, not a script. The
seven state names share no substring, so step order does not matter. A state
outside the list leaves text in place and the item turns visibly unsupported
instead of reporting a wrong number.

Three triggers were missing:

- ZFS pool fragmentation above {$ZFS.FRAG.WARN}. The macro was documented with
  a default of 60 but no trigger used it, so it did nothing.
- APT repository configuration has warnings, Info severity. This is the common
  case of the enterprise repository being enabled without a subscription. A
  host on the no-subscription repository reports one warning permanently, hence
  {$PVE.APT.REPO.WARN.MAX} to raise the tolerance there.
- Package updates pending, Info severity and disabled by default, matching the
  item it depends on.

The replication failure trigger now carries the error message PVE reported in
its event name, so the reason is visible without opening the item.

Verified: no threshold macro is left without a trigger using it.
The subscription due date was only available as text, without any alert.

- New item pve.subscription.nextduedate.epoch carrying the due date as a Unix
  timestamp
- Trigger "expires in less than {$PVE.SUBSCRIPTION.EXPIRE.DAYS}", Warning,
  lead time 30 days by default
- Trigger "has expired", Average, once the date has passed

Neither needs an enable macro: a host without a subscription reports no due
date, so the item stays empty and the triggers cannot fire.
{$PVE.SUBSCRIPTION.ALERT} still governs only the status trigger for notfound,
invalid and suspended.

Both the advance warning and the status trigger depend on the expired trigger,
so a lapsed subscription raises one problem instead of three.

This is the one place where JavaScript preprocessing is unavoidable.
/nodes/{node}/subscription reports nextduedate as a plain date string, unlike
the certificate endpoint which already delivers an epoch, and Zabbix has no
preprocessing step that parses a date. "Already expired" could be done with
date(), "expires in N days" cannot. One line, with a format check: an
unexpected format throws so the item turns visibly unsupported instead of
yielding a wrong date.
@Garfieldttt
Garfieldttt marked this pull request as ready for review August 20, 2026 14:06
Thomas Rzen added 4 commits August 21, 2026 09:18
Backup jobs from /cluster/backup. The template watched the backup runs only, so
a job that was disabled or is no longer advanced by the scheduler started no run,
reported no failure and stayed invisible. Adds a master item, a job counter, a
discovery rule with enabled and next run prototypes, and three triggers.

Running backups from /nodes/{node}/tasks with source=active and typefilter=vzdump.
The task list defaults to source=archive, where a task appears only once it has
finished, so a backup that never finishes produced no signal at all and the status
item kept reporting the previous successful run. Adds a master item, a running
counter, the real start time and a stuck backup trigger, plus a calculated
duration prototype for the last finished run.

Preprocessing: six JavaScript steps replaced by native JSONPath filters using &&,
|| and parentheses, twelve steps down to six. The unused {#HA_TYPE} macro is
removed together with the script that produced it.

New macros: PVE.BACKUP.JOBS.MIN, PVE.BACKUP.JOB.STALE, PVE.BACKUP.JOB.ALERT and
PVE.BACKUP.RUN.MAX.

Not yet verified against a running Zabbix server: the import itself, and whether
the engine accepts the filter expressions.
Cluster nodes total ran a script that returned the nodes field of the
type=cluster entry and counted the type=node entries when that entry was
missing. Both branches produce the same number: /cluster/status lists one
entry per cluster member, offline ones included, and a host that is not in
a cluster has no cluster entry at all and exactly one node entry. A single
JSONPath filter with .length() covers both cases, the same pattern the
neighbouring Cluster nodes online item already uses. Verified against a
standalone node, where old and new both report 1. The item had no
description and now has one.

PVE disks raw ran a script that was only a guard: it passed the payload
through when data was an array and returned an empty list otherwise, so a
broken response produced an empty discovery instead of an error. The guard
now sits where it belongs, as a custom value error handler on the discovery
rule's JSONPath step. The item prototypes below it already handle their own
errors.

Four scripts remain, all for cases native preprocessing cannot express.
PVE backup raw and PVE tasks raw deduplicate to the most recent run: the
task list is sorted by pstart, which resets at every reboot, so a filter
with .first() would not reliably hit the newest entry, and .max() would
yield the timestamp without the status of that run. Disk temperature reads
two different SMART shapes, numbered attribute 194 or 190 for ATA and SAS
and the composite temperature line for NVMe, and a preprocessing pipeline
cannot branch. Subscription expiry converts a date string to a Unix
timestamp, for which Zabbix has no preprocessing step.
The screenshots showed Proxmox VE 8.4.6 on a light theme, captured in
August 2025, with the old dashboard layout of eight pages named PVE,
Storage, QEMU/KVM-VMs, LXC - Container, Backup, Nodes, Tasks and Network.
The template ships five pages named Overview, Virtual machines, LXC
containers, Storage and disks, and Nodes and HA, so the images no longer
matched what a user gets after the import. Replaced with current captures
of those five pages.

The file is now identical to the README of the upstream repository.
Severities. A guest at 99% CPU for five minutes was High, which claims an
outage where there is only work: a compile, a transcode or the guest backup
itself reaches that. Lowered to Average, and the second level of the same
pair from Average to Warning, for both QEMU and LXC. High memory usage on
the node was Average although the ZFS ARC counts towards used memory, so a
ZFS host sits high permanently without being short of memory; now Warning.
High CPU average compares the load against the number of cores, which
describes a busy machine rather than an overloaded one; now Warning. The
interface down trigger went the other way, from Warning to Average: it only
fires for an interface with autostart set that was up before, so a bridge
or bond member has actually gone.

ZFS pool health was one High trigger for every state other than ONLINE.
DEGRADED means the pool lost redundancy but keeps serving, the other states
mean it does not, and the item is numeric with a value map, so the two are
now separate triggers: DEGRADED at Average, everything from FAULTED upwards
at High.

ZFS fragmentation warned on the fragmentation value alone. That value
describes the free space, not the stored data, and on a pool with room left
it costs nothing. It only starts to hurt once the pool is also filling up,
because the allocator has to work from ever smaller free segments. The
trigger now requires the usage to exceed the new macro
{$ZFS.FRAG.UTIL.MIN}, default 70, deliberately below the 80 of the usage
warning so it arrives first.

An expired certificate stayed at Warning forever, since the advance warning
keeps matching once the date has passed. Added a separate trigger for the
expired state at Average, with the advance warning depending on it, the
same pattern the subscription triggers already use.

User expiry used fuzzytime, which compares symmetrically: the problem
closed itself two days after the account had expired. Replaced with the
now() comparison used for certificates and the subscription, guarded with
a check for a non-zero timestamp so accounts that never expire, which
report 0, are excluded.

Trigger names carried macros without their context while the expression
resolved one per pool, storage or disk. A per-entity value made the alert
text wrong. The names now use the same context form as the expression.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant