@@ -174,7 +174,7 @@ func (d OntapAPIREST) ValidateAPIVersion(ctx context.Context) error {
174174 return nil
175175}
176176
177- func (d OntapAPIREST ) VolumeCreate (ctx context.Context , volume Volume ) error {
177+ func (d OntapAPIREST ) VolumeCreate (ctx context.Context , volume Volume ) ( string , error ) {
178178 fields := LogFields {
179179 "Method" : "VolumeCreate" ,
180180 "Type" : "OntapAPIREST" ,
@@ -190,17 +190,17 @@ func (d OntapAPIREST) VolumeCreate(ctx context.Context, volume Volume) error {
190190 aggregateName = volume .Aggregates [0 ]
191191 }
192192
193- creationErr := d .api .VolumeCreate (ctx , volume .Name , aggregateName , volume .Size , volume .SpaceReserve ,
193+ volumeUUID , creationErr := d .api .VolumeCreate (ctx , volume .Name , aggregateName , volume .Size , volume .SpaceReserve ,
194194 volume .SnapshotPolicy , volume .UnixPermissions , volume .ExportPolicy , volume .SecurityStyle ,
195195 volume .TieringPolicy , volume .Comment , volume .Qos , volume .Encrypt , volume .SnapshotReserve , volume .DPVolume )
196196 if creationErr != nil {
197- return fmt .Errorf ("error creating volume: %v" , creationErr )
197+ return "" , fmt .Errorf ("error creating volume: %v" , creationErr )
198198 }
199199
200- return nil
200+ return volumeUUID , nil
201201}
202202
203- func (d OntapAPIREST ) VolumeCreateBalanced (ctx context.Context , volume Volume ) error {
203+ func (d OntapAPIREST ) VolumeCreateBalanced (ctx context.Context , volume Volume ) ( string , error ) {
204204 fields := LogFields {
205205 "Method" : "VolumeCreateBalanced" ,
206206 "Type" : "OntapAPIREST" ,
@@ -213,17 +213,17 @@ func (d OntapAPIREST) VolumeCreateBalanced(ctx context.Context, volume Volume) e
213213
214214 // Double-check this API is supported
215215 if ! d .SupportsFeature (ctx , BalancedPlacement ) {
216- return errors .UnsupportedError ("ONTAP version does not support balanced placement" )
216+ return "" , errors .UnsupportedError ("ONTAP version does not support balanced placement" )
217217 }
218218
219- creationErr := d .api .VolumeCreateBalanced (ctx , volume .Name , volume .Size , volume .SpaceReserve ,
219+ volumeUUID , creationErr := d .api .VolumeCreateBalanced (ctx , volume .Name , volume .Size , volume .SpaceReserve ,
220220 volume .SnapshotPolicy , volume .UnixPermissions , volume .ExportPolicy , volume .SecurityStyle ,
221221 volume .TieringPolicy , volume .Comment , volume .Qos , volume .Encrypt , volume .SnapshotReserve , volume .DPVolume )
222222 if creationErr != nil {
223- return fmt .Errorf ("error creating volume: %v" , creationErr )
223+ return "" , fmt .Errorf ("error creating volume: %v" , creationErr )
224224 }
225225
226- return nil
226+ return volumeUUID , nil
227227}
228228
229229func (d OntapAPIREST ) VolumeModify (ctx context.Context , volume Volume ) error {
@@ -233,14 +233,61 @@ func (d OntapAPIREST) VolumeModify(ctx context.Context, volume Volume) error {
233233func (d OntapAPIREST ) VolumeDestroy (ctx context.Context , name string , force , skipRecoveryQueue bool ) error {
234234 destroyVolume := func () error {
235235 deletionErr := d .api .VolumeDestroy (ctx , name , skipRecoveryQueue )
236- if deletionErr != nil && ! IsVolumeBusyRESTError (deletionErr ) {
237- return backoff .Permanent (fmt .Errorf ("error destroying volume %v: %v" , name , deletionErr ))
238- }
239- if deletionErr != nil {
240- return fmt .Errorf ("error destroying volume %v: %v" , name , deletionErr )
236+ return classifyVolumeDeleteError (name , deletionErr )
237+ }
238+ return d .retryVolumeDestroy (ctx , name , destroyVolume )
239+ }
240+
241+ // VolumeDestroyByUUID deletes the volume identified by volumeUUID. Addressing the volume by UUID
242+ // avoids the lag in ONTAP's name index, which can report a just-created volume as missing. A UUID
243+ // that no longer resolves yields a NotFoundError, meaning the delete already completed.
244+ func (d OntapAPIREST ) VolumeDestroyByUUID (
245+ ctx context.Context , volumeUUID , volumeName string , force , skipRecoveryQueue bool ,
246+ ) error {
247+ fields := LogFields {
248+ "Method" : "VolumeDestroyByUUID" ,
249+ "Type" : "OntapAPIREST" ,
250+ "volume" : volumeName ,
251+ "uuid" : volumeUUID ,
252+ }
253+ Logd (ctx , d .driverName ,
254+ d .api .ClientConfig ().DebugTraceFlags ["method" ]).WithFields (fields ).Trace (">>>> VolumeDestroyByUUID" )
255+ defer Logd (ctx , d .driverName ,
256+ d .api .ClientConfig ().DebugTraceFlags ["method" ]).WithFields (fields ).Trace ("<<<< VolumeDestroyByUUID" )
257+
258+ if volumeUUID == "" {
259+ return errors .UnsupportedError ("no volume UUID provided; delete by name" )
260+ }
261+
262+ destroyVolume := func () error {
263+ deletionErr := d .api .VolumeDestroyByUUID (ctx , volumeUUID , skipRecoveryQueue )
264+ if errors .IsNotFoundError (deletionErr ) {
265+ return backoff .Permanent (errors .NotFoundError ("volume %s (UUID %s) does not exist" , volumeName , volumeUUID ))
241266 }
267+ return classifyVolumeDeleteError (volumeName , deletionErr )
268+ }
269+ return d .retryVolumeDestroy (ctx , volumeName , destroyVolume )
270+ }
271+
272+ // classifyVolumeDeleteError wraps a raw delete error so that a busy volume is retried while any
273+ // other failure aborts the retry loop immediately. A NotFoundError passes through with its type
274+ // intact, since callers rely on it to tell an already-deleted volume from a real failure.
275+ func classifyVolumeDeleteError (name string , deletionErr error ) error {
276+ if deletionErr == nil {
242277 return nil
243278 }
279+ if errors .IsNotFoundError (deletionErr ) {
280+ return backoff .Permanent (deletionErr )
281+ }
282+ if ! IsVolumeBusyRESTError (deletionErr ) {
283+ return backoff .Permanent (fmt .Errorf ("error destroying volume %v: %v" , name , deletionErr ))
284+ }
285+ return fmt .Errorf ("error destroying volume %v: %v" , name , deletionErr )
286+ }
287+
288+ // retryVolumeDestroy runs a delete operation with the shared busy-volume backoff, unwrapping the
289+ // permanent error (which may be a NotFoundError the caller treats as success) when the loop stops.
290+ func (d OntapAPIREST ) retryVolumeDestroy (ctx context.Context , name string , destroyVolume func () error ) error {
244291 destroyNotify := func (err error , duration time.Duration ) {
245292 Logc (ctx ).WithField ("increment" , duration ).Debug ("Volume busy, waiting." )
246293 }
0 commit comments