[lxc-devel] [lxd/master] Issue #7274 Proper Support For Moving Between Pools
vpranav5 on Github
lxc-bot at linuxcontainers.org
Sat Dec 12 06:18:26 UTC 2020
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 1015 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20201211/ef05ca24/attachment.bin>
-------------- next part --------------
From fec87d5a2386c9d7c01754f53edb420eab2d3d25 Mon Sep 17 00:00:00 2001
From: Pranav Varanasi <varanasipranav at gmail.com>
Date: Fri, 11 Dec 2020 22:08:47 -0800
Subject: [PATCH 1/2] vpranav5: Version 2, Issue #7274 Proper Support For
Moving Between Pools
---
lxd/instance_post.go | 13 +++
lxd/storage/backend_lxd.go | 211 +++++++++++++++++++++++++++++++++++++
2 files changed, 224 insertions(+)
diff --git a/lxd/instance_post.go b/lxd/instance_post.go
index 32bb5c59e9..a4252615b4 100644
--- a/lxd/instance_post.go
+++ b/lxd/instance_post.go
@@ -425,6 +425,19 @@ func containerPostClusteringMigrate(d *Daemon, c instance.Instance, oldName, new
return response.InternalError(err)
}
+
+ // Call MoveInstance function to trigger MoveInstance logic on the server side
+ op, err := dest.MoveInstance(destName, instancePost)
+ if err != nil {
+ return errors.Wrap(err, "Failed to issue move instance API request")
+ }
+
+ err = op.Wait()
+ if err != nil {
+ return errors.Wrap(err, "Move instance operation failed")
+ }
+
+
return operations.OperationResponse(op)
}
diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 048d6728bd..77a0f60288 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -3791,3 +3791,214 @@ func (b *lxdBackend) CreateCustomVolumeFromBackup(srcBackup backup.Info, srcData
revert.Success()
return nil
}
+
+
+
+
+
+
+
+// MoveInstance
+func (b *lxdBackend) MoveInstance(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(), "snapshots": snapshots})
+ logger.Debug("MoveInstance started")
+ defer logger.Debug("MoveInstance finished")
+
+ if b.Status() == api.StoragePoolStatusPending {
+ return fmt.Errorf("Specified pool is not fully created")
+ }
+
+ if inst.Type() != src.Type() {
+ return fmt.Errorf("Instance types must match")
+ }
+
+ if src.Type() == instancetype.VM && src.IsRunning() {
+ return errors.Wrap(ErrNotImplemented, "Unable to perform VM live migration")
+ }
+
+ volType, err := InstanceTypeToVolumeType(inst.Type())
+ if err != nil {
+ return err
+ }
+
+ volDBType, err := VolumeTypeToDBType(volType)
+ if err != nil {
+ return err
+ }
+
+ contentType := InstanceContentType(inst)
+
+ // Get the root disk device config.
+ rootDiskConf, err := b.instanceRootVolumeConfig(inst)
+ if err != nil {
+ return err
+ }
+
+ // b is the target storage pool to move to
+
+ // Get the volume name on storage.
+ volStorageName := project.Instance(inst.Project(), inst.Name())
+
+ // Initialise a new volume containing the root disk config supplied in the new instance.
+ vol := b.newVolume(volType, contentType, volStorageName, rootDiskConf)
+
+ if b.driver.HasVolume(vol) {
+ return fmt.Errorf("Cannot create volume, already exists on target")
+ }
+
+ // Get the src volume name on storage.
+ srcVolStorageName := project.Instance(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, srcVolStorageName, nil)
+
+ revert := revert.New()
+ defer revert.Fail()
+
+ srcPool, err := GetPoolByInstance(b.state, src)
+ if err != nil {
+ return err
+ }
+
+ // Some driver backing stores require that running instances be frozen during copy.
+ if !src.IsSnapshot() && b.driver.Info().RunningCopyFreeze && src.IsRunning() && !src.IsFrozen() {
+ err = src.Freeze()
+ if err != nil {
+ return err
+ }
+
+ defer src.Unfreeze()
+ }
+
+ revert.Add(func() { b.DeleteInstance(inst, op) })
+
+ if b.Name() == srcPool.Name() {
+ logger.Debug("MoveInstance same-pool mode detected")
+ 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("MoveInstance 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, src.Project(), srcPool.Name(), src.Name(), volDBType)
+ if err != nil {
+ return err
+ }
+
+ for _, snapshot := range snapshots {
+ _, snapShotName, _ := shared.InstanceGetParentAndSnapshotName(snapshot.Name)
+ snapshotNames = append(snapshotNames, snapShotName)
+ }
+ }
+
+ // Negotiate the migration type to use.
+ offeredTypes := srcPool.MigrationTypes(contentType, false)
+ offerHeader := migration.TypesToHeader(offeredTypes...)
+ migrationTypes, err := migration.MatchTypes(offerHeader, FallbackMigrationType(contentType), b.MigrationTypes(contentType, false))
+ if err != nil {
+ return fmt.Errorf("Failed to negotiate copy migration type: %v", err)
+ }
+
+ var srcVolumeSize int64
+
+ // For VMs, get source volume size so that target can create the volume the same size.
+ if src.Type() == instancetype.VM {
+ srcVolumeSize, err = InstanceDiskBlockSize(srcPool, src, op)
+ if err != nil {
+ return errors.Wrapf(err, "Failed getting source disk size")
+ }
+ }
+
+ ctx, cancel := context.WithCancel(context.Background())
+
+ // Use in-memory pipe pair to simulate a connection between the sender and receiver.
+ aEnd, bEnd := memorypipe.NewPipePair(ctx)
+
+ // 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: migrationTypes[0],
+ TrackProgress: true, // Do use a progress tracker on sender.
+ }, op)
+
+ if err != nil {
+ cancel()
+ }
+ aEndErrCh <- err
+ }()
+
+ go func() {
+ err := b.CreateInstanceFromMigration(inst, bEnd, migration.VolumeTargetArgs{
+ Name: inst.Name(),
+ Snapshots: snapshotNames,
+ MigrationType: migrationTypes[0],
+ VolumeSize: srcVolumeSize, // Block size setting override.
+ TrackProgress: false, // Do not use a progress tracker on receiver.
+ }, op)
+
+ if err != nil {
+ cancel()
+ }
+ 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)
+ }
+
+ cancel()
+
+ if len(errs) > 0 {
+ return fmt.Errorf("Create instance volume from copy failed: %v", errs)
+ }
+
+
+ // Update the db record and delete reference to src stroage pool
+
+ // add new storage pool entry for target
+ db.UpdateStoragePoolAfterNodeJoin(b, volDBType)
+
+ // delete reference to src storage pool
+ db.RemoveStoragePool(src.Name())
+
+
+
+ }
+
+ 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.Success()
+ return nil
+}
+
+
+
+
+
From 920e65d1e5ff994d64b23f8665ebd7e41f6d9230 Mon Sep 17 00:00:00 2001
From: Pranav Varanasi <varanasipranav at gmail.com>
Date: Fri, 11 Dec 2020 22:11:21 -0800
Subject: [PATCH 2/2] vpranav5: version 3, Issue #7274 Proper Support For
Moving Between Pools
---
lxc/move.go | 18 ++++++++++++++++++
shared/api/instance.go | 2 ++
2 files changed, 20 insertions(+)
diff --git a/lxc/move.go b/lxc/move.go
index f77bf6f222..b6fb313bac 100644
--- a/lxc/move.go
+++ b/lxc/move.go
@@ -254,6 +254,24 @@ func moveClusterInstance(conf *config.Config, sourceResource, destResource, targ
if err != nil {
return errors.Wrap(err, i18n.G("Migration operation failure"))
}
+
+
+ var poolString string
+
+ // get Pool string entry to be used with MigrateContainer
+ req := api.InstancePost{Name: destName, Migration: true, PoolValue: poolString}
+
+ // argument to migrate container should be of api.ContainerPost but how to use poolString
+ op, err := source.MigrateContainer(sourceName, req)
+ if err != nil {
+ return errors.Wrap(err, i18n.G("Migrate Container API failure"))
+ }
+
+ err = op.Wait()
+ if err != nil {
+ return errors.Wrap(err, i18n.G("Migrate Container operation failure"))
+ }
+
return nil
}
diff --git a/shared/api/instance.go b/shared/api/instance.go
index cec6b4a258..3e18056818 100644
--- a/shared/api/instance.go
+++ b/shared/api/instance.go
@@ -38,8 +38,10 @@ type InstancePost struct {
InstanceOnly bool `json:"instance_only" yaml:"instance_only"`
ContainerOnly bool `json:"container_only" yaml:"container_only"` // Deprecated, use InstanceOnly.
Target *InstancePostTarget `json:"target" yaml:"target"`
+ PoolValue string `json:"pool_value" yaml:"pool_value"`
}
+
// InstancePostTarget represents the migration target host and operation.
//
// API extension: instances
More information about the lxc-devel
mailing list