Skip to content

Commit 2210058

Browse files
Add check for matching attributes on existing volume in create flow.
Co-authored-by: Clinton Knight <cknight@netapp.com>
1 parent 6ab538f commit 2210058

File tree

12 files changed

+179
-67
lines changed

12 files changed

+179
-67
lines changed

mocks/mock_storage_drivers/mock_ontap/mock_api.go

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

storage_drivers/ontap/api/abstraction.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ type OntapAPI interface {
9292
LunDestroy(ctx context.Context, lunPath string) error
9393
LunGetFSType(ctx context.Context, lunPath string) (string, error)
9494
LunGetAttribute(ctx context.Context, lunPath, attributeName string) (string, error)
95-
LunSetAttribute(ctx context.Context, lunPath, attribute, fstype, context, luks, formatOptions string) error
95+
LunSetAttribute(ctx context.Context, lunPath, attribute, fstype, context, luks, formatOptions, poolName string) error
9696
LunSetComment(ctx context.Context, lunPath, comment string) error
9797
LunSetQosPolicyGroup(ctx context.Context, lunPath string, qosPolicyGroup QosPolicyGroup) error
9898
LunGetByName(ctx context.Context, name string) (*Lun, error)

storage_drivers/ontap/api/abstraction_rest.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2224,7 +2224,7 @@ func (d OntapAPIREST) LunDestroy(ctx context.Context, lunPath string) error {
22242224
}
22252225

22262226
func (d OntapAPIREST) LunSetAttribute(
2227-
ctx context.Context, lunPath, attribute, fstype, context, luks, formatOptions string,
2227+
ctx context.Context, lunPath, attribute, fstype, context, luks, formatOptions, poolName string,
22282228
) error {
22292229
if strings.Contains(lunPath, failureLUNSetAttr) {
22302230
return errors.New("injected error")
@@ -2256,6 +2256,12 @@ func (d OntapAPIREST) LunSetAttribute(
22562256
}
22572257
}
22582258

2259+
// Save the pool name attribute at the end. Set new attribute as needed before this.
2260+
if err := d.api.LunSetAttribute(ctx, lunPath, "poolName", poolName); err != nil {
2261+
Logc(ctx).WithField("LUN", lunPath).Warning("Failed to save the pool name attribute for new LUN.")
2262+
return fmt.Errorf("failed to save the pool name attribute for new LUN: %w", err)
2263+
}
2264+
22592265
return nil
22602266
}
22612267

storage_drivers/ontap/api/abstraction_rest_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3791,18 +3791,19 @@ func TestLunSetAttribute(t *testing.T) {
37913791

37923792
// case 1: Positive test, update LUN attribute. context is empty.
37933793
rsi.EXPECT().LunSetAttribute(ctx, "/", "filesystem", "fake-FStype").Return(nil)
3794-
err := oapi.LunSetAttribute(ctx, "/", "filesystem", "fake-FStype", "", "", "")
3794+
rsi.EXPECT().LunSetAttribute(ctx, "/", "poolName", "").Return(nil)
3795+
err := oapi.LunSetAttribute(ctx, "/", "filesystem", "fake-FStype", "", "", "", "")
37953796
assert.NoError(t, err, "error returned while modifying a LUN attribute")
37963797

37973798
// case 2: Positive test, update LUN attribute. pass the value in context..
37983799
rsi.EXPECT().LunSetAttribute(ctx, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
37993800
err = oapi.LunSetAttribute(ctx, "/", "filesystem", "fake-FStype",
3800-
"context", "LUKS", "formatOptions")
3801+
"context", "LUKS", "formatOptions", "poolName")
38013802
assert.NoError(t, err, "error returned while modifying a LUN attribute")
38023803

38033804
// case 3 Negative test, update LUN attribute returned error..
38043805
err = oapi.LunSetAttribute(ctx, "failure_7c3a89e2_7d83_457b_9e29_bfdb082c1d8b",
3805-
"filesystem", "fake-FStype", "context", "LUKS", "formatOptions")
3806+
"filesystem", "fake-FStype", "context", "LUKS", "formatOptions", "poolName")
38063807
assert.Error(t, err, "no error returned while modifying a LUN attribute")
38073808
}
38083809

storage_drivers/ontap/api/abstraction_zapi.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ func (d OntapAPIZAPI) LunGetAttribute(ctx context.Context, lunPath, attributeNam
571571
}
572572

573573
func (d OntapAPIZAPI) LunSetAttribute(
574-
ctx context.Context, lunPath, attribute, fstype, context, luks, formatOptions string,
574+
ctx context.Context, lunPath, attribute, fstype, context, luks, formatOptions, poolName string,
575575
) error {
576576
var attrResponse interface{}
577577
var err error
@@ -607,6 +607,13 @@ func (d OntapAPIZAPI) LunSetAttribute(
607607
}
608608
}
609609

