Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Storage: Add Pure Storage storage driver #14599

Merged
merged 51 commits into from
Feb 4, 2025
Merged
Changes from 1 commit
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
44db9b7
api: Add Pure Storage driver API extension
MusicDin Jan 9, 2025
7a10271
lxd/storage/drivers/pure: Initial scaffolding for storage driver pure
MusicDin Sep 27, 2024
53cac8d
lxd/storage/drivers/driver_types: Add driver option PopulateParentVol…
MusicDin Dec 4, 2024
44d05ad
lxd/storage/backend_lxd: Ensure parent volume UUID is populated when …
MusicDin Dec 4, 2024
b6b8876
lxd/storage/drivers: Configure PopulateParentVolumeUUID for all drivers
MusicDin Dec 6, 2024
668ff1c
lxd/storage/drivers/load: Add pure as storage driver
MusicDin Nov 20, 2024
2313ef3
lxd/storage/drivers/pure: Add basic pool and volume configuration val…
MusicDin Sep 30, 2024
332f955
lxd/storage/drivers/pure: Add login and request wrapper
MusicDin Sep 30, 2024
26320b3
lxd/storage/drivers/pure: Create and delete storage pool
MusicDin Nov 20, 2024
4c9c611
lxd/storage/drivers/pure: Add utils for handling Pure Storage hosts
MusicDin Nov 20, 2024
bfb237c
lxd/storage/drivers/pure: Function to resolve Pure Storage volume name
MusicDin Nov 20, 2024
bc70f8e
lxd/storage/drivers/pure: Test volume name generation
MusicDin Dec 11, 2024
0460682
lxd/storage/drivers/pure: Add util function to retrieve network inter…
MusicDin Jan 9, 2025
dc9992b
lxd/storage/connectors: Add iSCSI connector
MusicDin Dec 18, 2024
0950016
lxd/storage/drivers/pure: Add iSCSI support and volume mapping utils
MusicDin Nov 20, 2024
685bbd4
lxd/storage/drivers/pure: Ensure multipath device is properly removed
MusicDin Jan 17, 2025
60c2ab0
lxd/storage/drivers/pure: Create, mount, and unmount volume
MusicDin Oct 3, 2024
9d37bd3
lxd/storage/drivers/pure: Add NVMe/TCP support
MusicDin Dec 5, 2024
ff78ff4
lxd/storage/drivers/pure: Delete volume
MusicDin Nov 20, 2024
c55b252
lxd/storage/drivers/volume: Add util function to get parent volume
MusicDin Feb 3, 2025
9f05ea1
lxd/storage/drivers/pure: Create and delete volume snapshots
MusicDin Oct 9, 2024
b9c5eb7
lxd/storage/drivers/pure: Add utils for retrieving storage arrays
MusicDin Nov 20, 2024
2224238
lxd/storage/drivers/pure: Extract storage pool, volume, and array spa…
MusicDin Nov 20, 2024
b217d75
lxd/storage/drivers/pure: Report resource usage of storage pools and …
MusicDin Oct 10, 2024
5931f83
lxd/storage/drivers/pure: Restore volume snapshots
MusicDin Oct 10, 2024
cf938f3
lxd/storage/drivers/pure: Get volume usage from Pure Storage
MusicDin Nov 20, 2024
f9d28da
lxd/storage/drivers/pure: Set or update volume quota
MusicDin Oct 10, 2024
1984b93
lxd/storage/drivers/pure: Allow volume copy with snapshots and optimi…
MusicDin Oct 15, 2024
050390e
lxd/storage/drivers/pure: Volume refresh
MusicDin Oct 16, 2024
a4c9246
lxd/storage/drivers/pure: Mount/unmount volume snapshot
MusicDin Oct 18, 2024
bba1c1b
lxd/storage/drivers/pure: Volume migration
MusicDin Oct 22, 2024
a8ca309
lxd/storage/drivers/pure: Handle cluster member volume move
MusicDin Nov 22, 2024
d59b924
lxd/storage/drivers/pure: Allow changing storage pool quota
MusicDin Nov 20, 2024
a41a5d4
lxd/storage/drivers/pure: Delete default protection groups when stora…
MusicDin Dec 22, 2024
2a93472
lxd/storage/drivers/pure: Wait for desired disk size after resize
MusicDin Jan 30, 2025
36c7cc9
lxd/storage/drivers/pure: Allow setting custom target addresses
MusicDin Jan 31, 2025
4b43b63
lxd/storage/backend_lxd: Ensure volatile uuid is set for regenerated …
MusicDin Jan 31, 2025
2a0051a
lxd/storage/utils: Add Pure Storage to common volume rules
MusicDin Dec 6, 2024
d573f87
test/backends: Helper functions for creating Pure Storage pools
MusicDin Nov 12, 2024
f30463e
test/includes/storage: Include Pure Storage driver if gateway and api…
MusicDin Dec 6, 2024
6b1a04b
test/storage_driver_pure: Add basic Pure Storage tests
MusicDin Nov 8, 2024
f7a0a9d
test/container_move: Use helper function to create Pure Storage pool
MusicDin Nov 12, 2024
12508d4
test/storage_local_volume_handling: Test Pure Storage with other avai…
MusicDin Nov 14, 2024
e6aaa1b
test/storage_snapshots: Use helper function to create Pure Storage pool
MusicDin Nov 14, 2024
a353baa
test/backup: Skip recovery tests for Pure Storage driver
MusicDin Nov 15, 2024
729aaac
docs: Add Pure Storage driver docs
MusicDin Oct 22, 2024
9418590
docs: Add example on how to create Pure Storage storage pool
MusicDin Oct 22, 2024
b5159d3
docs: Add explanation of Pure Storage remote storage
MusicDin Oct 22, 2024
c79502c
docs: Add Pure Storage and its features to table of supported storage…
MusicDin Oct 22, 2024
d0f8f5a
docs: Update wordlist
MusicDin Dec 6, 2024
40b6b51
docs: Update metadata
MusicDin Dec 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 97 additions & 4 deletions lxd/storage/drivers/driver_pure_volumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -1224,14 +1224,107 @@ func (d *pure) DeleteVolumeSnapshot(snapVol Volume, op *operations.Operation) er
return nil
}

