@@ -23,6 +23,7 @@ import (
2323 "fmt"
2424 "maps"
2525 "net"
26+ "slices"
2627 "strconv"
2728
2829 "github.com/container-storage-interface/spec/lib/go/csi"
@@ -40,6 +41,15 @@ import (
4041 "github.com/ceph/ceph-csi/internal/util/log"
4142)
4243
44+ // nvmeofMutableParams lists all NVMe-oF specific mutable parameters.
45+ var nvmeofMutableParams = []string {
46+ nvmeof .RwIosPerSecond ,
47+ nvmeof .RwMbytesPerSecond ,
48+ nvmeof .RMbytesPerSecond ,
49+ nvmeof .WMbytesPerSecond ,
50+ nvmeof .AllowHostNQNs ,
51+ }
52+
4353type Server struct {
4454 csi.UnimplementedControllerServer
4555
@@ -147,9 +157,22 @@ func (cs *Server) CreateVolume(
147157 defer cs .volumeLocks .Release (sourceVolumeID )
148158 }
149159
150- // Step 1: Create RBD volume through backend. if exists, it is ok.
160+ // Create a modified request without any mutable parameters for RBD
161+ // NVMe-oF handles its own mutable parameters separately
162+ rbdReq := & csi.CreateVolumeRequest {
163+ Name : req .GetName (),
164+ CapacityRange : req .GetCapacityRange (),
165+ VolumeCapabilities : req .GetVolumeCapabilities (),
166+ Parameters : req .GetParameters (),
167+ Secrets : req .GetSecrets (),
168+ VolumeContentSource : req .GetVolumeContentSource (),
169+ AccessibilityRequirements : req .GetAccessibilityRequirements (),
170+ // MutableParameters intentionally not set - NVMe-oF manages these separately
171+ }
172+
173+ // Step 2: Create RBD volume through backend. if exists, it is ok.
151174 // RBD backend automatically handles cloning when VolumeContentSource is present.
152- res , err := cs .backendServer .CreateVolume (ctx , req )
175+ res , err := cs .backendServer .CreateVolume (ctx , rbdReq )
153176 if err != nil {
154177 log .ErrorLog (ctx , "failed to create RBD volume: %v" , err )
155178
@@ -182,7 +205,7 @@ func (cs *Server) CreateVolume(
182205 // can be empty. if it was defined in config-map the rbd csi driver would have set it already
183206 rbdRadosNameSpace := res .GetVolume ().GetVolumeContext ()["radosNamespace" ]
184207
185- // Step 2 : Setup NVMe-oF resources
208+ // Step 4 : Setup NVMe-oF resources
186209 var nvmeofData * nvmeof.NVMeoFVolumeData
187210 // Defer: Cleanup NVMe-oF on any error (BEFORE the call!)
188211 defer func () {
@@ -203,13 +226,13 @@ func (cs *Server) CreateVolume(
203226
204227 return nil , status .Errorf (codes .Internal , "NVMe-oF setup failed: %v" , err )
205228 }
206- // step 3 : Populate volume context for NodeServer
229+ // Step 5 : Populate volume context for NodeServer
207230 err = populateVolumeContext (backend , nvmeofData )
208231 if err != nil {
209232 return nil , status .Errorf (codes .Internal , "failed to populate volume context: %v" , err )
210233 }
211234
212- // Step 4 : Store NVMe-oF metadata in the volume context
235+ // Step 6 : Store NVMe-oF metadata in the volume context
213236 err = cs .storeNVMeoFMetadata (ctx , req , volumeID , nvmeofData )
214237 if err != nil {
215238 return nil , err // Error already formatted with proper status code
@@ -370,13 +393,15 @@ func (cs *Server) ControllerModifyVolume(
370393 }
371394 defer cs .volumeLocks .Release (volumeID )
372395
373- // Step 2: Parse QoS parameters from mutable_parameters
374- hasRBDQoS := rbd . HasQoSParams ( params )
375- if hasRBDQoS {
376- log . ErrorLog ( ctx , "Cannot set RBD QoS parameters on NVMe-oF volumes" )
377-
378- return nil , status . Error ( codes . InvalidArgument , "cannot set RBD QoS parameters on NVMe-oF volumes" )
396+ // Step 2: Validate that only known parameters are provided
397+ for param := range params {
398+ if ! slices . Contains ( nvmeofMutableParams , param ) {
399+ return nil , status . Errorf ( codes . InvalidArgument ,
400+ "unknown mutable parameter: %s" , param )
401+ }
379402 }
403+
404+ // Step 3: Parse QoS parameters from mutable_parameters
380405 nvmeofQoS , err := nvmeof .NewNVMeoFQosVolumeFromParams (params )
381406 if err != nil {
382407 log .ErrorLog (ctx , "failed to parse NVMe-oF QoS parameters: %v" , err )
@@ -488,15 +513,19 @@ func validateCreateVolumeRequest(req *csi.CreateVolumeRequest) error {
488513 if countOfListeners > 0 && networkMask != "" {
489514 return errors .New ("must specify either 'listeners' xor 'networkMask', but got both" )
490515 }
491- // Validate QoS parameters - cannot mix RBD and NVMe-oF QoS
492- mutableParams := req .GetMutableParameters ()
493516
494- // check for RBD QoS parameters in both params and mutableParams
495- if hasRBDQoS := rbd .HasQoSParams (params ); hasRBDQoS {
517+ // Validate QoS parameters - cannot mix RBD and NVMe-oF QoS
518+ // Check for RBD QoS parameters in regular params (not mutableParams - already validated above)
519+ if rbd .HasQoSParams (params ) {
496520 return errors .New ("setting RBD QoS parameters on NVMe-oF volumes is not supported" )
497521 }
498- if hasRBDQoS := rbd .HasQoSParams (mutableParams ); hasRBDQoS {
499- return errors .New ("setting RBD QoS parameters on NVMe-oF volumes is not supported" )
522+
523+ // Validate that only known mutable parameters are provided
524+ mutableParams := req .GetMutableParameters ()
525+ for param := range mutableParams {
526+ if ! slices .Contains (nvmeofMutableParams , param ) {
527+ return fmt .Errorf ("unknown mutable parameter: %s" , param )
528+ }
500529 }
501530
502531 // It take the mutableParams value from the volumeAttributesClassName in the PersistentVolumeClaim yaml.
0 commit comments