Commit Graph

45 Commits

Author SHA1 Message Date
pulse-triage[bot] 0ef2bfa7f1 Keep local disk mounts beside system directories visible
Match automatic system-mount exclusions at directory boundaries rather than textual prefixes. Local /snapshots, /runtime and /var/lib/docker-data volumes retain capacity and usage through agent collection, both report paths and QGA aggregation; genuine system and remote/container mounts remain filtered. Regression controls include host usage alert onset and recovery.

Contract-Neutral: Restore existing filesystem eligibility without changing schemas, identities, explicit operator patterns or guest-command safety policy.
Change-source: pulse-maintainer
2026-10-07 15:36:53 +01:00
pulse-triage[bot] a61f1c7c82 chore(deps): qualify the native metrics dependency update
Update gopsutil and x/sys together with the purego version required by Darwin. Keep all other selected module versions unchanged and exercise real Linux CPU, memory, network and mount parsers alongside existing collector regressions.

Contract-Neutral: Dependency and compatibility-test maintenance only; no API, collection policy, platform support, deployment or privacy contract changes.
Change-source: pulse-maintainer
2026-09-10 00:03:20 +01:00
pulse-triage[bot] c8cead7d5a fix(agents): preserve explicit disk includes at ingestion
Carry the operator include override in disk reports so server filtering does not discard selected tmpfs mounts again. Keep automatic filtering for unmarked reports and agent exclusion precedence. Cover both ingest paths, collection, forwarding and wire compatibility for #1875.

Change-source: pulse-maintainer
2026-09-09 11:35:51 +01:00
pulse-triage[bot] e188a36eed test(hostmetrics): trace tmpfs includes through mountinfo
The failed #1875 beta retest leaves runtime environment and mount visibility unresolved. Existing fixtures stub partition enumeration, so cover the real gopsutil parser through filtering and capacity projection for single and combined tmpfs includes, missing includes and exclusion precedence. This proves the expected synthetic path, not the reporter installation or a product fix.

Change-source: pulse-maintainer
2026-09-09 10:28:59 +01:00
rcourtman b964eea767 fix(hostmetrics): retire disk probes before publishing results
A completed probe remained discoverable after its first caller returned,
allowing the next collection to reuse stale filesystem measurements.
Remove it and publish completion within one registry critical section.
Keep in-flight sharing, cancellation and timeout behaviour intact.

The controlled regression fails before this change. Twenty full package
runs and three race runs pass on the worker.

Refs #1928
2026-09-06 06:28:00 +01:00
pulse-triage[bot] fc80229b1b test: qualify unequal CPU reporting windows and collector replacement
Equal-cadence CPU regression checks can miss a shared cached percentage because both reporting loops expect the same average. Add unequal host and Docker intervals and replace the Docker collector to guard independent baseline ownership without resetting the host loop. This protects the existing issue #1894 repair; synthetic coverage does not establish installed-agent qualification or reporter resolution.

Contract-Neutral: regression-test coverage only; no runtime behaviour or product surface changes.
Change-source: pulse-maintainer
2026-09-05 12:08:33 +01:00
pulse-triage[bot] a0211b8242 fix(hostmetrics): skip disk probes for cancelled collections
An already-cancelled collector could still admit a filesystem syscall which ignores cancellation and blocks indefinitely. Reject expired contexts before registering work while retaining shared in-flight probes. The new regression test reproduces one unwanted probe before this fix; focused collector and guarded-probe tests pass ten repeats under the race detector.

Change-source: pulse-maintainer
2026-09-05 03:35:35 +01:00
pulse-triage[bot] 1989ba5187 perf(hostmetrics): reduce shared disk probe registry allocations
Shared probes prevent overlapping collectors from stacking blocked statfs calls, but their short-lived sync.Map entries added measured collection overhead. Use a mutex-protected typed map and allocate only for the owning caller, retaining deadlines, result publication and one in-flight syscall per mount.

