From 5b22f2fd3e7c3b7f2fcdf2e7af14c21237e00fd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 21 Nov 2023 23:52:09 -0500 Subject: [PATCH 1/7] lxd-to-incus: Allow evacuated servers when using CLUSTER_NO_STOP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- cmd/lxd-to-incus/validate.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmd/lxd-to-incus/validate.go b/cmd/lxd-to-incus/validate.go index 48c35f05897..744df6cef41 100644 --- a/cmd/lxd-to-incus/validate.go +++ b/cmd/lxd-to-incus/validate.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "os" "os/exec" lxdAPI "github.com/canonical/lxd/shared/api" @@ -345,6 +346,10 @@ func (c *cmdMigrate) validate(source Source, target Target) error { for _, member := range clusterMembers { if member.Status != "Online" { + if os.Getenv("CLUSTER_NO_STOP") == "1" && member.Status == "Evacuated" { + continue + } + errors = append(errors, fmt.Errorf("Cluster member %q isn't in the online state", member.ServerName)) } } From 415446801c1cc6f49acc5bc8b2605a73eee279b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Tue, 21 Nov 2023 23:58:48 -0500 Subject: [PATCH 2/7] lxd-to-incus: Fix ceph username MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- cmd/lxd-to-incus/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lxd-to-incus/main.go b/cmd/lxd-to-incus/main.go index 92b67a7ffc4..e193745256c 100644 --- a/cmd/lxd-to-incus/main.go +++ b/cmd/lxd-to-incus/main.go @@ -257,7 +257,7 @@ func (c *cmdMigrate) Run(app *cobra.Command, args []string) error { rbdPool = pool.Name } - renameCmd := []string{"rbd", "rename", "--cluster", cluster, "--name", client, fmt.Sprintf("%s/lxd_%s", rbdPool, rbdPool), fmt.Sprintf("%s/incus_%s", rbdPool, rbdPool)} + renameCmd := []string{"rbd", "rename", "--cluster", cluster, "--name", fmt.Sprintf("client.%s", client), fmt.Sprintf("%s/lxd_%s", rbdPool, rbdPool), fmt.Sprintf("%s/incus_%s", rbdPool, rbdPool)} if !util.ValueInSlice(pool.Name, rbdRenamed) { rewriteCommands = append(rewriteCommands, renameCmd) rbdRenamed = append(rbdRenamed, pool.Name) From e58cba5d12964cd04a7b104775381ce42fd9e1dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 22 Nov 2023 00:01:29 -0500 Subject: [PATCH 3/7] lxd-to-incus: Add missing line breaks in log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- cmd/lxd-to-incus/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/lxd-to-incus/main.go b/cmd/lxd-to-incus/main.go index e193745256c..8fe2d252f56 100644 --- a/cmd/lxd-to-incus/main.go +++ b/cmd/lxd-to-incus/main.go @@ -166,7 +166,7 @@ func (c *cmdMigrate) Run(app *cobra.Command, args []string) error { } if clustered { - _, _ = logFile.WriteString("Source server is a cluster") + _, _ = logFile.WriteString("Source server is a cluster\n") } fmt.Println("=> Connecting to the target server") @@ -432,7 +432,7 @@ Instead this tool will be providing specific commands for each of the servers. // Unmount potential mount points. for _, mount := range []string{"guestapi", "shmounts"} { - _, _ = logFile.WriteString(fmt.Sprintf("Unmounting %q", filepath.Join(targetPaths.Daemon, mount))) + _, _ = logFile.WriteString(fmt.Sprintf("Unmounting %q\n", filepath.Join(targetPaths.Daemon, mount))) _ = unix.Unmount(filepath.Join(targetPaths.Daemon, mount), unix.MNT_DETACH) } From 54a02d26d55567c31a2614eb7ed6bc2c04c5b8df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 22 Nov 2023 00:15:33 -0500 Subject: [PATCH 4/7] lxd-to-incus: Don't fail migration on a failed command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's much easier to recover a few commands afterwards than reverting everything. Signed-off-by: Stéphane Graber --- cmd/lxd-to-incus/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/lxd-to-incus/main.go b/cmd/lxd-to-incus/main.go index 8fe2d252f56..ac985d55ea0 100644 --- a/cmd/lxd-to-incus/main.go +++ b/cmd/lxd-to-incus/main.go @@ -549,8 +549,8 @@ Instead this tool will be providing specific commands for each of the servers. _, err := subprocess.RunCommand(cmd[0], cmd[1:]...) if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) - return err + _, _ = logFile.WriteString(fmt.Sprintf("Failed to run command: %v\n", err)) + fmt.Fprintf(os.Stderr, "Failed to run command: %v\n", err) } } } From 928786a24892b4ef6b0853aee4436632900f6664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 22 Nov 2023 00:25:14 -0500 Subject: [PATCH 5/7] lxd-to-incus: Fix format string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- cmd/lxd-to-incus/main.go | 92 ++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/cmd/lxd-to-incus/main.go b/cmd/lxd-to-incus/main.go index ac985d55ea0..6b9b3433307 100644 --- a/cmd/lxd-to-incus/main.go +++ b/cmd/lxd-to-incus/main.go @@ -98,7 +98,7 @@ func (c *cmdMigrate) Run(app *cobra.Command, args []string) error { err = logFile.Chmod(0600) if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to set permissions on log file: %w", err) } @@ -119,7 +119,7 @@ func (c *cmdMigrate) Run(app *cobra.Command, args []string) error { } if source == nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("No source server could be found") } @@ -139,7 +139,7 @@ func (c *cmdMigrate) Run(app *cobra.Command, args []string) error { } if target == nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("No target server could be found") } @@ -152,13 +152,13 @@ func (c *cmdMigrate) Run(app *cobra.Command, args []string) error { fmt.Println("=> Connecting to source server") srcClient, err = source.Connect() if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to connect to the source: %w", err) } srcServerInfo, _, err := srcClient.GetServer() if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to get source server info: %w", err) } @@ -172,7 +172,7 @@ func (c *cmdMigrate) Run(app *cobra.Command, args []string) error { fmt.Println("=> Connecting to the target server") targetClient, err = target.Connect() if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to connect to the target: %w", err) } @@ -180,7 +180,7 @@ func (c *cmdMigrate) Run(app *cobra.Command, args []string) error { if !c.flagClusterMember { err = c.validate(source, target) if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return err } } @@ -188,7 +188,7 @@ func (c *cmdMigrate) Run(app *cobra.Command, args []string) error { // Grab the path information. sourcePaths, err := source.Paths() if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to get source paths: %w", err) } @@ -210,27 +210,27 @@ func (c *cmdMigrate) Run(app *cobra.Command, args []string) error { if !clustered { storagePools, err = srcClient.GetStoragePools() if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Couldn't list storage pools: %w", err) } } else { clusterMembers, err := srcClient.GetClusterMembers() if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to retrieve the list of cluster members") } for _, member := range clusterMembers { poolNames, err := srcClient.UseTarget(member.ServerName).GetStoragePoolNames() if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Couldn't list storage pools: %w", err) } for _, poolName := range poolNames { pool, _, err := srcClient.UseTarget(member.ServerName).GetStoragePool(poolName) if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Couldn't get storage pool: %w", err) } @@ -281,7 +281,7 @@ func (c *cmdMigrate) Run(app *cobra.Command, args []string) error { // Mangle OVS/OVN. srcServerInfo, _, err := srcClient.GetServer() if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to get source server info: %w", err) } @@ -290,7 +290,7 @@ func (c *cmdMigrate) Run(app *cobra.Command, args []string) error { if !c.flagClusterMember { out, err := subprocess.RunCommand("ovs-vsctl", "get", "open_vswitch", ".", "external_ids:ovn-remote") if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to get OVN southbound database address: %w", err) } @@ -298,7 +298,7 @@ func (c *cmdMigrate) Run(app *cobra.Command, args []string) error { commands, err := ovnConvert(ovnNB, ovnSB) if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to prepare OVN conversion: %v", err) } @@ -306,14 +306,14 @@ func (c *cmdMigrate) Run(app *cobra.Command, args []string) error { err = ovnBackup(ovnNB, ovnSB, "/var/backups/") if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to backup the OVN database: %v", err) } } commands, err := ovsConvert() if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to prepare OVS conversion: %v", err) } @@ -342,7 +342,7 @@ Instances will come back online once the migration is complete. ok, err := c.global.asker.AskBool("Proceed with the migration? [default=no]: ", "no") if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return err } @@ -370,7 +370,7 @@ Instead this tool will be providing specific commands for each of the servers. ok, err := c.global.asker.AskBool("Proceed with the migration? [default=no]: ", "no") if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return err } @@ -392,7 +392,7 @@ Instead this tool will be providing specific commands for each of the servers. clusterMembers, err := srcClient.GetClusterMembers() if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to retrieve the list of cluster members") } @@ -402,13 +402,13 @@ Instead this tool will be providing specific commands for each of the servers. op, err := srcClient.UpdateClusterMemberState(member.ServerName, lxdAPI.ClusterMemberStatePost{Action: "evacuate", Mode: "stop"}) if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to stop workloads %q: %w", member.ServerName, err) } err = op.Wait() if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to stop workloads %q: %w", member.ServerName, err) } } @@ -442,19 +442,19 @@ Instead this tool will be providing specific commands for each of the servers. err = os.RemoveAll(targetPaths.Logs) if err != nil && !os.IsNotExist(err) { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to remove %q: %w", targetPaths.Logs, err) } err = os.RemoveAll(targetPaths.Cache) if err != nil && !os.IsNotExist(err) { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to remove %q: %w", targetPaths.Cache, err) } err = os.RemoveAll(targetPaths.Daemon) if err != nil && !os.IsNotExist(err) { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to remove %q: %w", targetPaths.Daemon, err) } @@ -464,13 +464,13 @@ Instead this tool will be providing specific commands for each of the servers. _, err = subprocess.RunCommand("mv", sourcePaths.Logs, targetPaths.Logs) if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to move %q to %q: %w", sourcePaths.Logs, targetPaths.Logs, err) } _, err = subprocess.RunCommand("mv", sourcePaths.Cache, targetPaths.Cache) if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to move %q to %q: %w", sourcePaths.Cache, targetPaths.Cache, err) } @@ -479,21 +479,21 @@ Instead this tool will be providing specific commands for each of the servers. err = os.MkdirAll(targetPaths.Daemon, 0711) if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to create target directory: %w", err) } _, _ = logFile.WriteString("Creating bind-mount of daemon path\n") err = unix.Mount(sourcePaths.Daemon, targetPaths.Daemon, "none", unix.MS_BIND|unix.MS_REC, "") if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to bind mount %q to %q: %w", sourcePaths.Daemon, targetPaths.Daemon, err) } _, _ = logFile.WriteString("Unmounting former mountpoint\n") err = unix.Unmount(sourcePaths.Daemon, unix.MNT_DETACH) if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to unmount source mount %q: %w", sourcePaths.Daemon, err) } @@ -507,7 +507,7 @@ Instead this tool will be providing specific commands for each of the servers. _, err = subprocess.RunCommand("mv", sourcePaths.Daemon, targetPaths.Daemon) if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to move %q to %q: %w", sourcePaths.Daemon, targetPaths.Daemon, err) } } @@ -518,13 +518,13 @@ Instead this tool will be providing specific commands for each of the servers. _, err = subprocess.RunCommand("cp", "-R", filepath.Join(targetPaths.Daemon, "database"), filepath.Join(targetPaths.Daemon, "database.pre-migrate")) if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to backup the database: %w", err) } err = migrateDatabase(filepath.Join(targetPaths.Daemon, "database")) if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to migrate database in %q: %w", filepath.Join(targetPaths.Daemon, "database"), err) } @@ -535,7 +535,7 @@ Instead this tool will be providing specific commands for each of the servers. err = os.WriteFile(filepath.Join(targetPaths.Daemon, "database", "patch.global.sql"), []byte(strings.Join(rewriteStatements, "\n")+"\n"), 0600) if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to write database path: %w", err) } } @@ -571,7 +571,7 @@ Instead this tool will be providing specific commands for each of the servers. err = os.RemoveAll(filepath.Join(targetPaths.Daemon, dir)) if err != nil && !os.IsNotExist(err) { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to delete %q: %w", dir, err) } } @@ -583,7 +583,7 @@ Instead this tool will be providing specific commands for each of the servers. continue } - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to read entries in %q: %w", filepath.Join(targetPaths.Daemon, dir), err) } @@ -598,14 +598,14 @@ Instead this tool will be providing specific commands for each of the servers. oldTarget, err := os.Readlink(srcPath) if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to resolve symlink %q: %w", srcPath, err) } newTarget := strings.Replace(oldTarget, sourcePaths.Daemon, targetPaths.Daemon, 1) err = os.Remove(srcPath) if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to delete symlink %q: %w", srcPath, err) } @@ -613,7 +613,7 @@ Instead this tool will be providing specific commands for each of the servers. err = os.Symlink(newTarget, srcPath) if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to create symlink %q: %w", srcPath, err) } } @@ -625,7 +625,7 @@ Instead this tool will be providing specific commands for each of the servers. err = target.Start() if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to start the target server: %w", err) } @@ -696,7 +696,7 @@ Instead this tool will be providing specific commands for each of the servers. _, _, err = targetClient.GetServer() if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to get target server info: %w", err) } @@ -707,7 +707,7 @@ Instead this tool will be providing specific commands for each of the servers. clusterMembers, err := targetClient.GetClusterMembers() if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to retrieve the list of cluster members") } @@ -717,13 +717,13 @@ Instead this tool will be providing specific commands for each of the servers. op, err := targetClient.UpdateClusterMemberState(member.ServerName, incusAPI.ClusterMemberStatePost{Action: "restore"}) if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to restore %q: %w", member.ServerName, err) } err = op.Wait() if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to restore %q: %w", member.ServerName, err) } } @@ -733,7 +733,7 @@ Instead this tool will be providing specific commands for each of the servers. if !c.flagYes { ok, err := c.global.asker.AskBool("Uninstall the LXD package? [default=no]: ", "no") if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return err } @@ -749,7 +749,7 @@ Instead this tool will be providing specific commands for each of the servers. err = source.Purge() if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %w\n", err)) + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to uninstall the source server: %w", err) } From 52c7582f44f6e963816a1bfbd18f8bfd05ad8651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 22 Nov 2023 00:38:55 -0500 Subject: [PATCH 6/7] lxd-to-incus: Split OVS commands from OVN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- cmd/lxd-to-incus/main.go | 83 ++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 34 deletions(-) diff --git a/cmd/lxd-to-incus/main.go b/cmd/lxd-to-incus/main.go index 6b9b3433307..d538b02d1bb 100644 --- a/cmd/lxd-to-incus/main.go +++ b/cmd/lxd-to-incus/main.go @@ -278,46 +278,40 @@ func (c *cmdMigrate) Run(app *cobra.Command, args []string) error { } } - // Mangle OVS/OVN. - srcServerInfo, _, err := srcClient.GetServer() - if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) - return fmt.Errorf("Failed to get source server info: %w", err) - } + // Mangle OVN. + if !c.flagClusterMember { + srcServerInfo, _, err := srcClient.GetServer() + if err != nil { + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) + return fmt.Errorf("Failed to get source server info: %w", err) + } - ovnNB, ok := srcServerInfo.Config["network.ovn.northbound_connection"].(string) - if ok && ovnNB != "" { - if !c.flagClusterMember { - out, err := subprocess.RunCommand("ovs-vsctl", "get", "open_vswitch", ".", "external_ids:ovn-remote") - if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) - return fmt.Errorf("Failed to get OVN southbound database address: %w", err) - } + ovnNB, ok := srcServerInfo.Config["network.ovn.northbound_connection"].(string) + if ok && ovnNB != "" { + if !c.flagClusterMember { + out, err := subprocess.RunCommand("ovs-vsctl", "get", "open_vswitch", ".", "external_ids:ovn-remote") + if err != nil { + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) + return fmt.Errorf("Failed to get OVN southbound database address: %w", err) + } - ovnSB := strings.TrimSpace(strings.Replace(out, "\"", "", -1)) + ovnSB := strings.TrimSpace(strings.Replace(out, "\"", "", -1)) - commands, err := ovnConvert(ovnNB, ovnSB) - if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) - return fmt.Errorf("Failed to prepare OVN conversion: %v", err) - } + commands, err := ovnConvert(ovnNB, ovnSB) + if err != nil { + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) + return fmt.Errorf("Failed to prepare OVN conversion: %v", err) + } - rewriteCommands = append(rewriteCommands, commands...) + rewriteCommands = append(rewriteCommands, commands...) - err = ovnBackup(ovnNB, ovnSB, "/var/backups/") - if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) - return fmt.Errorf("Failed to backup the OVN database: %v", err) + err = ovnBackup(ovnNB, ovnSB, "/var/backups/") + if err != nil { + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) + return fmt.Errorf("Failed to backup the OVN database: %v", err) + } } } - - commands, err := ovsConvert() - if err != nil { - _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) - return fmt.Errorf("Failed to prepare OVS conversion: %v", err) - } - - rewriteCommands = append(rewriteCommands, commands...) } // Log rewrite actions. @@ -694,12 +688,33 @@ Instead this tool will be providing specific commands for each of the servers. fmt.Println("=> Checking the target server") _, _ = logFile.WriteString("Checking target server\n") - _, _, err = targetClient.GetServer() + targetServerInfo, _, err := targetClient.GetServer() if err != nil { _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) return fmt.Errorf("Failed to get target server info: %w", err) } + // Fix OVS. + ovnNB, ok := targetServerInfo.Config["network.ovn.northbound_connection"] + if ok && ovnNB != "" { + commands, err := ovsConvert() + if err != nil { + _, _ = logFile.WriteString(fmt.Sprintf("ERROR: %v\n", err)) + return fmt.Errorf("Failed to prepare OVS conversion: %v", err) + } + + _, _ = logFile.WriteString("Running OVS conversion commands:\n") + for _, cmd := range commands { + _, _ = logFile.WriteString(fmt.Sprintf(" - %+v\n", cmd)) + + _, err := subprocess.RunCommand(cmd[0], cmd[1:]...) + if err != nil { + _, _ = logFile.WriteString(fmt.Sprintf("Failed to run command: %v\n", err)) + fmt.Fprintf(os.Stderr, "Failed to run command: %v\n", err) + } + } + } + // Cluster restore. if !c.flagClusterMember && clustered { fmt.Println("=> Restoring the cluster") From db308f6632ce57fe575eeaa123e676d7bdeb6fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Wed, 22 Nov 2023 01:36:31 -0500 Subject: [PATCH 7/7] lxd-to-incus: Fix typo in OVS migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber --- cmd/lxd-to-incus/ovn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/lxd-to-incus/ovn.go b/cmd/lxd-to-incus/ovn.go index d0418d8142c..39faa20420e 100644 --- a/cmd/lxd-to-incus/ovn.go +++ b/cmd/lxd-to-incus/ovn.go @@ -31,7 +31,7 @@ func ovsConvert() ([][]string, error) { newValue := strings.Join(values, ",") if oldValue != newValue { - commands = append(commands, []string{"ovs-vsctl", "set", "openv_vswitch", ".", fmt.Sprintf("external-ids:ovn-bridge-mappings=%s", newValue)}) + commands = append(commands, []string{"ovs-vsctl", "set", "open_vswitch", ".", fmt.Sprintf("external-ids:ovn-bridge-mappings=%s", newValue)}) } return commands, nil