[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