[lxc-devel] [lxd/master] Backup instance config

tomponline on Github lxc-bot at linuxcontainers.org
Wed Nov 27 15:46:05 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 502 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20191127/e181915e/attachment.bin>
-------------- next part --------------
From 272104730cd03f8263074f14bd9cb4b94d0c571c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 27 Nov 2019 12:25:05 +0000
Subject: [PATCH 1/5] lxd/backup/backup/instance/config: Adds instance config
 backup.yml tools

Moved from main package and cleaned up.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/backup/backup_instance_config.go | 122 +++++++++++++++++++++++++++
 1 file changed, 122 insertions(+)
 create mode 100644 lxd/backup/backup_instance_config.go

diff --git a/lxd/backup/backup_instance_config.go b/lxd/backup/backup_instance_config.go
new file mode 100644
index 0000000000..a871ad0a7e
--- /dev/null
+++ b/lxd/backup/backup_instance_config.go
@@ -0,0 +1,122 @@
+package backup
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+
+	"gopkg.in/yaml.v2"
+
+	"github.com/lxc/lxd/lxd/db"
+	"github.com/lxc/lxd/lxd/project"
+	"github.com/lxc/lxd/shared"
+	"github.com/lxc/lxd/shared/api"
+)
+
+// InstanceConfig represents the config of an instance that can be stored in a backup.yaml file.
+type InstanceConfig struct {
+	Container *api.Instance           `yaml:"container"`
+	Snapshots []*api.InstanceSnapshot `yaml:"snapshots"`
+	Pool      *api.StoragePool        `yaml:"pool"`
+	Volume    *api.StorageVolume      `yaml:"volume"`
+}
+
+// ParseInstanceConfigYamlFile decodes the yaml file at path specified into an InstanceConfig.
+func ParseInstanceConfigYamlFile(path string) (*InstanceConfig, error) {
+	data, err := ioutil.ReadFile(path)
+	if err != nil {
+		return nil, err
+	}
+
+	backup := InstanceConfig{}
+	if err := yaml.Unmarshal(data, &backup); err != nil {
+		return nil, err
+	}
+
+	return &backup, nil
+}
+
+// updateRootDevicePool updates the root disk device in the supplied list of devices to the pool
+// specified. Returns true if a root disk device has been found and updated otherwise false.
+func updateRootDevicePool(devices map[string]map[string]string, poolName string) bool {
+	if devices != nil {
+		devName, _, err := shared.GetRootDiskDevice(devices)
+		if err == nil {
+			devices[devName]["pool"] = poolName
+			return true
+		}
+	}
+
+	return false
+}
+
+// UpdateInstanceConfigStoragePool changes the pool information in the backup.yaml to the pool
+// specified in b.Pool.
+func UpdateInstanceConfigStoragePool(c *db.Cluster, b Info) error {
+	// Load the storage pool.
+	_, pool, err := c.StoragePoolGet(b.Pool)
+	if err != nil {
+		return err
+	}
+
+	f := func(path string) error {
+		// Read in the backup.yaml file.
+		backup, err := ParseInstanceConfigYamlFile(path)
+		if err != nil {
+			return err
+		}
+
+		rootDiskDeviceFound := false
+
+		// Change the pool in the backup.yaml.
+		backup.Pool = pool
+
+		if updateRootDevicePool(backup.Container.Devices, pool.Name) {
+			rootDiskDeviceFound = true
+		}
+
+		if updateRootDevicePool(backup.Container.ExpandedDevices, pool.Name) {
+			rootDiskDeviceFound = true
+		}
+
+		for _, snapshot := range backup.Snapshots {
+			updateRootDevicePool(snapshot.Devices, pool.Name)
+			updateRootDevicePool(snapshot.ExpandedDevices, pool.Name)
+		}
+
+		if !rootDiskDeviceFound {
+			return fmt.Errorf("No root device could be found")
+		}
+
+		file, err := os.Create(path)
+		if err != nil {
+			return err
+		}
+		defer file.Close()
+
+		data, err := yaml.Marshal(&backup)
+		if err != nil {
+			return err
+		}
+
+		_, err = file.Write(data)
+		if err != nil {
+			return err
+		}
+
+		return nil
+	}
+
+	err = f(shared.VarPath("storage-pools", pool.Name, "containers", project.Prefix(b.Project, b.Name), "backup.yaml"))
+	if err != nil {
+		return err
+	}
+
+	for _, snap := range b.Snapshots {
+		err = f(shared.VarPath("storage-pools", pool.Name, "containers-snapshots", project.Prefix(b.Project, b.Name), snap, "backup.yaml"))
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}

From d8a2ef2d0523113ef24d741162ebb0f4bdef5a97 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 27 Nov 2019 12:26:08 +0000
Subject: [PATCH 2/5] lxd/api/internal: Removes slurpBackupFile and switches to
 backup.ParseInstanceConfigYamlFile

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/api_internal.go | 20 ++------------------
 1 file changed, 2 insertions(+), 18 deletions(-)

diff --git a/lxd/api_internal.go b/lxd/api_internal.go
index 957b0107b7..38ee0da172 100644
--- a/lxd/api_internal.go
+++ b/lxd/api_internal.go
@@ -4,7 +4,6 @@ import (
 	"database/sql"
 	"encoding/json"
 	"fmt"
-	"io/ioutil"
 	"net/http"
 	"os"
 	"path/filepath"
@@ -15,8 +14,8 @@ import (
 
 	"github.com/gorilla/mux"
 	"github.com/pkg/errors"
-	"gopkg.in/yaml.v2"
 
+	"github.com/lxc/lxd/lxd/backup"
 	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/db/cluster"
 	"github.com/lxc/lxd/lxd/db/node"
@@ -390,21 +389,6 @@ func internalSQLExec(tx *sql.Tx, query string, result *internalSQLResult) error
 	return nil
 }
 
-func slurpBackupFile(path string) (*backupFile, error) {
-	data, err := ioutil.ReadFile(path)
-	if err != nil {
-		return nil, err
-	}
-
-	backup := backupFile{}
-
-	if err := yaml.Unmarshal(data, &backup); err != nil {
-		return nil, err
-	}
-
-	return &backup, nil
-}
-
 type internalImportPost struct {
 	Name  string `json:"name" yaml:"name"`
 	Force bool   `json:"force" yaml:"force"`
@@ -476,7 +460,7 @@ func internalImport(d *Daemon, r *http.Request) response.Response {
 
 	// Read in the backup.yaml file.
 	backupYamlPath := filepath.Join(containerMntPoint, "backup.yaml")
-	backup, err := slurpBackupFile(backupYamlPath)
+	backup, err := backup.ParseInstanceConfigYamlFile(backupYamlPath)
 	if err != nil {
 		return response.SmartError(err)
 	}

From 50837149c75edcc9fa90f5a86e50d2fe5dada67a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 27 Nov 2019 12:26:38 +0000
Subject: [PATCH 3/5] lxd/backup: Removes backupFixStoragePool

Replaced by backup.UpdateInstanceConfigStoragePool

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/backup.go | 94 ---------------------------------------------------
 1 file changed, 94 deletions(-)

diff --git a/lxd/backup.go b/lxd/backup.go
index be3b24511b..15189bffd7 100644
--- a/lxd/backup.go
+++ b/lxd/backup.go
@@ -101,100 +101,6 @@ func backupCreate(s *state.State, args db.InstanceBackupArgs, sourceInst instanc
 	return nil
 }
 
-// fixBackupStoragePool changes the pool information in the backup.yaml. This
-// is done only if the provided pool doesn't exist. In this case, the pool of
-// the default profile will be used.
-func backupFixStoragePool(c *db.Cluster, b backup.Info, useDefaultPool bool) error {
-	var poolName string
-
-	if useDefaultPool {
-		// Get the default profile
-		_, profile, err := c.ProfileGet("default", "default")
-		if err != nil {
-			return err
-		}
-
-		_, v, err := shared.GetRootDiskDevice(profile.Devices)
-		if err != nil {
-			return err
-		}
-
-		poolName = v["pool"]
-	} else {
-		poolName = b.Pool
-	}
-
-	// Get the default's profile pool
-	_, pool, err := c.StoragePoolGet(poolName)
-	if err != nil {
-		return err
-	}
-
-	f := func(path string) error {
-		// Read in the backup.yaml file.
-		backup, err := slurpBackupFile(path)
-		if err != nil {
-			return err
-		}
-
-		rootDiskDeviceFound := false
-
-		// Change the pool in the backup.yaml
-		backup.Pool = pool
-		if backup.Container.Devices != nil {
-			devName, _, err := shared.GetRootDiskDevice(backup.Container.Devices)
-			if err == nil {
-				backup.Container.Devices[devName]["pool"] = poolName
-				rootDiskDeviceFound = true
-			}
-		}
-
-		if backup.Container.ExpandedDevices != nil {
-			devName, _, err := shared.GetRootDiskDevice(backup.Container.ExpandedDevices)
-			if err == nil {
-				backup.Container.ExpandedDevices[devName]["pool"] = poolName
-				rootDiskDeviceFound = true
-			}
-		}
-
-		if !rootDiskDeviceFound {
-			return fmt.Errorf("No root device could be found")
-		}
-
-		file, err := os.Create(path)
-		if err != nil {
-			return err
-		}
-		defer file.Close()
-
-		data, err := yaml.Marshal(&backup)
-		if err != nil {
-			return err
-		}
-
-		_, err = file.Write(data)
-		if err != nil {
-			return err
-		}
-
-		return nil
-	}
-
-	err = f(shared.VarPath("storage-pools", pool.Name, "containers", project.Prefix(b.Project, b.Name), "backup.yaml"))
-	if err != nil {
-		return err
-	}
-
-	for _, snap := range b.Snapshots {
-		err = f(shared.VarPath("storage-pools", pool.Name, "containers-snapshots", project.Prefix(b.Project, b.Name), snap,
-			"backup.yaml"))
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
 func backupCreateTarball(s *state.State, path string, b backup.Backup, c instance.Instance) error {
 	// Create the index
 	pool, err := c.StoragePool()

From cfc105ae56a054510a2ead417cf71f46314e58ed Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 27 Nov 2019 12:28:25 +0000
Subject: [PATCH 4/5] lxd/containers/post: Updates instanceCreateFromBackup
 usage

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/containers_post.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/containers_post.go b/lxd/containers_post.go
index eae96c5597..c3440ce4fb 100644
--- a/lxd/containers_post.go
+++ b/lxd/containers_post.go
@@ -657,7 +657,7 @@ func createFromBackup(d *Daemon, project string, data io.Reader, pool string) re
 
 		// Dump tarball to storage.
 		f.Seek(0, 0)
-		revertFunc, err := instanceCreateFromBackup(d.State(), *bInfo, f, pool != "")
+		revertFunc, err := instanceCreateFromBackup(d.State(), *bInfo, f)
 		if err != nil {
 			return errors.Wrap(err, "Create instance from backup")
 		}

From ed788bd0682a20aa1710b1358ad76fce614a33d9 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 27 Nov 2019 15:43:27 +0000
Subject: [PATCH 5/5] lxd/container: Updates instanceCreateFromBackup signature

- Removes need for customBool variable and instead will just always update backup.yaml file to use current storage pool.
- This simplifies the function signature needed later for pool.CreateInstanceFromBackup().

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/container.go | 27 ++++++++++-----------------
 1 file changed, 10 insertions(+), 17 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index 125e5c4b8e..625c57ffd4 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -307,13 +307,10 @@ func instanceCreateAsEmpty(d *Daemon, args db.InstanceArgs) (instance.Instance,
 
 // instanceCreateFromBackup imports a backup file to restore an instance. Returns a revert function
 // that can be called to delete the files created during import if subsequent steps fail.
-func instanceCreateFromBackup(s *state.State, info backup.Info, srcData io.ReadSeeker, customPool bool) (func(), error) {
-	var fixBackupFile = false
-	poolName := info.Pool
-
+func instanceCreateFromBackup(s *state.State, info backup.Info, srcData io.ReadSeeker) (func(), error) {
 	// Check storage pool from index.yaml exists. If the storage pool in the index.yaml doesn't
-	// exist, try and use the default profile's storage pool.
-	_, _, err := s.Cluster.StoragePoolGet(poolName)
+	// exist, try and use the project's default profile storage pool.
+	_, _, err := s.Cluster.StoragePoolGet(info.Pool)
 	if errors.Cause(err) == db.ErrNoSuchObject {
 		// The backup is in binary format so we cannot alter the backup.yaml.
 		if info.HasBinaryFormat {
@@ -332,10 +329,7 @@ func instanceCreateFromBackup(s *state.State, info backup.Info, srcData io.ReadS
 		}
 
 		// Use the default-profile's root pool.
-		poolName = v["pool"]
-
-		// Mark requirement to change the pool information in the backup.yaml file.
-		fixBackupFile = true
+		info.Pool = v["pool"]
 	} else if err != nil {
 		return nil, err
 	}
@@ -385,7 +379,7 @@ func instanceCreateFromBackup(s *state.State, info backup.Info, srcData io.ReadS
 		srcData = tarData
 	}
 
-	pool, err := storagePoolInit(s, poolName)
+	pool, err := storagePoolInit(s, info.Pool)
 	if err != nil {
 		return nil, err
 	}
@@ -396,12 +390,11 @@ func instanceCreateFromBackup(s *state.State, info backup.Info, srcData io.ReadS
 		return nil, err
 	}
 
-	if fixBackupFile || customPool {
-		// Update pool information in the backup.yaml file.
-		err = backupFixStoragePool(s.Cluster, info, !customPool)
-		if err != nil {
-			return nil, err
-		}
+	// Update pool information in the backup.yaml file.
+	// Requires the volume and snapshots be mounted from pool.ContainerBackupLoad().
+	err = backup.UpdateInstanceConfigStoragePool(s.Cluster, info)
+	if err != nil {
+		return nil, err
 	}
 
 	// Create revert function to remove the files created so far.


More information about the lxc-devel mailing list