feat(agent): zfs collector for pools + datasets/snapshots with fixture tests
This commit is contained in:
parent
6fca450d7e
commit
8c3e953e4e
5 changed files with 305 additions and 0 deletions
37
agent/test/fixtures/zfs/zfs_list.json
vendored
Normal file
37
agent/test/fixtures/zfs/zfs_list.json
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"output_version": { "command": "zfs list", "vers_major": 0, "vers_minor": 1 },
|
||||
"datasets": {
|
||||
"rpool": {
|
||||
"name": "rpool",
|
||||
"type": "FILESYSTEM",
|
||||
"properties": {
|
||||
"used": { "value": "200000000000" },
|
||||
"available": { "value": "300000000000" },
|
||||
"usedbysnapshots": { "value": "5000000000" }
|
||||
}
|
||||
},
|
||||
"rpool/data": {
|
||||
"name": "rpool/data",
|
||||
"type": "FILESYSTEM",
|
||||
"properties": {
|
||||
"used": { "value": "100000000000" },
|
||||
"available": { "value": "300000000000" },
|
||||
"usedbysnapshots": { "value": "2000000000" }
|
||||
}
|
||||
},
|
||||
"rpool/data@daily-2026-04-20": {
|
||||
"name": "rpool/data@daily-2026-04-20",
|
||||
"type": "SNAPSHOT",
|
||||
"properties": {
|
||||
"creation": { "value": "1745107200" }
|
||||
}
|
||||
},
|
||||
"rpool/data@daily-2026-04-21": {
|
||||
"name": "rpool/data@daily-2026-04-21",
|
||||
"type": "SNAPSHOT",
|
||||
"properties": {
|
||||
"creation": { "value": "1745193600" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
agent/test/fixtures/zfs/zpool_list.json
vendored
Normal file
23
agent/test/fixtures/zfs/zpool_list.json
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"output_version": { "command": "zpool list", "vers_major": 0, "vers_minor": 1 },
|
||||
"pools": {
|
||||
"rpool": {
|
||||
"name": "rpool",
|
||||
"size": 500000000000,
|
||||
"alloc": 200000000000,
|
||||
"free": 300000000000,
|
||||
"frag": 17,
|
||||
"cap": 40,
|
||||
"health": "ONLINE"
|
||||
},
|
||||
"tank": {
|
||||
"name": "tank",
|
||||
"size": 8000000000000,
|
||||
"alloc": 6000000000000,
|
||||
"free": 2000000000000,
|
||||
"frag": 55,
|
||||
"cap": 75,
|
||||
"health": "DEGRADED"
|
||||
}
|
||||
}
|
||||
}
|
||||
45
agent/test/fixtures/zfs/zpool_status.json
vendored
Normal file
45
agent/test/fixtures/zfs/zpool_status.json
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"output_version": { "command": "zpool status", "vers_major": 0, "vers_minor": 1 },
|
||||
"pools": {
|
||||
"rpool": {
|
||||
"name": "rpool",
|
||||
"state": "ONLINE",
|
||||
"scan": {
|
||||
"function": "scrub",
|
||||
"state": "FINISHED",
|
||||
"end_time": "Sat Apr 19 02:00:00 2026"
|
||||
},
|
||||
"error_count": "0",
|
||||
"vdevs": {
|
||||
"mirror-0": {
|
||||
"name": "mirror-0",
|
||||
"vdev_type": "mirror",
|
||||
"state": "ONLINE",
|
||||
"read_errors": "0",
|
||||
"write_errors": "0",
|
||||
"checksum_errors": "0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tank": {
|
||||
"name": "tank",
|
||||
"state": "DEGRADED",
|
||||
"scan": {
|
||||
"function": "scrub",
|
||||
"state": "FINISHED",
|
||||
"end_time": "Tue Mar 01 08:00:00 2026"
|
||||
},
|
||||
"error_count": "2",
|
||||
"vdevs": {
|
||||
"raidz2-0": {
|
||||
"name": "raidz2-0",
|
||||
"vdev_type": "raidz2",
|
||||
"state": "DEGRADED",
|
||||
"read_errors": "0",
|
||||
"write_errors": "0",
|
||||
"checksum_errors": "2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
62
agent/test/proxmox_agent/collectors/zfs_test.exs
Normal file
62
agent/test/proxmox_agent/collectors/zfs_test.exs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
defmodule ProxmoxAgent.Collectors.ZfsTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
alias ProxmoxAgent.Collectors.Zfs
|
||||
|
||||
@fixtures Path.expand("../../fixtures/zfs", __DIR__)
|
||||
|
||||
defp fake_runner do
|
||||
fn
|
||||
"zpool", ["list", "-j", "--json-int"] ->
|
||||
{:ok, File.read!(Path.join(@fixtures, "zpool_list.json"))}
|
||||
|
||||
"zpool", ["status", "-j", "--json-flat-vdevs", "--json-int"] ->
|
||||
{:ok, File.read!(Path.join(@fixtures, "zpool_status.json"))}
|
||||
|
||||
"zfs", ["list", "-j", "--json-int", "-t", "all"] ->
|
||||
{:ok, File.read!(Path.join(@fixtures, "zfs_list.json"))}
|
||||
end
|
||||
end
|
||||
|
||||
describe "collect_pools/1" do
|
||||
test "returns a summary per pool" do
|
||||
sample = Zfs.collect_pools(runner: fake_runner())
|
||||
assert is_list(sample.pools)
|
||||
assert length(sample.pools) == 2
|
||||
rpool = Enum.find(sample.pools, &(&1.name == "rpool"))
|
||||
tank = Enum.find(sample.pools, &(&1.name == "tank"))
|
||||
|
||||
assert rpool.health == "ONLINE"
|
||||
assert rpool.capacity_percent == 40
|
||||
assert rpool.fragmentation_percent == 17
|
||||
assert rpool.size_bytes == 500_000_000_000
|
||||
assert rpool.error_count == 0
|
||||
assert rpool.degraded_vdev_count == 0
|
||||
|
||||
assert tank.health == "DEGRADED"
|
||||
assert tank.error_count == 2
|
||||
assert tank.degraded_vdev_count == 1
|
||||
end
|
||||
|
||||
test "populates errors list when zpool fails" do
|
||||
failing = fn _, _ -> {:error, {:enoent, "zpool"}} end
|
||||
sample = Zfs.collect_pools(runner: failing)
|
||||
assert sample.pools == []
|
||||
assert length(sample.errors) >= 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "collect_datasets/1" do
|
||||
test "returns datasets and per-dataset snapshot summary" do
|
||||
sample = Zfs.collect_datasets(runner: fake_runner())
|
||||
assert length(sample.datasets) == 2
|
||||
|
||||
rpool_data = Enum.find(sample.datasets, &(&1.name == "rpool/data"))
|
||||
assert rpool_data.used_bytes == 100_000_000_000
|
||||
assert rpool_data.usedbysnapshots_bytes == 2_000_000_000
|
||||
assert rpool_data.snapshot_count == 2
|
||||
assert rpool_data.newest_snapshot_unix == 1_745_193_600
|
||||
assert rpool_data.oldest_snapshot_unix == 1_745_107_200
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue