The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6547

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) ===
This is a WIP that links the new storage layer for instance migration source.
From 37fbfa8f7c502c69380a80812dfa90bce529765d Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 3 Dec 2019 15:40:21 +0000
Subject: [PATCH 01/11] lxd/storage/backend/lxd: Comment typos

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/backend_lxd.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 18840f0008..a347fb62e8 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -842,8 +842,8 @@ func (b *lxdBackend) CreateInstanceFromMigration(inst 
instance.Instance, conn io
                }()
 
                // If the negotiated migration method is rsync and the 
instance's base image is
-               // already on the host then pre-create the instance's volume 
using the locla image
-               // to try and speed up the rsync of the incoming volume by 
avoiding the new to
+               // already on the host then pre-create the instance's volume 
using the local image
+               // to try and speed up the rsync of the incoming volume by 
avoiding the need to
                // transfer the base image files too.
                if args.MigrationType.FSType == migration.MigrationFSType_RSYNC 
{
                        fingerprint := 
inst.ExpandedConfig()["volatile.base_image"]

From 33be0f9b99db85d3ecb081059616d12f8662c217 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 4 Dec 2019 08:56:06 +0000
Subject: [PATCH 02/11] lxd/storage/drivers/drive/dir: Add support for 2-phase
 migration

Adds support for a final rootfs volume sync stage which is sometimes requested 
by sender.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/drivers/driver_dir.go | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/lxd/storage/drivers/driver_dir.go 
b/lxd/storage/drivers/driver_dir.go
index 2a6b83c6b3..7d8a0563bd 100644
--- a/lxd/storage/drivers/driver_dir.go
+++ b/lxd/storage/drivers/driver_dir.go
@@ -431,7 +431,24 @@ func (d *dir) CreateVolumeFromMigration(vol Volume, conn 
io.ReadWriteCloser, vol
                        wrapper = migration.ProgressTracker(op, "fs_progress", 
vol.name)
                }
 
-               return rsync.Recv(path, conn, wrapper, 
volTargetArgs.MigrationType.Features)
+               err = rsync.Recv(path, conn, wrapper, 
volTargetArgs.MigrationType.Features)
+               if err != nil {
+                       return err
+               }
+
+               // Receive the final main volume sync if needed.
+               if volTargetArgs.Live {
+                       if volTargetArgs.TrackProgress {
+                               wrapper = migration.ProgressTracker(op, 
"fs_progress", vol.name)
+                       }
+
+                       err = rsync.Recv(path, conn, wrapper, 
volTargetArgs.MigrationType.Features)
+                       if err != nil {
+                               return err
+                       }
+               }
+
+               return nil
        }, op)
        if err != nil {
                return err

From c9f000ad7828f419b7b95fc45e0318be60311795 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 4 Dec 2019 08:57:06 +0000
Subject: [PATCH 03/11] lxd/migration/migration/volumes: Adds Live property to
 VolumeTargetArgs

Allows source node to indicate to target node that a 2-phase volume sync is 
needed.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/migration/migration_volumes.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lxd/migration/migration_volumes.go 
b/lxd/migration/migration_volumes.go
index 18b329d1a9..391a0e8353 100644
--- a/lxd/migration/migration_volumes.go
+++ b/lxd/migration/migration_volumes.go
@@ -34,6 +34,7 @@ type VolumeTargetArgs struct {
        MigrationType Type
        TrackProgress bool
        Refresh       bool
+       Live          bool
 }
 
 // TypesToHeader converts one or more Types to a MigrationHeader. It uses the 
first type argument

From c3661764266e2fb0aa12a9c7c71aaa19c436f3b3 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 4 Dec 2019 09:00:48 +0000
Subject: [PATCH 04/11] lxd/migrate/container: Add support for 2-phase sync in
 migrationSink.Do()

This fixes a bug where the criu property was not being populated in the 
migration response header.

This field is leveraged in the source to indicate whether the instance is 
running and whether a 2-phase sync is needed so its important this is sent back 
even if criu not being used.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/migrate_container.go | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/lxd/migrate_container.go b/lxd/migrate_container.go
index 9c8f5787bf..b33dcd37f0 100644
--- a/lxd/migrate_container.go
+++ b/lxd/migrate_container.go
@@ -893,6 +893,7 @@ func (c *migrationSink) Do(state *state.State, migrateOp 
*operations.Operation)
                                MigrationType: respType,
                                Refresh:       args.Refresh, // Indicate to 
receiver volume should exist.
                                TrackProgress: false,        // Do not use a 
progress tracker on receiver.
+                               Live:          args.Live,    // Indicates we 
will get a final rootfs sync.
                        }
 
                        // At this point we have already figured out the parent 
