package main import ( "bytes" "encoding/csv" "fmt" "log" "strings" "time" "golang.org/x/crypto/ssh" ) type SSHConfig struct { signer ssh.Signer config *ssh.ClientConfig client *ssh.Client logged bool name string zfs map[string]string snapshot []Snapshot now time.Time } func (s *SSHConfig) getLastSnapshot(path string) (Snapshot, error) { if *debugFlag { log.Printf("SSHConfig.getLastSnapshot : Start %s:%s (%d snapshots)", s.name, path, len(s.snapshot)) } var last Snapshot for _, v := range s.snapshot { if v.Path() == path { last = v } else { if len(string(last)) > 0 { return last, nil } } } if len(string(last)) > 0 { return last, nil } return last, fmt.Errorf("no snapshot") } func (s *SSHConfig) isLastSnapshot(snapshot Snapshot) bool { if *debugFlag { log.Printf("SSHConfig.isLastSnapshot : Start %s:%s", s.name, string(snapshot)) } _, err := s.getNextSnapshot(snapshot) if err != nil { return true } else { return false } } func (s *SSHConfig) getFirstSnapshot(path string) (Snapshot, error) { if *debugFlag { log.Printf("SSHConfig.getFirstSnapshot : Start %s:%s", s.name, path) } var first Snapshot for _, v := range s.snapshot { if v.Path() == path { first = v if *debugFlag { log.Printf("SSHConfig.getFirstSnapshot : Return %s", string(first)) } return first, nil } } return first, fmt.Errorf("no snapshot") } func (s *SSHConfig) getNextSnapshot(snapshot Snapshot) (Snapshot, error) { if *debugFlag { log.Printf("SSHConfig.getNextSnapshot : Start %s:%s", s.name, string(snapshot)) } var next Snapshot for id, v := range s.snapshot { if v == snapshot { if len(s.snapshot) > id+1 { next = s.snapshot[id+1] if next.Path() == snapshot.Path() { return next, nil } else { return next, fmt.Errorf("no snapshot") } } else { return next, fmt.Errorf("no snapshot") } } } return next, fmt.Errorf("no snapshot") } func (s *SSHConfig) getSnapshotList() error { if *debugFlag { log.Printf("SSHConfig.getSnapshotList : %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.getSnapshotList : %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 -t snapshot -o name") if err != nil { if *debugFlag { log.Printf("SSHConfig.getSnapshotList : %s : session.Run() : %s", s.name, err) } return err } s.snapshot = make([]Snapshot, 0) csvReader := csv.NewReader(&b) csvReader.Comma = '\t' csvReader.FieldsPerRecord = 1 csvData, err := csvReader.ReadAll() if err != nil { if *debugFlag { log.Printf("SSHConfig.getSnapshotList : %s : csvReader.ReadAll() : %s", s.name, err) } return err } for _, rec := range csvData { s.snapshot = append(s.snapshot, Snapshot(rec[0])) } if *debugFlag { log.Printf("SSHConfig.getSnapshotList : %s : read %d zfs snapshots", s.name, len(s.snapshot)) } session.Close() 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) getTime() error { if *debugFlag { log.Printf("SSHConfig.getTime : %s : Start", s.name) } session, err := s.client.NewSession() if err != nil { if *debugFlag { log.Printf("SSHConfig.getTime : %s : client.NewSession() : %s", s.name, err) } return err } var b bytes.Buffer session.Stdout = &b err = session.Run("TZ=\"" + cfg.Timezone + "\" date +\"%F %T\"") if err != nil { if *debugFlag { log.Printf("SSHConfig.getTime : %s : session.Run() : %s", s.name, err) } return err } s.now, err = time.Parse("2006-01-02 15:04:05\n", b.String()) if err != nil { if *debugFlag { log.Printf("SSHConfig.getTime : %s : time.Parse() : %s", s.name, err) } return err } if *debugFlag { log.Printf("SSHConfig.getTime : %s : now is %s", s.name, s.now.String()) } session.Close() return nil } func (s *SSHConfig) exec(cmd string) error { if *debugFlag { log.Printf("SSHConfig.exec : %s : Start %s", s.name, cmd) } session, err := s.client.NewSession() if err != nil { if *debugFlag { log.Printf("SSHConfig.exec : %s : client().NewSession(%s) : %s", s.name, cmd, err) } return err } 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 } 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 }