Skip to content

Commit

Permalink
Merge pull request #12386 from monstermunchkin/issues/11445-server-si…
Browse files Browse the repository at this point in the history
…de-move-for-custom-storage-volumes

Support server side copy/move of custom storage volumes in clusters
  • Loading branch information
tomponline authored Oct 25, 2023
2 parents b0abd20 + b7e758a commit 9f78fdb
Show file tree
Hide file tree
Showing 7 changed files with 421 additions and 4 deletions.
8 changes: 7 additions & 1 deletion client/lxd_storage_volumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -555,8 +555,10 @@ func (r *ProtocolLXD) CopyStoragePoolVolume(pool string, source InstanceServer,
return nil, fmt.Errorf("Failed to get destination connection info: %w", err)
}

clusterInternalVolumeCopy := r.CheckExtension("cluster_internal_custom_volume_copy") == nil

// Copy the storage pool volume locally.
if destInfo.URL == sourceInfo.URL && destInfo.SocketPath == sourceInfo.SocketPath && (volume.Location == r.clusterTarget || (volume.Location == "none" && r.clusterTarget == "")) {
if destInfo.URL == sourceInfo.URL && destInfo.SocketPath == sourceInfo.SocketPath && (volume.Location == r.clusterTarget || (volume.Location == "none" && r.clusterTarget == "") || clusterInternalVolumeCopy) {
// Project handling
if destInfo.Project != sourceInfo.Project {
if !r.HasExtension("storage_api_project") {
Expand All @@ -566,6 +568,10 @@ func (r *ProtocolLXD) CopyStoragePoolVolume(pool string, source InstanceServer,
req.Source.Project = sourceInfo.Project
}

if clusterInternalVolumeCopy {
req.Source.Location = sourceInfo.Target
}

// Send the request
op, _, err := r.queryOperation("POST", fmt.Sprintf("/storage-pools/%s/volumes/%s", url.PathEscape(pool), url.PathEscape(volume.Type)), req, "", true)
if err != nil {
Expand Down
6 changes: 6 additions & 0 deletions doc/api-extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2312,3 +2312,9 @@ no effect on existing devices.
This API extension indicates that the `/1.0/operations/{id}/wait` endpoint exists on the server. This indicates to the client
that the endpoint can be used to wait for an operation to complete rather than waiting for an operation event via the
`/1.0/events` endpoint.

## `cluster_internal_custom_volume_copy`

This extension adds support for copying and moving custom storage volumes within a cluster with a single API call.
Calling `POST /1.0/storage-pools/<pool>/custom?target=<target>` will copy the custom volume specified in the `source` part of the request.
Calling `POST /1.0/storage-pools/<pool>/custom/<volume>?target=<target>` will move the custom volume from the source, specified in the `source` part of the request, to the target.
7 changes: 7 additions & 0 deletions doc/rest-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5827,6 +5827,8 @@ definitions:
example: foo
type: string
x-go-name: Project
source:
$ref: '#/definitions/StorageVolumeSource'
target:
$ref: '#/definitions/StorageVolumePostTarget'
volume_only:
Expand Down Expand Up @@ -5981,6 +5983,11 @@ definitions:
example: X509 PEM certificate
type: string
x-go-name: Certificate
location:
description: What cluster member this record was found on
example: lxd01
type: string
x-go-name: Location
mode:
description: Whether to use pull or push mode (for migration)
example: pull
Expand Down
31 changes: 31 additions & 0 deletions lxd/db/storage_volumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -953,3 +953,34 @@ func (c *ClusterTx) GetStorageVolumeURIs(ctx context.Context, project string) ([

return uris, nil
}

// UpdateStorageVolumeNode changes the name of a storage volume and the cluster member hosting it.
// It's meant to be used when moving a storage volume backed by ceph from one cluster node to another.
func (c *ClusterTx) UpdateStorageVolumeNode(ctx context.Context, projectName string, oldName string, newName string, newMemberName string, poolID int64, volumeType int) error {
volume, err := c.GetStoragePoolVolume(ctx, poolID, projectName, volumeType, oldName, false)
if err != nil {
return err
}

member, err := c.GetNodeByName(ctx, newMemberName)
if err != nil {
return fmt.Errorf("Failed to get new member %q info: %w", newMemberName, err)
}

stmt := "UPDATE storage_volumes SET node_id=?, name=? WHERE id=?"
result, err := c.tx.Exec(stmt, member.ID, newName, volume.ID)
if err != nil {
return fmt.Errorf("Failed to update volumes's name and member ID: %w", err)
}

n, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("Failed to get rows affected by volume update: %w", err)
}

if n != 1 {
return fmt.Errorf("Unexpected number of updated rows in storage_volumes table: %d", n)
}

return nil
}
Loading

0 comments on commit 9f78fdb

Please sign in to comment.