610+
// Save the pool name attribute at the end. Set new attribute as needed before this.
611+
attrResponse, err = d.api.LunSetAttribute(lunPath, "poolName", poolName)
612+
if err = azgo.GetError(ctx, attrResponse, err); err != nil {
613+
Logc(ctx).WithField("LUN", lunPath).Warning("Failed to save the pool name attribute for new LUN.")
614+
return fmt.Errorf("failed to save the pool name attribute for new LUN: %w", err)
615+
}
616+
610617
return nil
611618
}
612619

storage_drivers/ontap/api/abstraction_zapi_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,18 +132,19 @@ func TestLunSetAttributeZapi(t *testing.T) {
132132

133133
// case 1a: Positive test, update LUN attribute - fsType.
134134
zapi.EXPECT().LunSetAttribute(tempLunPath, tempAttribute, "fake-FStype").Return(&response, nil).Times(1)
135-
err := oapi.LunSetAttribute(ctx, tempLunPath, tempAttribute, "fake-FStype", "", "", "")
135+
zapi.EXPECT().LunSetAttribute(tempLunPath, "poolName", "").Return(&response, nil).Times(1)
136+
err := oapi.LunSetAttribute(ctx, tempLunPath, tempAttribute, "fake-FStype", "", "", "", "")
136137
assert.NoError(t, err, "error returned while modifying a LUN attribute")
137138

138139
// case 1b: Negative test, d.api.LunSetAttribute for fsType return error
139140
zapi.EXPECT().LunSetAttribute(tempLunPath, tempAttribute, "fake-FStype").Return(nil, errors.New("error")).Times(1)
140-
err = oapi.LunSetAttribute(ctx, tempLunPath, tempAttribute, "fake-FStype", "", "", "")
141+
err = oapi.LunSetAttribute(ctx, tempLunPath, tempAttribute, "fake-FStype", "", "", "", "")
141142
assert.Error(t, err)
142143

143-
// case 2: Positive test, update LUN attributes those are: context, luks, formatOptions.
144+
// case 2: Positive test, update LUN attributes those are: context, luks, formatOptions, poolName.
144145
zapi.EXPECT().LunSetAttribute(tempLunPath, gomock.Any(), gomock.Any()).Return(&response, nil).AnyTimes()
145146
err = oapi.LunSetAttribute(ctx, tempLunPath, "filesystem", "",
146-
"context", "LUKS", "formatOptions")
147+
"context", "LUKS", "formatOptions", "poolName")
147148
assert.NoError(t, err, "error returned while modifying a LUN attribute")
148149
}
149150

