The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6511
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === Includes https://github.com/lxc/lxd/pull/6510
From 5530061122c032c3f0f3b0ba95e3890229fd38fe Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 26 Nov 2019 10:19:18 +0000 Subject: [PATCH 01/16] lxd/storage: Renames interfaces.go to pool_interface.go Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/{interfaces.go => pool_interface.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lxd/storage/{interfaces.go => pool_interface.go} (100%) diff --git a/lxd/storage/interfaces.go b/lxd/storage/pool_interface.go similarity index 100% rename from lxd/storage/interfaces.go rename to lxd/storage/pool_interface.go From b6c98a2fc835e554607c3f0060c387748352fa16 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 26 Nov 2019 10:19:41 +0000 Subject: [PATCH 02/16] lxd/storage/pool/interface: Removes Instance interface Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/pool_interface.go | 54 +++++++++++++---------------------- 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/lxd/storage/pool_interface.go b/lxd/storage/pool_interface.go index 11695abe4b..ca821cc4a9 100644 --- a/lxd/storage/pool_interface.go +++ b/lxd/storage/pool_interface.go @@ -3,9 +3,7 @@ package storage import ( "io" - deviceConfig "github.com/lxc/lxd/lxd/device/config" "github.com/lxc/lxd/lxd/instance" - "github.com/lxc/lxd/lxd/instance/instancetype" "github.com/lxc/lxd/lxd/migration" "github.com/lxc/lxd/lxd/operations" "github.com/lxc/lxd/lxd/state" @@ -13,18 +11,6 @@ import ( "github.com/lxc/lxd/shared/api" ) -// Instance represents the storage relevant subset of a LXD instance. -type Instance interface { - Name() string - Project() string - Type() instancetype.Type - - IsRunning() bool - IsSnapshot() bool - DeferTemplateApply(trigger string) error - ExpandedDevices() deviceConfig.Devices -} - // Pool represents a LXD storage pool. type Pool interface { // Internal. @@ -42,32 +28,32 @@ type Pool interface { Unmount() (bool, error) // Instances. - CreateInstance(inst Instance, op *operations.Operation) error - CreateInstanceFromBackup(inst Instance, sourcePath string, op *operations.Operation) error - CreateInstanceFromCopy(inst Instance, src Instance, snapshots bool, op *operations.Operation) error - CreateInstanceFromImage(inst Instance, fingerprint string, op *operations.Operation) error - CreateInstanceFromMigration(inst Instance, conn io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) error - RenameInstance(inst Instance, newName string, op *operations.Operation) error - DeleteInstance(inst Instance, op *operations.Operation) error - - MigrateInstance(inst Instance, conn io.ReadWriteCloser, args migration.VolumeSourceArgs, op *operations.Operation) error + CreateInstance(inst instance.Instance, op *operations.Operation) error + CreateInstanceFromBackup(inst instance.Instance, sourcePath string, op *operations.Operation) error + CreateInstanceFromCopy(inst instance.Instance, src instance.Instance, snapshots bool, op *operations.Operation) error + CreateInstanceFromImage(inst instance.Instance, fingerprint string, op *operations.Operation) error + CreateInstanceFromMigration(inst instance.Instance, conn io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) error + RenameInstance(inst instance.Instance, newName string, op *operations.Operation) error + DeleteInstance(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 - BackupInstance(inst Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error + BackupInstance(inst instance.Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error - GetInstanceUsage(inst Instance) (int64, error) - SetInstanceQuota(inst Instance, size string, op *operations.Operation) error + GetInstanceUsage(inst instance.Instance) (int64, error) + SetInstanceQuota(inst instance.Instance, size string, op *operations.Operation) error - MountInstance(inst Instance, op *operations.Operation) (bool, error) - UnmountInstance(inst Instance, op *operations.Operation) (bool, error) - GetInstanceDisk(inst Instance) (string, error) + MountInstance(inst instance.Instance, op *operations.Operation) (bool, error) + UnmountInstance(inst instance.Instance, op *operations.Operation) (bool, error) + GetInstanceDisk(inst instance.Instance) (string, error) // Instance snapshots. CreateInstanceSnapshot(inst instance.Instance, src instance.Instance, op *operations.Operation) error - RenameInstanceSnapshot(inst Instance, newName string, op *operations.Operation) error - DeleteInstanceSnapshot(inst Instance, op *operations.Operation) error - RestoreInstanceSnapshot(inst Instance, op *operations.Operation) error - MountInstanceSnapshot(inst Instance, op *operations.Operation) (bool, error) - UnmountInstanceSnapshot(inst Instance, op *operations.Operation) (bool, error) + RenameInstanceSnapshot(inst instance.Instance, newName string, op *operations.Operation) error + DeleteInstanceSnapshot(inst instance.Instance, op *operations.Operation) error + RestoreInstanceSnapshot(inst instance.Instance, op *operations.Operation) error + MountInstanceSnapshot(inst instance.Instance, op *operations.Operation) (bool, error) + UnmountInstanceSnapshot(inst instance.Instance, op *operations.Operation) (bool, error) // Images. EnsureImage(fingerprint string, op *operations.Operation) error From 4762e9c4cdb64b2fdbb2ba5a4d9c24d9c6752185 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 26 Nov 2019 10:20:03 +0000 Subject: [PATCH 03/16] lxd/storage: Switches to use instance.Instance interface Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/backend_lxd.go | 38 ++++++++++++++++++------------------- lxd/storage/backend_mock.go | 38 ++++++++++++++++++------------------- lxd/storage/load.go | 3 ++- 3 files changed, 40 insertions(+), 39 deletions(-) diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index 76f85c0e90..ac8ea670a3 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -278,7 +278,7 @@ func (b *lxdBackend) removeInstanceSnapshotSymlinkIfUnused(instanceType instance } // CreateInstance creates an empty instance. -func (b *lxdBackend) CreateInstance(inst Instance, op *operations.Operation) error { +func (b *lxdBackend) CreateInstance(inst instance.Instance, op *operations.Operation) error { logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()}) logger.Debug("CreateInstance started") defer logger.Debug("CreateInstance finished") @@ -327,12 +327,12 @@ func (b *lxdBackend) CreateInstance(inst Instance, op *operations.Operation) err return nil } -func (b *lxdBackend) CreateInstanceFromBackup(inst Instance, sourcePath string, op *operations.Operation) error { +func (b *lxdBackend) CreateInstanceFromBackup(inst instance.Instance, sourcePath string, op *operations.Operation) error { return ErrNotImplemented } // CreateInstanceFromCopy copies an instance volume and optionally its snapshots to new volume(s). -func (b *lxdBackend) CreateInstanceFromCopy(inst Instance, src Instance, snapshots bool, op *operations.Operation) error { +func (b *lxdBackend) CreateInstanceFromCopy(inst instance.Instance, src instance.Instance, snapshots bool, op *operations.Operation) error { logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name(), "src": src.Name(), "snapshots": snapshots}) logger.Debug("CreateInstanceFromCopy started") defer logger.Debug("CreateInstanceFromCopy finished") @@ -632,7 +632,7 @@ func (b *lxdBackend) imageFiller(fingerprint string, op *operations.Operation) f } // CreateInstanceFromImage creates a new volume for an instance populated with the image requested. -func (b *lxdBackend) CreateInstanceFromImage(inst Instance, fingerprint string, op *operations.Operation) error { +func (b *lxdBackend) CreateInstanceFromImage(inst instance.Instance, fingerprint string, op *operations.Operation) error { logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()}) logger.Debug("CreateInstanceFromImage started") defer logger.Debug("CreateInstanceFromImage finished") @@ -703,7 +703,7 @@ func (b *lxdBackend) CreateInstanceFromImage(inst Instance, fingerprint string, // CreateInstanceFromMigration receives an instance being migrated. // The args.Name and args.Config fields are ignored and, instance properties are used instead. -func (b *lxdBackend) CreateInstanceFromMigration(inst Instance, conn io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) error { +func (b *lxdBackend) CreateInstanceFromMigration(inst instance.Instance, conn io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) error { logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name(), "args": args}) logger.Debug("CreateInstanceFromMigration started") defer logger.Debug("CreateInstanceFromMigration finished") @@ -746,7 +746,7 @@ func (b *lxdBackend) CreateInstanceFromMigration(inst Instance, conn io.ReadWrit } // RenameInstance renames the instance's root volume and any snapshot volumes. -func (b *lxdBackend) RenameInstance(inst Instance, newName string, op *operations.Operation) error { +func (b *lxdBackend) RenameInstance(inst instance.Instance, newName string, op *operations.Operation) error { logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name(), "newName": newName}) logger.Debug("RenameInstance started") defer logger.Debug("RenameInstance finished") @@ -853,7 +853,7 @@ func (b *lxdBackend) RenameInstance(inst Instance, newName string, op *operation } // DeleteInstance removes the instance's root volume (all snapshots need to be removed first). -func (b *lxdBackend) DeleteInstance(inst Instance, op *operations.Operation) error { +func (b *lxdBackend) DeleteInstance(inst instance.Instance, op *operations.Operation) error { logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()}) logger.Debug("DeleteInstance started") defer logger.Debug("DeleteInstance finished") @@ -917,7 +917,7 @@ func (b *lxdBackend) DeleteInstance(inst Instance, op *operations.Operation) err // MigrateInstance sends an instance volume for migration. // The args.Name field is ignored and the name of the instance is used instead. -func (b *lxdBackend) MigrateInstance(inst Instance, conn io.ReadWriteCloser, args migration.VolumeSourceArgs, op *operations.Operation) error { +func (b *lxdBackend) MigrateInstance(inst instance.Instance, conn io.ReadWriteCloser, args migration.VolumeSourceArgs, op *operations.Operation) error { logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name(), "args": args}) logger.Debug("MigrateInstance started") defer logger.Debug("MigrateInstance finished") @@ -948,12 +948,12 @@ func (b *lxdBackend) MigrateInstance(inst Instance, conn io.ReadWriteCloser, arg return nil } -func (b *lxdBackend) BackupInstance(inst Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error { +func (b *lxdBackend) BackupInstance(inst instance.Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error { return ErrNotImplemented } // GetInstanceUsage returns the disk usage of the instance's root volume. -func (b *lxdBackend) GetInstanceUsage(inst Instance) (int64, error) { +func (b *lxdBackend) GetInstanceUsage(inst instance.Instance) (int64, error) { logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()}) logger.Debug("GetInstanceUsage started") defer logger.Debug("GetInstanceUsage finished") @@ -968,7 +968,7 @@ func (b *lxdBackend) GetInstanceUsage(inst Instance) (int64, error) { // SetInstanceQuota sets the quota on the instance's root volume. // Returns ErrRunningQuotaResizeNotSupported if the instance is running and the storage driver // doesn't support resizing whilst the instance is running. -func (b *lxdBackend) SetInstanceQuota(inst Instance, size string, op *operations.Operation) error { +func (b *lxdBackend) SetInstanceQuota(inst instance.Instance, size string, op *operations.Operation) error { logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()}) logger.Debug("SetInstanceQuota started") defer logger.Debug("SetInstanceQuota finished") @@ -990,7 +990,7 @@ func (b *lxdBackend) SetInstanceQuota(inst Instance, size string, op *operations } // MountInstance mounts the instance's root volume. -func (b *lxdBackend) MountInstance(inst Instance, op *operations.Operation) (bool, error) { +func (b *lxdBackend) MountInstance(inst instance.Instance, op *operations.Operation) (bool, error) { logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()}) logger.Debug("MountInstance started") defer logger.Debug("MountInstance finished") @@ -1008,7 +1008,7 @@ func (b *lxdBackend) MountInstance(inst Instance, op *operations.Operation) (boo } // UnmountInstance unmounts the instance's root volume. -func (b *lxdBackend) UnmountInstance(inst Instance, op *operations.Operation) (bool, error) { +func (b *lxdBackend) UnmountInstance(inst instance.Instance, op *operations.Operation) (bool, error) { logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()}) logger.Debug("UnmountInstance started") defer logger.Debug("UnmountInstance finished") @@ -1026,7 +1026,7 @@ func (b *lxdBackend) UnmountInstance(inst Instance, op *operations.Operation) (b } // GetInstanceDisk returns the location of the disk. -func (b *lxdBackend) GetInstanceDisk(inst Instance) (string, error) { +func (b *lxdBackend) GetInstanceDisk(inst instance.Instance) (string, error) { if inst.Type() != instancetype.VM { return "", ErrNotImplemented } @@ -1104,7 +1104,7 @@ func (b *lxdBackend) CreateInstanceSnapshot(inst instance.Instance, src instance } // RenameInstanceSnapshot renames an instance snapshot. -func (b *lxdBackend) RenameInstanceSnapshot(inst Instance, newName string, op *operations.Operation) error { +func (b *lxdBackend) RenameInstanceSnapshot(inst instance.Instance, newName string, op *operations.Operation) error { logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name(), "newName": newName}) logger.Debug("RenameInstanceSnapshot started") defer logger.Debug("RenameInstanceSnapshot finished") @@ -1152,7 +1152,7 @@ func (b *lxdBackend) RenameInstanceSnapshot(inst Instance, newName string, op *o } // DeleteInstanceSnapshot removes the snapshot volume for the supplied snapshot instance. -func (b *lxdBackend) DeleteInstanceSnapshot(inst Instance, op *operations.Operation) error { +func (b *lxdBackend) DeleteInstanceSnapshot(inst instance.Instance, op *operations.Operation) error { logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()}) logger.Debug("DeleteInstanceSnapshot started") defer logger.Debug("DeleteInstanceSnapshot finished") @@ -1199,13 +1199,13 @@ func (b *lxdBackend) DeleteInstanceSnapshot(inst Instance, op *operations.Operat return nil } -func (b *lxdBackend) RestoreInstanceSnapshot(inst Instance, op *operations.Operation) error { +func (b *lxdBackend) RestoreInstanceSnapshot(inst instance.Instance, op *operations.Operation) error { return ErrNotImplemented } // MountInstanceSnapshot mounts an instance snapshot. It is mounted as read only so that the // snapshot cannot be modified. -func (b *lxdBackend) MountInstanceSnapshot(inst Instance, op *operations.Operation) (bool, error) { +func (b *lxdBackend) MountInstanceSnapshot(inst instance.Instance, op *operations.Operation) (bool, error) { logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()}) logger.Debug("MountInstanceSnapshot started") defer logger.Debug("MountInstanceSnapshot finished") @@ -1230,7 +1230,7 @@ func (b *lxdBackend) MountInstanceSnapshot(inst Instance, op *operations.Operati } // UnmountInstanceSnapshot unmounts an instance snapshot. -func (b *lxdBackend) UnmountInstanceSnapshot(inst Instance, op *operations.Operation) (bool, error) { +func (b *lxdBackend) UnmountInstanceSnapshot(inst instance.Instance, op *operations.Operation) (bool, error) { logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()}) logger.Debug("UnmountInstanceSnapshot started") defer logger.Debug("UnmountInstanceSnapshot finished") diff --git a/lxd/storage/backend_mock.go b/lxd/storage/backend_mock.go index c8e14c1aa1..3fb98dec58 100644 --- a/lxd/storage/backend_mock.go +++ b/lxd/storage/backend_mock.go @@ -59,35 +59,35 @@ func (b *mockBackend) Unmount() (bool, error) { return true, nil } -func (b *mockBackend) CreateInstance(i Instance, op *operations.Operation) error { +func (b *mockBackend) CreateInstance(inst instance.Instance, op *operations.Operation) error { return nil } -func (b *mockBackend) CreateInstanceFromBackup(i Instance, sourcePath string, op *operations.Operation) error { +func (b *mockBackend) CreateInstanceFromBackup(inst instance.Instance, sourcePath string, op *operations.Operation) error { return nil } -func (b *mockBackend) CreateInstanceFromCopy(i Instance, src Instance, snapshots bool, op *operations.Operation) error { +func (b *mockBackend) CreateInstanceFromCopy(inst instance.Instance, src instance.Instance, snapshots bool, op *operations.Operation) error { return nil } -func (b *mockBackend) CreateInstanceFromImage(i Instance, fingerprint string, op *operations.Operation) error { +func (b *mockBackend) CreateInstanceFromImage(inst instance.Instance, fingerprint string, op *operations.Operation) error { return nil } -func (b *mockBackend) CreateInstanceFromMigration(inst Instance, conn io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) error { +func (b *mockBackend) CreateInstanceFromMigration(inst instance.Instance, conn io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) error { return nil } -func (b *mockBackend) RenameInstance(i Instance, newName string, op *operations.Operation) error { +func (b *mockBackend) RenameInstance(inst instance.Instance, newName string, op *operations.Operation) error { return nil } -func (b *mockBackend) DeleteInstance(i Instance, op *operations.Operation) error { +func (b *mockBackend) DeleteInstance(inst instance.Instance, op *operations.Operation) error { return nil } -func (b *mockBackend) MigrateInstance(inst Instance, conn io.ReadWriteCloser, args migration.VolumeSourceArgs, op *operations.Operation) error { +func (b *mockBackend) MigrateInstance(inst instance.Instance, conn io.ReadWriteCloser, args migration.VolumeSourceArgs, op *operations.Operation) error { return nil } @@ -95,27 +95,27 @@ func (b *mockBackend) RefreshInstance(i instance.Instance, src instance.Instance return nil } -func (b *mockBackend) BackupInstance(i Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error { +func (b *mockBackend) BackupInstance(inst instance.Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error { return nil } -func (b *mockBackend) GetInstanceUsage(i Instance) (int64, error) { +func (b *mockBackend) GetInstanceUsage(inst instance.Instance) (int64, error) { return 0, nil } -func (b *mockBackend) SetInstanceQuota(i Instance, size string, op *operations.Operation) error { +func (b *mockBackend) SetInstanceQuota(inst instance.Instance, size string, op *operations.Operation) error { return nil } -func (b *mockBackend) MountInstance(i Instance, op *operations.Operation) (bool, error) { +func (b *mockBackend) MountInstance(inst instance.Instance, op *operations.Operation) (bool, error) { return true, nil } -func (b *mockBackend) UnmountInstance(i Instance, op *operations.Operation) (bool, error) { +func (b *mockBackend) UnmountInstance(inst instance.Instance, op *operations.Operation) (bool, error) { return true, nil } -func (b *mockBackend) GetInstanceDisk(i Instance) (string, error) { +func (b *mockBackend) GetInstanceDisk(inst instance.Instance) (string, error) { return "", nil } @@ -123,23 +123,23 @@ func (b *mockBackend) CreateInstanceSnapshot(i instance.Instance, src instance.I return nil } -func (b *mockBackend) RenameInstanceSnapshot(i Instance, newName string, op *operations.Operation) error { +func (b *mockBackend) RenameInstanceSnapshot(inst instance.Instance, newName string, op *operations.Operation) error { return nil } -func (b *mockBackend) DeleteInstanceSnapshot(i Instance, op *operations.Operation) error { +func (b *mockBackend) DeleteInstanceSnapshot(inst instance.Instance, op *operations.Operation) error { return nil } -func (b *mockBackend) RestoreInstanceSnapshot(i Instance, op *operations.Operation) error { +func (b *mockBackend) RestoreInstanceSnapshot(inst instance.Instance, op *operations.Operation) error { return nil } -func (b *mockBackend) MountInstanceSnapshot(i Instance, op *operations.Operation) (bool, error) { +func (b *mockBackend) MountInstanceSnapshot(inst instance.Instance, op *operations.Operation) (bool, error) { return true, nil } -func (b *mockBackend) UnmountInstanceSnapshot(i Instance, op *operations.Operation) (bool, error) { +func (b *mockBackend) UnmountInstanceSnapshot(inst instance.Instance, op *operations.Operation) (bool, error) { return true, nil } diff --git a/lxd/storage/load.go b/lxd/storage/load.go index bdad09351b..c530bcf9cd 100644 --- a/lxd/storage/load.go +++ b/lxd/storage/load.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/lxc/lxd/lxd/db" + "github.com/lxc/lxd/lxd/instance" "github.com/lxc/lxd/lxd/operations" "github.com/lxc/lxd/lxd/state" "github.com/lxc/lxd/lxd/storage/drivers" @@ -151,7 +152,7 @@ func GetPoolByName(state *state.State, name string) (Pool, error) { // GetPoolByInstance retrieves the pool from the database using the instance's pool. // If the pool's driver is not recognised then drivers.ErrUnknownDriver is returned. If the pool's // driver does not support the instance's type then drivers.ErrNotImplemented is returned. -func GetPoolByInstance(s *state.State, inst Instance) (Pool, error) { +func GetPoolByInstance(s *state.State, inst instance.Instance) (Pool, error) { poolName, err := s.Cluster.InstancePool(inst.Project(), inst.Name()) if err != nil { return nil, err From 9b80beed14fb4b67c61ee844150d6589ef857db9 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 26 Nov 2019 09:15:45 +0000 Subject: [PATCH 04/16] lxd/container/put: Renames containerSnapRestore to instanceSnapRestore Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/container_put.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lxd/container_put.go b/lxd/container_put.go index de99565ace..0e221afb13 100644 --- a/lxd/container_put.go +++ b/lxd/container_put.go @@ -92,7 +92,7 @@ func containerPut(d *Daemon, r *http.Request) response.Response { } else { // Snapshot Restore do = func(op *operations.Operation) error { - return containerSnapRestore(d.State(), project, name, configRaw.Restore, configRaw.Stateful) + return instanceSnapRestore(d.State(), project, name, configRaw.Restore, configRaw.Stateful) } opType = db.OperationSnapshotRestore @@ -109,13 +109,13 @@ func containerPut(d *Daemon, r *http.Request) response.Response { return operations.OperationResponse(op) } -func containerSnapRestore(s *state.State, project, name, snap string, stateful bool) error { +func instanceSnapRestore(s *state.State, project, name, snap string, stateful bool) error { // normalize snapshot name if !shared.IsSnapshot(snap) { snap = name + shared.SnapshotDelimiter + snap } - c, err := instanceLoadByProjectAndName(s, project, name) + inst, err := instanceLoadByProjectAndName(s, project, name) if err != nil { return err } @@ -130,7 +130,7 @@ func containerSnapRestore(s *state.State, project, name, snap string, stateful b } } - err = c.Restore(source, stateful) + err = inst.Restore(source, stateful) if err != nil { return err } From a249a3100af6eb96ddf4fc9349a8a244311c9478 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 26 Nov 2019 09:36:56 +0000 Subject: [PATCH 05/16] lxd/container/lxc: Links snapshot Restore() to new storage pkg Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/container_lxc.go | 106 +++++++++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 39 deletions(-) diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go index c93b7c22bf..4a8a41a203 100644 --- a/lxd/container_lxc.go +++ b/lxd/container_lxc.go @@ -3316,26 +3316,41 @@ func (c *containerLXC) Backups() ([]backup.Backup, error) { return backups, nil } +// Restore restores a snapshot. func (c *containerLXC) Restore(sourceContainer instance.Instance, stateful bool) error { var ctxMap log.Ctx - // Initialize storage interface for the container. - err := c.initStorage() - if err != nil { - return err - } + // Initialize storage interface for the container and mount the rootfs for criu state check. + // Check if we can load new storage layer for pool driver type. + pool, err := storagePools.GetPoolByInstance(c.state, c) + if err != storageDrivers.ErrUnknownDriver && err != storageDrivers.ErrNotImplemented { + if err != nil { + return err + } - ourStart, err := c.StorageStart() - if err != nil { - return err - } - if ourStart { - defer c.StorageStop() + ourStart, err := pool.MountInstance(c, nil) + if err != nil { + return err + } + if ourStart { + defer pool.UnmountInstance(c, nil) + } + } else { + err = c.initStorage() + if err != nil { + return err + } + + ourStart, err := c.StorageStart() + if err != nil { + return err + } + if ourStart { + defer c.StorageStop() + } } - /* let's also check for CRIU if necessary, before doing a bunch of - * filesystem manipulations - */ + // Check for CRIU if necessary, before doing a bunch of filesystem manipulations. if shared.PathExists(c.StatePath()) { _, err := exec.LookPath("criu") if err != nil { @@ -3343,14 +3358,14 @@ func (c *containerLXC) Restore(sourceContainer instance.Instance, stateful bool) } } - // Stop the container + // Stop the container. wasRunning := false if c.IsRunning() { wasRunning = true ephemeral := c.IsEphemeral() if ephemeral { - // Unset ephemeral flag + // Unset ephemeral flag. args := db.InstanceArgs{ Architecture: c.Architecture(), Config: c.LocalConfig(), @@ -3368,7 +3383,7 @@ func (c *containerLXC) Restore(sourceContainer instance.Instance, stateful bool) return err } - // On function return, set the flag back on + // On function return, set the flag back on. defer func() { args.Ephemeral = ephemeral c.Update(args, true) @@ -3382,12 +3397,22 @@ func (c *containerLXC) Restore(sourceContainer instance.Instance, stateful bool) } // Ensure that storage is mounted for state path checks. - ourStart, err := c.StorageStart() - if err != nil { - return err - } - if ourStart { - defer c.StorageStop() + if pool != nil { + ourStart, err := pool.MountInstance(c, nil) + if err != nil { + return err + } + if ourStart { + defer pool.UnmountInstance(c, nil) + } + } else { + ourStart, err := c.StorageStart() + if err != nil { + return err + } + if ourStart { + defer c.StorageStop() + } } } @@ -3401,14 +3426,21 @@ func (c *containerLXC) Restore(sourceContainer instance.Instance, stateful bool) logger.Info("Restoring container", ctxMap) - // Restore the rootfs - err = c.storage.ContainerRestore(c, sourceContainer) - if err != nil { - logger.Error("Failed restoring container filesystem", ctxMap) - return err + // Restore the rootfs. + if pool != nil { + err = pool.RestoreInstanceSnapshot(c, sourceContainer, nil) + if err != nil { + return err + } + } else { + err = c.storage.ContainerRestore(c, sourceContainer) + if err != nil { + logger.Error("Failed restoring container filesystem", ctxMap) + return err + } } - // Restore the configuration + // Restore the configuration. args := db.InstanceArgs{ Architecture: sourceContainer.Architecture(), Config: sourceContainer.LocalConfig(), @@ -3427,16 +3459,14 @@ func (c *containerLXC) Restore(sourceContainer instance.Instance, stateful bool) return err } - // 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. + // 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 = writeBackupFile(c) if err != nil { return err } - // If the container wasn't running but was stateful, should we restore - // it as running? + // If the container wasn't running but was stateful, should we restore it as running? if stateful == true { if !shared.PathExists(c.StatePath()) { return fmt.Errorf("Stateful snapshot restore requested by snapshot is stateless") @@ -3455,14 +3485,13 @@ func (c *containerLXC) Restore(sourceContainer instance.Instance, stateful bool) preDumpDir: "", } - // Checkpoint + // Checkpoint. err := c.Migrate(&criuMigrationArgs) if err != nil { return err } - // Remove the state from the parent container; we only keep - // this in snapshots. + // Remove the state from the parent container; we only keep this in snapshots. err2 := os.RemoveAll(c.StatePath()) if err2 != nil { logger.Error("Failed to delete snapshot state", log.Ctx{"path": c.StatePath(), "err": err2}) @@ -3483,14 +3512,13 @@ func (c *containerLXC) Restore(sourceContainer instance.Instance, stateful bool) "snapshot_name": c.name, }) - // Restart the container + // Restart the container. if wasRunning { logger.Info("Restored container", ctxMap) return c.Start(false) } logger.Info("Restored container", ctxMap) - return nil } From 8c9b1439f0c80930aff5ad3ba37f4a1b9bd80ad7 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 26 Nov 2019 09:37:27 +0000 Subject: [PATCH 06/16] lxd/storage/interfaces: RestoreInstanceSnapshot signature Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/pool_interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/storage/pool_interface.go b/lxd/storage/pool_interface.go index ca821cc4a9..e00cbcc24f 100644 --- a/lxd/storage/pool_interface.go +++ b/lxd/storage/pool_interface.go @@ -51,7 +51,7 @@ type Pool interface { CreateInstanceSnapshot(inst instance.Instance, src instance.Instance, op *operations.Operation) error RenameInstanceSnapshot(inst instance.Instance, newName string, op *operations.Operation) error DeleteInstanceSnapshot(inst instance.Instance, op *operations.Operation) error - RestoreInstanceSnapshot(inst instance.Instance, op *operations.Operation) error + RestoreInstanceSnapshot(inst instance.Instance, src instance.Instance, op *operations.Operation) error MountInstanceSnapshot(inst instance.Instance, op *operations.Operation) (bool, error) UnmountInstanceSnapshot(inst instance.Instance, op *operations.Operation) (bool, error) From 4ee7fee451566eaafe68e171c17e78bf66a4f3dc Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 26 Nov 2019 09:37:48 +0000 Subject: [PATCH 07/16] lxd/storage/backend/mock: RestoreInstanceSnapshot signature Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/backend_mock.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/storage/backend_mock.go b/lxd/storage/backend_mock.go index 3fb98dec58..cb3a464b71 100644 --- a/lxd/storage/backend_mock.go +++ b/lxd/storage/backend_mock.go @@ -131,7 +131,7 @@ func (b *mockBackend) DeleteInstanceSnapshot(inst instance.Instance, op *operati return nil } -func (b *mockBackend) RestoreInstanceSnapshot(inst instance.Instance, op *operations.Operation) error { +func (b *mockBackend) RestoreInstanceSnapshot(inst instance.Instance, src instance.Instance, op *operations.Operation) error { return nil } From 47a7e123e85bbd17afd3b5b4175cff10b9e56619 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 26 Nov 2019 10:14:04 +0000 Subject: [PATCH 08/16] lxd/storage/backend/lxd: Implements RestoreInstanceSnapshot Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/backend_lxd.go | 62 +++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index ac8ea670a3..7693bf90dc 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -1060,11 +1060,11 @@ func (b *lxdBackend) CreateInstanceSnapshot(inst instance.Instance, src instance } if !inst.IsSnapshot() { - return fmt.Errorf("Instance must be snapshot") + return fmt.Errorf("Instance must be a snapshot") } if src.IsSnapshot() { - return fmt.Errorf("Source instance cannot be snapshot") + return fmt.Errorf("Source instance cannot be a snapshot") } // Check we can convert the instance to the volume type needed. @@ -1199,8 +1199,62 @@ func (b *lxdBackend) DeleteInstanceSnapshot(inst instance.Instance, op *operatio return nil } -func (b *lxdBackend) RestoreInstanceSnapshot(inst instance.Instance, op *operations.Operation) error { - return ErrNotImplemented +// RestoreInstanceSnapshot restores an instance snapshot. +func (b *lxdBackend) RestoreInstanceSnapshot(inst instance.Instance, src instance.Instance, op *operations.Operation) error { + logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name(), "src": src.Name()}) + logger.Debug("RestoreInstanceSnapshot started") + defer logger.Debug("RestoreInstanceSnapshot finished") + + if inst.Type() != src.Type() { + return fmt.Errorf("Instance types must match") + } + + if inst.IsSnapshot() { + return fmt.Errorf("Instance must not be snapshot") + } + + if !src.IsSnapshot() { + return fmt.Errorf("Source instance must be a snapshot") + } + + // Target instance must not be running. + if inst.IsRunning() { + return fmt.Errorf("Instance must not be running to restore") + } + + // Check we can convert the instance to the volume type needed. + volType, err := InstanceTypeToVolumeType(inst.Type()) + if err != nil { + return err + } + + contentType := drivers.ContentTypeFS + if inst.Type() == instancetype.VM { + contentType = drivers.ContentTypeBlock + } + + // Find the root device config for source snapshot instance. + _, rootDiskConf, err := shared.GetRootDiskDevice(src.ExpandedDevices().CloneNative()) + if err != nil { + return err + } + + // Get the volume name on storage. + volStorageName := project.Prefix(inst.Project(), inst.Name()) + + _, snapshotName, isSnap := shared.InstanceGetParentAndSnapshotName(src.Name()) + if !isSnap { + return fmt.Errorf("Volume name must be a snapshot") + } + + // Use the source snapshot's rootfs config (as this will later be restored into inst too). + vol := b.newVolume(volType, contentType, volStorageName, rootDiskConf) + err = b.driver.RestoreVolume(vol, snapshotName, op) + if err != nil { + return err + } + + return nil } // MountInstanceSnapshot mounts an instance snapshot. It is mounted as read only so that the From f988bc1b94ad1715c98ec191c23ae3809f423d9e Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 26 Nov 2019 11:18:14 +0000 Subject: [PATCH 09/16] lxd/container/backup: Comment tweaks and inst var rename Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/container_backup.go | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/lxd/container_backup.go b/lxd/container_backup.go index f8ab231f82..d8cc1f1a53 100644 --- a/lxd/container_backup.go +++ b/lxd/container_backup.go @@ -81,7 +81,7 @@ func containerBackupsPost(d *Daemon, r *http.Request) response.Response { project := projectParam(r) name := mux.Vars(r)["name"] - // Handle requests targeted to a container on a different node + // Handle requests targeted to a container on a different node. resp, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType) if err != nil { return response.SmartError(err) @@ -90,7 +90,7 @@ func containerBackupsPost(d *Daemon, r *http.Request) response.Response { return resp } - c, err := instanceLoadByProjectAndName(d.State(), project, name) + inst, err := instanceLoadByProjectAndName(d.State(), project, name) if err != nil { return response.SmartError(err) } @@ -103,11 +103,11 @@ func containerBackupsPost(d *Daemon, r *http.Request) response.Response { expiry, _ := rj.GetString("expires_at") if expiry == "" { - // Disable expiration by setting it to zero time + // Disable expiration by setting it to zero time. rj["expires_at"] = time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC) } - // Create body with correct expiry + // Create body with correct expiry. body, err := json.Marshal(rj) if err != nil { return response.InternalError(err) @@ -121,8 +121,8 @@ func containerBackupsPost(d *Daemon, r *http.Request) response.Response { } if req.Name == "" { - // come up with a name - backups, err := c.Backups() + // come up with a name. + backups, err := inst.Backups() if err != nil { return response.BadRequest(err) } @@ -132,7 +132,7 @@ func containerBackupsPost(d *Daemon, r *http.Request) response.Response { max := 0 for _, backup := range backups { - // Ignore backups not containing base + // Ignore backups not containing base. if !strings.HasPrefix(backup.Name(), base) { continue } @@ -151,7 +151,7 @@ func containerBackupsPost(d *Daemon, r *http.Request) response.Response { req.Name = fmt.Sprintf("backup%d", max) } - // Validate the name + // Validate the name. if strings.Contains(req.Name, "/") { return response.BadRequest(fmt.Errorf("Backup names may not contain slashes")) } @@ -162,7 +162,7 @@ func containerBackupsPost(d *Daemon, r *http.Request) response.Response { backup := func(op *operations.Operation) error { args := db.InstanceBackupArgs{ Name: fullName, - InstanceID: c.ID(), + InstanceID: inst.ID(), CreationDate: time.Now(), ExpiryDate: req.ExpiresAt, InstanceOnly: instanceOnly, @@ -170,7 +170,7 @@ func containerBackupsPost(d *Daemon, r *http.Request) response.Response { CompressionAlgorithm: req.CompressionAlgorithm, } - err := backupCreate(d.State(), args, c) + err := backupCreate(d.State(), args, inst) if err != nil { return errors.Wrap(err, "Create backup") } @@ -179,7 +179,8 @@ func containerBackupsPost(d *Daemon, r *http.Request) response.Response { } resources := map[string][]string{} - resources["containers"] = []string{name} + resources["instances"] = []string{name} + resources["containers"] = resources["instances"] resources["backups"] = []string{req.Name} op, err := operations.OperationCreate(d.State(), project, operations.OperationClassTask, From c7c4f757824dbf4367faa892e8bbda9cb9a0d83d Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 26 Nov 2019 11:45:28 +0000 Subject: [PATCH 10/16] lxd/backup: Links backupCreate to new storage pkg Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/backup.go | 71 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/lxd/backup.go b/lxd/backup.go index b9557fd5b2..be3b24511b 100644 --- a/lxd/backup.go +++ b/lxd/backup.go @@ -19,6 +19,8 @@ import ( "github.com/lxc/lxd/lxd/operations" "github.com/lxc/lxd/lxd/project" "github.com/lxc/lxd/lxd/state" + storagePools "github.com/lxc/lxd/lxd/storage" + storageDrivers "github.com/lxc/lxd/lxd/storage/drivers" "github.com/lxc/lxd/lxd/task" "github.com/lxc/lxd/shared" log "github.com/lxc/lxd/shared/log15" @@ -26,9 +28,9 @@ import ( "github.com/pkg/errors" ) -// Create a new backup -func backupCreate(s *state.State, args db.InstanceBackupArgs, sourceContainer instance.Instance) error { - // Create the database entry +// Create a new backup. +func backupCreate(s *state.State, args db.InstanceBackupArgs, sourceInst instance.Instance) error { + // Create the database entry. err := s.Cluster.ContainerBackupCreate(args) if err != nil { if err == db.ErrAlreadyDefined { @@ -38,44 +40,65 @@ func backupCreate(s *state.State, args db.InstanceBackupArgs, sourceContainer in return errors.Wrap(err, "Insert backup info into database") } - // Get the backup struct - b, err := backup.LoadByName(s, sourceContainer.Project(), args.Name) + revert := true + defer func() { + if !revert { + return + } + s.Cluster.ContainerBackupRemove(args.Name) + }() + + // Get the backup struct. + b, err := backup.LoadByName(s, sourceInst.Project(), args.Name) if err != nil { return errors.Wrap(err, "Load backup object") } b.SetCompressionAlgorithm(args.CompressionAlgorithm) - ourStart, err := sourceContainer.StorageStart() - if err != nil { - return err - } - if ourStart { - defer sourceContainer.StorageStop() - } - - // Create a temporary path for the backup + // Create a temporary path for the backup. tmpPath, err := ioutil.TempDir(shared.VarPath("backups"), "lxd_backup_") if err != nil { return err } defer os.RemoveAll(tmpPath) - if sourceContainer.Type() != instancetype.Container { - return fmt.Errorf("Instance type must be container") - } + pool, err := storagePools.GetPoolByInstance(s, sourceInst) + if err != storageDrivers.ErrUnknownDriver && err != storageDrivers.ErrNotImplemented { + if err != nil { + return errors.Wrap(err, "Load instance storage pool") + } - ct := sourceContainer.(*containerLXC) + err = pool.BackupInstance(sourceInst, tmpPath, b.OptimizedStorage(), !b.InstanceOnly(), nil) + if err != nil { + return errors.Wrap(err, "Backup create") + } + } else if sourceInst.Type() == instancetype.Container { + ourStart, err := sourceInst.StorageStart() + if err != nil { + return err + } + if ourStart { + defer sourceInst.StorageStop() + } + + ct := sourceInst.(*containerLXC) + err = ct.Storage().ContainerBackupCreate(tmpPath, *b, sourceInst) + if err != nil { + return errors.Wrap(err, "Backup create") + } + } else { + return fmt.Errorf("Instance type not supported") + } - // Now create the empty snapshot - err = ct.Storage().ContainerBackupCreate(tmpPath, *b, sourceContainer) + // Pack the backup. + err = backupCreateTarball(s, tmpPath, *b, sourceInst) if err != nil { - s.Cluster.ContainerBackupRemove(args.Name) - return errors.Wrap(err, "Backup storage") + return err } - // Pack the backup - return backupCreateTarball(s, tmpPath, *b, sourceContainer) + revert = false + return nil } // fixBackupStoragePool changes the pool information in the backup.yaml. This From 948a978d95a9da85c8e143ec0927caa00191990e Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 26 Nov 2019 13:24:16 +0000 Subject: [PATCH 11/16] lxd/storage/drivers/driver/dir: rsync.LocalCopy return value consistency Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_dir.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/storage/drivers/driver_dir.go b/lxd/storage/drivers/driver_dir.go index cc6da99bc8..fcc02a0807 100644 --- a/lxd/storage/drivers/driver_dir.go +++ b/lxd/storage/drivers/driver_dir.go @@ -657,9 +657,9 @@ func (d *dir) RestoreVolume(vol Volume, snapshotName string, op *operations.Oper // Restore using rsync. bwlimit := d.config["rsync.bwlimit"] - output, err := rsync.LocalCopy(srcPath, volPath, bwlimit, true) + _, err := rsync.LocalCopy(srcPath, volPath, bwlimit, true) if err != nil { - return fmt.Errorf("Failed to rsync volume: %s: %s", string(output), err) + return fmt.Errorf("Failed to rsync volume: %s", err) } return nil From 5de417189b639b2523bc027ff2280f1a02b97fcd Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 26 Nov 2019 13:25:33 +0000 Subject: [PATCH 12/16] lxd/storage/backend/lxd: Ensures all instance functions use project aware storage names Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/backend_lxd.go | 46 ++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index 7693bf90dc..bbb4fefa88 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -307,7 +307,10 @@ func (b *lxdBackend) CreateInstance(inst instance.Instance, op *operations.Opera return err } - vol := b.newVolume(volType, contentType, project.Prefix(inst.Project(), inst.Name()), rootDiskConf) + // Get the volume name on storage. + volStorageName := project.Prefix(inst.Project(), inst.Name()) + + vol := b.newVolume(volType, contentType, volStorageName, rootDiskConf) err = b.driver.CreateVolume(vol, nil, op) if err != nil { return err @@ -366,11 +369,17 @@ func (b *lxdBackend) CreateInstanceFromCopy(inst instance.Instance, src instance return err } + // Get the volume name on storage. + volStorageName := project.Prefix(inst.Project(), inst.Name()) + // Initialise a new volume containing the root disk config supplied in the new instance. - vol := b.newVolume(volType, contentType, project.Prefix(inst.Project(), inst.Name()), rootDiskConf) + vol := b.newVolume(volType, contentType, volStorageName, rootDiskConf) + + // Get the src volume name on storage. + srcVolStorageName := project.Prefix(src.Project(), src.Name()) // We don't need to use the source instance's root disk config, so set to nil. - srcVol := b.newVolume(volType, contentType, project.Prefix(src.Project(), src.Name()), nil) + srcVol := b.newVolume(volType, contentType, srcVolStorageName, nil) revert := true defer func() { @@ -505,11 +514,17 @@ func (b *lxdBackend) RefreshInstance(inst instance.Instance, src instance.Instan return err } + // Get the volume name on storage. + volStorageName := project.Prefix(inst.Project(), inst.Name()) + // Initialise a new volume containing the root disk config supplied in the new instance. - vol := b.newVolume(volType, contentType, project.Prefix(inst.Project(), inst.Name()), rootDiskConf) + vol := b.newVolume(volType, contentType, volStorageName, rootDiskConf) + + // Get the src volume name on storage. + srcVolStorageName := project.Prefix(src.Project(), src.Name()) // We don't need to use the source instance's root disk config, so set to nil. - srcVol := b.newVolume(volType, contentType, project.Prefix(src.Project(), src.Name()), nil) + srcVol := b.newVolume(volType, contentType, srcVolStorageName, nil) srcSnapVols := []drivers.Volume{} for _, snapInst := range srcSnapshots { @@ -518,7 +533,10 @@ func (b *lxdBackend) RefreshInstance(inst instance.Instance, src instance.Instan // disk config, so set to nil. This is because snapshots are immutable yet // the instance and its snapshots can be transferred between pools, so using // the data from the snapshot is incorrect. - srcSnapVol := b.newVolume(volType, contentType, project.Prefix(snapInst.Project(), snapInst.Name()), nil) + + // Get the snap volume name on storage. + snapVolStorageName := project.Prefix(snapInst.Project(), snapInst.Name()) + srcSnapVol := b.newVolume(volType, contentType, snapVolStorageName, nil) srcSnapVols = append(srcSnapVols, srcSnapVol) } @@ -661,7 +679,10 @@ func (b *lxdBackend) CreateInstanceFromImage(inst instance.Instance, fingerprint return err } - vol := b.newVolume(volType, contentType, project.Prefix(inst.Project(), inst.Name()), rootDiskConf) + // Get the volume name on storage. + volStorageName := project.Prefix(inst.Project(), inst.Name()) + + vol := b.newVolume(volType, contentType, volStorageName, rootDiskConf) // If the driver doesn't support optimized image volumes then create a new empty volume and // populate it with the contents of the image archive. @@ -735,7 +756,10 @@ func (b *lxdBackend) CreateInstanceFromMigration(inst instance.Instance, conn io args.Config = rootDiskConf args.Name = inst.Name() - vol := b.newVolume(volType, contentType, args.Name, args.Config) + // Get the volume name on storage. + volStorageName := project.Prefix(inst.Project(), args.Name) + + vol := b.newVolume(volType, contentType, volStorageName, args.Config) err = b.driver.CreateVolumeFromMigration(vol, conn, args, op) if err != nil { conn.Close() @@ -939,7 +963,11 @@ func (b *lxdBackend) MigrateInstance(inst instance.Instance, conn io.ReadWriteCl } args.Name = inst.Name() // Override args.Name to ensure instance volume is sent. - vol := b.newVolume(volType, contentType, args.Name, rootDiskConf) + + // Get the volume name on storage. + volStorageName := project.Prefix(inst.Project(), args.Name) + + vol := b.newVolume(volType, contentType, volStorageName, rootDiskConf) err = b.driver.MigrateVolume(vol, conn, args, op) if err != nil { return err From 122850516a92e549a982c02047a805cc02bd9687 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 26 Nov 2019 11:59:38 +0000 Subject: [PATCH 13/16] lxd/storage/backend/lxd: Implements BackupInstance Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/backend_lxd.go | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index bbb4fefa88..1a904bc203 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -976,8 +976,38 @@ func (b *lxdBackend) MigrateInstance(inst instance.Instance, conn io.ReadWriteCl return nil } +// BackupInstance creates an instance backup. func (b *lxdBackend) BackupInstance(inst instance.Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error { - return ErrNotImplemented + logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name(), "targetPath": targetPath, "optimized": optimized, "snapshots": snapshots}) + logger.Debug("BackupInstance started") + defer logger.Debug("BackupInstance finished") + + volType, err := InstanceTypeToVolumeType(inst.Type()) + if err != nil { + return err + } + + contentType := drivers.ContentTypeFS + if inst.Type() == instancetype.VM { + contentType = drivers.ContentTypeBlock + } + + // Get the root disk device config. + _, rootDiskConf, err := shared.GetRootDiskDevice(inst.ExpandedDevices().CloneNative()) + if err != nil { + return err + } + + // Get the volume name on storage. + volStorageName := project.Prefix(inst.Project(), inst.Name()) + + vol := b.newVolume(volType, contentType, volStorageName, rootDiskConf) + err = b.driver.BackupVolume(vol, targetPath, optimized, snapshots, op) + if err != nil { + return err + } + + return nil } // GetInstanceUsage returns the disk usage of the instance's root volume. From cab161f0e47adc02db09119c2ade06780e2d5959 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 26 Nov 2019 13:23:01 +0000 Subject: [PATCH 14/16] lxd/storage/drivers/interface: Adds BackupVolume Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/interface.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lxd/storage/drivers/interface.go b/lxd/storage/drivers/interface.go index 064a707936..11d8343f05 100644 --- a/lxd/storage/drivers/interface.go +++ b/lxd/storage/drivers/interface.go @@ -69,4 +69,7 @@ type Driver interface { MigrationTypes(contentType ContentType) []migration.Type MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs migration.VolumeSourceArgs, op *operations.Operation) error CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, op *operations.Operation) error + + // Backup. + BackupVolume(vol Volume, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error } From e9444bf236557e35416769c2642ec3789a8c00b1 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 26 Nov 2019 13:23:16 +0000 Subject: [PATCH 15/16] lxd/storage/drivers/driver/cephfs: Adds BackupVolume placeholder Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_cephfs.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lxd/storage/drivers/driver_cephfs.go b/lxd/storage/drivers/driver_cephfs.go index b3622e86ce..98b593cd80 100644 --- a/lxd/storage/drivers/driver_cephfs.go +++ b/lxd/storage/drivers/driver_cephfs.go @@ -987,3 +987,7 @@ func (d *cephfs) getConfig(clusterName string, userName string) ([]string, strin return cephMon, cephSecret, nil } + +func (d *cephfs) BackupVolume(vol Volume, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error { + return ErrNotImplemented +} From df97224bb12d7635f3dda61506211a7c727c31d2 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 26 Nov 2019 13:23:57 +0000 Subject: [PATCH 16/16] lxd/storage/drivers/driver/dir: Adds BackupVolume Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/drivers/driver_dir.go | 52 +++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/lxd/storage/drivers/driver_dir.go b/lxd/storage/drivers/driver_dir.go index fcc02a0807..66c85d9ce7 100644 --- a/lxd/storage/drivers/driver_dir.go +++ b/lxd/storage/drivers/driver_dir.go @@ -893,3 +893,55 @@ func (d *dir) RenameVolumeSnapshot(volType VolumeType, volName string, snapshotN return nil } + +// BackupVolume copies a volume (and optionally its snapshots) to a specified target path. +// This driver does not support optimized backups. +func (d *dir) BackupVolume(vol Volume, targetPath string, _, snapshots bool, op *operations.Operation) error { + bwlimit := d.config["rsync.bwlimit"] + + var parentVolDir string + + // Backups only implemented for containers currently. + if vol.volType == VolumeTypeContainer { + parentVolDir = "container" + } else { + return ErrNotImplemented + } + + // Handle snapshots. + if snapshots { + snapshotsPath := filepath.Join(targetPath, "snapshots") + snapshots, err := vol.Snapshots(op) + if err != nil { + return err + } + + // Create the snapshot path. + if len(snapshots) > 0 { + err = os.MkdirAll(snapshotsPath, 0711) + if err != nil { + return err + } + } + + for _, snap := range snapshots { + _, snapName, _ := shared.InstanceGetParentAndSnapshotName(snap.Name()) + target := filepath.Join(snapshotsPath, snapName) + + // Copy the snapshot. + _, err := rsync.LocalCopy(snap.MountPath(), target, bwlimit, true) + if err != nil { + return fmt.Errorf("Failed to rsync: %s", err) + } + } + } + + // Copy the parent volume itself. + target := filepath.Join(targetPath, parentVolDir) + _, err := rsync.LocalCopy(vol.MountPath(), target, bwlimit, true) + if err != nil { + return fmt.Errorf("Failed to rsync: %s", err) + } + + return nil +}
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel