Skip to content

Commit 78e7312

Browse files
committed
Add support for volume/share name resolve
1 parent a3ce768 commit 78e7312

File tree

12 files changed

+205
-31
lines changed

12 files changed

+205
-31
lines changed

docs/cinder-csi-plugin/examples.md

Lines changed: 10 additions & 10 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
497-
volumes in the OpenStack cloud. The scheduler will try to place the volume in
498-
the same block storage server as the volume with UUID
499-
`1b4e28ba-2fa1-11ec-8d3d-0242ac130003` and avoid placing the volume in the same
500-
block storage server as the volumes with UUIDs
501-
`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.
496+
`pv-k8s--cluster-1b5f47bf-0119-442e-8529-254c36e43644` are names or UUIDs of
497+
the already provisioned volumes in the OpenStack cloud. The scheduler will try
498+
to place the volume in the same block storage server as the volume with the
499+
`1b4e28ba-2fa1-11ec-8d3d-0242ac130003` UUID and avoid placing the volume in the
500+
same block storage server as the volumes with the
501+
`1b4e28ba-2fa1-11ec-8d3d-0242ac130004` UUID and the
502+
`pv-k8s--cluster-1b5f47bf-0119-442e-8529-254c36e43644` volume name. If the
503+
scheduler hints are not satisfied, the volume will not be provisioned with an
504+
error message in 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)