container's root
@@ -950,7 +951,6 @@ func (c *migrationSink) Do(state *state.State, migrateOp 
*operations.Operation)
 
                respHeader = migration.MigrationHeader{
                        Fs:            &myType,
-                       Criu:          criuType,
                        Snapshots:     offerHeader.Snapshots,
                        SnapshotNames: offerHeader.SnapshotNames,
                        Refresh:       &c.refresh,
@@ -985,6 +985,9 @@ func (c *migrationSink) Do(state *state.State, migrateOp 
*operations.Operation)
                return fmt.Errorf("Instance type not supported")
        }
 
+       // Add CRIU info to response.
+       respHeader.Criu = criuType
+
        if c.refresh {
                // Get our existing snapshots.
                targetSnapshots, err := c.src.instance.Snapshots()
@@ -1078,7 +1081,12 @@ func (c *migrationSink) Do(state *state.State, migrateOp 
*operations.Operation)
                                fsConn = c.src.fsConn
                        }
 
+                       // Default to not expecting to receive the final rootfs 
sync.
                        sendFinalFsDelta := false
+
+                       // If we are doing a stateful live transfer or the CRIU 
type indicates we
+                       // are doing a stateless transfer with a running 
instance then we should
+                       // expect the source to send us a final rootfs sync.
                        if live {
                                sendFinalFsDelta = true
                        }

From a5782c43723b934e536bf32cc6baaf84fc46463f Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 4 Dec 2019 13:35:56 +0000
Subject: [PATCH 05/11] lxd/migrate/container: Sends refresh request indicator
 in migration response header

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/migrate_container.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lxd/migrate_container.go b/lxd/migrate_container.go
index b33dcd37f0..ec94023b63 100644
--- a/lxd/migrate_container.go
+++ b/lxd/migrate_container.go
@@ -884,6 +884,7 @@ func (c *migrationSink) Do(state *state.State, migrateOp 
*operations.Operation)
                respHeader = migration.TypesToHeader(respType)
                respHeader.SnapshotNames = offerHeader.SnapshotNames
                respHeader.Snapshots = offerHeader.Snapshots
+               respHeader.Refresh = &c.refresh
 
                // Translate the legacy MigrationSinkArgs to a VolumeTargetArgs 
suitable for use
                // with the new storage layer.
@@ -910,7 +911,7 @@ func (c *migrationSink) Do(state *state.State, migrateOp 
*operations.Operation)
                        }
 
                        // A zero length Snapshots slice indicates volume only 
