ZFS: dedup toggle per pool and dedup option for new datasets
build / build (push) Successful in 1m4s
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:
@@ -124,7 +124,9 @@ type zfsVM struct {
|
||||
S sched.Status
|
||||
Th config.Thresholds
|
||||
Datasets []datasetRow
|
||||
Form createForm // last submitted values (re-rendered after an error)
|
||||
// PoolDedup maps a pool to the dedup property of its root dataset.
|
||||
PoolDedup map[string]string
|
||||
Form createForm // last submitted values (re-rendered after an error)
|
||||
}
|
||||
|
||||
type datasetRow struct {
|
||||
@@ -134,15 +136,18 @@ type datasetRow struct {
|
||||
}
|
||||
|
||||
type createForm struct {
|
||||
Pool, Name, Owner, Compression, Quota string
|
||||
Pool, Name, Owner, Compression, Dedup, Quota string
|
||||
}
|
||||
|
||||
func (s *Server) zfsView(st sched.Status, form createForm) zfsVM {
|
||||
vm := zfsVM{S: st, Th: s.cfg.Get().Thresholds, Form: form}
|
||||
vm := zfsVM{S: st, Th: s.cfg.Get().Thresholds, Form: form, PoolDedup: map[string]string{}}
|
||||
if vm.Form.Owner == "" {
|
||||
vm.Form.Owner = defaultOwner
|
||||
}
|
||||
for _, d := range st.Datasets {
|
||||
if !strings.Contains(d.Name, "/") {
|
||||
vm.PoolDedup[d.Name] = d.Dedup
|
||||
}
|
||||
row := datasetRow{Dataset: d, DomID: strings.NewReplacer("/", "-", ".", "_", ":", "_").Replace(d.Name)}
|
||||
row.CanDestroy = strings.Contains(d.Name, "/") && d.Refer < zfsadm.EmptyRefer
|
||||
for _, o := range st.Datasets {
|
||||
@@ -170,6 +175,7 @@ func (s *Server) zfsCreate(w http.ResponseWriter, r *http.Request) {
|
||||
Name: strings.Trim(strings.TrimSpace(r.FormValue("name")), "/"),
|
||||
Owner: strings.TrimSpace(r.FormValue("owner")),
|
||||
Compression: r.FormValue("compression"),
|
||||
Dedup: r.FormValue("dedup"),
|
||||
Quota: strings.ToUpper(strings.TrimSpace(r.FormValue("quota"))),
|
||||
}
|
||||
st := s.sch.Status()
|
||||
@@ -191,20 +197,20 @@ func (s *Server) zfsCreate(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, err := range []error{zfsadm.ValidateOwner(f.Owner), zfsadm.ValidateCompression(f.Compression), zfsadm.ValidateSize(f.Quota)} {
|
||||
for _, err := range []error{zfsadm.ValidateOwner(f.Owner), zfsadm.ValidateCompression(f.Compression), zfsadm.ValidateDedup(f.Dedup), zfsadm.ValidateSize(f.Quota)} {
|
||||
if err != nil {
|
||||
fail(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
err := s.admin.Create(full, zfsadm.CreateOpts{Compression: f.Compression, Quota: f.Quota, Owner: f.Owner})
|
||||
err := s.admin.Create(full, zfsadm.CreateOpts{Compression: f.Compression, Dedup: f.Dedup, Quota: f.Quota, Owner: f.Owner})
|
||||
s.sch.RefreshStorage()
|
||||
if err != nil {
|
||||
log.Printf("zfs create %s: %v", full, err)
|
||||
fail(err.Error())
|
||||
return
|
||||
}
|
||||
log.Printf("dataset %s created (owner %s, compression %q, quota %q)", full, f.Owner, f.Compression, f.Quota)
|
||||
log.Printf("dataset %s created (owner %s, compression %q, dedup %q, quota %q)", full, f.Owner, f.Compression, f.Dedup, f.Quota)
|
||||
http.Redirect(w, r, "/zfs?msg="+url.QueryEscape("Dataset "+full+" created")+"#"+url.PathEscape(f.Pool), http.StatusSeeOther)
|
||||
}
|
||||
|
||||
@@ -243,6 +249,37 @@ func (s *Server) zfsDestroy(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/zfs?msg="+url.QueryEscape("Dataset "+name+" destroyed"), http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// zfsDedup turns dedup on/off on a pool's root dataset (inherited by children).
|
||||
func (s *Server) zfsDedup(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
pool := strings.TrimSpace(r.FormValue("pool"))
|
||||
value := r.FormValue("dedup")
|
||||
st := s.sch.Status()
|
||||
fail := func(msg string) {
|
||||
s.render(w, r, "zfs", pageData{Title: "ZFS", Data: s.zfsView(st, createForm{}), Alerts: len(st.Alerts), Error: "Dedup not changed: " + msg})
|
||||
}
|
||||
if !hasPool(st, pool) {
|
||||
fail("unknown pool " + pool)
|
||||
return
|
||||
}
|
||||
if value != "on" && value != "off" {
|
||||
fail("dedup must be on or off")
|
||||
return
|
||||
}
|
||||
err := s.admin.SetDedup(pool, value)
|
||||
s.sch.RefreshStorage()
|
||||
if err != nil {
|
||||
log.Printf("zfs set dedup=%s %s: %v", value, pool, err)
|
||||
fail(err.Error())
|
||||
return
|
||||
}
|
||||
log.Printf("dedup=%s set on pool %s", value, pool)
|
||||
http.Redirect(w, r, "/zfs?msg="+url.QueryEscape("Dedup "+value+" for "+pool)+"#"+url.PathEscape(pool), http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func hasPool(st sched.Status, name string) bool {
|
||||
for _, p := range st.Pools {
|
||||
if p.Name == name {
|
||||
|
||||
Reference in New Issue
Block a user