The paired filtering benchmark drops from 244 to 206 allocations and 21345 to 20129 bytes per operation. Five hostmetrics package repeats pass under the race detector, including new concurrent healthy-mount coverage. This is local mocked-collection evidence, not release or real-mount qualification.

Change-source: pulse-maintainer
2026-09-05 03:21:07 +01:00
pulse-triage[bot] 3461c6cbd8 Deduplicate concurrent host mount usage probes
Host and Docker collectors can enter the same mount probe before either timeout expires. Register the in-flight call atomically and share its result and deadline so a blocked syscall cannot accumulate duplicate workers. Preserve the guard until the syscall returns even when a waiter is cancelled.

Change-source: pulse-maintainer
2026-09-04 22:58:19 +01:00
pulse-triage[bot] 0f972f42f2 Isolate host and Docker CPU sampling baselines (#1894)
Near-synchronous host and Docker reports consumed a shared CPU baseline, measuring collection bursts rather than each module's reporting interval. Retain a collector per host collector and a separate Docker module collector while preserving the package-level convenience API and disk filters.

Add an interleaved-counter regression covering both collection entry points. It fails when routed through the shared baseline and passes with isolated state. All hostmetrics, hostagent and dockeragent tests pass, as do the focused CPU regression tests under the race detector.

Change-source: pulse-maintainer
2026-09-04 22:29:32 +01:00
pulse-triage[bot] 437323412f Keep explicitly included tmpfs mounts distinct
Operators can explicitly select tmpfs paths that automatic filesystem
filtering would otherwise hide. Multiple such mounts commonly share both the
generic tmpfs device name and capacity, so applying normal storage
deduplication to them silently dropped configured disks from collection.

Limit device-and-capacity deduplication to normally visible storage while
preserving it for NAS bind mounts and subvolumes.

Change-source: issue #1875
2026-09-04 05:49:44 +01:00
pulse-triage[bot] 9d5cdfa088 Collect disks from the agent mount namespace
gopsutil defaults to PID 1's mount table, which differs from the namespace created for Pulse's hardened systemd service. Enumerate the collector's own mount table so explicitly included tmpfs and bind mounts are discoverable, while preserving deliberate host-proc overrides for container deployments.

Change-source: pulse-maintainer
2026-09-04 00:28:55 +01:00
pulse-triage[bot] ab556fc212 Skip numbered block-device partitions in disk I/O
Change-source: pulse-maintainer
2026-09-01 19:36:13 +01:00
rcourtman af6d482515 Keep an unresponsive mount from freezing host disk collection
A hard-mounted network filesystem with an unreachable server blocks
statfs in an uninterruptible kernel wait. The collector issued that
syscall inline for every mount, so one dead NFS mount silently froze
the whole reporting cycle and stretched agent shutdown into the kernel
retry window, and it paid that price for mounts the fstype filter was
going to discard anyway.

Decide type- and mountpoint-based skips before the usage syscall, so
network filesystems are never probed unless explicitly included, and
bound every remaining usage call with a timeout that leaves at most one
in-flight probe per mountpoint. A mount whose call never returned is
skipped on later cycles and re-included when the stalled call answers.

Refs discussion #1747

Contract-Neutral: Discussion #1747: behavioral bugfix in host disk collection; no payload or API shape change, filtered mounts were never reported
2026-08-21 06:43:31 +01:00
courtmanr@gmail.com 4dac4dd163 Allow agents to include filtered disk mounts 2026-08-11 16:37:37 +01:00
rcourtman 4e67a93d49 Keep virtio and Xen disks in host disk I/O collection
99ad8c2c4 routed collectDiskIO through fsfilters.IsVirtualBlockDevice to
stop ZFS zvols being collected. That helper answers "can this device report
SMART", which is the wrong question for I/O accounting: it also matches
vd* and xvd*, so every agent running inside a KVM, Proxmox or Xen VM lost
host disk I/O entirely, since vda is the real disk on those machines. It
also dropped md, nbd, rbd, drbd and pmem, none of which were part of the
report.

Add IsNonPhysicalDiskIODevice for the I/O question and use it here. It
covers only loop, ram, zram, dm- and zd, so the zvol fix for #1671 stands
while everything that was collected before 99ad8c2c4 is collected again.
Tests assert vda and xvda survive the I/O filter while still being excluded
from SMART, so the two predicates cannot be conflated again.

Contract-Neutral: behavioural regression fix, no contract delta

Refs #1671
2026-08-03 17:39:55 +01:00
rcourtman 99ad8c2c4b Skip virtual block devices in host disk I/O collection
collectDiskIO hand-rolled a partial exclusion list (loop, ram, dm-) while
the two sibling paths, agent SMART collection and the server-side resource
registry, both use fsfilters.IsVirtualBlockDevice. ZFS zvols were therefore
collected as if they were physical disks: a Proxmox host with ZFS-backed
guest storage exposes one zd<N> device per zvol, so a few hundred guests
produced a few hundred phantom disks in every agent report, driving
sustained metrics and resource_changes growth.

Route the filter through the canonical helper so zd, zram, nbd, rbd, drbd,
md, pmem, vd and xvd are all excluded. md and dm- aggregates additionally
restated the I/O of their own physical members, so counting them alongside
those members double counted host disk I/O.

Refs #1671
2026-08-03 09:43:11 +01:00
courtmanr@gmail.com 218661a396 feat(storage): surface Proxmox ZFS datasets 2026-07-30 16:45:13 +01:00
courtmanr@gmail.com 07a0d84464 Average host CPU usage across the report interval (#1648)
collectCPUUsage took a blocking 1-second spot sample once per report
interval, which overstates CPU on mostly-idle guests (reported on a
2-vCPU aarch64 KVM guest) and delayed every Collect() by a second.
Track the previous cumulative cpu.Times sample and report busy/total
across the elapsed interval instead, falling back to the spot sample
only for the first collection and counter resets (reboot, migration).
CPUUsagePercent keeps its meaning in the report schema.

Contract-Neutral: hostmetrics sampling behavior change with no subsystem contract surface delta
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 22:49:08 +01:00
rcourtman 1ef4a68735 Clarify disk exclusion behavior 2026-07-24 09:20:26 +01:00
rcourtman cefc032a37 Fix patch-release follow-up regressions
Refs #1510

Refs #1501

Refs #1507

Refs #1442

- persist scoped workloads status filters across platform navigation
- derive host memory pressure from available memory
- reapply system settings after every monitor reload path
- bound PBS backup snapshot polling workers during large backup scans
2026-07-04 18:37:59 +01:00
rcourtman ba8cfc8229 Harden release gate workflow 2026-06-14 19:19:39 +01:00
rcourtman ff5a0b4957 memory(cache): extend the reclaimable split to standalone host agents
f62f35e24 restored the v5 used | cache | free memory split for Proxmox
nodes and guests, but standalone host agents still reported a flat
used/free pair, so the Machines page memory bar could not show the
reclaimable segment. Flagged by the Machines page v5 parity audit.

- Host agent reports cacheBytes (gopsutil Available minus Free); the
  ZFS ARC adjustment recomputes free so used + cache + free still
  covers the total.
- ApplyHostReport maps the field into models.Memory.Cache and clamps
  inconsistent or older-agent reports so used + cache never exceeds
  total.
- AgentMemoryMeta carries cache onto unified resources so the frontend
  agent payload exposes it.
- Mock generic hosts split a third of non-used pages as cache, and the
  node-linked host conversion now holds the invariant instead of
  stacking the node's cache on top of a recomputed free.
- Contracts: monitoring, unified-resources, and storage-recovery now
  document the split (also covering the f62f35e24 node/guest surface,
  which landed without contract deltas).
2026-06-11 21:47:30 +01:00
rcourtman 6b5f57a866 Prefer the root-mounted dataset for ZFS pool summaries
Back-port v5 fix b83f717ce to v6. zfsMountpointScore now gives top
priority (score 0) to a dataset whose mountpoint is exactly '/', even a
child dataset like rpool/ROOT/pve-1, so it outranks a pool-root dataset
mounted elsewhere (e.g. rpool at /mnt/storage). Previously a pool-root
dataset won unconditionally, so the pool summary could reflect the wrong
dataset's usage. Updates scoring tests and adds a regression case.
2026-06-04 09:43:07 +01:00
rcourtman 778a2577b6 feat: Pulse v6 release 2026-03-18 16:06:30 +00:00
rcourtman ac83074fc2 fix(hostmetrics): skip network mounts before usage probe (#1313) 2026-03-03 20:27:41 +00:00
rcourtman 6c720b7aea fix(freebsd): use golang.org/x/sys/unix.SysctlRaw instead of syscall.SysctlRaw
syscall.SysctlRaw is Darwin-only in Go's standard library; FreeBSD
requires the equivalent from golang.org/x/sys/unix. This fixes the
Docker cross-compilation build failure for the freebsd/amd64 target.

(cherry picked from commit 5fe16c75a075b817f90b7192d8270a7bd6677017)
2026-02-18 13:00:02 +00:00
rcourtman efa916ee2a fix(memory): correct memory reporting for Linux VMs and FreeBSD ZFS ARC
Linux VM page cache (#1270): QEMU VM memory now falls back to Proxmox
RRD's memavailable metric (which excludes reclaimable page cache) when
the qemu-guest-agent doesn't provide MemInfo.Available. Previously the
fallback was detailedStatus.Mem (total - MemFree), inflating usage to
80%+ on VMs with normal Linux page cache. Mirrors the existing LXC
rrd-memavailable path.

FreeBSD ZFS ARC (#1264, #1051): The host agent now reads
kstat.zfs.misc.arcstats.size via SysctlRaw on FreeBSD and subtracts
the ARC size from reported memory usage. ZFS ARC is reclaimable under
memory pressure (like Linux SReclaimable) but gopsutil counts it as
wired/non-reclaimable, causing false 90%+ memory alerts on TrueNAS
and FreeBSD hosts. Build-tagged so it compiles cleanly on all platforms.

Fixes #1270
Fixes #1264
Fixes #1051

(cherry picked from commit 94502f83ff9ffc6da28aaadc946a2f7d8b4e9bac)
2026-02-18 12:56:53 +00:00
rcourtman 049a3e424c Add memory regression tests for agent and scheduler 2026-02-04 19:33:29 +00:00
rcourtman f0a356c016 fix: ZFS pool usage now includes zvols and all pool consumers
The previous reconciliation logic (issue #1052) used per-dataset statfs
values for Total and Used. On Proxmox systems, statfs on a mounted
dataset (e.g. rpool/ROOT/pve-1) only reports that dataset's own usage,
completely missing zvols (VM disk images) and other datasets. This caused
storage bars to show ~0% usage (a few GB of OS files) when the pool
actually had terabytes of VM data allocated.

Fix: derive usable pool capacity from the ratio of dataset Free (usable
pool-available from statfs) to zpool Free (raw pool-available from zpool
list). This ratio converts raw zpool Size to usable total, and Used is
computed as Total - Free. This captures all pool consumers including
zvols, handles RAIDZ parity overhead and mirrors uniformly, and produces
correct usage percentages.

Verified with tests for RAIDZ, mirrors, and both with zvols present.
2026-01-29 12:08:38 +00:00
rcourtman 824d65830c Add debug logging to ZFS disk collection for diagnostics
Adds zerolog debug statements throughout the ZFS collection pipeline
(collector.go and zfs.go) to trace partition discovery, dataset
collection, zpool stats fetching, and pool summarization. This will
help diagnose issues like empty storage bars on mirror-vdev pools.
2026-01-28 17:30:53 +00:00
rcourtman 61bb582d82 fix: disk-exclude now works with device paths and disk I/O
- Add MatchesDiskExclude() to check both device path and mountpoint
- Add MatchesDeviceExclude() for device-only matching
- Update collectDisks to check device in addition to mountpoint
- Update collectDiskIO to respect disk exclusions
- Patterns like /dev/sda, sda, or /mnt/backup all work now

Related to #1142
2026-01-21 19:03:05 +00:00
rcourtman 1816e2dbb8 fix(agent): use dataset used capacity for RAIDZ pools instead of zpool alloc
For RAIDZ pools, zpool ALLOC includes parity overhead, but users expect
to see actual data usage. Now using dataset Used value (from statfs)
when RAIDZ is detected, matching the existing fix for total capacity.

Fixes the second part of #1052 where used capacity was inflated.
2026-01-10 15:25:28 +00:00
rcourtman 49272bd48c fix: Show usable RAIDZ capacity instead of raw pool size
For RAIDZ/mirror pools, zpool list SIZE reports raw capacity (sum of
all disks), but users expect usable capacity (accounting for parity).
The dataset stats from statfs give the correct usable capacity.

Now uses dataset Total when it's smaller than zpool Size, indicating
RAIDZ/mirror overhead.

Related to #1052
2026-01-08 09:38:18 +00:00
rcourtman 3fdf753a5b Enhance devcontainer and CI workflows
- Add persistent volume mounts for Go/npm caches (faster rebuilds)
- Add shell config with helpful aliases and custom prompt
- Add comprehensive devcontainer documentation
- Add pre-commit hooks for Go formatting and linting
- Use go-version-file in CI workflows instead of hardcoded versions
- Simplify docker compose commands with --wait flag
- Add gitignore entries for devcontainer auth files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 22:29:15 +00:00
rcourtman 4ce1d551e4 fix: Deduplicate disks by device+total to fix Synology storage overcounting. Related to #953
Synology NAS creates multiple shared folders (e.g., /volume1/docker, /volume1/photos)
that are all mount points on the same underlying BTRFS volume. Each reported the same
16TB total, causing Pulse to show 64TB+ instead of 16TB.

The fix tracks device+total combinations and only counts each unique pair once.
When duplicates are found, the shallowest mountpoint (e.g., /volume1) is preferred.

Added a unit test to verify the deduplication works correctly.
2025-12-29 14:03:32 +00:00
rcourtman c1422882bd feat: Add disk exclusion filter for host agent. Closes #896
Users can now exclude specific mount points from disk monitoring:
- Via CLI: --disk-exclude /mnt/backup --disk-exclude '/media/*'
- Via env: PULSE_DISK_EXCLUDE=/mnt/backup,*pbs*

Patterns support:
- Exact paths: /mnt/backup
- Prefix patterns: /mnt/ext*
- Contains patterns: *pbs*

This addresses the common case where external disks or
PBS datastores are being monitored but shouldn't be.
2025-12-25 12:04:40 +00:00
rcourtman fdb2a07f56 fix(agent): find zpool binary on TrueNAS SCALE (#718)
Enhanced zpool binary lookup to try common paths when exec.LookPath fails.
This fixes issue #718 where TrueNAS SCALE reports inflated storage because
the agent runs with a restricted PATH that doesn't include /usr/sbin.

Changes:
- Added findZpool() helper that tries common paths like /usr/sbin/zpool,
  /sbin/zpool, /usr/local/sbin/zpool for TrueNAS/FreeBSD/Linux systems
- Added commonZpoolPaths variable listing typical zpool locations
- Added tests for the new findZpool function

This ensures zpool list is used for accurate pool-level capacity instead
of falling back to dataset-level summation.
2025-12-18 16:23:56 +00:00
rcourtman 8948e84fe5 feat: AI features, agent improvements, and host monitoring enhancements
AI Chat Integration:
- Multi-provider support (Anthropic, OpenAI, Ollama)
- Streaming responses with markdown rendering
- Agent command execution for remote troubleshooting
- Context-aware conversations with host/container metadata

Agent Updates:
- Add --enable-proxmox flag for automatic PVE/PBS token setup
- Improve auto-update with semver comparison (prevents downgrades)
- Add updatedFrom tracking to report previous version after update
- Reduce initial update check delay from 30s to 5s
- Add agent version column to Hosts page table

Host Metrics:
- Add DiskIO stats collection (read/write bytes, ops, time)
- Improve disk filtering to exclude Docker overlay mounts
- Add RAID array monitoring via mdadm
- Enhanced temperature sensor parsing

Frontend:
- New Agent Version column on Hosts overview table
- Improved node modal with agent-first installation flow
- Add DiskIO display in host drawer
- Better responsive handling for metric bars
2025-12-05 10:37:02 +00:00
rcourtman da51449392 fix: Exclude TrueNAS Docker overlay mounts from disk stats
Host agent was including Docker overlay2 mounts from TrueNAS SCALE's
.ix-apps directory in disk totals. These mounts inherit the ZFS pool's
AVAIL space, causing massively inflated storage numbers (e.g., 173 TB
per container overlay instead of actual usage).

Changes:
- Add /mnt/.ix-apps/docker/ to container overlay path exclusions
- Use ShouldSkipFilesystem() in host agent disk collection (was only
  using ShouldIgnoreReadOnlyFilesystem() which missed container paths)
- Add test cases for TrueNAS overlay paths

Related to #718
2025-12-04 03:03:04 +00:00
rcourtman e0ccd2b8db Add unit tests for ZFS storage utility functions (hostmetrics)
65 test cases covering 8 functions:
- parseZpoolList: zpool command output parsing (15 cases)
- uniqueZFSPools: pool name deduplication (7 cases)
- bestZFSMountpoints: mountpoint selection logic (8 cases)
- zfsMountpointScore: mountpoint scoring algorithm (7 cases)
- zfsPoolFromDevice: pool name extraction (6 cases)
- calculatePercent: percentage calculation (7 cases)
- clampPercent: value clamping (8 cases)
- bestZFSPoolDatasets: dataset selection (7 cases)

First comprehensive unit test coverage for internal/hostmetrics
package ZFS utilities.
2025-11-30 12:50:58 +00:00
courtmanr@gmail.com 85461618fd Fix ZFS storage reporting on TrueNAS SCALE (#718)
- Refactor collector to support mocking
- Fix ZFS detection to support 'fuse.zfs' and case-insensitivity
- Add regression tests for ZFS dataset deduplication
2025-11-22 23:53:39 +00:00
rcourtman 45a8cf68ac fix(hostmetrics): dedupe ZFS pools for usable storage
Related to #718
2025-11-18 23:38:11 +00:00
rcourtman 2e1ef44ecd Filter read-only filesystems from host agent disk metrics (related to #690)
Squashfs snap mounts on Ubuntu (and similar read-only filesystems like
erofs on Home Assistant OS) always report near-full usage and trigger
false disk alerts. The filter logic existed in Proxmox monitoring but
wasn't applied to host agents.

Changes:
- Extract read-only filesystem filter to shared pkg/fsfilters package
- Apply filter in hostmetrics.collectDisks() for host/docker agents
- Apply filter in monitor.ApplyHostReport() for backward compatibility
- Convert internal/monitoring/fs_filters.go to wrapper functions

This prevents squashfs, erofs, iso9660, cdfs, udf, cramfs, romfs, and
saturated overlay filesystems from generating alerts. Filtering happens
at both collection time (agents) and ingestion time (server) to ensure
older agents don't cause false alerts until they're updated.
2025-11-12 09:47:02 +00:00
rcourtman f2acdd59af Normalize docker agent version handling 2025-10-28 08:42:58 +00:00