WIP migration on running jail with minimized downtime

This commit is contained in:
yo 2022-06-05 18:43:02 +02:00
parent 542d2f96f6
commit 0bf825ee5a

View File

@ -3,10 +3,10 @@ package cmd
import ( import (
"os" "os"
"fmt" "fmt"
"strings"
"bufio" "bufio"
/* "errors" "errors"
"regexp" "strings"
/* "regexp"
"time" "time"
*/ */
) )
@ -45,7 +45,7 @@ func MigrateJail(args []string) {
if cj.Running == true { if cj.Running == true {
fmt.Printf("WARNING: Jail %s is running\n", cj.Name) fmt.Printf("WARNING: Jail %s is running\n", cj.Name)
fmt.Printf("Migration will stop it for data sync before starting on the new pool. You will be prompted for shutdown\n") fmt.Printf("Migration will stop it for data sync before starting on the new pool. You will be prompted for shutdown.\n")
fmt.Printf("Continue? (y/n) ") fmt.Printf("Continue? (y/n) ")
scanr := bufio.NewScanner(os.Stdin) scanr := bufio.NewScanner(os.Stdin)
scanr.Scan() scanr.Scan()
@ -59,11 +59,11 @@ func MigrateJail(args []string) {
zfs snapshot /iocage/jails/$jail@gocage_mig_first_snap zfs snapshot /iocage/jails/$jail@gocage_mig_first_snap
zfs snapshot /iocage/jails/$jail/root@gocage_mig_first_snap zfs snapshot /iocage/jails/$jail/root@gocage_mig_first_snap
zfs send jail@gocage_mig_first_snap | zfs receive destpool/jails/jail_name zfs send jail@gocage_mig_first_snap | zfs receive destpool/jails/jail_name
zfs send jail/root@@gocage_mig_first_snap | zfs receive destpool/jails/jail_name/root zfs send jail/root@gocage_mig_first_snap | zfs receive destpool/jails/jail_name/root
shutdown jail if needed shutdown jail if needed
if jail was shutdown if jail was shutdown
zfs snapshot /iocage/jails/$jail@gocage_mig_last_snap to get last data zfs snapshot /iocage/jails/$jail@gocage_mig_last_snap to get last data
zfs send jail/root@gocage_mig_last_snap | zfs receive -F destpool/jails/jail_name zfs send -i jail@gocage_mig_first_snap jail/root@gocage_mig_last_snap | zfs receive -F destpool/jails/jail_name
start jail on new dest start jail on new dest
zfs destroy destpool/jails/jail_name@gocage_mig_first_snap zfs destroy destpool/jails/jail_name@gocage_mig_first_snap
zfs destroy destpool/jails/jail_name/root@gocage_mig_first_snap zfs destroy destpool/jails/jail_name/root@gocage_mig_first_snap
@ -100,11 +100,64 @@ func MigrateJail(args []string) {
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
} }
fmt.Printf("Done\n") fmt.Printf("Done\n")
// Running jail needs a last snapshot for an incremental send/recv after shutting down.
if cj.Running == true {
fmt.Printf("Shutdown jail %s for last data sync, this could take some time.\n", cj.Name)
fmt.Printf("Continue? (y/n) ")
scanr := bufio.NewScanner(os.Stdin)
scanr.Scan()
if false == strings.EqualFold(scanr.Text(), "y") {
fmt.Printf("Migration aborted. Now cleaning destination pool.\n")
if err := CleanMigrateMess([]string{cj.Name}); err != nil {
fmt.Printf("Error: %v\n", err)
}
// TODO : Remove destination datasets, or handle the cas at the beginning of current function
// (when snapshot already exist on source and dest)
return
}
StopJail([]string{cj.Name})
fmt.Printf("Snapshot %s: ", dsconf)
if err = zfsSnapshot(dsconf, "gocage_mig_last_sync"); err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Done\n")
fmt.Printf("Snapshot %s: ", dsdata)
if err := zfsSnapshot(dsdata, "gocage_mig_last_sync"); err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Done\n")
fmt.Printf("Synchronize jail config to %s: ", dsconfdest)
if err := zfsCopyIncremental(fmt.Sprintf("%s@gocage_mig_init", dsconf),
fmt.Sprintf("%s@gocage_mig_last_sync", dsconf),
dsconfdest); err != nil {
fmt.Printf("Error: %v\n", err)
}
fmt.Printf("Done\n")
fmt.Printf("Synchronize jail filesystem dataset to %s: ", dsdatadest)
if err := zfsCopyIncremental(fmt.Sprintf("%s@gocage_mig_init", dsdata),
fmt.Sprintf("%s@gocage_mig_last_sync", dsdata),
dsdatadest); err != nil {
fmt.Printf("Error: %v\n", err)
}
fmt.Printf("Done\n")
// TODO : Start jail on new datastore (! Currently ListJails won't support the double with same name !)
// TODO : zfs destroy destpool/jails/jail_name@gocage_mig_first_snap
// TODO : zfs destroy destpool/jails/jail_name/root@gocage_mig_first_snap
// TODO : zfs destroy -r srcpool/iocage/jails/$jail
}
} }
} }
// Clean snapshots from an aborted migration // Clean snapshots from an aborted migration
func CleanMigrateMess(args []string) { func CleanMigrateMess(args []string) error {
var jailNames []string var jailNames []string
if len(args) > 0 { if len(args) > 0 {
@ -117,7 +170,7 @@ func CleanMigrateMess(args []string) {
cj, err := getJailFromArray(jn, gJails) cj, err := getJailFromArray(jn, gJails)
if cj == nil { if cj == nil {
fmt.Printf("Error getting jail %s: Not found\n", jn) fmt.Printf("Error getting jail %s: Not found\n", jn)
return return errors.New(fmt.Sprintf("Error getting jail %s: Not found\n", jn))
} }
cmd := fmt.Sprintf("zfs destroy %s@gocage_mig_init", strings.Join([]string{cj.Zpool, "iocage", "jails", jn}, "/")) cmd := fmt.Sprintf("zfs destroy %s@gocage_mig_init", strings.Join([]string{cj.Zpool, "iocage", "jails", jn}, "/"))
@ -125,7 +178,7 @@ func CleanMigrateMess(args []string) {
if err != nil { if err != nil {
if false == strings.HasSuffix(out, "could not find any snapshots to destroy; check snapshot names.\n") { if false == strings.HasSuffix(out, "could not find any snapshots to destroy; check snapshot names.\n") {
fmt.Printf("Error executing command %s: %v; command returned: %s\n", cmd, err, out) fmt.Printf("Error executing command %s: %v; command returned: %s\n", cmd, err, out)
return return errors.New(fmt.Sprintf("Error executing command %s: %v; command returned: %s\n", cmd, err, out))
} }
} }
cmd = fmt.Sprintf("zfs destroy %s@gocage_mig_init", strings.Join([]string{cj.Zpool, "iocage", "jails", jn, "root"}, "/")) cmd = fmt.Sprintf("zfs destroy %s@gocage_mig_init", strings.Join([]string{cj.Zpool, "iocage", "jails", jn, "root"}, "/"))
@ -133,8 +186,26 @@ func CleanMigrateMess(args []string) {
if err != nil { if err != nil {
if false == strings.HasSuffix(out, "could not find any snapshots to destroy; check snapshot names.\n") { if false == strings.HasSuffix(out, "could not find any snapshots to destroy; check snapshot names.\n") {
fmt.Printf("Error executing command %s: %v; command returned: %s\n", cmd, err, out) fmt.Printf("Error executing command %s: %v; command returned: %s\n", cmd, err, out)
return return errors.New(fmt.Sprintf("Error executing command %s: %v; command returned: %s\n", cmd, err, out))
}
}
cmd = fmt.Sprintf("zfs destroy %s@gocage_mig_last_sync", strings.Join([]string{cj.Zpool, "iocage", "jails", jn}, "/"))
out, err = executeCommand(cmd)
if err != nil {
if false == strings.HasSuffix(out, "could not find any snapshots to destroy; check snapshot names.\n") {
fmt.Printf("Error executing command %s: %v; command returned: %s\n", cmd, err, out)
return errors.New(fmt.Sprintf("Error executing command %s: %v; command returned: %s\n", cmd, err, out))
}
}
cmd = fmt.Sprintf("zfs destroy %s@gocage_mig_last_sync", strings.Join([]string{cj.Zpool, "iocage", "jails", jn, "root"}, "/"))
out, err = executeCommand(cmd)
if err != nil {
if false == strings.HasSuffix(out, "could not find any snapshots to destroy; check snapshot names.\n") {
fmt.Printf("Error executing command %s: %v; command returned: %s\n", cmd, err, out)
return errors.New(fmt.Sprintf("Error executing command %s: %v; command returned: %s\n", cmd, err, out))
} }
} }
} }
return nil
} }