Skip to content

Commit 775c96c

Browse files
committed
Add support for volume/share name resolve
1 parent 75cd995 commit 775c96c

File tree

12 files changed

+200
-26
lines changed

12 files changed

+200
-26
lines changed

docs/cinder-csi-plugin/examples.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ metadata:
481481
name: csi-pvc-cinderplugin
482482
annotations:
483483
cinder.csi.openstack.org/affinity: "1b4e28ba-2fa1-11ec-8d3d-0242ac130003"
484-
cinder.csi.openstack.org/anti-affinity: "1b4e28ba-2fa1-11ec-8d3d-0242ac130004,1b4e28ba-2fa1-11ec-8d3d-0242ac130005"
484+
cinder.csi.openstack.org/anti-affinity: "1b4e28ba-2fa1-11ec-8d3d-0242ac130004,pv-k8s--cluster-1b5f47bf-0119-442e-8529-254c36e43644"
485485
spec:
486486
accessModes:
487487
- ReadWriteOnce
@@ -493,12 +493,12 @@ spec:
493493

494494
where `1b4e28ba-2fa1-11ec-8d3d-0242ac130003`,
495495
`1b4e28ba-2fa1-11ec-8d3d-0242ac130004` and
496-
`1b4e28ba-2fa1-11ec-8d3d-0242ac130005` are the UUIDs of the already provisioned
496+
`pv-k8s--cluster-1b5f47bf-0119-442e-8529-254c36e43644` are the UUIDs of the already provisioned
497497
volumes in the OpenStack cloud. The scheduler will try to place the volume in
498498
the same block storage server as the volume with UUID
499499
`1b4e28ba-2fa1-11ec-8d3d-0242ac130003` and avoid placing the volume in the same
500500
block storage server as the volumes with UUIDs
501501
`1b4e28ba-2fa1-11ec-8d3d-0242ac130004` and
502-
`1b4e28ba-2fa1-11ec-8d3d-0242ac130005`. If the scheduler hints are not
503-
satisfied, the volume will not be provisioned with an error message in the
504-
controller logs.
502+
`pv-k8s--cluster-1b5f47bf-0119-442e-8529-254c36e43644`. If the scheduler hints
503+
are not satisfied, the volume will not be provisioned with an error message in
504+
the controller logs.

docs/cinder-csi-plugin/using-cinder-csi-plugin.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -291,14 +291,14 @@ following PVC annotations are supported:
291291

292292
| Annotation Name | Description | Example |
293293
|------------------------- |-----------------|----------|
294-
| `cinder.csi.openstack.org/affinity` | Volume affinity to existing volume or volumes UUIDs. The value should be a comma-separated list of volume UUIDs. | `cinder.csi.openstack.org/affinity: "1b4e28ba-2fa1-11ec-8d3d-0242ac130003"` |
295-
| `cinder.csi.openstack.org/anti-affinity` | Volume anti-affinity to existing volume or volumes UUIDs. The value should be a comma-separated list of volume UUIDs. | `cinder.csi.openstack.org/anti-affinity: "1b4e28ba-2fa1-11ec-8d3d-0242ac130004,1b4e28ba-2fa1-11ec-8d3d-0242ac130005"` |
294+
| `cinder.csi.openstack.org/affinity` | Volume affinity to existing volume or volumes names/UUIDs. The value should be a comma-separated list of volume names/UUIDs. | `cinder.csi.openstack.org/affinity: "1b4e28ba-2fa1-11ec-8d3d-0242ac130003"` |
295+
| `cinder.csi.openstack.org/anti-affinity` | Volume anti-affinity to existing volume or volumes names/UUIDs. The value should be a comma-separated list of volume names/UUIDs. | `cinder.csi.openstack.org/anti-affinity: "1b4e28ba-2fa1-11ec-8d3d-0242ac130004,pv-k8s--cluster-1b5f47bf-0119-442e-8529-254c36e43644"` |
296296

297297
If the PVC annotation is set, the volume will be created according to the
298-
existing volume UUIDs placements, i.e. on the same host as the
298+
existing volume names/UUIDs placements, i.e. on the same host as the
299299
`1b4e28ba-2fa1-11ec-8d3d-0242ac130003` volume and not on the same host as the
300300
`1b4e28ba-2fa1-11ec-8d3d-0242ac130004` and
301-
`1b4e28ba-2fa1-11ec-8d3d-0242ac130005` volumes.
301+
`pv-k8s--cluster-1b5f47bf-0119-442e-8529-254c36e43644` volumes.
302302

