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

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) ===
Fixes rsync features negotiation for CRIU transport when preferred volume transport isn't rsync.
From a2fe2426468124812b176ae6618fbd6fae344e57 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 28 Jan 2020 17:36:02 +0000
Subject: [PATCH 1/2] lxd: Updates usage of migration.MatchTypes

Uses preferred negotiated transport type for volume migration.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/migrate_container.go       | 17 +++++++++--------
 lxd/migrate_storage_volumes.go | 10 +++++-----
 lxd/storage/backend_lxd.go     | 18 +++++++++---------
 3 files changed, 23 insertions(+), 22 deletions(-)

diff --git a/lxd/migrate_container.go b/lxd/migrate_container.go
index 4f4b08128e..a3a7ca58f4 100644
--- a/lxd/migrate_container.go
+++ b/lxd/migrate_container.go
@@ -470,12 +470,13 @@ func (s *migrationSourceWs) Do(state *state.State, 
migrateOp *operations.Operati
        }
 
        var legacyDriver MigrationStorageSourceDriver
-       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.
+       var legacyCleanup func()            // Called after migration, to 
remove any temporary snapshots, etc.
+       var migrationTypes []migration.Type // Negotiated migration types.
+       var rsyncBwlimit string             // Used for CRIU state and legacy 
storage rsync transfers.
 
        // Handle rsync options.
        rsyncFeatures := respHeader.GetRsyncFeaturesSlice()
