From 7c8f83ad8a88b498aee52ff40b8c78fd0ddac9e7 Mon Sep 17 00:00:00 2001 From: swenjun Date: Wed, 14 Feb 2024 22:33:04 -0800 Subject: [PATCH 01/23] added flexcache resource, data source and data sources. --- .../data-source.tf | 6 + .../provider.tf | 51 ++ .../variables.tf | 11 + .../data-source.tf | 8 + .../provider.tf | 51 ++ .../variables.tf | 11 + .../provider.tf | 51 ++ .../resource.tf | 28 + .../variables.tf | 11 + internal/interfaces/storage_flexcache.go | 153 ++++ internal/provider/provider.go | 3 + .../provider/storage_flexcache_data_source.go | 333 +++++++++ .../provider/storage_flexcache_resource.go | 695 ++++++++++++++++++ .../storage_flexcache_resource_test.go | 94 +++ .../storage_flexcaches_data_source.go | 356 +++++++++ internal/restclient/rest_client.go | 21 +- 16 files changed, 1877 insertions(+), 6 deletions(-) create mode 100644 examples/data-sources/netapp-ontap_storage_flexcache/data-source.tf create mode 100644 examples/data-sources/netapp-ontap_storage_flexcache/provider.tf create mode 100644 examples/data-sources/netapp-ontap_storage_flexcache/variables.tf create mode 100644 examples/data-sources/netapp-ontap_storage_flexcaches/data-source.tf create mode 100644 examples/data-sources/netapp-ontap_storage_flexcaches/provider.tf create mode 100644 examples/data-sources/netapp-ontap_storage_flexcaches/variables.tf create mode 100644 examples/resources/netapp-ontap_storage_flexcache/provider.tf create mode 100644 examples/resources/netapp-ontap_storage_flexcache/resource.tf create mode 100644 examples/resources/netapp-ontap_storage_flexcache/variables.tf create mode 100644 internal/interfaces/storage_flexcache.go create mode 100644 internal/provider/storage_flexcache_data_source.go create mode 100644 internal/provider/storage_flexcache_resource.go create mode 100644 internal/provider/storage_flexcache_resource_test.go create mode 100644 internal/provider/storage_flexcaches_data_source.go diff --git a/examples/data-sources/netapp-ontap_storage_flexcache/data-source.tf b/examples/data-sources/netapp-ontap_storage_flexcache/data-source.tf new file mode 100644 index 00000000..868f2b62 --- /dev/null +++ b/examples/data-sources/netapp-ontap_storage_flexcache/data-source.tf @@ -0,0 +1,6 @@ +data "netapp-ontap_storage_flexcache_data_source" "storage_flexcache" { + # required to know which system to interface with + cx_profile_name = "cluster5" + name = "fc5" + svm_name = "automation" +} diff --git a/examples/data-sources/netapp-ontap_storage_flexcache/provider.tf b/examples/data-sources/netapp-ontap_storage_flexcache/provider.tf new file mode 100644 index 00000000..9a9d3db8 --- /dev/null +++ b/examples/data-sources/netapp-ontap_storage_flexcache/provider.tf @@ -0,0 +1,51 @@ +terraform { + required_providers { + netapp-ontap = { + source = "NetApp/netapp-ontap" + version = "0.0.1" + } + } +} + + +provider "netapp-ontap" { + # A connection profile defines how to interface with an ONTAP cluster or svm. + # At least one is required. + connection_profiles = [ + { + name = "cluster1" + hostname = "********219" + username = var.username + password = var.password + validate_certs = var.validate_certs + }, + { + name = "cluster2" + hostname = "********222" + username = var.username + password = var.password + validate_certs = var.validate_certs + }, + { + name = "cluster3" + hostname = "10.193.176.159" + username = var.username + password = var.password + validate_certs = var.validate_certs + }, + { + name = "cluster4" + hostname = "10.193.180.108" + username = var.username + password = var.password + validate_certs = var.validate_certs + }, + { + name = "cluster5" + hostname = "10.193.176.186" + username = var.username + password = var.password + validate_certs = var.validate_certs + } + ] +} diff --git a/examples/data-sources/netapp-ontap_storage_flexcache/variables.tf b/examples/data-sources/netapp-ontap_storage_flexcache/variables.tf new file mode 100644 index 00000000..b79f07ed --- /dev/null +++ b/examples/data-sources/netapp-ontap_storage_flexcache/variables.tf @@ -0,0 +1,11 @@ +# Terraform will prompt for values, unless a tfvars file is present. +variable "username" { + type = string +} +variable "password" { + type = string + sensitive = true +} +variable "validate_certs" { + type = bool +} diff --git a/examples/data-sources/netapp-ontap_storage_flexcaches/data-source.tf b/examples/data-sources/netapp-ontap_storage_flexcaches/data-source.tf new file mode 100644 index 00000000..6088d136 --- /dev/null +++ b/examples/data-sources/netapp-ontap_storage_flexcaches/data-source.tf @@ -0,0 +1,8 @@ +data "netapp-ontap_storage_flexcaches_data_source" "storage_flexcache" { + # required to know which system to interface with + cx_profile_name = "cluster5" + filter = { + name = "aggr*" + svm_name = "automation" + } +} diff --git a/examples/data-sources/netapp-ontap_storage_flexcaches/provider.tf b/examples/data-sources/netapp-ontap_storage_flexcaches/provider.tf new file mode 100644 index 00000000..9a9d3db8 --- /dev/null +++ b/examples/data-sources/netapp-ontap_storage_flexcaches/provider.tf @@ -0,0 +1,51 @@ +terraform { + required_providers { + netapp-ontap = { + source = "NetApp/netapp-ontap" + version = "0.0.1" + } + } +} + + +provider "netapp-ontap" { + # A connection profile defines how to interface with an ONTAP cluster or svm. + # At least one is required. + connection_profiles = [ + { + name = "cluster1" + hostname = "********219" + username = var.username + password = var.password + validate_certs = var.validate_certs + }, + { + name = "cluster2" + hostname = "********222" + username = var.username + password = var.password + validate_certs = var.validate_certs + }, + { + name = "cluster3" + hostname = "10.193.176.159" + username = var.username + password = var.password + validate_certs = var.validate_certs + }, + { + name = "cluster4" + hostname = "10.193.180.108" + username = var.username + password = var.password + validate_certs = var.validate_certs + }, + { + name = "cluster5" + hostname = "10.193.176.186" + username = var.username + password = var.password + validate_certs = var.validate_certs + } + ] +} diff --git a/examples/data-sources/netapp-ontap_storage_flexcaches/variables.tf b/examples/data-sources/netapp-ontap_storage_flexcaches/variables.tf new file mode 100644 index 00000000..b79f07ed --- /dev/null +++ b/examples/data-sources/netapp-ontap_storage_flexcaches/variables.tf @@ -0,0 +1,11 @@ +# Terraform will prompt for values, unless a tfvars file is present. +variable "username" { + type = string +} +variable "password" { + type = string + sensitive = true +} +variable "validate_certs" { + type = bool +} diff --git a/examples/resources/netapp-ontap_storage_flexcache/provider.tf b/examples/resources/netapp-ontap_storage_flexcache/provider.tf new file mode 100644 index 00000000..9a9d3db8 --- /dev/null +++ b/examples/resources/netapp-ontap_storage_flexcache/provider.tf @@ -0,0 +1,51 @@ +terraform { + required_providers { + netapp-ontap = { + source = "NetApp/netapp-ontap" + version = "0.0.1" + } + } +} + + +provider "netapp-ontap" { + # A connection profile defines how to interface with an ONTAP cluster or svm. + # At least one is required. + connection_profiles = [ + { + name = "cluster1" + hostname = "********219" + username = var.username + password = var.password + validate_certs = var.validate_certs + }, + { + name = "cluster2" + hostname = "********222" + username = var.username + password = var.password + validate_certs = var.validate_certs + }, + { + name = "cluster3" + hostname = "10.193.176.159" + username = var.username + password = var.password + validate_certs = var.validate_certs + }, + { + name = "cluster4" + hostname = "10.193.180.108" + username = var.username + password = var.password + validate_certs = var.validate_certs + }, + { + name = "cluster5" + hostname = "10.193.176.186" + username = var.username + password = var.password + validate_certs = var.validate_certs + } + ] +} diff --git a/examples/resources/netapp-ontap_storage_flexcache/resource.tf b/examples/resources/netapp-ontap_storage_flexcache/resource.tf new file mode 100644 index 00000000..63f27bf4 --- /dev/null +++ b/examples/resources/netapp-ontap_storage_flexcache/resource.tf @@ -0,0 +1,28 @@ +resource "netapp-ontap_storage_flexcache_resource" "storage_flexcache" { + # required to know which system to interface with + cx_profile_name = "cluster5" + name = "fc10" + svm_name = "automation" + origins = [ + { + volume = { + name = "vol1" + }, + svm = { + name = "automation" + } + } + ] + size = 400 + size_unit = "mb" + guarantee = { + type = "none" + } + dr_cache = false + global_file_locking_enabled = false + aggregates = [ + { + name = "aggr1" + } + ] +} diff --git a/examples/resources/netapp-ontap_storage_flexcache/variables.tf b/examples/resources/netapp-ontap_storage_flexcache/variables.tf new file mode 100644 index 00000000..b79f07ed --- /dev/null +++ b/examples/resources/netapp-ontap_storage_flexcache/variables.tf @@ -0,0 +1,11 @@ +# Terraform will prompt for values, unless a tfvars file is present. +variable "username" { + type = string +} +variable "password" { + type = string + sensitive = true +} +variable "validate_certs" { + type = bool +} diff --git a/internal/interfaces/storage_flexcache.go b/internal/interfaces/storage_flexcache.go new file mode 100644 index 00000000..efaad612 --- /dev/null +++ b/internal/interfaces/storage_flexcache.go @@ -0,0 +1,153 @@ +package interfaces + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/mitchellh/mapstructure" + "github.com/netapp/terraform-provider-netapp-ontap/internal/restclient" + "github.com/netapp/terraform-provider-netapp-ontap/internal/utils" +) + +// StorageFlexcacheGetDataModelONTAP describes the GET record data model using go types for mapping. +type StorageFlexcacheGetDataModelONTAP struct { + Name string + SVM svm + Aggregates []StorageFlexcacheAggregate `mapstructure:"aggregates"` + Origins []StorageFlexcacheOrigin `mapstructure:"origins"` + JunctionPath string `mapstructure:"junction_path,omitempty"` + Size int `mapstructure:"size,omitempty"` + Path string `mapstructure:"path,omitempty"` + Guarantee StorageFlexcacheGuarantee `mapstructure:"guarantee,omitempty"` + DrCache bool `mapstructure:"dr_cache,omitempty"` + GlobalFileLockingEnabled bool `mapstructure:"global_file_locking_enabled,omitempty"` + UseTieredAggregate bool `mapstructure:"use_tiered_aggregate,omitempty"` + ConstituentsPerAggregate int `mapstructure:"constituents_per_aggregate,omitempty"` + UUID string +} + +type StorageFlexcacheResourceModel struct { + Name string `mapstructure:"name,omitempty"` + SVM svm `mapstructure:"svm,omitempty"` + Origins []map[string]interface{} `mapstructure:"origins,omitempty"` + JunctionPath string `mapstructure:"junction_path,omitempty"` + Size int `mapstructure:"size,omitempty"` + Path string `mapstructure:"path,omitempty"` + Guarantee StorageFlexcacheGuarantee `mapstructure:"guarantee,omitempty"` + DrCache bool `mapstructure:"dr_cache"` + GlobalFileLockingEnabled bool `mapstructure:"global_file_locking_enabled"` + UseTieredAggregate bool `mapstructure:"use_tiered_aggregate"` + ConstituentsPerAggregate int `mapstructure:"constituents_per_aggregate,omitempty"` + Aggregates []map[string]interface{} `mapstructure:"aggregates,omitempty"` +} + +type StorageFlexcacheGuarantee struct { + Type string `mapstructure:"type,omitempty"` +} + +type StorageFlexcacheOrigin struct { + Volume StorageFlexcacheVolume `mapstructure:"volume"` + SVM StorageFlexcacheSVM `mapstructure:"svm"` +} + +type StorageFlexcacheVolume struct { + Name string `mapstructure:"name,omitempty"` + ID string `mapstructure:"uuid,omitempty"` +} + +type StorageFlexcacheSVM struct { + Name string `mapstructure:"name,omitempty"` + ID string `mapstructure:"uuid,omitempty"` +} + +type StorageFlexcacheAggregate struct { + Name string `mapstructure:"name,omitempty"` + ID string `mapstructure:"uuid,omitempty"` +} + +// StorageFlexcacheDataSourceFilterModel describes the data source data model for queries. +type StorageFlexcacheDataSourceFilterModel struct { + Name string `mapstructure:"name"` + SVMName string `mapstructure:"svm.name"` +} + +// GetStorageFlexcacheByName to get flexcache info by name +func GetStorageFlexcacheByName(errorHandler *utils.ErrorHandler, r restclient.RestClient, name string, svmName string) (*StorageFlexcacheGetDataModelONTAP, error) { + query := r.NewQuery() + query.Add("name", name) + query.Add("svm.name", svmName) + query.Fields([]string{"size", "path", "origins", "guarantee.type", "constituents_per_aggregate", "dr_cache", "global_file_locking_enabled", "use_tiered_aggregate", "aggregates"}) + statusCode, response, err := r.GetNilOrOneRecord("storage/flexcache/flexcaches", query, nil) + if err != nil { + return nil, errorHandler.MakeAndReportError("error reading flexcache info", fmt.Sprintf("error on GET storage/flexcache/flexcaches: %s", err)) + } + var dataONTAP *StorageFlexcacheGetDataModelONTAP + if err := mapstructure.Decode(response, &dataONTAP); err != nil { + return nil, errorHandler.MakeAndReportError("error decoding flexcache info", fmt.Sprintf("error on decode storage/flexcache/flexcaches: %s, statusCode %d, response %#v", err, statusCode, response)) + } + tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Read flexcache source - udata: %#v", dataONTAP)) + return dataONTAP, nil +} + +// GetStorageFlexcaches to get flexcaches info by filter +func GetStorageFlexcaches(errorHandler *utils.ErrorHandler, r restclient.RestClient, filter *StorageFlexcacheDataSourceFilterModel) ([]StorageFlexcacheGetDataModelONTAP, error) { + api := "storage/flexcache/flexcaches" + query := r.NewQuery() + query.Fields([]string{"size", "path", "origins", "guarantee.type", "constituents_per_aggregate", "dr_cache", "global_file_locking_enabled", "use_tiered_aggregate", "aggregates"}) + if filter != nil { + var filterMap map[string]interface{} + if err := mapstructure.Decode(filter, &filterMap); err != nil { + return nil, errorHandler.MakeAndReportError("error encoding storage flexcache filter info", fmt.Sprintf("error on filter %#v: %s", filter, err)) + } + query.SetValues(filterMap) + } + + statusCode, response, err := r.GetZeroOrMoreRecords(api, query, nil) + if err == nil && response == nil { + err = fmt.Errorf("no response for GET %s", api) + } + if err != nil { + return nil, errorHandler.MakeAndReportError("error reading storage flexcache info", fmt.Sprintf("error on GET %s: %s, statusCode %d", api, err, statusCode)) + } + + var dataONTAP []StorageFlexcacheGetDataModelONTAP + for _, info := range response { + var record StorageFlexcacheGetDataModelONTAP + if err := mapstructure.Decode(info, &record); err != nil { + return nil, errorHandler.MakeAndReportError(fmt.Sprintf("failed to decode response from GET %s", api), + fmt.Sprintf("error: %s, statusCode %d, info %#v", err, statusCode, info)) + } + dataONTAP = append(dataONTAP, record) + } + tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Read storage flexcache data source: %#v", dataONTAP)) + return dataONTAP, nil +} + +// POST API returns result, but does not include the attributes that are not set. Make a spearate GET call to get all attributes. +func CreateStorageFlexcache(errorHandler *utils.ErrorHandler, r restclient.RestClient, data StorageFlexcacheResourceModel) error { + var body map[string]interface{} + if err := mapstructure.Decode(data, &body); err != nil { + return errorHandler.MakeAndReportError("error encoding flexcache body", fmt.Sprintf("error on encoding storage/flexcache/flexcaches body: %s, body: %#v", err, data)) + } + //The use-tiered-aggregate option is only supported when auto provisioning the FlexCache volume + if _, ok := body["aggregates"]; ok { + delete(body, "use_tiered_aggregate") + } + query := r.NewQuery() + query.Add("return_records", "false") + statusCode, _, err := r.CallCreateMethod("storage/flexcache/flexcaches", query, body) + if err != nil { + return errorHandler.MakeAndReportError("error creating flexcache", fmt.Sprintf("error on POST storage/flexcache/flexcaches: %s, statusCode %d", err, statusCode)) + } + + return nil + +} + +func DeleteStorageFlexcache(errorHandler *utils.ErrorHandler, r restclient.RestClient, id string) error { + statusCode, _, err := r.CallDeleteMethod("storage/flexcache/flexcaches/"+id, nil, nil) + if err != nil { + return errorHandler.MakeAndReportError("error deleting flexcache", fmt.Sprintf("error on DELETE storage/flexcache/flexcaches: %s, statusCode %d", err, statusCode)) + } + return nil +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 28534d88..d799489e 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -158,6 +158,7 @@ func (p *ONTAPProvider) Resources(ctx context.Context) []func() resource.Resourc NewStorageVolumeResource, NewStorageVolumeSnapshotResource, NewSvmResource, + NewStorageFlexcacheRsource, } } @@ -202,6 +203,8 @@ func (p *ONTAPProvider) DataSources(ctx context.Context) []func() datasource.Dat NewStorageVolumesDataSource, NewSvmDataSource, NewSvmsDataSource, + NewFlexcacheDataSource, + NewFlexcachesDataSource, } } diff --git a/internal/provider/storage_flexcache_data_source.go b/internal/provider/storage_flexcache_data_source.go new file mode 100644 index 00000000..b51b68b6 --- /dev/null +++ b/internal/provider/storage_flexcache_data_source.go @@ -0,0 +1,333 @@ +package provider + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/netapp/terraform-provider-netapp-ontap/internal/interfaces" + "github.com/netapp/terraform-provider-netapp-ontap/internal/utils" +) + +// Ensure provider defined types fully satisfy framework interfaces +var _ datasource.DataSource = &StorageFlexcacheDataSource{} + +// NewFlexcacheDataSource is a helper function to simplify the provider implementation. +func NewFlexcacheDataSource() datasource.DataSource { + return &StorageFlexcacheDataSource{ + config: resourceOrDataSourceConfig{ + name: "storage_flexcache_data_source", + }, + } +} + +// AggregateResource defines the resource implementation. +type StorageFlexcacheDataSource struct { + config resourceOrDataSourceConfig +} + +// AggregateResourceModel describes the resource data model. +type StorageFlexcacheDataSourceModel struct { + CxProfileName types.String `tfsdk:"cx_profile_name"` + Name types.String `tfsdk:"name"` + SvmName types.String `tfsdk:"svm_name"` + Origins types.Set `tfsdk:"origins"` + JunctionPath types.String `tfsdk:"junction_path"` + Size types.Int64 `tfsdk:"size"` + SizeUnit types.String `tfsdk:"size_unit"` + ConstituentsPerAggregate types.Int64 `tfsdk:"constituents_per_aggregate"` + DrCache types.Bool `tfsdk:"dr_cache"` + Guarantee types.Object `tfsdk:"guarantee"` + GlobalFileLockingEnabled types.Bool `tfsdk:"global_file_locking_enabled"` + Prepopulate types.Object `tfsdk:"prepopulate"` + UseTieredAggregate types.Bool `tfsdk:"use_tiered_aggregate"` + Aggregates types.Set `tfsdk:"aggregates"` + ID types.String `tfsdk:"id"` +} + +// Metadata returns the resource type name. +func (r *StorageFlexcacheDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_" + r.config.name +} + +// Schema defines the schema for the resource. +func (r *StorageFlexcacheDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + // This description is used by the documentation generator and the language server. + MarkdownDescription: "Flexcache resource", + + Attributes: map[string]schema.Attribute{ + "cx_profile_name": schema.StringAttribute{ + MarkdownDescription: "Connection profile name", + Required: true, + }, + "name": schema.StringAttribute{ + MarkdownDescription: "The name of the flexcache volume", + Required: true, + }, + "svm_name": schema.StringAttribute{ + MarkdownDescription: "Name of the svm to use", + Required: true, + }, + "aggregates": schema.SetNestedAttribute{ + MarkdownDescription: "", + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + MarkdownDescription: "Name of the aggregate", + Computed: true, + }, + "id": schema.StringAttribute{ + MarkdownDescription: "UUID of the aggregate", + Computed: true, + }, + }, + }, + }, + "origins": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "volume": schema.SingleNestedAttribute{ + MarkdownDescription: "Origin volume", + Required: true, + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + MarkdownDescription: "Name of the origin volume", + Computed: true, + }, + "id": schema.StringAttribute{ + MarkdownDescription: "ID of the origin volume", + Computed: true, + }, + }, + }, + "svm": schema.SingleNestedAttribute{ + MarkdownDescription: "Origin volume SVM", + Required: true, + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + MarkdownDescription: "Name of the origin volume", + Computed: true, + }, + "id": schema.StringAttribute{ + MarkdownDescription: "ID of the origin volume", + Computed: true, + }, + }, + }, + }, + }, + MarkdownDescription: "Set of the origin volumes", + Computed: true, + }, + "junction_path": schema.StringAttribute{ + MarkdownDescription: "Name of the junction path", + Computed: true, + }, + "size": schema.Int64Attribute{ + MarkdownDescription: "The size of the flexcache volume", + Computed: true, + }, + "size_unit": schema.StringAttribute{ + MarkdownDescription: "The unit used to interpret the size parameter", + Computed: true, + }, + "constituents_per_aggregate": schema.Int64Attribute{ + MarkdownDescription: "The number of constituents per aggregate", + Computed: true, + }, + "dr_cache": schema.BoolAttribute{ + MarkdownDescription: "The state of the dr cache", + Computed: true, + }, + "guarantee": schema.SingleNestedAttribute{ + MarkdownDescription: "The guarantee of the volume", + Computed: true, + Attributes: map[string]schema.Attribute{ + "type": schema.StringAttribute{ + MarkdownDescription: "The type of guarantee", + Computed: true, + }, + }, + }, + "global_file_locking_enabled": schema.BoolAttribute{ + MarkdownDescription: "The state of the global file locking", + Computed: true, + }, + "use_tiered_aggregate": schema.BoolAttribute{ + MarkdownDescription: "The state of the use tiered aggregates", + Computed: true, + }, + "id": schema.StringAttribute{ + MarkdownDescription: "The UUID of the flexcache volume", + Computed: true, + }, + }, + } +} + +// Configure adds the provider configured client to the resource. +func (r *StorageFlexcacheDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + config, ok := req.ProviderData.(Config) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected Config, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + } + r.config.providerConfig = config +} + +// Read refreshes the Terraform state with the latest data. +func (d *StorageFlexcacheDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data StorageFlexcacheDataSourceModel + + // Read Terraform configuration data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + errorHandler := utils.NewErrorHandler(ctx, &resp.Diagnostics) + // we need to defer setting the client until we can read the connection profile name + client, err := getRestClient(errorHandler, d.config, data.CxProfileName) + if err != nil { + // error reporting done inside NewClient + return + } + flexcache, err := interfaces.GetStorageFlexcacheByName(errorHandler, *client, data.Name.ValueString(), data.SvmName.ValueString()) + if err != nil { + return + } + if flexcache == nil { + errorHandler.MakeAndReportError("No flexcache found", fmt.Sprintf("Flexcache %s not found.", data.Name)) + return + } + + size, size_unit := interfaces.ByteFormat(int64(flexcache.Size)) + data.Size = types.Int64Value(int64(size)) + data.SizeUnit = types.StringValue(size_unit) + data.JunctionPath = types.StringValue(flexcache.JunctionPath) + data.ConstituentsPerAggregate = types.Int64Value(int64(flexcache.ConstituentsPerAggregate)) + data.DrCache = types.BoolValue(flexcache.DrCache) + data.GlobalFileLockingEnabled = types.BoolValue(flexcache.GlobalFileLockingEnabled) + data.UseTieredAggregate = types.BoolValue(flexcache.UseTieredAggregate) + + elementTypes := map[string]attr.Type{ + "type": types.StringType, + } + elements := map[string]attr.Value{ + "type": types.StringValue(flexcache.Guarantee.Type), + } + objectValue, diags := types.ObjectValue(elementTypes, elements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + data.Guarantee = objectValue + + //Origins + setElements := []attr.Value{} + for _, origin := range flexcache.Origins { + nestedElementTypes := map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + } + nestedVolumeElements := map[string]attr.Value{ + "name": types.StringValue(origin.Volume.Name), + "id": types.StringValue(origin.Volume.ID), + } + nestedSVMElements := map[string]attr.Value{ + "name": types.StringValue(origin.SVM.Name), + "id": types.StringValue(origin.SVM.ID), + } + originVolumeObjectValue, diags := types.ObjectValue(nestedElementTypes, nestedVolumeElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + originSVMObjectValue, _ := types.ObjectValue(nestedElementTypes, nestedSVMElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + + elementTypes := map[string]attr.Type{ + "volume": types.ObjectType{AttrTypes: nestedElementTypes}, + "svm": types.ObjectType{AttrTypes: nestedElementTypes}, + } + elements := map[string]attr.Value{ + "volume": originVolumeObjectValue, + "svm": originSVMObjectValue, + } + objectValue, diags := types.ObjectValue(elementTypes, elements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + setElements = append(setElements, objectValue) + } + + setValue, diags := types.SetValue(types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "volume": types.ObjectType{AttrTypes: map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + }}, + "svm": types.ObjectType{AttrTypes: map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + }}, + }, + }, setElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + + data.Origins = setValue + + //aggregate + setElements = []attr.Value{} + for _, aggregate := range flexcache.Aggregates { + nestedElementTypes := map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + } + nestedElements := map[string]attr.Value{ + "name": types.StringValue(aggregate.Name), + "id": types.StringValue(aggregate.ID), + } + objectValue, diags := types.ObjectValue(nestedElementTypes, nestedElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + setElements = append(setElements, objectValue) + } + setValue, diags = types.SetValue(types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + }, + }, setElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + data.Aggregates = setValue + data.ID = types.StringValue(flexcache.UUID) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + +} diff --git a/internal/provider/storage_flexcache_resource.go b/internal/provider/storage_flexcache_resource.go new file mode 100644 index 00000000..db6ea9ec --- /dev/null +++ b/internal/provider/storage_flexcache_resource.go @@ -0,0 +1,695 @@ +package provider + +import ( + "context" + "fmt" + "log" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/mitchellh/mapstructure" + "github.com/netapp/terraform-provider-netapp-ontap/internal/interfaces" + "github.com/netapp/terraform-provider-netapp-ontap/internal/utils" +) + +// Ensure provider defined types fully satisfy framework interfaces +var _ resource.Resource = &StorageFlexcacheResource{} + +// NewStorageFlexcacheRsource is a helper function to simplify the provider implementation. +func NewStorageFlexcacheRsource() resource.Resource { + return &StorageFlexcacheResource{ + config: resourceOrDataSourceConfig{ + name: "storage_flexcache_resource", + }, + } +} + +// StorageFlexcacheResource defines the resource implementation. +type StorageFlexcacheResource struct { + config resourceOrDataSourceConfig +} + +// StorageFlexcacheResourceModel describes the resource data model. +type StorageFlexcacheResourceModel struct { + CxProfileName types.String `tfsdk:"cx_profile_name"` + Name types.String `tfsdk:"name"` + SvmName types.String `tfsdk:"svm_name"` + Origins types.Set `tfsdk:"origins"` + JunctionPath types.String `tfsdk:"junction_path"` + Size types.Int64 `tfsdk:"size"` + SizeUnit types.String `tfsdk:"size_unit"` + ConstituentsPerAggregate types.Int64 `tfsdk:"constituents_per_aggregate"` + DrCache types.Bool `tfsdk:"dr_cache"` + Guarantee types.Object `tfsdk:"guarantee"` + GlobalFileLockingEnabled types.Bool `tfsdk:"global_file_locking_enabled"` + UseTieredAggregate types.Bool `tfsdk:"use_tiered_aggregate"` + ID types.String `tfsdk:"id"` + Aggregates types.Set `tfsdk:"aggregates"` +} + +type StorageFlexCacheResourceOrigin struct { + Volume types.Object `tfsdk:"volume"` + SVM types.Object `tfsdk:"svm"` +} + +type StorageFlexCacheResourceOriginVolume struct { + Name types.String `tfsdk:"name"` + ID types.String `tfsdk:"id"` +} + +type StorageFlexCacheResourceOriginSVM struct { + Name types.String `tfsdk:"name"` + ID types.String `tfsdk:"id"` +} + +type StorageFlexCacheResourceOriginAggregate struct { + Name types.String `tfsdk:"name"` + ID types.String `tfsdk:"id"` +} + +type StorageFlexCacheGuarantee struct { + GuaranteeType types.String `tfsdk:"type"` +} + +type StorageFlexCachePrepopulate struct { + DirPaths types.List `tfsdk:"dir_paths"` + ExcludeDirPaths types.List `tfsdk:"exclude_dir_paths"` + Recurse types.Bool `tfsdk:"recurse"` +} + +// Metadata returns the resource type name. +func (r *StorageFlexcacheResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_" + r.config.name +} + +// Configure adds the provider configured client to the resource. +func (r *StorageFlexcacheResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + config, ok := req.ProviderData.(Config) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected Config, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + } + r.config.providerConfig = config +} + +// Schema defines the schema for the resource. +func (r *StorageFlexcacheResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + // This description is used by the documentation generator and the language server. + MarkdownDescription: "Flexcache resource", + + Attributes: map[string]schema.Attribute{ + "cx_profile_name": schema.StringAttribute{ + MarkdownDescription: "Connection profile name", + Required: true, + }, + "name": schema.StringAttribute{ + MarkdownDescription: "The name of the flexcache volume to manage", + Required: true, + }, + "svm_name": schema.StringAttribute{ + MarkdownDescription: "Name of the svm to use", + Required: true, + }, + //there could be a space not enough or storage type error if the aggreates are not set + "aggregates": schema.SetNestedAttribute{ + MarkdownDescription: "Set of the aggregates to use", + Optional: true, + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + MarkdownDescription: "Name of the aggregate", + Optional: true, + Computed: true, + }, + "id": schema.StringAttribute{ + MarkdownDescription: "UUID of the aggregate", + Optional: true, + Computed: true, + }, + }, + }, + }, + "origins": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "volume": schema.SingleNestedAttribute{ + MarkdownDescription: "origin volume", + Required: true, + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + MarkdownDescription: "Name of the origin volume", + Optional: true, + Computed: true, + }, + "id": schema.StringAttribute{ + MarkdownDescription: "ID of the origin volume", + Optional: true, + Computed: true, + }, + }, + }, + "svm": schema.SingleNestedAttribute{ + MarkdownDescription: "origin volume SVM", + Required: true, + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + MarkdownDescription: "Name of the origin volume SVM", + Optional: true, + Computed: true, + }, + "id": schema.StringAttribute{ + MarkdownDescription: "ID of the origin volume SVM", + Optional: true, + Computed: true, + }, + }, + }, + }, + }, + MarkdownDescription: "Set of the origin volumes", + Required: true, + }, + "junction_path": schema.StringAttribute{ + MarkdownDescription: "Name of the junction path", + Computed: true, + Optional: true, + }, + "size": schema.Int64Attribute{ + MarkdownDescription: "The size of the flexcache volume", + Computed: true, + Optional: true, + }, + "size_unit": schema.StringAttribute{ + MarkdownDescription: "The unit used to interpret the size parameter", + Computed: true, + Optional: true, + }, + "constituents_per_aggregate": schema.Int64Attribute{ + MarkdownDescription: "The number of constituents per aggregate", + Computed: true, + Optional: true, + }, + "dr_cache": schema.BoolAttribute{ + MarkdownDescription: "The state of the dr cache", + Computed: true, + Optional: true, + }, + "guarantee": schema.SingleNestedAttribute{ + MarkdownDescription: "The guarantee of the volume", + Computed: true, + Optional: true, + Attributes: map[string]schema.Attribute{ + "type": schema.StringAttribute{ + MarkdownDescription: "The type of guarantee", + Computed: true, + Optional: true, + }, + }, + }, + "global_file_locking_enabled": schema.BoolAttribute{ + MarkdownDescription: "The state of the global file locking", + Computed: true, + Optional: true, + }, + "use_tiered_aggregate": schema.BoolAttribute{ + MarkdownDescription: "The state of the use tiered aggregates", + Computed: true, + Optional: true, + }, + "id": schema.StringAttribute{ + MarkdownDescription: "The ID of the volume", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + }, + } +} + +// Read refreshes the Terraform state with the latest data. +func (d *StorageFlexcacheResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data StorageFlexcacheResourceModel + + // Read Terraform configuration data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + errorHandler := utils.NewErrorHandler(ctx, &resp.Diagnostics) + // we need to defer setting the client until we can read the connection profile name + client, err := getRestClient(errorHandler, d.config, data.CxProfileName) + if err != nil { + // error reporting done inside NewClient + return + } + + flexcache, err := interfaces.GetStorageFlexcacheByName(errorHandler, *client, data.Name.ValueString(), data.SvmName.ValueString()) + if err != nil { + return + } + if flexcache == nil { + errorHandler.MakeAndReportError("No flexcahce found", fmt.Sprintf("Flexcache %s not found.", data.Name)) + return + } + + size, size_unit := interfaces.ByteFormat(int64(flexcache.Size)) + data.Size = types.Int64Value(int64(size)) + data.SizeUnit = types.StringValue(size_unit) + data.JunctionPath = types.StringValue(flexcache.JunctionPath) + data.ConstituentsPerAggregate = types.Int64Value(int64(flexcache.ConstituentsPerAggregate)) + data.DrCache = types.BoolValue(flexcache.DrCache) + data.GlobalFileLockingEnabled = types.BoolValue(flexcache.GlobalFileLockingEnabled) + data.UseTieredAggregate = types.BoolValue(flexcache.UseTieredAggregate) + + elementTypes := map[string]attr.Type{ + "type": types.StringType, + } + elements := map[string]attr.Value{ + "type": types.StringValue(flexcache.Guarantee.Type), + } + objectValue, diags := types.ObjectValue(elementTypes, elements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + data.Guarantee = objectValue + + setElements := []attr.Value{} + for _, origin := range flexcache.Origins { + nestedElementTypes := map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + } + nestedVolumeElements := map[string]attr.Value{ + "name": types.StringValue(origin.Volume.Name), + "id": types.StringValue(origin.Volume.ID), + } + nestedSVMElements := map[string]attr.Value{ + "name": types.StringValue(origin.SVM.Name), + "id": types.StringValue(origin.SVM.ID), + } + originVolumeObjectValue, diags := types.ObjectValue(nestedElementTypes, nestedVolumeElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + originSVMObjectValue, _ := types.ObjectValue(nestedElementTypes, nestedSVMElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + + elementTypes := map[string]attr.Type{ + "volume": types.ObjectType{AttrTypes: nestedElementTypes}, + "svm": types.ObjectType{AttrTypes: nestedElementTypes}, + } + elements := map[string]attr.Value{ + "volume": originVolumeObjectValue, + "svm": originSVMObjectValue, + } + objectValue, diags := types.ObjectValue(elementTypes, elements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + setElements = append(setElements, objectValue) + } + + setValue, diags := types.SetValue(types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "volume": types.ObjectType{AttrTypes: map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + }}, + "svm": types.ObjectType{AttrTypes: map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + }}, + }, + }, setElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + + data.Origins = setValue + + setElements = []attr.Value{} + for _, aggregate := range flexcache.Aggregates { + nestedElementTypes := map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + } + nestedElements := map[string]attr.Value{ + "name": types.StringValue(aggregate.Name), + "id": types.StringValue(aggregate.ID), + } + objectValue, diags := types.ObjectValue(nestedElementTypes, nestedElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + setElements = append(setElements, objectValue) + } + setValue, diags = types.SetValue(types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + }, + }, setElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + data.Aggregates = setValue + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + +} + +func (r *StorageFlexcacheResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data *StorageFlexcacheResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + errorHandler := utils.NewErrorHandler(ctx, &resp.Diagnostics) + client, err := getRestClient(errorHandler, r.config, data.CxProfileName) + if err != nil { + // error reporting done inside NewClient + return + } + var request interfaces.StorageFlexcacheResourceModel + if _, ok := interfaces.POW2BYTEMAP[data.SizeUnit.ValueString()]; !ok { + errorHandler.MakeAndReportError("error creating flexcache", fmt.Sprintf("invalid input for size_unit: %s, required one of: bytes, b, kb, mb, gb, tb, pb, eb, zb, yb", data.SizeUnit.ValueString())) + return + } + + request.Size = int(data.Size.ValueInt64()) * interfaces.POW2BYTEMAP[data.SizeUnit.ValueString()] + request.Name = data.Name.ValueString() + request.SVM.Name = data.SvmName.ValueString() + if !data.JunctionPath.IsUnknown() { + request.JunctionPath = data.JunctionPath.ValueString() + } + if !data.ConstituentsPerAggregate.IsUnknown() { + request.ConstituentsPerAggregate = int(data.ConstituentsPerAggregate.ValueInt64()) + } + if !data.DrCache.IsUnknown() { + request.DrCache = data.DrCache.ValueBool() + } + if !data.GlobalFileLockingEnabled.IsUnknown() { + request.GlobalFileLockingEnabled = data.GlobalFileLockingEnabled.ValueBool() + } + if !data.UseTieredAggregate.IsUnknown() { + request.UseTieredAggregate = data.UseTieredAggregate.ValueBool() + } + if !data.Guarantee.IsUnknown() { + var Guarantee StorageFlexCacheGuarantee + diags := data.Guarantee.As(ctx, &Guarantee, basetypes.ObjectAsOptions{}) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + request.Guarantee.Type = Guarantee.GuaranteeType.ValueString() + } + if !data.Origins.IsUnknown() { + origins := []interfaces.StorageFlexcacheOrigin{} + + elements := make([]types.Object, 0, len(data.Origins.Elements())) + diags := data.Origins.ElementsAs(ctx, &elements, false) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + for _, v := range elements { + var origin StorageFlexCacheResourceOrigin + diags := v.As(ctx, &origin, basetypes.ObjectAsOptions{}) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + interfaceOrigin := interfaces.StorageFlexcacheOrigin{} + if !origin.Volume.IsUnknown() { + var volume StorageFlexCacheResourceOriginVolume + diags := origin.Volume.As(ctx, &volume, basetypes.ObjectAsOptions{}) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + interfaceVolume := interfaces.StorageFlexcacheVolume{} + if !volume.Name.IsUnknown() { + interfaceVolume.Name = volume.Name.ValueString() + + } + if !volume.ID.IsUnknown() { + interfaceVolume.ID = volume.ID.ValueString() + } + interfaceOrigin.Volume = interfaceVolume + } + if !origin.SVM.IsUnknown() { + var svm StorageFlexCacheResourceOriginSVM + diags := origin.SVM.As(ctx, &svm, basetypes.ObjectAsOptions{}) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + interfaceSVM := interfaces.StorageFlexcacheSVM{} + if !svm.Name.IsUnknown() { + + interfaceSVM.Name = svm.Name.ValueString() + } + if !svm.ID.IsUnknown() { + + interfaceSVM.ID = svm.ID.ValueString() + } + interfaceOrigin.SVM = interfaceSVM + } + + origins = append(origins, interfaceOrigin) + + } + + err := mapstructure.Decode(origins, &request.Origins) + if err != nil { + errorHandler.MakeAndReportError("error creating flexcache", fmt.Sprintf("error on encoding copies info: %s, copies %#v", err, origins)) + return + } + } + + if !data.Aggregates.IsUnknown() { + aggregates := []interfaces.StorageFlexcacheAggregate{} + + elements := make([]types.Object, 0, len(data.Aggregates.Elements())) + diags := data.Aggregates.ElementsAs(ctx, &elements, false) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + for _, v := range elements { + var aggregate StorageFlexCacheResourceOriginAggregate + diags := v.As(ctx, &aggregate, basetypes.ObjectAsOptions{}) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + interfaceOriginAggregate := interfaces.StorageFlexcacheAggregate{} + if !aggregate.Name.IsUnknown() { + interfaceOriginAggregate.Name = aggregate.Name.ValueString() + } + if !aggregate.ID.IsUnknown() { + interfaceOriginAggregate.ID = aggregate.ID.ValueString() + } + aggregates = append(aggregates, interfaceOriginAggregate) + + } + + err := mapstructure.Decode(aggregates, &request.Aggregates) + if err != nil { + errorHandler.MakeAndReportError("error creating flexcache", fmt.Sprintf("error on encoding copies info: %s, copies %#v", err, aggregates)) + return + } + } + + err = interfaces.CreateStorageFlexcache(errorHandler, *client, request) + if err != nil { + return + } + + flexcache, err := interfaces.GetStorageFlexcacheByName(errorHandler, *client, data.Name.ValueString(), data.SvmName.ValueString()) + if err != nil { + return + } + if flexcache == nil { + errorHandler.MakeAndReportError("No flexcache found", fmt.Sprintf("flexcache %s not found.", data.Name)) + return + } + size, size_unit := interfaces.ByteFormat(int64(flexcache.Size)) + data.Size = types.Int64Value(int64(size)) + data.SizeUnit = types.StringValue(size_unit) + data.JunctionPath = types.StringValue(flexcache.JunctionPath) + data.ConstituentsPerAggregate = types.Int64Value(int64(flexcache.ConstituentsPerAggregate)) + data.DrCache = types.BoolValue(flexcache.DrCache) + data.GlobalFileLockingEnabled = types.BoolValue(flexcache.GlobalFileLockingEnabled) + data.UseTieredAggregate = types.BoolValue(flexcache.UseTieredAggregate) + data.ID = types.StringValue(flexcache.UUID) + + elementTypes := map[string]attr.Type{ + "type": types.StringType, + } + elements := map[string]attr.Value{ + "type": types.StringValue(flexcache.Guarantee.Type), + } + objectValue, diags := types.ObjectValue(elementTypes, elements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + data.Guarantee = objectValue + + setElements := []attr.Value{} + for _, origin := range flexcache.Origins { + nestedElementTypes := map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + } + nestedVolumeElements := map[string]attr.Value{ + "name": types.StringValue(origin.Volume.Name), + "id": types.StringValue(origin.Volume.ID), + } + nestedSVMElements := map[string]attr.Value{ + "name": types.StringValue(origin.SVM.Name), + "id": types.StringValue(origin.SVM.ID), + } + originVolumeObjectValue, diags := types.ObjectValue(nestedElementTypes, nestedVolumeElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + originSVMObjectValue, _ := types.ObjectValue(nestedElementTypes, nestedSVMElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + + elementTypes := map[string]attr.Type{ + "volume": types.ObjectType{AttrTypes: nestedElementTypes}, + "svm": types.ObjectType{AttrTypes: nestedElementTypes}, + } + elements := map[string]attr.Value{ + "volume": originVolumeObjectValue, + "svm": originSVMObjectValue, + } + objectValue, diags := types.ObjectValue(elementTypes, elements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + log.Printf("objectValue is: %#v", objectValue) + setElements = append(setElements, objectValue) + } + + setValue, diags := types.SetValue(types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "volume": types.ObjectType{AttrTypes: map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + }}, + "svm": types.ObjectType{AttrTypes: map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + }}, + }, + }, setElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + + data.Origins = setValue + + setElements = []attr.Value{} + for _, aggregate := range flexcache.Aggregates { + nestedElementTypes := map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + } + nestedElements := map[string]attr.Value{ + "name": types.StringValue(aggregate.Name), + "id": types.StringValue(aggregate.ID), + } + objectValue, diags := types.ObjectValue(nestedElementTypes, nestedElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + setElements = append(setElements, objectValue) + } + setValue, diags = types.SetValue(types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + }, + }, setElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + data.Aggregates = setValue + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + +} + +func (r *StorageFlexcacheResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data *StorageFlexcacheResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + errorHandler := utils.NewErrorHandler(ctx, &resp.Diagnostics) + client, err := getRestClient(errorHandler, r.config, data.CxProfileName) + if err != nil { + // error reporting done inside NewClient + return + } + + if data.ID.IsUnknown() { + errorHandler.MakeAndReportError("UUID is null", "flexcache UUID is null") + return + } + + err = interfaces.DeleteStorageFlexcache(errorHandler, *client, data.ID.ValueString()) + if err != nil { + return + } +} + +// If not specified in PATCH, prepopulate.recurse is default to true. +// prepopulate.dir_paths is requried. +func (r *StorageFlexcacheResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + + errorHandler := utils.NewErrorHandler(ctx, &resp.Diagnostics) + errorHandler.MakeAndReportError("Update not available", "No update can be done on flexcache resource.") + +} diff --git a/internal/provider/storage_flexcache_resource_test.go b/internal/provider/storage_flexcache_resource_test.go new file mode 100644 index 00000000..90fb3df8 --- /dev/null +++ b/internal/provider/storage_flexcache_resource_test.go @@ -0,0 +1,94 @@ +package provider + +import ( + "fmt" + "os" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccStorageFlexcacheResource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Test non existant SVM + { + Config: testAccStorageFlexcacheResourceConfig("non-existant", "terraformTest4"), + ExpectError: regexp.MustCompile("2621462"), + }, + // test bad volume name + { + Config: testAccStorageFlexcacheResourceConfig("non-existant", "name-cant-have-dashes"), + ExpectError: regexp.MustCompile("917888"), + }, + // Read testing + { + Config: testAccStorageFlexcacheResourceConfig("automation", "accFlexcache"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("netapp-ontap_storage_flexcache_resource.example", "name", "accFlexcache"), + resource.TestCheckNoResourceAttr("netapp-ontap_storage_flexcache_resource.example", "volname"), + ), + }, + }, + }) +} + +func testAccStorageFlexcacheResourceConfig(svm, volName string) string { + if host == "" || admin == "" || password == "" { + // host = os.Getenv("TF_ACC_NETAPP_HOST2") + // admin = os.Getenv("TF_ACC_NETAPP_USER") + // password = os.Getenv("TF_ACC_NETAPP_PASS") + host = "10.193.176.186" + admin = "admin" + password = "netapp1!" + + } + if host == "" || admin == "" || password == "" { + fmt.Println("TF_ACC_NETAPP_HOST, TF_ACC_NETAPP_USER, and TF_ACC_NETAPP_PASS must be set for acceptance tests") + os.Exit(1) + } + return fmt.Sprintf(` +provider "netapp-ontap" { + connection_profiles = [ + { + name = "cluster5" + hostname = "%s" + username = "%s" + password = "%s" + validate_certs = false + }, + ] +} + +resource "netapp-ontap_storage_flexcache_resource" "example" { + cx_profile_name = "cluster5" + name = "%s" + svm_name = "%s" + + origins = [ + { + volume = { + name = "vol1" + }, + svm = { + name = "automation" + } + } + ] + size = 400 + size_unit = "mb" + guarantee = { + type = "none" + } + dr_cache = false + global_file_locking_enabled = false + aggregates = [ + { + name = "aggr1" + } + ] +}`, host, admin, password, volName, svm) +} diff --git a/internal/provider/storage_flexcaches_data_source.go b/internal/provider/storage_flexcaches_data_source.go new file mode 100644 index 00000000..bee87b30 --- /dev/null +++ b/internal/provider/storage_flexcaches_data_source.go @@ -0,0 +1,356 @@ +package provider + +import ( + "context" + "fmt" + "log" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/netapp/terraform-provider-netapp-ontap/internal/interfaces" + "github.com/netapp/terraform-provider-netapp-ontap/internal/utils" +) + +// Ensure provider defined types fully satisfy framework interfaces +var _ datasource.DataSource = &StorageFlexcachesDataSource{} + +// NewFlexcacheDataSource is a helper function to simplify the provider implementation. +func NewFlexcachesDataSource() datasource.DataSource { + return &StorageFlexcacheDataSource{ + config: resourceOrDataSourceConfig{ + name: "storage_flexcaches_data_source", + }, + } +} + +// AggregateResource defines the resource implementation. +type StorageFlexcachesDataSource struct { + config resourceOrDataSourceConfig +} + +// AggregateResourceModel describes the resource data model. +type StorageFlexcachesDataSourceModel struct { + CxProfileName types.String `tfsdk:"cx_profile_name"` + StorageFlexcaches []StorageFlexcacheDataSourceModel `tfsdk:"storage_flexcaches"` + Filter *StorageFlexcacheDataSourceFilterModel `tfsdk:"filter"` +} + +type StorageFlexcacheDataSourceFilterModel struct { + Name types.String `tfsdk:"name"` + SVMName types.String `tfsdk:"svm_name"` +} + +// Metadata returns the resource type name. +func (r *StorageFlexcachesDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_" + r.config.name +} + +// Schema defines the schema for the resource. +func (r *StorageFlexcachesDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + // This description is used by the documentation generator and the language server. + MarkdownDescription: "Flexcache resource", + + Attributes: map[string]schema.Attribute{ + "cx_profile_name": schema.StringAttribute{ + MarkdownDescription: "Connection profile name", + Required: true, + }, + "filter": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + MarkdownDescription: "StorageFlexcache name", + Optional: true, + }, + "svm_name": schema.StringAttribute{ + MarkdownDescription: "StorageFlexcache svm name", + Optional: true, + }, + }, + Optional: true, + }, + "storage_flexcaches": schema.ListNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "cx_profile_name": schema.StringAttribute{ + MarkdownDescription: "Connection profile name", + Required: true, + }, + "name": schema.StringAttribute{ + MarkdownDescription: "The name of the flexcache volume to manage", + Required: true, + }, + "svm_name": schema.StringAttribute{ + MarkdownDescription: "Name of the svm to use", + Required: true, + }, + "aggregates": schema.SetNestedAttribute{ + MarkdownDescription: "", + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + MarkdownDescription: "Name of the aggregate", + Computed: true, + }, + "id": schema.StringAttribute{ + MarkdownDescription: "ID of the aggregate", + Computed: true, + }, + }, + }, + }, + "origins": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "volume": schema.SingleNestedAttribute{ + MarkdownDescription: "Origin volume", + Required: true, + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + MarkdownDescription: "Name of the origin volume", + Computed: true, + }, + "id": schema.StringAttribute{ + MarkdownDescription: "ID of the origin volume", + Computed: true, + }, + }, + }, + "svm": schema.SingleNestedAttribute{ + MarkdownDescription: "Origin volume SVM", + Required: true, + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + MarkdownDescription: "Name of the origin volume SVM", + Computed: true, + }, + "id": schema.StringAttribute{ + MarkdownDescription: "ID of the origin volume SVM", + Computed: true, + }, + }, + }, + }, + }, + MarkdownDescription: "Set of the origin volumes", + Computed: true, + }, + "junction_path": schema.StringAttribute{ + MarkdownDescription: "Name of the junction path", + Computed: true, + }, + "size": schema.Int64Attribute{ + MarkdownDescription: "The size of the flexcache volume", + Computed: true, + }, + "size_unit": schema.StringAttribute{ + MarkdownDescription: "The unit used to interpret the size parameter", + Computed: true, + }, + "constituents_per_aggregate": schema.Int64Attribute{ + MarkdownDescription: "The number of constituents per aggregate", + Computed: true, + }, + "dr_cache": schema.BoolAttribute{ + MarkdownDescription: "The state of the dr cache", + Computed: true, + }, + "guarantee": schema.SingleNestedAttribute{ + MarkdownDescription: "The guarantee of the volume", + Computed: true, + Attributes: map[string]schema.Attribute{ + "type": schema.StringAttribute{ + MarkdownDescription: "The type of the guarantee", + Computed: true, + }, + }, + }, + "global_file_locking_enabled": schema.BoolAttribute{ + MarkdownDescription: "The state of the global file locking", + Computed: true, + }, + "use_tiered_aggregate": schema.BoolAttribute{ + MarkdownDescription: "The state of the use tiered aggregates", + Computed: true, + }, + "id": schema.StringAttribute{ + MarkdownDescription: "The UUID of the flexcache volume", + Computed: true, + }, + }, + }, + }, + }, + } +} + +// Configure adds the provider configured client to the resource. +func (r *StorageFlexcachesDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + config, ok := req.ProviderData.(Config) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected Config, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + } + r.config.providerConfig = config +} + +// Read refreshes the Terraform state with the latest data. +func (d *StorageFlexcachesDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data StorageFlexcachesDataSourceModel + + // Read Terraform configuration data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + errorHandler := utils.NewErrorHandler(ctx, &resp.Diagnostics) + // we need to defer setting the client until we can read the connection profile name + client, err := getRestClient(errorHandler, d.config, data.CxProfileName) + if err != nil { + // error reporting done inside NewClient + return + } + var filter *interfaces.StorageFlexcacheDataSourceFilterModel = nil + if data.Filter != nil { + filter = &interfaces.StorageFlexcacheDataSourceFilterModel{ + Name: data.Filter.Name.ValueString(), + SVMName: data.Filter.SVMName.ValueString(), + } + } + + restInfo, err := interfaces.GetStorageFlexcaches(errorHandler, *client, filter) + if err != nil { + return + } + data.StorageFlexcaches = make([]StorageFlexcacheDataSourceModel, len(restInfo)) + for index, record := range restInfo { + + vsize, vunits := interfaces.ByteFormat(int64(record.Size)) + + data.StorageFlexcaches[index] = StorageFlexcacheDataSourceModel{} + data.StorageFlexcaches[index].CxProfileName = data.CxProfileName + data.StorageFlexcaches[index].Name = types.StringValue(record.Name) + data.StorageFlexcaches[index].SvmName = types.StringValue(record.SVM.Name) + data.StorageFlexcaches[index].Size = types.Int64Value(int64(vsize)) + data.StorageFlexcaches[index].SizeUnit = types.StringValue(vunits) + data.StorageFlexcaches[index].JunctionPath = types.StringValue(record.JunctionPath) + data.StorageFlexcaches[index].ConstituentsPerAggregate = types.Int64Value(int64(record.ConstituentsPerAggregate)) + data.StorageFlexcaches[index].DrCache = types.BoolValue(record.DrCache) + data.StorageFlexcaches[index].GlobalFileLockingEnabled = types.BoolValue(record.GlobalFileLockingEnabled) + data.StorageFlexcaches[index].UseTieredAggregate = types.BoolValue(record.UseTieredAggregate) + data.StorageFlexcaches[index].ID = types.StringValue(record.UUID) + + //origin + setElements := []attr.Value{} + for _, origin := range record.Origins { + nestedElementTypes := map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + } + nestedVolumeElements := map[string]attr.Value{ + "name": types.StringValue(origin.Volume.Name), + "id": types.StringValue(origin.Volume.ID), + } + nestedSVMElements := map[string]attr.Value{ + "name": types.StringValue(origin.SVM.Name), + "id": types.StringValue(origin.SVM.ID), + } + originVolumeObjectValue, diags := types.ObjectValue(nestedElementTypes, nestedVolumeElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + originSVMObjectValue, _ := types.ObjectValue(nestedElementTypes, nestedSVMElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + + elementTypes := map[string]attr.Type{ + "volume": types.ObjectType{AttrTypes: nestedElementTypes}, + "svm": types.ObjectType{AttrTypes: nestedElementTypes}, + } + elements := map[string]attr.Value{ + "volume": originVolumeObjectValue, + "svm": originSVMObjectValue, + } + objectValue, diags := types.ObjectValue(elementTypes, elements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + log.Printf("objectValue is: %#v", objectValue) + setElements = append(setElements, objectValue) + } + + setValue, diags := types.SetValue(types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "volume": types.ObjectType{AttrTypes: map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + }}, + "svm": types.ObjectType{AttrTypes: map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + }}, + }, + }, setElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + data.StorageFlexcaches[index].Origins = setValue + + //aggregate + setElements = []attr.Value{} + log.Printf("flexcache.Aggregates is: %#v", record.Aggregates) + for _, aggregate := range record.Aggregates { + nestedElementTypes := map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + } + nestedElements := map[string]attr.Value{ + "name": types.StringValue(aggregate.Name), + "id": types.StringValue(aggregate.ID), + } + objectValue, diags := types.ObjectValue(nestedElementTypes, nestedElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + setElements = append(setElements, objectValue) + } + setValue, diags = types.SetValue(types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "name": types.StringType, + "id": types.StringType, + }, + }, setElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + data.StorageFlexcaches[index].Aggregates = setValue + } + + // Write logs using the tflog package + // Documentation: https://terraform.io/plugin/log + tflog.Debug(ctx, fmt.Sprintf("read a data source: %#v", data)) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + +} diff --git a/internal/restclient/rest_client.go b/internal/restclient/rest_client.go index ec44dfdc..7e64d669 100644 --- a/internal/restclient/rest_client.go +++ b/internal/restclient/rest_client.go @@ -248,11 +248,18 @@ func (r *RestClient) Wait(uuid string) (int, RestResponse, error) { } else if job.State == "success" { return statusCode, RestResponse{}, nil } else { - if job.Error.Code != "" { - errorMessage := fmt.Errorf("fail to get job status. Error code: %s. Message: %s, Target: %s", job.Error.Code, job.Error.Message, job.Error.Target) - return statusCode, RestResponse{}, errorMessage + // if job struct ifself contains message and code, jobError struct might be empty. Vice versa. + if job.Error != (jobError{}) { + if job.Error.Code != "" { + errorMessage := fmt.Errorf("fail to get job status. Error code: %s. Message: %s, Target: %s", job.Error.Code, job.Error.Message, job.Error.Target) + return statusCode, RestResponse{}, errorMessage + } + return statusCode, RestResponse{}, fmt.Errorf("fail to get job status. Unknown error") + } else { + if job.Code != 0 { + return statusCode, RestResponse{}, fmt.Errorf("Job UUID %s failed. Error code: %d. Message: %s", uuid, job.Code, job.Message) + } } - return statusCode, RestResponse{}, fmt.Errorf("fail to get job status. Unknown error") } time.Sleep(10 * time.Second) } @@ -262,8 +269,10 @@ func (r *RestClient) Wait(uuid string) (int, RestResponse, error) { // Job is ONTAP API job data structure type Job struct { - State string - Error jobError + State string + Error jobError + Code int + Message string } type jobError struct { From 1f1ba5b331ac9c98c3786cf4fd95912a5ebb841b Mon Sep 17 00:00:00 2001 From: swenjun Date: Thu, 15 Feb 2024 15:42:31 -0800 Subject: [PATCH 02/23] fix build fails. --- internal/interfaces/storage_flexcache.go | 9 ++++++++- internal/provider/storage_flexcache_data_source.go | 12 ++++++------ internal/provider/storage_flexcache_resource.go | 14 ++++++++++---- .../provider/storage_flexcaches_data_source.go | 11 ++++++----- internal/restclient/rest_client.go | 7 +++---- 5 files changed, 33 insertions(+), 20 deletions(-) diff --git a/internal/interfaces/storage_flexcache.go b/internal/interfaces/storage_flexcache.go index efaad612..24e83625 100644 --- a/internal/interfaces/storage_flexcache.go +++ b/internal/interfaces/storage_flexcache.go @@ -26,6 +26,7 @@ type StorageFlexcacheGetDataModelONTAP struct { UUID string } +// StorageFlexcacheResourceModel describes the resource data model. type StorageFlexcacheResourceModel struct { Name string `mapstructure:"name,omitempty"` SVM svm `mapstructure:"svm,omitempty"` @@ -41,25 +42,30 @@ type StorageFlexcacheResourceModel struct { Aggregates []map[string]interface{} `mapstructure:"aggregates,omitempty"` } +// StorageFlexcacheGuarantee describes the guarantee data model of Guarantee within StorageFlexcacheResourceModel. type StorageFlexcacheGuarantee struct { Type string `mapstructure:"type,omitempty"` } +// StorageFlexcacheOrigin describes the origin data model of Origin within StorageFlexcacheResourceModel. type StorageFlexcacheOrigin struct { Volume StorageFlexcacheVolume `mapstructure:"volume"` SVM StorageFlexcacheSVM `mapstructure:"svm"` } +// StorageFlexcacheVolume describes the volume data model of Volume within StorageFlexcacheOrigin. type StorageFlexcacheVolume struct { Name string `mapstructure:"name,omitempty"` ID string `mapstructure:"uuid,omitempty"` } +// StorageFlexcacheSVM describes the svm data model of SVM within StorageFlexcacheOrigin. type StorageFlexcacheSVM struct { Name string `mapstructure:"name,omitempty"` ID string `mapstructure:"uuid,omitempty"` } +// StorageFlexcacheAggregate describes the aggregate data model of Aggregate within StorageFlexcacheResourceModel. type StorageFlexcacheAggregate struct { Name string `mapstructure:"name,omitempty"` ID string `mapstructure:"uuid,omitempty"` @@ -71,7 +77,7 @@ type StorageFlexcacheDataSourceFilterModel struct { SVMName string `mapstructure:"svm.name"` } -// GetStorageFlexcacheByName to get flexcache info by name +// GetStorageFlexcacheByName to get flexcache info by name. func GetStorageFlexcacheByName(errorHandler *utils.ErrorHandler, r restclient.RestClient, name string, svmName string) (*StorageFlexcacheGetDataModelONTAP, error) { query := r.NewQuery() query.Add("name", name) @@ -144,6 +150,7 @@ func CreateStorageFlexcache(errorHandler *utils.ErrorHandler, r restclient.RestC } +// DeleteStorageFlexcache to delete flexcache by id. func DeleteStorageFlexcache(errorHandler *utils.ErrorHandler, r restclient.RestClient, id string) error { statusCode, _, err := r.CallDeleteMethod("storage/flexcache/flexcaches/"+id, nil, nil) if err != nil { diff --git a/internal/provider/storage_flexcache_data_source.go b/internal/provider/storage_flexcache_data_source.go index b51b68b6..780eed98 100644 --- a/internal/provider/storage_flexcache_data_source.go +++ b/internal/provider/storage_flexcache_data_source.go @@ -24,12 +24,12 @@ func NewFlexcacheDataSource() datasource.DataSource { } } -// AggregateResource defines the resource implementation. +// StorageFlexcacheDataSource implements the datasource interface and defines the data model for the resource. type StorageFlexcacheDataSource struct { config resourceOrDataSourceConfig } -// AggregateResourceModel describes the resource data model. +// StorageFlexcacheDataSourceModel describes the resource data model. type StorageFlexcacheDataSourceModel struct { CxProfileName types.String `tfsdk:"cx_profile_name"` Name types.String `tfsdk:"name"` @@ -188,7 +188,7 @@ func (r *StorageFlexcacheDataSource) Configure(ctx context.Context, req datasour } // Read refreshes the Terraform state with the latest data. -func (d *StorageFlexcacheDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { +func (r *StorageFlexcacheDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { var data StorageFlexcacheDataSourceModel // Read Terraform configuration data into the model @@ -200,7 +200,7 @@ func (d *StorageFlexcacheDataSource) Read(ctx context.Context, req datasource.Re errorHandler := utils.NewErrorHandler(ctx, &resp.Diagnostics) // we need to defer setting the client until we can read the connection profile name - client, err := getRestClient(errorHandler, d.config, data.CxProfileName) + client, err := getRestClient(errorHandler, r.config, data.CxProfileName) if err != nil { // error reporting done inside NewClient return @@ -214,9 +214,9 @@ func (d *StorageFlexcacheDataSource) Read(ctx context.Context, req datasource.Re return } - size, size_unit := interfaces.ByteFormat(int64(flexcache.Size)) + size, sizeUnit := interfaces.ByteFormat(int64(flexcache.Size)) data.Size = types.Int64Value(int64(size)) - data.SizeUnit = types.StringValue(size_unit) + data.SizeUnit = types.StringValue(sizeUnit) data.JunctionPath = types.StringValue(flexcache.JunctionPath) data.ConstituentsPerAggregate = types.Int64Value(int64(flexcache.ConstituentsPerAggregate)) data.DrCache = types.BoolValue(flexcache.DrCache) diff --git a/internal/provider/storage_flexcache_resource.go b/internal/provider/storage_flexcache_resource.go index db6ea9ec..ab7b2e67 100644 --- a/internal/provider/storage_flexcache_resource.go +++ b/internal/provider/storage_flexcache_resource.go @@ -52,30 +52,36 @@ type StorageFlexcacheResourceModel struct { Aggregates types.Set `tfsdk:"aggregates"` } +// StorageFlexCacheResourceOrigin describes the origin data model of Origin within StorageFlexcacheResourceModel. type StorageFlexCacheResourceOrigin struct { Volume types.Object `tfsdk:"volume"` SVM types.Object `tfsdk:"svm"` } +// StorageFlexCacheResourceOriginVolume describes the volume data model of Volume within StorageFlexcacheOrigin. type StorageFlexCacheResourceOriginVolume struct { Name types.String `tfsdk:"name"` ID types.String `tfsdk:"id"` } +// StorageFlexCacheResourceOriginSVM describes the SVM data model of SVM within StorageFlexcacheOrigin. type StorageFlexCacheResourceOriginSVM struct { Name types.String `tfsdk:"name"` ID types.String `tfsdk:"id"` } +// StorageFlexCacheResourceOriginAggregate describes the aggregate data model of Aggregate within StorageFlexcacheResourceModel. type StorageFlexCacheResourceOriginAggregate struct { Name types.String `tfsdk:"name"` ID types.String `tfsdk:"id"` } +// StorageFlexCacheGuarantee describes the guarantee data model of Guarantee within StorageFlexcacheResourceModel. type StorageFlexCacheGuarantee struct { GuaranteeType types.String `tfsdk:"type"` } +// StorageFlexCachePrepopulate describes the prepopulate data model of Prepopulate within StorageFlexcacheResourceModel. type StorageFlexCachePrepopulate struct { DirPaths types.List `tfsdk:"dir_paths"` ExcludeDirPaths types.List `tfsdk:"exclude_dir_paths"` @@ -242,7 +248,7 @@ func (r *StorageFlexcacheResource) Schema(ctx context.Context, req resource.Sche } // Read refreshes the Terraform state with the latest data. -func (d *StorageFlexcacheResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { +func (r *StorageFlexcacheResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { var data StorageFlexcacheResourceModel // Read Terraform configuration data into the model @@ -254,7 +260,7 @@ func (d *StorageFlexcacheResource) Read(ctx context.Context, req resource.ReadRe errorHandler := utils.NewErrorHandler(ctx, &resp.Diagnostics) // we need to defer setting the client until we can read the connection profile name - client, err := getRestClient(errorHandler, d.config, data.CxProfileName) + client, err := getRestClient(errorHandler, r.config, data.CxProfileName) if err != nil { // error reporting done inside NewClient return @@ -269,9 +275,9 @@ func (d *StorageFlexcacheResource) Read(ctx context.Context, req resource.ReadRe return } - size, size_unit := interfaces.ByteFormat(int64(flexcache.Size)) + size, sizeUnit := interfaces.ByteFormat(int64(flexcache.Size)) data.Size = types.Int64Value(int64(size)) - data.SizeUnit = types.StringValue(size_unit) + data.SizeUnit = types.StringValue(sizeUnit) data.JunctionPath = types.StringValue(flexcache.JunctionPath) data.ConstituentsPerAggregate = types.Int64Value(int64(flexcache.ConstituentsPerAggregate)) data.DrCache = types.BoolValue(flexcache.DrCache) diff --git a/internal/provider/storage_flexcaches_data_source.go b/internal/provider/storage_flexcaches_data_source.go index bee87b30..d19a8215 100644 --- a/internal/provider/storage_flexcaches_data_source.go +++ b/internal/provider/storage_flexcaches_data_source.go @@ -17,7 +17,7 @@ import ( // Ensure provider defined types fully satisfy framework interfaces var _ datasource.DataSource = &StorageFlexcachesDataSource{} -// NewFlexcacheDataSource is a helper function to simplify the provider implementation. +// NewFlexcachesDataSource is a helper function to simplify the provider implementation. func NewFlexcachesDataSource() datasource.DataSource { return &StorageFlexcacheDataSource{ config: resourceOrDataSourceConfig{ @@ -26,18 +26,19 @@ func NewFlexcachesDataSource() datasource.DataSource { } } -// AggregateResource defines the resource implementation. +// StorageFlexcachesDataSource defines the resource implementation. type StorageFlexcachesDataSource struct { config resourceOrDataSourceConfig } -// AggregateResourceModel describes the resource data model. +// StorageFlexcachesDataSourceModel describes the resource data model. type StorageFlexcachesDataSourceModel struct { CxProfileName types.String `tfsdk:"cx_profile_name"` StorageFlexcaches []StorageFlexcacheDataSourceModel `tfsdk:"storage_flexcaches"` Filter *StorageFlexcacheDataSourceFilterModel `tfsdk:"filter"` } +// StorageFlexcacheDataSourceModel describes the data source data model for queries. type StorageFlexcacheDataSourceFilterModel struct { Name types.String `tfsdk:"name"` SVMName types.String `tfsdk:"svm_name"` @@ -206,7 +207,7 @@ func (r *StorageFlexcachesDataSource) Configure(ctx context.Context, req datasou } // Read refreshes the Terraform state with the latest data. -func (d *StorageFlexcachesDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { +func (r *StorageFlexcachesDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { var data StorageFlexcachesDataSourceModel // Read Terraform configuration data into the model @@ -218,7 +219,7 @@ func (d *StorageFlexcachesDataSource) Read(ctx context.Context, req datasource.R errorHandler := utils.NewErrorHandler(ctx, &resp.Diagnostics) // we need to defer setting the client until we can read the connection profile name - client, err := getRestClient(errorHandler, d.config, data.CxProfileName) + client, err := getRestClient(errorHandler, r.config, data.CxProfileName) if err != nil { // error reporting done inside NewClient return diff --git a/internal/restclient/rest_client.go b/internal/restclient/rest_client.go index 7e64d669..cde73597 100644 --- a/internal/restclient/rest_client.go +++ b/internal/restclient/rest_client.go @@ -255,10 +255,9 @@ func (r *RestClient) Wait(uuid string) (int, RestResponse, error) { return statusCode, RestResponse{}, errorMessage } return statusCode, RestResponse{}, fmt.Errorf("fail to get job status. Unknown error") - } else { - if job.Code != 0 { - return statusCode, RestResponse{}, fmt.Errorf("Job UUID %s failed. Error code: %d. Message: %s", uuid, job.Code, job.Message) - } + } + if job.Code != 0 { + return statusCode, RestResponse{}, fmt.Errorf("Job UUID %s failed. Error code: %d. Message: %s", uuid, job.Code, job.Message) } } time.Sleep(10 * time.Second) From adbd057a24e8fa8f8d3760f38246e969b13f269f Mon Sep 17 00:00:00 2001 From: swenjun Date: Fri, 23 Feb 2024 14:15:09 -0800 Subject: [PATCH 03/23] added missing files --- CHANGELOG.md | 7 +- .../storage_flexcache_data_source.md | 80 ++++++++++++++++++ .../storage_flexcaches_data_source.md | 80 ++++++++++++++++++ docs/resources/storage_flexcache_resource.md | 84 +++++++++++++++++++ .../provider.tf | 52 +----------- .../provider.tf | 52 +----------- scripts/generate_docs.py | 3 +- 7 files changed, 253 insertions(+), 105 deletions(-) create mode 100644 docs/data-sources/storage_flexcache_data_source.md create mode 100644 docs/data-sources/storage_flexcaches_data_source.md create mode 100644 docs/resources/storage_flexcache_resource.md mode change 100644 => 120000 examples/data-sources/netapp-ontap_storage_flexcaches/provider.tf mode change 100644 => 120000 examples/resources/netapp-ontap_storage_flexcache/provider.tf diff --git a/CHANGELOG.md b/CHANGELOG.md index db20b207..1832fb23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,8 @@ FEATURES: * **New Data Source:** `netapp_ontap_cluster_peers_data_source` ([#50](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/50)) * **New Data Source:** `netapp-ontap_protocols_cifs_local_user_data_source` ([#55](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/55)) * **New Data Source:** `netapp-ontap_protocols_cifs_local_users_data_source` ([#55](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/55)) -* **New Data Source** `netapp-ontap_security_account_data_source` ([#22](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/22)) -* **New Data Source** `netapp-ontap_security_accounts_data_source` ([#22](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/22)) +* **New Data Source:** `netapp-ontap_security_account_data_source` ([#22](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/22)) +* **New Data Source:** `netapp-ontap_security_accounts_data_source` ([#22](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/22)) * **New Data Source:** `netapp-ontap_protocols_cifs_user_group_privilege_data_source` ([#57](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/57)) * **New Data Source:** `netapp-ontap_protocols_cifs_user_group_privileges_data_source` ([#57](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/57)) * **New Data Source:** `netapp-ontap_storage_lun_data_source` ([#12](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/12)) @@ -17,11 +17,14 @@ FEATURES: * **New Data Source:** `netapp_ontap_protocols_cifs_local_group_members_data_source` ([#122](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/122)) * **New Data Source:** `netapp_ontap_protocols_cifs_server_data_source` ([#24](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/24)) * **New Data Source:** `netapp_ontap_protocols_cifs_servers_data_source` ([#24](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/24)) +* **New Data Source:** `netapp_ontap_storage_flexcache_data_source` ([#47](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/47)) +* **New Data Source:** `netapp_ontap_storage_flexcaches_data_source` ([#47](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/47)) * **New Resource:** `netapp-ontap_protocols_cifs_local_group_resource` ([#53](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/53)) * **New Resource:** `netapp-ontap_protocols_cifs_local_user_resource` ([#56](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/56)) * **New Resource:** `netapp-ontap_protocols_cifs_user_group_privilege_resource` ([#58](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/58)) * **New Resource:** `netapp-ontap_svm_peers_resource` ([#51](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/51)) * **New Resource:** `netapp-ontap_protocols_cifs_user_group_member_resource` ([#123](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/123)) +* **New Resource:** `netapp-ontap_storage_flexcache_resource` ([#46](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/46)) ENHANCEMENTS: * **netapp-ontap_protocols_nfs_export_policy_resource**: Add support for import ([#34](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/34)) diff --git a/docs/data-sources/storage_flexcache_data_source.md b/docs/data-sources/storage_flexcache_data_source.md new file mode 100644 index 00000000..ad483b34 --- /dev/null +++ b/docs/data-sources/storage_flexcache_data_source.md @@ -0,0 +1,80 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "netapp-ontap_storage_flexcache_data_source Data Source - terraform-provider-netapp-ontap" +subcategory: "" +description: |- + Flexcache resource +--- + +# netapp-ontap_storage_flexcache_data_source (Data Source) + +Flexcache resource + + + + +## Schema + +### Required + +- `cx_profile_name` (String) Connection profile name +- `name` (String) The name of the flexcache volume +- `svm_name` (String) Name of the svm to use + +### Read-Only + +- `aggregates` (Attributes Set) (see [below for nested schema](#nestedatt--aggregates)) +- `constituents_per_aggregate` (Number) The number of constituents per aggregate +- `dr_cache` (Boolean) The state of the dr cache +- `global_file_locking_enabled` (Boolean) The state of the global file locking +- `guarantee` (Attributes) The guarantee of the volume (see [below for nested schema](#nestedatt--guarantee)) +- `id` (String) The UUID of the flexcache volume +- `junction_path` (String) Name of the junction path +- `origins` (Attributes Set) Set of the origin volumes (see [below for nested schema](#nestedatt--origins)) +- `size` (Number) The size of the flexcache volume +- `size_unit` (String) The unit used to interpret the size parameter +- `use_tiered_aggregate` (Boolean) The state of the use tiered aggregates + + +### Nested Schema for `aggregates` + +Read-Only: + +- `id` (String) UUID of the aggregate +- `name` (String) Name of the aggregate + + + +### Nested Schema for `guarantee` + +Read-Only: + +- `type` (String) The type of guarantee + + + +### Nested Schema for `origins` + +Required: + +- `svm` (Attributes) Origin volume SVM (see [below for nested schema](#nestedatt--origins--svm)) +- `volume` (Attributes) Origin volume (see [below for nested schema](#nestedatt--origins--volume)) + + +### Nested Schema for `origins.svm` + +Read-Only: + +- `id` (String) ID of the origin volume +- `name` (String) Name of the origin volume + + + +### Nested Schema for `origins.volume` + +Read-Only: + +- `id` (String) ID of the origin volume +- `name` (String) Name of the origin volume + + diff --git a/docs/data-sources/storage_flexcaches_data_source.md b/docs/data-sources/storage_flexcaches_data_source.md new file mode 100644 index 00000000..8ed8d54d --- /dev/null +++ b/docs/data-sources/storage_flexcaches_data_source.md @@ -0,0 +1,80 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "netapp-ontap_storage_flexcaches_data_source Data Source - terraform-provider-netapp-ontap" +subcategory: "" +description: |- + Flexcache resource +--- + +# netapp-ontap_storage_flexcaches_data_source (Data Source) + +Flexcache resource + + + + +## Schema + +### Required + +- `cx_profile_name` (String) Connection profile name +- `name` (String) The name of the flexcache volume +- `svm_name` (String) Name of the svm to use + +### Read-Only + +- `aggregates` (Attributes Set) (see [below for nested schema](#nestedatt--aggregates)) +- `constituents_per_aggregate` (Number) The number of constituents per aggregate +- `dr_cache` (Boolean) The state of the dr cache +- `global_file_locking_enabled` (Boolean) The state of the global file locking +- `guarantee` (Attributes) The guarantee of the volume (see [below for nested schema](#nestedatt--guarantee)) +- `id` (String) The UUID of the flexcache volume +- `junction_path` (String) Name of the junction path +- `origins` (Attributes Set) Set of the origin volumes (see [below for nested schema](#nestedatt--origins)) +- `size` (Number) The size of the flexcache volume +- `size_unit` (String) The unit used to interpret the size parameter +- `use_tiered_aggregate` (Boolean) The state of the use tiered aggregates + + +### Nested Schema for `aggregates` + +Read-Only: + +- `id` (String) UUID of the aggregate +- `name` (String) Name of the aggregate + + + +### Nested Schema for `guarantee` + +Read-Only: + +- `type` (String) The type of guarantee + + + +### Nested Schema for `origins` + +Required: + +- `svm` (Attributes) Origin volume SVM (see [below for nested schema](#nestedatt--origins--svm)) +- `volume` (Attributes) Origin volume (see [below for nested schema](#nestedatt--origins--volume)) + + +### Nested Schema for `origins.svm` + +Read-Only: + +- `id` (String) ID of the origin volume +- `name` (String) Name of the origin volume + + + +### Nested Schema for `origins.volume` + +Read-Only: + +- `id` (String) ID of the origin volume +- `name` (String) Name of the origin volume + + diff --git a/docs/resources/storage_flexcache_resource.md b/docs/resources/storage_flexcache_resource.md new file mode 100644 index 00000000..483c9cbc --- /dev/null +++ b/docs/resources/storage_flexcache_resource.md @@ -0,0 +1,84 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "netapp-ontap_storage_flexcache_resource Resource - terraform-provider-netapp-ontap" +subcategory: "storage" +description: |- + Flexcache resource +--- + +# netapp-ontap_storage_flexcache_resource (Resource) + +Flexcache resource + + + + +## Schema + +### Required + +- `cx_profile_name` (String) Connection profile name +- `name` (String) The name of the flexcache volume to manage +- `origins` (Attributes Set) Set of the origin volumes (see [below for nested schema](#nestedatt--origins)) +- `svm_name` (String) Name of the svm to use + +### Optional + +- `aggregates` (Attributes Set) Set of the aggregates to use (see [below for nested schema](#nestedatt--aggregates)) +- `constituents_per_aggregate` (Number) The number of constituents per aggregate +- `dr_cache` (Boolean) The state of the dr cache +- `global_file_locking_enabled` (Boolean) The state of the global file locking +- `guarantee` (Attributes) The guarantee of the volume (see [below for nested schema](#nestedatt--guarantee)) +- `junction_path` (String) Name of the junction path +- `size` (Number) The size of the flexcache volume +- `size_unit` (String) The unit used to interpret the size parameter +- `use_tiered_aggregate` (Boolean) The state of the use tiered aggregates + +### Read-Only + +- `id` (String) The ID of the volume + + +### Nested Schema for `origins` + +Required: + +- `svm` (Attributes) origin volume SVM (see [below for nested schema](#nestedatt--origins--svm)) +- `volume` (Attributes) origin volume (see [below for nested schema](#nestedatt--origins--volume)) + + +### Nested Schema for `origins.svm` + +Optional: + +- `id` (String) ID of the origin volume SVM +- `name` (String) Name of the origin volume SVM + + + +### Nested Schema for `origins.volume` + +Optional: + +- `id` (String) ID of the origin volume +- `name` (String) Name of the origin volume + + + + +### Nested Schema for `aggregates` + +Optional: + +- `id` (String) UUID of the aggregate +- `name` (String) Name of the aggregate + + + +### Nested Schema for `guarantee` + +Optional: + +- `type` (String) The type of guarantee + + diff --git a/examples/data-sources/netapp-ontap_storage_flexcaches/provider.tf b/examples/data-sources/netapp-ontap_storage_flexcaches/provider.tf deleted file mode 100644 index 9a9d3db8..00000000 --- a/examples/data-sources/netapp-ontap_storage_flexcaches/provider.tf +++ /dev/null @@ -1,51 +0,0 @@ -terraform { - required_providers { - netapp-ontap = { - source = "NetApp/netapp-ontap" - version = "0.0.1" - } - } -} - - -provider "netapp-ontap" { - # A connection profile defines how to interface with an ONTAP cluster or svm. - # At least one is required. - connection_profiles = [ - { - name = "cluster1" - hostname = "********219" - username = var.username - password = var.password - validate_certs = var.validate_certs - }, - { - name = "cluster2" - hostname = "********222" - username = var.username - password = var.password - validate_certs = var.validate_certs - }, - { - name = "cluster3" - hostname = "10.193.176.159" - username = var.username - password = var.password - validate_certs = var.validate_certs - }, - { - name = "cluster4" - hostname = "10.193.180.108" - username = var.username - password = var.password - validate_certs = var.validate_certs - }, - { - name = "cluster5" - hostname = "10.193.176.186" - username = var.username - password = var.password - validate_certs = var.validate_certs - } - ] -} diff --git a/examples/data-sources/netapp-ontap_storage_flexcaches/provider.tf b/examples/data-sources/netapp-ontap_storage_flexcaches/provider.tf new file mode 120000 index 00000000..c6b7138f --- /dev/null +++ b/examples/data-sources/netapp-ontap_storage_flexcaches/provider.tf @@ -0,0 +1 @@ +../../provider/provider.tf \ No newline at end of file diff --git a/examples/resources/netapp-ontap_storage_flexcache/provider.tf b/examples/resources/netapp-ontap_storage_flexcache/provider.tf deleted file mode 100644 index 9a9d3db8..00000000 --- a/examples/resources/netapp-ontap_storage_flexcache/provider.tf +++ /dev/null @@ -1,51 +0,0 @@ -terraform { - required_providers { - netapp-ontap = { - source = "NetApp/netapp-ontap" - version = "0.0.1" - } - } -} - - -provider "netapp-ontap" { - # A connection profile defines how to interface with an ONTAP cluster or svm. - # At least one is required. - connection_profiles = [ - { - name = "cluster1" - hostname = "********219" - username = var.username - password = var.password - validate_certs = var.validate_certs - }, - { - name = "cluster2" - hostname = "********222" - username = var.username - password = var.password - validate_certs = var.validate_certs - }, - { - name = "cluster3" - hostname = "10.193.176.159" - username = var.username - password = var.password - validate_certs = var.validate_certs - }, - { - name = "cluster4" - hostname = "10.193.180.108" - username = var.username - password = var.password - validate_certs = var.validate_certs - }, - { - name = "cluster5" - hostname = "10.193.176.186" - username = var.username - password = var.password - validate_certs = var.validate_certs - } - ] -} diff --git a/examples/resources/netapp-ontap_storage_flexcache/provider.tf b/examples/resources/netapp-ontap_storage_flexcache/provider.tf new file mode 120000 index 00000000..c6b7138f --- /dev/null +++ b/examples/resources/netapp-ontap_storage_flexcache/provider.tf @@ -0,0 +1 @@ +../../provider/provider.tf \ No newline at end of file diff --git a/scripts/generate_docs.py b/scripts/generate_docs.py index 791f32dc..617b122b 100755 --- a/scripts/generate_docs.py +++ b/scripts/generate_docs.py @@ -51,7 +51,8 @@ "storage_volume_snapshot_data_source.md", "storage_volume_resource.md", "storage_volume_data_source.md", - "storage_volume_snapshot_resource.md"], + "storage_volume_snapshot_resource.md", + "storage_flexcache_resource.md"], 'support': [], 'svm': ["svm_resource.md", "svm_peers_resource.md"], From 41d43ff52ee7dcaca07429b14aaf5d5cd5424312 Mon Sep 17 00:00:00 2001 From: swenjun Date: Fri, 23 Feb 2024 15:25:29 -0800 Subject: [PATCH 04/23] Add edited files. --- internal/provider/storage_flexcache_data_source.go | 2 +- internal/provider/storage_flexcache_resource_test.go | 10 +++------- internal/provider/storage_flexcaches_data_source.go | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/internal/provider/storage_flexcache_data_source.go b/internal/provider/storage_flexcache_data_source.go index 780eed98..fd38f254 100644 --- a/internal/provider/storage_flexcache_data_source.go +++ b/internal/provider/storage_flexcache_data_source.go @@ -16,7 +16,7 @@ import ( var _ datasource.DataSource = &StorageFlexcacheDataSource{} // NewFlexcacheDataSource is a helper function to simplify the provider implementation. -func NewFlexcacheDataSource() datasource.DataSource { +func NewStorageFlexcacheDataSource() datasource.DataSource { return &StorageFlexcacheDataSource{ config: resourceOrDataSourceConfig{ name: "storage_flexcache_data_source", diff --git a/internal/provider/storage_flexcache_resource_test.go b/internal/provider/storage_flexcache_resource_test.go index 90fb3df8..e31cbb5e 100644 --- a/internal/provider/storage_flexcache_resource_test.go +++ b/internal/provider/storage_flexcache_resource_test.go @@ -38,13 +38,9 @@ func TestAccStorageFlexcacheResource(t *testing.T) { func testAccStorageFlexcacheResourceConfig(svm, volName string) string { if host == "" || admin == "" || password == "" { - // host = os.Getenv("TF_ACC_NETAPP_HOST2") - // admin = os.Getenv("TF_ACC_NETAPP_USER") - // password = os.Getenv("TF_ACC_NETAPP_PASS") - host = "10.193.176.186" - admin = "admin" - password = "netapp1!" - + host = os.Getenv("TF_ACC_NETAPP_HOST2") + admin = os.Getenv("TF_ACC_NETAPP_USER") + password = os.Getenv("TF_ACC_NETAPP_PASS") } if host == "" || admin == "" || password == "" { fmt.Println("TF_ACC_NETAPP_HOST, TF_ACC_NETAPP_USER, and TF_ACC_NETAPP_PASS must be set for acceptance tests") diff --git a/internal/provider/storage_flexcaches_data_source.go b/internal/provider/storage_flexcaches_data_source.go index d19a8215..9ca601ae 100644 --- a/internal/provider/storage_flexcaches_data_source.go +++ b/internal/provider/storage_flexcaches_data_source.go @@ -18,7 +18,7 @@ import ( var _ datasource.DataSource = &StorageFlexcachesDataSource{} // NewFlexcachesDataSource is a helper function to simplify the provider implementation. -func NewFlexcachesDataSource() datasource.DataSource { +func NewStorageFlexcachesDataSource() datasource.DataSource { return &StorageFlexcacheDataSource{ config: resourceOrDataSourceConfig{ name: "storage_flexcaches_data_source", From 6c25289a25abc7ed3a00f2b711a88578686260da Mon Sep 17 00:00:00 2001 From: swenjun Date: Fri, 23 Feb 2024 15:50:52 -0800 Subject: [PATCH 05/23] fix build --- internal/interfaces/storage_flexcache.go | 1 + internal/provider/storage_flexcache_data_source.go | 2 +- internal/provider/storage_flexcache_resource.go | 7 +++++-- internal/provider/storage_flexcaches_data_source.go | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/internal/interfaces/storage_flexcache.go b/internal/interfaces/storage_flexcache.go index 24e83625..d7021d8d 100644 --- a/internal/interfaces/storage_flexcache.go +++ b/internal/interfaces/storage_flexcache.go @@ -129,6 +129,7 @@ func GetStorageFlexcaches(errorHandler *utils.ErrorHandler, r restclient.RestCli return dataONTAP, nil } +// CreateStorageFlexcache creates flexcache. // POST API returns result, but does not include the attributes that are not set. Make a spearate GET call to get all attributes. func CreateStorageFlexcache(errorHandler *utils.ErrorHandler, r restclient.RestClient, data StorageFlexcacheResourceModel) error { var body map[string]interface{} diff --git a/internal/provider/storage_flexcache_data_source.go b/internal/provider/storage_flexcache_data_source.go index fd38f254..68412c34 100644 --- a/internal/provider/storage_flexcache_data_source.go +++ b/internal/provider/storage_flexcache_data_source.go @@ -15,7 +15,7 @@ import ( // Ensure provider defined types fully satisfy framework interfaces var _ datasource.DataSource = &StorageFlexcacheDataSource{} -// NewFlexcacheDataSource is a helper function to simplify the provider implementation. +// NewStorageFlexcacheDataSource is a helper function to simplify the provider implementation. func NewStorageFlexcacheDataSource() datasource.DataSource { return &StorageFlexcacheDataSource{ config: resourceOrDataSourceConfig{ diff --git a/internal/provider/storage_flexcache_resource.go b/internal/provider/storage_flexcache_resource.go index ab7b2e67..e2eaec90 100644 --- a/internal/provider/storage_flexcache_resource.go +++ b/internal/provider/storage_flexcache_resource.go @@ -390,6 +390,7 @@ func (r *StorageFlexcacheResource) Read(ctx context.Context, req resource.ReadRe } +// Create creates a new flexcache volume func (r *StorageFlexcacheResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { var data *StorageFlexcacheResourceModel resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) @@ -546,9 +547,9 @@ func (r *StorageFlexcacheResource) Create(ctx context.Context, req resource.Crea errorHandler.MakeAndReportError("No flexcache found", fmt.Sprintf("flexcache %s not found.", data.Name)) return } - size, size_unit := interfaces.ByteFormat(int64(flexcache.Size)) + size, sizeUnit := interfaces.ByteFormat(int64(flexcache.Size)) data.Size = types.Int64Value(int64(size)) - data.SizeUnit = types.StringValue(size_unit) + data.SizeUnit = types.StringValue(sizeUnit) data.JunctionPath = types.StringValue(flexcache.JunctionPath) data.ConstituentsPerAggregate = types.Int64Value(int64(flexcache.ConstituentsPerAggregate)) data.DrCache = types.BoolValue(flexcache.DrCache) @@ -663,6 +664,7 @@ func (r *StorageFlexcacheResource) Create(ctx context.Context, req resource.Crea } +// Delete removes the flexcache volume func (r *StorageFlexcacheResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { var data *StorageFlexcacheResourceModel @@ -691,6 +693,7 @@ func (r *StorageFlexcacheResource) Delete(ctx context.Context, req resource.Dele } } +// Update updates the flexcache volume // If not specified in PATCH, prepopulate.recurse is default to true. // prepopulate.dir_paths is requried. func (r *StorageFlexcacheResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { diff --git a/internal/provider/storage_flexcaches_data_source.go b/internal/provider/storage_flexcaches_data_source.go index 9ca601ae..8cb37e3b 100644 --- a/internal/provider/storage_flexcaches_data_source.go +++ b/internal/provider/storage_flexcaches_data_source.go @@ -17,7 +17,7 @@ import ( // Ensure provider defined types fully satisfy framework interfaces var _ datasource.DataSource = &StorageFlexcachesDataSource{} -// NewFlexcachesDataSource is a helper function to simplify the provider implementation. +// NewStorageFlexcachesDataSource is a helper function to simplify the provider implementation. func NewStorageFlexcachesDataSource() datasource.DataSource { return &StorageFlexcacheDataSource{ config: resourceOrDataSourceConfig{ From 462679cd4aa1180cec00c09238b89df7ee91365c Mon Sep 17 00:00:00 2001 From: swenjun Date: Fri, 23 Feb 2024 16:51:58 -0800 Subject: [PATCH 06/23] fix flexcaches data source. --- .../data-source.tf | 4 ++-- .../provider/storage_flexcache_data_source.go | 1 - .../provider/storage_flexcaches_data_source.go | 18 +++++++++++++++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/examples/data-sources/netapp-ontap_storage_flexcaches/data-source.tf b/examples/data-sources/netapp-ontap_storage_flexcaches/data-source.tf index 6088d136..8f95acf6 100644 --- a/examples/data-sources/netapp-ontap_storage_flexcaches/data-source.tf +++ b/examples/data-sources/netapp-ontap_storage_flexcaches/data-source.tf @@ -1,8 +1,8 @@ data "netapp-ontap_storage_flexcaches_data_source" "storage_flexcache" { # required to know which system to interface with - cx_profile_name = "cluster5" + cx_profile_name = "cluster4" filter = { - name = "aggr*" + name = "f*" svm_name = "automation" } } diff --git a/internal/provider/storage_flexcache_data_source.go b/internal/provider/storage_flexcache_data_source.go index 68412c34..39560cc2 100644 --- a/internal/provider/storage_flexcache_data_source.go +++ b/internal/provider/storage_flexcache_data_source.go @@ -42,7 +42,6 @@ type StorageFlexcacheDataSourceModel struct { DrCache types.Bool `tfsdk:"dr_cache"` Guarantee types.Object `tfsdk:"guarantee"` GlobalFileLockingEnabled types.Bool `tfsdk:"global_file_locking_enabled"` - Prepopulate types.Object `tfsdk:"prepopulate"` UseTieredAggregate types.Bool `tfsdk:"use_tiered_aggregate"` Aggregates types.Set `tfsdk:"aggregates"` ID types.String `tfsdk:"id"` diff --git a/internal/provider/storage_flexcaches_data_source.go b/internal/provider/storage_flexcaches_data_source.go index 8cb37e3b..fd5e57e8 100644 --- a/internal/provider/storage_flexcaches_data_source.go +++ b/internal/provider/storage_flexcaches_data_source.go @@ -19,7 +19,7 @@ var _ datasource.DataSource = &StorageFlexcachesDataSource{} // NewStorageFlexcachesDataSource is a helper function to simplify the provider implementation. func NewStorageFlexcachesDataSource() datasource.DataSource { - return &StorageFlexcacheDataSource{ + return &StorageFlexcachesDataSource{ config: resourceOrDataSourceConfig{ name: "storage_flexcaches_data_source", }, @@ -74,6 +74,8 @@ func (r *StorageFlexcachesDataSource) Schema(ctx context.Context, req datasource Optional: true, }, "storage_flexcaches": schema.ListNestedAttribute{ + Computed: true, + MarkdownDescription: "", NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ "cx_profile_name": schema.StringAttribute{ @@ -254,6 +256,20 @@ func (r *StorageFlexcachesDataSource) Read(ctx context.Context, req datasource.R data.StorageFlexcaches[index].UseTieredAggregate = types.BoolValue(record.UseTieredAggregate) data.StorageFlexcaches[index].ID = types.StringValue(record.UUID) + //guarantee + elementTypes := map[string]attr.Type{ + "type": types.StringType, + } + elements := map[string]attr.Value{ + "type": types.StringValue(record.Guarantee.Type), + } + objectValue, diags := types.ObjectValue(elementTypes, elements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + data.StorageFlexcaches[index].Guarantee = objectValue + //origin setElements := []attr.Value{} for _, origin := range record.Origins { From 9ef5dc7c6de92c75c71db34ed44939bda781ff3f Mon Sep 17 00:00:00 2001 From: swenjun Date: Wed, 28 Feb 2024 14:21:19 -0800 Subject: [PATCH 07/23] Lint. --- internal/provider/storage_flexcaches_data_source.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/storage_flexcaches_data_source.go b/internal/provider/storage_flexcaches_data_source.go index fd5e57e8..998e02db 100644 --- a/internal/provider/storage_flexcaches_data_source.go +++ b/internal/provider/storage_flexcaches_data_source.go @@ -38,7 +38,7 @@ type StorageFlexcachesDataSourceModel struct { Filter *StorageFlexcacheDataSourceFilterModel `tfsdk:"filter"` } -// StorageFlexcacheDataSourceModel describes the data source data model for queries. +// StorageFlexcacheDataSourceFilterModel describes the data source data model for queries. type StorageFlexcacheDataSourceFilterModel struct { Name types.String `tfsdk:"name"` SVMName types.String `tfsdk:"svm_name"` From 87c8ffeef5e6e8173b482088bda0fe28590596e1 Mon Sep 17 00:00:00 2001 From: swenjun Date: Mon, 11 Mar 2024 14:06:43 -0700 Subject: [PATCH 08/23] added import. --- .../provider/storage_flexcache_resource.go | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/internal/provider/storage_flexcache_resource.go b/internal/provider/storage_flexcache_resource.go index e2eaec90..878fb93b 100644 --- a/internal/provider/storage_flexcache_resource.go +++ b/internal/provider/storage_flexcache_resource.go @@ -4,14 +4,17 @@ import ( "context" "fmt" "log" + "strings" "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/mitchellh/mapstructure" "github.com/netapp/terraform-provider-netapp-ontap/internal/interfaces" "github.com/netapp/terraform-provider-netapp-ontap/internal/utils" @@ -702,3 +705,20 @@ func (r *StorageFlexcacheResource) Update(ctx context.Context, req resource.Upda errorHandler.MakeAndReportError("Update not available", "No update can be done on flexcache resource.") } + +// ImportState imports the state of the flexcache +func (r *StorageFlexcacheResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + tflog.Debug(ctx, fmt.Sprintf("import req an flexcache resource: %#v", req)) + idParts := strings.Split(req.ID, ",") + if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" { + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + fmt.Sprint("Expected ID in the format 'name,svm_name,cx_profile_name', got: ", req.ID), + ) + return + } + + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("name"), idParts[0])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("svm_name"), idParts[1])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("cx_profile_name"), idParts[2])...) +} From 8bc49111c87771b305405b8d5f48b1f27ae37b70 Mon Sep 17 00:00:00 2001 From: swenjun Date: Tue, 26 Mar 2024 15:26:38 -0700 Subject: [PATCH 09/23] Edited docs for flexcache data source and resource --- .../storage_flexcache_data_source.md | 18 +++- .../storage_flexcaches_data_source.md | 86 +++++++++++++------ docs/resources/storage_flexcache_resource.md | 34 ++++++++ 3 files changed, 111 insertions(+), 27 deletions(-) diff --git a/docs/data-sources/storage_flexcache_data_source.md b/docs/data-sources/storage_flexcache_data_source.md index ad483b34..cf318bef 100644 --- a/docs/data-sources/storage_flexcache_data_source.md +++ b/docs/data-sources/storage_flexcache_data_source.md @@ -1,15 +1,27 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs page_title: "netapp-ontap_storage_flexcache_data_source Data Source - terraform-provider-netapp-ontap" -subcategory: "" +subcategory: "storage" description: |- - Flexcache resource + Flexcache data source --- # netapp-ontap_storage_flexcache_data_source (Data Source) -Flexcache resource +Retrieves the details of a flexcache. +### Related ONTAP commands +* volume flexcache show + +## Example Usage +```terraform +data "netapp-ontap_storage_flexcache_data_source" "storage_flexcache" { + # required to know which system to interface with + cx_profile_name = "cluster1" + name = "fc1" + svm_name = "automation" +} +``` diff --git a/docs/data-sources/storage_flexcaches_data_source.md b/docs/data-sources/storage_flexcaches_data_source.md index 8ed8d54d..eb10baf4 100644 --- a/docs/data-sources/storage_flexcaches_data_source.md +++ b/docs/data-sources/storage_flexcaches_data_source.md @@ -1,14 +1,28 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs page_title: "netapp-ontap_storage_flexcaches_data_source Data Source - terraform-provider-netapp-ontap" -subcategory: "" +subcategory: "storage" description: |- - Flexcache resource + Flexcaches data source --- # netapp-ontap_storage_flexcaches_data_source (Data Source) -Flexcache resource +Retrieves the details of one or more flexcaches by filter. + +### Related ONTAP commands +* volume flexcache show + +## Example Usage +data "netapp-ontap_storage_flexcaches_data_source" "storage_flexcache" { + # required to know which system to interface with + cx_profile_name = "cluster1" + filter = { + name = "fc*" + svm_name = "automation" + } +} +``` @@ -18,59 +32,83 @@ Flexcache resource ### Required - `cx_profile_name` (String) Connection profile name -- `name` (String) The name of the flexcache volume -- `svm_name` (String) Name of the svm to use + +### Optional + +- `filter` (Attributes) (see [below for nested schema](#nestedatt--filter)) ### Read-Only -- `aggregates` (Attributes Set) (see [below for nested schema](#nestedatt--aggregates)) +- `storage_flexcaches` (Attributes List) (see [below for nested schema](#nestedatt--storage_flexcaches)) + + +### Nested Schema for `filter` + +Optional: + +- `name` (String) StorageFlexcache name +- `svm_name` (String) StorageFlexcache svm name + + + +### Nested Schema for `storage_flexcaches` + +Required: + +- `cx_profile_name` (String) Connection profile name +- `name` (String) The name of the flexcache volume to manage +- `svm_name` (String) Name of the svm to use + +Read-Only: + +- `aggregates` (Attributes Set) (see [below for nested schema](#nestedatt--storage_flexcaches--aggregates)) - `constituents_per_aggregate` (Number) The number of constituents per aggregate - `dr_cache` (Boolean) The state of the dr cache - `global_file_locking_enabled` (Boolean) The state of the global file locking -- `guarantee` (Attributes) The guarantee of the volume (see [below for nested schema](#nestedatt--guarantee)) +- `guarantee` (Attributes) The guarantee of the volume (see [below for nested schema](#nestedatt--storage_flexcaches--guarantee)) - `id` (String) The UUID of the flexcache volume - `junction_path` (String) Name of the junction path -- `origins` (Attributes Set) Set of the origin volumes (see [below for nested schema](#nestedatt--origins)) +- `origins` (Attributes Set) Set of the origin volumes (see [below for nested schema](#nestedatt--storage_flexcaches--origins)) - `size` (Number) The size of the flexcache volume - `size_unit` (String) The unit used to interpret the size parameter - `use_tiered_aggregate` (Boolean) The state of the use tiered aggregates - -### Nested Schema for `aggregates` + +### Nested Schema for `storage_flexcaches.aggregates` Read-Only: -- `id` (String) UUID of the aggregate +- `id` (String) ID of the aggregate - `name` (String) Name of the aggregate - -### Nested Schema for `guarantee` + +### Nested Schema for `storage_flexcaches.guarantee` Read-Only: -- `type` (String) The type of guarantee +- `type` (String) The type of the guarantee - -### Nested Schema for `origins` + +### Nested Schema for `storage_flexcaches.origins` Required: -- `svm` (Attributes) Origin volume SVM (see [below for nested schema](#nestedatt--origins--svm)) -- `volume` (Attributes) Origin volume (see [below for nested schema](#nestedatt--origins--volume)) +- `svm` (Attributes) Origin volume SVM (see [below for nested schema](#nestedatt--storage_flexcaches--origins--svm)) +- `volume` (Attributes) Origin volume (see [below for nested schema](#nestedatt--storage_flexcaches--origins--volume)) - -### Nested Schema for `origins.svm` + +### Nested Schema for `storage_flexcaches.origins.svm` Read-Only: -- `id` (String) ID of the origin volume -- `name` (String) Name of the origin volume +- `id` (String) ID of the origin volume SVM +- `name` (String) Name of the origin volume SVM - -### Nested Schema for `origins.volume` + +### Nested Schema for `storage_flexcaches.origins.volume` Read-Only: diff --git a/docs/resources/storage_flexcache_resource.md b/docs/resources/storage_flexcache_resource.md index 483c9cbc..63e80016 100644 --- a/docs/resources/storage_flexcache_resource.md +++ b/docs/resources/storage_flexcache_resource.md @@ -10,6 +10,40 @@ description: |- Flexcache resource +### Related ONTAP commands +* volume flexcache create +* volume flexcache prepopulate start + +## Example Usage +resource "netapp-ontap_storage_flexcache_resource" "storage_flexcache" { + # required to know which system to interface with + cx_profile_name = "cluster1" + name = "fc1" + svm_name = "automation" + origins = [ + { + volume = { + name = "vol1" + }, + svm = { + name = "automation" + } + } + ] + size = 400 + size_unit = "mb" + guarantee = { + type = "none" + } + dr_cache = false + global_file_locking_enabled = false + aggregates = [ + { + name = "aggr1" + } + ] +} +``` From 43971873fda1d281670762a9add3a350b8c66e09 Mon Sep 17 00:00:00 2001 From: swenjun Date: Tue, 26 Mar 2024 15:32:27 -0700 Subject: [PATCH 10/23] Added test for import in acc test. --- internal/provider/storage_flexcache_resource_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/internal/provider/storage_flexcache_resource_test.go b/internal/provider/storage_flexcache_resource_test.go index e31cbb5e..9adcedd0 100644 --- a/internal/provider/storage_flexcache_resource_test.go +++ b/internal/provider/storage_flexcache_resource_test.go @@ -32,6 +32,15 @@ func TestAccStorageFlexcacheResource(t *testing.T) { resource.TestCheckNoResourceAttr("netapp-ontap_storage_flexcache_resource.example", "volname"), ), }, + // Test importing a resource + { + ResourceName: "netapp-ontap_storage_flexcache_resource.example", + ImportState: true, + ImportStateId: fmt.Sprintf("%s,%s,%s", "accFlexcache", "automation", "cluster4"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("netapp-ontap_storage_flexcache_resource.example", "name", "accFlexcache"), + ), + }, }, }) } From 1272f53f34c54d45704890dfa689337485033f9c Mon Sep 17 00:00:00 2001 From: swenjun Date: Tue, 26 Mar 2024 15:45:01 -0700 Subject: [PATCH 11/23] Edit generate_docs.py --- scripts/generate_docs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/generate_docs.py b/scripts/generate_docs.py index 93b6ebd5..6028aa28 100755 --- a/scripts/generate_docs.py +++ b/scripts/generate_docs.py @@ -52,6 +52,8 @@ "storage_volume_resource.md", "storage_volume_data_source.md", "storage_volume_snapshot_resource.md", + "storage_flexcache_data_source.md", + "storage_flexcaches_data_source.md", "storage_flexcache_resource.md"], 'support': [], 'svm': ["svm_resource.md", From 9293dbc436b74d27f1f438bdb1ef6710244a1bf3 Mon Sep 17 00:00:00 2001 From: swenjun Date: Mon, 1 Apr 2024 14:40:30 -0700 Subject: [PATCH 12/23] edit flexcache acc test. --- internal/provider/storage_flexcache_resource_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/provider/storage_flexcache_resource_test.go b/internal/provider/storage_flexcache_resource_test.go index 9adcedd0..b04fc9a7 100644 --- a/internal/provider/storage_flexcache_resource_test.go +++ b/internal/provider/storage_flexcache_resource_test.go @@ -76,14 +76,14 @@ resource "netapp-ontap_storage_flexcache_resource" "example" { origins = [ { volume = { - name = "vol1" + name = "acc_test_storage_flexcache_origin_volume" }, svm = { - name = "automation" + name = "acc_test" } } ] - size = 400 + size = 200 size_unit = "mb" guarantee = { type = "none" @@ -92,7 +92,7 @@ resource "netapp-ontap_storage_flexcache_resource" "example" { global_file_locking_enabled = false aggregates = [ { - name = "aggr1" + name = "acc_test" } ] }`, host, admin, password, volName, svm) From 8825b54709d64999af85e5a58e90a18341e3e85d Mon Sep 17 00:00:00 2001 From: swenjun Date: Mon, 1 Apr 2024 15:24:19 -0700 Subject: [PATCH 13/23] edit flexcache example. --- .../netapp-ontap_storage_flexcache/resource.tf | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/resources/netapp-ontap_storage_flexcache/resource.tf b/examples/resources/netapp-ontap_storage_flexcache/resource.tf index 63f27bf4..88db7e13 100644 --- a/examples/resources/netapp-ontap_storage_flexcache/resource.tf +++ b/examples/resources/netapp-ontap_storage_flexcache/resource.tf @@ -1,15 +1,15 @@ resource "netapp-ontap_storage_flexcache_resource" "storage_flexcache" { # required to know which system to interface with - cx_profile_name = "cluster5" + cx_profile_name = "cluster4" name = "fc10" - svm_name = "automation" + svm_name = "acc_test" origins = [ { volume = { - name = "vol1" + name = "acc_test_storage_flexcache_origin_volume" }, svm = { - name = "automation" + name = "acc_test" } } ] @@ -22,7 +22,7 @@ resource "netapp-ontap_storage_flexcache_resource" "storage_flexcache" { global_file_locking_enabled = false aggregates = [ { - name = "aggr1" + name = "acc_test" } ] } From 0eda4148d4a8631cea57452d8c5f88a6cf0eeba3 Mon Sep 17 00:00:00 2001 From: swenjun Date: Tue, 2 Apr 2024 10:31:19 -0700 Subject: [PATCH 14/23] edit flexcache acc test. --- internal/provider/storage_flexcache_resource_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/storage_flexcache_resource_test.go b/internal/provider/storage_flexcache_resource_test.go index b04fc9a7..137e8602 100644 --- a/internal/provider/storage_flexcache_resource_test.go +++ b/internal/provider/storage_flexcache_resource_test.go @@ -49,7 +49,7 @@ func testAccStorageFlexcacheResourceConfig(svm, volName string) string { if host == "" || admin == "" || password == "" { host = os.Getenv("TF_ACC_NETAPP_HOST2") admin = os.Getenv("TF_ACC_NETAPP_USER") - password = os.Getenv("TF_ACC_NETAPP_PASS") + password = os.Getenv("TF_ACC_NETAPP_PASS2") } if host == "" || admin == "" || password == "" { fmt.Println("TF_ACC_NETAPP_HOST, TF_ACC_NETAPP_USER, and TF_ACC_NETAPP_PASS must be set for acceptance tests") From 78268542c6254b27782966d0e5cd0fb9dfc7a837 Mon Sep 17 00:00:00 2001 From: swenjun Date: Tue, 2 Apr 2024 10:47:11 -0700 Subject: [PATCH 15/23] edit acc test. --- internal/provider/snapmirror_resource_test.go | 2 +- internal/provider/storage_aggregate_resource_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/provider/snapmirror_resource_test.go b/internal/provider/snapmirror_resource_test.go index 1bfb88c4..41a77e29 100644 --- a/internal/provider/snapmirror_resource_test.go +++ b/internal/provider/snapmirror_resource_test.go @@ -50,7 +50,7 @@ func TestAccSnapmirrorResource(t *testing.T) { func testAccSnapmirrorResourceBasicConfig(sourceEndpoint string, destinationEndpoint string) string { host := os.Getenv("TF_ACC_NETAPP_HOST3") admin := os.Getenv("TF_ACC_NETAPP_USER") - password := os.Getenv("TF_ACC_NETAPP_PASS") + password := os.Getenv("TF_ACC_NETAPP_PASS2") if host == "" || admin == "" || password == "" { fmt.Println("TF_ACC_NETAPP_HOST3, TF_ACC_NETAPP_USER, and TF_ACC_NETAPP_PASS must be set for acceptance tests") os.Exit(1) diff --git a/internal/provider/storage_aggregate_resource_test.go b/internal/provider/storage_aggregate_resource_test.go index 8565fc39..43431be7 100644 --- a/internal/provider/storage_aggregate_resource_test.go +++ b/internal/provider/storage_aggregate_resource_test.go @@ -41,7 +41,7 @@ func TestAccStorageAggregateResource(t *testing.T) { func testAccStorageAggregateResourceConfig(node string) string { host := os.Getenv("TF_ACC_NETAPP_HOST2") admin := os.Getenv("TF_ACC_NETAPP_USER") - password := os.Getenv("TF_ACC_NETAPP_PASS") + password := os.Getenv("TF_ACC_NETAPP_PASS2") if host == "" || admin == "" || password == "" { fmt.Println("TF_ACC_NETAPP_HOST2, TF_ACC_NETAPP_USER, and TF_ACC_NETAPP_PASS must be set for acceptance tests") os.Exit(1) From a6465a423bda3239d0141afa1fa4c1a81dad18d7 Mon Sep 17 00:00:00 2001 From: swenjun Date: Tue, 2 Apr 2024 11:05:45 -0700 Subject: [PATCH 16/23] edit acc test. --- internal/provider/snapmirror_resource_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/snapmirror_resource_test.go b/internal/provider/snapmirror_resource_test.go index 41a77e29..98fdd15c 100644 --- a/internal/provider/snapmirror_resource_test.go +++ b/internal/provider/snapmirror_resource_test.go @@ -82,7 +82,7 @@ resource "netapp-ontap_snapmirror_resource" "example" { func testAccSnapmirrorResourceUpdateConfig(sourceEndpoint string, destinationEndpoint string, policy string) string { host := os.Getenv("TF_ACC_NETAPP_HOST3") admin := os.Getenv("TF_ACC_NETAPP_USER") - password := os.Getenv("TF_ACC_NETAPP_PASS") + password := os.Getenv("TF_ACC_NETAPP_PASS2") if host == "" || admin == "" || password == "" { fmt.Println("TF_ACC_NETAPP_HOST3, TF_ACC_NETAPP_USER, and TF_ACC_NETAPP_PASS must be set for acceptance tests") os.Exit(1) From af2bf278f68f2c9d0abe0c67a5995ab078258d15 Mon Sep 17 00:00:00 2001 From: swenjun Date: Tue, 2 Apr 2024 12:02:39 -0700 Subject: [PATCH 17/23] Edit test. --- internal/provider/snapmirror_resource_test.go | 4 ++-- internal/provider/storage_aggregate_resource_test.go | 2 +- internal/provider/storage_flexcache_resource_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/provider/snapmirror_resource_test.go b/internal/provider/snapmirror_resource_test.go index 5e9ea536..6f24c3a5 100644 --- a/internal/provider/snapmirror_resource_test.go +++ b/internal/provider/snapmirror_resource_test.go @@ -51,7 +51,7 @@ func TestAccSnapmirrorResource(t *testing.T) { func testAccSnapmirrorResourceBasicConfig(sourceEndpoint string, destinationEndpoint string) string { host := os.Getenv("TF_ACC_NETAPP_HOST4") admin := os.Getenv("TF_ACC_NETAPP_USER") - password := os.Getenv("TF_ACC_NETAPP_PASS2") + password := os.Getenv("TF_ACC_NETAPP_PASS") if host == "" || admin == "" || password == "" { fmt.Println("TF_ACC_NETAPP_HOST4, TF_ACC_NETAPP_USER, and TF_ACC_NETAPP_PASS must be set for acceptance tests") os.Exit(1) @@ -83,7 +83,7 @@ resource "netapp-ontap_snapmirror_resource" "example" { func testAccSnapmirrorResourceUpdateConfig(sourceEndpoint string, destinationEndpoint string, policy string) string { host := os.Getenv("TF_ACC_NETAPP_HOST4") admin := os.Getenv("TF_ACC_NETAPP_USER") - password := os.Getenv("TF_ACC_NETAPP_PASS2") + password := os.Getenv("TF_ACC_NETAPP_PASS") if host == "" || admin == "" || password == "" { fmt.Println("TF_ACC_NETAPP_HOST4, TF_ACC_NETAPP_USER, and TF_ACC_NETAPP_PASS must be set for acceptance tests") os.Exit(1) diff --git a/internal/provider/storage_aggregate_resource_test.go b/internal/provider/storage_aggregate_resource_test.go index 774a5dc1..a83e2e56 100644 --- a/internal/provider/storage_aggregate_resource_test.go +++ b/internal/provider/storage_aggregate_resource_test.go @@ -19,7 +19,7 @@ func TestAccStorageAggregateResource(t *testing.T) { ExpectError: regexp.MustCompile("is an invalid value"), }, { - Config: testAccStorageAggregateResourceConfig("swenjun-vsim1"), + Config: testAccStorageAggregateResourceConfig("swenjun-vsim2"), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("netapp-ontap_storage_aggregate_resource.example", "name", "acc_test_aggr"), resource.TestCheckNoResourceAttr("netapp-ontap_storage_aggregate_resource.example", "vol"), diff --git a/internal/provider/storage_flexcache_resource_test.go b/internal/provider/storage_flexcache_resource_test.go index 137e8602..7948eb57 100644 --- a/internal/provider/storage_flexcache_resource_test.go +++ b/internal/provider/storage_flexcache_resource_test.go @@ -26,7 +26,7 @@ func TestAccStorageFlexcacheResource(t *testing.T) { }, // Read testing { - Config: testAccStorageFlexcacheResourceConfig("automation", "accFlexcache"), + Config: testAccStorageFlexcacheResourceConfig("acc_test", "accFlexcache"), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("netapp-ontap_storage_flexcache_resource.example", "name", "accFlexcache"), resource.TestCheckNoResourceAttr("netapp-ontap_storage_flexcache_resource.example", "volname"), @@ -36,7 +36,7 @@ func TestAccStorageFlexcacheResource(t *testing.T) { { ResourceName: "netapp-ontap_storage_flexcache_resource.example", ImportState: true, - ImportStateId: fmt.Sprintf("%s,%s,%s", "accFlexcache", "automation", "cluster4"), + ImportStateId: fmt.Sprintf("%s,%s,%s", "accFlexcache", "acc_test", "cluster4"), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("netapp-ontap_storage_flexcache_resource.example", "name", "accFlexcache"), ), From c2abe50c57a2cd0ac6116c12910793a29b99db50 Mon Sep 17 00:00:00 2001 From: swenjun Date: Tue, 2 Apr 2024 12:17:29 -0700 Subject: [PATCH 18/23] edit acc test. --- internal/provider/storage_flexcache_resource_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/storage_flexcache_resource_test.go b/internal/provider/storage_flexcache_resource_test.go index 7948eb57..5e07286a 100644 --- a/internal/provider/storage_flexcache_resource_test.go +++ b/internal/provider/storage_flexcache_resource_test.go @@ -36,7 +36,7 @@ func TestAccStorageFlexcacheResource(t *testing.T) { { ResourceName: "netapp-ontap_storage_flexcache_resource.example", ImportState: true, - ImportStateId: fmt.Sprintf("%s,%s,%s", "accFlexcache", "acc_test", "cluster4"), + ImportStateId: fmt.Sprintf("%s,%s,%s", "accFlexcache", "acc_test", "cluster5"), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("netapp-ontap_storage_flexcache_resource.example", "name", "accFlexcache"), ), From f509bacec56343f4a8391c4916cc6c3257c47718 Mon Sep 17 00:00:00 2001 From: swenjun Date: Tue, 2 Apr 2024 12:25:44 -0700 Subject: [PATCH 19/23] added id in read function. --- internal/provider/storage_flexcache_resource.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/storage_flexcache_resource.go b/internal/provider/storage_flexcache_resource.go index 878fb93b..3b8b09ce 100644 --- a/internal/provider/storage_flexcache_resource.go +++ b/internal/provider/storage_flexcache_resource.go @@ -286,7 +286,7 @@ func (r *StorageFlexcacheResource) Read(ctx context.Context, req resource.ReadRe data.DrCache = types.BoolValue(flexcache.DrCache) data.GlobalFileLockingEnabled = types.BoolValue(flexcache.GlobalFileLockingEnabled) data.UseTieredAggregate = types.BoolValue(flexcache.UseTieredAggregate) - + data.ID = types.StringValue(flexcache.UUID) elementTypes := map[string]attr.Type{ "type": types.StringType, } From f02e01cbe0fae3d96883157878443aee51fcd464 Mon Sep 17 00:00:00 2001 From: swenjun Date: Tue, 2 Apr 2024 14:07:39 -0700 Subject: [PATCH 20/23] edit acc test and reportcard.yml --- .github/workflows/reportcard.yml | 2 +- internal/provider/cluster_peers_resource_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/reportcard.yml b/.github/workflows/reportcard.yml index bb042e94..4ac0cbf0 100644 --- a/.github/workflows/reportcard.yml +++ b/.github/workflows/reportcard.yml @@ -12,7 +12,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: '1.19.3' + go-version: '1.22' - name: Set up goreportcard working-directory: /tmp diff --git a/internal/provider/cluster_peers_resource_test.go b/internal/provider/cluster_peers_resource_test.go index 11985d45..7a169f8d 100644 --- a/internal/provider/cluster_peers_resource_test.go +++ b/internal/provider/cluster_peers_resource_test.go @@ -29,11 +29,11 @@ func TestAccClusterPeersResource(t *testing.T) { }, // Import and read { - ResourceName: "netapp-ontap_cluster_peers_resource.example", + ResourceName: "netapp-ontap_svm_peers_resource.example", ImportState: true, - ImportStateId: fmt.Sprintf("%s,%s", "acc_test_cluster2", "cluster4"), + ImportStateId: fmt.Sprintf("%s,%s,%s,%s", "snapmirror_dest_dp", "snapmirror_dest_svm", "swenjuncluster-1", "cluster4"), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("netapp-ontap_cluster_peers_resource.example", "name", "acc_test_cluster2"), + resource.TestCheckResourceAttr("netapp-ontap_svm_peers_resource.example", "svm.name", "snapmirror_dest_dp"), ), }, }, From db763ba0b4edaaf8a5aa4d96b706cdc9d38fc564 Mon Sep 17 00:00:00 2001 From: swenjun Date: Tue, 2 Apr 2024 15:11:57 -0700 Subject: [PATCH 21/23] edit acc test. --- internal/provider/cluster_peers_resource_test.go | 6 +++--- internal/provider/svm_peers_resource_test.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/provider/cluster_peers_resource_test.go b/internal/provider/cluster_peers_resource_test.go index 7a169f8d..11985d45 100644 --- a/internal/provider/cluster_peers_resource_test.go +++ b/internal/provider/cluster_peers_resource_test.go @@ -29,11 +29,11 @@ func TestAccClusterPeersResource(t *testing.T) { }, // Import and read { - ResourceName: "netapp-ontap_svm_peers_resource.example", + ResourceName: "netapp-ontap_cluster_peers_resource.example", ImportState: true, - ImportStateId: fmt.Sprintf("%s,%s,%s,%s", "snapmirror_dest_dp", "snapmirror_dest_svm", "swenjuncluster-1", "cluster4"), + ImportStateId: fmt.Sprintf("%s,%s", "acc_test_cluster2", "cluster4"), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("netapp-ontap_svm_peers_resource.example", "svm.name", "snapmirror_dest_dp"), + resource.TestCheckResourceAttr("netapp-ontap_cluster_peers_resource.example", "name", "acc_test_cluster2"), ), }, }, diff --git a/internal/provider/svm_peers_resource_test.go b/internal/provider/svm_peers_resource_test.go index 1d074457..72348c24 100644 --- a/internal/provider/svm_peers_resource_test.go +++ b/internal/provider/svm_peers_resource_test.go @@ -39,9 +39,9 @@ func TestAccSvmPeersResource(t *testing.T) { { ResourceName: "netapp-ontap_svm_peers_resource.example", ImportState: true, - ImportStateId: fmt.Sprintf("%s,%s,%s,%s", "acc_test_peer", "acc_test", "swenjuncluster-1", "cluster4"), + ImportStateId: fmt.Sprintf("%s,%s,%s,%s", "snapmirror_dest_dp", "snapmirror_dest_svm", "swenjuncluster-1", "cluster4"), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("netapp-ontap_svm_peers_resource.example", "svm.name", "acc_test_peer"), + resource.TestCheckResourceAttr("netapp-ontap_svm_peers_resource.example", "svm.name", "snapmirror_dest_dp"), ), }, }, From 1d25ce89023e553f167e1b46a862eae0cd15ffc3 Mon Sep 17 00:00:00 2001 From: swenjun Date: Wed, 3 Apr 2024 16:22:41 -0700 Subject: [PATCH 22/23] Edited examples/resources/netapp-ontap_storage_flexcache/resource.tf --- examples/resources/netapp-ontap_storage_flexcache/resource.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/resources/netapp-ontap_storage_flexcache/resource.tf b/examples/resources/netapp-ontap_storage_flexcache/resource.tf index 88db7e13..18443351 100644 --- a/examples/resources/netapp-ontap_storage_flexcache/resource.tf +++ b/examples/resources/netapp-ontap_storage_flexcache/resource.tf @@ -1,7 +1,7 @@ resource "netapp-ontap_storage_flexcache_resource" "storage_flexcache" { # required to know which system to interface with cx_profile_name = "cluster4" - name = "fc10" + name = "acc_test_storage_flexcache_volume" svm_name = "acc_test" origins = [ { From 46c87f5478d96ad8266776d8e9ee1a3b567b2058 Mon Sep 17 00:00:00 2001 From: swenjun Date: Fri, 5 Apr 2024 12:23:09 -0700 Subject: [PATCH 23/23] added if statements for size and size_unit --- internal/provider/storage_flexcache_resource.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/internal/provider/storage_flexcache_resource.go b/internal/provider/storage_flexcache_resource.go index 3b8b09ce..29f1aad5 100644 --- a/internal/provider/storage_flexcache_resource.go +++ b/internal/provider/storage_flexcache_resource.go @@ -407,12 +407,15 @@ func (r *StorageFlexcacheResource) Create(ctx context.Context, req resource.Crea return } var request interfaces.StorageFlexcacheResourceModel - if _, ok := interfaces.POW2BYTEMAP[data.SizeUnit.ValueString()]; !ok { - errorHandler.MakeAndReportError("error creating flexcache", fmt.Sprintf("invalid input for size_unit: %s, required one of: bytes, b, kb, mb, gb, tb, pb, eb, zb, yb", data.SizeUnit.ValueString())) - return + if !data.SizeUnit.IsUnknown() { + if _, ok := interfaces.POW2BYTEMAP[data.SizeUnit.ValueString()]; !ok { + errorHandler.MakeAndReportError("error creating flexcache", fmt.Sprintf("invalid input for size_unit: %s, required one of: bytes, b, kb, mb, gb, tb, pb, eb, zb, yb", data.SizeUnit.ValueString())) + return + } + } + if !data.Size.IsUnknown() { + request.Size = int(data.Size.ValueInt64()) * interfaces.POW2BYTEMAP[data.SizeUnit.ValueString()] } - - request.Size = int(data.Size.ValueInt64()) * interfaces.POW2BYTEMAP[data.SizeUnit.ValueString()] request.Name = data.Name.ValueString() request.SVM.Name = data.SvmName.ValueString() if !data.JunctionPath.IsUnknown() {