303303
## Local Development
304304

docs/manila-csi-plugin/using-manila-csi-plugin.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -286,20 +286,19 @@ following PVC annotations are supported:
286286

287287
| Annotation Name | Description | Example |
288288
|------------------------- |-----------------|----------|
289-
| `manila.csi.openstack.org/affinity` | Share affinity to existing share or shares UUIDs. The value should be a comma-separated list of share UUIDs. | `manila.csi.openstack.org/affinity: "1b4e28ba-2fa1-11ec-8d3d-0242ac130003"` |
290-
| `manila.csi.openstack.org/anti-affinity` | Share anti-affinity to existing share or shares UUIDs. The value should be a comma-separated list of share UUIDs. | `manila.csi.openstack.org/anti-affinity: "1b4e28ba-2fa1-11ec-8d3d-0242ac130004,1b4e28ba-2fa1-11ec-8d3d-0242ac130005"` |
289+
| `manila.csi.openstack.org/affinity` | Share affinity to existing share or shares names/UUIDs. The value should be a comma-separated list of share names or UUIDs. | `manila.csi.openstack.org/affinity: "1b4e28ba-2fa1-11ec-8d3d-0242ac130003"` |
290+
| `manila.csi.openstack.org/anti-affinity` | Share anti-affinity to existing share or shares names/UUIDs. The value should be a comma-separated list of share names/UUIDs. | `manila.csi.openstack.org/anti-affinity: "1b4e28ba-2fa1-11ec-8d3d-0242ac130004,pv-default-50c5a3b3-e0b5-48d6-a163-4e68956aeb54"` |
291291
| `manila.csi.openstack.org/group-id` | The UUID of the share group to which the provisioned share must belong. The share group must be created before the PVC is created. | `manila.csi.openstack.org/group-id: "1b4e28ba-2fa1-11ec-8d3d-0242ac130006"` |
292292

293293
If the PVC annotation is set, the share will be created according to the
294-
existing share UUIDs placements, i.e. on the same host as the
294+
existing share names/UUIDs placements, i.e. on the same host as the
295295
`1b4e28ba-2fa1-11ec-8d3d-0242ac130003` share and not on the same host as the
296296
`1b4e28ba-2fa1-11ec-8d3d-0242ac130004` and
297-
`1b4e28ba-2fa1-11ec-8d3d-0242ac130005` shares.
297+
`pv-default-50c5a3b3-e0b5-48d6-a163-4e68956aeb54` shares.
298298

299299
The `manila.csi.openstack.org/group-id` annotation value overrides the storage
300300
class `groupID` parameter if both are set.
301301

302302
## For developers
303303

