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-08-21 14:37:47 +02:00
name string
addr string
user string
key string
zfs * BoxZfs
sshPool pool . Pool
created bool
online bool
allowDirectConnect 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-08-21 14:37:47 +02:00
func ( c * Config ) NewBox ( name , addr , user , key string , direct bool ) ( 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 ,
} ,
2023-08-21 14:37:47 +02:00
sshPool : p ,
online : false ,
created : true ,
allowDirectConnect : true , //FIXME use direct
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
2023-08-21 14:37:47 +02:00
directTransfer bool
2023-06-29 22:58:24 +02:00
)
2022-04-16 14:57:45 +02:00
2023-08-21 14:37:47 +02:00
if cfg . box [ from . Box ( ) ] . allowDirectConnect && cfg . box [ to . Box ( ) ] . allowDirectConnect {
directTransfer = true
} else {
directTransfer = false
}
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-21 14:37:47 +02:00
if directTransfer {
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 )
} else {
//handle indirect transfer
2023-06-29 22:58:24 +02:00
}
2023-08-21 14:37:47 +02:00
2023-06-29 22:58:24 +02:00
}
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 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-21 14:37:47 +02:00
if directTransfer {
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
}
} else {
// handle indirect transfer
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
}