storage_drivers/ontap/ontap_asa.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -420,9 +420,9 @@ func (d *ASAStorageDriver) Create(
420420

421421
// Save the fstype in a LUN attribute so we know what to do in Attach. If this fails, clean up and
422422
// move on to the next pool.
423-
// Save the context, fstype, and LUKS value in LUN comment
423+
// Save the context, fstype, LUKS value, and pool name in LUN comment
424424
err = d.API.LunSetAttribute(ctx, name, LUNAttributeFSType, fstype, string(d.Config.DriverContext),
425-
luksEncryption, formatOptions)
425+
luksEncryption, formatOptions, storagePool.Name())
426426
if err != nil {
427427

428428
errMessage := fmt.Sprintf("error saving file system type for LUN %s: %v", name, err)

storage_drivers/ontap/ontap_asa_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,7 @@ func TestCreateASA(t *testing.T) {
737737
mockAPI.EXPECT().TieringPolicyValue(ctx).Return("fake").Times(1)
738738
mockAPI.EXPECT().LunCreate(ctx, gomock.Any()).Return(nil).Times(1)
739739
mockAPI.EXPECT().LunSetComment(ctx, volumeName, labels).Return(nil).Times(1)
740-
mockAPI.EXPECT().LunSetAttribute(ctx, volumeName, LUNAttributeFSType, storagePool.InternalAttributes()[FileSystemType], string(driver.Config.DriverContext), storagePool.InternalAttributes()[LUKSEncryption], storagePool.InternalAttributes()[FormatOptions]).Return(nil).Times(1)
740+
mockAPI.EXPECT().LunSetAttribute(ctx, volumeName, LUNAttributeFSType, storagePool.InternalAttributes()[FileSystemType], string(driver.Config.DriverContext), storagePool.InternalAttributes()[LUKSEncryption], storagePool.InternalAttributes()[FormatOptions], storagePool.Name()).Return(nil).Times(1)
741741
},
742742
verify: func(t *testing.T, err error) {
743743
assert.NoError(t, err, "Should not be an error")
@@ -763,7 +763,7 @@ func TestCreateASA(t *testing.T) {
763763
mockAPI.EXPECT().TieringPolicyValue(ctx).Return("fake").Times(1)
764764
mockAPI.EXPECT().LunCreate(ctx, gomock.Any()).Return(nil).Times(1)
765765
mockAPI.EXPECT().LunSetComment(ctx, volumeName, labels).Return(nil).Times(1)
766-
mockAPI.EXPECT().LunSetAttribute(ctx, volumeName, LUNAttributeFSType, storagePool.InternalAttributes()[FileSystemType], string(driver.Config.DriverContext), storagePool.InternalAttributes()[LUKSEncryption], storagePool.InternalAttributes()[FormatOptions]).Return(nil).Times(1)
766+
mockAPI.EXPECT().LunSetAttribute(ctx, volumeName, LUNAttributeFSType, storagePool.InternalAttributes()[FileSystemType], string(driver.Config.DriverContext), storagePool.InternalAttributes()[LUKSEncryption], storagePool.InternalAttributes()[FormatOptions], storagePool.Name()).Return(nil).Times(1)
767767
},
768768
verify: func(t *testing.T, err error) {
769769
assert.NoError(t, err, "Should not be an error")
@@ -782,7 +782,7 @@ func TestCreateASA(t *testing.T) {
782782
assert.Equal(t, expectedLabels, labels, "Labels should match the expected value")
783783
return nil
784784
}).Times(1)
785-
mockAPI.EXPECT().LunSetAttribute(ctx, volumeName, LUNAttributeFSType, storagePool.InternalAttributes()[FileSystemType], string(driver.Config.DriverContext), storagePool.InternalAttributes()[LUKSEncryption], storagePool.InternalAttributes()[FormatOptions]).Return(nil).Times(1)
785+
mockAPI.EXPECT().LunSetAttribute(ctx, volumeName, LUNAttributeFSType, storagePool.InternalAttributes()[FileSystemType], string(driver.Config.DriverContext), storagePool.InternalAttributes()[LUKSEncryption], storagePool.InternalAttributes()[FormatOptions], storagePool.Name()).Return(nil).Times(1)
786786
},
787787
verify: func(t *testing.T, err error) {
788788
assert.NoError(t, err, "Should not be an error")
@@ -857,7 +857,8 @@ func TestCreateASA(t *testing.T) {
857857
storagePool.InternalAttributes()[FileSystemType],
858858
string(driver.Config.DriverContext),
859859
storagePool.InternalAttributes()[LUKSEncryption],
860-
storagePool.InternalAttributes()[FormatOptions]).
860+
storagePool.InternalAttributes()[FormatOptions],
861+
storagePool.Name()).
861862
Return(errors.New("api-error")).Times(1)
862863
mockAPI.EXPECT().LunDestroy(ctx, volumeName).Return(nil).Times(1)
863864
},

storage_drivers/ontap/ontap_san.go

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,11 @@ func (d *SANStorageDriver) validate(ctx context.Context) error {
265265
return nil
266266
}
267267

