From e45b9e372e4cc3b5c4c6490841443c8b6469de86 Mon Sep 17 00:00:00 2001 From: swenjun Date: Mon, 8 Apr 2024 21:06:51 -0700 Subject: [PATCH 1/4] Edit cluster data source. --- CHANGELOG.md | 1 + docs/data-sources/cluster_data_source.md | 53 +++++- internal/interfaces/cluster.go | 32 +++- internal/interfaces/cluster_test.go | 2 +- internal/provider/cluster_data_source.go | 195 ++++++++++++++++++++++- 5 files changed, 266 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bb85894..3d7bafe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ ENHANCEMENTS: * **netapp-ontap_networking_ip_interface_resource**: Add support for import ([#32](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/32)) * **netapp-ontap_protocols_nfs_export_policy_rule_resource**: Add support for import ([#35](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/35)) * **netapp-ontap_networking_ip_route_resource**: Add support for import ([#33](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/33)) +* **netapp-ontap_cluster** Add contact, locaton, dns_domains, name_servers, timezone, certificate, ntp_servers, management_interfaces options ([#16](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/16)) BUG FIXES: * **netapp-ontap_protocols_nfs_service**: Fixed issue with version check failing for minor versions diff --git a/docs/data-sources/cluster_data_source.md b/docs/data-sources/cluster_data_source.md index 2335addb..0f59fb7b 100644 --- a/docs/data-sources/cluster_data_source.md +++ b/docs/data-sources/cluster_data_source.md @@ -1,7 +1,7 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs page_title: "netapp-ontap_cluster_data_source Data Source - terraform-provider-netapp-ontap" -subcategory: "Cluster" +subcategory: "cluster" description: |- Cluster data source --- @@ -9,13 +9,8 @@ description: |- # netapp-ontap_cluster_data_source (Data Source) Cluster data source -## Example Usage -```terraform -data "netapp-ontap_cluster_data_source" "cluster" { - # required to know which system to interface with - cx_profile_name = "cluster2" -} -``` + + ## Schema @@ -26,10 +21,44 @@ data "netapp-ontap_cluster_data_source" "cluster" { ### Read-Only +- `certificate` (Attributes) Certificate (see [below for nested schema](#nestedatt--certificate)) +- `contact` (String) Contact information. Example: support@company.com +- `dns_domains` (Set of String) A list of DNS domains. +- `location` (String) Location information +- `management_interfaces` (Attributes Set) A list of network interface (see [below for nested schema](#nestedatt--management_interfaces)) - `name` (String) Cluster name +- `name_servers` (Set of String) The list of IP addresses of the DNS servers. Addresses can be either IPv4 or IPv6 addresses. - `nodes` (Attributes List) Cluster Nodes (see [below for nested schema](#nestedatt--nodes)) +- `ntp_servers` (Set of String) Host name, IPv4 address, or IPv6 address for the external NTP time servers. +- `time_zone` (Attributes) Time zone (see [below for nested schema](#nestedatt--time_zone)) - `version` (Attributes) ONTAP software version (see [below for nested schema](#nestedatt--version)) + +### Nested Schema for `certificate` + +Read-Only: + +- `id` (String) + + + +### Nested Schema for `management_interfaces` + +Read-Only: + +- `id` (String) ID +- `ip` (Attributes) IP address (see [below for nested schema](#nestedatt--management_interfaces--ip)) +- `name` (String) Name + + +### Nested Schema for `management_interfaces.ip` + +Read-Only: + +- `address` (String) IP address + + + ### Nested Schema for `nodes` @@ -39,6 +68,14 @@ Read-Only: - `name` (String) + +### Nested Schema for `time_zone` + +Read-Only: + +- `name` (String) Time zone + + ### Nested Schema for `version` diff --git a/internal/interfaces/cluster.go b/internal/interfaces/cluster.go index 75154666..e01884a5 100644 --- a/internal/interfaces/cluster.go +++ b/internal/interfaces/cluster.go @@ -13,8 +13,30 @@ import ( type ClusterGetDataModelONTAP struct { // ConfigurableAttribute types.String `json:"configurable_attribute"` // ID types.String `json:"id"` - Name string - Version versionModelONTAP + Name string + Version versionModelONTAP + Contact string + Location string + DnsDomains []string `mapstructure:"dns_domains"` + NameServers []string `mapstructure:"name_servers"` + NtpServers []string `mapstructure:"ntp_servers"` + TimeZone timeZone `mapstructure:"timezone"` + ClusterCertificate ClusterCertificate `mapstructure:"certificate"` + ManagementInterfaces []mgmtInterface `mapstructure:"management_interfaces"` +} + +type timeZone struct { + Name string +} + +type mgmtInterface struct { + IP ipAddress `mapstructure:"ip"` + Name string `mapstructure:"name"` + ID string `mapstructure:"uuid"` +} + +type ClusterCertificate struct { + ID string `mapstructure:"uuid"` } type versionModelONTAP struct { @@ -28,20 +50,22 @@ type ipAddress struct { Address string } -type mgmtInterface struct { +type noddMgmtInterface struct { IP ipAddress } // ClusterNodeGetDataModelONTAP describes the GET record data model using go types for mapping. type ClusterNodeGetDataModelONTAP struct { Name string - ManagementInterfaces []mgmtInterface `mapstructure:"management_interfaces"` + ManagementInterfaces []noddMgmtInterface `mapstructure:"management_interfaces"` // Version versionModelONTAP } // GetCluster to get cluster info func GetCluster(errorHandler *utils.ErrorHandler, r restclient.RestClient) (*ClusterGetDataModelONTAP, error) { statusCode, response, err := r.GetNilOrOneRecord("cluster", nil, nil) + query := r.NewQuery() + query.Fields([]string{"name", "location", "contact", "dns_domains", "name_servers", "ntp_servers", "management_interfaces", "timezone", "certificate"}) if err == nil && response == nil { err = fmt.Errorf("no response for GET cluster") } diff --git a/internal/interfaces/cluster_test.go b/internal/interfaces/cluster_test.go index 691e3213..ecaf61c0 100644 --- a/internal/interfaces/cluster_test.go +++ b/internal/interfaces/cluster_test.go @@ -95,7 +95,7 @@ func TestGetClusterNodes(t *testing.T) { errorHandler := utils.NewErrorHandler(context.Background(), &diag.Diagnostics{}) record := ClusterNodeGetDataModelONTAP{ Name: "cluster1-01", - ManagementInterfaces: []mgmtInterface{ + ManagementInterfaces: []noddMgmtInterface{ {ipAddress{"10.11.12.13"}}, {ipAddress{"10.11.12.14"}}, }, diff --git a/internal/provider/cluster_data_source.go b/internal/provider/cluster_data_source.go index 70117d45..85393e97 100644 --- a/internal/provider/cluster_data_source.go +++ b/internal/provider/cluster_data_source.go @@ -4,6 +4,7 @@ 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" @@ -33,10 +34,18 @@ type ClusterDataSource struct { type ClusterDataSourceModel struct { // ConfigurableAttribute types.String `tfsdk:"configurable_attribute"` // ID types.String `tfsdk:"id"` - CxProfileName types.String `tfsdk:"cx_profile_name"` - Name types.String `tfsdk:"name"` - Version *versionModel `tfsdk:"version"` - Nodes []NodeDataSourceModel `tfsdk:"nodes"` + CxProfileName types.String `tfsdk:"cx_profile_name"` + Name types.String `tfsdk:"name"` + Version *versionModel `tfsdk:"version"` + Nodes []NodeDataSourceModel `tfsdk:"nodes"` + Contact types.String `tfsdk:"contact"` + Location types.String `tfsdk:"location"` + DnsDomains types.Set `tfsdk:"dns_domains"` + NameServers types.Set `tfsdk:"name_servers"` + TimeZone types.Object `tfsdk:"timezone"` + Certificate types.Object `tfsdk:"certificate"` + NtpServers types.Set `tfsdk:"ntp_servers"` + ManagementInterfaces types.Set `tfsdk:"management_interfaces"` } // NodeDataSourceModel describes the data source data model. @@ -95,6 +104,79 @@ func (d *ClusterDataSource) Schema(ctx context.Context, req datasource.SchemaReq }, }, }, + "contact": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Contact information. Example: support@company.com", + }, + "location": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Location information", + }, + // "password": schema.StringAttribute{ + // Computed: true, + // Sensitive: true, + // MarkdownDescription: "Password", + // }, + "dns_domains": schema.SetAttribute{ + ElementType: types.StringType, + Computed: true, + MarkdownDescription: "A list of DNS domains.", + }, + "name_servers": schema.SetAttribute{ + ElementType: types.StringType, + Computed: true, + MarkdownDescription: "The list of IP addresses of the DNS servers. Addresses can be either IPv4 or IPv6 addresses.", + }, + "timezone": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Time zone", + }, + }, + Computed: true, + MarkdownDescription: "Time zone", + }, + "certificate": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + }, + }, + Computed: true, + MarkdownDescription: "Certificate", + }, + "ntp_servers": schema.SetAttribute{ + ElementType: types.StringType, + Computed: true, + MarkdownDescription: "Host name, IPv4 address, or IPv6 address for the external NTP time servers.", + }, + "management_interfaces": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "ip": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "address": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "IP address", + }, + }, + Computed: true, + MarkdownDescription: "IP address", + }, + "name": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Name", + }, + "id": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "ID", + }, + }, + }, + Computed: true, + MarkdownDescription: "A list of network interface", + }, }, } } @@ -148,6 +230,111 @@ func (d *ClusterDataSource) Read(ctx context.Context, req datasource.ReadRequest data.Version = &versionModel{ Full: types.StringValue(cluster.Version.Full), } + data.Contact = types.StringValue(cluster.Contact) + data.Location = types.StringValue(cluster.Location) + + // dns domains + elements := []attr.Value{} + for _, dnsDomain := range cluster.DnsDomains { + elements = append(elements, types.StringValue(dnsDomain)) + } + setValue, diags := types.SetValue(types.StringType, elements) + resp.Diagnostics.Append(diags...) + data.DnsDomains = setValue + + //name servers + elements = []attr.Value{} + for _, nameServer := range cluster.NameServers { + elements = append(elements, types.StringValue(nameServer)) + } + setValue, diags = types.SetValue(types.StringType, elements) + resp.Diagnostics.Append(diags...) + data.NameServers = setValue + // time zone + elementTypes := map[string]attr.Type{ + "name": types.StringType, + } + objectElements := map[string]attr.Value{ + "name": types.StringValue(cluster.TimeZone.Name), + } + objectValue, diags := types.ObjectValue(elementTypes, objectElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + data.TimeZone = objectValue + + // certificate + elementTypes = map[string]attr.Type{ + "id": types.StringType, + } + objectElements = map[string]attr.Value{ + "id": types.StringValue(cluster.ClusterCertificate.ID), + } + objectValue, diags = types.ObjectValue(elementTypes, objectElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + data.Certificate = objectValue + + // ntp servers + elements = []attr.Value{} + for _, ntpServer := range cluster.NtpServers { + elements = append(elements, types.StringValue(ntpServer)) + } + setValue, diags = types.SetValue(types.StringType, elements) + resp.Diagnostics.Append(diags...) + data.NtpServers = setValue + + // management interfaces + setElements := []attr.Value{} + for _, mgmInterface := range cluster.ManagementInterfaces { + nestedElementTypes := map[string]attr.Type{ + "address": types.StringType, + } + nestedVolumeElements := map[string]attr.Value{ + "address": types.StringValue(mgmInterface.IP.Address), + } + originVolumeObjectValue, diags := types.ObjectValue(nestedElementTypes, nestedVolumeElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + + elementTypes := map[string]attr.Type{ + "ip": types.ObjectType{AttrTypes: nestedElementTypes}, + "name": types.StringType, + "id": types.StringType, + } + elements := map[string]attr.Value{ + "ip": originVolumeObjectValue, + "name": types.StringValue(mgmInterface.Name), + "id": types.StringValue(mgmInterface.ID), + } + 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{ + "ip": types.ObjectType{AttrTypes: map[string]attr.Type{ + "address": types.StringType, + }}, + "name": types.StringType, + "id": types.StringType, + }, + }, setElements) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } + + data.ManagementInterfaces = setValue nodes, err := interfaces.GetClusterNodes(errorHandler, *client) if err != nil { From 69c994729c955b033b79b71eab9bf0b7255ad98d Mon Sep 17 00:00:00 2001 From: swenjun Date: Tue, 9 Apr 2024 16:08:09 -0700 Subject: [PATCH 2/4] Lint --- internal/interfaces/cluster.go | 3 ++- internal/provider/cluster_data_source.go | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/interfaces/cluster.go b/internal/interfaces/cluster.go index e01884a5..77a0671c 100644 --- a/internal/interfaces/cluster.go +++ b/internal/interfaces/cluster.go @@ -17,7 +17,7 @@ type ClusterGetDataModelONTAP struct { Version versionModelONTAP Contact string Location string - DnsDomains []string `mapstructure:"dns_domains"` + DNSDomains []string `mapstructure:"dns_domains"` NameServers []string `mapstructure:"name_servers"` NtpServers []string `mapstructure:"ntp_servers"` TimeZone timeZone `mapstructure:"timezone"` @@ -35,6 +35,7 @@ type mgmtInterface struct { ID string `mapstructure:"uuid"` } +// ClusterCertificate describes the GET record data model using go types for mapping. type ClusterCertificate struct { ID string `mapstructure:"uuid"` } diff --git a/internal/provider/cluster_data_source.go b/internal/provider/cluster_data_source.go index 85393e97..b41233f9 100644 --- a/internal/provider/cluster_data_source.go +++ b/internal/provider/cluster_data_source.go @@ -40,7 +40,7 @@ type ClusterDataSourceModel struct { Nodes []NodeDataSourceModel `tfsdk:"nodes"` Contact types.String `tfsdk:"contact"` Location types.String `tfsdk:"location"` - DnsDomains types.Set `tfsdk:"dns_domains"` + DNSDomains types.Set `tfsdk:"dns_domains"` NameServers types.Set `tfsdk:"name_servers"` TimeZone types.Object `tfsdk:"timezone"` Certificate types.Object `tfsdk:"certificate"` @@ -235,12 +235,12 @@ func (d *ClusterDataSource) Read(ctx context.Context, req datasource.ReadRequest // dns domains elements := []attr.Value{} - for _, dnsDomain := range cluster.DnsDomains { + for _, dnsDomain := range cluster.DNSDomains { elements = append(elements, types.StringValue(dnsDomain)) } setValue, diags := types.SetValue(types.StringType, elements) resp.Diagnostics.Append(diags...) - data.DnsDomains = setValue + data.DNSDomains = setValue //name servers elements = []attr.Value{} From eb4cfcc2e443e75f3e183e742bb3c88ba92babac Mon Sep 17 00:00:00 2001 From: swenjun Date: Tue, 9 Apr 2024 16:23:12 -0700 Subject: [PATCH 3/4] Added examples. --- docs/data-sources/cluster_data_source.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/data-sources/cluster_data_source.md b/docs/data-sources/cluster_data_source.md index 0f59fb7b..b741391a 100644 --- a/docs/data-sources/cluster_data_source.md +++ b/docs/data-sources/cluster_data_source.md @@ -8,8 +8,18 @@ description: |- # netapp-ontap_cluster_data_source (Data Source) -Cluster data source - +Retrieves the details of a cluster data source + +### Related ONTAP commands +cluster show + +## Example Usage +```terraform +data "netapp-ontap_cluster_data_source" "cluster" { + # required to know which system to interface with + cx_profile_name = "cluster4" +} +``` From 31845cecad41361225ce605571b90eec1e23efb8 Mon Sep 17 00:00:00 2001 From: swenjun Date: Thu, 11 Apr 2024 15:54:48 -0700 Subject: [PATCH 4/4] Fix according to comment. --- docs/data-sources/cluster_data_source.md | 2 +- internal/provider/cluster_data_source.go | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/docs/data-sources/cluster_data_source.md b/docs/data-sources/cluster_data_source.md index b741391a..d1fc28a6 100644 --- a/docs/data-sources/cluster_data_source.md +++ b/docs/data-sources/cluster_data_source.md @@ -1,7 +1,7 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs page_title: "netapp-ontap_cluster_data_source Data Source - terraform-provider-netapp-ontap" -subcategory: "cluster" +subcategory: "Cluster" description: |- Cluster data source --- diff --git a/internal/provider/cluster_data_source.go b/internal/provider/cluster_data_source.go index b41233f9..d8064b0b 100644 --- a/internal/provider/cluster_data_source.go +++ b/internal/provider/cluster_data_source.go @@ -112,11 +112,6 @@ func (d *ClusterDataSource) Schema(ctx context.Context, req datasource.SchemaReq Computed: true, MarkdownDescription: "Location information", }, - // "password": schema.StringAttribute{ - // Computed: true, - // Sensitive: true, - // MarkdownDescription: "Password", - // }, "dns_domains": schema.SetAttribute{ ElementType: types.StringType, Computed: true,