The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6476
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) === Implements CreateInstanceFromCopy and its dependencies: CreateInstanceFromMigration and MigrateInstance. Hooks CreateInstanceFromCopy into LXD API functionality for supported pool drivers.
From f05f3b32c95cdb8779542c38e9bfff74e96570ec Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:43:51 +0000 Subject: [PATCH 01/21] lxd/container: Adds operation arg to instanceCreateAsCopy Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/container.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/container.go b/lxd/container.go index 55aaed404c..62c58392d9 100644 --- a/lxd/container.go +++ b/lxd/container.go @@ -570,7 +570,7 @@ func instanceCreateFromImage(d *Daemon, args db.InstanceArgs, hash string, op *o return inst, nil } -func instanceCreateAsCopy(s *state.State, args db.InstanceArgs, sourceInst Instance, instanceOnly bool, refresh bool) (Instance, error) { +func instanceCreateAsCopy(s *state.State, args db.InstanceArgs, sourceInst Instance, instanceOnly bool, refresh bool, op *operations.Operation) (Instance, error) { var inst, revertInst Instance var err error From b67393185468a32228b19c09666dc8154975cf68 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:46:25 +0000 Subject: [PATCH 02/21] lxd/containers/post: Passes operation to instanceCreateAsCopy Signed-off-by: Thomas Parrott <thomas.parr...@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 dacc135db2..fc9b50c15b 100644 --- a/lxd/containers_post.go +++ b/lxd/containers_post.go @@ -604,7 +604,7 @@ func createFromCopy(d *Daemon, project string, req *api.InstancesPost) response. run := func(op *operations.Operation) error { instanceOnly := req.Source.InstanceOnly || req.Source.ContainerOnly - _, err := instanceCreateAsCopy(d.State(), args, source, instanceOnly, req.Source.Refresh) + _, err := instanceCreateAsCopy(d.State(), args, source, instanceOnly, req.Source.Refresh, op) if err != nil { return err } From 9f439ab952aa1199284e2044cfd4adee48bdcef8 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Tue, 19 Nov 2019 12:09:23 +0000 Subject: [PATCH 03/21] lxd/container: Links instanceCreateAsCopy to new storage pkg Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/container.go | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/lxd/container.go b/lxd/container.go index 62c58392d9..08eafd8abc 100644 --- a/lxd/container.go +++ b/lxd/container.go @@ -701,9 +701,25 @@ func instanceCreateAsCopy(s *state.State, args db.InstanceArgs, sourceInst Insta return nil, err } } else { - err = inst.Storage().ContainerCopy(inst, sourceInst, instanceOnly) - if err != nil { - return nil, err + // Check if we can load new storage layer for both target and source pool driver types. + pool, err := storagePools.GetPoolByInstance(s, inst) + _, srcPoolErr := storagePools.GetPoolByInstance(s, sourceInst) + if err != storageDrivers.ErrUnknownDriver && err != storageDrivers.ErrNotImplemented && srcPoolErr != storageDrivers.ErrUnknownDriver && srcPoolErr != storageDrivers.ErrNotImplemented { + if err != nil { + return nil, errors.Wrap(err, "Load instance storage pool") + } + + err = pool.CreateInstanceFromCopy(inst, sourceInst, !instanceOnly, op) + if err != nil { + return nil, errors.Wrap(err, "Create instance from copy") + } + } else if inst.Type() == instancetype.Container { + err = inst.Storage().ContainerCopy(inst, sourceInst, instanceOnly) + if err != nil { + return nil, err + } + } else { + return nil, fmt.Errorf("Instance type not supported") } } From 5b11204a364a250b981b18e48bf2aca3cf0edacd Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:44:20 +0000 Subject: [PATCH 04/21] lxd/container: source snapshot var naming for clarity Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/container.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lxd/container.go b/lxd/container.go index 08eafd8abc..cf5e79dc03 100644 --- a/lxd/container.go +++ b/lxd/container.go @@ -642,15 +642,15 @@ func instanceCreateAsCopy(s *state.State, args db.InstanceArgs, sourceInst Insta } } - for _, snap := range snapshots { - fields := strings.SplitN(snap.Name(), shared.SnapshotDelimiter, 2) + for _, srcSnap := range snapshots { + fields := strings.SplitN(srcSnap.Name(), shared.SnapshotDelimiter, 2) // Ensure that snapshot and parent instance have the // same storage pool in their local root disk device. // If the root disk device for the snapshot comes from a // profile on the new instance as well we don't need to // do anything. - snapDevices := snap.LocalDevices() + snapDevices := srcSnap.LocalDevices() if snapDevices != nil { snapLocalRootDiskDeviceKey, _, _ := shared.GetRootDiskDevice(snapDevices.CloneNative()) if snapLocalRootDiskDeviceKey != "" { @@ -666,15 +666,15 @@ func instanceCreateAsCopy(s *state.State, args db.InstanceArgs, sourceInst Insta newSnapName := fmt.Sprintf("%s/%s", inst.Name(), fields[1]) snapInstArgs := db.InstanceArgs{ - Architecture: snap.Architecture(), - Config: snap.LocalConfig(), + Architecture: srcSnap.Architecture(), + Config: srcSnap.LocalConfig(), Type: sourceInst.Type(), Snapshot: true, Devices: snapDevices, - Description: snap.Description(), - Ephemeral: snap.IsEphemeral(), + Description: srcSnap.Description(), + Ephemeral: srcSnap.IsEphemeral(), Name: newSnapName, - Profiles: snap.Profiles(), + Profiles: srcSnap.Profiles(), Project: args.Project, } @@ -684,8 +684,8 @@ func instanceCreateAsCopy(s *state.State, args db.InstanceArgs, sourceInst Insta return nil, err } - // Restore snapshot creation date. - err = s.Cluster.ContainerCreationUpdate(snapInst.ID(), snap.CreationDate()) + // Set snapshot creation date to that of the source snapshot. + err = s.Cluster.InstanceSnapshotCreationUpdate(snapInst.ID(), srcSnap.CreationDate()) if err != nil { return nil, err } From d55c969b7344fa81c1c721ae473d5def2de9a1f7 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:36:25 +0000 Subject: [PATCH 05/21] lxd/storage/interfaces: Exposes ExpandedDevices() on Instance interface Used for accessing the root device config when managing instance storage volumes. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/interfaces.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lxd/storage/interfaces.go b/lxd/storage/interfaces.go index b9e26a6305..1412555764 100644 --- a/lxd/storage/interfaces.go +++ b/lxd/storage/interfaces.go @@ -3,6 +3,7 @@ package storage import ( "io" + deviceConfig "github.com/lxc/lxd/lxd/device/config" "github.com/lxc/lxd/lxd/instance/instancetype" "github.com/lxc/lxd/lxd/migration" "github.com/lxc/lxd/lxd/operations" @@ -20,6 +21,7 @@ type Instance interface { IsRunning() bool IsSnapshot() bool DeferTemplateApply(trigger string) error + ExpandedDevices() deviceConfig.Devices } // Pool represents a LXD storage pool. From a2f4cda3b5ea701bbea8b0cf57b5af2db81efaa0 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:39:09 +0000 Subject: [PATCH 06/21] lxd/storage/interfaces: Updates Instance migration signatures Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/interfaces.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/storage/interfaces.go b/lxd/storage/interfaces.go index 1412555764..1c4adba620 100644 --- a/lxd/storage/interfaces.go +++ b/lxd/storage/interfaces.go @@ -45,11 +45,11 @@ type Pool interface { CreateInstanceFromBackup(i Instance, sourcePath string, op *operations.Operation) error CreateInstanceFromCopy(i Instance, src Instance, snapshots bool, op *operations.Operation) error CreateInstanceFromImage(i Instance, fingerprint string, op *operations.Operation) error - CreateInstanceFromMigration(i Instance, conn io.ReadWriteCloser, args migration.SinkArgs, op *operations.Operation) error + CreateInstanceFromMigration(i Instance, conn io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) error RenameInstance(i Instance, newName string, op *operations.Operation) error DeleteInstance(i Instance, op *operations.Operation) error - MigrateInstance(i Instance, snapshots bool, args migration.SourceArgs) (migration.StorageSourceDriver, error) + MigrateInstance(i Instance, conn io.ReadWriteCloser, args migration.VolumeSourceArgs, op *operations.Operation) error RefreshInstance(i Instance, src Instance, snapshots bool, op *operations.Operation) error BackupInstance(i Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error From 28d902588beb86b257543a821a5c2048db6553f3 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:40:18 +0000 Subject: [PATCH 07/21] lxd/storage/interfaces: Changes i arg var to inst to represent Instance We don't use "i" for instance variables as its confused with the commonly used "i" for iterator. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/interfaces.go | 42 +++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/lxd/storage/interfaces.go b/lxd/storage/interfaces.go index 1c4adba620..7632620464 100644 --- a/lxd/storage/interfaces.go +++ b/lxd/storage/interfaces.go @@ -41,32 +41,32 @@ type Pool interface { Unmount() (bool, error) // Instances. - CreateInstance(i Instance, op *operations.Operation) error - CreateInstanceFromBackup(i Instance, sourcePath string, op *operations.Operation) error - CreateInstanceFromCopy(i Instance, src Instance, snapshots bool, op *operations.Operation) error - CreateInstanceFromImage(i Instance, fingerprint string, op *operations.Operation) error - CreateInstanceFromMigration(i Instance, conn io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) error - RenameInstance(i Instance, newName string, op *operations.Operation) error - DeleteInstance(i Instance, op *operations.Operation) error + 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(i Instance, conn io.ReadWriteCloser, args migration.VolumeSourceArgs, op *operations.Operation) error - RefreshInstance(i Instance, src Instance, snapshots bool, op *operations.Operation) error - BackupInstance(i Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error + MigrateInstance(inst Instance, conn io.ReadWriteCloser, args migration.VolumeSourceArgs, op *operations.Operation) error + RefreshInstance(inst Instance, src Instance, snapshots bool, op *operations.Operation) error + BackupInstance(inst Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error - GetInstanceUsage(i Instance) (int64, error) - SetInstanceQuota(i Instance, size string, op *operations.Operation) error + GetInstanceUsage(inst Instance) (int64, error) + SetInstanceQuota(inst Instance, size string, op *operations.Operation) error - MountInstance(i Instance, op *operations.Operation) (bool, error) - UnmountInstance(i Instance, op *operations.Operation) (bool, error) - GetInstanceDisk(i Instance) (string, error) + MountInstance(inst Instance, op *operations.Operation) (bool, error) + UnmountInstance(inst Instance, op *operations.Operation) (bool, error) + GetInstanceDisk(inst Instance) (string, error) // Instance snapshots. - CreateInstanceSnapshot(i Instance, name string, op *operations.Operation) error - RenameInstanceSnapshot(i Instance, newName string, op *operations.Operation) error - DeleteInstanceSnapshot(i Instance, op *operations.Operation) error - RestoreInstanceSnapshot(i Instance, op *operations.Operation) error - MountInstanceSnapshot(i Instance, op *operations.Operation) (bool, error) - UnmountInstanceSnapshot(i Instance, op *operations.Operation) (bool, error) + CreateInstanceSnapshot(inst Instance, name string, 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) // Images. EnsureImage(fingerprint string, op *operations.Operation) error From 61b0f68e0755f6949746bd086aab38135e625826 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:41:00 +0000 Subject: [PATCH 08/21] lxd/storage/backend/mock: Updates instance migration signatures Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/backend_mock.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lxd/storage/backend_mock.go b/lxd/storage/backend_mock.go index 4fd603bd48..f87beec2ee 100644 --- a/lxd/storage/backend_mock.go +++ b/lxd/storage/backend_mock.go @@ -74,7 +74,7 @@ func (b *mockBackend) CreateInstanceFromImage(i Instance, fingerprint string, op return nil } -func (b *mockBackend) CreateInstanceFromMigration(i Instance, conn io.ReadWriteCloser, args migration.SinkArgs, op *operations.Operation) error { +func (b *mockBackend) CreateInstanceFromMigration(inst Instance, conn io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) error { return nil } @@ -86,8 +86,8 @@ func (b *mockBackend) DeleteInstance(i Instance, op *operations.Operation) error return nil } -func (b *mockBackend) MigrateInstance(i Instance, snapshots bool, args migration.SourceArgs) (migration.StorageSourceDriver, error) { - return nil, nil +func (b *mockBackend) MigrateInstance(inst Instance, conn io.ReadWriteCloser, args migration.VolumeSourceArgs, op *operations.Operation) error { + return nil } func (b *mockBackend) RefreshInstance(i Instance, src Instance, snapshots bool, op *operations.Operation) error { From 91cc70939a712aa1af049bd6fb765891f98a2ff1 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:45:49 +0000 Subject: [PATCH 09/21] lxd/container/lxc: Dont fail Delete() when removing storage fails Allows for partially created containers to be deleted. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/container_lxc.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go index 1c800c0b08..0c02c20ff9 100644 --- a/lxd/container_lxc.go +++ b/lxd/container_lxc.go @@ -3561,7 +3561,7 @@ func (c *containerLXC) Delete() error { // Remove snapshot volume and database record. err = pool.DeleteInstanceSnapshot(c, nil) if err != nil { - return err + logger.Error("Failed to delete instance snapshot volume", log.Ctx{"project": c.Project(), "instance": c.Name(), "err": err}) } } } else { @@ -3569,8 +3569,7 @@ func (c *containerLXC) Delete() error { // calling its Delete function. err := instanceDeleteSnapshots(c.state, c.Project(), c.Name()) if err != nil { - logger.Error("Failed to delete instance snapshots", log.Ctx{"project": c.Project(), "instance": c.Name(), "err": err}) - return err + logger.Error("Failed to delete instance snapshot volumes", log.Ctx{"project": c.Project(), "instance": c.Name(), "err": err}) } // Remove all backups. @@ -3590,7 +3589,7 @@ func (c *containerLXC) Delete() error { // Remove the storage volume, snapshot volumes and database records. err = pool.DeleteInstance(c, nil) if err != nil { - return err + logger.Error("Failed to delete instance volume", log.Ctx{"project": c.Project(), "instance": c.Name(), "err": err}) } } From 33eda3dbf4f10c3d711121780530dd43d931edc5 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:46:55 +0000 Subject: [PATCH 10/21] lxd/db/containers: Removes unused ContainerCreationUpdate replaces with InstanceSnapshotCreationUpdate This aims to make it clear that using the former ContainerCreationUpdate function to update creation date of instance snapshot IDs is a bug and should not be done (it will be fixed in a separate commit). Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/db/containers.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lxd/db/containers.go b/lxd/db/containers.go index 6f45b5b72c..271bb58599 100644 --- a/lxd/db/containers.go +++ b/lxd/db/containers.go @@ -973,11 +973,10 @@ func ContainerUpdate(tx *sql.Tx, id int, description string, architecture int, e return nil } -// ContainerCreationUpdate updates the cration_date field of the container -// with the given ID. -func (c *Cluster) ContainerCreationUpdate(id int, date time.Time) error { - stmt := `UPDATE instances SET creation_date=? WHERE id=?` - err := exec(c.db, stmt, date, id) +// InstanceSnapshotCreationUpdate updates the creation_date field of the instance snapshot with ID. +func (c *Cluster) InstanceSnapshotCreationUpdate(instanceID int, date time.Time) error { + stmt := `UPDATE instances_snapshots SET creation_date=? WHERE id=?` + err := exec(c.db, stmt, date, instanceID) return err } From 2f1ac2c611ebc30a265097547817e33e8620937b Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:48:28 +0000 Subject: [PATCH 11/21] lxd/migrate/storage/volumes: Fixes typo Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/migrate_storage_volumes.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/migrate_storage_volumes.go b/lxd/migrate_storage_volumes.go index 978d95ed88..90ebb3218c 100644 --- a/lxd/migrate_storage_volumes.go +++ b/lxd/migrate_storage_volumes.go @@ -145,7 +145,7 @@ func (s *migrationSourceWs) DoStorage(state *state.State, poolName string, volNa if pool != nil { migrationType, err := migration.MatchTypes(respHeader, migration.MigrationFSType_RSYNC, poolMigrationTypes) if err != nil { - logger.Errorf("Failed to neogotiate migration type: %v", err) + logger.Errorf("Failed to negotiate migration type: %v", err) s.sendControl(err) return err } From 7c2ba07760ae04046eee969f2f5f7443a7cadcac Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:48:56 +0000 Subject: [PATCH 12/21] lxd/migration/interfaces: Removes unused definitions Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/migration/interfaces.go | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 lxd/migration/interfaces.go diff --git a/lxd/migration/interfaces.go b/lxd/migration/interfaces.go deleted file mode 100644 index 7264e9ef91..0000000000 --- a/lxd/migration/interfaces.go +++ /dev/null @@ -1,12 +0,0 @@ -package migration - -// FIXME: empty stubs until we're ready to move the migration code over - -type SourceArgs struct { -} - -type SinkArgs struct { -} - -type StorageSourceDriver interface { -} From 06072d9dab2672e4fe39dddd80d73870790287a2 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:49:45 +0000 Subject: [PATCH 13/21] lxd/storage/backend/lxd: Updates CreateInstance to use root disk device config Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/backend_lxd.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index 14e72c511f..f905181ef8 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -296,7 +296,13 @@ func (b *lxdBackend) CreateInstance(inst Instance, op *operations.Operation) err contentType = drivers.ContentTypeBlock } - vol := b.newVolume(volType, contentType, project.Prefix(inst.Project(), inst.Name()), nil) + // Find the root device config for instance. + _, rootDiskConf, err := shared.GetRootDiskDevice(inst.ExpandedDevices().CloneNative()) + if err != nil { + return err + } + + vol := b.newVolume(volType, contentType, project.Prefix(inst.Project(), inst.Name()), rootDiskConf) err = b.driver.CreateVolume(vol, nil, op) if err != nil { return err From 00aac41b09226fe0490219b454bf66008450b2d7 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:50:25 +0000 Subject: [PATCH 14/21] lxd/storage/backend/lxd: Implements CreateInstanceFromCopy Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/backend_lxd.go | 142 ++++++++++++++++++++++++++++++++++++- 1 file changed, 141 insertions(+), 1 deletion(-) diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index f905181ef8..0a9a5921e4 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -326,8 +326,148 @@ func (b *lxdBackend) CreateInstanceFromBackup(inst Instance, sourcePath string, 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 { - return ErrNotImplemented + 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") + + if inst.Type() != src.Type() { + return fmt.Errorf("Instance types must match") + } + + volType, err := InstanceTypeToVolumeType(inst.Type()) + if err != nil { + return err + } + + volDBType, err := VolumeTypeToDBType(volType) + 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 + } + + // 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) + + // 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) + + revert := true + + srcPool, err := GetPoolByInstance(b.state, src) + if err != nil { + return err + } + + if b.Name() == srcPool.Name() { + logger.Debug("CreateInstanceFromCopy same-pool mode detected") + defer func() { + if !revert { + return + } + b.DeleteInstance(inst, op) + }() + + err = b.driver.CreateVolumeFromCopy(vol, srcVol, snapshots, op) + if err != nil { + return err + } + } else { + // We are copying volumes between storage pools so use migration system as it will + // be able to negotiate a common transfer method between pool types. + logger.Debug("CreateInstanceFromCopy cross-pool mode detected") + + // If we are copying snapshots, retrieve a list of snapshots from source volume. + snapshotNames := []string{} + if snapshots { + snapshots, err := VolumeSnapshotsGet(b.state, srcPool.Name(), src.Name(), volDBType) + if err != nil { + return err + } + + for _, snapshot := range snapshots { + _, snapShotName, _ := shared.ContainerGetParentAndSnapshotName(snapshot.Name) + snapshotNames = append(snapshotNames, snapShotName) + } + } + + // Use in-memory pipe pair to simulate a connection between the sender and receiver. + aEnd, bEnd := memorypipe.NewPipePair() + + // Negotiate the migration type to use. + offeredTypes := srcPool.MigrationTypes(contentType) + offerHeader := migration.TypesToHeader(offeredTypes...) + migrationType, err := migration.MatchTypes(offerHeader, migration.MigrationFSType_RSYNC, b.MigrationTypes(contentType)) + if err != nil { + return fmt.Errorf("Failed to negotiate copy migration type: %v", err) + } + + // Run sender and receiver in separate go routines to prevent deadlocks. + aEndErrCh := make(chan error, 1) + bEndErrCh := make(chan error, 1) + go func() { + err := srcPool.MigrateInstance(src, aEnd, migration.VolumeSourceArgs{ + Name: src.Name(), + Snapshots: snapshotNames, + MigrationType: migrationType, + TrackProgress: true, // Do use a progress tracker on sender. + }, op) + + aEndErrCh <- err + }() + + go func() { + err := b.CreateInstanceFromMigration(inst, bEnd, migration.VolumeTargetArgs{ + Name: inst.Name(), + Snapshots: snapshotNames, + MigrationType: migrationType, + TrackProgress: false, // Do not a progress tracker on receiver. + + }, op) + + bEndErrCh <- err + }() + + // Capture errors from the sender and receiver from their result channels. + errs := []error{} + aEndErr := <-aEndErrCh + if aEndErr != nil { + errs = append(errs, aEndErr) + } + + bEndErr := <-bEndErrCh + if bEndErr != nil { + errs = append(errs, bEndErr) + } + + if len(errs) > 0 { + return fmt.Errorf("Create instance volume from copy failed: %v", errs) + } + } + + err = b.ensureInstanceSymlink(inst.Type(), inst.Project(), inst.Name(), vol.MountPath()) + if err != nil { + return err + } + + err = inst.DeferTemplateApply("copy") + if err != nil { + return err + } + + revert = false + return nil } // imageFiller returns a function that can be used as a filler function with CreateVolume(). From 4ebad588c12a90d3318ec83c4627e1c47f339861 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:51:18 +0000 Subject: [PATCH 15/21] lxd/storage/backend/lxd: Updates CreateInstanceFromImage to use instance root disk config Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/backend_lxd.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index 0a9a5921e4..212de9a882 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -500,6 +500,11 @@ func (b *lxdBackend) CreateInstanceFromImage(inst Instance, fingerprint string, return err } + contentType := drivers.ContentTypeFS + if inst.Type() == instancetype.VM { + contentType = drivers.ContentTypeBlock + } + revert := true defer func() { if !revert { @@ -508,12 +513,13 @@ func (b *lxdBackend) CreateInstanceFromImage(inst Instance, fingerprint string, b.DeleteInstance(inst, op) }() - 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 } - vol := b.newVolume(volType, contentType, project.Prefix(inst.Project(), inst.Name()), nil) + vol := b.newVolume(volType, contentType, project.Prefix(inst.Project(), inst.Name()), 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. @@ -531,6 +537,7 @@ func (b *lxdBackend) CreateInstanceFromImage(inst Instance, fingerprint string, return err } + // No config for an image volume so set to nil. imgVol := b.newVolume(drivers.VolumeTypeImage, contentType, fingerprint, nil) err = b.driver.CreateVolumeFromCopy(vol, imgVol, false, op) if err != nil { From fe161fd0537cac33eed87f7fd829b290d34e46a1 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:51:41 +0000 Subject: [PATCH 16/21] lxd/storage/backend/lxd: Implements CreateInstanceFromMigration Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/backend_lxd.go | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index 212de9a882..89c59ab8ae 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -559,8 +559,41 @@ func (b *lxdBackend) CreateInstanceFromImage(inst Instance, fingerprint string, return nil } -func (b *lxdBackend) CreateInstanceFromMigration(inst Instance, conn io.ReadWriteCloser, args migration.SinkArgs, op *operations.Operation) error { - return ErrNotImplemented +// 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 { + logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name(), "args": args}) + logger.Debug("CreateInstanceFromMigration started") + defer logger.Debug("CreateInstanceFromMigration finished") + + 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 instance. + _, rootDiskConf, err := shared.GetRootDiskDevice(inst.ExpandedDevices().CloneNative()) + if err != nil { + return err + } + + // Override args.Name and args.Config to ensure volume is created based on instance. + args.Config = rootDiskConf + args.Name = inst.Name() + + vol := b.newVolume(volType, contentType, args.Name, args.Config) + err = b.driver.CreateVolumeFromMigration(vol, conn, args, op) + if err != nil { + conn.Close() + return err + } + + return nil } // RenameInstance renames the instance's root volume and any snapshot volumes. From 66fd38fa25580f7adf70bb2dd17cc3108fe6b751 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:51:54 +0000 Subject: [PATCH 17/21] lxd/storage/backend/lxd: Implements MigrateInstance Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/backend_lxd.go | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index 89c59ab8ae..e2f0a91bae 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -766,8 +766,37 @@ func (b *lxdBackend) DeleteInstance(inst Instance, op *operations.Operation) err return nil } -func (b *lxdBackend) MigrateInstance(inst Instance, snapshots bool, args migration.SourceArgs) (migration.StorageSourceDriver, error) { - return nil, ErrNotImplemented +// 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 { + logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name(), "args": args}) + logger.Debug("MigrateInstance started") + defer logger.Debug("MigrateInstance 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 + } + + args.Name = inst.Name() // Override args.Name to ensure instance volume is sent. + vol := b.newVolume(volType, contentType, args.Name, rootDiskConf) + err = b.driver.MigrateVolume(vol, conn, args, op) + if err != nil { + return err + } + + return nil } func (b *lxdBackend) RefreshInstance(inst Instance, src Instance, snapshots bool, op *operations.Operation) error { From 049e5b671e229dcefe750f316c00bd491b2dee39 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:52:18 +0000 Subject: [PATCH 18/21] lxd/storage/backend/lxd: Adds comment to EnsureImage explaining for volume config not needed Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/backend_lxd.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index e2f0a91bae..fc587b95c0 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -1086,9 +1086,9 @@ func (b *lxdBackend) EnsureImage(fingerprint string, op *operations.Operation) e contentType = drivers.ContentTypeBlock } - // Create the new image volume. - vol := b.newVolume(drivers.VolumeTypeImage, contentType, fingerprint, nil) - err = b.driver.CreateVolume(vol, b.imageFiller(fingerprint, op), op) + // Create the new image volume. No config for an image volume so set to nil. + imgVol := b.newVolume(drivers.VolumeTypeImage, contentType, fingerprint, nil) + err = b.driver.CreateVolume(imgVol, b.imageFiller(fingerprint, op), op) if err != nil { return err } From 0f3864d2887229913b3e00f54d0abb58e1d6395e Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:52:45 +0000 Subject: [PATCH 19/21] lxd/storage/backend/lxd: Comment consistency in CreateCustomVolumeFromCopy Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/backend_lxd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index fc587b95c0..7b4b17375e 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -1279,7 +1279,7 @@ func (b *lxdBackend) CreateCustomVolumeFromCopy(volName, desc string, config map // to negotiate a common transfer method between pool types. logger.Debug("CreateCustomVolumeFromCopy cross-pool mode detected") - // Create in-memory pipe pair to simulate a connection between the sender and receiver. + // Use in-memory pipe pair to simulate a connection between the sender and receiver. aEnd, bEnd := memorypipe.NewPipePair() // Negotiate the migration type to use. From a6956cc6cffbece99d7ecc876df132a28250537c Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:53:07 +0000 Subject: [PATCH 20/21] lxd/storage/backend/lxd: Add comment to MigrateCustomVolume explaining volume config not needed Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/backend_lxd.go | 1 + 1 file changed, 1 insertion(+) diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index 7b4b17375e..db740e13d1 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -1343,6 +1343,7 @@ func (b *lxdBackend) MigrateCustomVolume(conn io.ReadWriteCloser, args migration logger.Debug("MigrateCustomVolume started") defer logger.Debug("MigrateCustomVolume finished") + // Volume config not needed to send a volume so set to nil. vol := b.newVolume(drivers.VolumeTypeCustom, drivers.ContentTypeFS, args.Name, nil) err := b.driver.MigrateVolume(vol, conn, args, op) if err != nil { From 5d53653e88469a70d76c060b04e493cbcac8bac3 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 20 Nov 2019 14:53:33 +0000 Subject: [PATCH 21/21] lxd/storage/backend/lxd: Close migration connection on error in CreateCustomVolumeFromMigration Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/storage/backend_lxd.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index db740e13d1..83e0c9bcf4 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -1399,7 +1399,8 @@ func (b *lxdBackend) CreateCustomVolumeFromMigration(conn io.ReadWriteCloser, ar vol := b.newVolume(drivers.VolumeTypeCustom, drivers.ContentTypeFS, args.Name, args.Config) err = b.driver.CreateVolumeFromMigration(vol, conn, args, op) if err != nil { - return nil + conn.Close() + return err } revertDBVolumes = nil
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel