backup/box.go

206 lines
5.5 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"
"golang.org/x/crypto/ssh"
2021-11-14 05:21:22 +01:00
)
2021-11-14 03:53:13 +01:00
type Box struct {
2023-06-29 22:58:24 +02: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
}
2023-06-29 22:58:24 +02:00
type BoxSshPool struct {
signer ssh.Signer
config *ssh.ClientConfig
client *ssh.Client
logged bool
mx sync.Mutex
2021-11-14 10:47:45 +01:00
}
2023-06-29 22:58:24 +02:00
func (c *Config) NewBox(name, addr, user, key string) (b *Box, err error) {
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,
},
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
}
2023-06-29 22:58:24 +02:00
func TransferZfs(from, to Addr) error {
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
}
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)
}
fromFromSnapshotId := -1
lastToSnapshot := toSnapshots[len(toSnapshots)-1]
log.WithFields(log.Fields{"from": from, "to": to}).Debugf("searching last snapshot %s", lastToSnapshot.String())
for id, v := range fromSnapshots {
if v.name == lastToSnapshot.name {
fromFromSnapshotId = id
log.WithFields(log.Fields{"from": from, "to": to}).Debugf("found %s", v.String())
break
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 22:58:24 +02:00
if fromSnapshots[fromFromSnapshotId].name != lastToSnapshot.name {
log.WithFields(log.Fields{"from": from, "to": to}).Debugf("transfering from %s to %s", fromSnapshots[fromFromSnapshotId].name, fromSnapshots[len(fromSnapshots)-1].name)
2021-11-14 08:58:47 +01:00
2023-06-29 22:58:24 +02:00
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
}
2021-11-14 03:53:13 +01:00
2023-06-29 22:58:24 +02:00
for _, v := range fromSnapshots[fromFromSnapshotId:] {
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
}