diff --git a/cmd/migrate.go b/cmd/migrate.go index 86dd341..5aeb3ef 100644 --- a/cmd/migrate.go +++ b/cmd/migrate.go @@ -3,10 +3,10 @@ package cmd import ( "os" "fmt" - "strings" "bufio" -/* "errors" - "regexp" + "errors" + "strings" +/* "regexp" "time" */ ) @@ -45,7 +45,7 @@ func MigrateJail(args []string) { if cj.Running == true { 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) ") scanr := bufio.NewScanner(os.Stdin) scanr.Scan() @@ -59,11 +59,11 @@ func MigrateJail(args []string) { zfs snapshot /iocage/jails/$jail@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/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 if jail was shutdown 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 zfs destroy destpool/jails/jail_name@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("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 -func CleanMigrateMess(args []string) { +func CleanMigrateMess(args []string) error { var jailNames []string if len(args) > 0 { @@ -117,7 +170,7 @@ func CleanMigrateMess(args []string) { cj, err := getJailFromArray(jn, gJails) if cj == nil { 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}, "/")) @@ -125,7 +178,7 @@ func CleanMigrateMess(args []string) { 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 + 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"}, "/")) @@ -133,8 +186,26 @@ func CleanMigrateMess(args []string) { 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 + 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 }