[lxc-devel] [lxd/master] Storage Instance Backup Config
tomponline on Github
lxc-bot at linuxcontainers.org
Wed Jan 8 12:06:52 UTC 2020
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 1137 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200108/38632e4c/attachment.bin>
-------------- next part --------------
From ef680d12388f78b31ac6e2ea4647bdaf879265a6 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 6 Jan 2020 16:20:26 +0000
Subject: [PATCH 01/20] lxd/storage/drivers/driver/dir/volumes: Use
SetVolumeQuota from UpdateVolume
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/storage/drivers/driver_dir_volumes.go | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/lxd/storage/drivers/driver_dir_volumes.go b/lxd/storage/drivers/driver_dir_volumes.go
index 0c0e850539..67df0ffe44 100644
--- a/lxd/storage/drivers/driver_dir_volumes.go
+++ b/lxd/storage/drivers/driver_dir_volumes.go
@@ -197,13 +197,7 @@ func (d *dir) UpdateVolume(vol Volume, changedConfig map[string]string) error {
}
if _, changed := changedConfig["size"]; changed {
- volID, err := d.getVolID(vol.volType, vol.name)
- if err != nil {
- return err
- }
-
- // Set the quota if specified in volConfig or pool config.
- err = d.setQuota(vol.MountPath(), volID, changedConfig["size"])
+ err := d.SetVolumeQuota(vol, changedConfig["size"], nil)
if err != nil {
return err
}
From e64114a6bd020619c4649ef93b79cc529111e0f0 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 7 Jan 2020 09:20:51 +0000
Subject: [PATCH 02/20] lxd/storage/backend/lxd: Makes specific lock name for
volume EnsureImage action
Avoids deadlock with MountTask lock during same process.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/storage/backend_lxd.go | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index e4219293da..82c102be5c 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -1784,9 +1784,10 @@ func (b *lxdBackend) EnsureImage(fingerprint string, op *operations.Operation) e
return nil // Nothing to do for drivers that don't support optimized images volumes.
}
- // We need to lock this operation to ensure that the image is not being
- // created multiple times.
- unlock := locking.Lock(b.name, string(drivers.VolumeTypeImage), fingerprint)
+ // We need to lock this operation to ensure that the image is not being created multiple times.
+ // Uses a lock name of "EnsureImage_<fingerprint>" to avoid deadlocking with CreateVolume below that also
+ // establishes a lock on the volume type & name if it needs to mount the volume before filling.
+ unlock := locking.Lock(b.name, string(drivers.VolumeTypeImage), fmt.Sprintf("EnsureImage_%v", fingerprint))
defer unlock()
// There's no need to pass the content type or config. Both are not needed
From e0018c7aa106cc3233cd0615ce1284753155ced1 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 7 Jan 2020 09:21:41 +0000
Subject: [PATCH 03/20] lxd/storage/drivers/volume: Adds UnmountTask function
Allows a volume to be temporarily unmounted (if mounted) and a function be run before volume then being re-mounted (if previously mounted).
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/storage/drivers/volume.go | 48 +++++++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
diff --git a/lxd/storage/drivers/volume.go b/lxd/storage/drivers/volume.go
index b7ec773dff..fcba388445 100644
--- a/lxd/storage/drivers/volume.go
+++ b/lxd/storage/drivers/volume.go
@@ -183,6 +183,54 @@ func (v Volume) MountTask(task func(mountPath string, op *operations.Operation)
return task(v.MountPath(), op)
}
+// UnmountTask runs the supplied task after unmounting the volume if needed. If the volume was unmounted
+// for this then it is mounted when the task finishes.
+func (v Volume) UnmountTask(task func(op *operations.Operation) error, op *operations.Operation) error {
+ isSnap := v.IsSnapshot()
+
+ // If the volume is a snapshot then call the snapshot specific mount/unmount functions as
+ // these will mount the snapshot read only.
+ if isSnap {
+ unlock := locking.Lock(v.pool, string(v.volType), v.name)
+
+ ourUnmount, err := v.driver.UnmountVolumeSnapshot(v, op)
+ if err != nil {
+ unlock()
+ return err
+ }
+
+ unlock()
+
+ if ourUnmount {
+ defer func() {
+ unlock := locking.Lock(v.pool, string(v.volType), v.name)
+ v.driver.MountVolumeSnapshot(v, op)
+ unlock()
+ }()
+ }
+ } else {
+ unlock := locking.Lock(v.pool, string(v.volType), v.name)
+
+ ourUnmount, err := v.driver.UnmountVolume(v, op)
+ if err != nil {
+ unlock()
+ return err
+ }
+
+ unlock()
+
+ if ourUnmount {
+ defer func() {
+ unlock := locking.Lock(v.pool, string(v.volType), v.name)
+ v.driver.MountVolume(v, op)
+ unlock()
+ }()
+ }
+ }
+
+ return task(op)
+}
+
// Snapshots returns a list of snapshots for the volume.
func (v Volume) Snapshots(op *operations.Operation) ([]Volume, error) {
if v.IsSnapshot() {
From 431aee92688a5745f34917113b5661689e14bec1 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 7 Jan 2020 09:22:32 +0000
Subject: [PATCH 04/20] lxd/storage/drivers/utils: Adds volume filesystem
shrink and grow functions
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/storage/drivers/utils.go | 63 ++++++++++++++++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/lxd/storage/drivers/utils.go b/lxd/storage/drivers/utils.go
index 768aab8b69..07a8b32268 100644
--- a/lxd/storage/drivers/utils.go
+++ b/lxd/storage/drivers/utils.go
@@ -10,6 +10,7 @@ import (
"golang.org/x/sys/unix"
+ "github.com/lxc/lxd/lxd/operations"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/units"
)
@@ -401,3 +402,65 @@ func resolveMountOptions(options string) (uintptr, string) {
return mountFlags, strings.Join(tmp, ",")
}
+
+// shrinkFileSystem shrinks a filesystem if it is supported. Ext4 volumes will be unmounted temporarily if needed.
+func shrinkFileSystem(fsType string, devPath string, vol Volume, byteSize int64) error {
+ strSize := fmt.Sprintf("%dK", byteSize/1024)
+
+ switch fsType {
+ case "": // if not specified, default to ext4.
+ fallthrough
+ case "xfs":
+ return fmt.Errorf(`Shrinking not supported for filesystem type "%s". A dump, mkfs, and restore are required`, fsType)
+ case "ext4":
+ return vol.UnmountTask(func(op *operations.Operation) error {
+ _, err := shared.TryRunCommand("e2fsck", "-f", "-y", devPath)
+ if err != nil {
+ return err
+ }
+
+ _, err = shared.TryRunCommand("resize2fs", devPath, strSize)
+ if err != nil {
+ return err
+ }
+
+ return nil
+ }, nil)
+ case "btrfs":
+ _, err := shared.TryRunCommand("btrfs", "filesystem", "resize", strSize, vol.MountPath())
+ if err != nil {
+ return err
+ }
+ default:
+ return fmt.Errorf(`Shrinking not supported for filesystem type "%s"`, fsType)
+ }
+
+ return nil
+}
+
+// growFileSystem grows a filesystem if it is supported. The volume will be mounted temporarily if needed.
+func growFileSystem(fsType string, devPath string, vol Volume) error {
+ return vol.MountTask(func(mountPath string, op *operations.Operation) error {
+ var msg string
+ var err error
+ switch fsType {
+ case "": // if not specified, default to ext4
+ fallthrough
+ case "ext4":
+ msg, err = shared.TryRunCommand("resize2fs", devPath)
+ case "xfs":
+ msg, err = shared.TryRunCommand("xfs_growfs", devPath)
+ case "btrfs":
+ msg, err = shared.TryRunCommand("btrfs", "filesystem", "resize", "max", mountPath)
+ default:
+ return fmt.Errorf(`Growing not supported for filesystem type "%s"`, fsType)
+ }
+
+ if err != nil {
+ errorMsg := fmt.Sprintf(`Could not extend underlying %s filesystem for "%s": %s`, fsType, devPath, msg)
+ return fmt.Errorf(errorMsg)
+ }
+
+ return nil
+ }, nil)
+}
From 8ee162b99649c873a9131caf2d1b234b867da645 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 7 Jan 2020 15:56:27 +0000
Subject: [PATCH 05/20] lxd/storage/drivers/errors: Adds "not supported" error
type
Can be used when a driver doesn't support a specific feature.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/storage/drivers/errors.go | 3 +++
1 file changed, 3 insertions(+)
diff --git a/lxd/storage/drivers/errors.go b/lxd/storage/drivers/errors.go
index 56d1bdab3c..e9fb157bc6 100644
--- a/lxd/storage/drivers/errors.go
+++ b/lxd/storage/drivers/errors.go
@@ -9,3 +9,6 @@ var ErrNotImplemented = fmt.Errorf("Not implemented")
// ErrUnknownDriver is the "Unknown driver" error
var ErrUnknownDriver = fmt.Errorf("Unknown driver")
+
+// ErrNotSupported is the "Not supported" error
+var ErrNotSupported = fmt.Errorf("Not supported")
From f5763456414eccfb8488374f38d99cff3773cbaf Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 7 Jan 2020 15:55:52 +0000
Subject: [PATCH 06/20] lxd/container/lxc: Detects storage drivers that dont
support volume usage stats
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/container_lxc.go | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 7eede32fcd..e1058c6595 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -5811,7 +5811,9 @@ func (c *containerLXC) diskState() map[string]api.InstanceStateDisk {
usage, err = pool.GetInstanceUsage(c)
if err != nil {
- logger.Error("Error getting disk usage", log.Ctx{"project": c.Project(), "instance": c.Name(), "err": err})
+ if err != storageDrivers.ErrNotSupported {
+ logger.Error("Error getting disk usage", log.Ctx{"project": c.Project(), "instance": c.Name(), "err": err})
+ }
continue
}
} else {
From 567ec03f0a49fb5ae47e22aa0548923e254d3ff1 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 7 Jan 2020 16:37:49 +0000
Subject: [PATCH 07/20] lxd/storage/drivers/generic: Improves
genericBackupUnpack
- Mounts volumes so that this can work with non-dir based drivers.
- Returns a valid post hook function that should be used to unmount the primary volume after DB records created.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/storage/drivers/generic.go | 85 +++++++++++++++++++++-------------
1 file changed, 53 insertions(+), 32 deletions(-)
diff --git a/lxd/storage/drivers/generic.go b/lxd/storage/drivers/generic.go
index 058456e5df..f1bac2babf 100644
--- a/lxd/storage/drivers/generic.go
+++ b/lxd/storage/drivers/generic.go
@@ -183,26 +183,13 @@ func genericCreateVolumeFromMigration(d Driver, initVolume func(vol Volume) (fun
}
// genericBackupUnpack unpacks a non-optimized backup tarball through a storage driver.
+// Returns a post hook function that should be called once the database entries for the restored backup have been
+// created and a revert function that can be used to undo the actions this function performs should something
+// subsequently fail.
func genericBackupUnpack(d Driver, vol Volume, snapshots []string, srcData io.ReadSeeker, op *operations.Operation) (func(vol Volume) error, func(), error) {
revert := revert.New()
defer revert.Fail()
- // Define a revert function that will be used both to revert if an error occurs inside this
- // function but also return it for use from the calling functions if no error internally.
- revertHook := func() {
- for _, snapName := range snapshots {
- fullSnapshotName := GetSnapshotVolumeName(vol.name, snapName)
- snapVol := NewVolume(d, d.Name(), vol.volType, vol.contentType, fullSnapshotName, vol.config, vol.poolConfig)
- d.DeleteVolumeSnapshot(snapVol, op)
- }
-
- // And lastly the main volume.
- d.DeleteVolume(vol, op)
- }
-
- // Only execute the revert function if we have had an error internally.
- revert.Add(revertHook)
-
// Find the compression algorithm used for backup source data.
srcData.Seek(0, 0)
tarArgs, _, _, err := shared.DetectCompressionFile(srcData)
@@ -210,11 +197,16 @@ func genericBackupUnpack(d Driver, vol Volume, snapshots []string, srcData io.Re
return nil, nil, err
}
- // Create the main volume.
+ if d.HasVolume(vol) {
+ return nil, nil, fmt.Errorf("Cannot restore volume, already exists on target")
+ }
+
+ // Create new empty volume.
err = d.CreateVolume(vol, nil, nil)
if err != nil {
return nil, nil, err
}
+ revert.Add(func() { d.DeleteVolume(vol, op) })
if len(snapshots) > 0 {
// Create new snapshots directory.
@@ -225,28 +217,56 @@ func genericBackupUnpack(d Driver, vol Volume, snapshots []string, srcData io.Re
}
for _, snapName := range snapshots {
- // Prepare tar arguments.
- args := append(tarArgs, []string{
- "-",
- "--recursive-unlink",
- "--xattrs-include=*",
- "--strip-components=3",
- "-C", vol.MountPath(), fmt.Sprintf("backup/snapshots/%s", snapName),
- }...)
-
- // Extract snapshots.
- srcData.Seek(0, 0)
- err = shared.RunCommandWithFds(srcData, nil, "tar", args...)
+ err = vol.MountTask(func(mountPath string, op *operations.Operation) error {
+ // Prepare tar arguments.
+ args := append(tarArgs, []string{
+ "-",
+ "--recursive-unlink",
+ "--xattrs-include=*",
+ "--strip-components=3",
+ "-C", mountPath, fmt.Sprintf("backup/snapshots/%s", snapName),
+ }...)
+
+ // Extract snapshot.
+ srcData.Seek(0, 0)
+ err = shared.RunCommandWithFds(srcData, nil, "tar", args...)
+ if err != nil {
+ return err
+ }
+
+ return nil
+ }, op)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ snapVol, err := vol.NewSnapshot(snapName)
if err != nil {
return nil, nil, err
}
- fullSnapshotName := GetSnapshotVolumeName(vol.name, snapName)
- snapVol := NewVolume(d, d.Name(), vol.volType, vol.contentType, fullSnapshotName, vol.config, vol.poolConfig)
err = d.CreateVolumeSnapshot(snapVol, op)
if err != nil {
return nil, nil, err
}
+ revert.Add(func() { d.DeleteVolumeSnapshot(snapVol, op) })
+ }
+
+ // Mount main volume and leave mounted (as is needed during backup.yaml generation during latter parts of
+ // the backup restoration process).
+ ourMount, err := d.MountVolume(vol, op)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // Create a post hook function that will be called at the end of the backup restore process to unmount
+ // the volume if needed.
+ postHook := func(vol Volume) error {
+ if ourMount {
+ d.UnmountVolume(vol, op)
+ }
+
+ return nil
}
// Prepare tar extraction arguments.
@@ -265,6 +285,7 @@ func genericBackupUnpack(d Driver, vol Volume, snapshots []string, srcData io.Re
return nil, nil, err
}
+ revertExternal := revert.Clone() // Clone before calling revert.Success() so we can return the Fail func.
revert.Success()
- return nil, revertHook, nil
+ return postHook, revertExternal.Fail, nil
}
From f9e793cb1ab0d89ff59dba87a0a3311fb6ee335c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 7 Jan 2020 17:47:06 +0000
Subject: [PATCH 08/20] lxd/revert: Adds Clone function to revert
Can be used to copy a set of revert steps before calling Success() so that the Fail() function on the cloned revert can be returned as an external revert function.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/revert/revert.go | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/lxd/revert/revert.go b/lxd/revert/revert.go
index df4a3b2578..dbe65b7edf 100644
--- a/lxd/revert/revert.go
+++ b/lxd/revert/revert.go
@@ -31,3 +31,17 @@ func (r *Reverter) Fail() {
func (r *Reverter) Success() {
r.revertFuncs = nil
}
+
+// Clone returns a copy of the reverter with the current set of revert functions added.
+// This can be used if you want to return a reverting function to an external caller but do not want to actually
+// execute the previously deferred reverter.Fail() function.
+func (r *Reverter) Clone() *Reverter {
+ rNew := New()
+ rNew.revertFuncs = make([]func(), 0, len(r.revertFuncs))
+
+ for _, f := range r.revertFuncs {
+ rNew.revertFuncs = append(rNew.revertFuncs, f)
+ }
+
+ return rNew
+}
From c9fce06103b7319ca1f168e73983876b74075c95 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 7 Jan 2020 17:44:42 +0000
Subject: [PATCH 09/20] lxd/storage/drivers/utils: Comments on wipeDirectory
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/storage/drivers/utils.go | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lxd/storage/drivers/utils.go b/lxd/storage/drivers/utils.go
index 07a8b32268..91c5e939e4 100644
--- a/lxd/storage/drivers/utils.go
+++ b/lxd/storage/drivers/utils.go
@@ -15,8 +15,9 @@ import (
"github.com/lxc/lxd/shared/units"
)
+// wipeDirectory empties the contents of a directory, but leaves it in place.
func wipeDirectory(path string) error {
- // List all entries
+ // List all entries.
entries, err := ioutil.ReadDir(path)
if err != nil {
if os.IsNotExist(err) {
@@ -24,7 +25,7 @@ func wipeDirectory(path string) error {
}
}
- // Individually wipe all entries
+ // Individually wipe all entries.
for _, entry := range entries {
entryPath := filepath.Join(path, entry.Name())
err := os.RemoveAll(entryPath)
From 6b5fd5bd48712904ced95699e9b4c3acda805923 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 8 Jan 2020 10:18:05 +0000
Subject: [PATCH 10/20] lxd/containers/post: Improves comment in
createFromBackup
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/containers_post.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lxd/containers_post.go b/lxd/containers_post.go
index 1ba3baef48..f45d796c4f 100644
--- a/lxd/containers_post.go
+++ b/lxd/containers_post.go
@@ -724,8 +724,8 @@ func createFromBackup(d *Daemon, project string, data io.Reader, pool string) re
return errors.Wrap(err, "Load instance")
}
- // Run the storage post hook to perform any final actions now that the instance
- // has been created in the database.
+ // Run the storage post hook to perform any final actions now that the instance has been created
+ // in the database (this normally includes unmounting volumes that were mounted).
if postHook != nil {
err = postHook(c)
if err != nil {
From 7361ab9944f6f3e4af4c27a9649f3a73aedb70ca Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 8 Jan 2020 11:39:12 +0000
Subject: [PATCH 11/20] lxd/storage/pool/interface: Adds BackupInstanceConfig
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/storage/pool_interface.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/lxd/storage/pool_interface.go b/lxd/storage/pool_interface.go
index 6e4dd65a62..506a381352 100644
--- a/lxd/storage/pool_interface.go
+++ b/lxd/storage/pool_interface.go
@@ -34,6 +34,7 @@ type Pool interface {
RenameInstance(inst instance.Instance, newName string, op *operations.Operation) error
DeleteInstance(inst instance.Instance, op *operations.Operation) error
UpdateInstance(inst instance.Instance, newDesc string, newConfig map[string]string, op *operations.Operation) error
+ BackupInstanceConfig(inst instance.Instance, op *operations.Operation) error
MigrateInstance(inst instance.Instance, conn io.ReadWriteCloser, args migration.VolumeSourceArgs, op *operations.Operation) error
RefreshInstance(inst instance.Instance, src instance.Instance, srcSnapshots []instance.Instance, op *operations.Operation) error
From 29bbe2f304ee505af3572f1b5e835cf784e251a6 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 8 Jan 2020 11:39:35 +0000
Subject: [PATCH 12/20] lxd/storage/backend/mock: Adds BackupInstanceConfig
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/storage/backend_mock.go | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/lxd/storage/backend_mock.go b/lxd/storage/backend_mock.go
index 43a9becec2..dac18c53a6 100644
--- a/lxd/storage/backend_mock.go
+++ b/lxd/storage/backend_mock.go
@@ -92,6 +92,10 @@ func (b *mockBackend) UpdateInstance(inst instance.Instance, newDesc string, new
return nil
}
+func (b *mockBackend) BackupInstanceConfig(inst instance.Instance, op *operations.Operation) error {
+ return nil
+}
+
func (b *mockBackend) MigrateInstance(inst instance.Instance, conn io.ReadWriteCloser, args migration.VolumeSourceArgs, op *operations.Operation) error {
return nil
}
From 0df01f841ec56b7b7680118abb7b613da10875b2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 8 Jan 2020 11:40:37 +0000
Subject: [PATCH 13/20] lxd/storage/backend/lxd: Adds error checking to
MountTask in CreateInstanceFromBackup
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/storage/backend_lxd.go | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 82c102be5c..aa2e4fe1f8 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -470,9 +470,12 @@ func (b *lxdBackend) CreateInstanceFromBackup(srcBackup backup.Info, srcData io.
}
// Update pool information in the backup.yaml file.
- vol.MountTask(func(mountPath string, op *operations.Operation) error {
+ err = vol.MountTask(func(mountPath string, op *operations.Operation) error {
return backup.UpdateInstanceConfigStoragePool(b.state.Cluster, srcBackup, mountPath)
}, op)
+ if err != nil {
+ return nil, nil, err
+ }
var postHook func(instance.Instance) error
if volPostHook != nil {
From 99baeafc55454e497d61ca15c45e55d412736668 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 8 Jan 2020 11:41:00 +0000
Subject: [PATCH 14/20] lxd/storage/backend/lxd: Implements
BackupInstanceConfig
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/storage/backend_lxd.go | 99 ++++++++++++++++++++++++++++++++++++++
1 file changed, 99 insertions(+)
diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index aa2e4fe1f8..b27d4a82e4 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -8,6 +8,9 @@ import (
"regexp"
"strings"
+ "github.com/pkg/errors"
+ yaml "gopkg.in/yaml.v2"
+
"github.com/lxc/lxd/lxd/backup"
"github.com/lxc/lxd/lxd/db"
"github.com/lxc/lxd/lxd/instance"
@@ -2641,3 +2644,99 @@ func (b *lxdBackend) createStorageStructure(path string) error {
return nil
}
+
+// BackupInstanceConfig writes the instance's config to the backup.yaml file on the storage device.
+func (b *lxdBackend) BackupInstanceConfig(inst instance.Instance, op *operations.Operation) error {
+ logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()})
+ logger.Debug("BackupInstanceConfig started")
+ defer logger.Debug("BackupInstanceConfig finished")
+
+ // We only write backup files out for actual instances.
+ if inst.IsSnapshot() {
+ return nil
+ }
+
+ // Immediately return if the instance directory doesn't exist yet.
+ if !shared.PathExists(inst.Path()) {
+ return os.ErrNotExist
+ }
+
+ // Generate the YAML.
+ ci, _, err := inst.Render()
+ if err != nil {
+ return errors.Wrap(err, "Failed to render instance metadata")
+ }
+
+ snapshots, err := inst.Snapshots()
+ if err != nil {
+ return errors.Wrap(err, "Failed to get snapshots")
+ }
+
+ var sis []*api.InstanceSnapshot
+
+ for _, s := range snapshots {
+ si, _, err := s.Render()
+ if err != nil {
+ return err
+ }
+
+ sis = append(sis, si.(*api.InstanceSnapshot))
+ }
+
+ volType, err := InstanceTypeToVolumeType(inst.Type())
+ if err != nil {
+ return err
+ }
+
+ volDBType, err := VolumeTypeToDBType(volType)
+ if err != nil {
+ return err
+ }
+
+ contentType := InstanceContentType(inst)
+
+ _, volume, err := b.state.Cluster.StoragePoolNodeVolumeGetTypeByProject(inst.Project(), inst.Name(), volDBType, b.ID())
+ if err != nil {
+ return err
+ }
+
+ data, err := yaml.Marshal(&backup.InstanceConfig{
+ Container: ci.(*api.Instance),
+ Snapshots: sis,
+ Pool: &b.db,
+ Volume: volume,
+ })
+ if err != nil {
+ return err
+ }
+
+ // Get the volume name on storage.
+ volStorageName := project.Prefix(inst.Project(), inst.Name())
+
+ // We don't need to use the volume's config for mounting so set to nil.
+ vol := b.newVolume(volType, contentType, volStorageName, nil)
+
+ // Update pool information in the backup.yaml file.
+ err = vol.MountTask(func(mountPath string, op *operations.Operation) error {
+ // Write the YAML
+ f, err := os.Create(filepath.Join(inst.Path(), "backup.yaml"))
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ err = f.Chmod(0400)
+ if err != nil {
+ return err
+ }
+
+ err = shared.WriteAll(f, data)
+ if err != nil {
+ return err
+ }
+
+ return nil
+ }, op)
+
+ return err
+}
From e10028d0109cb6aebbb7af197501a5038192868a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 8 Jan 2020 11:41:36 +0000
Subject: [PATCH 15/20] lxd/instance/instance/interface: Adds BackupConfig
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/instance/instance_interface.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/lxd/instance/instance_interface.go b/lxd/instance/instance_interface.go
index 3951db18c3..b7fb17b5e3 100644
--- a/lxd/instance/instance_interface.go
+++ b/lxd/instance/instance_interface.go
@@ -29,6 +29,7 @@ type Instance interface {
Restore(source Instance, stateful bool) error
Snapshots() ([]Instance, error)
Backups() ([]backup.Backup, error)
+ BackupConfig() error
// Config handling
Rename(newName string) error
From af11af13c0a94c0eec4adbb3856977411f9da520 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 8 Jan 2020 11:41:58 +0000
Subject: [PATCH 16/20] lxd/instance/qemu/vm/qemu: Implements BackupConfig
Only uses new storage package.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/instance/qemu/vm_qemu.go | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/lxd/instance/qemu/vm_qemu.go b/lxd/instance/qemu/vm_qemu.go
index b1c0536e72..dd666f61b4 100644
--- a/lxd/instance/qemu/vm_qemu.go
+++ b/lxd/instance/qemu/vm_qemu.go
@@ -1861,7 +1861,7 @@ func (vm *Qemu) Update(args db.InstanceArgs, userRequested bool) error {
return errors.Wrap(err, "Failed to update database")
}
- err = instance.WriteBackupFile(vm.state, vm)
+ err = vm.BackupConfig()
if err != nil && !os.IsNotExist(err) {
return errors.Wrap(err, "Failed to write backup file")
}
@@ -3296,3 +3296,13 @@ func (vm *Qemu) maasUpdate(oldDevices map[string]map[string]string) error {
return vm.state.MAAS.CreateContainer(project.Prefix(vm.project, vm.name), interfaces)
}
+
+// BackupConfig writes the instance's backup.yaml file to storage.
+func (vm *Qemu) BackupConfig() error {
+ pool, err := vm.getStoragePool()
+ if err != nil {
+ return err
+ }
+
+ return pool.BackupInstanceConfig(vm, nil)
+}
From de857d3af6b08b2aa06e2aa73bebec419422ad33 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 8 Jan 2020 11:42:30 +0000
Subject: [PATCH 17/20] lxd/container/lxc: Implements BackupConfig
Uses new storage package where possible, otherwise fallsback to old function.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/container_lxc.go | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index e1058c6595..d978999b2a 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -7032,3 +7032,19 @@ func (rw *lxcCgroupReadWriter) Set(version cgroup.Backend, controller string, ke
return rw.cc.SetCgroupItem(key, value)
}
+
+// BackupConfig writes the instance's backup.yaml file to storage.
+func (c *containerLXC) BackupConfig() error {
+ // Check if we can load new storage layer for pool driver type.
+ pool, err := c.getStoragePool()
+ if err != storageDrivers.ErrUnknownDriver && err != storageDrivers.ErrNotImplemented {
+ if err != nil {
+ return err
+ }
+
+ return pool.BackupInstanceConfig(c, nil)
+ }
+
+ // Fallback to legacy backup function for old storage drivers.
+ return instance.WriteBackupFile(c.state, c)
+}
From 4f4dc63fb29522f4a3154ff027f82019e2263920 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 8 Jan 2020 11:43:02 +0000
Subject: [PATCH 18/20] lxd/container: Switches to inst.BackupConfig()
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/container.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lxd/container.go b/lxd/container.go
index 8aed96c556..1ad46af469 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -730,7 +730,7 @@ func instanceCreateAsSnapshot(s *state.State, args db.InstanceArgs, sourceInstan
}
// Attempt to update backup.yaml for instance.
- err = instance.WriteBackupFile(s, sourceInstance)
+ err = sourceInstance.BackupConfig()
if err != nil {
return nil, err
}
@@ -1035,7 +1035,7 @@ func instanceConfigureInternal(state *state.State, c instance.Instance) error {
return fmt.Errorf("Instance type not supported")
}
- err = instance.WriteBackupFile(state, c)
+ err = c.BackupConfig()
if err != nil {
return err
}
From 239f741f02c8b8fc4f6264a78aa9f1369fa4d02b Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 8 Jan 2020 11:43:19 +0000
Subject: [PATCH 19/20] lxd/container/lxc: Switches to inst.BackupConfig()
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/container_lxc.go | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index d978999b2a..7a83f1d99d 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -2225,7 +2225,7 @@ func (c *containerLXC) startCommon() (string, []func() error, error) {
}
// Update the backup.yaml file
- err = instance.WriteBackupFile(c.state, c)
+ err = c.BackupConfig()
if err != nil {
if ourStart {
c.unmount()
@@ -3331,7 +3331,7 @@ func (c *containerLXC) Restore(sourceContainer instance.Instance, stateful bool)
// The old backup file may be out of date (e.g. it doesn't have all the current snapshots of
// the container listed); let's write a new one to be safe.
- err = instance.WriteBackupFile(c.state, c)
+ err = c.BackupConfig()
if err != nil {
return err
}
@@ -4511,7 +4511,7 @@ func (c *containerLXC) Update(args db.InstanceArgs, userRequested bool) error {
// Only update the backup file if it already exists (indicating the instance is mounted).
if shared.PathExists(filepath.Join(c.Path(), "backup.yaml")) {
- err := instance.WriteBackupFile(c.state, c)
+ err := c.BackupConfig()
if err != nil && !os.IsNotExist(err) {
return errors.Wrap(err, "Failed to write backup file")
}
From 22e53c105df917bf4fe2712f47c297663d93a842 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 8 Jan 2020 11:43:45 +0000
Subject: [PATCH 20/20] lxd/instance/instance/utils: Removes warning and makes
critical error when yaml file creation fails
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/instance/instance_utils.go | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/lxd/instance/instance_utils.go b/lxd/instance/instance_utils.go
index 9d25e4a853..d510e2c412 100644
--- a/lxd/instance/instance_utils.go
+++ b/lxd/instance/instance_utils.go
@@ -451,7 +451,7 @@ func LoadByProjectAndName(s *state.State, project, name string) (Instance, error
return inst, nil
}
-// WriteBackupFile writes instance's config to a file.
+// WriteBackupFile writes instance's config to a file. Deprecated, use inst.BackupConfig().
func WriteBackupFile(state *state.State, inst Instance) error {
// We only write backup files out for actual instances.
if inst.IsSnapshot() {
@@ -515,12 +515,6 @@ func WriteBackupFile(state *state.State, inst Instance) error {
return err
}
- // Ensure the container is currently mounted.
- if !shared.PathExists(inst.RootfsPath()) {
- logger.Debug("Unable to update backup.yaml at this time", log.Ctx{"name": inst.Name(), "project": inst.Project()})
- return nil
- }
-
// Write the YAML
f, err := os.Create(filepath.Join(inst.Path(), "backup.yaml"))
if err != nil {
More information about the lxc-devel
mailing list