304304
If you'd like to contribute to CSI Manila, check out `docs/manila-csi-plugin/developers-csi-manila.md` to get you started.
305-

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.22.0
55
require (
66
github.com/container-storage-interface/spec v1.9.0
77
github.com/go-chi/chi/v5 v5.0.8
8+
github.com/google/uuid v1.6.0
89
github.com/gophercloud/gophercloud/v2 v2.1.2-0.20241016132526-1c4dd03733fe
910
github.com/gophercloud/utils/v2 v2.0.0-20240701101423-2401526caee5
1011
github.com/hashicorp/go-version v1.6.0
@@ -91,7 +92,6 @@ require (
9192
github.com/google/go-cmp v0.6.0 // indirect
9293
github.com/google/gofuzz v1.2.0 // indirect
9394
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af // indirect
94-
github.com/google/uuid v1.6.0 // indirect
9595
github.com/gorilla/websocket v1.5.0 // indirect
9696
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
9797
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect

pkg/csi/cinder/controllerserver.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,16 +200,29 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
200200
// Set scheduler hints if affinity or anti-affinity is set in PVC annotations
201201
var schedulerHints volumes.SchedulerHintOptsBuilder
202202
var volCtx map[string]string
203-
affinity := util.SplitTrimJoin(pvcAnnotations[affinityKey], ',')
204-
antiAffinity := util.SplitTrimJoin(pvcAnnotations[antiAffinityKey], ',')
203+
affinity := pvcAnnotations[affinityKey]
204+
antiAffinity := pvcAnnotations[antiAffinityKey]
205205
if affinity != "" || antiAffinity != "" {
206+
klog.V(4).Infof("CreateVolume: Getting scheduler hints: affinity=%s, anti-affinity=%s", affinity, antiAffinity)
207+
208+
// resolve volume names to UUIDs
209+
affinity, err = cloud.ResolveVolumeListToUUIDs(affinity)
210+
if err != nil {
211+
return nil, status.Errorf(codes.InvalidArgument, "failed to resolve affinity volume UUIDs: %v", err)
212+
}
213+
antiAffinity, err = cloud.ResolveVolumeListToUUIDs(antiAffinity)
214+
if err != nil {
215+
return nil, status.Errorf(codes.InvalidArgument, "failed to resolve anti-affinity volume UUIDs: %v", err)
216+
}
217+
206218
volCtx = util.SetMapIfNotEmpty(volCtx, "affinity", affinity)
207219
volCtx = util.SetMapIfNotEmpty(volCtx, "anti-affinity", antiAffinity)
208220
schedulerHints = &volumes.SchedulerHintOpts{
209221
SameHost: util.SplitTrim(affinity, ','),
210222
DifferentHost: util.SplitTrim(antiAffinity, ','),
211223
}
212-
klog.V(4).Infof("CreateVolume: Setting scheduler hints: affinity=%s, anti-affinity=%s", affinity, antiAffinity)
224+
225+
klog.V(4).Infof("CreateVolume: Resolved scheduler hints: affinity=%s, anti-affinity=%s", affinity, antiAffinity)
213226
}
214227

215228
vol, err := cloud.CreateVolume(opts, schedulerHints)

pkg/csi/cinder/openstack/openstack.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ type IOpenStack interface {
5656
GetAttachmentDiskPath(instanceID, volumeID string) (string, error)
5757
GetVolume(volumeID string) (*volumes.Volume, error)
5858
GetVolumesByName(name string) ([]volumes.Volume, error)
59+
GetVolumeByName(name string) (*volumes.Volume, error)
5960
CreateSnapshot(name, volID string, tags map[string]string) (*snapshots.Snapshot, error)
6061
ListSnapshots(filters map[string]string) ([]snapshots.Snapshot, string, error)
6162
DeleteSnapshot(snapID string) error
@@ -72,6 +73,7 @@ type IOpenStack interface {
7273
GetMaxVolLimit() int64
7374
GetMetadataOpts() metadata.Opts
7475
GetBlockStorageOpts() BlockStorageOpts
76+
ResolveVolumeListToUUIDs(volumes string) (string, error)
7577
}
7678

7779
type OpenStack struct {

pkg/csi/cinder/openstack/openstack_mock.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@ limitations under the License.
1717
package openstack
1818

1919
import (
20+
"fmt"
21+
2022
"github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/backups"
2123
"github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/snapshots"
2224
"github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes"
2325
"github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers"
2426
"github.com/stretchr/testify/mock"
27+
"k8s.io/cloud-provider-openstack/pkg/util/errors"
2528
"k8s.io/cloud-provider-openstack/pkg/util/metadata"
2629
)
2730

@@ -234,6 +237,24 @@ func (_m *OpenStackMock) GetVolumesByName(name string) ([]volumes.Volume, error)
234237
return r0, r1
235238
}
236239

240+
// GetVolumeByName provides a mock function with given fields: name
241+
func (_m *OpenStackMock) GetVolumeByName(name string) (*volumes.Volume, error) {
242+
vols, err := _m.GetVolumesByName(name)
243+
if err != nil {
244+
return nil, err
245+
}
246+
247+
if len(vols) == 0 {
248+
return nil, errors.ErrNotFound
249+
}
250+
251+
if len(vols) > 1 {
252+
return nil, fmt.Errorf("found %d volumes with name %q", len(vols), name)
253+
}
254+
255+
return &vols[0], nil
256+
}
257+
237258
// ListSnapshots provides a mock function with given fields: limit, offset, filters
238259
func (_m *OpenStackMock) ListSnapshots(filters map[string]string) ([]snapshots.Snapshot, string, error) {
239260
ret := _m.Called(filters)
@@ -501,3 +522,8 @@ func (_m *OpenStackMock) GetMetadataOpts() metadata.Opts {
501522
func (_m *OpenStackMock) GetBlockStorageOpts() BlockStorageOpts {
502523
return BlockStorageOpts{}
503524
}
525+
526+
// ResolveVolumeListToUUIDs provides a mock function to return volume UUIDs
527+
func (_m *OpenStackMock) ResolveVolumeListToUUIDs(v string) (string, error) {
528+
return v, nil
529+
}

pkg/csi/cinder/openstack/openstack_volumes.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,18 @@ import (
2020
"context"
2121
"fmt"
2222
"net/url"
23+
"strings"
2324
"time"
2425

2526
"github.com/gophercloud/gophercloud/v2/openstack"
2627
"github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v3/volumes"
2728
"github.com/gophercloud/gophercloud/v2/openstack/compute/v2/volumeattach"
2829
"github.com/gophercloud/gophercloud/v2/pagination"
30+
"google.golang.org/grpc/codes"
31+
"google.golang.org/grpc/status"
2932
"k8s.io/apimachinery/pkg/util/wait"
3033
"k8s.io/cloud-provider-openstack/pkg/metrics"
34+
"k8s.io/cloud-provider-openstack/pkg/util"
3135
cpoerrors "k8s.io/cloud-provider-openstack/pkg/util/errors"
3236

3337
"k8s.io/klog/v2"
@@ -140,6 +144,25 @@ func (os *OpenStack) GetVolumesByName(n string) ([]volumes.Volume, error) {
140144
return vols, nil
141145
}
142146

147+
// GetVolumeByName is a wrapper around GetVolumesByName that returns a single Volume reference
148+
// with the specified name
149+
func (os *OpenStack) GetVolumeByName(n string) (*volumes.Volume, error) {
150+
vols, err := os.GetVolumesByName(n)
151+
if err != nil {
152+
return nil, err
153+
}
154+
155+
if len(vols) == 0 {
156+
return nil, cpoerrors.ErrNotFound
157+
}
158+
159+
if len(vols) > 1 {
160+
return nil, fmt.Errorf("found %d volumes with name %q", len(vols), n)
161+
}
162+
163+
return &vols[0], nil
164+
}
165+
143166
// DeleteVolume delete a volume
144167
func (os *OpenStack) DeleteVolume(volumeID string) error {
145168
used, err := os.diskIsUsed(volumeID)
@@ -414,3 +437,39 @@ func (os *OpenStack) diskIsUsed(volumeID string) (bool, error) {
414437
func (os *OpenStack) GetBlockStorageOpts() BlockStorageOpts {
415438
return os.bsOpts
416439
}
440+
441+
// ResolveVolumeListToUUIDs resolves a list of volume names or UUIDs to a
442+
// string of UUIDs
443+
func (os *OpenStack) ResolveVolumeListToUUIDs(affinityList string) (string, error) {
444+
list := util.SplitTrim(affinityList, ',')
445+
if len(list) == 0 {
446+
return "", nil
447+
}
448+
449+
affinityUUIDs := make([]string, 0, len(list))
450+
for _, v := range list {
451+
var volume *volumes.Volume
452+
var err error
453+
454+
if id, e := util.UUID(v); e == nil {
455+
// First try to get volume by ID
456+
volume, err = os.GetVolume(id)
457+
if err != nil && cpoerrors.IsNotFound(err) {
458+
volume, err = os.GetVolumeByName(v)
459+
}
460+
} else {
461+
// If not a UUID, try to get volume by name
462+
volume, err = os.GetVolumeByName(v)
463+
}
464+
if err != nil {
465+
if cpoerrors.IsNotFound(err) {
466+
return "", status.Errorf(codes.NotFound, "referenced volume %s not found: %v", v, err)
467+
}
468+
return "", status.Errorf(codes.Internal, "failed to resolve volume %s: %v", v, err)
469+
}
470+
471+
affinityUUIDs = append(affinityUUIDs, volume.ID)
472+
}
473+
474+
return strings.Join(affinityUUIDs, ","), nil
475+
}

pkg/csi/manila/controllerserver.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,22 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
148148
for k, v := range pvcAnnotations {
149149
klog.V(4).Infof("CreateVolume: retrieved %q pvc annotation: %s: %s", k, v, shareName)
150150
}
151-
shareOpts.Affinity = util.SplitTrimJoin(pvcAnnotations[affinityKey], ',')
152-
shareOpts.AntiAffinity = util.SplitTrimJoin(pvcAnnotations[antiAffinityKey], ',')
153-
if shareOpts.Affinity != "" || shareOpts.AntiAffinity != "" {
154-
klog.V(4).Infof("CreateVolume: Setting scheduler hints: affinity=%s, anti-affinity=%s", shareOpts.Affinity, shareOpts.AntiAffinity)
151+
affinity := pvcAnnotations[affinityKey]
152+
antiAffinity := pvcAnnotations[antiAffinityKey]
153+
if affinity != "" || antiAffinity != "" {
154+
klog.V(4).Infof("CreateVolume: Getting scheduler hints: affinity=%s, anti-affinity=%s", affinity, antiAffinity)
155+
156+
// resolve share names to UUIDs
157+
shareOpts.Affinity, err = resolveShareListToUUIDs(manilaClient, affinity)
158+
if err != nil {
159+
return nil, status.Errorf(codes.InvalidArgument, "failed to resolve affinity share UUIDs: %v", err)
160+
}
161+
shareOpts.AntiAffinity, err = resolveShareListToUUIDs(manilaClient, antiAffinity)
162+
if err != nil {
163+
return nil, status.Errorf(codes.InvalidArgument, "failed to resolve anti-affinity share UUIDs: %v", err)
164+
}
165+
166+
klog.V(4).Infof("CreateVolume: Resolved scheduler hints: affinity=%v, anti-affinity=%v", shareOpts.Affinity, shareOpts.AntiAffinity)
155167
}
156168

157169
// override the storage class group ID if it is set in the PVC annotation

pkg/csi/manila/share.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ package manila
1818

1919
import (
2020
"fmt"
21+
"strings"
2122
"time"
2223

2324
"github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/shares"
2425
"google.golang.org/grpc/codes"
2526
"google.golang.org/grpc/status"
2627
"k8s.io/apimachinery/pkg/util/wait"
2728
"k8s.io/cloud-provider-openstack/pkg/csi/manila/manilaclient"
29+
"k8s.io/cloud-provider-openstack/pkg/util"
2830
clouderrors "k8s.io/cloud-provider-openstack/pkg/util/errors"
2931
"k8s.io/klog/v2"
3032
)
@@ -197,3 +199,38 @@ func waitForShareStatus(manilaClient manilaclient.Interface, shareID string, val
197199
return false, fmt.Errorf("share %s is in an unexpected state: wanted either %v or %s, got %s", shareID, validTransientStates, desiredStatus, share.Status)
198200
})
199201
}
202+
203+
func resolveShareListToUUIDs(manilaClient manilaclient.Interface, affinityList string) (string, error) {
204+
list := util.SplitTrim(affinityList, ',')
205+
if len(list) == 0 {
206+
return "", nil
207+
}
208+
209+
affinityUUIDs := make([]string, 0, len(list))
210+
for _, v := range list {
211+
var share *shares.Share
212+
var err error
213+
214+
if id, e := util.UUID(v); e == nil {
215+
// First try to get share by ID
216+
share, err = manilaClient.GetShareByID(id)
217+
if err != nil && clouderrors.IsNotFound(err) {
218+
// If not found by ID, try to get share by ID as name
219+
share, err = manilaClient.GetShareByName(v)
220+
}
221+
} else {
222+
// If not a UUID, try to get share by name
223+
share, err = manilaClient.GetShareByName(v)
224+
}
225+
if err != nil {
226+
if clouderrors.IsNotFound(err) {
227+
return "", status.Errorf(codes.NotFound, "referenced share %s not found: %v", v, err)
228+
}
229+
return "", status.Errorf(codes.Internal, "failed to resolve share %s: %v", v, err)
230+
}
231+
232+
affinityUUIDs = append(affinityUUIDs, share.ID)
233+
}
234+
235+
return strings.Join(affinityUUIDs, ","), nil
236+
}

0 commit comments

Comments
 (0)