2021-11-14 03:53:13 +01:00
|
|
|
package main
|
|
|
|
|
2021-11-14 05:21:22 +01:00
|
|
|
import (
|
2021-11-14 08:58:47 +01:00
|
|
|
"bytes"
|
|
|
|
"encoding/csv"
|
2021-11-14 05:21:22 +01:00
|
|
|
"fmt"
|
|
|
|
"log"
|
2021-11-14 08:58:47 +01:00
|
|
|
"strings"
|
2021-11-14 05:21:22 +01:00
|
|
|
)
|
2021-11-14 03:53:13 +01:00
|
|
|
|
|
|
|
type Box struct {
|
|
|
|
Addr string `json:"addr"`
|
|
|
|
User string `json:"user"`
|
|
|
|
Key string `json:"key"`
|
2021-11-14 05:21:22 +01:00
|
|
|
Name string `json:"-"`
|
2021-11-14 03:53:13 +01:00
|
|
|
ssh *SSHConfig
|
2021-11-14 08:58:47 +01:00
|
|
|
zfs *ZFSConfig
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|
|
|
|
|
2021-11-14 10:47:45 +01:00
|
|
|
func (b *Box) ZFSTakeSnapshot(schedule, path string) (err error) {
|
|
|
|
err = b.SnapshotInitialize()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
b.zfs.M.Lock()
|
|
|
|
defer b.zfs.M.Unlock()
|
|
|
|
|
|
|
|
if *debugFlag {
|
|
|
|
log.Printf("Box.ZFSTakeSnapshot : %s : taking snapshot on %s for %s", b.Name, path, schedule)
|
|
|
|
}
|
|
|
|
|
|
|
|
timestamp := cfg.Now.Format("2006-01-02_15.04.05")
|
|
|
|
name := fmt.Sprintf("%s-%s--%s", schedule, timestamp, cfg.Zfsnap[schedule])
|
|
|
|
_, err = b.ssh.exec("zfs snapshot " + path + "@" + name)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
b.zfs.SnapshotAdded = true
|
|
|
|
b.zfs.SnapshotList = append(b.zfs.SnapshotList, Snapshot(path+"@"+name))
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-11-14 10:20:44 +01:00
|
|
|
func (b *Box) ZFSGetLastSnapshot(path string) (last Snapshot, err error) {
|
|
|
|
err = b.SnapshotInitialize()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-11-14 10:47:45 +01:00
|
|
|
b.zfs.M.Lock()
|
|
|
|
defer b.zfs.M.Unlock()
|
|
|
|
|
2021-11-14 10:20:44 +01:00
|
|
|
if *debugFlag {
|
|
|
|
log.Printf("Box.ZFSGetLastSnapshot : %s : Start %s (%d snapshots)", b.Name, path, len(b.zfs.SnapshotList))
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, v := range b.zfs.SnapshotList {
|
|
|
|
if v.Path() == path {
|
|
|
|
last = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(string(last)) == 0 {
|
|
|
|
err = fmt.Errorf("no snapshot")
|
|
|
|
}
|
|
|
|
return
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|
|
|
|
|
2021-11-14 10:20:44 +01:00
|
|
|
func (b *Box) ZFSIsLastSnapshot(src Snapshot) (is bool, err error) {
|
|
|
|
err = b.SnapshotInitialize()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-11-14 10:47:45 +01:00
|
|
|
b.zfs.M.Lock()
|
|
|
|
defer b.zfs.M.Unlock()
|
|
|
|
|
2021-11-14 10:20:44 +01:00
|
|
|
if *debugFlag {
|
|
|
|
log.Printf("SSHConfig.isLastSnapshot : %s : Start %s", b.Name, string(src))
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = b.ZFSGetNextSnapshot(src)
|
|
|
|
if err != nil {
|
|
|
|
if err.Error() == "no snapshot" {
|
|
|
|
is = true
|
|
|
|
err = nil
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
is = false
|
|
|
|
}
|
|
|
|
return
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|
|
|
|
|
2021-11-14 10:20:44 +01:00
|
|
|
func (b *Box) ZFSGetFirstSnapshot(path string) (first Snapshot, err error) {
|
|
|
|
err = b.SnapshotInitialize()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-11-14 10:47:45 +01:00
|
|
|
b.zfs.M.Lock()
|
|
|
|
defer b.zfs.M.Unlock()
|
|
|
|
|
2021-11-14 10:20:44 +01:00
|
|
|
if *debugFlag {
|
|
|
|
log.Printf("SSHConfig.getFirstSnapshot : Start %s:%s", b.Name, path)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, v := range b.zfs.SnapshotList {
|
|
|
|
if v.Path() == path {
|
|
|
|
first = v
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err = fmt.Errorf("no snapshot")
|
|
|
|
return
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Box) ZFSGetNextSnapshot(src Snapshot) (next Snapshot, err error) {
|
2021-11-14 10:20:44 +01:00
|
|
|
err = b.SnapshotInitialize()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-11-14 10:47:45 +01:00
|
|
|
b.zfs.M.Lock()
|
|
|
|
defer b.zfs.M.Unlock()
|
|
|
|
|
2021-11-14 10:20:44 +01:00
|
|
|
if *debugFlag {
|
|
|
|
log.Printf("Box.ZFSGetNextSnapshot : Start %s:%s", b.Name, string(src))
|
|
|
|
}
|
|
|
|
|
|
|
|
for id, v := range b.zfs.SnapshotList {
|
|
|
|
if v == src {
|
|
|
|
if len(b.zfs.SnapshotList) > id+1 {
|
|
|
|
next = b.zfs.SnapshotList[id+1]
|
|
|
|
if next.Path() == src.Path() {
|
|
|
|
return
|
|
|
|
} else {
|
|
|
|
err = fmt.Errorf("no snapshot")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
err = fmt.Errorf("no snapshot")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err = fmt.Errorf("no snapshot")
|
|
|
|
return
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Box) ZFSUpdateSnapshotList() (err error) {
|
2021-11-14 10:20:44 +01:00
|
|
|
b.zfs.M.Lock()
|
|
|
|
if b.zfs.SnapshotDeleted || b.zfs.SnapshotAdded {
|
|
|
|
b.zfs.SnapshotInitialized = false
|
|
|
|
}
|
|
|
|
b.zfs.M.Unlock()
|
|
|
|
|
|
|
|
err = b.SnapshotInitialize()
|
|
|
|
return
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|
|
|
|
|
2021-11-14 10:20:44 +01:00
|
|
|
func (b *Box) ZFSGetSnapshotList() (snaps []Snapshot, err error) {
|
|
|
|
err = b.SnapshotInitialize()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
b.zfs.M.Lock()
|
|
|
|
defer b.zfs.M.Unlock()
|
|
|
|
|
|
|
|
snaps = b.zfs.SnapshotList
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Box) SnapshotInitialize() (err error) {
|
|
|
|
b.zfs.M.Lock()
|
|
|
|
defer b.zfs.M.Unlock()
|
|
|
|
|
|
|
|
if b.zfs.SnapshotInitialized {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if *debugFlag {
|
|
|
|
log.Printf("Box.SnapshotInitialize : %s : Start", b.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
b.zfs.SnapshotList = make([]Snapshot, 0)
|
|
|
|
|
|
|
|
var buf *bytes.Buffer
|
|
|
|
buf, err = b.SSHExec("zfs list -H -t snapshot -o name")
|
|
|
|
|
|
|
|
csvReader := csv.NewReader(buf)
|
|
|
|
csvReader.Comma = '\t'
|
|
|
|
csvReader.FieldsPerRecord = 1
|
|
|
|
|
|
|
|
csvData, err := csvReader.ReadAll()
|
|
|
|
if err != nil {
|
|
|
|
if *debugFlag {
|
|
|
|
log.Printf("Box.SnapshotInitialize : %s : csvReader.ReadAll() : %s", b.Name, err)
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, rec := range csvData {
|
|
|
|
b.zfs.SnapshotList = append(b.zfs.SnapshotList, Snapshot(rec[0]))
|
|
|
|
}
|
|
|
|
|
|
|
|
if *debugFlag {
|
|
|
|
log.Printf("Box.SnapshotInitialize : %s : read %d zfs snapshots", b.Name, len(b.zfs.SnapshotList))
|
|
|
|
}
|
|
|
|
|
|
|
|
b.zfs.SnapshotInitialized = true
|
|
|
|
b.zfs.SnapshotAdded = false
|
|
|
|
b.zfs.SnapshotDeleted = false
|
|
|
|
|
|
|
|
return nil
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Box) ZFSUpdateList() (err error) {
|
2021-11-14 08:58:47 +01:00
|
|
|
b.zfs.M.Lock()
|
|
|
|
if b.zfs.ZFSDeleted || b.zfs.ZFSAdded {
|
|
|
|
b.zfs.ZFSInitialized = false
|
|
|
|
}
|
|
|
|
b.zfs.M.Unlock()
|
|
|
|
|
|
|
|
err = b.ZFSInitialize()
|
|
|
|
return
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Box) ZFSIsZFS(path string) bool {
|
2021-11-14 08:58:47 +01:00
|
|
|
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
|
|
|
|
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Box) ZFSCreateZFS(path string) (err error) {
|
2021-11-14 08:58:47 +01:00
|
|
|
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) 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
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|
|
|
|
|
2021-11-14 08:58:47 +01:00
|
|
|
func (b *Box) SSHExec(cmd string) (buf *bytes.Buffer, err error) {
|
|
|
|
buf, err = b.ssh.exec(cmd)
|
|
|
|
return
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|