Skip to content

Commit 1b1e1b0

Browse files
authored
Regional file support (#132)
1 parent ad403e8 commit 1b1e1b0

File tree

7 files changed

+451
-39
lines changed

7 files changed

+451
-39
lines changed

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ module github.com/IBM/ibm-vpc-file-csi-driver
33
go 1.23.10
44

55
require (
6-
github.com/IBM/ibm-csi-common v1.1.21
7-
github.com/IBM/ibmcloud-volume-file-vpc v1.2.10
8-
github.com/IBM/ibmcloud-volume-interface v1.2.13
6+
github.com/IBM/ibm-csi-common v1.1.22
7+
github.com/IBM/ibmcloud-volume-file-vpc v1.2.13
8+
github.com/IBM/ibmcloud-volume-interface v1.2.15
99
github.com/IBM/secret-utils-lib v1.1.14
1010
github.com/container-storage-interface/spec v1.11.0
1111
github.com/golang/glog v1.2.4

go.sum

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ github.com/IBM-Cloud/ibm-cloud-cli-sdk v0.6.7 h1:eHgfQl6IeSmzWUyiSi13CvoFYsovoyq
77
github.com/IBM-Cloud/ibm-cloud-cli-sdk v0.6.7/go.mod h1:RiUvKuHKTBmBApDMUQzBL14pQUGKcx/IioKQPIcRQjs=
88
github.com/IBM/go-sdk-core/v5 v5.17.4 h1:VGb9+mRrnS2HpHZFM5hy4J6ppIWnwNrw0G+tLSgcJLc=
99
github.com/IBM/go-sdk-core/v5 v5.17.4/go.mod h1:KsAAI7eStAWwQa4F96MLy+whYSh39JzNjklZRbN/8ns=
10-
github.com/IBM/ibm-csi-common v1.1.21 h1:/OmjpF+YgmkLmtehFmmfWp9kHGP9iMNkTU/KQDYXzrY=
11-
github.com/IBM/ibm-csi-common v1.1.21/go.mod h1:AgjHhkYgDwYz1OmFGTgtOv+GCMsD67Yel+ZKsY90PYY=
12-
github.com/IBM/ibmcloud-volume-file-vpc v1.2.10 h1:kzhBIAYd4j37jdWBjADANM+hBuun4QKdwPS90xYx33E=
13-
github.com/IBM/ibmcloud-volume-file-vpc v1.2.10/go.mod h1:IQx9BDp+AE/IWAvjOS0i7oNz3oDkOg/pPkVx/bZqWSs=
14-
github.com/IBM/ibmcloud-volume-interface v1.2.13 h1:7/uANoxNafhB0npJMUJfcFWgiUiovlcYudsk3mucY1g=
15-
github.com/IBM/ibmcloud-volume-interface v1.2.13/go.mod h1:F9QxX6NZ4LLPGQ6YWVnYpW0JV+H+pZp8RY10PAZ76JA=
10+
github.com/IBM/ibm-csi-common v1.1.22 h1:SCDOKErw9CruPeq7Auj4RMq9P1qMzF6ePrt7X0/pwgI=
11+
github.com/IBM/ibm-csi-common v1.1.22/go.mod h1:AgjHhkYgDwYz1OmFGTgtOv+GCMsD67Yel+ZKsY90PYY=
12+
github.com/IBM/ibmcloud-volume-file-vpc v1.2.13 h1:C4wQGoEZsE+VXsSenK49azohmEZoZVIgtTGyiRxOuY0=
13+
github.com/IBM/ibmcloud-volume-file-vpc v1.2.13/go.mod h1:pjmMcdnlfRu4TmS6Enw7GooBmPWtMcfkQGuNaAdhZmU=
14+
github.com/IBM/ibmcloud-volume-interface v1.2.15 h1:x2hoo0shR7ZPdnHoA2nJXbvzTNmAHYUhA/SgOIX9D+0=
15+
github.com/IBM/ibmcloud-volume-interface v1.2.15/go.mod h1:F9QxX6NZ4LLPGQ6YWVnYpW0JV+H+pZp8RY10PAZ76JA=
1616
github.com/IBM/secret-common-lib v1.1.13 h1:94YIuXew6lBXf0cfhyfM1iNUbUsV34abc4+BGvnXCTQ=
1717
github.com/IBM/secret-common-lib v1.1.13/go.mod h1:33mQHLZyAq7HmS9SYDQXpf0OhBhe4TRHDaRBktDxnm0=
1818
github.com/IBM/secret-utils-lib v1.1.14 h1:Gv5Ca2hZTQMr9+PkOq7AE2lUUnNEeQJ0uiKaxKT1Sdk=

pkg/ibmcsidriver/constants.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
// Package ibmcsidriver ...
2020
package ibmcsidriver
2121

22+
import "github.com/IBM/ibm-csi-common/pkg/utils"
23+
2224
const (
2325
// Profile ...
2426
Profile = "profile"
@@ -32,6 +34,9 @@ const (
3234
// IOPS per PVC
3335
IOPS = "iops"
3436

37+
// Throughput ...
38+
Throughput = "throughput"
39+
3540
// SizeRangeSupported ...
3641
SizeRangeSupported = "sizeRange"
3742

@@ -59,6 +64,9 @@ const (
5964
// DP2Profile ...
6065
DP2Profile = "dp2"
6166

67+
// RFSProfile ...
68+
RFSProfile = "rfs"
69+
6270
// ClassVersion ...
6371
ClassVersion = "classVersion"
6472

@@ -104,6 +112,9 @@ const (
104112
//ENISubnetID ...
105113
ENISubnetID = "ENISubnetId"
106114

115+
//ProfileLabel
116+
ProfileLabel = "profile"
117+
107118
//ENISecurityGroupIds ...
108119
ENISecurityGroupIDs = "ENISecurityGroupIds"
109120

@@ -116,6 +127,9 @@ const (
116127
// VolumeCRNLabel ...
117128
VolumeCRNLabel = "volumeCRN"
118129

130+
//VolumeHrefLabel ...
131+
VolumeHrefLabel = "volumeHref"
132+
119133
// ClusterIDLabel ...
120134
ClusterIDLabel = "clusterID"
121135

@@ -128,6 +142,9 @@ const (
128142
// IOPSLabel ...
129143
IOPSLabel = "iops"
130144

145+
// ThroughputLabel ...
146+
ThroughputLabel = "throughput"
147+
131148
// ZoneLabel ...
132149
ZoneLabel = "zone"
133150

@@ -175,10 +192,13 @@ const (
175192

176193
// ConfigmapDataKey
177194
ConfigmapDataKey = "vpc_subnet_ids"
195+
196+
// MinimumRFSVolumeSizeInBytes ... This is minimum size require for rfs profile
197+
MinimumRFSVolumeSizeInBytes int64 = 1 * utils.GiB
178198
)
179199

180200
// SupportedFS the supported FS types
181201
var SupportedFS = []string{"nfs"}
182202

183203
// SupportedProfile the supported profile names
184-
var SupportedProfile = []string{"dp2"}
204+
var SupportedProfile = []string{"dp2", "rfs"}

pkg/ibmcsidriver/controller.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ func (csiCS *CSIControllerServer) CreateVolume(ctx context.Context, req *csi.Cre
110110
return nil, commonError.GetCSIError(ctxLogger, commonError.InvalidParameters, requestID, err)
111111
}
112112

113+
// Check if RFS Profile is accessible
114+
if requestedVolume.Profile != nil && requestedVolume.Profile.Name == RFSProfile && !csiCS.Driver.rfsEnabled {
115+
return nil, commonError.GetCSIError(ctxLogger, commonError.ProfileNotAllowlisted, requestID, nil, RFSProfile)
116+
}
117+
113118
// TODO: Determine Zones and Region for the disk
114119

115120
// Validate if volume Already Exists

pkg/ibmcsidriver/controller_helper.go

Lines changed: 78 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,16 @@ var dp2CapacityIopsRanges = []classRange{
6565
}
6666

6767
// normalize the requested capacity(in GiB) to what is supported by the driver
68-
func getRequestedCapacity(capRange *csi.CapacityRange) (int64, error) {
68+
func getRequestedCapacity(capRange *csi.CapacityRange, profileName string) (int64, error) {
6969
// Input is in bytes from csi
7070
var capBytes int64
7171
// Default case where nothing is set
7272
if capRange == nil {
73-
capBytes = utils.MinimumVolumeSizeInBytes
73+
if profileName == RFSProfile { // RFS profile minimum size is 1GB
74+
capBytes = MinimumRFSVolumeSizeInBytes
75+
} else {
76+
capBytes = utils.MinimumVolumeSizeInBytes // dp2 minimum size is 10 GB
77+
}
7478
// returns in GiB
7579
return capBytes, nil
7680
}
@@ -97,7 +101,8 @@ func getRequestedCapacity(capRange *csi.CapacityRange) (int64, error) {
97101

98102
// Limit is more than Required, but larger than Minimum. So we just set capcity to Minimum
99103
// Too small, default
100-
if capBytes < utils.MinimumVolumeSizeInBytes {
104+
// If profile is RFS profile then no need to check for minimum size as the RoundUpBytes will giving minimum value as 1 GiB
105+
if capBytes < utils.MinimumVolumeSizeInBytes && profileName != RFSProfile {
101106
capBytes = utils.MinimumVolumeSizeInBytes
102107
}
103108

@@ -227,6 +232,16 @@ func getVolumeParameters(logger *zap.Logger, req *csi.CreateVolumeRequest, confi
227232
iops := value
228233
volume.Iops = &iops
229234
}
235+
case Throughput:
236+
// getting throughput value from storage class if it is provided
237+
if len(value) != 0 {
238+
bandwidth, errParse := strconv.ParseInt(value, 10, 32)
239+
if errParse != nil {
240+
err = fmt.Errorf("'<%v>' is invalid, value of '%s' should be an int32 type", value, key)
241+
} else {
242+
volume.VPCVolume.Bandwidth = int32(bandwidth)
243+
}
244+
}
230245
case UID:
231246
uid, err = strconv.Atoi(value)
232247
if err != nil {
@@ -267,9 +282,15 @@ func getVolumeParameters(logger *zap.Logger, req *csi.CreateVolumeRequest, confi
267282
}
268283
}
269284

285+
if volume.VPCVolume.Profile == nil {
286+
err = fmt.Errorf("Volume profile is empty. Supported profiles are: %v", SupportedProfile)
287+
logger.Error("getVolumeParameters", zap.NamedError("InvalidRequest", err))
288+
return volume, err
289+
}
290+
270291
// Get the requested capacity from the request
271292
capacityRange := req.GetCapacityRange()
272-
capBytes, err := getRequestedCapacity(capacityRange)
293+
capBytes, err := getRequestedCapacity(capacityRange, volume.VPCVolume.Profile.Name)
273294
if err != nil {
274295
err = fmt.Errorf("invalid PVC capacity size: '%v'", err)
275296
logger.Error("getVolumeParameters", zap.NamedError("invalid parameter", err))
@@ -297,16 +318,11 @@ func getVolumeParameters(logger *zap.Logger, req *csi.CreateVolumeRequest, confi
297318
return volume, err
298319
}
299320

300-
if volume.VPCVolume.Profile != nil && volume.VPCVolume.Profile.Name != DP2Profile {
301-
// Specify IOPS only for custom class or DP2 class
302-
volume.Iops = nil
303-
}
304-
305321
//If ENI/VNI enabled then check for scenarios where zone and subnetId is mandatory
306322
if volume.VPCVolume.AccessControlMode == SecurityGroup {
307323

308-
//Zone and Region is mandatory if subnetID or primaryIPID/primaryIPAddress is user defined
309-
if (len(strings.TrimSpace(volume.Az)) == 0 || len(strings.TrimSpace(volume.Region)) == 0) && (len(volume.VPCVolume.SubnetID) != 0 || (volume.VPCVolume.PrimaryIP != nil)) {
324+
//Zone or Region is mandatory if subnetID or primaryIPID/primaryIPAddress is user defined for DP2 profile
325+
if volume.VPCVolume.Profile.Name == DP2Profile && (len(strings.TrimSpace(volume.Az)) == 0 || len(strings.TrimSpace(volume.Region)) == 0) && (len(volume.VPCVolume.SubnetID) != 0 || (volume.VPCVolume.PrimaryIP != nil)) {
310326
err = fmt.Errorf("zone and region is mandatory if subnetID or PrimaryIPID or PrimaryIPAddress is provided")
311327
logger.Error("getVolumeParameters", zap.NamedError("InvalidParameter", err))
312328
return volume, err
@@ -329,8 +345,30 @@ func getVolumeParameters(logger *zap.Logger, req *csi.CreateVolumeRequest, confi
329345

330346
//TODO port the code from VPC BLOCK to find region if zone is given
331347

332-
//If the zone is not provided in storage class parameters then we pick from the Topology
333-
if len(strings.TrimSpace(volume.Az)) == 0 {
348+
// validate bandwidth for dp2 profile
349+
if volume.VPCVolume.Profile.Name == DP2Profile && volume.VPCVolume.Bandwidth > 0 {
350+
err = fmt.Errorf("bandwidth is not supported for dp2 profile; please remove the property from storage class")
351+
logger.Error("getVolumeParameters", zap.NamedError("invalidParameter", err))
352+
return volume, err
353+
}
354+
355+
// validation zone and iops for 'rfs' profile
356+
if volume.VPCVolume.Profile.Name == RFSProfile {
357+
if volume.Iops != nil && len(strings.TrimSpace(*volume.Iops)) > 0 {
358+
err = fmt.Errorf("iops is not supported for rfs profile; please remove the iops parameter from the storage class")
359+
logger.Error("getVolumeParameters", zap.NamedError("invalidParameter", err))
360+
return volume, err
361+
}
362+
if len(strings.TrimSpace(volume.Az)) > 0 {
363+
err = fmt.Errorf("zone is not supported for rfs profile; please remove the zone parameter from the storage class")
364+
logger.Error("getVolumeParameters", zap.NamedError("invalidParameter", err))
365+
return volume, err
366+
}
367+
}
368+
369+
// If the zone is not provided in storage class parameters then we pick from the Topology
370+
// We need to do this only for dp2 profile and skip for rfs profile
371+
if len(strings.TrimSpace(volume.Az)) == 0 && volume.VPCVolume.Profile.Name == DP2Profile {
334372
zones, err := pickTargetTopologyParams(req.GetAccessibilityRequirements())
335373
if err != nil {
336374
err = fmt.Errorf("unable to fetch zone information: '%v'", err)
@@ -491,19 +529,24 @@ func overrideParams(logger *zap.Logger, req *csi.CreateVolumeRequest, config *co
491529
volume.Region = value
492530
}
493531
case IOPS:
494-
// Override IOPS only for custom or dp2
495-
if volume.Capacity != nil && volume.VPCVolume.Profile != nil && volume.VPCVolume.Profile.Name == DP2Profile {
496-
var iops int
497-
var check bool
498-
iops, err = strconv.Atoi(value)
532+
if len(value) != 0 {
533+
_, err = strconv.Atoi(value)
499534
if err != nil {
500535
err = fmt.Errorf("%v:<%v> invalid value", key, value)
501536
} else {
502-
if check, err = isValidCapacityIOPS(*(volume.Capacity), iops, volume.VPCVolume.Profile.Name); check {
503-
iopsStr := value
504-
logger.Info("override", zap.Any(IOPS, value))
505-
volume.Iops = &iopsStr
506-
}
537+
iopsStr := value
538+
logger.Info("override", zap.Any(IOPS, value))
539+
volume.Iops = &iopsStr
540+
}
541+
}
542+
case Throughput:
543+
// getting throughput value from storage class if it is provided
544+
if len(value) != 0 {
545+
bandwidth, errParse := strconv.ParseInt(value, 10, 32)
546+
if errParse != nil {
547+
err = fmt.Errorf("'<%v>' is invalid, value of '%s' should be an int32 type", value, key)
548+
} else {
549+
volume.Bandwidth = int32(bandwidth)
507550
}
508551
}
509552
case SecurityGroupIDs:
@@ -587,15 +630,25 @@ func createCSIVolumeResponse(vol provider.Volume, volAccessPointResponse provide
587630

588631
// Update labels for PV objects
589632
labels[VolumeIDLabel] = vol.VolumeID + VolumeIDSeperator + volAccessPointResponse.AccessPointID
590-
labels[VolumeCRNLabel] = vol.CRN
591633
labels[ClusterIDLabel] = clusterID
634+
635+
if vol.VPCVolume.Profile != nil && vol.VPCVolume.Profile.Name != "" {
636+
labels[ProfileLabel] = vol.VPCVolume.Profile.Name
637+
}
638+
639+
if vol.VPCVolume.Href != "" {
640+
labels[VolumeHrefLabel] = vol.VPCVolume.Href
641+
}
642+
592643
labels[VolumeCRNLabel] = vol.CRN
593644
labels[ClusterIDLabel] = clusterID
594645
labels[Tag] = strings.Join(vol.Tags, ",")
595646
if vol.Iops != nil && len(*vol.Iops) > 0 {
596647
labels[IOPSLabel] = *vol.Iops
597648
}
598-
649+
if vol.VPCVolume.Bandwidth > 0 {
650+
labels[ThroughputLabel] = strconv.Itoa(int(vol.VPCVolume.Bandwidth)) + " " + "mbps"
651+
}
599652
labels[FileShareIDLabel] = vol.VolumeID
600653
labels[FileShareTargetIDLabel] = volAccessPointResponse.AccessPointID
601654

0 commit comments

Comments
 (0)