268-
// destroyVolumeIfNoLUN attempts to destroy volume if there exists a volume with no associated LUN.
269-
// This is used to make Create() idempotent by cleaning up a Flexvol with no LUN.
268+
// cleanupIncompleteLUN attempts to destroy volume if there exists a volume with no associated LUN.
269+
// This is used to make Create() idempotent by cleaning up a Flexvol with no LUN or
270+
// the LUN exists but is associated with a different pool as it was not cleaned up properly and creation is retried
271+
// with different pool.
272+
// This can happen if volume creation succeeded but LUN creation failed in a previous Create() call.
270273
// Returns (Volume State, error)
271274
//
272275
// Caller can check for:
@@ -276,15 +279,18 @@ func (d *SANStorageDriver) validate(ctx context.Context) error {
276279
// - Could not destroy the required volume for an error.
277280
// Volume state:true indicating both volume and required LUN exist.
278281
// Volume state:false indicating no volume existed or cleaned up now.
279-
func (d *SANStorageDriver) destroyVolumeIfNoLUN(ctx context.Context, volConfig *storage.VolumeConfig) (bool, error) {
282+
func (d *SANStorageDriver) cleanupIncompleteLUN(
283+
ctx context.Context, volConfig *storage.VolumeConfig, poolName string,
284+
) (bool, error) {
280285
name := volConfig.InternalName
281286
fields := LogFields{
282-
"Method": "destroyVolumeIfNoLUN",
283-
"Type": "SANStorageDriver",
284-
"name": name,
287+
"Method": "cleanupIncompleteLUN",
288+
"Type": "SANStorageDriver",
289+
"name": name,
290+
"poolName": poolName,
285291
}
286-
Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace(">>>> destroyVolumeIfNoLUN")
287-
defer Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace("<<<< destroyVolumeIfNoLUN")
292+
Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace(">>>> cleanupIncompleteLUN")
293+
defer Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace("<<<< cleanupIncompleteLUN")
288294

289295
volExists, err := d.API.VolumeExists(ctx, name)
290296
if err != nil {
@@ -302,20 +308,44 @@ func (d *SANStorageDriver) destroyVolumeIfNoLUN(ctx context.Context, volConfig *
302308
// Verify if LUN exists.
303309
newLUNPath := lunPath(name)
304310
extantLUN, err := d.API.LunGetByName(ctx, newLUNPath)
305-
if extantLUN != nil {
306-
// Volume and LUN both exist. No clean up needed.
307-
return true, nil
308-
}
309-
if !errors.IsNotFoundError(err) {
311+
if err != nil && !errors.IsNotFoundError(err) {
310312
// Could not verify if LUN exists. Clean up pending.
311313
return false, fmt.Errorf("error checking for existing LUN %s: %v", newLUNPath, err)
312314
}
313-
// LUN does not exist, but volume. Initiate clean-up.
314-
if err = d.API.VolumeDestroy(ctx, name, true, true); err != nil {
315-
Logc(ctx).WithField("volume", name).Errorf("Could not clean up volume: %v", err)
316-
return true, fmt.Errorf("could not clean up partial create of vol/lun: %v", err)
315+
316+
var destroyReason string
317+
var destroyErrorMsg string
318+
319+
if extantLUN != nil {
320+
// Both the volume and LUN exist. Check if the last attribute set on the LUN, i.e., the pool name, matches.
321+
lunPoolName, err := d.API.LunGetAttribute(ctx, newLUNPath, "poolName")
322+
if err != nil || lunPoolName != poolName {
323+
// If there is an error getting pool name or pool name doesn't match
324+
Logc(ctx).WithFields(LogFields{
325+
"LUN": newLUNPath,
326+
"lunPoolName": lunPoolName,
327+
"inputPoolName": poolName,
328+
"error": err,
329+
}).Info("Pool name not found or mismatch detected. Destroying volume.")
330+
331+
destroyReason = "Destroyed volume with mismatched pool name."
332+
destroyErrorMsg = "could not destroy volume with mismatched pool"
333+
} else {
334+
// Pool name matches or no pool name attribute. No clean up needed.
335+
return true, nil
336+
}
337+
} else {
338+
// LUN does not exist, but volume does. Initiate clean-up.
339+
destroyReason = "Cleaned up volume since LUN create failed."
340+
destroyErrorMsg = "could not clean up partial create of vol/lun"
317341
}
318-
Logc(ctx).WithField("volume", name).Debug("Cleaned up volume since LUN create failed.")
342+
343+
if err := d.API.VolumeDestroy(ctx, name, true, true); err != nil {
344+
Logc(ctx).WithError(err).WithField("volume", name).Errorf("Could not clean up volume")
345+
return true, fmt.Errorf("%s: %v", destroyErrorMsg, err)
346+
}
347+
Logc(ctx).WithField("volume", name).Debug(destroyReason)
348+
319349
return false, nil
320350
}
321351

@@ -338,7 +368,7 @@ func (d *SANStorageDriver) Create(
338368
defer Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace("<<<< Create")
339369

340370
// Early exit if volume+LUN exist. Clean up volume if no LUN exists.
341-
volExists, err := d.destroyVolumeIfNoLUN(ctx, volConfig)
371+
volExists, err := d.cleanupIncompleteLUN(ctx, volConfig, storagePool.Name())
342372
if err != nil {
343373
return fmt.Errorf("failure checking for existence of volume and cleaning if any: %v", err)
344374
}
@@ -580,9 +610,9 @@ func (d *SANStorageDriver) Create(
580610

581611
// Save the fstype in a LUN attribute so we know what to do in Attach. If this fails, clean up and
582612
// move on to the next pool.
583-
// Save the context, fstype, and LUKS value in LUN comment
613+
// Save the context, fstype, LUKS value, and pool name in LUN comment
584614
err = d.API.LunSetAttribute(ctx, lunPath, LUNAttributeFSType, fstype, string(d.Config.DriverContext),
585-
luksEncryption, formatOptions)
615+
luksEncryption, formatOptions, storagePool.Name())
586616
if err != nil {
587617

588618
errMessage := fmt.Sprintf("ONTAP-SAN pool %s/%s; error saving file system type for LUN %s: %v",

storage_drivers/ontap/ontap_san_economy.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,7 @@ func (d *SANEconomyStorageDriver) Create(
751751

752752
// Save the fstype in a LUN attribute so we know what to do in Attach
753753
err = d.API.LunSetAttribute(ctx, lunPathEco, LUNAttributeFSType, fstype, string(d.Config.DriverContext),
754-
luksEncryption, formatOptions)
754+
luksEncryption, formatOptions, storagePool.Name())
755755
if err != nil {
756756

757757
errMessage := fmt.Sprintf(

0 commit comments

Comments
 (0)