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
}
2023-08-22 13:23:48 +02: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
2023-08-22 13:23:48 +02:00
} else {
2024-11-17 15:14:36 +01:00
count ++
2023-08-22 13:23:48 +02:00
}
}
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" )
2023-08-22 13:23:48 +02:00
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
}
2023-08-22 13:23:48 +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 )
2023-08-22 13:23:48 +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
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
}