From bcb13be2a81ceb22dfcc8eff8eed1a9d66355b38 Mon Sep 17 00:00:00 2001 From: Kuba Mazurkiewicz <132581633+kuba-mazurkiewicz@users.noreply.github.com> Date: Thu, 8 Aug 2024 11:06:29 +0200 Subject: [PATCH] Update wireless profile api endpoint (#110) --- CHANGELOG.md | 1 + docs/data-sources/wireless_profile.md | 44 ++++++ docs/guides/changelog.md | 1 + docs/resources/wireless_profile.md | 17 +- .../data-source.tf | 3 + .../catalystcenter_wireless_profile/import.sh | 2 +- .../resource.tf | 4 +- gen/definitions/wireless_profile.yaml | 27 ++-- ..._source_catalystcenter_wireless_profile.go | 146 ++++++++++++++++++ ...ce_catalystcenter_wireless_profile_test.go | 80 ++++++++++ .../model_catalystcenter_wireless_profile.go | 72 +++++---- internal/provider/provider.go | 1 + ...esource_catalystcenter_wireless_profile.go | 41 ++--- ...ce_catalystcenter_wireless_profile_test.go | 8 +- templates/guides/changelog.md.tmpl | 1 + 15 files changed, 366 insertions(+), 82 deletions(-) create mode 100644 docs/data-sources/wireless_profile.md create mode 100644 examples/data-sources/catalystcenter_wireless_profile/data-source.tf create mode 100644 internal/provider/data_source_catalystcenter_wireless_profile.go create mode 100644 internal/provider/data_source_catalystcenter_wireless_profile_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index a750452a..f9a3c01a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## 0.1.10 (unreleased) +- BREAKING CHANGE: Modified `wireless_profile` resource to use `/intent/api/v1/wirelessProfiles` API endpoint, this resource now only works with Catalyst Center version 2.3.7.6+ - Add `wireless_device_provision` resource - Add `fabric_provision_device` resource and data source - Add `assign_templates_to_tag` resource and data source diff --git a/docs/data-sources/wireless_profile.md b/docs/data-sources/wireless_profile.md new file mode 100644 index 00000000..56fb7068 --- /dev/null +++ b/docs/data-sources/wireless_profile.md @@ -0,0 +1,44 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "catalystcenter_wireless_profile Data Source - terraform-provider-catalystcenter" +subcategory: "Wireless" +description: |- + This data source can read the Wireless Profile. +--- + +# catalystcenter_wireless_profile (Data Source) + +This data source can read the Wireless Profile. + +## Example Usage + +```terraform +data "catalystcenter_wireless_profile" "example" { + id = "76d24097-41c4-4558-a4d0-a8c07ac08470" +} +``` + + +## Schema + +### Required + +- `id` (String) The id of the object + +### Read-Only + +- `ssid_details` (Attributes List) SSID Details (see [below for nested schema](#nestedatt--ssid_details)) +- `wireless_profile_name` (String) Wireless Network Profile Name + + +### Nested Schema for `ssid_details` + +Read-Only: + +- `dot11be_profile_id` (String) 802.11be Profile Id. Applicable to IOS controllers with version 17.15 and higher. 802.11be Profiles if passed, should be same across all SSIDs in network profile being configured +- `enable_fabric` (Boolean) True if fabric is enabled, else False. Flex and fabric cannot be enabled simultaneously and a profile can only contain either flex SSIDs or fabric SSIDs and not both at the same time +- `enable_flex_connect` (Boolean) True if flex connect is enabled, else False. Flex and fabric cannot be enabled simultaneously and a profile can only contain either flex SSIDs or fabric SSIDs and not both at the same time +- `interface_name` (String) Interface Name +- `local_to_vlan` (Number) Local To Vlan Id +- `ssid_name` (String) SSID Name +- `wlan_profile_name` (String) WLAN Profile Name diff --git a/docs/guides/changelog.md b/docs/guides/changelog.md index 0f9d98a3..f399dd51 100644 --- a/docs/guides/changelog.md +++ b/docs/guides/changelog.md @@ -9,6 +9,7 @@ description: |- ## 0.1.10 (unreleased) +- BREAKING CHANGE: Modified `wireless_profile` resource to use `/intent/api/v1/wirelessProfiles` API endpoint, this resource now only works with Catalyst Center version 2.3.7.6+ - Add `wireless_device_provision` resource - Add `fabric_provision_device` resource and data source - Add `assign_templates_to_tag` resource and data source diff --git a/docs/resources/wireless_profile.md b/docs/resources/wireless_profile.md index b6649d3c..e394f666 100644 --- a/docs/resources/wireless_profile.md +++ b/docs/resources/wireless_profile.md @@ -14,10 +14,10 @@ This resource creates a wireless network profile. To associate a wireless networ ```terraform resource "catalystcenter_wireless_profile" "example" { - name = "Wireless_Profile_1" + wireless_profile_name = "Wireless_Profile_1" ssid_details = [ { - name = "mySSID1" + ssid_name = "mySSID1" enable_fabric = true enable_flex_connect = false } @@ -30,7 +30,7 @@ resource "catalystcenter_wireless_profile" "example" { ### Required -- `name` (String) Profile Name +- `wireless_profile_name` (String) Wireless Network Profile Name ### Optional @@ -45,15 +45,16 @@ resource "catalystcenter_wireless_profile" "example" { Required: -- `name` (String) Ssid Name +- `ssid_name` (String) SSID Name Optional: -- `enable_fabric` (Boolean) `true` if ssid is fabric else `false` -- `enable_flex_connect` (Boolean) `true` if flex connect is enabled else `false` +- `dot11be_profile_id` (String) 802.11be Profile Id. Applicable to IOS controllers with version 17.15 and higher. 802.11be Profiles if passed, should be same across all SSIDs in network profile being configured +- `enable_fabric` (Boolean) True if fabric is enabled, else False. Flex and fabric cannot be enabled simultaneously and a profile can only contain either flex SSIDs or fabric SSIDs and not both at the same time +- `enable_flex_connect` (Boolean) True if flex connect is enabled, else False. Flex and fabric cannot be enabled simultaneously and a profile can only contain either flex SSIDs or fabric SSIDs and not both at the same time - `interface_name` (String) Interface Name + - Default value: `management` - `local_to_vlan` (Number) Local To Vlan Id -- `policy_profile_name` (String) Policy Profile Name - `wlan_profile_name` (String) WLAN Profile Name ## Import @@ -61,5 +62,5 @@ Optional: Import is supported using the following syntax: ```shell -terraform import catalystcenter_wireless_profile.example "" +terraform import catalystcenter_wireless_profile.example "4b0b7a80-44c0-4bf2-bab5-fc24b4e0a17e" ``` diff --git a/examples/data-sources/catalystcenter_wireless_profile/data-source.tf b/examples/data-sources/catalystcenter_wireless_profile/data-source.tf new file mode 100644 index 00000000..13a3e678 --- /dev/null +++ b/examples/data-sources/catalystcenter_wireless_profile/data-source.tf @@ -0,0 +1,3 @@ +data "catalystcenter_wireless_profile" "example" { + id = "76d24097-41c4-4558-a4d0-a8c07ac08470" +} diff --git a/examples/resources/catalystcenter_wireless_profile/import.sh b/examples/resources/catalystcenter_wireless_profile/import.sh index 7bc209be..ef76597b 100644 --- a/examples/resources/catalystcenter_wireless_profile/import.sh +++ b/examples/resources/catalystcenter_wireless_profile/import.sh @@ -1 +1 @@ -terraform import catalystcenter_wireless_profile.example "" +terraform import catalystcenter_wireless_profile.example "4b0b7a80-44c0-4bf2-bab5-fc24b4e0a17e" diff --git a/examples/resources/catalystcenter_wireless_profile/resource.tf b/examples/resources/catalystcenter_wireless_profile/resource.tf index c473cc50..ff3ee7c6 100644 --- a/examples/resources/catalystcenter_wireless_profile/resource.tf +++ b/examples/resources/catalystcenter_wireless_profile/resource.tf @@ -1,8 +1,8 @@ resource "catalystcenter_wireless_profile" "example" { - name = "Wireless_Profile_1" + wireless_profile_name = "Wireless_Profile_1" ssid_details = [ { - name = "mySSID1" + ssid_name = "mySSID1" enable_fabric = true enable_flex_connect = false } diff --git a/gen/definitions/wireless_profile.yaml b/gen/definitions/wireless_profile.yaml index 2e8f4990..4e9fde23 100644 --- a/gen/definitions/wireless_profile.yaml +++ b/gen/definitions/wireless_profile.yaml @@ -1,43 +1,38 @@ --- name: Wireless Profile -rest_endpoint: /dna/intent/api/v2/wireless/profile +rest_endpoint: /intent/api/v1/wirelessProfiles id_from_query_path: response +id_from_query_path_attribute: id +import_no_id: true get_from_all: true -no_data_source: true -put_no_id: true -id_from_query_path_attribute: instanceUuid res_description: 'This resource creates a wireless network profile. To associate a wireless network profile with a site, use the `catalystcenter_associate_site_to_network_profile` resource.' doc_category: Wireless attributes: - model_name: wirelessProfileName - tf_name: name - delete_query_param_name: name - delete_query_param: true type: String match_id: true - description: Profile Name + description: Wireless Network Profile Name example: Wireless_Profile_1 - model_name: ssidDetails type: List description: SSID Details attributes: - model_name: ssidName - tf_name: name type: String id: true - description: Ssid Name + description: SSID Name example: mySSID1 test_value: catalystcenter_wireless_enterprise_ssid.test.name - model_name: enableFabric type: Bool - description: "`true` if ssid is fabric else `false`" + description: "True if fabric is enabled, else False. Flex and fabric cannot be enabled simultaneously and a profile can only contain either flex SSIDs or fabric SSIDs and not both at the same time" example: true - model_name: enableFlexConnect data_path: flexConnect type: Bool - description: "`true` if flex connect is enabled else `false`" + description: "True if flex connect is enabled, else False. Flex and fabric cannot be enabled simultaneously and a profile can only contain either flex SSIDs or fabric SSIDs and not both at the same time" example: false - model_name: localToVlan data_path: flexConnect @@ -49,19 +44,17 @@ attributes: type: String description: Interface Name exclude_test: true + default_value: management example: management - model_name: wlanProfileName - write_only: true type: String description: WLAN Profile Name exclude_test: true example: mySSID1_profile - - model_name: policyProfileName - write_only: true + - model_name: dot11beProfileId type: String - description: Policy Profile Name + description: 802.11be Profile Id. Applicable to IOS controllers with version 17.15 and higher. 802.11be Profiles if passed, should be same across all SSIDs in network profile being configured exclude_test: true - example: mySSID1_profile test_prerequisites: | resource "catalystcenter_wireless_enterprise_ssid" "test" { name = "mySSID1" diff --git a/internal/provider/data_source_catalystcenter_wireless_profile.go b/internal/provider/data_source_catalystcenter_wireless_profile.go new file mode 100644 index 00000000..2e56d46b --- /dev/null +++ b/internal/provider/data_source_catalystcenter_wireless_profile.go @@ -0,0 +1,146 @@ +// Copyright © 2023 Cisco Systems, Inc. and its affiliates. +// All rights reserved. +// +// Licensed under the Mozilla Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://mozilla.org/MPL/2.0/ +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: MPL-2.0 + +package provider + +// Section below is generated&owned by "gen/generator.go". //template:begin imports +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-log/tflog" + cc "github.com/netascode/go-catalystcenter" +) + +// End of section. //template:end imports + +// Section below is generated&owned by "gen/generator.go". //template:begin model + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ datasource.DataSource = &WirelessProfileDataSource{} + _ datasource.DataSourceWithConfigure = &WirelessProfileDataSource{} +) + +func NewWirelessProfileDataSource() datasource.DataSource { + return &WirelessProfileDataSource{} +} + +type WirelessProfileDataSource struct { + client *cc.Client +} + +func (d *WirelessProfileDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_wireless_profile" +} + +func (d *WirelessProfileDataSource) 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: "This data source can read the Wireless Profile.", + + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + MarkdownDescription: "The id of the object", + Required: true, + }, + "wireless_profile_name": schema.StringAttribute{ + MarkdownDescription: "Wireless Network Profile Name", + Computed: true, + }, + "ssid_details": schema.ListNestedAttribute{ + MarkdownDescription: "SSID Details", + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "ssid_name": schema.StringAttribute{ + MarkdownDescription: "SSID Name", + Computed: true, + }, + "enable_fabric": schema.BoolAttribute{ + MarkdownDescription: "True if fabric is enabled, else False. Flex and fabric cannot be enabled simultaneously and a profile can only contain either flex SSIDs or fabric SSIDs and not both at the same time", + Computed: true, + }, + "enable_flex_connect": schema.BoolAttribute{ + MarkdownDescription: "True if flex connect is enabled, else False. Flex and fabric cannot be enabled simultaneously and a profile can only contain either flex SSIDs or fabric SSIDs and not both at the same time", + Computed: true, + }, + "local_to_vlan": schema.Int64Attribute{ + MarkdownDescription: "Local To Vlan Id", + Computed: true, + }, + "interface_name": schema.StringAttribute{ + MarkdownDescription: "Interface Name", + Computed: true, + }, + "wlan_profile_name": schema.StringAttribute{ + MarkdownDescription: "WLAN Profile Name", + Computed: true, + }, + "dot11be_profile_id": schema.StringAttribute{ + MarkdownDescription: "802.11be Profile Id. Applicable to IOS controllers with version 17.15 and higher. 802.11be Profiles if passed, should be same across all SSIDs in network profile being configured", + Computed: true, + }, + }, + }, + }, + }, + } +} + +func (d *WirelessProfileDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, _ *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + d.client = req.ProviderData.(*CcProviderData).Client +} + +// End of section. //template:end model + +// Section below is generated&owned by "gen/generator.go". //template:begin read +func (d *WirelessProfileDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var config WirelessProfile + + // Read config + diags := req.Config.Get(ctx, &config) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, fmt.Sprintf("%s: Beginning Read", config.Id.String())) + + params := "" + res, err := d.client.Get(config.getPath() + params) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to retrieve object, got error: %s", err)) + return + } + res = res.Get("response.#(id==\"" + config.Id.ValueString() + "\")") + + config.fromBody(ctx, res) + + tflog.Debug(ctx, fmt.Sprintf("%s: Read finished successfully", config.Id.ValueString())) + + diags = resp.State.Set(ctx, &config) + resp.Diagnostics.Append(diags...) +} + +// End of section. //template:end read diff --git a/internal/provider/data_source_catalystcenter_wireless_profile_test.go b/internal/provider/data_source_catalystcenter_wireless_profile_test.go new file mode 100644 index 00000000..7537e88d --- /dev/null +++ b/internal/provider/data_source_catalystcenter_wireless_profile_test.go @@ -0,0 +1,80 @@ +// Copyright © 2023 Cisco Systems, Inc. and its affiliates. +// All rights reserved. +// +// Licensed under the Mozilla Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://mozilla.org/MPL/2.0/ +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: MPL-2.0 + +package provider + +// Section below is generated&owned by "gen/generator.go". //template:begin imports +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +// End of section. //template:end imports + +// Section below is generated&owned by "gen/generator.go". //template:begin testAccDataSource +func TestAccDataSourceCcWirelessProfile(t *testing.T) { + var checks []resource.TestCheckFunc + checks = append(checks, resource.TestCheckResourceAttr("data.catalystcenter_wireless_profile.test", "wireless_profile_name", "Wireless_Profile_1")) + checks = append(checks, resource.TestCheckResourceAttr("data.catalystcenter_wireless_profile.test", "ssid_details.0.enable_fabric", "true")) + checks = append(checks, resource.TestCheckResourceAttr("data.catalystcenter_wireless_profile.test", "ssid_details.0.enable_flex_connect", "false")) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceCcWirelessProfilePrerequisitesConfig + testAccDataSourceCcWirelessProfileConfig(), + Check: resource.ComposeTestCheckFunc(checks...), + }, + }, + }) +} + +// End of section. //template:end testAccDataSource + +// Section below is generated&owned by "gen/generator.go". //template:begin testPrerequisites +const testAccDataSourceCcWirelessProfilePrerequisitesConfig = ` +resource "catalystcenter_wireless_enterprise_ssid" "test" { + name = "mySSID1" + security_level = "wpa3_enterprise" + passphrase = "Cisco123" +} + +` + +// End of section. //template:end testPrerequisites + +// Section below is generated&owned by "gen/generator.go". //template:begin testAccDataSourceConfig +func testAccDataSourceCcWirelessProfileConfig() string { + config := `resource "catalystcenter_wireless_profile" "test" {` + "\n" + config += ` wireless_profile_name = "Wireless_Profile_1"` + "\n" + config += ` ssid_details = [{` + "\n" + config += ` ssid_name = catalystcenter_wireless_enterprise_ssid.test.name` + "\n" + config += ` enable_fabric = true` + "\n" + config += ` enable_flex_connect = false` + "\n" + config += ` }]` + "\n" + config += `}` + "\n" + + config += ` + data "catalystcenter_wireless_profile" "test" { + id = catalystcenter_wireless_profile.test.id + } + ` + return config +} + +// End of section. //template:end testAccDataSourceConfig diff --git a/internal/provider/model_catalystcenter_wireless_profile.go b/internal/provider/model_catalystcenter_wireless_profile.go index 8e8113cb..e5523684 100644 --- a/internal/provider/model_catalystcenter_wireless_profile.go +++ b/internal/provider/model_catalystcenter_wireless_profile.go @@ -30,26 +30,26 @@ import ( // Section below is generated&owned by "gen/generator.go". //template:begin types type WirelessProfile struct { - Id types.String `tfsdk:"id"` - Name types.String `tfsdk:"name"` - SsidDetails []WirelessProfileSsidDetails `tfsdk:"ssid_details"` + Id types.String `tfsdk:"id"` + WirelessProfileName types.String `tfsdk:"wireless_profile_name"` + SsidDetails []WirelessProfileSsidDetails `tfsdk:"ssid_details"` } type WirelessProfileSsidDetails struct { - Name types.String `tfsdk:"name"` + SsidName types.String `tfsdk:"ssid_name"` EnableFabric types.Bool `tfsdk:"enable_fabric"` EnableFlexConnect types.Bool `tfsdk:"enable_flex_connect"` LocalToVlan types.Int64 `tfsdk:"local_to_vlan"` InterfaceName types.String `tfsdk:"interface_name"` WlanProfileName types.String `tfsdk:"wlan_profile_name"` - PolicyProfileName types.String `tfsdk:"policy_profile_name"` + Dot11beProfileId types.String `tfsdk:"dot11be_profile_id"` } // End of section. //template:end types // Section below is generated&owned by "gen/generator.go". //template:begin getPath func (data WirelessProfile) getPath() string { - return "/dna/intent/api/v2/wireless/profile" + return "/intent/api/v1/wirelessProfiles" } // End of section. //template:end getPath @@ -66,15 +66,15 @@ func (data WirelessProfile) toBody(ctx context.Context, state WirelessProfile) s put = true } _ = put - if !data.Name.IsNull() { - body, _ = sjson.Set(body, "wirelessProfileName", data.Name.ValueString()) + if !data.WirelessProfileName.IsNull() { + body, _ = sjson.Set(body, "wirelessProfileName", data.WirelessProfileName.ValueString()) } if len(data.SsidDetails) > 0 { body, _ = sjson.Set(body, "ssidDetails", []interface{}{}) for _, item := range data.SsidDetails { itemBody := "" - if !item.Name.IsNull() { - itemBody, _ = sjson.Set(itemBody, "ssidName", item.Name.ValueString()) + if !item.SsidName.IsNull() { + itemBody, _ = sjson.Set(itemBody, "ssidName", item.SsidName.ValueString()) } if !item.EnableFabric.IsNull() { itemBody, _ = sjson.Set(itemBody, "enableFabric", item.EnableFabric.ValueBool()) @@ -91,8 +91,8 @@ func (data WirelessProfile) toBody(ctx context.Context, state WirelessProfile) s if !item.WlanProfileName.IsNull() { itemBody, _ = sjson.Set(itemBody, "wlanProfileName", item.WlanProfileName.ValueString()) } - if !item.PolicyProfileName.IsNull() { - itemBody, _ = sjson.Set(itemBody, "policyProfileName", item.PolicyProfileName.ValueString()) + if !item.Dot11beProfileId.IsNull() { + itemBody, _ = sjson.Set(itemBody, "dot11beProfileId", item.Dot11beProfileId.ValueString()) } body, _ = sjson.SetRaw(body, "ssidDetails.-1", itemBody) } @@ -105,18 +105,18 @@ func (data WirelessProfile) toBody(ctx context.Context, state WirelessProfile) s // Section below is generated&owned by "gen/generator.go". //template:begin fromBody func (data *WirelessProfile) fromBody(ctx context.Context, res gjson.Result) { if value := res.Get("wirelessProfileName"); value.Exists() { - data.Name = types.StringValue(value.String()) + data.WirelessProfileName = types.StringValue(value.String()) } else { - data.Name = types.StringNull() + data.WirelessProfileName = types.StringNull() } if value := res.Get("ssidDetails"); value.Exists() && len(value.Array()) > 0 { data.SsidDetails = make([]WirelessProfileSsidDetails, 0) value.ForEach(func(k, v gjson.Result) bool { item := WirelessProfileSsidDetails{} if cValue := v.Get("ssidName"); cValue.Exists() { - item.Name = types.StringValue(cValue.String()) + item.SsidName = types.StringValue(cValue.String()) } else { - item.Name = types.StringNull() + item.SsidName = types.StringNull() } if cValue := v.Get("enableFabric"); cValue.Exists() { item.EnableFabric = types.BoolValue(cValue.Bool()) @@ -136,7 +136,17 @@ func (data *WirelessProfile) fromBody(ctx context.Context, res gjson.Result) { if cValue := v.Get("interfaceName"); cValue.Exists() { item.InterfaceName = types.StringValue(cValue.String()) } else { - item.InterfaceName = types.StringNull() + item.InterfaceName = types.StringValue("management") + } + if cValue := v.Get("wlanProfileName"); cValue.Exists() { + item.WlanProfileName = types.StringValue(cValue.String()) + } else { + item.WlanProfileName = types.StringNull() + } + if cValue := v.Get("dot11beProfileId"); cValue.Exists() { + item.Dot11beProfileId = types.StringValue(cValue.String()) + } else { + item.Dot11beProfileId = types.StringNull() } data.SsidDetails = append(data.SsidDetails, item) return true @@ -148,14 +158,14 @@ func (data *WirelessProfile) fromBody(ctx context.Context, res gjson.Result) { // Section below is generated&owned by "gen/generator.go". //template:begin updateFromBody func (data *WirelessProfile) updateFromBody(ctx context.Context, res gjson.Result) { - if value := res.Get("wirelessProfileName"); value.Exists() && !data.Name.IsNull() { - data.Name = types.StringValue(value.String()) + if value := res.Get("wirelessProfileName"); value.Exists() && !data.WirelessProfileName.IsNull() { + data.WirelessProfileName = types.StringValue(value.String()) } else { - data.Name = types.StringNull() + data.WirelessProfileName = types.StringNull() } for i := range data.SsidDetails { keys := [...]string{"ssidName"} - keyValues := [...]string{data.SsidDetails[i].Name.ValueString()} + keyValues := [...]string{data.SsidDetails[i].SsidName.ValueString()} var r gjson.Result res.Get("ssidDetails").ForEach( @@ -176,10 +186,10 @@ func (data *WirelessProfile) updateFromBody(ctx context.Context, res gjson.Resul return true }, ) - if value := r.Get("ssidName"); value.Exists() && !data.SsidDetails[i].Name.IsNull() { - data.SsidDetails[i].Name = types.StringValue(value.String()) + if value := r.Get("ssidName"); value.Exists() && !data.SsidDetails[i].SsidName.IsNull() { + data.SsidDetails[i].SsidName = types.StringValue(value.String()) } else { - data.SsidDetails[i].Name = types.StringNull() + data.SsidDetails[i].SsidName = types.StringNull() } if value := r.Get("enableFabric"); value.Exists() && !data.SsidDetails[i].EnableFabric.IsNull() { data.SsidDetails[i].EnableFabric = types.BoolValue(value.Bool()) @@ -198,9 +208,19 @@ func (data *WirelessProfile) updateFromBody(ctx context.Context, res gjson.Resul } if value := r.Get("interfaceName"); value.Exists() && !data.SsidDetails[i].InterfaceName.IsNull() { data.SsidDetails[i].InterfaceName = types.StringValue(value.String()) - } else { + } else if data.SsidDetails[i].InterfaceName.ValueString() != "management" { data.SsidDetails[i].InterfaceName = types.StringNull() } + if value := r.Get("wlanProfileName"); value.Exists() && !data.SsidDetails[i].WlanProfileName.IsNull() { + data.SsidDetails[i].WlanProfileName = types.StringValue(value.String()) + } else { + data.SsidDetails[i].WlanProfileName = types.StringNull() + } + if value := r.Get("dot11beProfileId"); value.Exists() && !data.SsidDetails[i].Dot11beProfileId.IsNull() { + data.SsidDetails[i].Dot11beProfileId = types.StringValue(value.String()) + } else { + data.SsidDetails[i].Dot11beProfileId = types.StringNull() + } } } @@ -208,7 +228,7 @@ func (data *WirelessProfile) updateFromBody(ctx context.Context, res gjson.Resul // Section below is generated&owned by "gen/generator.go". //template:begin isNull func (data *WirelessProfile) isNull(ctx context.Context, res gjson.Result) bool { - if !data.Name.IsNull() { + if !data.WirelessProfileName.IsNull() { return false } if len(data.SsidDetails) > 0 { diff --git a/internal/provider/provider.go b/internal/provider/provider.go index ac73e51e..10c126e2 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -328,6 +328,7 @@ func (p *CcProvider) DataSources(ctx context.Context) []func() datasource.DataSo NewTransitPeerNetworkDataSource, NewUserDataSource, NewWirelessEnterpriseSSIDDataSource, + NewWirelessProfileDataSource, NewWirelessRFProfileDataSource, } } diff --git a/internal/provider/resource_catalystcenter_wireless_profile.go b/internal/provider/resource_catalystcenter_wireless_profile.go index a830f167..161ab3ec 100644 --- a/internal/provider/resource_catalystcenter_wireless_profile.go +++ b/internal/provider/resource_catalystcenter_wireless_profile.go @@ -29,6 +29,7 @@ import ( "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/stringdefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" @@ -68,8 +69,8 @@ func (r *WirelessProfileResource) Schema(ctx context.Context, req resource.Schem stringplanmodifier.UseStateForUnknown(), }, }, - "name": schema.StringAttribute{ - MarkdownDescription: helpers.NewAttributeDescription("Profile Name").String, + "wireless_profile_name": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("Wireless Network Profile Name").String, Required: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), @@ -80,16 +81,16 @@ func (r *WirelessProfileResource) Schema(ctx context.Context, req resource.Schem Optional: true, NestedObject: schema.NestedAttributeObject{ Attributes: map[string]schema.Attribute{ - "name": schema.StringAttribute{ - MarkdownDescription: helpers.NewAttributeDescription("Ssid Name").String, + "ssid_name": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("SSID Name").String, Required: true, }, "enable_fabric": schema.BoolAttribute{ - MarkdownDescription: helpers.NewAttributeDescription("`true` if ssid is fabric else `false`").String, + MarkdownDescription: helpers.NewAttributeDescription("True if fabric is enabled, else False. Flex and fabric cannot be enabled simultaneously and a profile can only contain either flex SSIDs or fabric SSIDs and not both at the same time").String, Optional: true, }, "enable_flex_connect": schema.BoolAttribute{ - MarkdownDescription: helpers.NewAttributeDescription("`true` if flex connect is enabled else `false`").String, + MarkdownDescription: helpers.NewAttributeDescription("True if flex connect is enabled, else False. Flex and fabric cannot be enabled simultaneously and a profile can only contain either flex SSIDs or fabric SSIDs and not both at the same time").String, Optional: true, }, "local_to_vlan": schema.Int64Attribute{ @@ -97,15 +98,17 @@ func (r *WirelessProfileResource) Schema(ctx context.Context, req resource.Schem Optional: true, }, "interface_name": schema.StringAttribute{ - MarkdownDescription: helpers.NewAttributeDescription("Interface Name").String, + MarkdownDescription: helpers.NewAttributeDescription("Interface Name").AddDefaultValueDescription("management").String, Optional: true, + Computed: true, + Default: stringdefault.StaticString("management"), }, "wlan_profile_name": schema.StringAttribute{ MarkdownDescription: helpers.NewAttributeDescription("WLAN Profile Name").String, Optional: true, }, - "policy_profile_name": schema.StringAttribute{ - MarkdownDescription: helpers.NewAttributeDescription("Policy Profile Name").String, + "dot11be_profile_id": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("802.11be Profile Id. Applicable to IOS controllers with version 17.15 and higher. 802.11be Profiles if passed, should be same across all SSIDs in network profile being configured").String, Optional: true, }, }, @@ -153,7 +156,7 @@ func (r *WirelessProfileResource) Create(ctx context.Context, req resource.Creat resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to retrieve object (GET), got error: %s, %s", err, res.String())) return } - plan.Id = types.StringValue(res.Get("response.#(wirelessProfileName==\"" + plan.Name.ValueString() + "\").instanceUuid").String()) + plan.Id = types.StringValue(res.Get("response.#(wirelessProfileName==\"" + plan.WirelessProfileName.ValueString() + "\").id").String()) tflog.Debug(ctx, fmt.Sprintf("%s: Create finished successfully", plan.Id.ValueString())) @@ -185,7 +188,7 @@ func (r *WirelessProfileResource) Read(ctx context.Context, req resource.ReadReq resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to retrieve object (GET), got error: %s, %s", err, res.String())) return } - res = res.Get("response.#(instanceUuid==\"" + state.Id.ValueString() + "\")") + res = res.Get("response.#(id==\"" + state.Id.ValueString() + "\")") // If every attribute is set to null we are dealing with an import operation and therefore reading all attributes if state.isNull(ctx, res) { @@ -223,7 +226,7 @@ func (r *WirelessProfileResource) Update(ctx context.Context, req resource.Updat body := plan.toBody(ctx, state) params := "" - res, err := r.client.Put(plan.getPath()+params, body) + res, err := r.client.Put(plan.getPath()+"/"+url.QueryEscape(plan.Id.ValueString())+params, body) if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to configure object (PUT), got error: %s, %s", err, res.String())) return @@ -249,8 +252,7 @@ func (r *WirelessProfileResource) Delete(ctx context.Context, req resource.Delet } tflog.Debug(ctx, fmt.Sprintf("%s: Beginning Delete", state.Id.ValueString())) - params := "?name=" + url.QueryEscape(state.Name.ValueString()) - res, err := r.client.Delete(state.getPath() + params) + res, err := r.client.Delete(state.getPath() + "/" + url.QueryEscape(state.Id.ValueString())) if err != nil { resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to delete object (DELETE), got error: %s, %s", err, res.String())) return @@ -265,16 +267,7 @@ func (r *WirelessProfileResource) Delete(ctx context.Context, req resource.Delet // Section below is generated&owned by "gen/generator.go". //template:begin import func (r *WirelessProfileResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - idParts := strings.Split(req.ID, ",") - - if len(idParts) != 1 || idParts[0] == "" { - resp.Diagnostics.AddError( - "Unexpected Import Identifier", - fmt.Sprintf("Expected import identifier with format: . Got: %q", req.ID), - ) - return - } - resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), idParts[0])...) + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) } // End of section. //template:end import diff --git a/internal/provider/resource_catalystcenter_wireless_profile_test.go b/internal/provider/resource_catalystcenter_wireless_profile_test.go index b83e12bf..c6bbdccd 100644 --- a/internal/provider/resource_catalystcenter_wireless_profile_test.go +++ b/internal/provider/resource_catalystcenter_wireless_profile_test.go @@ -30,7 +30,7 @@ import ( // Section below is generated&owned by "gen/generator.go". //template:begin testAcc func TestAccCcWirelessProfile(t *testing.T) { var checks []resource.TestCheckFunc - checks = append(checks, resource.TestCheckResourceAttr("catalystcenter_wireless_profile.test", "name", "Wireless_Profile_1")) + checks = append(checks, resource.TestCheckResourceAttr("catalystcenter_wireless_profile.test", "wireless_profile_name", "Wireless_Profile_1")) checks = append(checks, resource.TestCheckResourceAttr("catalystcenter_wireless_profile.test", "ssid_details.0.enable_fabric", "true")) checks = append(checks, resource.TestCheckResourceAttr("catalystcenter_wireless_profile.test", "ssid_details.0.enable_flex_connect", "false")) @@ -73,7 +73,7 @@ resource "catalystcenter_wireless_enterprise_ssid" "test" { // Section below is generated&owned by "gen/generator.go". //template:begin testAccConfigMinimal func testAccCcWirelessProfileConfig_minimum() string { config := `resource "catalystcenter_wireless_profile" "test" {` + "\n" - config += ` name = "Wireless_Profile_1"` + "\n" + config += ` wireless_profile_name = "Wireless_Profile_1"` + "\n" config += `}` + "\n" return config } @@ -83,9 +83,9 @@ func testAccCcWirelessProfileConfig_minimum() string { // Section below is generated&owned by "gen/generator.go". //template:begin testAccConfigAll func testAccCcWirelessProfileConfig_all() string { config := `resource "catalystcenter_wireless_profile" "test" {` + "\n" - config += ` name = "Wireless_Profile_1"` + "\n" + config += ` wireless_profile_name = "Wireless_Profile_1"` + "\n" config += ` ssid_details = [{` + "\n" - config += ` name = catalystcenter_wireless_enterprise_ssid.test.name` + "\n" + config += ` ssid_name = catalystcenter_wireless_enterprise_ssid.test.name` + "\n" config += ` enable_fabric = true` + "\n" config += ` enable_flex_connect = false` + "\n" config += ` }]` + "\n" diff --git a/templates/guides/changelog.md.tmpl b/templates/guides/changelog.md.tmpl index 0f9d98a3..f399dd51 100644 --- a/templates/guides/changelog.md.tmpl +++ b/templates/guides/changelog.md.tmpl @@ -9,6 +9,7 @@ description: |- ## 0.1.10 (unreleased) +- BREAKING CHANGE: Modified `wireless_profile` resource to use `/intent/api/v1/wirelessProfiles` API endpoint, this resource now only works with Catalyst Center version 2.3.7.6+ - Add `wireless_device_provision` resource - Add `fabric_provision_device` resource and data source - Add `assign_templates_to_tag` resource and data source