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