[lxc-devel] [lxd/master] Migration: CRIU rsync features

tomponline on Github lxc-bot at linuxcontainers.org
Tue Jan 28 17:41:56 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 397 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200128/0417137b/attachment.bin>
-------------- next part --------------
From a2fe2426468124812b176ae6618fbd6fae344e57 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at 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.parrott at 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.parrott at 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.parrott at 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) {


More information about the lxc-devel mailing list