diff --git a/app.go b/app.go index aec1002..81e0d69 100644 --- a/app.go +++ b/app.go @@ -402,7 +402,7 @@ func (a AppConfig) ExecBefore(schedule string) error { for k, v := range a.Before { re := regexp.MustCompile(k) if re.MatchString(schedule) { - err := cfg.Box[v.Box()].SSHExec(v.Path()) + _, err := cfg.Box[v.Box()].SSHExec(v.Path()) if err != nil { if *debugFlag { log.Printf("AppConfig.ExecBefore : %s : Error executing %s", a.Name, string(v)) @@ -422,7 +422,7 @@ func (a AppConfig) ExecAfter(schedule string) error { for k, v := range a.After { re := regexp.MustCompile(k) if re.MatchString(schedule) { - err := cfg.Box[v.Box()].SSHExec(v.Path()) + _, err := cfg.Box[v.Box()].SSHExec(v.Path()) if err != nil { if *debugFlag { log.Printf("AppConfig.ExecAfter : %s : Error executing %s on %s", a.Name, v.Path(), v.Box()) @@ -505,7 +505,7 @@ func (a AppConfig) SendSnapshots() error { if *debugFlag { log.Printf("AppConfig.SendSnapshots : %s : Initializing snapshot on %s from %s", a.Name, dest.Box(), string(sFirstSnapshot)) } - err = cfg.Box[dest.Box()].SSHExec("ssh " + cfg.Box[src.Box()].User + "@" + src.Box() + " zfs send " + string(sFirstSnapshot) + " | zfs recv -F " + dest.Path() + "/" + src.Box() + "/" + src.Path()) + _, err = cfg.Box[dest.Box()].SSHExec("ssh " + cfg.Box[src.Box()].User + "@" + src.Box() + " zfs send " + string(sFirstSnapshot) + " | zfs recv -F " + dest.Path() + "/" + src.Box() + "/" + src.Path()) if err != nil { if *debugFlag { log.Printf("AppConfig.SendSnapshots : %s : Initializing snapshot on %s from %s failed (%s)", a.Name, dest.Box(), string(sFirstSnapshot), err) @@ -520,7 +520,7 @@ func (a AppConfig) SendSnapshots() error { if *debugFlag { log.Printf("AppConfig.SendSnapshots : %s : Sending incrementally %s to %s", a.Name, string(sNextSnapshot), dest.Box()) } - err = cfg.Box[dest.Box()].SSHExec("ssh " + cfg.Box[src.Box()].User + "@" + src.Box() + " zfs send -I " + string(sCurrSnapshot) + " " + string(sNextSnapshot) + " | zfs recv " + dest.Path() + "/" + src.Box() + "/" + src.Path()) + _, err = cfg.Box[dest.Box()].SSHExec("ssh " + cfg.Box[src.Box()].User + "@" + src.Box() + " zfs send -I " + string(sCurrSnapshot) + " " + string(sNextSnapshot) + " | zfs recv " + dest.Path() + "/" + src.Box() + "/" + src.Path()) if err != nil { if *debugFlag { log.Printf("AppConfig.SendSnapshots : %s : Sending snapshot on %s from %s failed (%s)", a.Name, dest.Box(), string(sNextSnapshot), err) @@ -544,7 +544,7 @@ func (a AppConfig) SendSnapshots() error { if *debugFlag { log.Printf("AppConfig.SendSnapshots : %s : Sending incrementally %s to %s", a.Name, string(sNextSnapshot), dest.Box()) } - err = cfg.Box[dest.Box()].SSHExec("ssh " + cfg.Box[src.Box()].User + "@" + src.Box() + " zfs send -I " + string(sCurrSnapshot) + " " + string(sNextSnapshot) + " | zfs recv " + dest.Path() + "/" + src.Box() + "/" + src.Path()) + _, err = cfg.Box[dest.Box()].SSHExec("ssh " + cfg.Box[src.Box()].User + "@" + src.Box() + " zfs send -I " + string(sCurrSnapshot) + " " + string(sNextSnapshot) + " | zfs recv " + dest.Path() + "/" + src.Box() + "/" + src.Path()) if err != nil { if *debugFlag { log.Printf("AppConfig.SendSnapshots : %s : Sending snapshot on %s from %s failed (%s)", a.Name, dest.Box(), string(sNextSnapshot), err) @@ -576,7 +576,7 @@ func (a AppConfig) CleanupSnapshot() error { if *debugFlag { log.Printf("AppConfig.CleanupSnapshot : %s : cleaning snapshots on %s for%s", a.Name, k, v) } - err := cfg.Box[k].SSHExec("zfsnap destroy -p hourly- -p daily- -p weekly- -p monthly- -p yearly-" + v) + _, err := cfg.Box[k].SSHExec("zfsnap destroy -p hourly- -p daily- -p weekly- -p monthly- -p yearly-" + v) if err != nil { if *debugFlag { log.Printf("AppConfig.CleanupSnapshot : %s : Error executing zfsnap on %s", a.Name, k) diff --git a/box.go b/box.go index 84f1551..bed73a8 100644 --- a/box.go +++ b/box.go @@ -1,8 +1,11 @@ package main import ( + "bytes" + "encoding/csv" "fmt" "log" + "strings" ) type Box struct { @@ -11,6 +14,7 @@ type Box struct { Key string `json:"key"` Name string `json:"-"` ssh *SSHConfig + zfs *ZFSConfig } func (b *Box) ZFSGetLastSnapshot(path string) (s Snapshot, err error) { @@ -38,19 +42,114 @@ func (b *Box) ZFSGetSnapshotList() []Snapshot { } func (b *Box) ZFSUpdateList() (err error) { - return b.ssh.getZFSList() + b.zfs.M.Lock() + if b.zfs.ZFSDeleted || b.zfs.ZFSAdded { + b.zfs.ZFSInitialized = false + } + b.zfs.M.Unlock() + + err = b.ZFSInitialize() + return } func (b *Box) ZFSIsZFS(path string) bool { - return b.ssh.isZFS(path) + err := b.ZFSInitialize() + if err != nil { + return false + } + + b.zfs.M.Lock() + defer b.zfs.M.Unlock() + + if _, ok := b.zfs.ZFSMap[path]; ok { + return true + } + return false + } func (b *Box) ZFSCreateZFS(path string) (err error) { - return b.ssh.createZFS(path) + err = b.ZFSInitialize() + if err != nil { + return + } + + b.zfs.M.Lock() + defer b.zfs.M.Unlock() + + p := strings.Split(path, `/`) + var base string + for _, d := range p { + if base == "" { + base = d + } else { + base = base + `/` + d + } + if _, ok := b.zfs.ZFSMap[base]; !ok { + + if *debugFlag { + log.Printf("Box.ZFSCreateZFS : Creating %s:%s", b.Name, base) + } + + _, err = b.SSHExec("zfs create -o mountpoint=none " + base) + if err != nil { + if *debugFlag { + log.Printf("Box.ZFSCreateZFS : %s : SSHExec : %s", b.Name, err) + } + return + } + + b.zfs.ZFSMap[base] = "none" + b.zfs.ZFSAdded = true + } + } + + return } -func (b *Box) SSHExec(cmd string) (err error) { - return b.ssh.exec(cmd) +func (b *Box) ZFSInitialize() (err error) { + b.zfs.M.Lock() + defer b.zfs.M.Unlock() + + if b.zfs.ZFSInitialized { + return nil + } + + if *debugFlag { + log.Printf("Box.ZFSInitialize : %s : Start", b.Name) + } + + b.zfs.ZFSMap = make(map[string]string) + + var buf *bytes.Buffer + buf, err = b.SSHExec("zfs list -H -o name,mountpoint") + + csvReader := csv.NewReader(buf) + csvReader.Comma = '\t' + csvReader.FieldsPerRecord = 2 + + csvData, err := csvReader.ReadAll() + if err != nil { + if *debugFlag { + log.Printf("Box.ZFSInitialize : %s : csvReader.ReadAll() : %s", b.Name, err) + } + return err + } + + for _, rec := range csvData { + b.zfs.ZFSMap[rec[0]] = rec[1] + } + + b.zfs.ZFSInitialized = true + b.zfs.ZFSAdded = false + b.zfs.ZFSDeleted = false + + return nil +} + +func (b *Box) SSHExec(cmd string) (buf *bytes.Buffer, err error) { + buf, err = b.ssh.exec(cmd) + return } func (b *Box) ZFSTakeSnapshot(schedule, path string) (err error) { @@ -59,5 +158,6 @@ func (b *Box) ZFSTakeSnapshot(schedule, path string) (err error) { } timestamp := cfg.Now.Format("2006-01-02_15.04.05") name := fmt.Sprintf("%s-%s--%s", schedule, timestamp, cfg.Zfsnap[schedule]) - return b.ssh.exec("zfs snapshot " + path + "@" + name) + _, err = b.ssh.exec("zfs snapshot " + path + "@" + name) + return } diff --git a/config.go b/config.go index 6011234..6a94009 100644 --- a/config.go +++ b/config.go @@ -53,6 +53,7 @@ func (c *Config) Load() error { for k, v := range c.Box { v.Name = k + v.zfs = NewZFSConfig() s := &SSHConfig{ logged: false, name: k, diff --git a/ssh.go b/ssh.go index f663f0a..4eb58e1 100644 --- a/ssh.go +++ b/ssh.go @@ -5,7 +5,6 @@ import ( "encoding/csv" "fmt" "log" - "strings" "golang.org/x/crypto/ssh" ) @@ -145,82 +144,7 @@ func (s *SSHConfig) getSnapshotList() error { return nil } -func (s *SSHConfig) getZFSList() error { - if *debugFlag { - log.Printf("SSHConfig.getZFSList : %s : Start", s.name) - } - if !s.logged { - return fmt.Errorf("Client %s not logged in.", s.name) - } - - session, err := s.client.NewSession() - if err != nil { - if *debugFlag { - log.Printf("SSHConfig.getZFSList : %s : client.NewSession() : %s", s.name, err) - } - return err - } - - var b bytes.Buffer - session.Stdout = &b - - err = session.Run("TZ=\"" + cfg.Timezone + "\" zfs list -H -o name,mountpoint") - if err != nil { - if *debugFlag { - log.Printf("SSHConfig.getZFSList : %s : session.Run() : %s", s.name, err) - } - return err - } - - s.zfs = make(map[string]string) - - csvReader := csv.NewReader(&b) - csvReader.Comma = '\t' - csvReader.FieldsPerRecord = 2 - - csvData, err := csvReader.ReadAll() - if err != nil { - if *debugFlag { - log.Printf("SSHConfig.getZFSList : %s : csvReader.ReadAll() : %s", s.name, err) - } - return err - } - - for _, rec := range csvData { - s.zfs[rec[0]] = rec[1] - } - - if *debugFlag { - log.Printf("SSHConfig.getZFSList : %s : read %d zfs file systems", s.name, len(s.zfs)) - } - - session.Close() - - return nil - -} - -func (s *SSHConfig) isZFS(path string) bool { - if *debugFlag { - log.Printf("SSHConfig.isZFS : Start %s:%s", s.name, path) - } - if len(s.zfs) == 0 { - err := s.getZFSList() - if err != nil { - if *debugFlag { - log.Printf("SSHConfig.isZFS : s.getZFSList(%s) : %s", s.name, err) - } - return false - } - } - _, ok := s.zfs[path] - if ok { - return true - } - return false -} - -func (s *SSHConfig) exec(cmd string) error { +func (s *SSHConfig) exec(cmd string) (b *bytes.Buffer, err error) { if *debugFlag { log.Printf("SSHConfig.exec : %s : Start %s", s.name, cmd) } @@ -230,61 +154,22 @@ func (s *SSHConfig) exec(cmd string) error { if *debugFlag { log.Printf("SSHConfig.exec : %s : client().NewSession(%s) : %s", s.name, cmd, err) } - return err + return } + var buf bytes.Buffer + b = &buf + session.Stdout = b + err = session.Run("TZ=\"" + cfg.Timezone + "\" " + cmd) if err != nil { if *debugFlag { log.Printf("SSHConfig.exec : session(%s).Run(%s) : %s", s.name, cmd, err) } - return err + return } session.Close() - return nil -} - -func (s *SSHConfig) createZFS(path string) error { - if *debugFlag { - log.Printf("SSHConfig.createZFS : Start %s:%s", s.name, path) - } - if len(s.zfs) == 0 { - err := s.getZFSList() - if err != nil { - if *debugFlag { - log.Printf("SSHConfig.createZFS : s.getZFSList(%s) : %s", s.name, err) - } - return err - } - } - p := strings.Split(path, `/`) - var base string - for _, d := range p { - if base == "" { - base = d - } else { - base = base + `/` + d - } - if _, ok := s.zfs[base]; !ok { - - if *debugFlag { - log.Printf("SSHConfig.createZFS : Creating %s:%s", s.name, base) - } - - err := s.exec("zfs create -o mountpoint=none " + base) - if err != nil { - if *debugFlag { - log.Printf("SSHConfig.createZFS : s.exec(%s) : %s", s.name, err) - } - return err - } - - s.zfs[base] = "none" - } - } - - return nil - + return } diff --git a/version.go b/version.go index 4cce4ac..d182aef 100644 --- a/version.go +++ b/version.go @@ -1,6 +1,6 @@ // Code generated by version.sh (@generated) DO NOT EDIT. package main -var githash = "119d069" -var buildstamp = "2021-11-14_04:20:23" -var commits = "18" -var version = "119d069-b18 - 2021-11-14_04:20:23" +var githash = "2704149" +var buildstamp = "2021-11-14_07:57:44" +var commits = "20" +var version = "2704149-b20 - 2021-11-14_07:57:44" diff --git a/zfs.go b/zfs.go index 06ab7d0..6f21a0e 100644 --- a/zfs.go +++ b/zfs.go @@ -1 +1,27 @@ package main + +import "sync" + +type ZFSConfig struct { + SnapshotAdded bool + SnapshotDeleted bool + SnapshotInitialized bool + SnapshotList []Snapshot + ZFSAdded bool + ZFSDeleted bool + ZFSInitialized bool + ZFSMap map[string]string + M sync.Mutex +} + +func NewZFSConfig() (z *ZFSConfig) { + z = &ZFSConfig{ + SnapshotAdded: false, + SnapshotDeleted: false, + SnapshotInitialized: false, + ZFSAdded: false, + ZFSDeleted: false, + ZFSInitialized: false, + } + return +}