diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go index 91e0b5d303f3..19ba9cb765d0 100644 --- a/lxd/storage/backend_lxd.go +++ b/lxd/storage/backend_lxd.go @@ -250,11 +250,15 @@ func (b *lxdBackend) Create(clientType request.ClientType, op *operations.Operat } // GetNewVolume returns a drivers.Volume that doesn't yet exist in the database. -// It contains copies of the supplied volume config, including a new UUID, and the pools config. +// It contains copies of the supplied volume config, including a new UUID and +// default configuration for the poo driver, as well as the pool's config. // Use the returned drivers.Volume as the base for actions performed on the new volume. func (b *lxdBackend) GetNewVolume(volType drivers.VolumeType, contentType drivers.ContentType, volName string, volConfig map[string]string) drivers.Volume { newVol := b.GetVolume(volType, contentType, volName, volConfig) + // Fill default config. + b.Driver().FillVolumeConfig(newVol) + // Set a new UUID. newVol.Config()["volatile.uuid"] = uuid.New().String() return newVol @@ -682,7 +686,7 @@ func (b *lxdBackend) CreateInstance(inst instance.Instance, op *operations.Opera } // Validate config and create database entry for new storage volume. - err = VolumeDBCreate(b, inst.Project().Name, inst.Name(), "", volType, false, vol.Config(), inst.CreationDate(), time.Time{}, contentType, true, false) + err = VolumeDBCreate(b, inst.Project().Name, inst.Name(), vol, "", false, inst.CreationDate(), time.Time{}, true, false) if err != nil { return err } @@ -870,9 +874,12 @@ func (b *lxdBackend) CreateInstanceFromBackup(srcBackup backup.Info, srcData io. } } + // Fill volume config with driver defaults before writing to the database. + b.driver.FillVolumeConfig(vol) + // Validate config and create database entry for new storage volume. // Strip unsupported config keys (in case the export was made from a different type of storage pool). - err = VolumeDBCreate(b, inst.Project().Name, inst.Name(), volumeDescription, volType, false, volumeConfig, volumeCreationDate, time.Time{}, contentType, true, true) + err = VolumeDBCreate(b, inst.Project().Name, inst.Name(), vol, volumeDescription, false, volumeCreationDate, time.Time{}, true, true) if err != nil { return err } @@ -911,9 +918,12 @@ func (b *lxdBackend) CreateInstanceFromBackup(srcBackup backup.Info, srcData io. newSnapshotName := drivers.GetSnapshotVolumeName(inst.Name(), backupFileSnap) + // Ensure driver specific default config is filled in new volume. + snapVol := b.GetNewVolume(volType, contentType, newSnapshotName, volumeSnapConfig) + // Validate config and create database entry for new storage volume. // Strip unsupported config keys (in case the export was made from a different type of storage pool). - err = VolumeDBCreate(b, inst.Project().Name, newSnapshotName, volumeSnapDescription, volType, true, volumeSnapConfig, volumeSnapCreationDate, volumeSnapExpiryDate, contentType, true, true) + err = VolumeDBCreate(b, inst.Project().Name, newSnapshotName, snapVol, volumeSnapDescription, true, volumeSnapCreationDate, volumeSnapExpiryDate, true, true) if err != nil { return err } @@ -1114,7 +1124,7 @@ func (b *lxdBackend) CreateInstanceFromCopy(inst instance.Instance, src instance l.Debug("CreateInstanceFromCopy same-pool mode detected") // Validate config and create database entry for new storage volume. - err = VolumeDBCreate(b, inst.Project().Name, inst.Name(), "", vol.Type(), false, vol.Config(), inst.CreationDate(), time.Time{}, contentType, false, true) + err = VolumeDBCreate(b, inst.Project().Name, inst.Name(), vol, "", false, inst.CreationDate(), time.Time{}, false, true) if err != nil { return err } @@ -1135,7 +1145,7 @@ func (b *lxdBackend) CreateInstanceFromCopy(inst instance.Instance, src instance snapVol := b.GetNewVolume(volType, contentType, newSnapshotStorageName, srcConfig.VolumeSnapshots[i].Config) // Validate config and create database entry for new storage volume. - err = VolumeDBCreate(b, inst.Project().Name, newSnapshotName, srcConfig.VolumeSnapshots[i].Description, vol.Type(), true, snapVol.Config(), srcConfig.VolumeSnapshots[i].CreatedAt, volumeSnapExpiryDate, vol.ContentType(), false, true) + err = VolumeDBCreate(b, inst.Project().Name, newSnapshotName, snapVol, srcConfig.VolumeSnapshots[i].Description, true, srcConfig.VolumeSnapshots[i].CreatedAt, volumeSnapExpiryDate, false, true) if err != nil { return err } @@ -1417,7 +1427,7 @@ func (b *lxdBackend) RefreshCustomVolume(projectName string, srcProjectName stri targetSnapVol := b.GetNewVolume(drivers.VolumeTypeCustom, contentType, targetSnapVolStorageName, srcSnap.Config) // Validate config and create database entry for new storage volume from source volume config. - err = VolumeDBCreate(b, projectName, newSnapshotName, srcSnap.Description, drivers.VolumeTypeCustom, true, targetSnapVol.Config(), srcSnap.CreatedAt, snapExpiryDate, contentType, false, true) + err = VolumeDBCreate(b, projectName, newSnapshotName, targetSnapVol, srcSnap.Description, true, srcSnap.CreatedAt, snapExpiryDate, false, true) if err != nil { return err } @@ -1688,7 +1698,7 @@ func (b *lxdBackend) RefreshInstance(inst instance.Instance, src instance.Instan snapVol := b.GetNewVolume(volType, contentType, newSnapshotName, srcConfig.VolumeSnapshots[i].Config) // Validate config and create database entry for new storage volume. - err = VolumeDBCreate(b, inst.Project().Name, newSnapshotName, srcConfig.VolumeSnapshots[i].Description, volType, true, snapVol.Config(), srcConfig.VolumeSnapshots[i].CreatedAt, volumeSnapExpiryDate, contentType, false, true) + err = VolumeDBCreate(b, inst.Project().Name, newSnapshotName, snapVol, srcConfig.VolumeSnapshots[i].Description, true, srcConfig.VolumeSnapshots[i].CreatedAt, volumeSnapExpiryDate, false, true) if err != nil { return err } @@ -2037,7 +2047,7 @@ func (b *lxdBackend) CreateInstanceFromImage(inst instance.Instance, fingerprint } // Validate config and create database entry for new storage volume. - err = VolumeDBCreate(b, inst.Project().Name, inst.Name(), "", volType, false, vol.Config(), inst.CreationDate(), time.Time{}, contentType, true, false) + err = VolumeDBCreate(b, inst.Project().Name, inst.Name(), vol, "", false, inst.CreationDate(), time.Time{}, true, false) if err != nil { return err } @@ -2206,10 +2216,7 @@ func (b *lxdBackend) CreateInstanceFromMigration(inst instance.Instance, conn io if args.MigrationType.FSType == migration.MigrationFSType_RSYNC || args.MigrationType.FSType == migration.MigrationFSType_BLOCK_AND_RSYNC { vol.SetHasSource(false) - err = b.driver.FillVolumeConfig(vol) - if err != nil { - return fmt.Errorf("Failed filling volume config: %w", err) - } + b.driver.FillVolumeConfig(vol) } // Check if the volume exists on storage. @@ -2245,7 +2252,7 @@ func (b *lxdBackend) CreateInstanceFromMigration(inst instance.Instance, conn io } else { // Validate config and create database entry for new storage volume if not refreshing. // Strip unsupported config keys (in case the export was made from a different type of storage pool). - err = VolumeDBCreate(b, inst.Project().Name, inst.Name(), volumeDescription, volType, false, vol.Config(), inst.CreationDate(), time.Time{}, contentType, true, true) + err = VolumeDBCreate(b, inst.Project().Name, inst.Name(), vol, volumeDescription, false, inst.CreationDate(), time.Time{}, true, true) if err != nil { return err } @@ -2290,7 +2297,7 @@ func (b *lxdBackend) CreateInstanceFromMigration(inst instance.Instance, conn io // Validate config and create database entry for new storage volume. // Strip unsupported config keys (in case the export was made from a different type of storage pool). - err = VolumeDBCreate(b, inst.Project().Name, newSnapshotName, snapDescription, volType, true, snapVol.Config(), snapCreationDate, snapExpiryDate, contentType, true, true) + err = VolumeDBCreate(b, inst.Project().Name, newSnapshotName, snapVol, snapDescription, true, snapCreationDate, snapExpiryDate, true, true) if err != nil { return err } @@ -2465,10 +2472,6 @@ func (b *lxdBackend) CreateInstanceFromConversion(inst instance.Instance, conn i // Ensure storage volume settings are honored when doing conversion. vol.SetHasSource(false) - err = b.driver.FillVolumeConfig(vol) - if err != nil { - return fmt.Errorf("Failed filling volume config: %w", err) - } // Check if the volume exists in database dbVol, err := VolumeDBGet(b, inst.Project().Name, inst.Name(), volType) @@ -2495,7 +2498,7 @@ func (b *lxdBackend) CreateInstanceFromConversion(inst instance.Instance, conn i // Validate config and create database entry for new storage volume if not refreshing. // Strip unsupported config keys (in case the export was made from a different type of storage pool). - err = VolumeDBCreate(b, inst.Project().Name, inst.Name(), args.Description, volType, false, vol.Config(), inst.CreationDate(), time.Time{}, contentType, false, true) + err = VolumeDBCreate(b, inst.Project().Name, inst.Name(), vol, args.Description, false, inst.CreationDate(), time.Time{}, false, true) if err != nil { return err } @@ -3592,7 +3595,7 @@ func (b *lxdBackend) CreateInstanceSnapshot(inst instance.Instance, src instance defer revert.Fail() // Validate config and create database entry for new storage volume. - err = VolumeDBCreate(b, inst.Project().Name, inst.Name(), srcDBVol.Description, volType, true, vol.Config(), inst.CreationDate(), time.Time{}, contentType, false, true) + err = VolumeDBCreate(b, inst.Project().Name, inst.Name(), vol, srcDBVol.Description, true, inst.CreationDate(), time.Time{}, false, true) if err != nil { return err } @@ -4114,10 +4117,7 @@ func (b *lxdBackend) EnsureImage(fingerprint string, op *operations.Operation) e // Generate a temporary volume instance that represents how a new volume using pool defaults would // be configured. tmpImgVol := imgVol.Clone() - err := b.Driver().FillVolumeConfig(tmpImgVol) - if err != nil { - return err - } + b.Driver().FillVolumeConfig(tmpImgVol) // Add existing image volume's config to imgVol. imgVol = b.GetVolume(drivers.VolumeTypeImage, contentType, image.Fingerprint, imgDBVol.Config) @@ -4218,7 +4218,7 @@ func (b *lxdBackend) EnsureImage(fingerprint string, op *operations.Operation) e defer revert.Fail() // Validate config and create database entry for new storage volume. - err = VolumeDBCreate(b, api.ProjectDefaultName, image.Fingerprint, "", drivers.VolumeTypeImage, false, imgVol.Config(), time.Now().UTC(), time.Time{}, contentType, false, false) + err = VolumeDBCreate(b, api.ProjectDefaultName, image.Fingerprint, imgVol, "", false, time.Now().UTC(), time.Time{}, false, false) if err != nil { return err } @@ -4263,17 +4263,11 @@ func (b *lxdBackend) shouldUseOptimizedImage(fingerprint string, contentType dri // Create the image volume with the provided volume config. newImgVol := b.GetVolume(drivers.VolumeTypeImage, contentType, fingerprint, volConfig) - err := b.Driver().FillVolumeConfig(newImgVol) - if err != nil { - return false, err - } + b.Driver().FillVolumeConfig(newImgVol) // Create the image volume with pool's default settings. poolDefaultImgVol := b.GetVolume(drivers.VolumeTypeImage, contentType, fingerprint, nil) - err = b.Driver().FillVolumeConfig(poolDefaultImgVol) - if err != nil { - return false, err - } + b.Driver().FillVolumeConfig(poolDefaultImgVol) // If the new volume's config doesn't match the pool's default configuration, don't use an optimized image. if !volumeConfigsMatch(newImgVol, poolDefaultImgVol) { @@ -5281,7 +5275,7 @@ func (b *lxdBackend) CreateCustomVolume(projectName string, volName string, desc defer revert.Fail() // Validate config and create database entry for new storage volume. - err = VolumeDBCreate(b, projectName, volName, desc, vol.Type(), false, vol.Config(), time.Now().UTC(), time.Time{}, vol.ContentType(), false, false) + err = VolumeDBCreate(b, projectName, volName, vol, desc, false, time.Now().UTC(), time.Time{}, false, false) if err != nil { return err } @@ -5413,7 +5407,7 @@ func (b *lxdBackend) CreateCustomVolumeFromCopy(projectName string, srcProjectNa vol := b.GetNewVolume(drivers.VolumeTypeCustom, contentType, volStorageName, config) // Validate config and create database entry for new storage volume. - err = VolumeDBCreate(b, projectName, volName, desc, vol.Type(), false, vol.Config(), time.Now().UTC(), time.Time{}, vol.ContentType(), false, true) + err = VolumeDBCreate(b, projectName, volName, vol, desc, false, time.Now().UTC(), time.Time{}, false, true) if err != nil { return err } @@ -5434,7 +5428,7 @@ func (b *lxdBackend) CreateCustomVolumeFromCopy(projectName string, srcProjectNa snapVol := b.GetNewVolume(vol.Type(), contentType, newSnapshotName, srcConfig.VolumeSnapshots[i].Config) // Validate config and create database entry for new storage volume. - err = VolumeDBCreate(b, projectName, newSnapshotName, srcConfig.VolumeSnapshots[i].Description, vol.Type(), true, snapVol.Config(), srcConfig.VolumeSnapshots[i].CreatedAt, volumeSnapExpiryDate, vol.ContentType(), false, true) + err = VolumeDBCreate(b, projectName, newSnapshotName, snapVol, srcConfig.VolumeSnapshots[i].Description, true, srcConfig.VolumeSnapshots[i].CreatedAt, volumeSnapExpiryDate, false, true) if err != nil { return err } @@ -5825,9 +5819,12 @@ func (b *lxdBackend) CreateCustomVolumeFromMigration(projectName string, conn io defer revert.Fail() if !args.Refresh { + // Ensure driver specific default config is filled in new volume. + b.driver.FillVolumeConfig(vol) + // Validate config and create database entry for new storage volume. // Strip unsupported config keys (in case the export was made from a different type of storage pool). - err = VolumeDBCreate(b, projectName, args.Name, args.Description, vol.Type(), false, vol.Config(), time.Now().UTC(), time.Time{}, vol.ContentType(), true, true) + err = VolumeDBCreate(b, projectName, args.Name, vol, args.Description, false, time.Now().UTC(), time.Time{}, true, true) if err != nil { return err } @@ -5870,7 +5867,7 @@ func (b *lxdBackend) CreateCustomVolumeFromMigration(projectName string, conn io // Validate config and create database entry for new storage volume. // Strip unsupported config keys (in case the export was made from a different type of storage pool). - err = VolumeDBCreate(b, projectName, newSnapshotName, snapDescription, vol.Type(), true, snapVol.Config(), snapCreationDate, snapExpiryDate, vol.ContentType(), true, true) + err = VolumeDBCreate(b, projectName, newSnapshotName, snapVol, snapDescription, true, snapCreationDate, snapExpiryDate, true, true) if err != nil { return err } @@ -6456,7 +6453,7 @@ func (b *lxdBackend) ImportCustomVolume(projectName string, poolVol *backupConfi vol := b.GetNewVolume(drivers.VolumeTypeCustom, drivers.ContentType(poolVol.Volume.ContentType), volStorageName, poolVol.Volume.Config) // Validate config and create database entry for restored storage volume. - err := VolumeDBCreate(b, projectName, poolVol.Volume.Name, poolVol.Volume.Description, drivers.VolumeTypeCustom, false, vol.Config(), poolVol.Volume.CreatedAt, time.Time{}, drivers.ContentType(poolVol.Volume.ContentType), false, true) + err := VolumeDBCreate(b, projectName, poolVol.Volume.Name, vol, poolVol.Volume.Description, false, poolVol.Volume.CreatedAt, time.Time{}, false, true) if err != nil { return nil, err } @@ -6467,12 +6464,11 @@ func (b *lxdBackend) ImportCustomVolume(projectName string, poolVol *backupConfi for _, poolVolSnap := range poolVol.VolumeSnapshots { fullSnapName := drivers.GetSnapshotVolumeName(poolVol.Volume.Name, poolVolSnap.Name) - // Copy volume config from backup file if present - // (so VolumeDBCreate can safely modify the copy if needed). + // Ensure driver specific default config is filled in new volume. snapVol := b.GetNewVolume(drivers.VolumeTypeCustom, drivers.ContentType(poolVolSnap.ContentType), fullSnapName, poolVolSnap.Config) // Validate config and create database entry for restored storage volume. - err = VolumeDBCreate(b, projectName, fullSnapName, poolVolSnap.Description, drivers.VolumeTypeCustom, true, snapVol.Config(), poolVolSnap.CreatedAt, time.Time{}, drivers.ContentType(poolVolSnap.ContentType), false, true) + err = VolumeDBCreate(b, projectName, fullSnapName, snapVol, poolVolSnap.Description, true, poolVolSnap.CreatedAt, time.Time{}, false, true) if err != nil { return nil, err } @@ -6576,8 +6572,7 @@ func (b *lxdBackend) CreateCustomVolumeSnapshot(projectName, volName string, new } // Validate config and create database entry for new storage volume. - // Copy volume config from parent. - err = VolumeDBCreate(b, projectName, fullSnapshotName, description, drivers.VolumeTypeCustom, true, vol.Config(), time.Now().UTC(), newExpiryDate, drivers.ContentType(parentVol.ContentType), false, true) + err = VolumeDBCreate(b, projectName, fullSnapshotName, vol, description, true, time.Now().UTC(), newExpiryDate, false, true) if err != nil { return err } @@ -7350,10 +7345,7 @@ func (b *lxdBackend) detectUnknownCustomVolume(vol *drivers.Volume, projectVols // This may not always be the correct thing to do, but seeing as we don't know what the volume's config // was lets take a best guess that it was the default config. - err = b.driver.FillVolumeConfig(*vol) - if err != nil { - return fmt.Errorf("Failed filling custom volume default config: %w", err) - } + b.driver.FillVolumeConfig(*vol) // Check the filesystem detected is valid for the storage driver. err = b.driver.ValidateVolume(*vol, false) @@ -7410,10 +7402,7 @@ func (b *lxdBackend) detectUnknownBuckets(vol *drivers.Volume, projectVols map[s // This may not always be the correct thing to do, but seeing as we don't know what the volume's config // was lets take a best guess that it was the default config. - err = b.driver.FillVolumeConfig(*vol) - if err != nil { - return fmt.Errorf("Failed filling bucket default config: %w", err) - } + b.driver.FillVolumeConfig(*vol) // Check the detected filesystem is valid for the storage driver. err = b.driver.ValidateVolume(*vol, false) @@ -7481,7 +7470,6 @@ func (b *lxdBackend) ImportInstance(inst instance.Instance, poolVol *backupConfi // Generate the effective root device volume for instance. volStorageName := project.Instance(inst.Project().Name, inst.Name()) - // Copy the volume's config so VolumeDBCreate can safely modify the copy if needed. vol := b.GetNewVolume(volType, contentType, volStorageName, volumeConfig) // Create storage volume database records if in recover mode. @@ -7495,7 +7483,7 @@ func (b *lxdBackend) ImportInstance(inst instance.Instance, poolVol *backupConfi } // Validate config and create database entry for recovered storage volume. - err = VolumeDBCreate(b, inst.Project().Name, inst.Name(), "", volType, false, vol.Config(), creationDate, time.Time{}, contentType, false, true) + err = VolumeDBCreate(b, inst.Project().Name, inst.Name(), vol, "", false, creationDate, time.Time{}, false, true) if err != nil { return nil, err } @@ -7507,12 +7495,11 @@ func (b *lxdBackend) ImportInstance(inst instance.Instance, poolVol *backupConfi for _, poolVolSnap := range poolVol.VolumeSnapshots { fullSnapName := drivers.GetSnapshotVolumeName(inst.Name(), poolVolSnap.Name) - // Copy volume config from backup file if present, - // so VolumeDBCreate can safely modify the copy if needed. + // Ensure driver specific default config is filled in new volume. snapVol := b.GetNewVolume(volType, contentType, fullSnapName, poolVolSnap.Config) // Validate config and create database entry for recovered storage volume. - err = VolumeDBCreate(b, inst.Project().Name, fullSnapName, poolVolSnap.Description, volType, true, snapVol.Config(), poolVolSnap.CreatedAt, time.Time{}, contentType, false, true) + err = VolumeDBCreate(b, inst.Project().Name, fullSnapName, snapVol, poolVolSnap.Description, true, poolVolSnap.CreatedAt, time.Time{}, false, true) if err != nil { return nil, err } @@ -7529,12 +7516,11 @@ func (b *lxdBackend) ImportInstance(inst instance.Instance, poolVol *backupConfi for _, i := range snapshots { fullSnapName := i // Local var for revert. - // Copy the parent volume's config, - // so VolumeDBCreate can safely modify the copy if needed. + // Ensure driver specific default config is filled in new volume. snapVol := b.GetNewVolume(volType, contentType, fullSnapName, volumeConfig) // Validate config and create database entry for new storage volume. - err = VolumeDBCreate(b, inst.Project().Name, fullSnapName, "", volType, true, snapVol.Config(), time.Time{}, time.Time{}, contentType, false, true) + err = VolumeDBCreate(b, inst.Project().Name, fullSnapName, snapVol, "", true, time.Time{}, time.Time{}, false, true) if err != nil { return nil, err } @@ -7720,7 +7706,7 @@ func (b *lxdBackend) CreateCustomVolumeFromISO(projectName string, volName strin } // Validate config and create database entry for new storage volume. - err = VolumeDBCreate(b, projectName, volName, "", vol.Type(), false, vol.Config(), time.Now(), time.Time{}, vol.ContentType(), true, true) + err = VolumeDBCreate(b, projectName, volName, vol, "", false, time.Now(), time.Time{}, true, true) if err != nil { return fmt.Errorf("Failed creating database entry for custom volume: %w", err) } @@ -7817,7 +7803,7 @@ func (b *lxdBackend) CreateCustomVolumeFromBackup(srcBackup backup.Info, srcData // Validate config and create database entry for new storage volume. // Strip unsupported config keys (in case the export was made from a different type of storage pool). - err = VolumeDBCreate(b, srcBackup.Project, srcBackup.Name, srcBackup.Config.Volume.Description, vol.Type(), false, vol.Config(), srcBackup.Config.Volume.CreatedAt, time.Time{}, vol.ContentType(), true, true) + err = VolumeDBCreate(b, srcBackup.Project, srcBackup.Name, vol, srcBackup.Config.Volume.Description, false, srcBackup.Config.Volume.CreatedAt, time.Time{}, true, true) if err != nil { return err } @@ -7843,7 +7829,7 @@ func (b *lxdBackend) CreateCustomVolumeFromBackup(srcBackup backup.Info, srcData // Validate config and create database entry for new storage volume. // Strip unsupported config keys (in case the export was made from a different type of storage pool). - err = VolumeDBCreate(b, srcBackup.Project, fullSnapName, snapshot.Description, snapVol.Type(), true, snapVol.Config(), snapshot.CreatedAt, *snapshot.ExpiresAt, snapVol.ContentType(), true, true) + err = VolumeDBCreate(b, srcBackup.Project, fullSnapName, snapVol, snapshot.Description, true, snapshot.CreatedAt, *snapshot.ExpiresAt, true, true) if err != nil { return err } diff --git a/lxd/storage/backend_lxd_patches.go b/lxd/storage/backend_lxd_patches.go index ae094a123331..0db232e3e8bd 100644 --- a/lxd/storage/backend_lxd_patches.go +++ b/lxd/storage/backend_lxd_patches.go @@ -114,7 +114,8 @@ func patchMissingSnapshotRecords(b *lxdBackend) error { if !foundVolumeSnapshot { b.logger.Info("Creating missing volume snapshot record", logger.Ctx{"project": snapshots[i].Project, "instance": snapshots[i].Name}) - err = VolumeDBCreate(b, snapshots[i].Project, snapshots[i].Name, "Auto repaired", volType, true, dbVol.Config, snapshots[i].CreationDate, time.Time{}, contentType, false, true) + sanpVol := b.GetNewVolume(volType, contentType, snapshots[i].Name, dbVol.Config) + err = VolumeDBCreate(b, snapshots[i].Project, snapshots[i].Name, sanpVol, "Auto repaired", true, snapshots[i].CreationDate, time.Time{}, false, true) if err != nil { return err } diff --git a/lxd/storage/drivers/driver_btrfs_volumes.go b/lxd/storage/drivers/driver_btrfs_volumes.go index f0576911765c..fff7fa5ff506 100644 --- a/lxd/storage/drivers/driver_btrfs_volumes.go +++ b/lxd/storage/drivers/driver_btrfs_volumes.go @@ -411,7 +411,7 @@ func (d *btrfs) CreateVolumeFromBackup(vol VolumeCopy, srcBackup backup.Info, sr // createVolumeFromCopy creates a volume from copy by snapshotting the parent volume. // It also copies the source volume's snapshots and supports refreshing an already existing volume. -func (d *btrfs) createVolumeFromCopy(vol VolumeCopy, srcVol VolumeCopy, allowInconsistent bool, refresh bool, op *operations.Operation) error { +func (d *btrfs) createVolumeFromCopy(vol VolumeCopy, srcVol VolumeCopy, refresh bool, op *operations.Operation) error { revert := revert.New() defer revert.Fail() @@ -540,7 +540,7 @@ func (d *btrfs) createVolumeFromCopy(vol VolumeCopy, srcVol VolumeCopy, allowInc // CreateVolumeFromCopy provides same-pool volume copying functionality. func (d *btrfs) CreateVolumeFromCopy(vol VolumeCopy, srcVol VolumeCopy, allowInconsistent bool, op *operations.Operation) error { - return d.createVolumeFromCopy(vol, srcVol, allowInconsistent, false, op) + return d.createVolumeFromCopy(vol, srcVol, false, op) } // CreateVolumeFromMigration creates a volume being sent via a migration. @@ -589,7 +589,7 @@ func (d *btrfs) CreateVolumeFromMigration(vol VolumeCopy, conn io.ReadWriteClose } if volTargetArgs.Refresh && shared.ValueInSlice(migration.BTRFSFeatureSubvolumeUUIDs, volTargetArgs.MigrationType.Features) { - snapshots, err := d.volumeSnapshotsSorted(vol.Volume, op) + snapshots, err := d.volumeSnapshotsSorted(vol.Volume) if err != nil { return err } @@ -648,10 +648,10 @@ func (d *btrfs) CreateVolumeFromMigration(vol VolumeCopy, conn io.ReadWriteClose syncSubvolumes = migrationHeader.Subvolumes } - return d.createVolumeFromMigrationOptimized(vol.Volume, conn, volTargetArgs, preFiller, syncSubvolumes, op) + return d.createVolumeFromMigrationOptimized(vol.Volume, conn, volTargetArgs, syncSubvolumes, op) } -func (d *btrfs) createVolumeFromMigrationOptimized(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, subvolumes []BTRFSSubVolume, op *operations.Operation) error { +func (d *btrfs) createVolumeFromMigrationOptimized(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, subvolumes []BTRFSSubVolume, op *operations.Operation) error { revert := revert.New() defer revert.Fail() @@ -826,7 +826,7 @@ func (d *btrfs) createVolumeFromMigrationOptimized(vol Volume, conn io.ReadWrite // RefreshVolume provides same-pool volume and specific snapshots syncing functionality. func (d *btrfs) RefreshVolume(vol VolumeCopy, srcVol VolumeCopy, refreshSnapshots []string, allowInconsistent bool, op *operations.Operation) error { - return d.createVolumeFromCopy(vol, srcVol, allowInconsistent, true, op) + return d.createVolumeFromCopy(vol, srcVol, true, op) } // DeleteVolume deletes a volume of the storage device. If any snapshots of the volume remain then @@ -1190,7 +1190,7 @@ func (d *btrfs) MigrateVolume(vol VolumeCopy, conn io.ReadWriteCloser, volSrcArg if !volSrcArgs.VolumeOnly { // Generate restoration header, containing info on the subvolumes and how they should be restored. - snapshots, err = d.volumeSnapshotsSorted(vol.Volume, op) + snapshots, err = d.volumeSnapshotsSorted(vol.Volume) if err != nil { return err } @@ -1785,7 +1785,7 @@ func (d *btrfs) VolumeSnapshots(vol Volume, op *operations.Operation) ([]string, // volumeSnapshotsSorted returns a list of snapshots for the volume (ordered by subvolume ID). // Since the subvolume ID is incremental, this also represents the order of creation. -func (d *btrfs) volumeSnapshotsSorted(vol Volume, op *operations.Operation) ([]string, error) { +func (d *btrfs) volumeSnapshotsSorted(vol Volume) ([]string, error) { stdout := bytes.Buffer{} err := shared.RunCommandWithFds(d.state.ShutdownCtx, nil, &stdout, "btrfs", "subvolume", "list", GetPoolMountPath(vol.pool)) diff --git a/lxd/storage/drivers/driver_ceph_volumes.go b/lxd/storage/drivers/driver_ceph_volumes.go index b23b564c5d76..8a7d43afdf6b 100644 --- a/lxd/storage/drivers/driver_ceph_volumes.go +++ b/lxd/storage/drivers/driver_ceph_volumes.go @@ -35,7 +35,8 @@ import ( func (d *ceph) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Operation) error { // Function to rename an RBD volume. renameVolume := func(oldName string, newName string) error { - _, err := shared.RunCommand( + _, err := shared.RunCommandContext( + context.Background(), "rbd", "--id", d.config["ceph.user.name"], "--cluster", d.config["ceph.cluster_name"], @@ -390,7 +391,8 @@ func (d *ceph) CreateVolumeFromCopy(vol VolumeCopy, srcVol VolumeCopy, allowInco 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( + _, err = shared.RunCommandContext( + context.Background(), "rbd", "--id", d.config["ceph.user.name"], "--cluster", d.config["ceph.cluster_name"], @@ -1022,7 +1024,8 @@ func (d *ceph) DeleteVolume(vol Volume, op *operations.Operation) error { } // Delete snapshots. - _, err := shared.RunCommand( + _, err := shared.RunCommandContext( + context.Background(), "rbd", "--id", d.config["ceph.user.name"], "--cluster", d.config["ceph.cluster_name"], @@ -1116,14 +1119,11 @@ func (d *ceph) HasVolume(vol Volume) (bool, error) { } // FillVolumeConfig populate volume with default config. -func (d *ceph) FillVolumeConfig(vol Volume) error { +func (d *ceph) FillVolumeConfig(vol Volume) { // Copy volume.* configuration options from pool. // Exclude 'block.filesystem' and 'block.mount_options' // as this ones are handled below in this function and depends from volume type - err := d.fillVolumeConfig(&vol, "block.filesystem", "block.mount_options") - if err != nil { - return err - } + d.fillVolumeConfig(&vol, "block.filesystem", "block.mount_options") // Only validate filesystem config keys for filesystem volumes or VM block volumes (which have an // associated filesystem volume). @@ -1155,8 +1155,6 @@ func (d *ceph) FillVolumeConfig(vol Volume) error { vol.config["block.mount_options"] = "discard" } } - - return nil } // commonVolumeRules returns validation rules which are common for pool and volume. @@ -1926,7 +1924,8 @@ func (d *ceph) CreateVolumeSnapshot(snapVol Volume, op *operations.Operation) er // DeleteVolumeSnapshot removes a snapshot from the storage device. func (d *ceph) DeleteVolumeSnapshot(snapVol Volume, op *operations.Operation) error { // Check if snapshot exists, and return if not. - _, err := shared.RunCommand( + _, err := shared.RunCommandContext( + context.Background(), "rbd", "--id", d.config["ceph.user.name"], "--cluster", d.config["ceph.cluster_name"], @@ -2167,7 +2166,7 @@ func (d *ceph) VolumeSnapshots(vol Volume, op *operations.Operation) ([]string, return nil, err } - var ret []string + ret := make([]string, 0, len(snapshots)) for _, snap := range snapshots { // Ignore zombie snapshots as these are only used internally and @@ -2196,7 +2195,8 @@ func (d *ceph) restoreVolume(vol Volume, snapVol Volume, op *operations.Operatio _, snapshotName, _ := api.GetParentAndSnapshotName(snapVol.name) - _, err = shared.RunCommand( + _, err = shared.RunCommandContext( + context.Background(), "rbd", "--id", d.config["ceph.user.name"], "--cluster", d.config["ceph.cluster_name"], diff --git a/lxd/storage/drivers/driver_common.go b/lxd/storage/drivers/driver_common.go index 291e25d6ee7c..8397cc10892a 100644 --- a/lxd/storage/drivers/driver_common.go +++ b/lxd/storage/drivers/driver_common.go @@ -1,6 +1,7 @@ package drivers import ( + "context" "fmt" "io" "net/url" @@ -105,7 +106,7 @@ func (d *common) validatePool(config map[string]string, driverRules map[string]f // excludeKeys allow exclude some keys from copying to volume config. // Sometimes that can be useful when copying is dependant from specific conditions // and shouldn't be done in generic way. -func (d *common) fillVolumeConfig(vol *Volume, excludedKeys ...string) error { +func (d *common) fillVolumeConfig(vol *Volume, excludedKeys ...string) { for k := range d.config { if !strings.HasPrefix(k, "volume.") { continue @@ -144,13 +145,11 @@ func (d *common) fillVolumeConfig(vol *Volume, excludedKeys ...string) error { vol.config[volKey] = d.config[k] } } - - return nil } // FillVolumeConfig populate volume with default config. -func (d *common) FillVolumeConfig(vol Volume) error { - return d.fillVolumeConfig(&vol) +func (d *common) FillVolumeConfig(vol Volume) { + d.fillVolumeConfig(&vol) } // validateVolume validates a volume config against common rules and optional driver specific rules. @@ -291,7 +290,7 @@ func (d *common) moveGPTAltHeader(devPath string) error { return nil } - _, err = shared.RunCommand(path, "--move-second-header", devPath) + _, err = shared.RunCommandContext(context.Background(), path, "--move-second-header", devPath) if err == nil { d.logger.Debug("Moved GPT alternative header to end of disk", logger.Ctx{"dev": devPath}) return nil @@ -584,7 +583,7 @@ func (d *common) filesystemFreeze(path string) (func() error, error) { return nil, fmt.Errorf("Failed syncing filesystem %q: %w", path, err) } - _, err = shared.RunCommand("fsfreeze", "--freeze", path) + _, err = shared.RunCommandContext(context.Background(), "fsfreeze", "--freeze", path) if err != nil { return nil, fmt.Errorf("Failed freezing filesystem %q: %w", path, err) } @@ -592,7 +591,7 @@ func (d *common) filesystemFreeze(path string) (func() error, error) { d.logger.Info("Filesystem frozen", logger.Ctx{"path": path}) unfreezeFS := func() error { - _, err := shared.RunCommand("fsfreeze", "--unfreeze", path) + _, err := shared.RunCommandContext(context.Background(), "fsfreeze", "--unfreeze", path) if err != nil { return fmt.Errorf("Failed unfreezing filesystem %q: %w", path, err) } diff --git a/lxd/storage/drivers/driver_dir_volumes.go b/lxd/storage/drivers/driver_dir_volumes.go index a824286e36e1..a21694bbb3e7 100644 --- a/lxd/storage/drivers/driver_dir_volumes.go +++ b/lxd/storage/drivers/driver_dir_volumes.go @@ -226,21 +226,16 @@ func (d *dir) HasVolume(vol Volume) (bool, error) { } // FillVolumeConfig populate volume with default config. -func (d *dir) FillVolumeConfig(vol Volume) error { +func (d *dir) FillVolumeConfig(vol Volume) { initialSize := vol.config["size"] - err := d.fillVolumeConfig(&vol) - if err != nil { - return err - } + d.fillVolumeConfig(&vol) // Buckets do not support default volume size. // If size is specified manually, do not remove, so it triggers validation failure and an error to user. if vol.volType == VolumeTypeBucket && initialSize == "" { delete(vol.config, "size") } - - return nil } // ValidateVolume validates the supplied volume config. Optionally removes invalid keys from the volume's config. diff --git a/lxd/storage/drivers/driver_lvm_volumes.go b/lxd/storage/drivers/driver_lvm_volumes.go index 9b9db0f1be9c..9b867c6f6008 100644 --- a/lxd/storage/drivers/driver_lvm_volumes.go +++ b/lxd/storage/drivers/driver_lvm_volumes.go @@ -265,14 +265,11 @@ func (d *lvm) HasVolume(vol Volume) (bool, error) { } // FillVolumeConfig populate volume with default config. -func (d *lvm) FillVolumeConfig(vol Volume) error { +func (d *lvm) FillVolumeConfig(vol Volume) { // Copy volume.* configuration options from pool. // Exclude "block.filesystem" and "block.mount_options" as they depend on volume type (handled below). // Exclude "lvm.stripes", "lvm.stripes.size" as they only work on non-thin storage pools (handled below). - err := d.fillVolumeConfig(&vol, "block.filesystem", "block.mount_options", "lvm.stripes", "lvm.stripes.size") - if err != nil { - return err - } + d.fillVolumeConfig(&vol, "block.filesystem", "block.mount_options", "lvm.stripes", "lvm.stripes.size") // Only validate filesystem config keys for filesystem volumes or VM block volumes (which have an // associated filesystem volume). @@ -315,8 +312,6 @@ func (d *lvm) FillVolumeConfig(vol Volume) error { vol.config["lvm.stripes.size"] = d.config["lvm.stripes.size"] } } - - return nil } // commonVolumeRules returns validation rules which are common for pool and volume. diff --git a/lxd/storage/drivers/driver_powerflex_volumes.go b/lxd/storage/drivers/driver_powerflex_volumes.go index e1de0ed25060..19a20b72217c 100644 --- a/lxd/storage/drivers/driver_powerflex_volumes.go +++ b/lxd/storage/drivers/driver_powerflex_volumes.go @@ -376,14 +376,11 @@ func (d *powerflex) HasVolume(vol Volume) (bool, error) { } // FillVolumeConfig populate volume with default config. -func (d *powerflex) FillVolumeConfig(vol Volume) error { +func (d *powerflex) FillVolumeConfig(vol Volume) { // Copy volume.* configuration options from pool. // Exclude 'block.filesystem' and 'block.mount_options' // as these ones are handled below in this function and depend on the volume's type. - err := d.fillVolumeConfig(&vol, "block.filesystem", "block.mount_options") - if err != nil { - return err - } + d.fillVolumeConfig(&vol, "block.filesystem", "block.mount_options") // Only validate filesystem config keys for filesystem volumes or VM block volumes (which have an // associated filesystem volume). @@ -415,8 +412,6 @@ func (d *powerflex) FillVolumeConfig(vol Volume) error { vol.config["block.mount_options"] = "discard" } } - - return nil } // commonVolumeRules returns validation rules which are common for pool and volume. diff --git a/lxd/storage/drivers/driver_pure_volumes.go b/lxd/storage/drivers/driver_pure_volumes.go index d849a44fda09..aed0e2582b3c 100644 --- a/lxd/storage/drivers/driver_pure_volumes.go +++ b/lxd/storage/drivers/driver_pure_volumes.go @@ -671,14 +671,11 @@ func (d *pure) HasVolume(vol Volume) (bool, error) { } // FillVolumeConfig populate volume with default config. -func (d *pure) FillVolumeConfig(vol Volume) error { +func (d *pure) FillVolumeConfig(vol Volume) { // Copy volume.* configuration options from pool. // Exclude 'block.filesystem' and 'block.mount_options' // as these ones are handled below in this function and depend on the volume's type. - err := d.fillVolumeConfig(&vol, "block.filesystem", "block.mount_options") - if err != nil { - return err - } + d.fillVolumeConfig(&vol, "block.filesystem", "block.mount_options") // Only validate filesystem config keys for filesystem volumes or VM block volumes (which have an // associated filesystem volume). @@ -710,8 +707,6 @@ func (d *pure) FillVolumeConfig(vol Volume) error { vol.config["block.mount_options"] = "discard" } } - - return nil } // ValidateVolume validates the supplied volume config. diff --git a/lxd/storage/drivers/driver_zfs_volumes.go b/lxd/storage/drivers/driver_zfs_volumes.go index 2ce25fc6d15c..d1c04d948d7d 100644 --- a/lxd/storage/drivers/driver_zfs_volumes.go +++ b/lxd/storage/drivers/driver_zfs_volumes.go @@ -3508,7 +3508,7 @@ func (d *zfs) RenameVolumeSnapshot(vol Volume, newSnapshotName string, op *opera } // FillVolumeConfig populate volume with default config. -func (d *zfs) FillVolumeConfig(vol Volume) error { +func (d *zfs) FillVolumeConfig(vol Volume) { var excludedKeys []string // Copy volume.* configuration options from pool. @@ -3519,10 +3519,7 @@ func (d *zfs) FillVolumeConfig(vol Volume) error { excludedKeys = []string{"block.filesystem", "block.mount_options"} } - err := d.fillVolumeConfig(&vol, excludedKeys...) - if err != nil { - return err - } + d.fillVolumeConfig(&vol, excludedKeys...) // Only validate filesystem config keys for filesystem volumes. if d.isBlockBacked(vol) && vol.ContentType() == ContentTypeFS { @@ -3553,8 +3550,6 @@ func (d *zfs) FillVolumeConfig(vol Volume) error { vol.config["block.mount_options"] = "discard" } } - - return nil } func (d *zfs) isBlockBacked(vol Volume) bool { diff --git a/lxd/storage/drivers/interface.go b/lxd/storage/drivers/interface.go index d31d8528dab2..a7cfb35357c6 100644 --- a/lxd/storage/drivers/interface.go +++ b/lxd/storage/drivers/interface.go @@ -63,7 +63,7 @@ type Driver interface { DeleteBucketKey(bucket Volume, keyName string, op *operations.Operation) error // Volumes. - FillVolumeConfig(vol Volume) error + FillVolumeConfig(vol Volume) ValidateVolume(vol Volume, removeUnknownKeys bool) error CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Operation) error CreateVolumeFromCopy(vol VolumeCopy, srcVol VolumeCopy, allowInconsistent bool, op *operations.Operation) error diff --git a/lxd/storage/utils.go b/lxd/storage/utils.go index 76708f7b3997..2160874d7ee3 100644 --- a/lxd/storage/utils.go +++ b/lxd/storage/utils.go @@ -2,7 +2,9 @@ package storage import ( "context" + "crypto/sha256" "encoding/json" + "errors" "fmt" "os" "path/filepath" @@ -219,22 +221,23 @@ func VolumeDBGet(pool Pool, projectName string, volumeName string, volumeType dr } // VolumeDBCreate creates a volume in the database. -// If volumeConfig is supplied, it is modified with any driver level default config options (if not set). // If removeUnknownKeys is true, any unknown config keys are removed from volumeConfig rather than failing. -func VolumeDBCreate(pool Pool, projectName string, volumeName string, volumeDescription string, volumeType drivers.VolumeType, snapshot bool, volumeConfig map[string]string, creationDate time.Time, expiryDate time.Time, contentType drivers.ContentType, removeUnknownKeys bool, hasSource bool) error { +func VolumeDBCreate(pool Pool, projectName string, volumeName string, volume drivers.Volume, volumeDescription string, snapshot bool, creationDate time.Time, expiryDate time.Time, removeUnknownKeys bool, hasSource bool) error { p, ok := pool.(*lxdBackend) if !ok { return fmt.Errorf("Pool is not a lxdBackend") } // Prevent using this function to create storage volume bucket records. - if volumeType == drivers.VolumeTypeBucket { + if volume.Type() == drivers.VolumeTypeBucket { return fmt.Errorf("Cannot store volume using bucket type") } + volumeConfig := volume.Config() + // If the volumeType represents an instance type then check that the volumeConfig doesn't contain any of // the instance disk effective override fields (which should not be stored in the database). - if volumeType.IsInstance() { + if volume.Type().IsInstance() { for _, k := range instanceDiskVolumeEffectiveFields { _, found := volumeConfig[k] if found { @@ -244,12 +247,12 @@ func VolumeDBCreate(pool Pool, projectName string, volumeName string, volumeDesc } // Convert the volume type to our internal integer representation. - volDBType, err := VolumeTypeToDBType(volumeType) + volDBType, err := VolumeTypeToDBType(volume.Type()) if err != nil { return err } - volDBContentType, err := VolumeContentTypeToDBContentType(contentType) + volDBContentType, err := VolumeContentTypeToDBContentType(volume.ContentType()) if err != nil { return err } @@ -264,15 +267,21 @@ func VolumeDBCreate(pool Pool, projectName string, volumeName string, volumeDesc return err } - vol := drivers.NewVolume(pool.Driver(), pool.Name(), volType, contentType, volumeName, volumeConfig, pool.Driver().Config()) + vol := drivers.NewVolume(pool.Driver(), pool.Name(), volType, volume.ContentType(), volumeName, volumeConfig, pool.Driver().Config()) // Set source indicator. vol.SetHasSource(hasSource) - // Fill default config. - err = pool.Driver().FillVolumeConfig(vol) - if err != nil { - return err + bytes, _ := json.Marshal(vol.Config()) // Convert map to JSON + old := sha256.Sum256(bytes) + + pool.Driver().FillVolumeConfig(vol) + + bytes, _ = json.Marshal(vol.Config()) // Convert map to JSON + newHash := sha256.Sum256(bytes) + + if old != newHash { + return errors.New("Volume fill not previously performed") } // Validate config. @@ -292,7 +301,7 @@ func VolumeDBCreate(pool Pool, projectName string, volumeName string, volumeDesc return err }) if err != nil { - return fmt.Errorf("Error inserting volume %q for project %q in pool %q of type %q into database %q", volumeName, projectName, pool.Name(), volumeType, err) + return fmt.Errorf("Error inserting volume %q for project %q in pool %q of type %q into database %q", volumeName, projectName, pool.Name(), volume.Type(), err) } return nil @@ -395,13 +404,10 @@ func BucketDBCreate(ctx context.Context, pool Pool, projectName string, memberSp bucketVol := drivers.NewVolume(pool.Driver(), pool.Name(), drivers.VolumeTypeBucket, drivers.ContentTypeFS, bucketVolName, bucket.Config, pool.Driver().Config()) // Fill default config. - err := pool.Driver().FillVolumeConfig(bucketVol) - if err != nil { - return -1, err - } + pool.Driver().FillVolumeConfig(bucketVol) // Validate bucket name. - err = pool.Driver().ValidateBucket(bucketVol) + err := pool.Driver().ValidateBucket(bucketVol) if err != nil { return -1, err }