migration in
-                       // VolumeTargetArgs. So if VoluneOnly was requested, do 
not populate them.
+                       // VolumeTargetArgs. So if VolumeOnly was requested, do 
not populate them.
                        if !args.VolumeOnly {
                                volTargetArgs.Snapshots = make([]string, 0, 
len(args.Snapshots))
                                for _, snap := range args.Snapshots {

From 1c8622a1d035702eadde7d59db2a9a45b5ce00d2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 4 Dec 2019 13:34:18 +0000
Subject: [PATCH 06/11] lxd/rsync/rsync: Adds more info to error returned in
 sendSetup

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/rsync/rsync.go | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/lxd/rsync/rsync.go b/lxd/rsync/rsync.go
index d5d0ee1320..716a8db525 100644
--- a/lxd/rsync/rsync.go
+++ b/lxd/rsync/rsync.go
@@ -164,15 +164,17 @@ func sendSetup(name string, path string, bwlimit string, 
execPath string, featur
        select {
        case conn = <-chConn:
                if conn == nil {
+                       output, _ := ioutil.ReadAll(stderr)
                        cmd.Process.Kill()
                        cmd.Wait()
-                       return nil, nil, nil, fmt.Errorf("Failed to connect to 
rsync socket")
+                       return nil, nil, nil, fmt.Errorf("Failed to connect to 
rsync socket (%s)", string(output))
                }
 
        case <-time.After(10 * time.Second):
+               output, _ := ioutil.ReadAll(stderr)
                cmd.Process.Kill()
                cmd.Wait()
-               return nil, nil, nil, fmt.Errorf("rsync failed to spawn after 
10s")
+               return nil, nil, nil, fmt.Errorf("rsync failed to spawn after 
10s (%s)", string(output))
        }
 
        return cmd, *conn, stderr, nil

From 96ddbf92b735bb2a25d654fe107aa2c3bd5a483a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 3 Dec 2019 14:54:40 +0000
Subject: [PATCH 07/11] lxd/storage/drivers: Adds Config() function to return
 read-only copy of pool config

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/drivers/driver_common.go | 10 ++++++++++
 lxd/storage/drivers/interface.go     |  1 +
 2 files changed, 11 insertions(+)

diff --git a/lxd/storage/drivers/driver_common.go 
b/lxd/storage/drivers/driver_common.go
index c83225cefb..224f35209d 100644
--- a/lxd/storage/drivers/driver_common.go
+++ b/lxd/storage/drivers/driver_common.go
@@ -93,3 +93,13 @@ func (d *common) MigrationTypes(contentType ContentType) 
[]migration.Type {
                },
        }
 }
+
+// Config returns the storage pool config (as a copy, so not modifiable).
+func (d *common) Config() map[string]string {
+       confCopy := make(map[string]string, len(d.config))
+       for k, v := range d.config {
+               confCopy[k] = v
+       }
+
+       return confCopy
+}
diff --git a/lxd/storage/drivers/interface.go b/lxd/storage/drivers/interface.go
index 09082f128f..8297b2d7b9 100644
--- a/lxd/storage/drivers/interface.go
+++ b/lxd/storage/drivers/interface.go
@@ -21,6 +21,7 @@ type driver interface {
 // Driver represents a low-level storage driver.
 type Driver interface {
        // Internal.
+       Config() map[string]string
        Info() Info
        HasVolume(volType VolumeType, volName string) bool
 

From 7c76a4cf07d638d62a329a552a8fb66a3ae15cdc Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 4 Dec 2019 08:58:37 +0000
Subject: [PATCH 08/11] lxd/container/post: Minor cleanup and instance info
 output in containerPost

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container_post.go | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/lxd/container_post.go b/lxd/container_post.go
index f97254c430..6f8d82343c 100644
--- a/lxd/container_post.go
+++ b/lxd/container_post.go
@@ -84,7 +84,7 @@ func containerPost(d *Daemon, r *http.Request) 
response.Response {
                                return errors.Wrap(err, "Failed to get address 
of container's node")
                        }
                        if address == "" {
-                               // Local node
+                               // Local node.
                                sourceNodeOffline = false
                                return nil
                        }
@@ -129,7 +129,7 @@ func containerPost(d *Daemon, r *http.Request) 
response.Response {
        // Cases 1. and 2. are the ones for which the conditional will be true
        // and we'll either forward the request or load the container.
        if targetNode == "" || !sourceNodeOffline {
-               // 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)
@@ -164,7 +164,7 @@ func containerPost(d *Daemon, r *http.Request) 
response.Response {
                return response.BadRequest(err)
        }
 
-       // Check if stateful (backward compatibility)
+       // Check if stateful (backward compatibility).
        stateful := true
        _, err = reqRaw.GetBool("live")
        if err == nil {
@@ -234,7 +234,7 @@ func containerPost(d *Daemon, r *http.Request) 
response.Response {
                        return operations.OperationResponse(op)
                }
 
-               // Pull mode
+               // Pull mode.
                op, err := operations.OperationCreate(d.State(), project, 
operations.OperationClassWebsocket, db.OperationContainerMigrate, resources, 
ws.Metadata(), run, nil, ws.Connect)
                if err != nil {
                        return response.InternalError(err)
@@ -243,7 +243,7 @@ func containerPost(d *Daemon, r *http.Request) 
response.Response {
                return operations.OperationResponse(op)
        }
 
-       // Check that the name isn't already in use
+       // Check that the name isn't already in use.
        id, _ := d.cluster.ContainerID(project, req.Name)
        if id > 0 {
                return response.Conflict(fmt.Errorf("Name '%s' already in use", 
req.Name))
@@ -254,7 +254,8 @@ func containerPost(d *Daemon, r *http.Request) 
response.Response {
        }
 
        resources := map[string][]string{}
-       resources["containers"] = []string{name}
+       resources["instances"] = []string{name}
+       resources["containers"] = resources["instances"]
 
        op, err := operations.OperationCreate(d.State(), project, 
operations.OperationClassTask, db.OperationContainerRename, resources, nil, 
run, nil, nil)
        if err != nil {

From 93869cb9bde0a5d926e89f65f0a8e18c4b61a09e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 4 Dec 2019 14:27:53 +0000
Subject: [PATCH 09/11] lxd/migrate/container: Links migrationSourceWs.Do to
 new storage pkg

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/migrate_container.go | 158 +++++++++++++++++++++++++++------------
 1 file changed, 112 insertions(+), 46 deletions(-)

diff --git a/lxd/migrate_container.go b/lxd/migrate_container.go
index ec94023b63..7f019c2e29 100644
--- a/lxd/migrate_container.go
+++ b/lxd/migrate_container.go
@@ -341,9 +341,27 @@ func (s *migrationSourceWs) Do(state *state.State, 
migrateOp *operations.Operati
        ct := s.instance.(*containerLXC)
 
        var offerHeader migration.MigrationHeader
+       var poolMigrationTypes []migration.Type
+
+       // Check if we can load new storage layer for pool driver type.
+       pool, err := storagePools.GetPoolByInstance(state, s.instance)
+       if err != storageDrivers.ErrUnknownDriver && err != db.ErrNoSuchObject {
+               if err != nil {
+                       return err
+               }
+
+               poolMigrationTypes = 
pool.MigrationTypes(storagePools.InstanceContentType(s.instance))
+               if len(poolMigrationTypes) < 0 {
+                       return fmt.Errorf("No source migration types available")
+               }
+
+               // Convert the pool's migration type options to an offer header 
to target.
+               // Populate the Fs, ZfsFeatures and RsyncFeatures fields.
+               offerHeader = migration.TypesToHeader(poolMigrationTypes...)
+       } else if s.instance.Type() == instancetype.Container {
+               // Fallback to legacy storage layer and populate the Fs, 
ZfsFeatures and
+               // RsyncFeatures fields.
 
-       var pool storagePools.Pool // Placeholder for new storage pool.
-       if s.instance.Type() == instancetype.Container {
                // Storage needs to start unconditionally now, since we need to 
initialize a new
                // storage interface.
                ourStart, err := s.instance.StorageStart()
@@ -424,13 +442,13 @@ func (s *migrationSourceWs) Do(state *state.State, 
migrateOp *operations.Operati
        offerHeader.Snapshots = snapshots
 
        // Add predump info to source header.
-       usePreDumps := false
+       offerUsePreDumps := false
        maxDumpIterations := 0
        if s.live {
-               usePreDumps, maxDumpIterations = s.checkForPreDumpSupport()
+               offerUsePreDumps, maxDumpIterations = s.checkForPreDumpSupport()
        }
 
-       offerHeader.Predump = proto.Bool(usePreDumps)
+       offerHeader.Predump = proto.Bool(offerUsePreDumps)
 
        // Send offer to target.
        err = s.send(&offerHeader)
@@ -448,8 +466,9 @@ func (s *migrationSourceWs) Do(state *state.State, 
migrateOp *operations.Operati
        }
 
        var legacyDriver MigrationStorageSourceDriver
-       var abort func(err error) error
-       var bwlimit string
+       var legacyCleanup func()         // Called after migration, to remove 
any temporary snapshots, etc.
+       var migrationType migration.Type // Negotiated migration type.
+       var rsyncBwlimit string          // Used for CRIU state and legacy 
storage rsync transfers.
 
        // Handle rsync options.
        rsyncFeatures := respHeader.GetRsyncFeaturesSlice()
@@ -459,7 +478,50 @@ func (s *migrationSourceWs) Do(state *state.State, 
migrateOp *operations.Operati
                rsyncFeatures = []string{"xattrs", "delete", "compress"}
        }
 
-       if pool == nil {
+       // All failure paths need to do a few things to correctly handle errors 
before returning.
+       // Unfortunately, handling errors is not well-suited to defer as the 
code depends on the
+       // status of driver and the error value. The error value is especially 
tricky due to the
+       // common case of creating a new err variable (intentional or not) due 
to scoping and use
+       // of ":=".  Capturing err in a closure for use in defer would be 
fragile, which defeats
+       // the purpose of using defer. An abort function reduces the odds of 
mishandling errors
+       // without introducing the fragility of closing on err.
+       abort := func(err error) error {
+               if legacyCleanup != nil {
+                       legacyCleanup()
+               }
+
+               go s.sendControl(err)
+               return err
+       }
+
+       if pool != nil {
+               rsyncBwlimit = pool.Driver().Config()["rsync.bwlimit"]
+               migrationType, err = migration.MatchTypes(respHeader, 
migration.MigrationFSType_RSYNC, poolMigrationTypes)
+               if err != nil {
+                       logger.Errorf("Failed to negotiate migration type: %v", 
err)
+                       return abort(err)
+               }
+
+               sendSnapshotNames := snapshotNames
+
+               // If we are in refresh mode, only send the snapshots the 
target has asked for.
+               if respHeader.GetRefresh() {
+                       sendSnapshotNames = respHeader.GetSnapshotNames()
+               }
+
+               volSourceArgs := migration.VolumeSourceArgs{
+                       Name:          s.instance.Name(),
+                       MigrationType: migrationType,
+                       Snapshots:     sendSnapshotNames,
+                       TrackProgress: true,
+                       FinalSync:     false,
+               }
+
+               err = pool.MigrateInstance(s.instance, 
&shared.WebsocketIO{Conn: s.fsConn}, volSourceArgs, migrateOp)
+               if err != nil {
+                       return abort(err)
+               }
+       } else {
                // Handle zfs options.
                zfsFeatures := respHeader.GetZfsFeaturesSlice()
 
@@ -472,12 +534,11 @@ func (s *migrationSourceWs) Do(state *state.State, 
migrateOp *operations.Operati
                }
 
                // Initialize storage driver.
-               var fsErr error
-               legacyDriver, fsErr = ct.Storage().MigrationSource(sourceArgs)
-               if fsErr != nil {
-                       s.sendControl(fsErr)
-                       return fsErr
+               legacyDriver, err = ct.Storage().MigrationSource(sourceArgs)
+               if err != nil {
+                       return abort(err)
                }
+               legacyCleanup = legacyDriver.Cleanup
 
                if respHeader.GetRefresh() || *offerHeader.Fs != *respHeader.Fs 
{
                        myType := migration.MigrationFSType_RSYNC
@@ -492,36 +553,16 @@ func (s *migrationSourceWs) Do(state *state.State, 
migrateOp *operations.Operati
                        // Check if this storage pool has a rate limit set for 
rsync.
                        poolwritable := ct.Storage().GetStoragePoolWritable()
                        if poolwritable.Config != nil {
-                               bwlimit = poolwritable.Config["rsync.bwlimit"]
+                               rsyncBwlimit = 
poolwritable.Config["rsync.bwlimit"]
                        }
                }
 
-               // All failure paths need to do a few things to correctly 
handle errors before
-               // returning. Unfortunately, handling errors is not well-suited 
to defer as the code
-               // depends on the status of driver and the error value. The 
error value is
-               // especially tricky due to the common case of creating a new 
err variable
-               // (intentional or not) due to scoping and use of ":=".  
Capturing err in a closure
-               // for use in defer would be fragile, which defeats the purpose 
of using defer.
-               // An abort function reduces the odds of mishandling errors 
without introducing the
-               // fragility of closing on err.
-               abort = func(err error) error {
-                       legacyDriver.Cleanup()
-                       go s.sendControl(err)
-                       return err
-               }
-
-               err = legacyDriver.SendWhileRunning(s.fsConn, migrateOp, 
bwlimit, s.instanceOnly)
+               logger.Debugf("SendWhileRunning starting")
+               err = legacyDriver.SendWhileRunning(s.fsConn, migrateOp, 
rsyncBwlimit, s.instanceOnly)
                if err != nil {
                        return abort(err)
                }
-       }
-
-       // Check if the other side knows about pre-dumping and the associated 
rsync protocol.
-       usePreDumps = respHeader.GetPredump()
-       if usePreDumps {
-               logger.Debugf("The other side does support pre-copy")
-       } else {
-               logger.Debugf("The other side does not support pre-copy")
+               logger.Debugf("SendWhileRunning finished")
        }
 
        restoreSuccess := make(chan bool, 1)
@@ -606,7 +647,11 @@ func (s *migrationSourceWs) Do(state *state.State, 
migrateOp *operations.Operati
 
                        preDumpCounter := 0
                        preDumpDir := ""
-                       if usePreDumps {
+
+                       // Check if the other side knows about pre-dumping and 
the associated
+                       // rsync protocol.
+                       if respHeader.GetPredump() {
+                               logger.Debugf("The other side does support 
pre-copy")
                                final := false
                                for !final {
                                        preDumpCounter++
@@ -618,7 +663,7 @@ func (s *migrationSourceWs) Do(state *state.State, 
migrateOp *operations.Operati
                                        dumpDir := fmt.Sprintf("%03d", 
preDumpCounter)
                                        loopArgs := preDumpLoopArgs{
                                                checkpointDir: checkpointDir,
-                                               bwlimit:       bwlimit,
+                                               bwlimit:       rsyncBwlimit,
                                                preDumpDir:    preDumpDir,
                                                dumpDir:       dumpDir,
                                                final:         final,
@@ -632,6 +677,8 @@ func (s *migrationSourceWs) Do(state *state.State, 
migrateOp *operations.Operati
                                        preDumpDir = fmt.Sprintf("%03d", 
preDumpCounter)
                                        preDumpCounter++
                                }
+                       } else {
+                               logger.Debugf("The other side does not support 
pre-copy")
                        }
 
                        _, err = actionScriptOp.Run()
@@ -684,28 +731,47 @@ func (s *migrationSourceWs) Do(state *state.State, 
migrateOp *operations.Operati
                        }
                }
 
-               // We do the transger serially right now, but there's really no 
reason for us to;
+               // We do the transfer serially right now, but there's really no 
reason for us to;
                // since we have separate websockets, we can do it in parallel 
if we wanted to.
                // However assuming we're network bound, there's really no 
reason to do these in.
                // parallel. In the future when we're using p.haul's protocol, 
it will make sense
                // to do these in parallel.
                ctName, _, _ := 
shared.InstanceGetParentAndSnapshotName(s.instance.Name())
-               state := s.instance.DaemonState()
-               err = rsync.Send(ctName, shared.AddSlash(checkpointDir), 
&shared.WebsocketIO{Conn: s.criuConn}, nil, rsyncFeatures, bwlimit, 
state.OS.ExecPath)
+               err = rsync.Send(ctName, shared.AddSlash(checkpointDir), 
&shared.WebsocketIO{Conn: s.criuConn}, nil, rsyncFeatures, rsyncBwlimit, 
state.OS.ExecPath)
                if err != nil {
                        return abort(err)
                }
        }
 
-       if pool == nil {
-               if s.live || (respHeader.Criu != nil && *respHeader.Criu == 
migration.CRIUType_NONE) {
-                       err = legacyDriver.SendAfterCheckpoint(s.fsConn, 
bwlimit)
+       // If s.live is true or Criu is set to CRIUTYPE_NONE rather than nil, 
it indicates
+       // that the source container is running and that we should do a two 
stage transfer
+       // to minimize downtime.
+       if s.live || (respHeader.Criu != nil && *respHeader.Criu == 
migration.CRIUType_NONE) {
+               if pool != nil {
+                       volSourceArgs := migration.VolumeSourceArgs{
+                               Name:          s.instance.Name(),
+                               MigrationType: migrationType,
+                               TrackProgress: true,
+                               FinalSync:     true,
+                       }
+
+                       err = pool.MigrateInstance(s.instance, 
&shared.WebsocketIO{Conn: s.fsConn}, volSourceArgs, migrateOp)
                        if err != nil {
                                return abort(err)
                        }
+               } else {
+                       logger.Debugf("SendAfterCheckpoint starting")
+                       err = legacyDriver.SendAfterCheckpoint(s.fsConn, 
rsyncBwlimit)
+                       if err != nil {
+                               return abort(err)
+                       }
+                       logger.Debugf("SendAfterCheckpoint finished")
                }
+       }
 
-               legacyDriver.Cleanup()
+       // Perform any storage level cleanup, such as removing any temporary 
snapshots.
+       if legacyCleanup != nil {
+               legacyCleanup()
        }
 
        msg := migration.MigrationControl{}

From 6af0d9b05cb026096fac774be281f373ee99821e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 4 Dec 2019 14:28:16 +0000
Subject: [PATCH 10/11] lxd/migration/migration/volumes: Adds FinalSync bool to
 VolumeSourceArgs

Allows us to indicate to storage layer that this is final sync being performed 
and to alter behaviour accordingly.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/migration/migration_volumes.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lxd/migration/migration_volumes.go 
b/lxd/migration/migration_volumes.go
index 391a0e8353..883afc3f86 100644
--- a/lxd/migration/migration_volumes.go
+++ b/lxd/migration/migration_volumes.go
@@ -23,6 +23,7 @@ type VolumeSourceArgs struct {
        Snapshots     []string
        MigrationType Type
        TrackProgress bool
+       FinalSync     bool
 }
 
 // VolumeTargetArgs represents the arguments needed to setup a volume 
migration sink.

From 9f2fbbc755d6bfb93e783157e31e36c481896ed3 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 4 Dec 2019 14:29:02 +0000
Subject: [PATCH 11/11] lxd/storage/backend/lxd: Adds sanity check to
 MigrateInstance during FinalSync

Should never be transferring snapshots during final sync.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/backend_lxd.go | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index a347fb62e8..8173a7a450 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -1068,6 +1068,10 @@ func (b *lxdBackend) MigrateInstance(inst 
instance.Instance, conn io.ReadWriteCl
 
        contentType := InstanceContentType(inst)
 
+       if len(args.Snapshots) > 0 && args.FinalSync {
+               return fmt.Errorf("Snapshots should not be transferred during 
final sync")
+       }
+
        // Get the root disk device config.
        _, rootDiskConf, err := 
shared.GetRootDiskDevice(inst.ExpandedDevices().CloneNative())
        if err != nil {
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to