Skip to content

Commit

Permalink
Dynamically update SnapshotDir for ANF volume
Browse files Browse the repository at this point in the history
Extending tridentctl update volume command to ANF storage driver to allow dynamic modification of SnapshotDir of azure-netapp-files(ANF) volume/PV. Also, changing default value of SnapshotDir to true for a newly created ANF volume which uses NFSv4 protocol.
  • Loading branch information
aparna0508 authored Dec 15, 2023
1 parent 9e2a6ff commit e86b88a
Show file tree
Hide file tree
Showing 6 changed files with 499 additions and 57 deletions.
17 changes: 16 additions & 1 deletion storage/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,28 @@ type StorageBackend struct {
nodeAccessUpToDate bool
}

func (b *StorageBackend) UpdateVolume(ctx context.Context, volConfig *VolumeConfig, updateInfo *utils.VolumeUpdateInfo) (map[string]*Volume, error) {
func (b *StorageBackend) UpdateVolume(
ctx context.Context, volConfig *VolumeConfig, updateInfo *utils.VolumeUpdateInfo,
) (map[string]*Volume, error) {
Logc(ctx).WithFields(LogFields{
"backend": b.name,
"volume": volConfig.Name,
"volumeInternal": volConfig.InternalName,
"updateInfo": updateInfo,
}).Debug("Attempting volume update.")

// Ensure driver supports volume update operation
volUpdateDriver, ok := b.driver.(VolumeUpdater)
if !ok {
return nil, errors.UnsupportedError(
fmt.Sprintf("volume update is not supported for backend of type %v", b.driver.Name()))
}

// Ensure volume is managed
if volConfig.ImportNotManaged {
return nil, errors.NotManagedError("source volume %s is not managed by Trident", volConfig.InternalName)
}

return volUpdateDriver.Update(ctx, volConfig, updateInfo, b.volumes)
}

Expand Down
2 changes: 1 addition & 1 deletion storage_drivers/azure/api/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -1077,7 +1077,7 @@ func (c Client) ModifyVolume(
}

// Modify the export-rule to restrict the kerberos protocol type
if len(anfVolume.Properties.ExportPolicy.Rules) > 0 && &exportRule != nil {
if len(anfVolume.Properties.ExportPolicy.Rules) > 0 && exportRule != nil {
anfVolume.Properties.ExportPolicy.Rules[0].Nfsv41 = &exportRule.Nfsv41
anfVolume.Properties.ExportPolicy.Rules[0].Kerberos5ReadWrite = &exportRule.Kerberos5ReadWrite
anfVolume.Properties.ExportPolicy.Rules[0].Kerberos5ReadOnly = &exportRule.Kerberos5ReadOnly
Expand Down
117 changes: 116 additions & 1 deletion storage_drivers/azure/azure_anf.go
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,7 @@ func (d *NASStorageDriver) Create(
case nfsVersion41:
nfsV41Access = true
protocolTypes = []string{api.ProtocolTypeNFSv41}
snapshotDirBool = true
}

apiExportRule = api.ExportRule{
Expand Down Expand Up @@ -910,7 +911,7 @@ func (d *NASStorageDriver) Create(
// Update config to reflect values used to create volume
volConfig.Size = strconv.FormatUint(sizeBytes, 10)
volConfig.ServiceLevel = serviceLevel
volConfig.SnapshotDir = snapshotDir
volConfig.SnapshotDir = strconv.FormatBool(snapshotDirBool)
volConfig.UnixPermissions = unixPermissions

// Find a subnet
Expand Down Expand Up @@ -2252,3 +2253,117 @@ func constructVolumeAccessPath(
}
return ""
}

func (d *NASStorageDriver) Update(
ctx context.Context, volConfig *storage.VolumeConfig,
updateInfo *utils.VolumeUpdateInfo, allVolumes map[string]*storage.Volume,
) (map[string]*storage.Volume, error) {
name := volConfig.Name
fields := LogFields{
"Method": "Update",
"Type": "AzureNASStorageDriver",
"name": name,
"internalName": volConfig.InternalName,
"updateInfo": updateInfo,
}
Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace(">>>> Update")
defer Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace("<<<< Update")

updateErrorMsg := fmt.Sprintf("Failed to update volume %v.", name)

// Ensure there is something to update
if updateInfo == nil {
msg := fmt.Sprintf("nothing to update for volume %v", name)
err := errors.InvalidInputError(msg)
Logc(ctx).WithError(err).Error(updateErrorMsg)
return nil, err
}

// Update resource cache as needed
if err := d.SDK.RefreshAzureResources(ctx); err != nil {
updateErr := fmt.Errorf("could not update ANF resource cache; %v", err)
Logc(ctx).WithError(updateErr).Error(updateErrorMsg)
return nil, updateErr
}

// Get the volume
volume, err := d.SDK.Volume(ctx, volConfig)
if err != nil {
updateErr := fmt.Errorf("could not find volume %s; %v", name, err)
Logc(ctx).WithError(updateErr).Error(updateErrorMsg)
return nil, updateErr
}

// Heal the ID on legacy volumes
if volConfig.InternalID == "" {
volConfig.InternalID = volume.ID
}

// If the volume state isn't Available, return an error
if volume.ProvisioningState != api.StateAvailable {
updateErr := fmt.Errorf("volume %s state is %s, not %s", name, volume.ProvisioningState, api.StateAvailable)
Logc(ctx).WithError(updateErr).Error(updateErrorMsg)
return nil, updateErr
}

var updatedVols map[string]*storage.Volume
var updateError error

// Update snapshotDirectory for volume
if updateInfo.SnapshotDirectory != "" {
updatedVols, updateError = d.updateSnapshotDirectory(ctx, name, volume, updateInfo.SnapshotDirectory, allVolumes)
}

return updatedVols, updateError
}

func (d *NASStorageDriver) updateSnapshotDirectory(
ctx context.Context, name string, volume *api.FileSystem,
snapshotDir string, allVolumes map[string]*storage.Volume,
) (map[string]*storage.Volume, error) {
fields := LogFields{
"Method": "updateSnapshotDirectory",
"Type": "AzureNASStorageDriver",
"name": name,
"snapshotDir": snapshotDir,
}
Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace(">>>> updateSnapshotDirectory")
defer Logd(ctx, d.Name(), d.Config.DebugTraceFlags["method"]).WithFields(fields).Trace("<<<< updateSnapshotDirectory")

genericLogError := fmt.Sprintf("Failed to update snapshot directory for volume %v.", name)

// Ensure ACP is enabled
if err := acp.API().IsFeatureEnabled(ctx, acp.FeatureReadOnlyClone); err != nil {
Logc(ctx).WithFields(fields).WithError(err).Error(genericLogError)
return nil, err
}

// Validate request
snapDirBool, err := strconv.ParseBool(snapshotDir)
if err != nil {
failureErr := errors.InvalidInputError(fmt.Sprintf("invalid value for snapshot directory %v; %v", snapshotDir, err))
Logc(ctx).WithError(failureErr).Error(genericLogError)
return nil, failureErr
}

// Modify volume snapshotDirectory if the current value is different from requested
if volume.SnapshotDirectory != snapDirBool {
if err = d.SDK.ModifyVolume(ctx, volume, nil, nil, &snapDirBool, nil); err != nil {
Logc(ctx).WithError(err).Error(genericLogError)
return nil, err
}
}

// Update volConfig for the volume to ensure cache is appropriately updated
// Always do this, to ensure cache is always in consistent state with the requested value
// There could be a case where snapshotDirectory is modified directly in the backend and trident cache is not updated
var vol *storage.Volume
if vol = allVolumes[name]; vol == nil {
failureErr := fmt.Errorf("volume %v not found", name)
Logc(ctx).WithError(failureErr).Error(genericLogError)
return nil, failureErr
}
vol.Config.SnapshotDir = strconv.FormatBool(snapDirBool)

return map[string]*storage.Volume{name: vol}, nil
}
Loading

0 comments on commit e86b88a

Please sign in to comment.