ZFS: dedup toggle per pool and dedup option for new datasets
build / build (push) Successful in 1m4s

Pool cards show the dedup ratio and can set dedup=on|off on the pool root
dataset (inherited by children); the new-dataset form gets a dedup option.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 09:30:38 +03:00
co-authored by Claude Opus 5.5
parent b965d2c9db
commit 0cdee29359
9 changed files with 209 additions and 15 deletions
+11 -2
View File
@@ -35,6 +35,8 @@ type Pool struct {
Status string `json:"status,omitempty"`
Action string `json:"action,omitempty"`
Vdevs []Vdev `json:"vdevs"`
// DedupRatio is the pool-wide dedup ratio (1.00 = nothing deduplicated).
DedupRatio float64 `json:"dedup_ratio"`
}
type Dataset struct {
@@ -43,11 +45,12 @@ type Dataset struct {
Avail uint64 `json:"avail"`
Refer uint64 `json:"refer"`
Mountpoint string `json:"mountpoint"`
Dedup string `json:"dedup"` // dedup property: off, on, verify, sha256, ...
}
// ReadPools combines zpool list and zpool status for every pool.
func ReadPools() ([]Pool, error) {
out, err := run(20*time.Second, "zpool", "list", "-Hp", "-o", "name,size,alloc,free,cap,health,frag")
out, err := run(20*time.Second, "zpool", "list", "-Hp", "-o", "name,size,alloc,free,cap,health,frag,dedup")
if err != nil {
return nil, err
}
@@ -63,7 +66,7 @@ func ReadPools() ([]Pool, error) {
}
func ReadDatasets() ([]Dataset, error) {
out, err := run(20*time.Second, "zfs", "list", "-Hp", "-t", "filesystem", "-o", "name,used,avail,refer,mountpoint")
out, err := run(20*time.Second, "zfs", "list", "-Hp", "-t", "filesystem", "-o", "name,used,avail,refer,mountpoint,dedup")
if err != nil {
return nil, err
}
@@ -84,6 +87,9 @@ func parseZpoolList(b []byte) []Pool {
p.Free, _ = strconv.ParseUint(f[3], 10, 64)
p.CapPct = atoiPct(f[4])
p.FragPct = atoiPct(f[6])
if len(f) > 7 {
p.DedupRatio, _ = strconv.ParseFloat(strings.TrimSuffix(strings.TrimSpace(f[7]), "x"), 64)
}
if p.CapPct == 0 && p.Size > 0 {
p.CapPct = int(p.Alloc * 100 / p.Size)
}
@@ -220,6 +226,9 @@ func parseZfsList(b []byte) []Dataset {
d.Used, _ = strconv.ParseUint(f[1], 10, 64)
d.Avail, _ = strconv.ParseUint(f[2], 10, 64)
d.Refer, _ = strconv.ParseUint(f[3], 10, 64)
if len(f) > 5 {
d.Dedup = f[5]
}
out = append(out, d)
}
return out
+13
View File
@@ -119,6 +119,15 @@ func TestParseZpoolList(t *testing.T) {
if pools[1].CapPct != 90 || pools[1].FragPct != 0 {
t.Errorf("pool1 = %+v", pools[1])
}
if pools[0].DedupRatio != 0 {
t.Errorf("dedup ratio without the column = %v", pools[0].DedupRatio)
}
// with the dedup column; -p prints "1.35", without -p it is "1.35x"
pools = parseZpoolList([]byte("a\t1\t1\t0\t100\tONLINE\t0\t1.35\nb\t1\t1\t0\t100\tONLINE\t0\t2.00x\n"))
if len(pools) != 2 || pools[0].DedupRatio != 1.35 || pools[1].DedupRatio != 2 {
t.Errorf("dedup ratio = %+v", pools)
}
}
func TestParseZfsList(t *testing.T) {
@@ -127,4 +136,8 @@ func TestParseZfsList(t *testing.T) {
if len(ds) != 2 || ds[1].Name != "nas_pool_1_8_gb/appdata" || ds[1].Avail != 200 {
t.Errorf("datasets = %+v", ds)
}
ds = parseZfsList([]byte("tank\t1\t2\t3\t/tank\ton\n"))
if len(ds) != 1 || ds[0].Dedup != "on" || ds[0].Mountpoint != "/tank" {
t.Errorf("dedup column: %+v", ds)
}
}