backup/box.go

221 lines
5.8 KiB
Go
Raw Normal View History

2021-11-14 03:53:13 +01:00
package main
2021-11-14 05:21:22 +01:00
import (
2023-06-29 22:58:24 +02:00
"errors"
"regexp"
"sync"
"github.com/silenceper/pool"
log "github.com/sirupsen/logrus"
2021-11-14 05:21:22 +01:00
)
2021-11-14 03:53:13 +01:00
type Box struct {
2024-11-17 15:14:36 +01:00
name string
addr string
user string
key string
zfs *BoxZfs
sshPool pool.Pool
created bool
online bool
mx sync.Mutex
2021-11-14 03:53:13 +01:00
}
2024-11-17 15:14:36 +01:00
func (c *Config) NewBox(name, addr, user, key string) (b *Box, err error) {
2023-06-29 22:58:24 +02:00
log.WithFields(log.Fields{"name": name, "addr": addr, "user": user, "key": key}).Debugf("starting")
defer log.WithFields(log.Fields{"name": name, "addr": addr, "user": user, "key": key}).Debugf("done")
2021-11-14 11:09:58 +01:00
2023-06-29 22:58:24 +02:00
re := regexp.MustCompile(boxNamePattern)
if !re.MatchString(name) {
err := errors.New("invalid name")
log.WithFields(log.Fields{"name": b.name, "error": err}).Errorf("")
return nil, err
2022-04-16 14:57:45 +02:00
}
2023-06-29 22:58:24 +02:00
p, err := NewSshPool(name, addr, user, key)
2021-11-14 10:20:44 +01:00
if err != nil {
2023-06-29 22:58:24 +02:00
log.WithFields(log.Fields{"name": b.name, "call": "NewSshPool", "error": err}).Errorf("")
return nil, err
2021-11-14 11:09:58 +01:00
}
2023-06-29 22:58:24 +02:00
b = &Box{
name: name,
addr: addr,
user: user,
key: key,
zfs: &BoxZfs{
online: false,
},
2024-11-17 15:14:36 +01:00
sshPool: p,
online: false,
created: true,
2022-04-16 14:57:45 +02:00
}
2023-06-29 22:58:24 +02:00
b.zfs.box = b
2021-11-14 10:20:44 +01:00
2023-06-29 22:58:24 +02:00
return b, nil
2021-11-14 03:53:13 +01:00
}
2023-06-29 22:58:24 +02:00
func (b *Box) Open() error {
log.WithFields(log.Fields{"name": b.name}).Debugf("starting")
defer log.WithFields(log.Fields{"name": b.name}).Debugf("done")
2021-11-14 03:53:13 +01:00
2023-06-29 22:58:24 +02:00
b.mx.Lock()
defer b.mx.Unlock()
2021-11-14 11:09:58 +01:00
2023-06-29 22:58:24 +02:00
if b.online {
return nil
2022-04-16 14:57:45 +02:00
}
2023-06-29 22:58:24 +02:00
hostname, err := b.Exec("hostname")
2021-11-14 10:20:44 +01:00
if err != nil {
2023-06-29 22:58:24 +02:00
log.WithFields(log.Fields{"name": b.name, "call": "Exec", "attr": "hostname", "error": err}).Errorf("")
return err
2021-11-14 10:20:44 +01:00
}
2021-11-14 03:53:13 +01:00
2023-06-29 22:58:24 +02:00
log.WithFields(log.Fields{"name": b.name}).Debugf("hostname : %s", hostname)
2021-11-14 11:09:58 +01:00
2023-06-29 22:58:24 +02:00
b.online = true
2022-04-16 14:57:45 +02:00
2023-06-29 22:58:24 +02:00
if err := b.zfs.Open(); err != nil {
log.WithFields(log.Fields{"name": b.name, "call": "zfs.Open", "error": err}).Errorf("")
return err
2021-11-14 10:20:44 +01:00
}
2023-06-29 22:58:24 +02:00
return nil
2021-11-14 03:53:13 +01:00
}
2023-06-29 22:58:24 +02:00
func (b *Box) Close() error {
log.WithFields(log.Fields{"name": b.name}).Debugf("starting")
defer log.WithFields(log.Fields{"name": b.name}).Debugf("done")
2022-04-16 14:57:45 +02:00
2023-06-29 22:58:24 +02:00
b.mx.Lock()
defer b.mx.Unlock()
2022-04-16 14:57:45 +02:00
if !b.online {
2021-11-14 10:20:44 +01:00
return nil
}
2023-06-29 22:58:24 +02:00
if err := b.zfs.Close(); err != nil {
log.WithFields(log.Fields{"name": b.name, "call": "zfs.Close", "error": err}).Errorf("")
2021-11-14 10:20:44 +01:00
return err
}
2023-06-29 22:58:24 +02:00
b.online = false
2021-11-14 10:20:44 +01:00
return nil
2021-11-14 03:53:13 +01:00
}
2023-06-29 22:58:24 +02:00
func (b *Box) Exec(cmd string) (r string, err error) {
log.WithFields(log.Fields{"name": b.name, "cmd": cmd}).Debugf("starting")
defer log.WithFields(log.Fields{"name": b.name, "cmd": cmd}).Debugf("done")
2021-11-14 11:09:58 +01:00
2023-06-29 22:58:24 +02:00
if !b.created {
err := errors.New("box not initialized")
log.WithFields(log.Fields{"name": b.name, "error": err}).Errorf("")
return "", err
2022-04-16 14:57:45 +02:00
}
2023-06-29 22:58:24 +02:00
v, err := b.sshPool.Get()
2021-11-14 08:58:47 +01:00
if err != nil {
2023-06-29 22:58:24 +02:00
log.WithFields(log.Fields{"name": b.name, "error": err, "call": "SshPool.Get"}).Errorf("")
return "", err
2021-11-14 08:58:47 +01:00
}
2023-06-29 22:58:24 +02:00
defer b.sshPool.Put(v)
s := v.(*Ssh)
2021-11-14 08:58:47 +01:00
2023-06-29 22:58:24 +02:00
return s.Exec(cmd)
2021-11-14 03:53:13 +01:00
}
func TransferZfs(from Addr, to []Addr) (int, error) {
log.WithFields(log.Fields{"from": from, "to": to}).Debugf("starting")
defer log.WithFields(log.Fields{"from": from, "to": to}).Debugf("done")
count := 0
for _, dest := range to {
2024-11-17 15:14:36 +01:00
if err := TransferDirectZfs(from, dest); err != nil {
log.WithFields(log.Fields{"from": from, "to": to, "call": "TransferDirectZfs", "attr": dest, "error": err}).Errorf("")
return count, err
} else {
2024-11-17 15:14:36 +01:00
count++
}
}
return count, nil
}
func TransferDirectZfs(from, to Addr) error {
2023-06-29 22:58:24 +02:00
log.WithFields(log.Fields{"from": from, "to": to}).Debugf("starting")
defer log.WithFields(log.Fields{"from": from, "to": to}).Debugf("done")
2021-11-14 11:09:58 +01:00
2023-06-29 22:58:24 +02:00
var (
err error
fromSnapshots, toSnapshots []*ZfsSnapshot
)
2022-04-16 14:57:45 +02:00
2023-06-29 22:58:24 +02:00
if fromSnapshots, err = from.ValidSnapshots(); err != nil {
log.WithFields(log.Fields{"from": from, "to": to, "call": "ValidSnapshots", "attr": from, "error": err}).Errorf("")
return err
2022-04-16 14:57:45 +02:00
}
2021-11-14 08:58:47 +01:00
2023-06-29 22:58:24 +02:00
if len(fromSnapshots) == 0 {
2021-11-14 08:58:47 +01:00
return nil
}
2023-06-29 22:58:24 +02:00
if toSnapshots, err = to.ValidSnapshots(); err != nil {
log.WithFields(log.Fields{"from": from, "to": to, "call": "ValidSnapshots", "attr": to, "error": err}).Errorf("")
return err
2021-11-14 08:58:47 +01:00
}
2023-06-29 22:58:24 +02:00
if len(toSnapshots) == 0 {
log.WithFields(log.Fields{"from": from, "to": to}).Debugf("initiating destination")
if _, err := to.BoxExec("ssh " + from.Box() + " zfs send " + fromSnapshots[0].String() + " | zfs recv -F " + to.Path()); err != nil {
log.WithFields(log.Fields{"from": from, "to": to, "call": "BoxExec", "error": err}).Errorf("")
return err
2023-06-29 22:58:24 +02:00
}
newToSnapshot := &ZfsSnapshot{name: fromSnapshots[0].name, fs: cfg.box[to.Box()].zfs.filesystems[to.Path()]}
toSnapshots = append(toSnapshots, newToSnapshot)
cfg.box[to.Box()].zfs.filesystems[to.Path()].AddSnapshot(newToSnapshot)
2023-08-21 14:37:47 +02:00
2023-06-29 22:58:24 +02:00
}
2023-09-10 18:43:14 +02:00
fromFromSnapshotId := len(fromSnapshots) - 1
2023-09-10 18:38:53 +02:00
fromToSnapshotId := -1
for fromFromSnapshotId >= 0 {
2023-09-10 18:43:14 +02:00
fromToSnapshotId = len(toSnapshots) - 1
2023-09-10 18:38:53 +02:00
for fromToSnapshotId >= 0 {
if fromSnapshots[fromFromSnapshotId].name == toSnapshots[fromToSnapshotId].name {
break
}
2023-09-10 18:43:14 +02:00
fromToSnapshotId = fromToSnapshotId - 1
2023-09-10 18:38:53 +02:00
}
if fromToSnapshotId >= 0 {
2023-06-29 22:58:24 +02:00
break
2021-11-14 08:58:47 +01:00
}
2023-09-10 18:43:14 +02:00
fromFromSnapshotId = fromFromSnapshotId - 1
2021-11-14 08:58:47 +01:00
}
2023-06-29 22:58:24 +02:00
if fromFromSnapshotId == -1 {
err := errors.New("zfs snapshot unsync")
log.WithFields(log.Fields{"from": from, "to": to, "error": err}).Errorf("")
return err
2021-11-14 08:58:47 +01:00
}
2023-06-29 23:42:46 +02:00
if fromFromSnapshotId < len(fromSnapshots)-1 {
2023-06-29 22:58:24 +02:00
log.WithFields(log.Fields{"from": from, "to": to}).Debugf("transfering from %s to %s", fromSnapshots[fromFromSnapshotId].name, fromSnapshots[len(fromSnapshots)-1].name)
if _, err := to.BoxExec("ssh " + from.Box() + " zfs send -I " + fromSnapshots[fromFromSnapshotId].String() + " " + fromSnapshots[len(fromSnapshots)-1].String() + " | zfs recv -F " + to.Path()); err != nil {
log.WithFields(log.Fields{"from": from, "to": to, "call": "BoxExec", "error": err}).Errorf("")
return err
2023-06-29 22:58:24 +02:00
}
2021-11-14 03:53:13 +01:00
2023-06-30 00:07:07 +02:00
for _, v := range fromSnapshots[fromFromSnapshotId+1:] {
2023-06-29 22:58:24 +02:00
cfg.box[to.Box()].zfs.filesystems[to.Path()].AddSnapshot(&ZfsSnapshot{name: v.name, fs: cfg.box[to.Box()].zfs.filesystems[to.Path()]})
}
2022-04-16 14:57:45 +02:00
}
2023-06-29 22:58:24 +02:00
return nil
2022-09-07 16:21:03 +02:00
}