// MountVolumeSnapshot simulates mounting a volume snapshot.
// MountVolumeSnapshot creates a new temporary volume from a volume snapshot to allow mounting it.
func (d *pure) MountVolumeSnapshot(snapVol Volume, op *operations.Operation) error {
return d.MountVolume(snapVol, op)
revert := revert.New()
defer revert.Fail()

parentVol := snapVol.GetParent()

// Get the parent volume name.
parentVolName, err := d.getVolumeName(parentVol)
if err != nil {
return err
}

// Get the snapshot volume name.
snapVolName, err := d.getVolumeName(snapVol)
if err != nil {
return err
}

// A Pure Storage snapshot cannot be mounted. To mount a snapshot, a new volume
// has to be created from the snapshot.
err = d.client().copyVolumeSnapshot(snapVol.pool, parentVolName, snapVolName, snapVol.pool, snapVolName)
MusicDin marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MusicDin question for you:

What happens if I've got a snapshot mounted (so a temporary volume has been created), and then lxd gets stopped and the host restarted uncleanly, and i go to mount the snapshot again, will this call fail because the temporary volume already exists?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copyVolumeSnapshot creates a new volume, or overwrites an existing one if it already exists.

Mounting is handled by the NVMe/iSCSI if host is mapped with a certain volume.
When we attempt to create a mapping, the error is ignored if it already exists, so it cannot fail there.

The only potential issue I can see is that if LXD is abruptly shut down, it will not cleanup the temporary snapshot. For example, if the instance is then removed, this temporary snapshot could remain in Pure Storage.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^ Until the pool itself is removed, then everything is wiped.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or overwrites an existing one if it already exists.

OK cool,that was the bit I was mainly interested in.

For example, if the instance is then removed, this temporary snapshot could remain in Pure Storage.

Can we scan volumes on instance (or snapshot) delete to find any temporary volumes related and delete them too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we scan volumes on instance (or snapshot) delete to find any temporary volumes related and delete them too?

I cannot foreseen anything that would block us from doing that.
What is your suggestion, at which phase should this happen? Should we run the cleanup on LXD boot / when storage is loaded?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when deleting the instance snapshot, and when deleting the instance (which by extension is deleting the snapshot)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh yeah of course. So in practice this would mean when we are deleting a specific snapshot, we check if a volume exists with such uuid.

For example:

  1. Delete snapshot v1/snap0
  2. Check if there is an existing volume with snap0-uuid (obviously correctly resolving volume prefixes and suffixes in the process)
  3. Delete such volume if exists
  4. Delete an actual snapshot snap0

if err != nil {
return err
}

// Ensure temporary snapshot volume is remooved in case of an error.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a blocker to merge, but typo here

revert.Add(func() { _ = d.client().deleteVolume(snapVol.pool, snapVolName) })

// For VMs, also create the temporary filesystem volume snapshot.
if snapVol.IsVMBlock() {
snapFsVol := snapVol.NewVMBlockFilesystemVolume()
snapFsVol.SetParentUUID(snapVol.parentUUID)

parentFsVol := snapFsVol.GetParent()

snapFsVolName, err := d.getVolumeName(snapFsVol)
if err != nil {
return err
}

parentFsVolName, err := d.getVolumeName(parentFsVol)
if err != nil {
return err
}

err = d.client().copyVolumeSnapshot(snapVol.pool, parentFsVolName, snapFsVolName, snapVol.pool, snapFsVolName)
if err != nil {
return err
}

revert.Add(func() { _ = d.client().deleteVolume(snapVol.pool, snapFsVolName) })
}

err = d.MountVolume(snapVol, op)
if err != nil {
return err
}

revert.Success()
return nil
}

// UnmountVolumeSnapshot simulates unmounting a volume snapshot.
// UnmountVolumeSnapshot unmountes and deletes volume that was temporary created from a snapshot
// to allow mounting it.
func (d *pure) UnmountVolumeSnapshot(snapVol Volume, op *operations.Operation) (bool, error) {
return d.UnmountVolume(snapVol, false, op)
ourUnmount, err := d.UnmountVolume(snapVol, false, op)
if err != nil {
return false, err
}

if !ourUnmount {
return false, nil
}

snapVolName, err := d.getVolumeName(snapVol)
if err != nil {
return true, err
}

// Cleanup temporary snapshot volume.
err = d.client().deleteVolume(snapVol.pool, snapVolName)
if err != nil {
return true, err
}

// For VMs, also cleanup the temporary volume for a filesystem snapshot.
if snapVol.IsVMBlock() {
snapFsVol := snapVol.NewVMBlockFilesystemVolume()
snapFsVolName, err := d.getVolumeName(snapFsVol)
if err != nil {
return true, err
}

err = d.client().deleteVolume(snapVol.pool, snapFsVolName)
if err != nil {
return true, err
}
}

return ourUnmount, nil
}

// VolumeSnapshots returns a list of Pure Storage snapshot names for the given volume (in no particular order).
Expand Down