Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Standalone changes from the Linstor branch #1842

Merged
merged 8 commits into from
Mar 25, 2025
7 changes: 5 additions & 2 deletions cmd/incusd/migrate_storage_volumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ func (s *migrationSourceWs) DoStorage(state *state.State, projectName string, po
// to false here. The migration source/sender doesn't need to care whether
// or not it's doing a refresh as the migration sink/receiver will know
// this, and adjust the migration types accordingly.
poolMigrationTypes = pool.MigrationTypes(storageDrivers.ContentType(srcConfig.Volume.ContentType), false, !s.volumeOnly)
// The same applies for clusterMove and storageMove, which are set to the most optimized defaults.
poolMigrationTypes = pool.MigrationTypes(storageDrivers.ContentType(srcConfig.Volume.ContentType), false, !s.volumeOnly, true, false)
if len(poolMigrationTypes) == 0 {
return fmt.Errorf("No source migration types available")
}
Expand Down Expand Up @@ -338,10 +339,12 @@ func (c *migrationSink) DoStorage(state *state.State, projectName string, poolNa
// Refresh needs to be set.
offerHeader.Refresh = &c.refresh

clusterMove := req.Source.Location != ""

// 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.
respTypes, err := localMigration.MatchTypes(offerHeader, storagePools.FallbackMigrationType(contentType), pool.MigrationTypes(contentType, c.refresh, !c.volumeOnly))
respTypes, err := localMigration.MatchTypes(offerHeader, storagePools.FallbackMigrationType(contentType), pool.MigrationTypes(contentType, c.refresh, !c.volumeOnly, clusterMove, poolName != "" && req.Source.Pool != poolName || !clusterMove))
if err != nil {
return err
}
Expand Down
17 changes: 13 additions & 4 deletions internal/server/instance/drivers/driver_lxc.go
Original file line number Diff line number Diff line change
Expand Up @@ -5502,10 +5502,14 @@ func (d *lxc) MigrateSend(args instance.MigrateSendArgs) error {
return err
}

clusterMove := args.ClusterMoveSourceName != ""
storageMove := args.StoragePool != ""

// The refresh argument passed to MigrationTypes() is always set to false here.
// The migration source/sender doesn't need to care whether or not it's doing a refresh as the migration
// sink/receiver will know this, and adjust the migration types accordingly.
poolMigrationTypes := pool.MigrationTypes(storagePools.InstanceContentType(d), false, args.Snapshots)
// The same applies for clusterMove and storageMove, which are set to the most optimized defaults.
poolMigrationTypes := pool.MigrationTypes(storagePools.InstanceContentType(d), false, args.Snapshots, true, false)
if len(poolMigrationTypes) == 0 {
err := fmt.Errorf("No source migration types available")
op.Done(err)
Expand Down Expand Up @@ -5614,7 +5618,8 @@ func (d *lxc) MigrateSend(args instance.MigrateSendArgs) error {
AllowInconsistent: args.AllowInconsistent,
VolumeOnly: !args.Snapshots,
Info: &localMigration.Info{Config: srcConfig},
ClusterMove: args.ClusterMoveSourceName != "",
ClusterMove: clusterMove,
StorageMove: storageMove,
}

// Only send the snapshots that the target requests when refreshing.
Expand Down Expand Up @@ -6151,10 +6156,13 @@ func (d *lxc) MigrateReceive(args instance.MigrateReceiveArgs) error {
// However, to determine the correct migration type Refresh needs to be set.
offerHeader.Refresh = &args.Refresh

clusterMove := args.ClusterMoveSourceName != ""
storageMove := args.StoragePool != ""

// 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.
contentType := storagePools.InstanceContentType(d)
respTypes, err := localMigration.MatchTypes(offerHeader, storagePools.FallbackMigrationType(contentType), pool.MigrationTypes(contentType, args.Refresh, args.Snapshots))
respTypes, err := localMigration.MatchTypes(offerHeader, storagePools.FallbackMigrationType(contentType), pool.MigrationTypes(contentType, args.Refresh, args.Snapshots, clusterMove, storageMove))
if err != nil {
return err
}
Expand Down Expand Up @@ -6378,6 +6386,7 @@ func (d *lxc) MigrateReceive(args instance.MigrateReceiveArgs) error {
VolumeSize: offerHeader.GetVolumeSize(), // Block size setting override.
VolumeOnly: !args.Snapshots,
ClusterMoveSourceName: args.ClusterMoveSourceName,
StoragePool: args.StoragePool,
}

// At this point we have already figured out the parent container's root
Expand Down Expand Up @@ -6436,7 +6445,7 @@ func (d *lxc) MigrateReceive(args instance.MigrateReceiveArgs) error {
return fmt.Errorf("Failed creating instance on target: %w", err)
}

isRemoteClusterMove := args.ClusterMoveSourceName != "" && pool.Driver().Info().Remote
isRemoteClusterMove := clusterMove && pool.Driver().Info().Remote

// Only delete all instance volumes on error if the pool volume creation has succeeded to
// avoid deleting an existing conflicting volume.
Expand Down
17 changes: 12 additions & 5 deletions internal/server/instance/drivers/driver_qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -6702,11 +6702,15 @@ func (d *qemu) MigrateSend(args instance.MigrateSendArgs) error {
return err
}

clusterMove := args.ClusterMoveSourceName != ""
storageMove := args.StoragePool != ""

// The refresh argument passed to MigrationTypes() is always set
// to false here. The migration source/sender doesn't need to care whether
// or not it's doing a refresh as the migration sink/receiver will know
// this, and adjust the migration types accordingly.
poolMigrationTypes := pool.MigrationTypes(storagePools.InstanceContentType(d), false, args.Snapshots)
// The same applies for clusterMove and storageMove, which are set to the most optimized defaults.
poolMigrationTypes := pool.MigrationTypes(storagePools.InstanceContentType(d), false, args.Snapshots, true, false)
if len(poolMigrationTypes) == 0 {
err := fmt.Errorf("No source migration types available")
op.Done(err)
Expand Down Expand Up @@ -6804,8 +6808,8 @@ func (d *qemu) MigrateSend(args instance.MigrateSendArgs) error {
AllowInconsistent: args.AllowInconsistent,
VolumeOnly: !args.Snapshots,
Info: &localMigration.Info{Config: srcConfig},
ClusterMove: args.ClusterMoveSourceName != "",
StorageMove: args.StoragePool != "",
ClusterMove: clusterMove,
StorageMove: storageMove,
}

// Only send the snapshots that the target requests when refreshing.
Expand Down Expand Up @@ -7345,10 +7349,13 @@ func (d *qemu) MigrateReceive(args instance.MigrateReceiveArgs) error {
// However, to determine the correct migration type Refresh needs to be set.
offerHeader.Refresh = &args.Refresh

clusterMove := args.ClusterMoveSourceName != ""
storageMove := args.StoragePool != ""

// 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.
contentType := storagePools.InstanceContentType(d)
respTypes, err := localMigration.MatchTypes(offerHeader, storagePools.FallbackMigrationType(contentType), pool.MigrationTypes(contentType, args.Refresh, args.Snapshots))
respTypes, err := localMigration.MatchTypes(offerHeader, storagePools.FallbackMigrationType(contentType), pool.MigrationTypes(contentType, args.Refresh, args.Snapshots, clusterMove, storageMove))
if err != nil {
return err
}
Expand Down Expand Up @@ -7657,7 +7664,7 @@ func (d *qemu) MigrateReceive(args instance.MigrateReceiveArgs) error {

// Only delete all instance volumes on error if the pool volume creation has succeeded to
// avoid deleting an existing conflicting volume.
isRemoteClusterMove := args.ClusterMoveSourceName != "" && poolInfo.Remote
isRemoteClusterMove := clusterMove && poolInfo.Remote
if !volTargetArgs.Refresh && !isRemoteClusterMove {
revert.Add(func() {
snapshots, _ := d.Snapshots()
Expand Down
37 changes: 23 additions & 14 deletions internal/server/storage/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,13 @@ func (b *backend) Driver() drivers.Driver {
return b.driver
}

// MigrationTypes returns the migration transport method preferred when sending a migration,
// based on the migration method requested by the driver's ability. The snapshots argument
// indicates whether snapshots are migrated as well. It is used to determine whether to use
// optimized migration.
func (b *backend) MigrationTypes(contentType drivers.ContentType, refresh bool, copySnapshots bool) []localMigration.Type {
return b.driver.MigrationTypes(contentType, refresh, copySnapshots)
// MigrationTypes returns the migration transport method preferred when sending a migration, based
// on the migration method requested by the driver's ability. The copySnapshots argument indicates
// whether snapshots are migrated as well. clusterMove determines whether the migration is done
// within a cluster and storageMove determines whether the storage pool is changed by the migration.
// This method is used to determine whether to use optimized migration.
func (b *backend) MigrationTypes(contentType drivers.ContentType, refresh bool, copySnapshots bool, clusterMove bool, storageMove bool) []localMigration.Type {
return b.driver.MigrationTypes(contentType, refresh, copySnapshots, clusterMove, storageMove)
}

// Create creates the storage pool layout on the storage device.
Expand Down Expand Up @@ -1150,9 +1151,9 @@ func (b *backend) CreateInstanceFromCopy(inst instance.Instance, src instance.In
l.Debug("CreateInstanceFromCopy cross-pool mode detected")

// Negotiate the migration type to use.
offeredTypes := srcPool.MigrationTypes(contentType, false, snapshots)
offeredTypes := srcPool.MigrationTypes(contentType, false, snapshots, false, true)
offerHeader := localMigration.TypesToHeader(offeredTypes...)
migrationTypes, err := localMigration.MatchTypes(offerHeader, FallbackMigrationType(contentType), b.MigrationTypes(contentType, false, snapshots))
migrationTypes, err := localMigration.MatchTypes(offerHeader, FallbackMigrationType(contentType), b.MigrationTypes(contentType, false, snapshots, false, true))
if err != nil {
return fmt.Errorf("Failed to negotiate copy migration type: %w", err)
}
Expand Down Expand Up @@ -1196,6 +1197,7 @@ func (b *backend) CreateInstanceFromCopy(inst instance.Instance, src instance.In
AllowInconsistent: allowInconsistent,
VolumeOnly: !snapshots,
Info: &localMigration.Info{Config: srcConfig},
StorageMove: true,
}, op)
})

Expand All @@ -1208,6 +1210,7 @@ func (b *backend) CreateInstanceFromCopy(inst instance.Instance, src instance.In
VolumeSize: srcVolumeSize, // Block size setting override.
TrackProgress: false, // Do not use a progress tracker on receiver.
VolumeOnly: !snapshots,
StoragePool: srcPool.Name(),
}, op)
})

Expand Down Expand Up @@ -1398,9 +1401,9 @@ func (b *backend) RefreshCustomVolume(projectName string, srcProjectName string,
l.Debug("RefreshCustomVolume cross-pool mode detected")

// Negotiate the migration type to use.
offeredTypes := srcPool.MigrationTypes(contentType, true, snapshots)
offeredTypes := srcPool.MigrationTypes(contentType, true, snapshots, false, true)
offerHeader := localMigration.TypesToHeader(offeredTypes...)
migrationTypes, err := localMigration.MatchTypes(offerHeader, FallbackMigrationType(contentType), b.MigrationTypes(contentType, true, snapshots))
migrationTypes, err := localMigration.MatchTypes(offerHeader, FallbackMigrationType(contentType), b.MigrationTypes(contentType, true, snapshots, false, true))
if err != nil {
return fmt.Errorf("Failed to negotiate copy migration type: %w", err)
}
Expand Down Expand Up @@ -1456,6 +1459,7 @@ func (b *backend) RefreshCustomVolume(projectName string, srcProjectName string,
TrackProgress: true, // Do use a progress tracker on sender.
ContentType: string(contentType),
Info: &localMigration.Info{Config: srcConfig},
StorageMove: true,
}, op)
if err != nil {
cancel()
Expand All @@ -1476,6 +1480,7 @@ func (b *backend) RefreshCustomVolume(projectName string, srcProjectName string,
ContentType: string(contentType),
VolumeSize: volSize, // Block size setting override.
Refresh: true,
StoragePool: srcPoolName,
}, op)
if err != nil {
cancel()
Expand Down Expand Up @@ -1643,9 +1648,9 @@ func (b *backend) RefreshInstance(inst instance.Instance, src instance.Instance,
l.Debug("RefreshInstance cross-pool mode detected")

// Negotiate the migration type to use.
offeredTypes := srcPool.MigrationTypes(contentType, true, snapshots)
offeredTypes := srcPool.MigrationTypes(contentType, true, snapshots, false, true)
offerHeader := localMigration.TypesToHeader(offeredTypes...)
migrationTypes, err := localMigration.MatchTypes(offerHeader, FallbackMigrationType(contentType), b.MigrationTypes(contentType, true, snapshots))
migrationTypes, err := localMigration.MatchTypes(offerHeader, FallbackMigrationType(contentType), b.MigrationTypes(contentType, true, snapshots, false, true))
if err != nil {
return fmt.Errorf("Failed to negotiate copy migration type: %w", err)
}
Expand Down Expand Up @@ -1686,6 +1691,7 @@ func (b *backend) RefreshInstance(inst instance.Instance, src instance.Instance,
Refresh: true, // Indicate to sender to use incremental streams.
Info: &localMigration.Info{Config: srcConfig},
VolumeOnly: !snapshots,
StorageMove: true,
}, op)
})

Expand All @@ -1699,6 +1705,7 @@ func (b *backend) RefreshInstance(inst instance.Instance, src instance.Instance,
VolumeSize: srcVolumeSize,
TrackProgress: false, // Do not use a progress tracker on receiver.
VolumeOnly: !snapshots,
StoragePool: srcPool.Name(),
}, op)
})

Expand Down Expand Up @@ -4812,9 +4819,9 @@ func (b *backend) CreateCustomVolumeFromCopy(projectName string, srcProjectName
l.Debug("CreateCustomVolumeFromCopy cross-pool mode detected")

// Negotiate the migration type to use.
offeredTypes := srcPool.MigrationTypes(contentType, false, snapshots)
offeredTypes := srcPool.MigrationTypes(contentType, false, snapshots, false, true)
offerHeader := localMigration.TypesToHeader(offeredTypes...)
migrationTypes, err := localMigration.MatchTypes(offerHeader, FallbackMigrationType(contentType), b.MigrationTypes(contentType, false, snapshots))
migrationTypes, err := localMigration.MatchTypes(offerHeader, FallbackMigrationType(contentType), b.MigrationTypes(contentType, false, snapshots, false, true))
if err != nil {
return fmt.Errorf("Failed to negotiate copy migration type: %w", err)
}
Expand Down Expand Up @@ -4874,6 +4881,7 @@ func (b *backend) CreateCustomVolumeFromCopy(projectName string, srcProjectName
ContentType: string(contentType),
Info: &localMigration.Info{Config: srcConfig},
VolumeOnly: !snapshots,
StorageMove: true,
}, op)
if err != nil {
cancel()
Expand All @@ -4894,6 +4902,7 @@ func (b *backend) CreateCustomVolumeFromCopy(projectName string, srcProjectName
ContentType: string(contentType),
VolumeSize: volSize, // Block size setting override.
VolumeOnly: !snapshots,
StoragePool: srcPool.Name(),
}, op)
if err != nil {
cancel()
Expand Down
3 changes: 2 additions & 1 deletion internal/server/storage/backend_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ func (b *mockBackend) Driver() drivers.Driver {
return b.driver
}

func (b *mockBackend) MigrationTypes(contentType drivers.ContentType, refresh bool, copySnapshots bool) []migration.Type {
// MigrationTypes returns the type of transfer methods to be used when doing migrations between pools in preference order.
func (b *mockBackend) MigrationTypes(contentType drivers.ContentType, refresh bool, copySnapshots bool, clusterMove bool, storageMove bool) []migration.Type {
return []migration.Type{
{
FSType: FallbackMigrationType(contentType),
Expand Down
2 changes: 1 addition & 1 deletion internal/server/storage/drivers/driver_btrfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ func (d *btrfs) GetResources() (*api.ResourcesStoragePool, error) {
}

// MigrationType returns the type of transfer methods to be used when doing migrations between pools in preference order.
func (d *btrfs) MigrationTypes(contentType ContentType, refresh bool, copySnapshots bool) []localMigration.Type {
func (d *btrfs) MigrationTypes(contentType ContentType, refresh bool, copySnapshots bool, clusterMove bool, storageMove bool) []localMigration.Type {
var rsyncFeatures []string
btrfsFeatures := []string{migration.BTRFSFeatureMigrationHeader, migration.BTRFSFeatureSubvolumes, migration.BTRFSFeatureSubvolumeUUIDs}

Expand Down
2 changes: 1 addition & 1 deletion internal/server/storage/drivers/driver_ceph.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ func (d *ceph) GetResources() (*api.ResourcesStoragePool, error) {
}

// MigrationType returns the type of transfer methods to be used when doing migrations between pools in preference order.
func (d *ceph) MigrationTypes(contentType ContentType, refresh bool, copySnapshots bool) []localMigration.Type {
func (d *ceph) MigrationTypes(contentType ContentType, refresh bool, copySnapshots bool, clusterMove bool, storageMove bool) []localMigration.Type {
var rsyncFeatures []string

// Do not pass compression argument to rsync if the associated
Expand Down
2 changes: 1 addition & 1 deletion internal/server/storage/drivers/driver_cephfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ func (d *cephfs) GetResources() (*api.ResourcesStoragePool, error) {
}

// MigrationTypes returns the supported migration types and options supported by the driver.
func (d *cephfs) MigrationTypes(contentType ContentType, refresh bool, copySnapshots bool) []localMigration.Type {
func (d *cephfs) MigrationTypes(contentType ContentType, refresh bool, copySnapshots bool, clusterMove bool, storageMove bool) []localMigration.Type {
var rsyncFeatures []string

// Do not pass compression argument to rsync if the associated
Expand Down
2 changes: 1 addition & 1 deletion internal/server/storage/drivers/driver_cephobject.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,6 @@ func (d *cephobject) GetResources() (*api.ResourcesStoragePool, error) {
}

// MigrationTypes returns the supported migration types and options supported by the driver.
func (d *cephobject) MigrationTypes(contentType ContentType, refresh bool, copySnapshots bool) []migration.Type {
func (d *cephobject) MigrationTypes(contentType ContentType, refresh bool, copySnapshots bool, clusterMove bool, storageMove bool) []migration.Type {
return nil
}
2 changes: 1 addition & 1 deletion internal/server/storage/drivers/driver_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ func (d *common) validateVolume(vol Volume, driverRules map[string]func(value st

// MigrationType returns the type of transfer methods to be used when doing migrations between pools
// in preference order.
func (d *common) MigrationTypes(contentType ContentType, refresh bool, copySnapshots bool) []localMigration.Type {
func (d *common) MigrationTypes(contentType ContentType, refresh bool, copySnapshots bool, clusterMove bool, storageMove bool) []localMigration.Type {
var transportType migration.MigrationFSType
var rsyncFeatures []string

Expand Down
2 changes: 1 addition & 1 deletion internal/server/storage/drivers/driver_zfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,7 @@ func (d *zfs) GetResources() (*api.ResourcesStoragePool, error) {
}

// MigrationType returns the type of transfer methods to be used when doing migrations between pools in preference order.
func (d *zfs) MigrationTypes(contentType ContentType, refresh bool, copySnapshots bool) []localMigration.Type {
func (d *zfs) MigrationTypes(contentType ContentType, refresh bool, copySnapshots bool, clusterMove bool, storageMove bool) []localMigration.Type {
var rsyncFeatures []string

// Do not pass compression argument to rsync if the associated
Expand Down
2 changes: 1 addition & 1 deletion internal/server/storage/drivers/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ type Driver interface {
RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error

// Migration.
MigrationTypes(contentType ContentType, refresh bool, copySnapshots bool) []migration.Type
MigrationTypes(contentType ContentType, refresh bool, copySnapshots bool, clusterMove bool, storageMove bool) []migration.Type
MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error
CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error

Expand Down
2 changes: 1 addition & 1 deletion internal/server/storage/pool_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ type Pool interface {
RestoreCustomVolume(projectName string, volName string, snapshotName string, op *operations.Operation) error

// Custom volume migration.
MigrationTypes(contentType drivers.ContentType, refresh bool, copySnapshots bool) []migration.Type
MigrationTypes(contentType drivers.ContentType, refresh bool, copySnapshots bool, clusterMove bool, storageMove bool) []migration.Type
CreateCustomVolumeFromMigration(projectName string, conn io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) error
MigrateCustomVolume(projectName string, conn io.ReadWriteCloser, args *migration.VolumeSourceArgs, op *operations.Operation) error

Expand Down
Loading