+
        if !shared.StringInSlice("bidirectional", rsyncFeatures) {
                // If no bi-directional support, assume LXD 3.7 level.
                // NOTE: Do NOT extend this list of arguments.
@@ -507,7 +508,7 @@ func (s *migrationSourceWs) Do(state *state.State, 
migrateOp *operations.Operati
 
        if pool != nil {
                rsyncBwlimit = pool.Driver().Config()["rsync.bwlimit"]
-               migrationType, err = migration.MatchTypes(respHeader, 
migration.MigrationFSType_RSYNC, poolMigrationTypes)
+               migrationTypes, err = migration.MatchTypes(respHeader, 
migration.MigrationFSType_RSYNC, poolMigrationTypes)
                if err != nil {
                        logger.Errorf("Failed to negotiate migration type: %v", 
err)
                        return abort(err)
@@ -521,7 +522,7 @@ func (s *migrationSourceWs) Do(state *state.State, 
migrateOp *operations.Operati
                }
 
                volSourceArgs.Name = s.instance.Name()
-               volSourceArgs.MigrationType = migrationType
+               volSourceArgs.MigrationType = migrationTypes[0]
                volSourceArgs.Snapshots = sendSnapshotNames
                volSourceArgs.TrackProgress = true
                err = pool.MigrateInstance(s.instance, 
&shared.WebsocketIO{Conn: s.fsConn}, volSourceArgs, migrateOp)
@@ -942,13 +943,13 @@ func (c *migrationSink) Do(state *state.State, migrateOp 
*operations.Operation)
                // Extract the source's migration type and then match it 
against our pool's
                // supported types and features. If a match is found the 
combined features list
                // will be sent back to requester.
-               respType, err := migration.MatchTypes(offerHeader, 
migration.MigrationFSType_RSYNC, 
pool.MigrationTypes(storagePools.InstanceContentType(c.src.instance), 
c.refresh))
+               respTypes, err := migration.MatchTypes(offerHeader, 
migration.MigrationFSType_RSYNC, 
pool.MigrationTypes(storagePools.InstanceContentType(c.src.instance), 
c.refresh))
                if err != nil {
                        return err
                }
 
                // Convert response type to response header and copy snapshot 
info into it.
-               respHeader = migration.TypesToHeader(respType)
+               respHeader = migration.TypesToHeader(respTypes...)
                respHeader.SnapshotNames = offerHeader.SnapshotNames
                respHeader.Snapshots = offerHeader.Snapshots
                respHeader.Refresh = &c.refresh
@@ -958,7 +959,7 @@ func (c *migrationSink) Do(state *state.State, migrateOp 
*operations.Operation)
                myTarget = func(conn *websocket.Conn, op *operations.Operation, 
args MigrationSinkArgs) error {
                        volTargetArgs := migration.VolumeTargetArgs{
                                Name:          args.Instance.Name(),
-                               MigrationType: respType,
+                               MigrationType: respTypes[0],
                                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.
diff --git a/lxd/migrate_storage_volumes.go b/lxd/migrate_storage_volumes.go
index 553ba0bff8..52a5dde446 100644
--- a/lxd/migrate_storage_volumes.go
+++ b/lxd/migrate_storage_volumes.go
@@ -146,7 +146,7 @@ func (s *migrationSourceWs) DoStorage(state *state.State, 
poolName string, volNa
 
        // Use new storage layer for migration if supported.
        if pool != nil {
-               migrationType, err := migration.MatchTypes(respHeader, 
migration.MigrationFSType_RSYNC, poolMigrationTypes)
+               migrationTypes, err := migration.MatchTypes(respHeader, 
migration.MigrationFSType_RSYNC, poolMigrationTypes)
                if err != nil {
                        logger.Errorf("Failed to negotiate migration type: %v", 
err)
                        s.sendControl(err)
@@ -155,7 +155,7 @@ func (s *migrationSourceWs) DoStorage(state *state.State, 
poolName string, volNa
 
                volSourceArgs := &migration.VolumeSourceArgs{
                        Name:          volName,
-                       MigrationType: migrationType,
+                       MigrationType: migrationTypes[0],
                        Snapshots:     snapshotNames,
                        TrackProgress: true,
                }
@@ -346,13 +346,13 @@ func (c *migrationSink) DoStorage(state *state.State, 
poolName string, req *api.
                // Extract the source's migration type and then match it 
against our pool's
                // supported types and features. If a match is found the 
combined features list
                // will be sent back to requester.
-               respType, err := migration.MatchTypes(offerHeader, 
migration.MigrationFSType_RSYNC, 
pool.MigrationTypes(storageDrivers.ContentTypeFS, c.refresh))
+               respTypes, err := migration.MatchTypes(offerHeader, 
migration.MigrationFSType_RSYNC, 
pool.MigrationTypes(storageDrivers.ContentTypeFS, c.refresh))
                if err != nil {
                        return err
                }
 
                // Convert response type to response header and copy snapshot 
info into it.
-               respHeader = migration.TypesToHeader(respType)
+               respHeader = migration.TypesToHeader(respTypes...)
                respHeader.SnapshotNames = offerHeader.SnapshotNames
                respHeader.Snapshots = offerHeader.Snapshots
 
@@ -363,7 +363,7 @@ func (c *migrationSink) DoStorage(state *state.State, 
poolName string, req *api.
                                Name:          req.Name,
                                Config:        req.Config,
                                Description:   req.Description,
-                               MigrationType: respType,
+                               MigrationType: respTypes[0],
                                TrackProgress: true,
                        }
 
diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 82180a3b88..00eb8f2bc4 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -651,7 +651,7 @@ func (b *lxdBackend) CreateInstanceFromCopy(inst 
instance.Instance, src instance
                // Negotiate the migration type to use.
                offeredTypes := srcPool.MigrationTypes(contentType, false)
                offerHeader := migration.TypesToHeader(offeredTypes...)
-               migrationType, err := migration.MatchTypes(offerHeader, 
migration.MigrationFSType_RSYNC, b.MigrationTypes(contentType, false))
+               migrationTypes, err := migration.MatchTypes(offerHeader, 
migration.MigrationFSType_RSYNC, b.MigrationTypes(contentType, false))
                if err != nil {
                        return fmt.Errorf("Failed to negotiate copy migration 
type: %v", err)
                }
@@ -663,7 +663,7 @@ func (b *lxdBackend) CreateInstanceFromCopy(inst 
instance.Instance, src instance
                        err := srcPool.MigrateInstance(src, aEnd, 
&migration.VolumeSourceArgs{
                                Name:          src.Name(),
                                Snapshots:     snapshotNames,
-                               MigrationType: migrationType,
+                               MigrationType: migrationTypes[0],
                                TrackProgress: true, // Do use a progress 
tracker on sender.
                        }, op)
 
@@ -674,7 +674,7 @@ func (b *lxdBackend) CreateInstanceFromCopy(inst 
instance.Instance, src instance
                        err := b.CreateInstanceFromMigration(inst, bEnd, 
migration.VolumeTargetArgs{
                                Name:          inst.Name(),
                                Snapshots:     snapshotNames,
-                               MigrationType: migrationType,
+                               MigrationType: migrationTypes[0],
                                TrackProgress: false, // Do not use a progress 
tracker on receiver.
                        }, op)
 
@@ -792,7 +792,7 @@ func (b *lxdBackend) RefreshInstance(inst 
instance.Instance, src instance.Instan
                // Negotiate the migration type to use.
                offeredTypes := srcPool.MigrationTypes(contentType, true)
                offerHeader := migration.TypesToHeader(offeredTypes...)
-               migrationType, err := migration.MatchTypes(offerHeader, 
migration.MigrationFSType_RSYNC, b.MigrationTypes(contentType, true))
+               migrationTypes, err := migration.MatchTypes(offerHeader, 
migration.MigrationFSType_RSYNC, b.MigrationTypes(contentType, true))
                if err != nil {
                        return fmt.Errorf("Failed to negotiate copy migration 
type: %v", err)
                }
@@ -804,7 +804,7 @@ func (b *lxdBackend) RefreshInstance(inst 
instance.Instance, src instance.Instan
                        err := srcPool.MigrateInstance(src, aEnd, 
&migration.VolumeSourceArgs{
                                Name:          src.Name(),
                                Snapshots:     snapshotNames,
-                               MigrationType: migrationType,
+                               MigrationType: migrationTypes[0],
                                TrackProgress: true, // Do use a progress 
tracker on sender.
                        }, op)
 
@@ -815,7 +815,7 @@ func (b *lxdBackend) RefreshInstance(inst 
instance.Instance, src instance.Instan
                        err := b.CreateInstanceFromMigration(inst, bEnd, 
migration.VolumeTargetArgs{
                                Name:          inst.Name(),
                                Snapshots:     snapshotNames,
-                               MigrationType: migrationType,
+                               MigrationType: migrationTypes[0],
                                Refresh:       true,  // Indicate to receiver 
volume should exist.
                                TrackProgress: false, // Do not use a progress 
tracker on receiver.
                        }, op)
@@ -2214,7 +2214,7 @@ func (b *lxdBackend) CreateCustomVolumeFromCopy(volName, 
desc string, config map
        // Negotiate the migration type to use.
        offeredTypes := srcPool.MigrationTypes(drivers.ContentTypeFS, false)
        offerHeader := migration.TypesToHeader(offeredTypes...)
-       migrationType, err := migration.MatchTypes(offerHeader, 
migration.MigrationFSType_RSYNC, b.MigrationTypes(drivers.ContentTypeFS, false))
+       migrationTypes, err := migration.MatchTypes(offerHeader, 
migration.MigrationFSType_RSYNC, b.MigrationTypes(drivers.ContentTypeFS, false))
        if err != nil {
                return fmt.Errorf("Failed to negotiate copy migration type: 
%v", err)
        }
@@ -2226,7 +2226,7 @@ func (b *lxdBackend) CreateCustomVolumeFromCopy(volName, 
desc string, config map
                err := srcPool.MigrateCustomVolume(aEnd, 
&migration.VolumeSourceArgs{
                        Name:          srcVolName,
                        Snapshots:     snapshotNames,
-                       MigrationType: migrationType,
+                       MigrationType: migrationTypes[0],
                        TrackProgress: true, // Do use a progress tracker on 
sender.
                }, op)
 
@@ -2239,7 +2239,7 @@ func (b *lxdBackend) CreateCustomVolumeFromCopy(volName, 
desc string, config map
                        Description:   desc,
                        Config:        config,
                        Snapshots:     snapshotNames,
-                       MigrationType: migrationType,
+                       MigrationType: migrationTypes[0],
                        TrackProgress: false, // Do not use a progress tracker 
on receiver.
 
                }, op)

From a248d1127e9a630f30e97f713d4aa0c39b77c89e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 28 Jan 2020 17:39:40 +0000
Subject: [PATCH 2/2] lxd/migration/migration/volumes: Updates MatchTypes to
 return all supported migration types

Returns in preference order of local host.

This allows TypesToHeader function to generate rsync features in header, even 
if first preferred transport type isn't rsync.

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

diff --git a/lxd/migration/migration_volumes.go 
b/lxd/migration/migration_volumes.go
index 0ed03c3219..a4e089ae9e 100644
--- a/lxd/migration/migration_volumes.go
+++ b/lxd/migration/migration_volumes.go
@@ -13,8 +13,8 @@ import (
 // Type represents the migration transport type. It indicates the method by 
which the migration can
 // take place and what optional features are available.
 type Type struct {
-       FSType   MigrationFSType
-       Features []string
+       FSType   MigrationFSType // Transport mode selected.
+       Features []string        // Feature hints for selected FSType transport 
mode.
 }
 
 // VolumeSourceArgs represents the arguments needed to setup a volume 
migration source.
@@ -103,18 +103,19 @@ func TypesToHeader(types ...Type) MigrationHeader {
        return header
 }
 
-// MatchTypes attempts to find a matching migration transport type between an 
offered type sent
-// from a remote source and the types supported by a local storage pool. If a 
match is found then
-// a Type is returned containing the method and the matching optional features 
present in both.
-// The function also takes a fallback type which is used as an additional 
offer type preference
-// in case the preferred remote type is not compatible with the local type 
available.
-// It is expected that both sides of the migration will support the fallback 
type for the volume's
-// content type that is being migrated.
-func MatchTypes(offer MigrationHeader, fallbackType MigrationFSType, ourTypes 
[]Type) (Type, error) {
+// MatchTypes attempts to find matching migration transport types between an 
offered type sent from a remote
+// source and the types supported by a local storage pool. If matches are 
found then one or more Types are
+// returned containing the method and the matching optional features present 
in both. The function also takes a
+// fallback type which is used as an additional offer type preference in case 
the preferred remote type is not
+// compatible with the local type available. It is expected that both sides of 
the migration will support the
+// fallback type for the volume's content type that is being migrated.
+func MatchTypes(offer MigrationHeader, fallbackType MigrationFSType, ourTypes 
[]Type) ([]Type, error) {
        // Generate an offer types slice from the preferred type supplied from 
remote and the
        // fallback type supplied based on the content type of the transfer.
        offeredFSTypes := []MigrationFSType{offer.GetFs(), fallbackType}
 
+       matchedTypes := []Type{}
+
        // Find first matching type.
        for _, ourType := range ourTypes {
                for _, offerFSType := range offeredFSTypes {
@@ -144,26 +145,30 @@ func MatchTypes(offer MigrationHeader, fallbackType 
MigrationFSType, ourTypes []
                                }
                        }
 
-                       // Return type with combined features.
-                       return Type{
+                       // Append type with combined features.
+                       matchedTypes = append(matchedTypes, Type{
                                FSType:   ourType.FSType,
                                Features: commonFeatures,
-                       }, nil
+                       })
                }
        }
 
-       // No matching transport type found, generate an error with offered 
types and our types.
-       offeredTypeStrings := make([]string, 0, len(offeredFSTypes))
-       for _, offerFSType := range offeredFSTypes {
-               offeredTypeStrings = append(offeredTypeStrings, 
offerFSType.String())
-       }
+       if len(matchedTypes) < 1 {
+               // No matching transport type found, generate an error with 
offered types and our types.
+               offeredTypeStrings := make([]string, 0, len(offeredFSTypes))
+               for _, offerFSType := range offeredFSTypes {
+                       offeredTypeStrings = append(offeredTypeStrings, 
offerFSType.String())
+               }
 
-       ourTypeStrings := make([]string, 0, len(ourTypes))
-       for _, ourType := range ourTypes {
-               ourTypeStrings = append(ourTypeStrings, ourType.FSType.String())
+               ourTypeStrings := make([]string, 0, len(ourTypes))
+               for _, ourType := range ourTypes {
+                       ourTypeStrings = append(ourTypeStrings, 
ourType.FSType.String())
+               }
+
+               return matchedTypes, fmt.Errorf("No matching migration types 
found. Offered types: %v, our types: %v", offeredTypeStrings, ourTypeStrings)
        }
 
-       return Type{}, fmt.Errorf("No matching migration type found. Offered 
types: %v, our types: %v", offeredTypeStrings, ourTypeStrings)
+       return matchedTypes, nil
 }
 
 func progressWrapperRender(op *operations.Operation, key string, description 
string, progressInt int64, speedInt int64) {
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to