diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index ddb4c0a3ab39..656938a81330 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -731,11 +731,20 @@ func (b *lxdBackend) CreateInstanceFromBackup(srcBackup backup.Info, srcData io. vol := b.GetVolume(volType, contentType, volStorageName, volumeConfig) + sourceSnapshots := make([]drivers.Volume, 0, len(srcBackup.Config.VolumeSnapshots)) + for _, volSnap := range srcBackup.Config.VolumeSnapshots { + snapshotName := drivers.GetSnapshotVolumeName(vol.Name(), volSnap.Name) + snapshotStorageName := project.Instance(srcBackup.Project, snapshotName) + sourceSnapshots = append(sourceSnapshots, b.GetVolume(volType, contentType, snapshotStorageName, volSnap.Config)) + } + importRevert := revert.New() defer importRevert.Fail() + volCopy := drivers.NewVolumeCopy(vol, sourceSnapshots...) + // Unpack the backup into the new storage volume(s). - volPostHook, revertHook, err := b.driver.CreateVolumeFromBackup(vol, srcBackup, srcData, op) + volPostHook, revertHook, err := b.driver.CreateVolumeFromBackup(volCopy, srcBackup, srcData, op) if err != nil { return nil, nil, err } @@ -970,12 +979,28 @@ func (b *lxdBackend) CreateInstanceFromCopy(inst instance.Instance, src instance return fmt.Errorf("Source pool is not a lxdBackend") } - // Check source volume exists, and get its config. - srcConfig, err := srcPool.GenerateInstanceBackupConfig(src, snapshots, op) + // Check source volume exists, and get its config including all of the snapshots. + srcConfig, err := srcPool.GenerateInstanceBackupConfig(src, true, op) if err != nil { return fmt.Errorf("Failed generating instance copy config: %w", err) } + // Use the information from the backup config to create a list of all the source volume's snapshots. + // This way we don't have to retrieve them separately from the database. + sourceSnapshots := make([]drivers.Volume, 0, len(srcConfig.VolumeSnapshots)) + for _, sourceSnap := range srcConfig.VolumeSnapshots { + snapshotName := drivers.GetSnapshotVolumeName(src.Name(), sourceSnap.Name) + snapshotStorageName := project.Instance(src.Project().Name, snapshotName) + sourceSnapshots = append(sourceSnapshots, b.GetVolume(volType, contentType, snapshotStorageName, sourceSnap.Config)) + } + + // Unset the snapshots in the backup config if not requested by the caller. + // Those were only required to create the list of source volume snapshots. + if !snapshots { + srcConfig.Snapshots = nil + srcConfig.VolumeSnapshots = nil + } + // If we are copying snapshots, retrieve a list of snapshots from source volume. var snapshotNames []string if snapshots { @@ -1032,6 +1057,8 @@ func (b *lxdBackend) CreateInstanceFromCopy(inst instance.Instance, src instance revert.Add(func() { _ = VolumeDBDelete(b, inst.Project().Name, inst.Name(), volType) }) + targetSnapshots := make([]drivers.Volume, 0, len(snapshotNames)) + // Create database entries for new storage volume snapshots. for i, snapName := range snapshotNames { newSnapshotName := drivers.GetSnapshotVolumeName(inst.Name(), snapName) @@ -1047,6 +1074,9 @@ func (b *lxdBackend) CreateInstanceFromCopy(inst instance.Instance, src instance } revert.Add(func() { _ = VolumeDBDelete(b, inst.Project().Name, newSnapshotName, vol.Type()) }) + + newSnapshotStorageName := project.Instance(inst.Project().Name, newSnapshotName) + targetSnapshots = append(targetSnapshots, b.GetVolume(volType, contentType, newSnapshotStorageName, srcConfig.VolumeSnapshots[i].Config)) } // Generate the effective root device volume for instance. @@ -1055,7 +1085,10 @@ func (b *lxdBackend) CreateInstanceFromCopy(inst instance.Instance, src instance return err } - err = b.driver.CreateVolumeFromCopy(vol, srcVol, snapshots, allowInconsistent, op) + volCopy := drivers.NewVolumeCopy(vol, targetSnapshots...) + srcVolCopy := drivers.NewVolumeCopy(srcVol, sourceSnapshots...) + + err = b.driver.CreateVolumeFromCopy(volCopy, srcVolCopy, allowInconsistent, op) if err != nil { return err } @@ -1170,8 +1203,8 @@ func (b *lxdBackend) RefreshCustomVolume(projectName string, srcProjectName stri } } - // Check source volume exists and is custom type, and get its config. - srcConfig, err := srcPool.GenerateCustomVolumeBackupConfig(srcProjectName, srcVolName, snapshots, op) + // Check source volume exists and is custom type, and get its config including all of the snapshots. + srcConfig, err := srcPool.GenerateCustomVolumeBackupConfig(srcProjectName, srcVolName, true, op) if err != nil { return fmt.Errorf("Failed generating volume refresh config: %w", err) } @@ -1213,6 +1246,32 @@ func (b *lxdBackend) RefreshCustomVolume(projectName string, srcProjectName stri return fmt.Errorf("Storage pool does not support custom volume type") } + // Use the information from the backup config to create a list of all the source volume's snapshots. + // This way we don't have to retrieve them separately from the database. + sourceSnapshots := make([]drivers.Volume, 0, len(srcConfig.VolumeSnapshots)) + for _, sourceSnap := range srcConfig.VolumeSnapshots { + snapshotName := drivers.GetSnapshotVolumeName(srcVolName, sourceSnap.Name) + snapshotStorageName := project.StorageVolume(srcProjectName, snapshotName) + sourceSnapshots = append(sourceSnapshots, b.GetVolume(drivers.VolumeTypeCustom, contentType, snapshotStorageName, sourceSnap.Config)) + } + + // Unset the snapshots in the backup config if not requested by the caller. + // Those were only required to create the list of source volume snapshots. + if !snapshots { + srcConfig.VolumeSnapshots = nil + } + + targetSnaps, err := VolumeDBSnapshotsGet(b, projectName, volName, drivers.VolumeTypeCustom) + if err != nil { + return err + } + + targetSnapshots := make([]drivers.Volume, 0, len(targetSnaps)) + for _, targetSnap := range targetSnaps { + snapshotStorageName := project.StorageVolume(projectName, targetSnap.Name) + targetSnapshots = append(targetSnapshots, b.GetVolume(drivers.VolumeTypeCustom, contentType, snapshotStorageName, targetSnap.Config)) + } + revert := revert.New() defer revert.Fail() @@ -1229,11 +1288,6 @@ func (b *lxdBackend) RefreshCustomVolume(projectName string, srcProjectName stri }) } - targetSnaps, err := VolumeDBSnapshotsGet(b, projectName, volName, drivers.VolumeTypeCustom) - if err != nil { - return err - } - targetSnapshotsComparable := make([]ComparableSnapshot, 0, len(targetSnaps)) for _, targetSnap := range targetSnaps { _, targetSnapName, _ := api.GetParentAndSnapshotName(targetSnap.Name) @@ -1274,7 +1328,7 @@ func (b *lxdBackend) RefreshCustomVolume(projectName string, srcProjectName stri l.Debug("RefreshCustomVolume same-pool mode detected") // Only refresh the snapshots that the target needs. - srcSnapVols := make([]drivers.Volume, 0, len(srcConfig.VolumeSnapshots)) + srcSnapVols := make([]string, 0, len(srcConfig.VolumeSnapshots)) for _, srcSnap := range srcConfig.VolumeSnapshots { newSnapshotName := drivers.GetSnapshotVolumeName(volName, srcSnap.Name) snapExpiryDate := time.Time{} @@ -1294,10 +1348,16 @@ func (b *lxdBackend) RefreshCustomVolume(projectName string, srcProjectName stri srcSnapVolumeName := drivers.GetSnapshotVolumeName(srcVolName, srcSnap.Name) srcSnapVolStorageName := project.StorageVolume(projectName, srcSnapVolumeName) srcSnapVol := srcPool.GetVolume(drivers.VolumeTypeCustom, contentType, srcSnapVolStorageName, srcSnap.Config) - srcSnapVols = append(srcSnapVols, srcSnapVol) + srcSnapVols = append(srcSnapVols, srcSnap.Name) + + // Extend the list of target snaphots to not require loading all of them again from DB. + targetSnapshots = append(targetSnapshots, srcSnapVol) } - err = b.driver.RefreshVolume(vol, srcVol, srcSnapVols, false, op) + volCopy := drivers.NewVolumeCopy(vol, targetSnapshots...) + srcVolCopy := drivers.NewVolumeCopy(srcVol, sourceSnapshots...) + + err = b.driver.RefreshVolume(volCopy, srcVolCopy, srcSnapVols, false, op) if err != nil { return err } @@ -1457,16 +1517,24 @@ func (b *lxdBackend) RefreshInstance(inst instance.Instance, src instance.Instan return fmt.Errorf("Source pool is not a lxdBackend") } - // Check source volume exists, and get its config. - srcConfig, err := srcPool.GenerateInstanceBackupConfig(src, snapshots, op) + // Check source volume exists, and get its config including all of the snapshots. + srcConfig, err := srcPool.GenerateInstanceBackupConfig(src, true, op) if err != nil { return fmt.Errorf("Failed generating instance refresh config: %w", err) } // Ensure that only the requested snapshots are included in the source config. allSnapshots := srcConfig.VolumeSnapshots + sourceSnapshots := make([]drivers.Volume, 0, len(srcConfig.VolumeSnapshots)) srcConfig.VolumeSnapshots = make([]*api.StorageVolumeSnapshot, 0, len(srcSnapshots)) for i := range allSnapshots { + snapshotName := drivers.GetSnapshotVolumeName(src.Name(), allSnapshots[i].Name) + snapshotStorageName := project.Instance(src.Project().Name, snapshotName) + + // Use the information from the backup config to create a list of all the source volume's snapshots. + // This way we don't have to retrieve them separately from the database. + sourceSnapshots = append(sourceSnapshots, b.GetVolume(volType, contentType, snapshotStorageName, allSnapshots[i].Config)) + found := false for _, srcSnapshot := range srcSnapshots { _, srcSnapshotName, _ := api.GetParentAndSnapshotName(srcSnapshot.Name()) @@ -1481,18 +1549,20 @@ func (b *lxdBackend) RefreshInstance(inst instance.Instance, src instance.Instan } } + // Unset the snapshots in the backup config if not requested by the caller. + // Those were only required to create the list of source volume snapshots. + if !snapshots { + srcConfig.Snapshots = nil + srcConfig.VolumeSnapshots = nil + } + // Get source volume construct. srcVolStorageName := project.Instance(src.Project().Name, src.Name()) srcVol := b.GetVolume(volType, contentType, srcVolStorageName, srcConfig.Volume.Config) // Get source snapshot volume constructs. - srcSnapVols := make([]drivers.Volume, 0, len(srcConfig.VolumeSnapshots)) snapshotNames := make([]string, 0, len(srcConfig.VolumeSnapshots)) for i := range srcConfig.VolumeSnapshots { - newSnapshotName := drivers.GetSnapshotVolumeName(src.Name(), srcConfig.VolumeSnapshots[i].Name) - snapVolStorageName := project.Instance(src.Project().Name, newSnapshotName) - srcSnapVol := srcPool.GetVolume(volType, contentType, snapVolStorageName, srcConfig.VolumeSnapshots[i].Config) - srcSnapVols = append(srcSnapVols, srcSnapVol) snapshotNames = append(snapshotNames, srcConfig.VolumeSnapshots[i].Name) } @@ -1534,7 +1604,28 @@ func (b *lxdBackend) RefreshInstance(inst instance.Instance, src instance.Instan revert.Add(func() { _ = VolumeDBDelete(b, inst.Project().Name, newSnapshotName, volType) }) } - err = b.driver.RefreshVolume(vol, srcVol, srcSnapVols, allowInconsistent, op) + // Get all of the target instance's snapshots. + // Afterwards load the volume from the snapshot to ensure the right ordering. + instSnapshots, err := inst.Snapshots() + if err != nil { + return err + } + + targetSnapshots := make([]drivers.Volume, 0, len(instSnapshots)) + for _, instSnapshot := range instSnapshots { + snap, err := VolumeDBGet(b, inst.Project().Name, instSnapshot.Name(), volType) + if err != nil { + return err + } + + snapshotStorageName := project.Instance(inst.Project().Name, snap.Name) + targetSnapshots = append(targetSnapshots, b.GetVolume(volType, contentType, snapshotStorageName, snap.Config)) + } + + volCopy := drivers.NewVolumeCopy(vol, targetSnapshots...) + srcVolCopy := drivers.NewVolumeCopy(srcVol, sourceSnapshots...) + + err = b.driver.RefreshVolume(volCopy, srcVolCopy, snapshotNames, allowInconsistent, op) if err != nil { return err } @@ -1738,8 +1829,11 @@ func (b *lxdBackend) CreateInstanceFromImage(inst instance.Instance, fingerprint vol.SetConfigSize(newVolSize) l.Debug("Set new volume size", logger.Ctx{"size": newVolSize}) + volCopy := drivers.NewVolumeCopy(vol) + imgVolCopy := drivers.NewVolumeCopy(imgVol) + // Proceed to create a new volume by copying the optimized image volume. - err = b.driver.CreateVolumeFromCopy(vol, imgVol, false, false, op) + err = b.driver.CreateVolumeFromCopy(volCopy, imgVolCopy, false, op) // If the driver returns ErrCannotBeShrunk, this means that the cached volume that the new volume // is to be created from is larger than the requested new volume size, and cannot be shrunk. @@ -1995,7 +2089,27 @@ func (b *lxdBackend) CreateInstanceFromMigration(inst instance.Instance, conn io } } - err = b.driver.CreateVolumeFromMigration(vol, conn, args, &preFiller, op) + // Retrieve a list of target volume snapshots. + // Afterwards load the volume from the snapshot to ensure the right ordering. + instSnapshots, err := inst.Snapshots() + if err != nil { + return err + } + + targetSnapshots := make([]drivers.Volume, 0, len(instSnapshots)) + for _, instSnapshot := range instSnapshots { + snap, err := VolumeDBGet(b, inst.Project().Name, instSnapshot.Name(), volType) + if err != nil { + return err + } + + snapshotStorageName := project.Instance(inst.Project().Name, instSnapshot.Name()) + targetSnapshots = append(targetSnapshots, b.GetVolume(volType, contentType, snapshotStorageName, snap.Config)) + } + + volCopy := drivers.NewVolumeCopy(vol, targetSnapshots...) + + err = b.driver.CreateVolumeFromMigration(volCopy, conn, args, &preFiller, op) if err != nil { return err } @@ -2368,6 +2482,24 @@ func (b *lxdBackend) MigrateInstance(inst instance.Instance, conn io.ReadWriteCl return err } + // Retrieve a list of snapshots. + // Afterwards load the volume from the snapshot to ensure the right ordering. + instSnapshots, err := inst.Snapshots() + if err != nil { + return err + } + + sourceSnapshots := make([]drivers.Volume, 0, len(instSnapshots)) + for _, instSnapshot := range instSnapshots { + snap, err := VolumeDBGet(b, inst.Project().Name, instSnapshot.Name(), volType) + if err != nil { + return err + } + + snapshotStorageName := project.Instance(inst.Project().Name, snap.Name) + sourceSnapshots = append(sourceSnapshots, b.GetVolume(volType, contentType, snapshotStorageName, snap.Config)) + } + args.Name = inst.Name() // Override args.Name to ensure instance volume is sent. // Send migration index header frame with volume info and wait for receipt if not doing final sync. @@ -2405,7 +2537,9 @@ func (b *lxdBackend) MigrateInstance(inst instance.Instance, conn io.ReadWriteCl _ = filesystem.SyncFS(inst.RootfsPath()) } - err = b.driver.MigrateVolume(vol, conn, args, op) + volCopy := drivers.NewVolumeCopy(vol, sourceSnapshots...) + + err = b.driver.MigrateVolume(volCopy, conn, args, op) if err != nil { return err } @@ -2521,6 +2655,7 @@ func (b *lxdBackend) BackupInstance(inst instance.Instance, tarWriter *instancew } var snapNames []string + var sourceSnapshots []drivers.Volume if snapshots { // Get snapshots in age order, oldest first, and pass names to storage driver. instSnapshots, err := inst.Snapshots() @@ -2529,13 +2664,24 @@ func (b *lxdBackend) BackupInstance(inst instance.Instance, tarWriter *instancew } snapNames = make([]string, 0, len(instSnapshots)) + sourceSnapshots = make([]drivers.Volume, 0, len(instSnapshots)) for _, instSnapshot := range instSnapshots { + // Retrieve the underlying volume. + snapVol, err := VolumeDBGet(b, inst.Project().Name, instSnapshot.Name(), volType) + if err != nil { + return err + } + _, snapName, _ := api.GetParentAndSnapshotName(instSnapshot.Name()) snapNames = append(snapNames, snapName) + snapshotStorageName := project.Instance(inst.Project().Name, instSnapshot.Name()) + sourceSnapshots = append(sourceSnapshots, b.GetVolume(volType, contentType, snapshotStorageName, snapVol.Config)) } } - err = b.driver.BackupVolume(vol, tarWriter, optimized, snapNames, op) + volCopy := drivers.NewVolumeCopy(vol, sourceSnapshots...) + + err = b.driver.BackupVolume(volCopy, tarWriter, optimized, snapNames, op) if err != nil { return err } @@ -2618,7 +2764,6 @@ func (b *lxdBackend) SetInstanceQuota(inst instance.Instance, size string, vmSta } // Apply the main volume quota. - // There's no need to pass config as it's not needed when setting quotas. vol := b.GetVolume(volType, contentVolume, volStorageName, dbVol.Config) err = b.driver.SetVolumeQuota(vol, size, false, op) if err != nil { @@ -3057,7 +3202,7 @@ func (b *lxdBackend) RestoreInstanceSnapshot(inst instance.Instance, src instanc return err } - _, snapshotName, isSnap := api.GetParentAndSnapshotName(src.Name()) + _, _, isSnap := api.GetParentAndSnapshotName(src.Name()) if !isSnap { return fmt.Errorf("Volume name must be a snapshot") } @@ -3085,7 +3230,16 @@ func (b *lxdBackend) RestoreInstanceSnapshot(inst instance.Instance, src instanc }) } - err = b.driver.RestoreVolume(vol, snapshotName, op) + // Get the volume's snapshot from the DB. + dbSnapVol, err := VolumeDBGet(b, src.Project().Name, src.Name(), volType) + if err != nil { + return err + } + + snapshotStorageName := project.StorageVolume(src.Project().Name, dbSnapVol.Name) + snapVol := b.GetVolume(volType, contentType, snapshotStorageName, dbSnapVol.Config) + + err = b.driver.RestoreVolume(vol, snapVol, op) if err != nil { snapErr, ok := err.(drivers.ErrDeleteSnapshots) if ok { @@ -3110,7 +3264,7 @@ func (b *lxdBackend) RestoreInstanceSnapshot(inst instance.Instance, src instanc } // Now try restoring again. - err = b.driver.RestoreVolume(vol, snapshotName, op) + err = b.driver.RestoreVolume(vol, snapVol, op) if err != nil { return err } @@ -4395,13 +4549,7 @@ func (b *lxdBackend) CreateCustomVolume(projectName string, volName string, desc // Get the volume name on storage. volStorageName := project.StorageVolume(projectName, volName) - - // Validate config. vol := b.GetVolume(drivers.VolumeTypeCustom, contentType, volStorageName, config) - err = b.driver.ValidateVolume(vol, false) - if err != nil { - return err - } storagePoolSupported := false for _, supportedType := range b.Driver().Info().VolumeTypes { @@ -4476,8 +4624,8 @@ func (b *lxdBackend) CreateCustomVolumeFromCopy(projectName string, srcProjectNa } } - // Check source volume exists and is custom type, and get its config. - srcConfig, err := srcPool.GenerateCustomVolumeBackupConfig(srcProjectName, srcVolName, snapshots, op) + // Check source volume exists and is custom type, and get its config including all of the snapshots. + srcConfig, err := srcPool.GenerateCustomVolumeBackupConfig(srcProjectName, srcVolName, true, op) if err != nil { return fmt.Errorf("Failed generating volume copy config: %w", err) } @@ -4515,6 +4663,21 @@ func (b *lxdBackend) CreateCustomVolumeFromCopy(projectName string, srcProjectNa return fmt.Errorf("Storage pool does not support custom volume type") } + // Use the information from the backup config to create a list of all the source volume's snapshots. + // This way we don't have to retrieve them separately from the database. + sourceSnapshots := make([]drivers.Volume, 0, len(srcConfig.VolumeSnapshots)) + for _, sourceSnap := range srcConfig.VolumeSnapshots { + snapshotName := drivers.GetSnapshotVolumeName(srcVolName, sourceSnap.Name) + snapshotStorageName := project.StorageVolume(srcProjectName, snapshotName) + sourceSnapshots = append(sourceSnapshots, b.GetVolume(drivers.VolumeTypeCustom, contentType, snapshotStorageName, sourceSnap.Config)) + } + + // Unset the snapshots in the backup config if not requested by the caller. + // Those were only required to create the list of source volume snapshots. + if !snapshots { + srcConfig.VolumeSnapshots = nil + } + // If we are copying snapshots, retrieve a list of snapshots from source volume. var snapshotNames []string if snapshots { @@ -4548,6 +4711,8 @@ func (b *lxdBackend) CreateCustomVolumeFromCopy(projectName string, srcProjectNa revert.Add(func() { _ = VolumeDBDelete(b, projectName, volName, vol.Type()) }) + targetSnapshots := make([]drivers.Volume, 0, len(snapshotNames)) + // Create database entries for new storage volume snapshots. for i, snapName := range snapshotNames { newSnapshotName := drivers.GetSnapshotVolumeName(volName, snapName) @@ -4563,9 +4728,15 @@ func (b *lxdBackend) CreateCustomVolumeFromCopy(projectName string, srcProjectNa } revert.Add(func() { _ = VolumeDBDelete(b, projectName, newSnapshotName, vol.Type()) }) + + newSnapshotStorageName := project.StorageVolume(projectName, newSnapshotName) + targetSnapshots = append(targetSnapshots, b.GetVolume(drivers.VolumeTypeCustom, contentType, newSnapshotStorageName, srcConfig.VolumeSnapshots[i].Config)) } - err = b.driver.CreateVolumeFromCopy(vol, srcVol, snapshots, false, op) + volCopy := drivers.NewVolumeCopy(vol, targetSnapshots...) + srcVolCopy := drivers.NewVolumeCopy(srcVol, sourceSnapshots...) + + err = b.driver.CreateVolumeFromCopy(volCopy, srcVolCopy, false, op) if err != nil { return err } @@ -4826,7 +4997,22 @@ func (b *lxdBackend) MigrateCustomVolume(projectName string, conn io.ReadWriteCl } vol := b.GetVolume(drivers.VolumeTypeCustom, contentType, volStorageName, args.Info.Config.Volume.Config) - err = b.driver.MigrateVolume(vol, conn, args, op) + + // Retrieve a list of snapshots. + allSourceSnapshots, err := VolumeDBSnapshotsGet(b, projectName, args.Name, drivers.VolumeTypeCustom) + if err != nil { + return err + } + + sourceSnapshots := make([]drivers.Volume, 0, len(allSourceSnapshots)) + for _, sourceSnapshot := range allSourceSnapshots { + snapshotStorageName := project.StorageVolume(projectName, sourceSnapshot.Name) + sourceSnapshots = append(sourceSnapshots, b.GetVolume(drivers.VolumeTypeCustom, contentType, snapshotStorageName, sourceSnapshot.Config)) + } + + volCopy := drivers.NewVolumeCopy(vol, sourceSnapshots...) + + err = b.driver.MigrateVolume(volCopy, conn, args, op) if err != nil { return err } @@ -4968,7 +5154,21 @@ func (b *lxdBackend) CreateCustomVolumeFromMigration(projectName string, conn io } } - err = b.driver.CreateVolumeFromMigration(vol, conn, args, nil, op) + // Retrieve a list of target volume snapshots. + allTargetSnapshots, err := VolumeDBSnapshotsGet(b, projectName, args.Name, drivers.VolumeTypeCustom) + if err != nil { + return err + } + + targetSnapshots := make([]drivers.Volume, 0, len(allTargetSnapshots)) + for _, targetSnapshot := range allTargetSnapshots { + snapshotStorageName := project.StorageVolume(projectName, targetSnapshot.Name) + targetSnapshots = append(targetSnapshots, b.GetVolume(drivers.VolumeTypeCustom, vol.ContentType(), snapshotStorageName, targetSnapshot.Config)) + } + + volCopy := drivers.NewVolumeCopy(vol, targetSnapshots...) + + err = b.driver.CreateVolumeFromMigration(volCopy, conn, args, nil, op) if err != nil { return err } @@ -5786,7 +5986,17 @@ func (b *lxdBackend) RestoreCustomVolume(projectName, volName string, snapshotNa volStorageName := project.StorageVolume(projectName, volName) vol := b.GetVolume(drivers.VolumeTypeCustom, contentType, volStorageName, curVol.Config) - err = b.driver.RestoreVolume(vol, snapshotName, op) + // Get the volume's snapshot from the DB. + fullSnapshotName := drivers.GetSnapshotVolumeName(volName, snapshotName) + dbSnapVol, err := VolumeDBGet(b, projectName, fullSnapshotName, drivers.VolumeTypeCustom) + if err != nil { + return err + } + + snapshotStorageName := project.StorageVolume(projectName, dbSnapVol.Name) + snapVol := b.GetVolume(drivers.VolumeTypeCustom, contentType, snapshotStorageName, dbSnapVol.Config) + + err = b.driver.RestoreVolume(vol, snapVol, op) if err != nil { snapErr, ok := err.(drivers.ErrDeleteSnapshots) if ok { @@ -5799,7 +6009,7 @@ func (b *lxdBackend) RestoreCustomVolume(projectName, volName string, snapshotNa } // Now try again. - err = b.driver.RestoreVolume(vol, snapshotName, op) + err = b.driver.RestoreVolume(vol, snapVol, op) if err != nil { return err } @@ -6672,6 +6882,7 @@ func (b *lxdBackend) BackupCustomVolume(projectName string, volName string, tarW } var snapNames []string + var sourceSnapshots []drivers.Volume if snapshots { // Get snapshots in age order, oldest first, and pass names to storage driver. volSnaps, err := VolumeDBSnapshotsGet(b, projectName, volName, drivers.VolumeTypeCustom) @@ -6680,15 +6891,21 @@ func (b *lxdBackend) BackupCustomVolume(projectName string, volName string, tarW } snapNames = make([]string, 0, len(volSnaps)) + sourceSnapshots = make([]drivers.Volume, 0, len(volSnaps)) for _, volSnap := range volSnaps { _, snapName, _ := api.GetParentAndSnapshotName(volSnap.Name) snapNames = append(snapNames, snapName) + + snapshotStorageName := project.StorageVolume(projectName, volSnap.Name) + sourceSnapshots = append(sourceSnapshots, b.GetVolume(drivers.VolumeTypeCustom, contentType, snapshotStorageName, volSnap.Config)) } } vol := b.GetVolume(drivers.VolumeTypeCustom, drivers.ContentType(volume.ContentType), volStorageName, volume.Config) - err = b.driver.BackupVolume(vol, tarWriter, optimized, snapNames, op) + volCopy := drivers.NewVolumeCopy(vol, sourceSnapshots...) + + err = b.driver.BackupVolume(volCopy, tarWriter, optimized, snapNames, op) if err != nil { return err } @@ -6821,6 +7038,8 @@ func (b *lxdBackend) CreateCustomVolumeFromBackup(srcBackup backup.Info, srcData revert.Add(func() { _ = VolumeDBDelete(b, srcBackup.Project, srcBackup.Name, vol.Type()) }) + sourceSnapshots := make([]drivers.Volume, 0, len(srcBackup.Config.VolumeSnapshots)) + // Create database entries fro new storage volume snapshots. for _, s := range srcBackup.Config.VolumeSnapshots { snapshot := s // Local var for revert. @@ -6844,10 +7063,14 @@ func (b *lxdBackend) CreateCustomVolumeFromBackup(srcBackup backup.Info, srcData } revert.Add(func() { _ = VolumeDBDelete(b, srcBackup.Project, fullSnapName, snapVol.Type()) }) + + sourceSnapshots = append(sourceSnapshots, b.GetVolume(drivers.VolumeTypeCustom, snapVol.ContentType(), snapVolStorageName, snapVol.Config())) } + volCopy := drivers.NewVolumeCopy(vol, sourceSnapshots...) + // Unpack the backup into the new storage volume(s). - volPostHook, revertHook, err := b.driver.CreateVolumeFromBackup(vol, srcBackup, srcData, op) + volPostHook, revertHook, err := b.driver.CreateVolumeFromBackup(volCopy, srcBackup, srcData, op) if err != nil { return err } diff --git a/lxd/storage/drivers/driver_btrfs_volumes.go b/lxd/storage/drivers/driver_btrfs_volumes.go index 412974b33778..65a7e6792c1d 100644 --- a/lxd/storage/drivers/driver_btrfs_volumes.go +++ b/lxd/storage/drivers/driver_btrfs_volumes.go @@ -149,13 +149,13 @@ func (d *btrfs) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Op } // CreateVolumeFromBackup restores a backup tarball onto the storage device. -func (d *btrfs) CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) { +func (d *btrfs) CreateVolumeFromBackup(vol VolumeCopy, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) { // Handle the non-optimized tarballs through the generic unpacker. if !*srcBackup.OptimizedStorage { return genericVFSBackupUnpack(d, d.state.OS, vol, srcBackup.Snapshots, srcData, op) } - volExists, err := d.HasVolume(vol) + volExists, err := d.HasVolume(vol.Volume) if err != nil { return nil, nil, err } @@ -177,7 +177,7 @@ func (d *btrfs) CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcDat } // And lastly the main volume. - _ = d.DeleteVolume(vol, op) + _ = d.DeleteVolume(vol.Volume, op) } // Only execute the revert function if we have had an error internally. revert.Add(revertHook) @@ -359,7 +359,7 @@ func (d *btrfs) CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcDat srcFilePrefix = "volume" } - err = unpackVolume(vol, srcFilePrefix) + err = unpackVolume(vol.Volume, srcFilePrefix) if err != nil { return nil, nil, err } @@ -386,9 +386,9 @@ func (d *btrfs) CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcDat continue // All subvolumes are made writable during unpack process so we can skip these. } - v := vol + v := vol.Volume if subVol.Snapshot != "" { - v, _ = vol.NewSnapshot(subVol.Snapshot) + v, _ = vol.Volume.NewSnapshot(subVol.Snapshot) } path := filepath.Join(v.MountPath(), subVol.Path) @@ -404,12 +404,12 @@ func (d *btrfs) CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcDat } // CreateVolumeFromCopy provides same-pool volume copying functionality. -func (d *btrfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, allowInconsistent bool, op *operations.Operation) error { +func (d *btrfs) CreateVolumeFromCopy(vol VolumeCopy, srcVol VolumeCopy, allowInconsistent bool, op *operations.Operation) error { revert := revert.New() defer revert.Fail() // Scan source for subvolumes (so we can apply the readonly properties on the new volume). - subVols, err := d.getSubvolumesMetaData(srcVol) + subVols, err := d.getSubvolumesMetaData(srcVol.Volume) if err != nil { return err } @@ -442,7 +442,7 @@ func (d *btrfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bo // Resize volume to the size specified. Only uses volume "size" property and does not use pool/defaults // to give the caller more control over the size being used. - err = d.SetVolumeQuota(vol, vol.config["size"], false, op) + err = d.SetVolumeQuota(vol.Volume, vol.config["size"], false, op) if err != nil { return err } @@ -456,9 +456,9 @@ func (d *btrfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bo var snapshots []string // Get snapshot list if copying snapshots. - if copySnapshots && !srcVol.IsSnapshot() { + if len(vol.Snapshots) > 0 && !srcVol.IsSnapshot() { // Get the list of snapshots. - snapshots, err = d.VolumeSnapshots(srcVol, op) + snapshots, err = d.VolumeSnapshots(srcVol.Volume, op) if err != nil { return err } @@ -500,7 +500,7 @@ func (d *btrfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bo } // CreateVolumeFromMigration creates a volume being sent via a migration. -func (d *btrfs) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error { +func (d *btrfs) CreateVolumeFromMigration(vol VolumeCopy, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error { // Handle simple rsync and block_and_rsync through generic. if volTargetArgs.MigrationType.FSType == migration.MigrationFSType_RSYNC || volTargetArgs.MigrationType.FSType == migration.MigrationFSType_BLOCK_AND_RSYNC { return genericVFSCreateVolumeFromMigration(d, nil, vol, conn, volTargetArgs, preFiller, op) @@ -544,7 +544,7 @@ func (d *btrfs) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, v } if volTargetArgs.Refresh && shared.ValueInSlice(migration.BTRFSFeatureSubvolumeUUIDs, volTargetArgs.MigrationType.Features) { - snapshots, err := d.volumeSnapshotsSorted(vol, op) + snapshots, err := d.volumeSnapshotsSorted(vol.Volume, op) if err != nil { return err } @@ -603,7 +603,7 @@ func (d *btrfs) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, v syncSubvolumes = migrationHeader.Subvolumes } - return d.createVolumeFromMigrationOptimized(vol, conn, volTargetArgs, preFiller, syncSubvolumes, op) + return d.createVolumeFromMigrationOptimized(vol.Volume, conn, volTargetArgs, preFiller, syncSubvolumes, op) } func (d *btrfs) createVolumeFromMigrationOptimized(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, subvolumes []BTRFSSubVolume, op *operations.Operation) error { @@ -774,14 +774,14 @@ func (d *btrfs) createVolumeFromMigrationOptimized(vol Volume, conn io.ReadWrite } // RefreshVolume provides same-pool volume and specific snapshots syncing functionality. -func (d *btrfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, allowInconsistent bool, op *operations.Operation) error { +func (d *btrfs) RefreshVolume(vol VolumeCopy, srcVol VolumeCopy, refreshSnapshots []string, allowInconsistent bool, op *operations.Operation) error { // Get target snapshots - targetSnapshots, err := d.volumeSnapshotsSorted(vol, op) + targetSnapshots, err := d.volumeSnapshotsSorted(vol.Volume, op) if err != nil { return fmt.Errorf("Failed to get target snapshots: %w", err) } - srcSnapshotsAll, err := d.volumeSnapshotsSorted(srcVol, op) + srcSnapshotsAll, err := d.volumeSnapshotsSorted(srcVol.Volume, op) if err != nil { return fmt.Errorf("Failed to get source snapshots: %w", err) } @@ -790,7 +790,7 @@ func (d *btrfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, // as btrfs can then use an incremental streams instead of just copying the datasets. if len(targetSnapshots) == 0 || len(srcSnapshotsAll) == 0 { d.logger.Debug("Performing generic volume refresh") - return genericVFSCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, false, op) + return genericVFSCopyVolume(d, nil, vol.Volume, srcVol.Volume, refreshSnapshots, true, false, op) } d.logger.Debug("Performing optimized volume refresh") @@ -834,7 +834,7 @@ func (d *btrfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, // most recent identical snapshot of the source volume and target volume. lastIdenticalSnapshot := targetSnapshots[len(targetSnapshots)-1] - for i, snap := range srcSnapshots { + for i, refreshSnapshot := range refreshSnapshots { var srcSnap Volume if i == 0 { @@ -843,10 +843,18 @@ func (d *btrfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, return fmt.Errorf("Failed to create new snapshot volume: %w", err) } } else { - srcSnap = srcSnapshots[i-1] + srcSnap, err = srcVol.NewSnapshot(refreshSnapshots[i-1]) + if err != nil { + return fmt.Errorf("Failed to create new snapshot volume: %w", err) + } + } + + snap, err := srcVol.NewSnapshot(refreshSnapshot) + if err != nil { + return err } - err = transfer(snap, vol, srcSnap) + err = transfer(snap, vol.Volume, srcSnap) if err != nil { return err } @@ -871,7 +879,7 @@ func (d *btrfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, return err } - err = transfer(srcSnap, vol, parentSnap) + err = transfer(srcSnap, vol.Volume, parentSnap) if err != nil { return err } @@ -1236,13 +1244,13 @@ func (d *btrfs) readonlySnapshot(vol Volume) (string, revert.Hook, error) { } // MigrateVolume sends a volume for migration. -func (d *btrfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error { +func (d *btrfs) MigrateVolume(vol VolumeCopy, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error { // Handle simple rsync and block_and_rsync through generic. if volSrcArgs.MigrationType.FSType == migration.MigrationFSType_RSYNC || volSrcArgs.MigrationType.FSType == migration.MigrationFSType_BLOCK_AND_RSYNC { // If volume is filesystem type and is not already a snapshot, create a fast snapshot to ensure migration is consistent. // TODO add support for temporary snapshots of block volumes here. if vol.contentType == ContentTypeFS && !vol.IsSnapshot() { - snapshotPath, cleanup, err := d.readonlySnapshot(vol) + snapshotPath, cleanup, err := d.readonlySnapshot(vol.Volume) if err != nil { return err } @@ -1254,7 +1262,7 @@ func (d *btrfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *m vol.mountCustomPath = snapshotPath } - return genericVFSMigrateVolume(d, d.state, vol, conn, volSrcArgs, op) + return genericVFSMigrateVolume(d, d.state, vol.Volume, conn, volSrcArgs, op) } else if volSrcArgs.MigrationType.FSType != migration.MigrationFSType_BTRFS { return ErrNotSupported } @@ -1270,13 +1278,13 @@ func (d *btrfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *m if !volSrcArgs.VolumeOnly { // Generate restoration header, containing info on the subvolumes and how they should be restored. - snapshots, err = d.volumeSnapshotsSorted(vol, op) + snapshots, err = d.volumeSnapshotsSorted(vol.Volume, op) if err != nil { return err } } - migrationHeader, err := d.restorationHeader(vol, snapshots) + migrationHeader, err := d.restorationHeader(vol.Volume, snapshots) if err != nil { return err } @@ -1336,7 +1344,7 @@ func (d *btrfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *m } } - return d.migrateVolumeOptimized(vol, conn, volSrcArgs, migrationHeader.Subvolumes, op) + return d.migrateVolumeOptimized(vol.Volume, conn, volSrcArgs, migrationHeader.Subvolumes, op) } func (d *btrfs) migrateVolumeOptimized(vol Volume, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, subvolumes []BTRFSSubVolume, op *operations.Operation) error { @@ -1485,14 +1493,14 @@ func (d *btrfs) migrateVolumeOptimized(vol Volume, conn io.ReadWriteCloser, volS // BackupVolume copies a volume (and optionally its snapshots) to a specified target path. // This driver does not support optimized backups. -func (d *btrfs) BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots []string, op *operations.Operation) error { +func (d *btrfs) BackupVolume(vol VolumeCopy, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots []string, op *operations.Operation) error { // Handle the non-optimized tarballs through the generic packer. if !optimized { // Because the generic backup method will not take a consistent backup if files are being modified // as they are copied to the tarball, as BTRFS allows us to take a quick snapshot without impacting // the parent volume we do so here to ensure the backup taken is consistent. if vol.contentType == ContentTypeFS { - snapshotPath, cleanup, err := d.readonlySnapshot(vol) + snapshotPath, cleanup, err := d.readonlySnapshot(vol.Volume) if err != nil { return err } @@ -1518,7 +1526,7 @@ func (d *btrfs) BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWr } // Generate driver restoration header. - optimizedHeader, err := d.restorationHeader(vol, snapshots) + optimizedHeader, err := d.restorationHeader(vol.Volume, snapshots) if err != nil { return err } @@ -1725,7 +1733,7 @@ func (d *btrfs) BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWr fileNamePrefix = "volume" } - err = addVolume(vol, targetVolume, lastVolPath, fileNamePrefix) + err = addVolume(vol.Volume, targetVolume, lastVolPath, fileNamePrefix) if err != nil { return err } @@ -1900,10 +1908,11 @@ func (d *btrfs) volumeSnapshotsSorted(vol Volume, op *operations.Operation) ([]s } // RestoreVolume restores a volume from a snapshot. -func (d *btrfs) RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error { +func (d *btrfs) RestoreVolume(vol Volume, snapVol Volume, op *operations.Operation) error { revert := revert.New() defer revert.Fail() + _, snapshotName, _ := api.GetParentAndSnapshotName(snapVol.name) srcVol := NewVolume(d, d.name, vol.volType, vol.contentType, GetSnapshotVolumeName(vol.name, snapshotName), vol.config, vol.poolConfig) // Scan source for subvolumes (so we can apply the readonly properties on the restored snapshot). diff --git a/lxd/storage/drivers/driver_ceph_volumes.go b/lxd/storage/drivers/driver_ceph_volumes.go index 092ea8c9e72f..df664ae99ea7 100644 --- a/lxd/storage/drivers/driver_ceph_volumes.go +++ b/lxd/storage/drivers/driver_ceph_volumes.go @@ -314,12 +314,12 @@ func (d *ceph) getVolumeSize(volumeName string) (int64, error) { } // CreateVolumeFromBackup re-creates a volume from its exported state. -func (d *ceph) CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) { +func (d *ceph) CreateVolumeFromBackup(vol VolumeCopy, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) { return genericVFSBackupUnpack(d, d.state.OS, vol, srcBackup.Snapshots, srcData, op) } // CreateVolumeFromCopy provides same-pool volume copying functionality. -func (d *ceph) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, allowInconsistent bool, op *operations.Operation) error { +func (d *ceph) CreateVolumeFromCopy(vol VolumeCopy, srcVol VolumeCopy, allowInconsistent bool, op *operations.Operation) error { var err error revert := revert.New() defer revert.Fail() @@ -354,7 +354,7 @@ func (d *ceph) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots boo // Resize volume to the size specified. Only uses volume "size" property and does not use // pool/defaults to give the caller more control over the size being used. - err = d.SetVolumeQuota(vol, vol.config["size"], false, op) + err = d.SetVolumeQuota(vol.Volume, vol.config["size"], false, op) if err != nil { return err } @@ -364,28 +364,28 @@ func (d *ceph) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots boo // For VMs, also copy the filesystem volume. if vol.IsVMBlock() { - srcFSVol := srcVol.NewVMBlockFilesystemVolume() - fsVol := vol.NewVMBlockFilesystemVolume() - err := d.CreateVolumeFromCopy(fsVol, srcFSVol, copySnapshots, false, op) + srcFSVol := NewVolumeCopy(srcVol.NewVMBlockFilesystemVolume()) + fsVol := NewVolumeCopy(vol.NewVMBlockFilesystemVolume()) + err := d.CreateVolumeFromCopy(fsVol, srcFSVol, false, op) if err != nil { return err } // Delete on revert. - revert.Add(func() { _ = d.DeleteVolume(fsVol, op) }) + revert.Add(func() { _ = d.DeleteVolume(fsVol.Volume, op) }) } // Retrieve snapshots on the source. snapshots := []string{} - if !srcVol.IsSnapshot() && copySnapshots { - snapshots, err = d.VolumeSnapshots(srcVol, op) + if !srcVol.IsSnapshot() && len(vol.Snapshots) > 0 { + snapshots, err = d.VolumeSnapshots(srcVol.Volume, op) if err != nil { return err } } // Copy without snapshots. - if !copySnapshots || len(snapshots) == 0 { + if len(vol.Snapshots) == 0 || len(snapshots) == 0 { // If lightweight clone mode isn't enabled, perform a full copy of the volume. if shared.IsFalse(d.config["ceph.rbd.clone_copy"]) { _, err = shared.RunCommand( @@ -393,21 +393,21 @@ func (d *ceph) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots boo "--id", d.config["ceph.user.name"], "--cluster", d.config["ceph.cluster_name"], "cp", - d.getRBDVolumeName(srcVol, "", false, true), - d.getRBDVolumeName(vol, "", false, true), + d.getRBDVolumeName(srcVol.Volume, "", false, true), + d.getRBDVolumeName(vol.Volume, "", false, true), ) if err != nil { return err } - revert.Add(func() { _ = d.DeleteVolume(vol, op) }) + revert.Add(func() { _ = d.DeleteVolume(vol.Volume, op) }) - _, err = d.rbdMapVolume(vol) + _, err = d.rbdMapVolume(vol.Volume) if err != nil { return err } - revert.Add(func() { _ = d.rbdUnmapVolume(vol, true) }) + revert.Add(func() { _ = d.rbdUnmapVolume(vol.Volume, true) }) } else { parentVol := srcVol snapshotName := "readonly" @@ -418,33 +418,33 @@ func (d *ceph) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots boo if srcVol.IsSnapshot() { srcParentName, srcSnapOnlyName, _ := api.GetParentAndSnapshotName(srcVol.name) snapshotName = fmt.Sprintf("snapshot_%s", srcSnapOnlyName) - parentVol = NewVolume(d, d.name, srcVol.volType, srcVol.contentType, srcParentName, nil, nil) + parentVol = NewVolumeCopy(NewVolume(d, d.name, srcVol.volType, srcVol.contentType, srcParentName, nil, nil)) } else { // Create snapshot. - err := d.rbdCreateVolumeSnapshot(srcVol, snapshotName) + err := d.rbdCreateVolumeSnapshot(srcVol.Volume, snapshotName) if err != nil { return err } } // Protect volume so we can create clones of it. - err = d.rbdProtectVolumeSnapshot(parentVol, snapshotName) + err = d.rbdProtectVolumeSnapshot(parentVol.Volume, snapshotName) if err != nil { return err } - revert.Add(func() { _ = d.rbdUnprotectVolumeSnapshot(parentVol, snapshotName) }) + revert.Add(func() { _ = d.rbdUnprotectVolumeSnapshot(parentVol.Volume, snapshotName) }) } - err = d.rbdCreateClone(parentVol, snapshotName, vol) + err = d.rbdCreateClone(parentVol.Volume, snapshotName, vol.Volume) if err != nil { return err } - revert.Add(func() { _ = d.DeleteVolume(vol, op) }) + revert.Add(func() { _ = d.DeleteVolume(vol.Volume, op) }) } - err = postCreateTasks(vol) + err = postCreateTasks(vol.Volume) if err != nil { return err } @@ -456,15 +456,15 @@ func (d *ceph) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots boo // Copy with snapshots. // Create empty placeholder volume - err = d.rbdCreateVolume(vol, "0") + err = d.rbdCreateVolume(vol.Volume, "0") if err != nil { return err } - revert.Add(func() { _ = d.rbdDeleteVolume(vol) }) + revert.Add(func() { _ = d.rbdDeleteVolume(vol.Volume) }) // Receive over the placeholder volume we created above. - targetVolumeName := d.getRBDVolumeName(vol, "", false, true) + targetVolumeName := d.getRBDVolumeName(vol.Volume, "", false, true) lastSnap := "" @@ -482,13 +482,13 @@ func (d *ceph) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots boo } lastSnap = fmt.Sprintf("snapshot_%s", snap) - sourceVolumeName := d.getRBDVolumeName(srcVol, lastSnap, false, true) + sourceVolumeName := d.getRBDVolumeName(srcVol.Volume, lastSnap, false, true) err = d.copyWithSnapshots(sourceVolumeName, targetVolumeName, prev) if err != nil { return err } - revert.Add(func() { _ = d.rbdDeleteVolumeSnapshot(vol, snap) }) + revert.Add(func() { _ = d.rbdDeleteVolumeSnapshot(vol.Volume, snap) }) snapVol, err := vol.NewSnapshot(snap) if err != nil { @@ -502,14 +502,14 @@ func (d *ceph) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots boo } // Copy snapshot. - sourceVolumeName := d.getRBDVolumeName(srcVol, "", false, true) + sourceVolumeName := d.getRBDVolumeName(srcVol.Volume, "", false, true) err = d.copyWithSnapshots(sourceVolumeName, targetVolumeName, lastSnap) if err != nil { return err } - err = postCreateTasks(vol) + err = postCreateTasks(vol.Volume) if err != nil { return err } @@ -519,7 +519,7 @@ func (d *ceph) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots boo } // CreateVolumeFromMigration creates a volume being sent via a migration. -func (d *ceph) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error { +func (d *ceph) CreateVolumeFromMigration(vol VolumeCopy, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error { if volTargetArgs.ClusterMoveSourceName != "" { err := vol.EnsureMountPath() if err != nil { @@ -527,7 +527,7 @@ func (d *ceph) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, vo } if vol.IsVMBlock() { - fsVol := vol.NewVMBlockFilesystemVolume() + fsVol := NewVolumeCopy(vol.NewVMBlockFilesystemVolume()) err := d.CreateVolumeFromMigration(fsVol, conn, volTargetArgs, preFiller, op) if err != nil { return err @@ -545,22 +545,22 @@ func (d *ceph) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, vo } if vol.IsVMBlock() { - fsVol := vol.NewVMBlockFilesystemVolume() + fsVol := NewVolumeCopy(vol.NewVMBlockFilesystemVolume()) err := d.CreateVolumeFromMigration(fsVol, conn, volTargetArgs, preFiller, op) if err != nil { return err } } - recvName := d.getRBDVolumeName(vol, "", false, true) + recvName := d.getRBDVolumeName(vol.Volume, "", false, true) - volExists, err := d.HasVolume(vol) + volExists, err := d.HasVolume(vol.Volume) if err != nil { return err } if !volExists { - err := d.rbdCreateVolume(vol, "0") + err := d.rbdCreateVolume(vol.Volume, "0") if err != nil { return err } @@ -581,7 +581,7 @@ func (d *ceph) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, vo // Transfer the snapshots. for _, snapName := range volTargetArgs.Snapshots { - fullSnapshotName := d.getRBDVolumeName(vol, snapName, false, true) + fullSnapshotName := d.getRBDVolumeName(vol.Volume, snapName, false, true) wrapper := migration.ProgressWriter(op, "fs_progress", fullSnapshotName) err = d.receiveVolume(recvName, conn, wrapper) @@ -603,7 +603,7 @@ func (d *ceph) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, vo defer func() { // Delete all migration-send-* snapshots. - snaps, err := d.rbdListVolumeSnapshots(vol) + snaps, err := d.rbdListVolumeSnapshots(vol.Volume) if err != nil { return } @@ -613,7 +613,7 @@ func (d *ceph) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, vo continue } - _ = d.rbdDeleteVolumeSnapshot(vol, snap) + _ = d.rbdDeleteVolumeSnapshot(vol.Volume, snap) } }() @@ -625,12 +625,12 @@ func (d *ceph) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, vo } // Map the RBD volume. - devPath, err := d.rbdMapVolume(vol) + devPath, err := d.rbdMapVolume(vol.Volume) if err != nil { return err } - defer func() { _ = d.rbdUnmapVolume(vol, true) }() + defer func() { _ = d.rbdUnmapVolume(vol.Volume, true) }() // Re-generate the UUID. err = d.generateUUID(vol.ConfigBlockFilesystem(), devPath) @@ -642,8 +642,8 @@ func (d *ceph) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, vo } // RefreshVolume updates an existing volume to match the state of another. -func (d *ceph) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, allowInconsistent bool, op *operations.Operation) error { - return genericVFSCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, allowInconsistent, op) +func (d *ceph) RefreshVolume(vol VolumeCopy, srcVol VolumeCopy, refreshSnapshots []string, allowInconsistent bool, op *operations.Operation) error { + return genericVFSCopyVolume(d, nil, vol.Volume, srcVol.Volume, refreshSnapshots, true, allowInconsistent, op) } // DeleteVolume deletes a volume of the storage device. If any snapshots of the volume remain then @@ -1363,7 +1363,7 @@ func (d *ceph) RenameVolume(vol Volume, newVolName string, op *operations.Operat } // MigrateVolume sends a volume for migration. -func (d *ceph) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error { +func (d *ceph) MigrateVolume(vol VolumeCopy, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error { if volSrcArgs.ClusterMove { return nil // When performing a cluster member move don't do anything on the source member. } @@ -1382,7 +1382,7 @@ func (d *ceph) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *mi defer func() { _, _ = d.UnmountVolume(parentVol, false, op) }() - return genericVFSMigrateVolume(d, d.state, vol, conn, volSrcArgs, op) + return genericVFSMigrateVolume(d, d.state, vol.Volume, conn, volSrcArgs, op) } else if volSrcArgs.MigrationType.FSType != migration.MigrationFSType_RBD { return ErrNotSupported } @@ -1394,8 +1394,8 @@ func (d *ceph) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *mi } if vol.IsVMBlock() { - fsVol := vol.NewVMBlockFilesystemVolume() - err := d.MigrateVolume(fsVol, conn, volSrcArgs, op) + fsVolCopy := NewVolumeCopy(vol.NewVMBlockFilesystemVolume()) + err := d.MigrateVolume(fsVolCopy, conn, volSrcArgs, op) if err != nil { return err } @@ -1441,7 +1441,7 @@ func (d *ceph) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *mi } lastSnap = fmt.Sprintf("snapshot_%s", snapName) - sendSnapName := d.getRBDVolumeName(vol, lastSnap, false, true) + sendSnapName := d.getRBDVolumeName(vol.Volume, lastSnap, false, true) // Setup progress tracking. var wrapper *ioprogress.ProgressTracker @@ -1464,14 +1464,14 @@ func (d *ceph) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *mi runningSnapName := fmt.Sprintf("migration-send-%s", uuid.New().String()) - err := d.rbdCreateVolumeSnapshot(vol, runningSnapName) + err := d.rbdCreateVolumeSnapshot(vol.Volume, runningSnapName) if err != nil { return err } - defer func() { _ = d.rbdDeleteVolumeSnapshot(vol, runningSnapName) }() + defer func() { _ = d.rbdDeleteVolumeSnapshot(vol.Volume, runningSnapName) }() - cur := d.getRBDVolumeName(vol, runningSnapName, false, true) + cur := d.getRBDVolumeName(vol.Volume, runningSnapName, false, true) err = d.sendVolume(conn, cur, lastSnap, wrapper) if err != nil { @@ -1482,7 +1482,7 @@ func (d *ceph) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *mi } // BackupVolume creates an exported version of a volume. -func (d *ceph) BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots []string, op *operations.Operation) error { +func (d *ceph) BackupVolume(vol VolumeCopy, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots []string, op *operations.Operation) error { return genericVFSBackupVolume(d, vol, tarWriter, snapshots, op) } @@ -1801,7 +1801,7 @@ func (d *ceph) VolumeSnapshots(vol Volume, op *operations.Operation) ([]string, } // RestoreVolume restores a volume from a snapshot. -func (d *ceph) RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error { +func (d *ceph) RestoreVolume(vol Volume, snapVol Volume, op *operations.Operation) error { ourUnmount, err := d.UnmountVolume(vol, false, op) if err != nil { return err @@ -1811,6 +1811,8 @@ func (d *ceph) RestoreVolume(vol Volume, snapshotName string, op *operations.Ope defer func() { _ = d.MountVolume(vol, op) }() } + _, snapshotName, _ := api.GetParentAndSnapshotName(snapVol.name) + _, err = shared.RunCommand( "rbd", "--id", d.config["ceph.user.name"], @@ -1843,7 +1845,8 @@ func (d *ceph) RestoreVolume(vol Volume, snapshotName string, op *operations.Ope // For VM images, restore the filesystem volume too. if vol.IsVMBlock() { fsVol := vol.NewVMBlockFilesystemVolume() - err := d.RestoreVolume(fsVol, snapshotName, op) + fsSnapVol := snapVol.NewVMBlockFilesystemVolume() + err := d.RestoreVolume(fsVol, fsSnapVol, op) if err != nil { return err } diff --git a/lxd/storage/drivers/driver_cephfs_volumes.go b/lxd/storage/drivers/driver_cephfs_volumes.go index f6105f3489d9..fc9ae3c0b6d7 100644 --- a/lxd/storage/drivers/driver_cephfs_volumes.go +++ b/lxd/storage/drivers/driver_cephfs_volumes.go @@ -62,12 +62,12 @@ func (d *cephfs) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.O } // CreateVolumeFromBackup re-creates a volume from its exported state. -func (d *cephfs) CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) { +func (d *cephfs) CreateVolumeFromBackup(vol VolumeCopy, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) { return genericVFSBackupUnpack(d, d.state.OS, vol, srcBackup.Snapshots, srcData, op) } // CreateVolumeFromCopy copies an existing storage volume (with or without snapshots) into a new volume. -func (d *cephfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, allowInconsistent bool, op *operations.Operation) error { +func (d *cephfs) CreateVolumeFromCopy(vol VolumeCopy, srcVol VolumeCopy, allowInconsistent bool, op *operations.Operation) error { bwlimit := d.config["rsync.bwlimit"] // Create the main volume path. @@ -98,9 +98,9 @@ func (d *cephfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots b // Ensure the volume is mounted. err = vol.MountTask(func(mountPath string, op *operations.Operation) error { // If copying snapshots is indicated, check the source isn't itself a snapshot. - if copySnapshots && !srcVol.IsSnapshot() { + if len(vol.Snapshots) > 0 && !srcVol.IsSnapshot() { // Get the list of snapshots from the source. - srcSnapshots, err := srcVol.Snapshots(op) + srcSnapshots, err := srcVol.Volume.Snapshots(op) if err != nil { return err } @@ -127,7 +127,7 @@ func (d *cephfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots b } // Apply the volume quota if specified. - err = d.SetVolumeQuota(vol, vol.ConfigSize(), false, op) + err = d.SetVolumeQuota(vol.Volume, vol.ConfigSize(), false, op) if err != nil { return err } @@ -154,7 +154,7 @@ func (d *cephfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots b } // CreateVolumeFromMigration creates a new volume (with or without snapshots) from a migration data stream. -func (d *cephfs) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error { +func (d *cephfs) CreateVolumeFromMigration(vol VolumeCopy, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error { if volTargetArgs.MigrationType.FSType != migration.MigrationFSType_RSYNC { return ErrNotSupported } @@ -216,7 +216,7 @@ func (d *cephfs) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, if vol.contentType == ContentTypeFS { // Apply the size limit. - err = d.SetVolumeQuota(vol, vol.ConfigSize(), false, op) + err = d.SetVolumeQuota(vol.Volume, vol.ConfigSize(), false, op) if err != nil { return err } @@ -472,12 +472,12 @@ func (d *cephfs) RenameVolume(vol Volume, newVolName string, op *operations.Oper } // MigrateVolume streams the volume (with or without snapshots). -func (d *cephfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error { - return genericVFSMigrateVolume(d, d.state, vol, conn, volSrcArgs, op) +func (d *cephfs) MigrateVolume(vol VolumeCopy, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error { + return genericVFSMigrateVolume(d, d.state, vol.Volume, conn, volSrcArgs, op) } // BackupVolume creates an exported version of a volume. -func (d *cephfs) BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots []string, op *operations.Operation) error { +func (d *cephfs) BackupVolume(vol VolumeCopy, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots []string, op *operations.Operation) error { return genericVFSBackupVolume(d, vol, tarWriter, snapshots, op) } @@ -570,8 +570,9 @@ func (d *cephfs) VolumeSnapshots(vol Volume, op *operations.Operation) ([]string } // RestoreVolume resets a volume to its snapshotted state. -func (d *cephfs) RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error { +func (d *cephfs) RestoreVolume(vol Volume, snapVol Volume, op *operations.Operation) error { sourcePath := GetVolumeMountPath(d.name, vol.volType, vol.name) + _, snapshotName, _ := api.GetParentAndSnapshotName(snapVol.name) cephSnapPath := filepath.Join(sourcePath, ".snap", snapshotName) // Restore using rsync. diff --git a/lxd/storage/drivers/driver_common.go b/lxd/storage/drivers/driver_common.go index def3f899a376..4a42112fa44a 100644 --- a/lxd/storage/drivers/driver_common.go +++ b/lxd/storage/drivers/driver_common.go @@ -318,22 +318,22 @@ func (d *common) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.O } // CreateVolumeFromBackup re-creates a volume from its exported state. -func (d *common) CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) { +func (d *common) CreateVolumeFromBackup(vol VolumeCopy, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) { return nil, nil, ErrNotSupported } // CreateVolumeFromCopy copies an existing storage volume (with or without snapshots) into a new volume. -func (d *common) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, allowInconsistent bool, op *operations.Operation) error { +func (d *common) CreateVolumeFromCopy(vol VolumeCopy, srcVol VolumeCopy, allowInconsistent bool, op *operations.Operation) error { return ErrNotSupported } // CreateVolumeFromMigration creates a new volume (with or without snapshots) from a migration data stream. -func (d *common) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error { +func (d *common) CreateVolumeFromMigration(vol VolumeCopy, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error { return ErrNotSupported } // RefreshVolume updates an existing volume to match the state of another. -func (d *common) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, allowInconsistent bool, op *operations.Operation) error { +func (d *common) RefreshVolume(vol VolumeCopy, srcVol VolumeCopy, refreshSnapshots []string, allowInconsistent bool, op *operations.Operation) error { return ErrNotSupported } @@ -404,12 +404,12 @@ func (d *common) RenameVolume(vol Volume, newVolName string, op *operations.Oper } // MigrateVolume streams the volume (with or without snapshots). -func (d *common) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error { +func (d *common) MigrateVolume(vol VolumeCopy, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error { return ErrNotSupported } // BackupVolume creates an exported version of a volume. -func (d *common) BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots []string, op *operations.Operation) error { +func (d *common) BackupVolume(vol VolumeCopy, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots []string, op *operations.Operation) error { return ErrNotSupported } @@ -439,7 +439,7 @@ func (d *common) VolumeSnapshots(vol Volume, op *operations.Operation) ([]string } // RestoreVolume resets a volume to its snapshotted state. -func (d *common) RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error { +func (d *common) RestoreVolume(vol Volume, snapVol Volume, op *operations.Operation) error { return ErrNotSupported } diff --git a/lxd/storage/drivers/driver_dir_volumes.go b/lxd/storage/drivers/driver_dir_volumes.go index 9703b90379a9..ea6baf1dcd05 100644 --- a/lxd/storage/drivers/driver_dir_volumes.go +++ b/lxd/storage/drivers/driver_dir_volumes.go @@ -98,7 +98,7 @@ func (d *dir) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Oper } // CreateVolumeFromBackup restores a backup tarball onto the storage device. -func (d *dir) CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) { +func (d *dir) CreateVolumeFromBackup(vol VolumeCopy, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) { // Run the generic backup unpacker postHook, revertHook, err := genericVFSBackupUnpack(d.withoutGetVolID(), d.state.OS, vol, srcBackup.Snapshots, srcData, op) if err != nil { @@ -137,30 +137,34 @@ func (d *dir) CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcData } // CreateVolumeFromCopy provides same-pool volume copying functionality. -func (d *dir) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, allowInconsistent bool, op *operations.Operation) error { - var err error - var srcSnapshots []Volume +func (d *dir) CreateVolumeFromCopy(vol VolumeCopy, srcVol VolumeCopy, allowInconsistent bool, op *operations.Operation) error { + var srcSnapshots []string - if copySnapshots && !srcVol.IsSnapshot() { + if len(vol.Snapshots) > 0 && !srcVol.IsSnapshot() { // Get the list of snapshots from the source. - srcSnapshots, err = srcVol.Snapshots(op) + allSrcSnapshots, err := srcVol.Volume.Snapshots(op) if err != nil { return err } + + for _, srcSnapshot := range allSrcSnapshots { + _, snapshotName, _ := api.GetParentAndSnapshotName(srcSnapshot.name) + srcSnapshots = append(srcSnapshots, snapshotName) + } } // Run the generic copy. - return genericVFSCopyVolume(d, d.setupInitialQuota, vol, srcVol, srcSnapshots, false, allowInconsistent, op) + return genericVFSCopyVolume(d, d.setupInitialQuota, vol.Volume, srcVol.Volume, srcSnapshots, false, allowInconsistent, op) } // CreateVolumeFromMigration creates a volume being sent via a migration. -func (d *dir) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error { +func (d *dir) CreateVolumeFromMigration(vol VolumeCopy, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error { return genericVFSCreateVolumeFromMigration(d, d.setupInitialQuota, vol, conn, volTargetArgs, preFiller, op) } // RefreshVolume provides same-pool volume and specific snapshots syncing functionality. -func (d *dir) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, allowInconsistent bool, op *operations.Operation) error { - return genericVFSCopyVolume(d, d.setupInitialQuota, vol, srcVol, srcSnapshots, true, allowInconsistent, op) +func (d *dir) RefreshVolume(vol VolumeCopy, srcVol VolumeCopy, refreshSnapshots []string, allowInconsistent bool, op *operations.Operation) error { + return genericVFSCopyVolume(d, d.setupInitialQuota, vol.Volume, srcVol.Volume, refreshSnapshots, true, allowInconsistent, op) } // DeleteVolume deletes a volume of the storage device. If any snapshots of the volume remain then @@ -413,13 +417,13 @@ func (d *dir) RenameVolume(vol Volume, newVolName string, op *operations.Operati } // MigrateVolume sends a volume for migration. -func (d *dir) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error { - return genericVFSMigrateVolume(d, d.state, vol, conn, volSrcArgs, op) +func (d *dir) MigrateVolume(vol VolumeCopy, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error { + return genericVFSMigrateVolume(d, d.state, vol.Volume, conn, volSrcArgs, op) } // BackupVolume copies a volume (and optionally its snapshots) to a specified target path. // This driver does not support optimized backups. -func (d *dir) BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots []string, op *operations.Operation) error { +func (d *dir) BackupVolume(vol VolumeCopy, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots []string, op *operations.Operation) error { return genericVFSBackupVolume(d, vol, tarWriter, snapshots, op) } @@ -569,7 +573,8 @@ func (d *dir) VolumeSnapshots(vol Volume, op *operations.Operation) ([]string, e } // RestoreVolume restores a volume from a snapshot. -func (d *dir) RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error { +func (d *dir) RestoreVolume(vol Volume, snapVol Volume, op *operations.Operation) error { + _, snapshotName, _ := api.GetParentAndSnapshotName(snapVol.name) snapVol, err := vol.NewSnapshot(snapshotName) if err != nil { return err diff --git a/lxd/storage/drivers/driver_lvm_utils.go b/lxd/storage/drivers/driver_lvm_utils.go index a7b020590f03..a8d07f0e7bcb 100644 --- a/lxd/storage/drivers/driver_lvm_utils.go +++ b/lxd/storage/drivers/driver_lvm_utils.go @@ -511,7 +511,7 @@ func (d *lvm) resizeLogicalVolume(lvPath string, sizeBytes int64) error { } // copyThinpoolVolume makes an optimised copy of a thinpool volume by using thinpool snapshots. -func (d *lvm) copyThinpoolVolume(vol, srcVol Volume, srcSnapshots []Volume, refresh bool) error { +func (d *lvm) copyThinpoolVolume(vol, srcVol Volume, srcSnapshots []string, refresh bool) error { revert := revert.New() defer revert.Fail() @@ -526,8 +526,7 @@ func (d *lvm) copyThinpoolVolume(vol, srcVol Volume, srcSnapshots []Volume, refr } for _, srcSnapshot := range srcSnapshots { - _, snapName, _ := api.GetParentAndSnapshotName(srcSnapshot.name) - newFullSnapName := GetSnapshotVolumeName(vol.name, snapName) + newFullSnapName := GetSnapshotVolumeName(vol.name, srcSnapshot) newSnapVol := NewVolume(d, d.Name(), vol.volType, vol.contentType, newFullSnapName, vol.config, vol.poolConfig) volExists, err := d.HasVolume(newSnapVol) @@ -547,6 +546,11 @@ func (d *lvm) copyThinpoolVolume(vol, srcVol Volume, srcSnapshots []Volume, refr revert.Add(func() { _ = os.RemoveAll(newSnapVolPath) }) + srcSnapshot, err := srcVol.NewSnapshot(srcSnapshot) + if err != nil { + return err + } + // We do not modify the original snapshot so as to avoid damaging if it is corrupted for // some reason. If the filesystem needs to have a unique UUID generated in order to mount // this will be done at restore time to be safe. diff --git a/lxd/storage/drivers/driver_lvm_volumes.go b/lxd/storage/drivers/driver_lvm_volumes.go index 4962d0e5bf37..e69c42df0a5f 100644 --- a/lxd/storage/drivers/driver_lvm_volumes.go +++ b/lxd/storage/drivers/driver_lvm_volumes.go @@ -119,26 +119,31 @@ func (d *lvm) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Oper } // CreateVolumeFromBackup restores a backup tarball onto the storage device. -func (d *lvm) CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) { +func (d *lvm) CreateVolumeFromBackup(vol VolumeCopy, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) { return genericVFSBackupUnpack(d, d.state.OS, vol, srcBackup.Snapshots, srcData, op) } // CreateVolumeFromCopy provides same-pool volume copying functionality. -func (d *lvm) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, allowInconsistent bool, op *operations.Operation) error { +func (d *lvm) CreateVolumeFromCopy(vol VolumeCopy, srcVol VolumeCopy, allowInconsistent bool, op *operations.Operation) error { var err error - var srcSnapshots []Volume + var srcSnapshots []string - if copySnapshots && !srcVol.IsSnapshot() { + if len(vol.Snapshots) > 0 && !srcVol.IsSnapshot() { // Get the list of snapshots from the source. - srcSnapshots, err = srcVol.Snapshots(op) + allSrcSnapshots, err := srcVol.Volume.Snapshots(op) if err != nil { return err } + + for _, srcSnapshot := range allSrcSnapshots { + _, snapshotName, _ := api.GetParentAndSnapshotName(srcSnapshot.name) + srcSnapshots = append(srcSnapshots, snapshotName) + } } // We can use optimised copying when the pool is backed by an LVM thinpool. if d.usesThinpool() { - err = d.copyThinpoolVolume(vol, srcVol, srcSnapshots, false) + err = d.copyThinpoolVolume(vol.Volume, srcVol.Volume, srcSnapshots, false) if err != nil { return err } @@ -154,23 +159,23 @@ func (d *lvm) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool } // Otherwise run the generic copy. - return genericVFSCopyVolume(d, nil, vol, srcVol, srcSnapshots, false, allowInconsistent, op) + return genericVFSCopyVolume(d, nil, vol.Volume, srcVol.Volume, srcSnapshots, false, allowInconsistent, op) } // CreateVolumeFromMigration creates a volume being sent via a migration. -func (d *lvm) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error { +func (d *lvm) CreateVolumeFromMigration(vol VolumeCopy, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error { return genericVFSCreateVolumeFromMigration(d, nil, vol, conn, volTargetArgs, preFiller, op) } // RefreshVolume provides same-pool volume and specific snapshots syncing functionality. -func (d *lvm) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, allowInconsistent bool, op *operations.Operation) error { +func (d *lvm) RefreshVolume(vol VolumeCopy, srcVol VolumeCopy, refreshSnapshots []string, allowInconsistent bool, op *operations.Operation) error { // We can use optimised copying when the pool is backed by an LVM thinpool. if d.usesThinpool() { - return d.copyThinpoolVolume(vol, srcVol, srcSnapshots, true) + return d.copyThinpoolVolume(vol.Volume, srcVol.Volume, refreshSnapshots, true) } // Otherwise run the generic copy. - return genericVFSCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, allowInconsistent, op) + return genericVFSCopyVolume(d, nil, vol.Volume, srcVol.Volume, refreshSnapshots, true, allowInconsistent, op) } // DeleteVolume deletes a volume of the storage device. If any snapshots of the volume remain then this function @@ -848,13 +853,13 @@ func (d *lvm) RenameVolume(vol Volume, newVolName string, op *operations.Operati } // MigrateVolume sends a volume for migration. -func (d *lvm) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error { - return genericVFSMigrateVolume(d, d.state, vol, conn, volSrcArgs, op) +func (d *lvm) MigrateVolume(vol VolumeCopy, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error { + return genericVFSMigrateVolume(d, d.state, vol.Volume, conn, volSrcArgs, op) } // BackupVolume copies a volume (and optionally its snapshots) to a specified target path. // This driver does not support optimized backups. -func (d *lvm) BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWriter, _ bool, snapshots []string, op *operations.Operation) error { +func (d *lvm) BackupVolume(vol VolumeCopy, tarWriter *instancewriter.InstanceTarWriter, _ bool, snapshots []string, op *operations.Operation) error { return genericVFSBackupVolume(d, vol, tarWriter, snapshots, op) } @@ -1192,7 +1197,9 @@ func (d *lvm) VolumeSnapshots(vol Volume, op *operations.Operation) ([]string, e } // RestoreVolume restores a volume from a snapshot. -func (d *lvm) RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error { +func (d *lvm) RestoreVolume(vol Volume, snapVol Volume, op *operations.Operation) error { + _, snapshotName, _ := api.GetParentAndSnapshotName(snapVol.name) + restoreThinPoolVolume := func(restoreVol Volume) (revert.Hook, error) { // Instantiate snapshot volume from snapshot name. snapVol, err := restoreVol.NewSnapshot(snapshotName) diff --git a/lxd/storage/drivers/driver_mock.go b/lxd/storage/drivers/driver_mock.go index 4c5fb6a3e2eb..8ae6c1b03be7 100644 --- a/lxd/storage/drivers/driver_mock.go +++ b/lxd/storage/drivers/driver_mock.go @@ -81,22 +81,22 @@ func (d *mock) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Ope } // CreateVolumeFromBackup restores a backup tarball onto the storage device. -func (d *mock) CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) { +func (d *mock) CreateVolumeFromBackup(vol VolumeCopy, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) { return nil, nil, nil } // CreateVolumeFromCopy provides same-pool volume copying functionality. -func (d *mock) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, allowInconsistent bool, op *operations.Operation) error { +func (d *mock) CreateVolumeFromCopy(vol VolumeCopy, srcVol VolumeCopy, allowInconsistent bool, op *operations.Operation) error { return nil } // CreateVolumeFromMigration creates a volume being sent via a migration. -func (d *mock) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error { +func (d *mock) CreateVolumeFromMigration(vol VolumeCopy, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error { return nil } // RefreshVolume provides same-pool volume and specific snapshots syncing functionality. -func (d *mock) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, allowInconsistent bool, op *operations.Operation) error { +func (d *mock) RefreshVolume(vol VolumeCopy, srcVol VolumeCopy, refreshSnapshots []string, allowInconsistent bool, op *operations.Operation) error { return nil } @@ -170,13 +170,13 @@ func (d *mock) RenameVolume(vol Volume, newVolName string, op *operations.Operat } // MigrateVolume sends a volume for migration. -func (d *mock) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error { +func (d *mock) MigrateVolume(vol VolumeCopy, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error { return nil } // BackupVolume copies a volume (and optionally its snapshots) to a specified target path. // This driver does not support optimized backups. -func (d *mock) BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots []string, op *operations.Operation) error { +func (d *mock) BackupVolume(vol VolumeCopy, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots []string, op *operations.Operation) error { return nil } @@ -207,7 +207,7 @@ func (d *mock) VolumeSnapshots(vol Volume, op *operations.Operation) ([]string, } // RestoreVolume restores a volume from a snapshot. -func (d *mock) RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error { +func (d *mock) RestoreVolume(vol Volume, snapVol Volume, op *operations.Operation) error { return nil } diff --git a/lxd/storage/drivers/driver_zfs_volumes.go b/lxd/storage/drivers/driver_zfs_volumes.go index af64ebb4768d..788e49864ce1 100644 --- a/lxd/storage/drivers/driver_zfs_volumes.go +++ b/lxd/storage/drivers/driver_zfs_volumes.go @@ -338,13 +338,13 @@ func (d *zfs) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Oper } // CreateVolumeFromBackup re-creates a volume from its exported state. -func (d *zfs) CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) { +func (d *zfs) CreateVolumeFromBackup(vol VolumeCopy, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) { // Handle the non-optimized tarballs through the generic unpacker. if !*srcBackup.OptimizedStorage { return genericVFSBackupUnpack(d, d.state.OS, vol, srcBackup.Snapshots, srcData, op) } - volExists, err := d.HasVolume(vol) + volExists, err := d.HasVolume(vol.Volume) if err != nil { return nil, nil, err } @@ -366,7 +366,7 @@ func (d *zfs) CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcData } // And lastly the main volume. - _ = d.DeleteVolume(vol, op) + _ = d.DeleteVolume(vol.Volume, op) } // Only execute the revert function if we have had an error internally. @@ -422,7 +422,7 @@ func (d *zfs) CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcData vols = append(vols, vol.NewVMBlockFilesystemVolume()) } - vols = append(vols, vol) + vols = append(vols, vol.Volume) for _, v := range vols { // Find the compression algorithm used for backup source data. @@ -547,7 +547,7 @@ func (d *zfs) CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcData } // CreateVolumeFromCopy provides same-pool volume copying functionality. -func (d *zfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, allowInconsistent bool, op *operations.Operation) error { +func (d *zfs) CreateVolumeFromCopy(vol VolumeCopy, srcVol VolumeCopy, allowInconsistent bool, op *operations.Operation) error { var err error // Revert handling @@ -567,22 +567,22 @@ func (d *zfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool // For VMs, also copy the filesystem dataset. if vol.IsVMBlock() { // For VMs, also copy the filesystem volume. - srcFSVol := srcVol.NewVMBlockFilesystemVolume() - fsVol := vol.NewVMBlockFilesystemVolume() + srcFSVol := NewVolumeCopy(srcVol.NewVMBlockFilesystemVolume()) + fsVol := NewVolumeCopy(vol.NewVMBlockFilesystemVolume()) - err = d.CreateVolumeFromCopy(fsVol, srcFSVol, copySnapshots, false, op) + err = d.CreateVolumeFromCopy(fsVol, srcFSVol, false, op) if err != nil { return err } // Delete on revert. - revert.Add(func() { _ = d.DeleteVolume(fsVol, op) }) + revert.Add(func() { _ = d.DeleteVolume(fsVol.Volume, op) }) } // Retrieve snapshots on the source. snapshots := []string{} - if !srcVol.IsSnapshot() && copySnapshots { - snapshots, err = d.VolumeSnapshots(srcVol, op) + if !srcVol.IsSnapshot() && len(vol.Snapshots) > 0 { + snapshots, err = d.VolumeSnapshots(srcVol.Volume, op) if err != nil { return err } @@ -604,12 +604,12 @@ func (d *zfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool var srcSnapshot string if srcVol.volType == VolumeTypeImage { - srcSnapshot = fmt.Sprintf("%s@readonly", d.dataset(srcVol, false)) + srcSnapshot = fmt.Sprintf("%s@readonly", d.dataset(srcVol.Volume, false)) } else if srcVol.IsSnapshot() { - srcSnapshot = d.dataset(srcVol, false) + srcSnapshot = d.dataset(srcVol.Volume, false) } else { // Create a new snapshot for copy. - srcSnapshot = fmt.Sprintf("%s@copy-%s", d.dataset(srcVol, false), uuid.New().String()) + srcSnapshot = fmt.Sprintf("%s@copy-%s", d.dataset(srcVol.Volume, false), uuid.New().String()) _, err := shared.RunCommand("zfs", "snapshot", "-r", srcSnapshot) if err != nil { @@ -644,7 +644,7 @@ func (d *zfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool } // Delete the volume created on failure. - revert.Add(func() { _ = d.DeleteVolume(vol, op) }) + revert.Add(func() { _ = d.DeleteVolume(vol.Volume, op) }) // If zfs.clone_copy is disabled or source volume has snapshots, then use full copy mode. if shared.IsFalse(d.config["zfs.clone_copy"]) || len(snapshots) > 0 { @@ -653,10 +653,10 @@ func (d *zfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool // Send/receive the snapshot. var sender *exec.Cmd var receiver *exec.Cmd - if vol.ContentType() == ContentTypeBlock || d.isBlockBacked(vol) { - receiver = exec.Command("zfs", "receive", d.dataset(vol, false)) + if vol.ContentType() == ContentTypeBlock || d.isBlockBacked(vol.Volume) { + receiver = exec.Command("zfs", "receive", d.dataset(vol.Volume, false)) } else { - receiver = exec.Command("zfs", "receive", "-x", "mountpoint", d.dataset(vol, false)) + receiver = exec.Command("zfs", "receive", "-x", "mountpoint", d.dataset(vol.Volume, false)) } // Handle transferring snapshots. @@ -675,7 +675,7 @@ func (d *zfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool args := []string{"send"} // Check if nesting is required. - if d.needsRecursion(d.dataset(srcVol, false)) { + if d.needsRecursion(d.dataset(srcVol.Volume, false)) { args = append(args, "-R") if zfsRaw { @@ -685,7 +685,7 @@ func (d *zfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool if d.config["zfs.clone_copy"] == "rebase" { var err error - origin := d.dataset(srcVol, false) + origin := d.dataset(srcVol.Volume, false) for { fields := strings.SplitN(origin, "@", 2) @@ -764,14 +764,14 @@ func (d *zfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool } // Delete the snapshot. - _, err = shared.RunCommand("zfs", "destroy", "-r", fmt.Sprintf("%s@%s", d.dataset(vol, false), snapName)) + _, err = shared.RunCommand("zfs", "destroy", "-r", fmt.Sprintf("%s@%s", d.dataset(vol.Volume, false), snapName)) if err != nil { return err } // Cleanup unexpected snapshots. if len(snapshots) > 0 { - children, err := d.getDatasets(d.dataset(vol, false), "snapshot") + children, err := d.getDatasets(d.dataset(vol.Volume, false), "snapshot") if err != nil { return err } @@ -786,7 +786,7 @@ func (d *zfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool } // Delete the rest. - _, err := shared.RunCommand("zfs", "destroy", fmt.Sprintf("%s%s", d.dataset(vol, false), entry)) + _, err := shared.RunCommand("zfs", "destroy", fmt.Sprintf("%s%s", d.dataset(vol.Volume, false), entry)) if err != nil { return err } @@ -801,7 +801,7 @@ func (d *zfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool args = append(args, "-o", "volmode=none") } - args = append(args, srcSnapshot, d.dataset(vol, false)) + args = append(args, srcSnapshot, d.dataset(vol.Volume, false)) // Clone the snapshot. _, err := shared.RunCommand("zfs", args...) @@ -812,26 +812,26 @@ func (d *zfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool // Apply the properties. if vol.contentType == ContentTypeFS { - if !d.isBlockBacked(srcVol) { - err := d.setDatasetProperties(d.dataset(vol, false), "mountpoint=legacy", "canmount=noauto") + if !d.isBlockBacked(srcVol.Volume) { + err := d.setDatasetProperties(d.dataset(vol.Volume, false), "mountpoint=legacy", "canmount=noauto") if err != nil { return err } // Apply the blocksize. - err = d.setBlocksizeFromConfig(vol) + err = d.setBlocksizeFromConfig(vol.Volume) if err != nil { return err } } - if d.isBlockBacked(srcVol) && renegerateFilesystemUUIDNeeded(vol.ConfigBlockFilesystem()) { - _, err := d.activateVolume(vol) + if d.isBlockBacked(srcVol.Volume) && renegerateFilesystemUUIDNeeded(vol.ConfigBlockFilesystem()) { + _, err := d.activateVolume(vol.Volume) if err != nil { return err } - volPath, err := d.GetVolumeDiskPath(vol) + volPath, err := d.GetVolumeDiskPath(vol.Volume) if err != nil { return err } @@ -857,13 +857,13 @@ func (d *zfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool // leaving the filesystem in an inconsistent state if the resize couldn't be completed. This is because if // the resize fails we will delete the volume anyway so don't have to worry about it being inconsistent. var allowUnsafeResize bool - if d.isBlockBacked(vol) && vol.contentType == ContentTypeFS { + if d.isBlockBacked(vol.Volume) && vol.contentType == ContentTypeFS { allowUnsafeResize = true } // Resize volume to the size specified. Only uses volume "size" property and does not use pool/defaults // to give the caller more control over the size being used. - err = d.SetVolumeQuota(vol, vol.config["size"], allowUnsafeResize, op) + err = d.SetVolumeQuota(vol.Volume, vol.config["size"], allowUnsafeResize, op) if err != nil { return err } @@ -874,7 +874,7 @@ func (d *zfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool } // CreateVolumeFromMigration creates a volume being sent via a migration. -func (d *zfs) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error { +func (d *zfs) CreateVolumeFromMigration(vol VolumeCopy, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error { // Handle simple rsync and block_and_rsync through generic. if volTargetArgs.MigrationType.FSType == migration.MigrationFSType_RSYNC || volTargetArgs.MigrationType.FSType == migration.MigrationFSType_BLOCK_AND_RSYNC { return genericVFSCreateVolumeFromMigration(d, nil, vol, conn, volTargetArgs, preFiller, op) @@ -904,7 +904,7 @@ func (d *zfs) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, vol // If we're refreshing, send back all snapshots of the target. if volTargetArgs.Refresh && shared.ValueInSlice(migration.ZFSFeatureMigrationHeader, volTargetArgs.MigrationType.Features) { - snapshots, err := vol.Snapshots(op) + snapshots, err := vol.Volume.Snapshots(op) if err != nil { return fmt.Errorf("Failed getting volume snapshots: %w", err) } @@ -1022,7 +1022,7 @@ func (d *zfs) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, vol } } - return d.createVolumeFromMigrationOptimized(vol, conn, volTargetArgs, volumeOnly, preFiller, op) + return d.createVolumeFromMigrationOptimized(vol.Volume, conn, volTargetArgs, volumeOnly, preFiller, op) } func (d *zfs) createVolumeFromMigrationOptimized(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, volumeOnly bool, preFiller *VolumeFiller, op *operations.Operation) error { @@ -1046,9 +1046,7 @@ func (d *zfs) createVolumeFromMigrationOptimized(vol Volume, conn io.ReadWriteCl if len(snapshots) > 0 { lastIdenticalSnapshot := snapshots[len(snapshots)-1] - _, lastIdenticalSnapshotOnlyName, _ := api.GetParentAndSnapshotName(lastIdenticalSnapshot.Name()) - - err = d.RestoreVolume(vol, lastIdenticalSnapshotOnlyName, op) + err = d.RestoreVolume(vol, lastIdenticalSnapshot, op) if err != nil { return err } @@ -1210,19 +1208,19 @@ func (d *zfs) createVolumeFromMigrationOptimized(vol Volume, conn io.ReadWriteCl } // RefreshVolume updates an existing volume to match the state of another. -func (d *zfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, allowInconsistent bool, op *operations.Operation) error { +func (d *zfs) RefreshVolume(vol VolumeCopy, srcVol VolumeCopy, refreshSnapshots []string, allowInconsistent bool, op *operations.Operation) error { var err error var targetSnapshots []Volume var srcSnapshotsAll []Volume if !srcVol.IsSnapshot() { // Get target snapshots - targetSnapshots, err = vol.Snapshots(op) + targetSnapshots, err = vol.Volume.Snapshots(op) if err != nil { return fmt.Errorf("Failed to get target snapshots: %w", err) } - srcSnapshotsAll, err = srcVol.Snapshots(op) + srcSnapshotsAll, err = srcVol.Volume.Snapshots(op) if err != nil { return fmt.Errorf("Failed to get source snapshots: %w", err) } @@ -1232,12 +1230,12 @@ func (d *zfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, al // We cannot use generic vfs volume copy here, as zfs will complain if a generic // copy/refresh is followed by an optimized refresh. if len(targetSnapshots) == 0 || len(srcSnapshotsAll) == 0 { - err = d.DeleteVolume(vol, op) + err = d.DeleteVolume(vol.Volume, op) if err != nil { return err } - return d.CreateVolumeFromCopy(vol, srcVol, len(srcSnapshots) > 0, false, op) + return d.CreateVolumeFromCopy(vol, srcVol, false, op) } transfer := func(src Volume, target Volume, origin Volume) error { @@ -1307,13 +1305,13 @@ func (d *zfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, al _, lastIdenticalSnapshotOnlyName, _ := api.GetParentAndSnapshotName(lastIdenticalSnapshot.Name()) // Rollback target volume to the latest identical snapshot - err = d.RestoreVolume(vol, lastIdenticalSnapshotOnlyName, op) + err = d.RestoreVolume(vol.Volume, lastIdenticalSnapshot, op) if err != nil { return fmt.Errorf("Failed to restore volume: %w", err) } // Create all missing snapshots on the target using an incremental stream - for i, snap := range srcSnapshots { + for i, refreshSnapshot := range refreshSnapshots { var originSnap Volume if i == 0 { @@ -1322,16 +1320,24 @@ func (d *zfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, al return fmt.Errorf("Failed to create new snapshot volume: %w", err) } } else { - originSnap = srcSnapshots[i-1] + originSnap, err = srcVol.NewSnapshot(refreshSnapshots[i-1]) + if err != nil { + return fmt.Errorf("Failed to create new snapshot volume: %w", err) + } + } + + snap, err := srcVol.NewSnapshot(refreshSnapshot) + if err != nil { + return err } - err = transfer(snap, vol, originSnap) + err = transfer(snap, vol.Volume, originSnap) if err != nil { // Don't fail here. If it's not possible to perform an optimized refresh, do a generic // refresh instead. if errors.Is(err, ErrSnapshotDoesNotMatchIncrementalSource) { d.logger.Debug("Unable to perform an optimized refresh, doing a generic refresh", logger.Ctx{"err": err}) - return genericVFSCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, allowInconsistent, op) + return genericVFSCopyVolume(d, nil, vol.Volume, srcVol.Volume, refreshSnapshots, true, allowInconsistent, op) } return fmt.Errorf("Failed to transfer snapshot %q: %w", snap.name, err) @@ -1348,7 +1354,7 @@ func (d *zfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, al // refresh instead. if errors.Is(err, ErrSnapshotDoesNotMatchIncrementalSource) { d.logger.Debug("Unable to perform an optimized refresh, doing a generic refresh", logger.Ctx{"err": err}) - return genericVFSCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, allowInconsistent, op) + return genericVFSCopyVolume(d, nil, vol.Volume, srcVol.Volume, refreshSnapshots, true, allowInconsistent, op) } return fmt.Errorf("Failed to transfer snapshot %q: %w", snap.name, err) @@ -1371,13 +1377,13 @@ func (d *zfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, al latestSnapVol := srcSnapshotsAll[len(srcSnapshotsAll)-1] - err = transfer(srcSnap, vol, latestSnapVol) + err = transfer(srcSnap, vol.Volume, latestSnapVol) if err != nil { // Don't fail here. If it's not possible to perform an optimized refresh, do a generic // refresh instead. if errors.Is(err, ErrSnapshotDoesNotMatchIncrementalSource) { d.logger.Debug("Unable to perform an optimized refresh, doing a generic refresh", logger.Ctx{"err": err}) - return genericVFSCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, allowInconsistent, op) + return genericVFSCopyVolume(d, nil, vol.Volume, srcVol.Volume, refreshSnapshots, true, allowInconsistent, op) } return fmt.Errorf("Failed to transfer main volume: %w", err) @@ -1394,7 +1400,7 @@ func (d *zfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, al // refresh instead. if errors.Is(err, ErrSnapshotDoesNotMatchIncrementalSource) { d.logger.Debug("Unable to perform an optimized refresh, doing a generic refresh", logger.Ctx{"err": err}) - return genericVFSCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, allowInconsistent, op) + return genericVFSCopyVolume(d, nil, vol.Volume, srcVol.Volume, refreshSnapshots, true, allowInconsistent, op) } return fmt.Errorf("Failed to transfer main volume: %w", err) @@ -1402,7 +1408,7 @@ func (d *zfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, al } // Restore target volume from main source snapshot. - err = d.RestoreVolume(vol, snapUUID, op) + err = d.RestoreVolume(vol.Volume, srcSnap, op) if err != nil { return err } @@ -2400,7 +2406,7 @@ func (d *zfs) DelegateVolume(vol Volume, pid int) error { } // MigrateVolume sends a volume for migration. -func (d *zfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error { +func (d *zfs) MigrateVolume(vol VolumeCopy, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error { if !volSrcArgs.AllowInconsistent && vol.contentType == ContentTypeFS && vol.IsBlockBacked() { // When migrating using zfs volumes (not datasets), ensure that the filesystem is synced // otherwise the source and target volumes may differ. Tests have shown that only calling @@ -2423,7 +2429,7 @@ func (d *zfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *mig // If volume is filesystem type, create a fast snapshot to ensure migration is consistent. // TODO add support for temporary snapshots of block volumes here. if vol.contentType == ContentTypeFS && !vol.IsSnapshot() { - snapshotPath, cleanup, err := d.readonlySnapshot(vol) + snapshotPath, cleanup, err := d.readonlySnapshot(vol.Volume) if err != nil { return err } @@ -2435,7 +2441,7 @@ func (d *zfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *mig vol.mountCustomPath = snapshotPath } - return genericVFSMigrateVolume(d, d.state, vol, conn, volSrcArgs, op) + return genericVFSMigrateVolume(d, d.state, vol.Volume, conn, volSrcArgs, op) } else if volSrcArgs.MigrationType.FSType != migration.MigrationFSType_ZFS { return ErrNotSupported } @@ -2450,13 +2456,13 @@ func (d *zfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *mig // The target will validate the GUIDs and if successful proceed with the refresh. if shared.ValueInSlice(migration.ZFSFeatureMigrationHeader, volSrcArgs.MigrationType.Features) { - snapshots, err := d.VolumeSnapshots(vol, op) + snapshots, err := d.VolumeSnapshots(vol.Volume, op) if err != nil { return err } // Fill the migration header with the snapshot names and dataset GUIDs. - srcMigrationHeader, err = d.datasetHeader(vol, snapshots) + srcMigrationHeader, err = d.datasetHeader(vol.Volume, snapshots) if err != nil { return err } @@ -2479,7 +2485,7 @@ func (d *zfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *mig } // If we haven't negotiated zvol support, ensure volume is not a zvol. - if !shared.ValueInSlice(migration.ZFSFeatureZvolFilesystems, volSrcArgs.MigrationType.Features) && d.isBlockBacked(vol) { + if !shared.ValueInSlice(migration.ZFSFeatureZvolFilesystems, volSrcArgs.MigrationType.Features) && d.isBlockBacked(vol.Volume) { return fmt.Errorf("Filesystem zvol detected in source but target does not support receiving zvols") } @@ -2524,7 +2530,7 @@ func (d *zfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *mig } } - return d.migrateVolumeOptimized(vol, conn, volSrcArgs, incrementalStream, op) + return d.migrateVolumeOptimized(vol.Volume, conn, volSrcArgs, incrementalStream, op) } func (d *zfs) migrateVolumeOptimized(vol Volume, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, incremental bool, op *operations.Operation) error { @@ -2681,14 +2687,14 @@ func (d *zfs) readonlySnapshot(vol Volume) (string, revert.Hook, error) { } // BackupVolume creates an exported version of a volume. -func (d *zfs) BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots []string, op *operations.Operation) error { +func (d *zfs) BackupVolume(vol VolumeCopy, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots []string, op *operations.Operation) error { // Handle the non-optimized tarballs through the generic packer. if !optimized { // Because the generic backup method will not take a consistent backup if files are being modified // as they are copied to the tarball, as ZFS allows us to take a quick snapshot without impacting // the parent volume we do so here to ensure the backup taken is consistent. - if vol.contentType == ContentTypeFS && !d.isBlockBacked(vol) { - snapshotPath, cleanup, err := d.readonlySnapshot(vol) + if vol.contentType == ContentTypeFS && !d.isBlockBacked(vol.Volume) { + snapshotPath, cleanup, err := d.readonlySnapshot(vol.Volume) if err != nil { return err } @@ -2715,7 +2721,7 @@ func (d *zfs) BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWrit // Backup VM config volumes first. if vol.IsVMBlock() { - fsVol := vol.NewVMBlockFilesystemVolume() + fsVol := NewVolumeCopy(vol.NewVMBlockFilesystemVolume()) err := d.BackupVolume(fsVol, tarWriter, optimized, snapshots, op) if err != nil { return err @@ -2811,7 +2817,7 @@ func (d *zfs) BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWrit } // Create a temporary read-only snapshot. - srcSnapshot := fmt.Sprintf("%s@backup-%s", d.dataset(vol, false), uuid.New().String()) + srcSnapshot := fmt.Sprintf("%s@backup-%s", d.dataset(vol.Volume, false), uuid.New().String()) _, err := shared.RunCommand("zfs", "snapshot", "-r", srcSnapshot) if err != nil { return err @@ -3268,13 +3274,15 @@ func (d *zfs) VolumeSnapshots(vol Volume, op *operations.Operation) ([]string, e } // RestoreVolume restores a volume from a snapshot. -func (d *zfs) RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error { +func (d *zfs) RestoreVolume(vol Volume, snapVol Volume, op *operations.Operation) error { // Get the list of snapshots. entries, err := d.getDatasets(d.dataset(vol, false), "snapshot") if err != nil { return err } + _, snapshotName, _ := api.GetParentAndSnapshotName(snapVol.name) + // Check if more recent snapshots exist. idx := -1 snapshots := []string{} @@ -3352,7 +3360,8 @@ func (d *zfs) RestoreVolume(vol Volume, snapshotName string, op *operations.Oper // For VM images, restore the associated filesystem dataset too. if vol.IsVMBlock() { fsVol := vol.NewVMBlockFilesystemVolume() - err := d.RestoreVolume(fsVol, snapshotName, op) + fsSnapVol := snapVol.NewVMBlockFilesystemVolume() + err := d.RestoreVolume(fsVol, fsSnapVol, op) if err != nil { return err } diff --git a/lxd/storage/drivers/generic_vfs.go b/lxd/storage/drivers/generic_vfs.go index f043958691f4..c48738f0a647 100644 --- a/lxd/storage/drivers/generic_vfs.go +++ b/lxd/storage/drivers/generic_vfs.go @@ -280,7 +280,7 @@ func genericVFSMigrateVolume(d Driver, s *state.State, vol Volume, conn io.ReadW // genericVFSCreateVolumeFromMigration receives a volume and its snapshots over a non-optimized method. // initVolume is run against the main volume (not the snapshots) and is often used for quota initialization. -func genericVFSCreateVolumeFromMigration(d Driver, initVolume func(vol Volume) (revert.Hook, error), vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error { +func genericVFSCreateVolumeFromMigration(d Driver, initVolume func(vol Volume) (revert.Hook, error), vol VolumeCopy, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error { // Check migration transport type matches volume type. if IsContentBlock(vol.contentType) { if volTargetArgs.MigrationType.FSType != migration.MigrationFSType_BLOCK_AND_RSYNC { @@ -295,12 +295,12 @@ func genericVFSCreateVolumeFromMigration(d Driver, initVolume func(vol Volume) ( // Create the main volume if not refreshing. if !volTargetArgs.Refresh { - err := d.CreateVolume(vol, preFiller, op) + err := d.CreateVolume(vol.Volume, preFiller, op) if err != nil { return err } - revert.Add(func() { _ = d.DeleteVolume(vol, op) }) + revert.Add(func() { _ = d.DeleteVolume(vol.Volume, op) }) } recvFSVol := func(volName string, conn io.ReadWriteCloser, path string) error { @@ -358,7 +358,7 @@ func genericVFSCreateVolumeFromMigration(d Driver, initVolume func(vol Volume) ( pathBlock := "" if vol.IsVMBlock() || (IsContentBlock(vol.contentType) && vol.volType == VolumeTypeCustom) { - pathBlock, err = d.GetVolumeDiskPath(vol) + pathBlock, err = d.GetVolumeDiskPath(vol.Volume) if err != nil { return fmt.Errorf("Error getting VM block volume disk path: %w", err) } @@ -399,7 +399,7 @@ func genericVFSCreateVolumeFromMigration(d Driver, initVolume func(vol Volume) ( // Run volume-specific init logic. if initVolume != nil { - _, err := initVolume(vol) + _, err := initVolume(vol.Volume) if err != nil { return err } @@ -471,7 +471,7 @@ func genericVFSGetVolumeDiskPath(vol Volume) (string, error) { } // genericVFSBackupVolume is a generic BackupVolume implementation for VFS-only drivers. -func genericVFSBackupVolume(d Driver, vol Volume, tarWriter *instancewriter.InstanceTarWriter, snapshots []string, op *operations.Operation) error { +func genericVFSBackupVolume(d Driver, vol VolumeCopy, tarWriter *instancewriter.InstanceTarWriter, snapshots []string, op *operations.Operation) error { if len(snapshots) > 0 { // Check requested snapshot match those in storage. err := vol.SnapshotsMatch(snapshots, op) @@ -646,7 +646,7 @@ func genericVFSBackupVolume(d Driver, vol Volume, tarWriter *instancewriter.Inst prefix = "backup/volume" } - err := backupVolume(vol, prefix) + err := backupVolume(vol.Volume, prefix) if err != nil { return err } @@ -659,7 +659,7 @@ func genericVFSBackupVolume(d Driver, vol Volume, tarWriter *instancewriter.Inst // created and a revert function that can be used to undo the actions this function performs should something // subsequently fail. For VolumeTypeCustom volumes, a nil post hook is returned as it is expected that the DB // record be created before the volume is unpacked due to differences in the archive format that allows this. -func genericVFSBackupUnpack(d Driver, sysOS *sys.OS, vol Volume, snapshots []string, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) { +func genericVFSBackupUnpack(d Driver, sysOS *sys.OS, vol VolumeCopy, snapshots []string, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) { // Define function to unpack a volume from a backup tarball file. unpackVolume := func(r io.ReadSeeker, tarArgs []string, unpacker []string, srcPrefix string, mountPath string) error { volTypeName := "container" @@ -732,7 +732,7 @@ func genericVFSBackupUnpack(d Driver, sysOS *sys.OS, vol Volume, snapshots []str // Extract block file to block volume. if vol.contentType == ContentTypeBlock { - targetPath, err := d.GetVolumeDiskPath(vol) + targetPath, err := d.GetVolumeDiskPath(vol.Volume) if err != nil { return err } @@ -763,7 +763,7 @@ func genericVFSBackupUnpack(d Driver, sysOS *sys.OS, vol Volume, snapshots []str // Allow potentially destructive resize of volume as we are going to be // overwriting it entirely anyway. This allows shrinking of block volumes. allowUnsafeResize = true - err = d.SetVolumeQuota(vol, fmt.Sprintf("%d", size), allowUnsafeResize, op) + err = d.SetVolumeQuota(vol.Volume, fmt.Sprintf("%d", size), allowUnsafeResize, op) if err != nil { return err } @@ -818,7 +818,7 @@ func genericVFSBackupUnpack(d Driver, sysOS *sys.OS, vol Volume, snapshots []str return nil, nil, err } - volExists, err := d.HasVolume(vol) + volExists, err := d.HasVolume(vol.Volume) if err != nil { return nil, nil, err } @@ -828,12 +828,12 @@ func genericVFSBackupUnpack(d Driver, sysOS *sys.OS, vol Volume, snapshots []str } // Create new empty volume. - err = d.CreateVolume(vol, nil, nil) + err = d.CreateVolume(vol.Volume, nil, nil) if err != nil { return nil, nil, err } - revert.Add(func() { _ = d.DeleteVolume(vol, op) }) + revert.Add(func() { _ = d.DeleteVolume(vol.Volume, op) }) if len(snapshots) > 0 { // Create new snapshots directory. @@ -873,12 +873,12 @@ func genericVFSBackupUnpack(d Driver, sysOS *sys.OS, vol Volume, snapshots []str revert.Add(func() { _ = d.DeleteVolumeSnapshot(snapVol, op) }) } - err = d.MountVolume(vol, op) + err = d.MountVolume(vol.Volume, op) if err != nil { return nil, nil, err } - revert.Add(func() { _, _ = d.UnmountVolume(vol, false, op) }) + revert.Add(func() { _, _ = d.UnmountVolume(vol.Volume, false, op) }) backupPrefix := "backup/container" if vol.IsVMBlock() { @@ -918,7 +918,7 @@ func genericVFSBackupUnpack(d Driver, sysOS *sys.OS, vol Volume, snapshots []str } } else { // For custom volumes unmount now, there is no post hook as there is no backup.yaml to generate. - _, err = d.UnmountVolume(vol, false, op) + _, err = d.UnmountVolume(vol.Volume, false, op) if err != nil { return nil, nil, err } @@ -929,7 +929,7 @@ func genericVFSBackupUnpack(d Driver, sysOS *sys.OS, vol Volume, snapshots []str // genericVFSCopyVolume copies a volume and its snapshots using a non-optimized method. // initVolume is run against the main volume (not the snapshots) and is often used for quota initialization. -func genericVFSCopyVolume(d Driver, initVolume func(vol Volume) (revert.Hook, error), vol Volume, srcVol Volume, srcSnapshots []Volume, refresh bool, allowInconsistent bool, op *operations.Operation) error { +func genericVFSCopyVolume(d Driver, initVolume func(vol Volume) (revert.Hook, error), vol Volume, srcVol Volume, refreshSnapshots []string, refresh bool, allowInconsistent bool, op *operations.Operation) error { if vol.contentType != srcVol.contentType { return fmt.Errorf("Content type of source and target must be the same") } @@ -992,10 +992,8 @@ func genericVFSCopyVolume(d Driver, initVolume func(vol Volume) (revert.Hook, er // Ensure the volume is mounted. err := vol.MountTask(func(targetMountPath string, op *operations.Operation) error { // If copying snapshots is indicated, check the source isn't itself a snapshot. - if len(srcSnapshots) > 0 && !srcVol.IsSnapshot() { - for _, srcVol := range srcSnapshots { - _, snapName, _ := api.GetParentAndSnapshotName(srcVol.name) - + if len(refreshSnapshots) > 0 && !srcVol.IsSnapshot() { + for _, refreshSnapshot := range refreshSnapshots { // Mount the source snapshot and copy it to the target main volume. // A snapshot will then be taken next so it is stored in the correct volume and // subsequent filesystem rsync transfers benefit from only transferring the files @@ -1021,7 +1019,7 @@ func genericVFSCopyVolume(d Driver, initVolume func(vol Volume) (revert.Hook, er return err } - fullSnapName := GetSnapshotVolumeName(vol.name, snapName) + fullSnapName := GetSnapshotVolumeName(vol.name, refreshSnapshot) snapVol := NewVolume(d, d.Name(), vol.volType, vol.contentType, fullSnapName, vol.config, vol.poolConfig) // Create the snapshot itself. diff --git a/lxd/storage/drivers/interface.go b/lxd/storage/drivers/interface.go index b42123d4d884..936f4044b49c 100644 --- a/lxd/storage/drivers/interface.go +++ b/lxd/storage/drivers/interface.go @@ -65,8 +65,8 @@ type Driver interface { FillVolumeConfig(vol Volume) error ValidateVolume(vol Volume, removeUnknownKeys bool) error CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Operation) error - CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, allowInconsistent bool, op *operations.Operation) error - RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, allowInconsistent bool, op *operations.Operation) error + CreateVolumeFromCopy(vol VolumeCopy, srcVol VolumeCopy, allowInconsistent bool, op *operations.Operation) error + RefreshVolume(vol VolumeCopy, srcVol VolumeCopy, refreshSnapshots []string, allowInconsistent bool, op *operations.Operation) error DeleteVolume(vol Volume, op *operations.Operation) error RenameVolume(vol Volume, newName string, op *operations.Operation) error UpdateVolume(vol Volume, changedConfig map[string]string) error @@ -99,14 +99,14 @@ type Driver interface { DeleteVolumeSnapshot(snapVol Volume, op *operations.Operation) error RenameVolumeSnapshot(snapVol Volume, newSnapshotName string, op *operations.Operation) error VolumeSnapshots(vol Volume, op *operations.Operation) ([]string, error) - RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error + RestoreVolume(vol Volume, snapVol Volume, op *operations.Operation) error // Migration. MigrationTypes(contentType ContentType, refresh bool, copySnapshots 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 + MigrateVolume(vol VolumeCopy, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error + CreateVolumeFromMigration(vol VolumeCopy, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error // Backup. - BackupVolume(vol Volume, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots []string, op *operations.Operation) error - CreateVolumeFromBackup(vol Volume, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) + BackupVolume(vol VolumeCopy, tarWriter *instancewriter.InstanceTarWriter, optimized bool, snapshots []string, op *operations.Operation) error + CreateVolumeFromBackup(vol VolumeCopy, srcBackup backup.Info, srcData io.ReadSeeker, op *operations.Operation) (VolumePostHook, revert.Hook, error) } diff --git a/lxd/storage/drivers/volume.go b/lxd/storage/drivers/volume.go index 9f9b857effb1..2577318c9ae5 100644 --- a/lxd/storage/drivers/volume.go +++ b/lxd/storage/drivers/volume.go @@ -99,6 +99,12 @@ type Volume struct { hasSource bool // Whether the volume is created from a source volume. } +// VolumeCopy represents a volume and its snapshots for copy and refresh operations. +type VolumeCopy struct { + Volume + Snapshots []Volume +} + // NewVolume instantiates a new Volume struct. func NewVolume(driver Driver, poolName string, volType VolumeType, contentType ContentType, volName string, volConfig map[string]string, poolConfig map[string]string) Volume { return Volume{ @@ -578,3 +584,11 @@ func (v Volume) Clone() Volume { return NewVolume(v.driver, v.pool, v.volType, v.contentType, v.name, newConfig, newPoolConfig) } + +// NewVolumeCopy returns a container for copying a volume and its snapshots. +func NewVolumeCopy(vol Volume, snapshots ...Volume) VolumeCopy { + return VolumeCopy{ + Volume: vol, + Snapshots: snapshots, + } +}