diff --git a/docs/data-sources/volume.md b/docs/data-sources/volume.md index ed42ab3d..c7c0d9ce 100644 --- a/docs/data-sources/volume.md +++ b/docs/data-sources/volume.md @@ -47,16 +47,16 @@ data "netapp-ontap_volume" "storage_volume" { - `nas` (Attributes) (see [below for nested schema](#nestedatt--nas)) - `qos_policy_group` (String) Specifies a QoS policy group to be set on volume - `snaplock` (Attributes) (see [below for nested schema](#nestedatt--snaplock)) +- `snapshot_locking_enabled` (Boolean) Whether or not snapshot copy locking is enabled on the volume. - `snapshot_policy` (String) The name of the snapshot policy - `space` (Attributes) (see [below for nested schema](#nestedatt--space)) - `space_guarantee` (String) Space guarantee style for the volume - `state` (String) Whether the specified volume is online, or not +- `tags` (Set of String) Set of tags associated with the volume - `tiering` (Attributes) (see [below for nested schema](#nestedatt--tiering)) - `type` (String) The volume type, either read-write (RW) or data-protection (DP) -- `snapshot_locking_enabled` (Boolean) Whether or not snapshot copy locking is enabled on the volume - ### Nested Schema for `aggregates` Read-Only: @@ -93,10 +93,11 @@ Read-Only: Read-Only: +- `compaction` (String) The system can be enabled/disabled compaction - `compression` (String) Whether to enable compression for the volume (HDD and Flash Pool aggregates) -- `policy_name` (String) Allows a storage efficiency policy to be set on volume creation - `dedupe` (String) The system can be enabled/disabled dedupe -- `compaction` (String) The system can be enabled/disabled compression +- `policy_name` (String) Allows a storage efficiency policy to be set on volume creation + @@ -146,4 +147,5 @@ Read-Only: Read-Only: - `minimum_cooling_days` (Number) Determines how many days must pass before inactive data in a volume using the Auto or Snapshot-Only policy is considered cold and eligible for tiering +- `object_tags` (Set of String) Object tags are applied to objects in tiered storage - `policy_name` (String) The tiering policy that is to be associated with the volume diff --git a/docs/data-sources/volumes.md b/docs/data-sources/volumes.md index 50065592..79fd96fa 100644 --- a/docs/data-sources/volumes.md +++ b/docs/data-sources/volumes.md @@ -50,6 +50,9 @@ Optional: - `name` (String) StorageVolume name - `svm_name` (String) StorageVolume svm name +- `tags` (String) StorageVolume tag value +- `tiering_object_tags` (String) StorageVolume tiering object tag value + @@ -63,7 +66,7 @@ Required: Read-Only: -- `aggregates` (Attributes List) Aggregates the volume is on (see [below for nested schema](#nestedatt--aggregates)) +- `aggregates` (Attributes List) List of aggregates that the volume is on (see [below for nested schema](#nestedatt--storage_volumes--aggregates)) - `analytics` (Attributes) (see [below for nested schema](#nestedatt--storage_volumes--analytics)) - `autosize` (Attributes) (see [below for nested schema](#nestedatt--storage_volumes--autosize)) - `comment` (String) Sets a comment associated with the volume @@ -74,10 +77,12 @@ Read-Only: - `nas` (Attributes) (see [below for nested schema](#nestedatt--storage_volumes--nas)) - `qos_policy_group` (String) Specifies a QoS policy group to be set on volume - `snaplock` (Attributes) (see [below for nested schema](#nestedatt--storage_volumes--snaplock)) +- `snapshot_locking_enabled` (Boolean) Whether or not snapshot copy locking is enabled on the volume. - `snapshot_policy` (String) The name of the snapshot policy - `space` (Attributes) (see [below for nested schema](#nestedatt--storage_volumes--space)) - `space_guarantee` (String) Space guarantee style for the volume - `state` (String) Whether the specified volume is online, or not +- `tags` (Set of String) Set of tags associated with the volume - `tiering` (Attributes) (see [below for nested schema](#nestedatt--storage_volumes--tiering)) - `type` (String) The volume type, either read-write (RW) or data-protection (DP) @@ -97,8 +102,8 @@ Read-Only: - `state` (String) Set file system analytics state of the volume - + ### Nested Schema for `storage_volumes.autosize` Read-Only: @@ -107,9 +112,9 @@ Read-Only: - `maximum` (Number) Maximum size up to which a volume grows automatically. This size cannot be less than the current volume size, or less than or equal to the minimum size of volume. - `minimum` (Number) Minimum size up to which the volume shrinks automatically. This size cannot be greater than or equal to the maximum size of volume. - `mode` (String) Autosize mode for the volume. - grow - Volume automatically grows when the amount of used space is above the 'grow_threshold' value. - grow_shrink - Volume grows or shrinks in response to the amount of space used. - off - Autosizing of the volume is disabled. + grow - Volume automatically grows when the amount of used space is above the 'grow_threshold' value. + grow_shrink - Volume grows or shrinks in response to the amount of space used. + off - Autosizing of the volume is disabled. - `shrink_threshold` (Number) Used space threshold size, in percentage, for the automatic shrinkage of the volume. - `size_unit` (String) The unit used to interpret the minimum or maximum size parameters. @@ -119,7 +124,9 @@ Read-Only: Read-Only: +- `compaction` (String) The system can be enabled/disabled compaction - `compression` (String) Whether to enable compression for the volume (HDD and Flash Pool aggregates) +- `dedupe` (String) The system can be enabled/disabled dedupe - `policy_name` (String) Allows a storage efficiency policy to be set on volume creation @@ -172,4 +179,5 @@ Read-Only: Read-Only: - `minimum_cooling_days` (Number) Determines how many days must pass before inactive data in a volume using the Auto or Snapshot-Only policy is considered cold and eligible for tiering +- `object_tags` (Set of String) Object tags are applied to objects in tiered storage - `policy_name` (String) The tiering policy that is to be associated with the volume diff --git a/docs/resources/volume.md b/docs/resources/volume.md index 02aa84f3..f7c80eef 100644 --- a/docs/resources/volume.md +++ b/docs/resources/volume.md @@ -78,7 +78,7 @@ resource "netapp-ontap_volume" "example" { ### Required -- `aggregates` (Attributes List) Aggregates the volume is on (see [below for nested schema](#nestedatt--aggregates)) +- `aggregates` (Attributes Set) List of aggregates to place volume on (see [below for nested schema](#nestedatt--aggregates)) - `cx_profile_name` (String) Connection profile name - `name` (String) The name of the volume to manage - `space` (Attributes) (see [below for nested schema](#nestedatt--space)) @@ -95,19 +95,19 @@ resource "netapp-ontap_volume" "example" { - `nas` (Attributes) (see [below for nested schema](#nestedatt--nas)) - `qos_policy_group` (String) Specifies a QoS policy group to be set on volume - `snaplock` (Attributes) (see [below for nested schema](#nestedatt--snaplock)) +- `snapshot_locking_enabled` (Boolean) Whether or not snapshot copy locking is enabled on the volume. - `snapshot_policy` (String) The name of the snapshot policy - `space_guarantee` (String) Space guarantee style for the volume - `state` (String) Whether the specified volume is online, or not +- `tags` (Set of String) Set of tags associated with the volume - `tiering` (Attributes) (see [below for nested schema](#nestedatt--tiering)) - `type` (String) The volume type, either read-write (RW) or data-protection (DP) -- `snapshot_locking_enabled` (Boolean) Whether or not snapshot copy locking is enabled on the volume ### Read-Only - `id` (String) Volume identifier - ### Nested Schema for `aggregates` Required: @@ -167,10 +167,11 @@ Optional: Optional: +- `compaction` (String) The system can be enabled/disabled compaction - `compression` (String) Whether to enable compression for the volume (HDD and Flash Pool aggregates) -- `policy_name` (String) Allows a storage efficiency policy to be set on volume creation - `dedupe` (String) The system can be enabled/disabled dedupe -- `compaction` (String) The system can be enabled/disabled compression +- `policy_name` (String) Allows a storage efficiency policy to be set on volume creation + @@ -200,6 +201,7 @@ Optional: Optional: - `minimum_cooling_days` (Number) Determines how many days must pass before inactive data in a volume using the Auto or Snapshot-Only policy is considered cold and eligible for tiering +- `object_tags` (Set of String) Object tags are applied to objects in tiered storage - `policy_name` (String) The tiering policy that is to be associated with the volume ## Import diff --git a/internal/interfaces/storage_volume.go b/internal/interfaces/storage_volume.go index e31bf675..d2f5997b 100644 --- a/internal/interfaces/storage_volume.go +++ b/internal/interfaces/storage_volume.go @@ -14,49 +14,51 @@ import ( // StorageVolumeGetDataModelONTAP describes the GET record data model using go types for mapping. type StorageVolumeGetDataModelONTAP struct { - Name string - SVM svm - Space Space - State string - Type string - Comment string - SpaceGuarantee Guarantee `mapstructure:"guarantee"` - NAS NAS - QOS QOS - Encryption Encryption - Efficiency Efficiency - SnapshotPolicy SnapshotPolicy `mapstructure:"snapshot_policy,omitempty"` - TieringPolicy TieringPolicy `mapstructure:"tiering,omitempty"` - Snaplock Snaplock - Analytics Analytics - Language string - Aggregates []Aggregate - Autosize Autosize `mapstructure:"autosize,omitempty"` - UUID string - SnapshotLockingEnabled *bool `mapstructure:"snapshot_locking_enabled,omitempty"` + Name string + SVM svm + Space Space + State string + Type string + Comment string + SpaceGuarantee Guarantee `mapstructure:"guarantee"` + NAS NAS + QOS QOS + Encryption Encryption + Efficiency Efficiency + SnapshotPolicy SnapshotPolicy `mapstructure:"snapshot_policy,omitempty"` + TieringPolicy TieringPolicy `mapstructure:"tiering,omitempty"` + Snaplock Snaplock + Analytics Analytics + Language string + Aggregates []Aggregate + Autosize Autosize `mapstructure:"autosize,omitempty"` + UUID string + SnapshotLockingEnabled *bool `mapstructure:"snapshot_locking_enabled,omitempty"` + Tags []string `mapstructure:"_tags,omitempty"` } // StorageVolumeResourceModel describes the resource data model. type StorageVolumeResourceModel struct { - Name string `mapstructure:"name,omitempty"` - SVM svm `mapstructure:"svm,omitempty"` - Space Space `mapstructure:"space,omitempty"` - State string `mapstructure:"state,omitempty"` - Type string `mapstructure:"type,omitempty"` - Comment string `mapstructure:"comment,omitempty"` - SpaceGuarantee Guarantee `mapstructure:"guarantee,omitempty"` - NAS NAS `mapstructure:"nas,omitempty"` - QOS QOS `mapstructure:"qos,omitempty"` - Encryption Encryption `mapstructure:"encryption,omitempty"` - Efficiency Efficiency `mapstructure:"efficiency,omitempty"` - SnapshotPolicy SnapshotPolicy `mapstructure:"snapshot_policy,omitempty"` - TieringPolicy TieringPolicy `mapstructure:"tiering,omitempty"` - Snaplock Snaplock `mapstructure:"snaplock,omitempty"` - Analytics Analytics `mapstructure:"analytics,omitempty"` - Language string `mapstructure:"language,omitempty"` - Aggregates []map[string]interface{} `mapstructure:"aggregates,omitempty"` - Autosize Autosize `mapstructure:"autosize,omitempty"` - SnapshotLockingEnabled *bool `mapstructure:"snapshot_locking_enabled,omitempty"` + Name string `mapstructure:"name,omitempty"` + SVM svm `mapstructure:"svm,omitempty"` + Space Space `mapstructure:"space,omitempty"` + State string `mapstructure:"state,omitempty"` + Type string `mapstructure:"type,omitempty"` + Comment string `mapstructure:"comment,omitempty"` + SpaceGuarantee Guarantee `mapstructure:"guarantee,omitempty"` + NAS NAS `mapstructure:"nas,omitempty"` + QOS QOS `mapstructure:"qos,omitempty"` + Encryption Encryption `mapstructure:"encryption,omitempty"` + Efficiency Efficiency `mapstructure:"efficiency,omitempty"` + SnapshotPolicy SnapshotPolicy `mapstructure:"snapshot_policy,omitempty"` + TieringPolicy TieringPolicy `mapstructure:"tiering,omitempty"` + Snaplock Snaplock `mapstructure:"snaplock,omitempty"` + Analytics Analytics `mapstructure:"analytics,omitempty"` + Language string `mapstructure:"language,omitempty"` + Aggregates []map[string]interface{} `mapstructure:"aggregates,omitempty"` + Autosize Autosize `mapstructure:"autosize,omitempty"` + SnapshotLockingEnabled *bool `mapstructure:"snapshot_locking_enabled,omitempty"` + Tags []string `mapstructure:"tags,omitempty"` } // Aggregate describes the resource data model. @@ -102,8 +104,9 @@ type Policy struct { // TieringPolicy describes the resource data model. type TieringPolicy struct { - Policy string `mapstructure:"policy,omitempty"` - MinCoolingDays int `mapstructure:"min_cooling_days,omitempty"` + Policy string `mapstructure:"policy,omitempty"` + MinCoolingDays int `mapstructure:"min_cooling_days,omitempty"` + ObjectTags []string `mapstructure:"object_tags,omitempty"` } // Snapshot describes the resource data model. @@ -190,8 +193,10 @@ var POW2BYTEMAP = map[string]int{ // StorageVolumeDataSourceFilterModel describes the data source data model for queries. type StorageVolumeDataSourceFilterModel struct { - Name string `mapstructure:"name"` - SVMName string `mapstructure:"svm.name"` + Name string `mapstructure:"name"` + SVMName string `mapstructure:"svm.name"` + Tags string `mapstructure:"_tags"` + TieringObjectTags string `mapstructure:"tiering.object_tags"` } // GetUUIDVolumeByName get a volumes UUID by volume name @@ -226,7 +231,7 @@ func GetStorageVolume(errorHandler *utils.ErrorHandler, r restclient.RestClient, query := r.NewQuery() query.Fields([]string{"name", "svm.name", "aggregates", "space.size", "state", "type", "nas.export_policy.name", "nas.path", "guarantee.type", "space.snapshot.reserve_percent", "efficiency.dedupe", "efficiency.compaction", "nas.security_style", "encryption.enabled", "efficiency.policy.name", "nas.unix_permissions", "nas.gid", "nas.uid", "snapshot_policy.name", "language", "qos.policy.name", "snapshot_locking_enabled", - "tiering.policy", "comment", "efficiency.compression", "tiering.min_cooling_days", "space.logical_space.enforcement", "space.logical_space.reporting", "snaplock.type", "analytics.state", "autosize"}) + "tiering.policy", "comment", "efficiency.compression", "tiering.min_cooling_days", "tiering.object_tags", "space.logical_space.enforcement", "space.logical_space.reporting", "snaplock.type", "analytics.state", "autosize", "_tags"}) statusCode, response, err := r.GetNilOrOneRecord("storage/volumes/"+uuid, query, nil) if err != nil { return nil, errorHandler.MakeAndReportError("error reading volume info", fmt.Sprintf("error on GET storage/volumes: %s", err)) @@ -251,7 +256,7 @@ func GetStorageVolumeByName(errorHandler *utils.ErrorHandler, r restclient.RestC query.Add("return_records", "true") query.Fields([]string{"name", "uuid", "svm.name", "aggregates", "space.size", "state", "type", "nas.export_policy.name", "nas.path", "guarantee.type", "space.snapshot.reserve_percent", "efficiency.dedupe", "efficiency.compaction", "nas.security_style", "encryption.enabled", "efficiency.policy.name", "nas.unix_permissions", "nas.gid", "nas.uid", "snapshot_policy.name", "language", "qos.policy.name", "snapshot_locking_enabled", - "tiering.policy", "comment", "efficiency.compression", "tiering.min_cooling_days", "space.logical_space.enforcement", "space.logical_space.reporting", "snaplock.type", "analytics.state", "autosize"}) + "tiering.policy", "comment", "efficiency.compression", "tiering.min_cooling_days", "tiering.object_tags", "space.logical_space.enforcement", "space.logical_space.reporting", "snaplock.type", "analytics.state", "autosize", "_tags"}) statusCode, response, err := r.GetNilOrOneRecord("storage/volumes", query, nil) if err != nil { return nil, errorHandler.MakeAndReportError("error reading volume info by name", fmt.Sprintf("error on GET storage/volumes: %s", err)) @@ -278,7 +283,7 @@ func GetStorageVolumes(errorHandler *utils.ErrorHandler, r restclient.RestClient query := r.NewQuery() query.Fields([]string{"name", "svm.name", "aggregates", "space.size", "state", "type", "nas.export_policy.name", "nas.path", "guarantee.type", "space.snapshot.reserve_percent", "efficiency.dedupe", "efficiency.compaction", "nas.security_style", "encryption.enabled", "efficiency.policy.name", "nas.unix_permissions", "nas.gid", "nas.uid", "snapshot_policy.name", "language", "qos.policy.name", "snapshot_locking_enabled", - "tiering.policy", "comment", "efficiency.compression", "tiering.min_cooling_days", "space.logical_space.enforcement", "space.logical_space.reporting", "snaplock.type", "analytics.state", "autosize"}) + "tiering.policy", "comment", "efficiency.compression", "tiering.min_cooling_days", "tiering.object_tags", "space.logical_space.enforcement", "space.logical_space.reporting", "snaplock.type", "analytics.state", "autosize", "_tags"}) if filter != nil { var filterMap map[string]interface{} diff --git a/internal/provider/storage/storage_volume_data_source.go b/internal/provider/storage/storage_volume_data_source.go index 6a673088..6f6d1e8d 100644 --- a/internal/provider/storage/storage_volume_data_source.go +++ b/internal/provider/storage/storage_volume_data_source.go @@ -3,7 +3,6 @@ package storage import ( "context" "fmt" - "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/types" @@ -41,27 +40,28 @@ type StorageVolumeDataSource struct { // StorageVolumeDataSourceModel describes the data source data model. type StorageVolumeDataSourceModel struct { - CxProfileName types.String `tfsdk:"cx_profile_name"` - Name types.String `tfsdk:"name"` - SVMName types.String `tfsdk:"svm_name"` - State types.String `tfsdk:"state"` - Type types.String `tfsdk:"type"` - SpaceGuarantee types.String `tfsdk:"space_guarantee"` - Encrypt types.Bool `tfsdk:"encryption"` - SnapshotPolicy types.String `tfsdk:"snapshot_policy"` - Language types.String `tfsdk:"language"` - QOSPolicyGroup types.String `tfsdk:"qos_policy_group"` - Comment types.String `tfsdk:"comment"` - Aggregates []StorageVolumeDataSourceAggregates `tfsdk:"aggregates"` - ID types.String `tfsdk:"id"` - Space *StorageVolumeDataSourceSpace `tfsdk:"space"` - Nas *StorageVolumeDataSourceNas `tfsdk:"nas"` - Tiering *StorageVolumeDataSourceTiering `tfsdk:"tiering"` - Efficiency *StorageVolumeDataSourceEfficiency `tfsdk:"efficiency"` - SnapLock *StorageVolumeDataSourceSnapLock `tfsdk:"snaplock"` - Analytics *StorageVolumeDataSourceAnalytics `tfsdk:"analytics"` - Autosize *StorageVolumeDataSourceAutosize `tfsdk:"autosize"` - SnapshotLockingEnabled types.Bool `tfsdk:"snapshot_locking_enabled"` + CxProfileName types.String `tfsdk:"cx_profile_name"` + Name types.String `tfsdk:"name"` + SVMName types.String `tfsdk:"svm_name"` + State types.String `tfsdk:"state"` + Type types.String `tfsdk:"type"` + SpaceGuarantee types.String `tfsdk:"space_guarantee"` + Encrypt types.Bool `tfsdk:"encryption"` + SnapshotPolicy types.String `tfsdk:"snapshot_policy"` + Language types.String `tfsdk:"language"` + QOSPolicyGroup types.String `tfsdk:"qos_policy_group"` + Comment types.String `tfsdk:"comment"` + Aggregates []StorageVolumeDataSourceAggregates `tfsdk:"aggregates"` + ID types.String `tfsdk:"id"` + Space *StorageVolumeDataSourceSpace `tfsdk:"space"` + Nas *StorageVolumeDataSourceNas `tfsdk:"nas"` + Tiering *StorageVolumeDataSourceTiering `tfsdk:"tiering"` + Efficiency *StorageVolumeDataSourceEfficiency `tfsdk:"efficiency"` + SnapLock *StorageVolumeDataSourceSnapLock `tfsdk:"snaplock"` + Analytics *StorageVolumeDataSourceAnalytics `tfsdk:"analytics"` + Autosize *StorageVolumeDataSourceAutosize `tfsdk:"autosize"` + SnapshotLockingEnabled types.Bool `tfsdk:"snapshot_locking_enabled"` + Tags types.Set `tfsdk:"tags"` } // StorageVolumeDataSourceAggregates describes the analytics model. @@ -91,6 +91,7 @@ type StorageVolumeDataSourceEfficiency struct { type StorageVolumeDataSourceTiering struct { Policy types.String `tfsdk:"policy_name"` MinimumCoolingDays types.Int64 `tfsdk:"minimum_cooling_days"` + ObjectTags types.Set `tfsdk:"object_tags"` } // StorageVolumeDataSourceNas describes the Nas model. @@ -266,6 +267,11 @@ func (d *StorageVolumeDataSource) Schema(ctx context.Context, req datasource.Sch MarkdownDescription: "Determines how many days must pass before inactive data in a volume using the Auto or Snapshot-Only policy is considered cold and eligible for tiering", Computed: true, }, + "object_tags": schema.SetAttribute{ + ElementType: types.StringType, + MarkdownDescription: "Object tags are applied to objects in tiered storage", + Computed: true, + }, }, }, "efficiency": schema.SingleNestedAttribute{ @@ -350,6 +356,11 @@ func (d *StorageVolumeDataSource) Schema(ctx context.Context, req datasource.Sch MarkdownDescription: "Whether or not snapshot copy locking is enabled on the volume.", Computed: true, }, + "tags": schema.SetAttribute{ + ElementType: types.StringType, + MarkdownDescription: "Set of tags associated with the volume", + Computed: true, + }, }, } } @@ -417,9 +428,15 @@ func (d *StorageVolumeDataSource) Read(ctx context.Context, req datasource.ReadR SecurityStyle: types.StringValue(volume.NAS.SecurityStyle), UnixPermissions: types.Int64Value(int64(volume.NAS.UnixPermissions)), } + objectTagsSet, diags := stringSliceToSet(ctx, volume.TieringPolicy.ObjectTags) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } data.Tiering = &StorageVolumeDataSourceTiering{ Policy: types.StringValue(volume.TieringPolicy.Policy), MinimumCoolingDays: types.Int64Value(int64(volume.TieringPolicy.MinCoolingDays)), + ObjectTags: objectTagsSet, } data.Efficiency = &StorageVolumeDataSourceEfficiency{ Policy: types.StringValue(volume.Efficiency.Policy.Name), @@ -446,6 +463,12 @@ func (d *StorageVolumeDataSource) Read(ctx context.Context, req datasource.ReadR Mode: types.StringValue(volume.Autosize.Mode), } tflog.Debug(ctx, fmt.Sprintf("autosize info compiled: %#v", data.Autosize)) + tagsSet, diags := stringSliceToSet(ctx, volume.Tags) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + data.Tags = tagsSet data.ID = types.StringValue(volume.UUID) // Write logs using the tflog package diff --git a/internal/provider/storage/storage_volume_resource.go b/internal/provider/storage/storage_volume_resource.go index 977a2140..852aace8 100644 --- a/internal/provider/storage/storage_volume_resource.go +++ b/internal/provider/storage/storage_volume_resource.go @@ -20,6 +20,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" @@ -54,6 +55,36 @@ func NewStorageVolumeResourceAlias() resource.Resource { } } +// stringSliceToSet converts a slice of strings to a types.Set +func stringSliceToSet(ctx context.Context, strings []string) (types.Set, diag.Diagnostics) { + var diags diag.Diagnostics + + if len(strings) > 0 { + stringValues := make([]attr.Value, len(strings)) + for i, s := range strings { + stringValues[i] = types.StringValue(s) + } + stringsSet, setDiags := types.SetValue(types.StringType, stringValues) + diags.Append(setDiags...) + return stringsSet, diags + } + + return types.SetNull(types.StringType), diags +} + +// setToStringSlice converts a types.Set to a slice of strings +func setToStringSlice(ctx context.Context, set types.Set) ([]string, diag.Diagnostics) { + var diags diag.Diagnostics + var stringSlice []string + + if !set.IsUnknown() && !set.IsNull() { + setDiags := set.ElementsAs(ctx, &stringSlice, false) + diags.Append(setDiags...) + } + + return stringSlice, diags +} + // StorageVolumeResource defines the resource implementation. type StorageVolumeResource struct { config connection.ResourceOrDataSourceConfig @@ -61,27 +92,28 @@ type StorageVolumeResource struct { // StorageVolumeResourceModel describes the resource data model. type StorageVolumeResourceModel struct { - CxProfileName types.String `tfsdk:"cx_profile_name"` - Name types.String `tfsdk:"name"` - SVMName types.String `tfsdk:"svm_name"` - State types.String `tfsdk:"state"` - Type types.String `tfsdk:"type"` - SpaceGuarantee types.String `tfsdk:"space_guarantee"` - Encrypt types.Bool `tfsdk:"encryption"` - SnapshotPolicy types.String `tfsdk:"snapshot_policy"` - Language types.String `tfsdk:"language"` - QOSPolicyGroup types.String `tfsdk:"qos_policy_group"` - Comment types.String `tfsdk:"comment"` - Aggregates []StorageVolumeResourceAggregates `tfsdk:"aggregates"` - ID types.String `tfsdk:"id"` - Space types.Object `tfsdk:"space"` - Nas types.Object `tfsdk:"nas"` - Tiering types.Object `tfsdk:"tiering"` - Efficiency types.Object `tfsdk:"efficiency"` - SnapLock types.Object `tfsdk:"snaplock"` - Analytics types.Object `tfsdk:"analytics"` - Autosize types.Object `tfsdk:"autosize"` - SnapshotLockingEnabled types.Bool `tfsdk:"snapshot_locking_enabled"` + CxProfileName types.String `tfsdk:"cx_profile_name"` + Name types.String `tfsdk:"name"` + SVMName types.String `tfsdk:"svm_name"` + State types.String `tfsdk:"state"` + Type types.String `tfsdk:"type"` + SpaceGuarantee types.String `tfsdk:"space_guarantee"` + Encrypt types.Bool `tfsdk:"encryption"` + SnapshotPolicy types.String `tfsdk:"snapshot_policy"` + Language types.String `tfsdk:"language"` + QOSPolicyGroup types.String `tfsdk:"qos_policy_group"` + Comment types.String `tfsdk:"comment"` + Aggregates []StorageVolumeResourceAggregates `tfsdk:"aggregates"` + ID types.String `tfsdk:"id"` + Space types.Object `tfsdk:"space"` + Nas types.Object `tfsdk:"nas"` + Tiering types.Object `tfsdk:"tiering"` + Efficiency types.Object `tfsdk:"efficiency"` + SnapLock types.Object `tfsdk:"snaplock"` + Analytics types.Object `tfsdk:"analytics"` + Autosize types.Object `tfsdk:"autosize"` + SnapshotLockingEnabled types.Bool `tfsdk:"snapshot_locking_enabled"` + Tags types.Set `tfsdk:"tags"` } // StorageVolumeResourceAggregates describes the analytics model. @@ -111,6 +143,7 @@ type StorageVolumeResourceEfficiency struct { type StorageVolumeResourceTiering struct { Policy types.String `tfsdk:"policy_name"` MinimumCoolingDays types.Int64 `tfsdk:"minimum_cooling_days"` + ObjectTags types.Set `tfsdk:"object_tags"` } // StorageVolumeResourceNas describes the Nas model. @@ -381,6 +414,15 @@ func (r *StorageVolumeResource) Schema(ctx context.Context, req resource.SchemaR int64planmodifier.UseStateForUnknown(), }, }, + "object_tags": schema.SetAttribute{ + ElementType: types.StringType, + MarkdownDescription: "Object tags are applied to objects in tiered storage", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Set{ + setplanmodifier.UseStateForUnknown(), + }, + }, }, }, "efficiency": schema.SingleNestedAttribute{ @@ -538,6 +580,15 @@ func (r *StorageVolumeResource) Schema(ctx context.Context, req resource.SchemaR }, }, }, + "tags": schema.SetAttribute{ + ElementType: types.StringType, + MarkdownDescription: "Set of tags associated with the volume", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Set{ + setplanmodifier.UseStateForUnknown(), + }, + }, "id": schema.StringAttribute{ Computed: true, MarkdownDescription: "Volume identifier", @@ -627,7 +678,7 @@ func (r *StorageVolumeResource) Read(ctx context.Context, req resource.ReadReque data.ID = types.StringValue(response.UUID) } else { response, err = interfaces.GetStorageVolume(errorHandler, *client, data.ID.ValueString()) - + if err != nil { return } @@ -652,11 +703,11 @@ func (r *StorageVolumeResource) Read(ctx context.Context, req resource.ReadReque "reporting": types.BoolType, "enforcement": types.BoolType, } - nestedEslements := map[string]attr.Value{ + nestedElements := map[string]attr.Value{ "reporting": types.BoolValue(response.Space.LogicalSpace.Reporting), "enforcement": types.BoolValue(response.Space.LogicalSpace.Enforcement), } - logicalObjectValue, _ := types.ObjectValue(nestedElementTypes, nestedEslements) + logicalObjectValue, _ := types.ObjectValue(nestedElementTypes, nestedElements) elementTypes := map[string]attr.Type{ "size": types.Int64Type, "size_unit": types.StringType, @@ -733,10 +784,16 @@ func (r *StorageVolumeResource) Read(ctx context.Context, req resource.ReadReque elementTypes = map[string]attr.Type{ "minimum_cooling_days": types.Int64Type, "policy_name": types.StringType, + "object_tags": types.SetType{ElemType: types.StringType}, + } + objectTagsSet, diags := stringSliceToSet(ctx, response.TieringPolicy.ObjectTags) + if diags.HasError() { + resp.Diagnostics.Append(diags...) } elements = map[string]attr.Value{ "minimum_cooling_days": types.Int64Value(int64(response.TieringPolicy.MinCoolingDays)), "policy_name": types.StringValue(response.TieringPolicy.Policy), + "object_tags": objectTagsSet, } objectValue, diags = types.ObjectValue(elementTypes, elements) if diags.HasError() { @@ -832,6 +889,14 @@ func (r *StorageVolumeResource) Read(ctx context.Context, req resource.ReadReque } data.Autosize = objectValue + // Tags + tagsSet, diags := stringSliceToSet(ctx, response.Tags) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + data.Tags = tagsSet + // Save updated data into Terraform state resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) } @@ -992,6 +1057,12 @@ func (r *StorageVolumeResource) Create(ctx context.Context, req resource.CreateR if !tiering.MinimumCoolingDays.IsUnknown() { request.TieringPolicy.MinCoolingDays = int(tiering.MinimumCoolingDays.ValueInt64()) } + objectTags, diags := setToStringSlice(ctx, tiering.ObjectTags) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + request.TieringPolicy.ObjectTags = objectTags } if !data.SnapLock.IsUnknown() { @@ -1040,6 +1111,15 @@ func (r *StorageVolumeResource) Create(ctx context.Context, req resource.CreateR } } + if !data.Tags.IsUnknown() && !data.Tags.IsNull() { + tags, diags := setToStringSlice(ctx, data.Tags) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + request.Tags = tags + } + if resp.Diagnostics.HasError() { return } @@ -1139,10 +1219,16 @@ func (r *StorageVolumeResource) Create(ctx context.Context, req resource.CreateR elementTypes = map[string]attr.Type{ "minimum_cooling_days": types.Int64Type, "policy_name": types.StringType, + "object_tags": types.SetType{ElemType: types.StringType}, + } + objectTagsSet, diags := stringSliceToSet(ctx, response.TieringPolicy.ObjectTags) + if diags.HasError() { + resp.Diagnostics.Append(diags...) } elements = map[string]attr.Value{ "minimum_cooling_days": types.Int64Value(int64(response.TieringPolicy.MinCoolingDays)), "policy_name": types.StringValue(response.TieringPolicy.Policy), + "object_tags": objectTagsSet, } objectValue, diags = types.ObjectValue(elementTypes, elements) if diags.HasError() { @@ -1371,6 +1457,12 @@ func (r *StorageVolumeResource) Update(ctx context.Context, req resource.UpdateR } request.TieringPolicy.Policy = tiering.Policy.ValueString() request.TieringPolicy.MinCoolingDays = int(tiering.MinimumCoolingDays.ValueInt64()) + objectTags, diags := setToStringSlice(ctx, tiering.ObjectTags) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + request.TieringPolicy.ObjectTags = objectTags } if !plan.SnapLock.Equal(state.SnapLock) { @@ -1408,6 +1500,15 @@ func (r *StorageVolumeResource) Update(ctx context.Context, req resource.UpdateR request.Autosize.Mode = autosize.Mode.ValueString() } + if !plan.Tags.Equal(state.Tags) { + tags, diags := setToStringSlice(ctx, plan.Tags) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + request.Tags = tags + } + ignoreOptions := []string{} if ignoreEncrption { ignoreOptions = append(ignoreOptions, "encryption") @@ -1600,14 +1701,21 @@ func readVolume(ctx context.Context, client *restclient.RestClient, data *Storag } data.Efficiency = objectValue - //Tiering + // Tiering elementTypes = map[string]attr.Type{ "minimum_cooling_days": types.Int64Type, "policy_name": types.StringType, + "object_tags": types.SetType{ElemType: types.StringType}, + } + // Convert object tags to types.List + objectTagsSet, diags := stringSliceToSet(ctx, response.TieringPolicy.ObjectTags) + if diags.HasError() { + allDiags.Append(diags...) } elements = map[string]attr.Value{ "minimum_cooling_days": types.Int64Value(int64(response.TieringPolicy.MinCoolingDays)), "policy_name": types.StringValue(response.TieringPolicy.Policy), + "object_tags": objectTagsSet, } objectValue, diags = types.ObjectValue(elementTypes, elements) if diags.HasError() { @@ -1685,5 +1793,12 @@ func readVolume(ctx context.Context, client *restclient.RestClient, data *Storag } data.Autosize = objectValue + tagsSet, diags := stringSliceToSet(ctx, response.Tags) + if diags.HasError() { + allDiags.Append(diags...) + return allDiags + } + data.Tags = tagsSet + return allDiags } diff --git a/internal/provider/storage/storage_volumes_data_source.go b/internal/provider/storage/storage_volumes_data_source.go index 49746a64..3a7c8dbd 100644 --- a/internal/provider/storage/storage_volumes_data_source.go +++ b/internal/provider/storage/storage_volumes_data_source.go @@ -48,8 +48,10 @@ type StorageVolumesDataSourceModel struct { // StorageVolumeDataSourceFilterModel describes the data source data model for queries. type StorageVolumeDataSourceFilterModel struct { - Name types.String `tfsdk:"name"` - SVMName types.String `tfsdk:"svm_name"` + Name types.String `tfsdk:"name"` + SVMName types.String `tfsdk:"svm_name"` + TieringObjectTags types.String `tfsdk:"tiering_object_tags"` + Tags types.String `tfsdk:"tags"` } // Metadata returns the data source type name. @@ -78,6 +80,14 @@ func (d *StorageVolumesDataSource) Schema(ctx context.Context, req datasource.Sc MarkdownDescription: "StorageVolume svm name", Optional: true, }, + "tags": schema.StringAttribute{ + MarkdownDescription: "StorageVolume tag value", + Optional: true, + }, + "tiering_object_tags": schema.StringAttribute{ + MarkdownDescription: "StorageVolume tiering object tag value", + Optional: true, + }, }, Optional: true, }, @@ -211,6 +221,11 @@ func (d *StorageVolumesDataSource) Schema(ctx context.Context, req datasource.Sc MarkdownDescription: "Determines how many days must pass before inactive data in a volume using the Auto or Snapshot-Only policy is considered cold and eligible for tiering", Computed: true, }, + "object_tags": schema.SetAttribute{ + ElementType: types.StringType, + MarkdownDescription: "Object tags are applied to objects in tiered storage", + Computed: true, + }, }, }, "efficiency": schema.SingleNestedAttribute{ @@ -291,6 +306,11 @@ func (d *StorageVolumesDataSource) Schema(ctx context.Context, req datasource.Sc Computed: true, MarkdownDescription: "Volume identifier", }, + "tags": schema.SetAttribute{ + ElementType: types.StringType, + MarkdownDescription: "Set of tags associated with the volume", + Computed: true, + }, }, }, Computed: true, @@ -338,8 +358,10 @@ func (d *StorageVolumesDataSource) Read(ctx context.Context, req datasource.Read var filter *interfaces.StorageVolumeDataSourceFilterModel = nil if data.Filter != nil { filter = &interfaces.StorageVolumeDataSourceFilterModel{ - Name: data.Filter.Name.ValueString(), - SVMName: data.Filter.SVMName.ValueString(), + Name: data.Filter.Name.ValueString(), + SVMName: data.Filter.SVMName.ValueString(), + Tags: data.Filter.Tags.ValueString(), + TieringObjectTags: data.Filter.TieringObjectTags.ValueString(), } } restInfo, err := interfaces.GetStorageVolumes(errorHandler, *client, filter) @@ -358,6 +380,18 @@ func (d *StorageVolumesDataSource) Read(ctx context.Context, req datasource.Read aggregates[i].Name = types.StringValue(v.Name) } + objectTagsSet, diags := stringSliceToSet(ctx, record.TieringPolicy.ObjectTags) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + + tagsSet, diags := stringSliceToSet(ctx, record.Tags) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + data.StorageVolumes[index] = StorageVolumeDataSourceModel{ CxProfileName: types.String(data.CxProfileName), Name: types.StringValue(record.Name), @@ -391,6 +425,7 @@ func (d *StorageVolumesDataSource) Read(ctx context.Context, req datasource.Read Tiering: &StorageVolumeDataSourceTiering{ Policy: types.StringValue(record.TieringPolicy.Policy), MinimumCoolingDays: types.Int64Value(int64(record.TieringPolicy.MinCoolingDays)), + ObjectTags: objectTagsSet, }, Efficiency: &StorageVolumeDataSourceEfficiency{ Policy: types.StringValue(record.Efficiency.Policy.Name), @@ -412,7 +447,8 @@ func (d *StorageVolumesDataSource) Read(ctx context.Context, req datasource.Read GrowThreshold: types.Int64Value(int64(record.Autosize.GrowThreshold)), Mode: types.StringValue(record.Autosize.Mode), }, - ID: types.StringValue(record.UUID), + ID: types.StringValue(record.UUID), + Tags: tagsSet, } }