From 2f45ebff84ea5143e2da11eadeb7255102920050 Mon Sep 17 00:00:00 2001 From: Cameron Schaeffer Date: Mon, 24 Nov 2025 16:04:13 +0000 Subject: [PATCH] feat: add crypto engine compliance shield disable support - Add iosxe_crypto_engine resource and data source - Support for disabling crypto engine compliance shield - Tested on Cat8K 17.15, Cat9K 17.15, and Cat8K 17.12 - Full RESTCONF support across all tested platforms --- CHANGELOG.md | 4 + docs/data-sources/crypto_engine.md | 30 ++ docs/guides/changelog.md | 37 ++ docs/resources/crypto_engine.md | 43 ++ .../iosxe_crypto_engine/data-source.tf | 2 + .../resources/iosxe_crypto_engine/import.sh | 1 + .../resources/iosxe_crypto_engine/resource.tf | 3 + gen/definitions/crypto_engine.yaml | 8 + .../data_source_iosxe_crypto_engine.go | 147 ++++++ .../data_source_iosxe_crypto_engine_test.go | 69 +++ .../provider/model_iosxe_crypto_engine.go | 280 ++++++++++ internal/provider/provider.go | 2 + .../provider/resource_iosxe_crypto_engine.go | 499 ++++++++++++++++++ .../resource_iosxe_crypto_engine_test.go | 96 ++++ templates/guides/changelog.md.tmpl | 37 ++ 15 files changed, 1258 insertions(+) create mode 100644 docs/data-sources/crypto_engine.md create mode 100644 docs/resources/crypto_engine.md create mode 100644 examples/data-sources/iosxe_crypto_engine/data-source.tf create mode 100644 examples/resources/iosxe_crypto_engine/import.sh create mode 100644 examples/resources/iosxe_crypto_engine/resource.tf create mode 100644 gen/definitions/crypto_engine.yaml create mode 100644 internal/provider/data_source_iosxe_crypto_engine.go create mode 100644 internal/provider/data_source_iosxe_crypto_engine_test.go create mode 100644 internal/provider/model_iosxe_crypto_engine.go create mode 100644 internal/provider/resource_iosxe_crypto_engine.go create mode 100644 internal/provider/resource_iosxe_crypto_engine_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index b4bc8e93..81648958 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.11.0 + +- Add `iosxe_crypto_engine` resource and data source for crypto engine compliance shield disable configuration + ## 0.10.2 - Fix issue with incorrect reading of lists via NETCONF diff --git a/docs/data-sources/crypto_engine.md b/docs/data-sources/crypto_engine.md new file mode 100644 index 00000000..aabd513a --- /dev/null +++ b/docs/data-sources/crypto_engine.md @@ -0,0 +1,30 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "iosxe_crypto_engine Data Source - terraform-provider-iosxe" +subcategory: "Crypto" +description: |- + This data source can read the Crypto Engine configuration. +--- + +# iosxe_crypto_engine (Data Source) + +This data source can read the Crypto Engine configuration. + +## Example Usage + +```terraform +data "iosxe_crypto_engine" "example" { +} +``` + + +## Schema + +### Optional + +- `device` (String) A device name from the provider configuration. + +### Read-Only + +- `compliance_shield_disable` (Boolean) Allow weak crypto to be configured +- `id` (String) The path of the retrieved object. diff --git a/docs/guides/changelog.md b/docs/guides/changelog.md index 665e52f1..3f8e22a9 100644 --- a/docs/guides/changelog.md +++ b/docs/guides/changelog.md @@ -7,6 +7,43 @@ description: |- # Changelog +## 0.11.0 + +- Add `iosxe_crypto_engine` resource and data source for crypto engine compliance shield disable configuration +- Fix `iosxe_yang` resource payload ordering with NETCONF, [link](https://github.com/CiscoDevNet/terraform-provider-iosxe/issues/372) +- Add `ip_igmp_version` attribute to `iosxe_interface_ethernet`, `iosxe_interface_loopback`, `iosxe_interface_port_channel`, `iosxe_interface_port_channel_subinterface`, `iosxe_interface_tunnel`, and `iosxe_interface_vlan` resources and data sources +- Add `ip_default_gateway` attribute to `iosxe_system` resource and data source for default gateway configuration on non-routing devices +- Add `device_classifier` attribute to `iosxe_system` resource and data source for endpoint device classification +- Add `table_maps` attribute to `iosxe_system` resource and data source for QoS table map configuration with DSCP/CoS value translation +- Enhance `set_communities` attribute documentation in `iosxe_route_map` to clarify support for well-known BGP community values (internet, local-AS, no-advertise, no-export, gshut) +- Add `route_map` attribute to `iosxe_bgp_l2vpn_evpn_neighbor` resource and data source +- Add `import_path_selection_all` and `ipv4_unicast_aggregate_addresses.summary_only` attributes to `iosxe_bgp_address_family_ipv4_vrf` resource and data source +- BREAKING CHANGE: Rename `evpn_instance` to `evpn_instance_legacy` and `evpn_instance_vni` to `evpn_instance_vni_legacy` in `iosxe_vlan_configuration` resource and data source +- Add `evpn_instance`, `evpn_instance_vni`, and `evpn_instance_protected` attributes to `iosxe_vlan_configuration` resource and data source +- Add `evpn_instance_profile` and `evpn_instance_profile_protected` attributes to `iosxe_vlan_configuration` resource and data source +- Add `ttl` attribute to `iosxe_flow_exporter` resource and data source +- Add `match_routing_vrf_input`, `match_vxlan_vnid`, `match_vxlan_vtep_input`, and `match_vxlan_vtep_output` attributes to `iosxe_flow_record` resource and data source +- Add `register_source_interface_loopback` attributes to `iosxe_pim` resource and data source +- Add `iosxe_bgp_address_family_ipv4_mvpn` resource and data source +- Add `iosxe_bgp_ipv4_mvpn_neighbor` resource and data source +- Add `vlan_based_multicast_advertise` attribute to `iosxe_evpn_instance` resource and data source +- Add `multicast_advertise` attribute to `iosxe_evpn` resource and data source +- Add `carrier_delay_msec` and `hold_queues` attributes to `iosxe_interface_ethernet` resource and data source +- Add `iosxe_pim_ipv6` resource and data source +- Add `iosxe_interface_pim_ipv6` resource and data source +- Add `iosxe_multicast` resource and data source +- Add `deadtime` attribute to AAA group server radius in `iosxe_aaa` resource and data source +- Add `key_encryption`, `automate_tester_ignore_auth_port`, and `automate_tester_idle_time` attributes to `iosxe_radius` resource and data source +- Add `authentication_mac_move_permit` and `authentication_mac_move_deny_uncontrolled` attributes to `iosxe_system` resource and data source +- Add `dot1x` and `dot1x_default_*` attributes to `iosxe_aaa_accounting` resource and data source +- BREAKING CHANGE: Rename `iosxe_tacacs_server` resource and data source to `iosxe_tacacs` +- Add `port` attribute to `iosxe_tacacs` resource and data source +- BREAKING CHANGE: Add new `iosxe_tacacs_server` resource and data source +- Add `enable_default_group_legacy`, `enable_default_enable_legacy`, `enable_default_line_legacy` and `enable_default_none_legacy` attributes to `iosxe_aaa_authentication` resource and data source +- Add `iosxe_isis` resource and data source +- Add `iosxe_interface_isis` resource and data source +- Add `ip_router_isis` attribute to `iosxe_interface_ethernet`, `iosxe_interface_loopback`, `iosxe_interface_port_channel_subinterface`, `iosxe_interface_port_channel`, `iosxe_interface_tunnel` and `iosxe_interface_vlan` resources and data sources + ## 0.10.2 - Fix issue with incorrect reading of lists via NETCONF diff --git a/docs/resources/crypto_engine.md b/docs/resources/crypto_engine.md new file mode 100644 index 00000000..cbc84b36 --- /dev/null +++ b/docs/resources/crypto_engine.md @@ -0,0 +1,43 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "iosxe_crypto_engine Resource - terraform-provider-iosxe" +subcategory: "Crypto" +description: |- + This resource can manage the Crypto Engine configuration. +--- + +# iosxe_crypto_engine (Resource) + +This resource can manage the Crypto Engine configuration. + +## Example Usage + +```terraform +resource "iosxe_crypto_engine" "example" { + compliance_shield_disable = true +} +``` + + +## Schema + +### Optional + +- `compliance_shield_disable` (Boolean) Allow weak crypto to be configured +- `delete_mode` (String) Configure behavior when deleting/destroying the resource. Either delete the entire object (YANG container) being managed, or only delete the individual resource attributes configured explicitly and leave everything else as-is. Default value is `all`. + - Choices: `all`, `attributes` +- `device` (String) A device name from the provider configuration. + +### Read-Only + +- `id` (String) The path of the object. + +## Import + +Import is supported using the following syntax: + +The [`terraform import` command](https://developer.hashicorp.com/terraform/cli/commands/import) can be used, for example: + +```shell +terraform import iosxe_crypto_engine.example "" +``` diff --git a/examples/data-sources/iosxe_crypto_engine/data-source.tf b/examples/data-sources/iosxe_crypto_engine/data-source.tf new file mode 100644 index 00000000..687f359f --- /dev/null +++ b/examples/data-sources/iosxe_crypto_engine/data-source.tf @@ -0,0 +1,2 @@ +data "iosxe_crypto_engine" "example" { +} diff --git a/examples/resources/iosxe_crypto_engine/import.sh b/examples/resources/iosxe_crypto_engine/import.sh new file mode 100644 index 00000000..5a842e50 --- /dev/null +++ b/examples/resources/iosxe_crypto_engine/import.sh @@ -0,0 +1 @@ +terraform import iosxe_crypto_engine.example "" diff --git a/examples/resources/iosxe_crypto_engine/resource.tf b/examples/resources/iosxe_crypto_engine/resource.tf new file mode 100644 index 00000000..8d3aa044 --- /dev/null +++ b/examples/resources/iosxe_crypto_engine/resource.tf @@ -0,0 +1,3 @@ +resource "iosxe_crypto_engine" "example" { + compliance_shield_disable = true +} diff --git a/gen/definitions/crypto_engine.yaml b/gen/definitions/crypto_engine.yaml new file mode 100644 index 00000000..4d29eefe --- /dev/null +++ b/gen/definitions/crypto_engine.yaml @@ -0,0 +1,8 @@ +--- +name: Crypto Engine +path: Cisco-IOS-XE-native:native/crypto/Cisco-IOS-XE-crypto:engine +doc_category: Crypto +attributes: + - yang_name: compliance/shield/disable + tf_name: compliance_shield_disable + example: true diff --git a/internal/provider/data_source_iosxe_crypto_engine.go b/internal/provider/data_source_iosxe_crypto_engine.go new file mode 100644 index 00000000..b00f2df2 --- /dev/null +++ b/internal/provider/data_source_iosxe_crypto_engine.go @@ -0,0 +1,147 @@ +// 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 + +// Code generated by "gen/generator.go"; DO NOT EDIT. + +package provider + +// Section below is generated&owned by "gen/generator.go". //template:begin imports +import ( + "context" + "fmt" + + "github.com/CiscoDevNet/terraform-provider-iosxe/internal/provider/helpers" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +// 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 = &CryptoEngineDataSource{} + _ datasource.DataSourceWithConfigure = &CryptoEngineDataSource{} +) + +func NewCryptoEngineDataSource() datasource.DataSource { + return &CryptoEngineDataSource{} +} + +type CryptoEngineDataSource struct { + data *IosxeProviderData +} + +func (d *CryptoEngineDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_crypto_engine" +} + +func (d *CryptoEngineDataSource) 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 Crypto Engine configuration.", + + Attributes: map[string]schema.Attribute{ + "device": schema.StringAttribute{ + MarkdownDescription: "A device name from the provider configuration.", + Optional: true, + }, + "id": schema.StringAttribute{ + MarkdownDescription: "The path of the retrieved object.", + Computed: true, + }, + "compliance_shield_disable": schema.BoolAttribute{ + MarkdownDescription: "Allow weak crypto to be configured", + Computed: true, + }, + }, + } +} + +func (d *CryptoEngineDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, _ *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + d.data = req.ProviderData.(*IosxeProviderData) +} + +// End of section. //template:end model + +// Section below is generated&owned by "gen/generator.go". //template:begin read + +func (d *CryptoEngineDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var config CryptoEngineData + + // 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.getPath())) + + device, ok := d.data.Devices[config.Device.ValueString()] + if !ok { + resp.Diagnostics.AddAttributeError(path.Root("device"), "Invalid device", fmt.Sprintf("Device '%s' does not exist in provider configuration.", config.Device.ValueString())) + return + } + + if device.Protocol == "restconf" { + res, err := device.RestconfClient.GetData(config.getPath()) + if res.StatusCode == 404 { + config = CryptoEngineData{Device: config.Device} + } else { + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to retrieve object (%s), got error: %s", config.getPath(), err)) + return + } + + config.fromBody(ctx, res.Res) + } + } else { + // Serialize NETCONF operations when reuse disabled (concurrent reads allowed when reuse enabled) + locked := helpers.AcquireNetconfLock(&device.NetconfOpMutex, device.ReuseConnection, false) + if locked { + defer device.NetconfOpMutex.Unlock() + } + defer helpers.CloseNetconfConnection(ctx, device.NetconfClient, device.ReuseConnection) + + filter := helpers.GetXpathFilter(config.getXPath()) + res, err := device.NetconfClient.GetConfig(ctx, "running", filter) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to retrieve object (%s), got error: %s", config.getPath(), err)) + return + } + + config.fromBodyXML(ctx, res.Res) + } + + config.Id = types.StringValue(config.getPath()) + + tflog.Debug(ctx, fmt.Sprintf("%s: Read finished successfully", config.getPath())) + + diags = resp.State.Set(ctx, &config) + resp.Diagnostics.Append(diags...) +} + +// End of section. //template:end read diff --git a/internal/provider/data_source_iosxe_crypto_engine_test.go b/internal/provider/data_source_iosxe_crypto_engine_test.go new file mode 100644 index 00000000..974a31ed --- /dev/null +++ b/internal/provider/data_source_iosxe_crypto_engine_test.go @@ -0,0 +1,69 @@ +// 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 + +// Code generated by "gen/generator.go"; DO NOT EDIT. + +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 TestAccDataSourceIosxeCryptoEngine(t *testing.T) { + var checks []resource.TestCheckFunc + checks = append(checks, resource.TestCheckResourceAttr("data.iosxe_crypto_engine.test", "compliance_shield_disable", "true")) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceIosxeCryptoEngineConfig(), + Check: resource.ComposeTestCheckFunc(checks...), + }, + }, + }) +} + +// End of section. //template:end testAccDataSource + +// Section below is generated&owned by "gen/generator.go". //template:begin testPrerequisites +// End of section. //template:end testPrerequisites + +// Section below is generated&owned by "gen/generator.go". //template:begin testAccDataSourceConfig + +func testAccDataSourceIosxeCryptoEngineConfig() string { + config := `resource "iosxe_crypto_engine" "test" {` + "\n" + config += ` delete_mode = "attributes"` + "\n" + config += ` compliance_shield_disable = true` + "\n" + config += `}` + "\n" + + config += ` + data "iosxe_crypto_engine" "test" { + depends_on = [iosxe_crypto_engine.test] + } + ` + return config +} + +// End of section. //template:end testAccDataSourceConfig diff --git a/internal/provider/model_iosxe_crypto_engine.go b/internal/provider/model_iosxe_crypto_engine.go new file mode 100644 index 00000000..28b66b3c --- /dev/null +++ b/internal/provider/model_iosxe_crypto_engine.go @@ -0,0 +1,280 @@ +// 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 + +// Code generated by "gen/generator.go"; DO NOT EDIT. + +package provider + +// Section below is generated&owned by "gen/generator.go". //template:begin imports +import ( + "context" + "fmt" + "regexp" + + "github.com/CiscoDevNet/terraform-provider-iosxe/internal/provider/helpers" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/netascode/go-netconf" + "github.com/netascode/xmldot" + "github.com/tidwall/gjson" + "github.com/tidwall/sjson" +) + +// End of section. //template:end imports + +// Section below is generated&owned by "gen/generator.go". //template:begin types +type CryptoEngine struct { + Device types.String `tfsdk:"device"` + Id types.String `tfsdk:"id"` + DeleteMode types.String `tfsdk:"delete_mode"` + ComplianceShieldDisable types.Bool `tfsdk:"compliance_shield_disable"` +} + +type CryptoEngineData struct { + Device types.String `tfsdk:"device"` + Id types.String `tfsdk:"id"` + ComplianceShieldDisable types.Bool `tfsdk:"compliance_shield_disable"` +} + +// End of section. //template:end types + +// Section below is generated&owned by "gen/generator.go". //template:begin getPath + +func (data CryptoEngine) getPath() string { + return "Cisco-IOS-XE-native:native/crypto/Cisco-IOS-XE-crypto:engine" +} + +func (data CryptoEngineData) getPath() string { + return "Cisco-IOS-XE-native:native/crypto/Cisco-IOS-XE-crypto:engine" +} + +// if last path element has a key -> remove it +func (data CryptoEngine) getPathShort() string { + path := data.getPath() + re := regexp.MustCompile(`(.*)=[^\/]*$`) + matches := re.FindStringSubmatch(path) + if len(matches) <= 1 { + return path + } + return matches[1] +} + +// getXPath returns the XPath for NETCONF operations +func (data CryptoEngine) getXPath() string { + path := "Cisco-IOS-XE-native:native/crypto/Cisco-IOS-XE-crypto:engine" + return path +} + +func (data CryptoEngineData) getXPath() string { + path := "Cisco-IOS-XE-native:native/crypto/Cisco-IOS-XE-crypto:engine" + return path +} + +// End of section. //template:end getPath + +// Section below is generated&owned by "gen/generator.go". //template:begin toBody + +func (data CryptoEngine) toBody(ctx context.Context) string { + body := `{"` + helpers.LastElement(data.getPath()) + `":{}}` + if !data.ComplianceShieldDisable.IsNull() && !data.ComplianceShieldDisable.IsUnknown() { + if data.ComplianceShieldDisable.ValueBool() { + body, _ = sjson.Set(body, helpers.LastElement(data.getPath())+"."+"compliance.shield.disable", map[string]string{}) + } + } + return body +} + +// End of section. //template:end toBody + +// Section below is generated&owned by "gen/generator.go". //template:begin toBodyXML + +func (data CryptoEngine) toBodyXML(ctx context.Context) string { + body := netconf.Body{} + if !data.ComplianceShieldDisable.IsNull() && !data.ComplianceShieldDisable.IsUnknown() { + if data.ComplianceShieldDisable.ValueBool() { + body = helpers.SetFromXPath(body, data.getXPath()+"/compliance/shield/disable", "") + } else { + body = helpers.RemoveFromXPath(body, data.getXPath()+"/compliance/shield/disable") + } + } + bodyString, err := body.String() + if err != nil { + tflog.Error(ctx, fmt.Sprintf("Error converting body to string: %s", err)) + } + return bodyString +} + +// End of section. //template:end toBodyXML + +// Section below is generated&owned by "gen/generator.go". //template:begin updateFromBody + +func (data *CryptoEngine) updateFromBody(ctx context.Context, res gjson.Result) { + prefix := helpers.LastElement(data.getPath()) + "." + if res.Get(helpers.LastElement(data.getPath())).IsArray() { + prefix += "0." + } + if value := res.Get(prefix + "compliance.shield.disable"); !data.ComplianceShieldDisable.IsNull() { + if value.Exists() { + data.ComplianceShieldDisable = types.BoolValue(true) + } else { + data.ComplianceShieldDisable = types.BoolValue(false) + } + } else { + data.ComplianceShieldDisable = types.BoolNull() + } +} + +// End of section. //template:end updateFromBody + +// Section below is generated&owned by "gen/generator.go". //template:begin updateFromBodyXML + +func (data *CryptoEngine) updateFromBodyXML(ctx context.Context, res xmldot.Result) { + if value := helpers.GetFromXPath(res, "data"+data.getXPath()+"/compliance/shield/disable"); !data.ComplianceShieldDisable.IsNull() { + if value.Exists() { + data.ComplianceShieldDisable = types.BoolValue(true) + } else { + data.ComplianceShieldDisable = types.BoolValue(false) + } + } else { + data.ComplianceShieldDisable = types.BoolNull() + } +} + +// End of section. //template:end updateFromBodyXML + +// Section below is generated&owned by "gen/generator.go". //template:begin fromBody + +func (data *CryptoEngine) fromBody(ctx context.Context, res gjson.Result) { + prefix := helpers.LastElement(data.getPath()) + "." + if res.Get(helpers.LastElement(data.getPath())).IsArray() { + prefix += "0." + } + if value := res.Get(prefix + "compliance.shield.disable"); value.Exists() { + data.ComplianceShieldDisable = types.BoolValue(true) + } else { + data.ComplianceShieldDisable = types.BoolValue(false) + } +} + +// End of section. //template:end fromBody + +// Section below is generated&owned by "gen/generator.go". //template:begin fromBodyData + +func (data *CryptoEngineData) fromBody(ctx context.Context, res gjson.Result) { + prefix := helpers.LastElement(data.getPath()) + "." + if res.Get(helpers.LastElement(data.getPath())).IsArray() { + prefix += "0." + } + if value := res.Get(prefix + "compliance.shield.disable"); value.Exists() { + data.ComplianceShieldDisable = types.BoolValue(true) + } else { + data.ComplianceShieldDisable = types.BoolValue(false) + } +} + +// End of section. //template:end fromBodyData + +// Section below is generated&owned by "gen/generator.go". //template:begin fromBodyXML + +func (data *CryptoEngine) fromBodyXML(ctx context.Context, res xmldot.Result) { + if value := helpers.GetFromXPath(res, "data"+data.getXPath()+"/compliance/shield/disable"); value.Exists() { + data.ComplianceShieldDisable = types.BoolValue(true) + } else { + data.ComplianceShieldDisable = types.BoolValue(false) + } +} + +// End of section. //template:end fromBodyXML + +// Section below is generated&owned by "gen/generator.go". //template:begin fromBodyDataXML + +func (data *CryptoEngineData) fromBodyXML(ctx context.Context, res xmldot.Result) { + if value := helpers.GetFromXPath(res, "data"+data.getXPath()+"/compliance/shield/disable"); value.Exists() { + data.ComplianceShieldDisable = types.BoolValue(true) + } else { + data.ComplianceShieldDisable = types.BoolValue(false) + } +} + +// End of section. //template:end fromBodyDataXML + +// Section below is generated&owned by "gen/generator.go". //template:begin getDeletedItems + +func (data *CryptoEngine) getDeletedItems(ctx context.Context, state CryptoEngine) []string { + deletedItems := make([]string, 0) + if !state.ComplianceShieldDisable.IsNull() && data.ComplianceShieldDisable.IsNull() { + deletedItems = append(deletedItems, fmt.Sprintf("%v/compliance/shield/disable", state.getPath())) + } + + return deletedItems +} + +// End of section. //template:end getDeletedItems + +// Section below is generated&owned by "gen/generator.go". //template:begin addDeletedItemsXML + +func (data *CryptoEngine) addDeletedItemsXML(ctx context.Context, state CryptoEngine, body string) string { + b := netconf.NewBody(body) + if !state.ComplianceShieldDisable.IsNull() && data.ComplianceShieldDisable.IsNull() { + b = helpers.RemoveFromXPath(b, state.getXPath()+"/compliance/shield/disable") + } + + b = helpers.CleanupRedundantRemoveOperations(b) + return b.Res() +} + +// End of section. //template:end addDeletedItemsXML + +// Section below is generated&owned by "gen/generator.go". //template:begin getEmptyLeafsDelete + +func (data *CryptoEngine) getEmptyLeafsDelete(ctx context.Context) []string { + emptyLeafsDelete := make([]string, 0) + if !data.ComplianceShieldDisable.IsNull() && !data.ComplianceShieldDisable.ValueBool() { + emptyLeafsDelete = append(emptyLeafsDelete, fmt.Sprintf("%v/compliance/shield/disable", data.getPath())) + } + + return emptyLeafsDelete +} + +// End of section. //template:end getEmptyLeafsDelete + +// Section below is generated&owned by "gen/generator.go". //template:begin getDeletePaths + +func (data *CryptoEngine) getDeletePaths(ctx context.Context) []string { + var deletePaths []string + if !data.ComplianceShieldDisable.IsNull() { + deletePaths = append(deletePaths, fmt.Sprintf("%v/compliance/shield/disable", data.getPath())) + } + + return deletePaths +} + +// End of section. //template:end getDeletePaths + +// Section below is generated&owned by "gen/generator.go". //template:begin addDeletePathsXML + +func (data *CryptoEngine) addDeletePathsXML(ctx context.Context, body string) string { + b := netconf.NewBody(body) + if !data.ComplianceShieldDisable.IsNull() { + b = helpers.RemoveFromXPath(b, data.getXPath()+"/compliance/shield/disable") + } + + b = helpers.CleanupRedundantRemoveOperations(b) + return b.Res() +} + +// End of section. //template:end addDeletePathsXML diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 2728bf53..49efc2c3 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -619,6 +619,7 @@ func (p *IosxeProvider) Resources(ctx context.Context) []func() resource.Resourc NewClockResource, NewCommunityListExpandedResource, NewCommunityListStandardResource, + NewCryptoEngineResource, NewCryptoIKEv2Resource, NewCryptoIKEv2KeyringResource, NewCryptoIKEv2PolicyResource, @@ -725,6 +726,7 @@ func (p *IosxeProvider) DataSources(ctx context.Context) []func() datasource.Dat NewClockDataSource, NewCommunityListExpandedDataSource, NewCommunityListStandardDataSource, + NewCryptoEngineDataSource, NewCryptoIKEv2DataSource, NewCryptoIKEv2KeyringDataSource, NewCryptoIKEv2PolicyDataSource, diff --git a/internal/provider/resource_iosxe_crypto_engine.go b/internal/provider/resource_iosxe_crypto_engine.go new file mode 100644 index 00000000..dc670664 --- /dev/null +++ b/internal/provider/resource_iosxe_crypto_engine.go @@ -0,0 +1,499 @@ +// 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 + +// Code generated by "gen/generator.go"; DO NOT EDIT. + +package provider + +// Section below is generated&owned by "gen/generator.go". //template:begin imports +import ( + "context" + "fmt" + "strings" + + "github.com/CiscoDevNet/terraform-provider-iosxe/internal/provider/helpers" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "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/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/netascode/go-netconf" + "github.com/netascode/go-restconf" +) + +// End of section. //template:end imports + +// Section below is generated&owned by "gen/generator.go". //template:begin model + +// Ensure provider defined types fully satisfy framework interfaces +var ( + _ resource.Resource = &CryptoEngineResource{} + _ resource.ResourceWithImportState = &CryptoEngineResource{} +) + +func NewCryptoEngineResource() resource.Resource { + return &CryptoEngineResource{} +} + +type CryptoEngineResource struct { + data *IosxeProviderData +} + +func (r *CryptoEngineResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_crypto_engine" +} + +func (r *CryptoEngineResource) 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: "This resource can manage the Crypto Engine configuration.", + + Attributes: map[string]schema.Attribute{ + "device": schema.StringAttribute{ + MarkdownDescription: "A device name from the provider configuration.", + Optional: true, + }, + "id": schema.StringAttribute{ + MarkdownDescription: "The path of the object.", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "delete_mode": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("Configure behavior when deleting/destroying the resource. Either delete the entire object (YANG container) being managed, or only delete the individual resource attributes configured explicitly and leave everything else as-is. Default value is `all`.").AddStringEnumDescription("all", "attributes").String, + Optional: true, + Validators: []validator.String{ + stringvalidator.OneOf("all", "attributes"), + }, + }, + "compliance_shield_disable": schema.BoolAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("Allow weak crypto to be configured").String, + Optional: true, + }, + }, + } +} + +func (r *CryptoEngineResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.data = req.ProviderData.(*IosxeProviderData) +} + +// End of section. //template:end model + +// Section below is generated&owned by "gen/generator.go". //template:begin create + +func (r *CryptoEngineResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan CryptoEngine + + // Read plan + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, fmt.Sprintf("%s: Beginning Create", plan.getPath())) + + device, ok := r.data.Devices[plan.Device.ValueString()] + if !ok { + resp.Diagnostics.AddAttributeError(path.Root("device"), "Invalid device", fmt.Sprintf("Device '%s' does not exist in provider configuration.", plan.Device.ValueString())) + return + } + + if device.Managed { + if device.Protocol == "restconf" { + // Create object + body := plan.toBody(ctx) + + emptyLeafsDelete := plan.getEmptyLeafsDelete(ctx) + tflog.Debug(ctx, fmt.Sprintf("List of empty leafs to delete: %+v", emptyLeafsDelete)) + + if YangPatch { + edits := []restconf.YangPatchEdit{restconf.NewYangPatchEdit("merge", plan.getPath(), restconf.Body{Str: body})} + for _, i := range emptyLeafsDelete { + edits = append(edits, restconf.NewYangPatchEdit("remove", i, restconf.Body{})) + } + _, err := device.RestconfClient.YangPatchData("", "1", "", edits) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to configure object, got error: %s", err)) + return + } + } else { + res, err := device.RestconfClient.PatchData(plan.getPathShort(), body) + if len(res.Errors.Error) > 0 && res.Errors.Error[0].ErrorMessage == "patch to a nonexistent resource" { + _, err = device.RestconfClient.PutData(plan.getPath(), body) + } + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to configure object (PATCH, %s), got error: %s", plan.getPathShort(), err)) + return + } + for _, i := range emptyLeafsDelete { + res, err := device.RestconfClient.DeleteData(i) + if err != nil && res.StatusCode != 404 { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to delete object (%s), got error: %s", i, err)) + return + } + } + } + } else { + // Serialize NETCONF operations when reuse disabled, or writes when reuse enabled + locked := helpers.AcquireNetconfLock(&device.NetconfOpMutex, device.ReuseConnection, true) + if locked { + defer device.NetconfOpMutex.Unlock() + } + defer helpers.CloseNetconfConnection(ctx, device.NetconfClient, device.ReuseConnection) + + body := plan.toBodyXML(ctx) + + if err := helpers.EditConfig(ctx, device.NetconfClient, body, device.AutoCommit); err != nil { + resp.Diagnostics.AddError("Client Error", err.Error()) + return + } + } + } + + plan.Id = types.StringValue(plan.getPath()) + + tflog.Debug(ctx, fmt.Sprintf("%s: Create finished successfully", plan.getPath())) + + diags = resp.State.Set(ctx, &plan) + resp.Diagnostics.Append(diags...) + + helpers.SetFlagImporting(ctx, false, resp.Private, &resp.Diagnostics) +} + +// End of section. //template:end create + +// Section below is generated&owned by "gen/generator.go". //template:begin read + +func (r *CryptoEngineResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state CryptoEngine + + // Read state + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, fmt.Sprintf("%s: Beginning Read", state.Id.ValueString())) + + device, ok := r.data.Devices[state.Device.ValueString()] + if !ok { + resp.Diagnostics.AddAttributeError(path.Root("device"), "Invalid device", fmt.Sprintf("Device '%s' does not exist in provider configuration.", state.Device.ValueString())) + return + } + + if device.Managed { + imp, diags := helpers.IsFlagImporting(ctx, req) + if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { + return + } + + if device.Protocol == "restconf" { + res, err := device.RestconfClient.GetData(state.Id.ValueString()) + if res.StatusCode == 404 { + state = CryptoEngine{Device: state.Device, Id: state.Id} + } else { + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to retrieve object (%s), got error: %s", state.Id.ValueString(), err)) + return + } + + // After `terraform import` we switch to a full read. + if imp { + state.fromBody(ctx, res.Res) + } else { + state.updateFromBody(ctx, res.Res) + } + } + } else { + // Serialize NETCONF operations when reuse disabled (concurrent reads allowed when reuse enabled) + locked := helpers.AcquireNetconfLock(&device.NetconfOpMutex, device.ReuseConnection, false) + if locked { + defer device.NetconfOpMutex.Unlock() + } + defer helpers.CloseNetconfConnection(ctx, device.NetconfClient, device.ReuseConnection) + + filter := helpers.GetXpathFilter(state.getXPath()) + res, err := device.NetconfClient.GetConfig(ctx, "running", filter) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to retrieve object (%s), got error: %s", state.getPath(), err)) + return + } + + if helpers.IsGetConfigResponseEmpty(&res) && helpers.IsListPath(state.getXPath()) { + tflog.Debug(ctx, fmt.Sprintf("%s: Resource does not exist", state.Id.ValueString())) + resp.State.RemoveResource(ctx) + return + } + + // After `terraform import` we switch to a full read. + if imp { + state.fromBodyXML(ctx, res.Res) + } else { + state.updateFromBodyXML(ctx, res.Res) + } + } + } + + tflog.Debug(ctx, fmt.Sprintf("%s: Read finished successfully", state.Id.ValueString())) + + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + + helpers.SetFlagImporting(ctx, false, resp.Private, &resp.Diagnostics) +} + +// End of section. //template:end read + +// Section below is generated&owned by "gen/generator.go". //template:begin update + +func (r *CryptoEngineResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan, state CryptoEngine + + // Read plan + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Read state + diags = req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, fmt.Sprintf("%s: Beginning Update", plan.Id.ValueString())) + + device, ok := r.data.Devices[plan.Device.ValueString()] + if !ok { + resp.Diagnostics.AddAttributeError(path.Root("device"), "Invalid device", fmt.Sprintf("Device '%s' does not exist in provider configuration.", plan.Device.ValueString())) + return + } + + if device.Managed { + if device.Protocol == "restconf" { + body := plan.toBody(ctx) + + deletedItems := plan.getDeletedItems(ctx, state) + tflog.Debug(ctx, fmt.Sprintf("Removed items to delete: %+v", deletedItems)) + + emptyLeafsDelete := plan.getEmptyLeafsDelete(ctx) + tflog.Debug(ctx, fmt.Sprintf("List of empty leafs to delete: %+v", emptyLeafsDelete)) + + if YangPatch { + var edits []restconf.YangPatchEdit + for _, i := range deletedItems { + edits = append(edits, restconf.NewYangPatchEdit("remove", i, restconf.Body{})) + } + edits = append(edits, restconf.NewYangPatchEdit("merge", plan.getPath(), restconf.Body{Str: body})) + for _, i := range emptyLeafsDelete { + edits = append(edits, restconf.NewYangPatchEdit("remove", i, restconf.Body{})) + } + _, err := device.RestconfClient.YangPatchData("", "1", "", edits) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to update object, got error: %s", err)) + return + } + } else { + for _, i := range deletedItems { + res, err := device.RestconfClient.DeleteData(i) + if err != nil && res.StatusCode != 404 { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to delete object (%s), got error: %s", i, err)) + return + } + } + res, err := device.RestconfClient.PatchData(plan.getPathShort(), body) + if len(res.Errors.Error) > 0 && res.Errors.Error[0].ErrorMessage == "patch to a nonexistent resource" { + _, err = device.RestconfClient.PutData(plan.getPath(), body) + } + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to configure object (PATCH, %s), got error: %s", plan.getPathShort(), err)) + return + } + for _, i := range emptyLeafsDelete { + res, err := device.RestconfClient.DeleteData(i) + if err != nil && res.StatusCode != 404 { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to delete object (%s), got error: %s", i, err)) + return + } + } + } + } else { + // Serialize NETCONF operations when reuse disabled, or writes when reuse enabled + locked := helpers.AcquireNetconfLock(&device.NetconfOpMutex, device.ReuseConnection, true) + if locked { + defer device.NetconfOpMutex.Unlock() + } + defer helpers.CloseNetconfConnection(ctx, device.NetconfClient, device.ReuseConnection) + + body := plan.toBodyXML(ctx) + body = plan.addDeletedItemsXML(ctx, state, body) + + if err := helpers.EditConfig(ctx, device.NetconfClient, body, device.AutoCommit); err != nil { + resp.Diagnostics.AddError("Client Error", err.Error()) + return + } + } + } + + tflog.Debug(ctx, fmt.Sprintf("%s: Update finished successfully", plan.Id.ValueString())) + + diags = resp.State.Set(ctx, &plan) + resp.Diagnostics.Append(diags...) +} + +// End of section. //template:end update + +// Section below is generated&owned by "gen/generator.go". //template:begin delete + +func (r *CryptoEngineResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state CryptoEngine + + // Read state + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, fmt.Sprintf("%s: Beginning Delete", state.Id.ValueString())) + + device, ok := r.data.Devices[state.Device.ValueString()] + if !ok { + resp.Diagnostics.AddAttributeError(path.Root("device"), "Invalid device", fmt.Sprintf("Device '%s' does not exist in provider configuration.", state.Device.ValueString())) + return + } + + if device.Managed { + deleteMode := "all" + if state.DeleteMode.ValueString() == "all" { + deleteMode = "all" + } else if state.DeleteMode.ValueString() == "attributes" { + deleteMode = "attributes" + } + + if deleteMode == "all" { + if device.Protocol == "restconf" { + res, err := device.RestconfClient.DeleteData(state.Id.ValueString()) + if err != nil && res.StatusCode != 404 && res.StatusCode != 400 { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to delete object (%s), got error: %s", state.Id.ValueString(), err)) + return + } + } else { + // NETCONF - Serialize write operations + locked := helpers.AcquireNetconfLock(&device.NetconfOpMutex, device.ReuseConnection, true) + if locked { + defer device.NetconfOpMutex.Unlock() + } + defer helpers.CloseNetconfConnection(ctx, device.NetconfClient, device.ReuseConnection) + + body := netconf.Body{} + body = helpers.RemoveFromXPath(body, state.getXPath()) + + if err := helpers.EditConfig(ctx, device.NetconfClient, body.Res(), device.AutoCommit); err != nil { + resp.Diagnostics.AddError("Client Error", err.Error()) + return + } + } + } else { + if device.Protocol == "restconf" { + deletePaths := state.getDeletePaths(ctx) + tflog.Debug(ctx, fmt.Sprintf("Paths to delete: %+v", deletePaths)) + + if YangPatch { + edits := []restconf.YangPatchEdit{} + for _, i := range deletePaths { + edits = append(edits, restconf.NewYangPatchEdit("remove", i, restconf.Body{})) + } + _, err := device.RestconfClient.YangPatchData("", "1", "", edits) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to delete object, got error: %s", err)) + return + } + } else { + for _, i := range deletePaths { + res, err := device.RestconfClient.DeleteData(i) + if err != nil && res.StatusCode != 404 { + resp.Diagnostics.AddWarning("Client Warning", fmt.Sprintf("Failed to delete object (%s), got error: %s", i, err)) + } + } + } + } else { + // NETCONF - Serialize write operations + locked := helpers.AcquireNetconfLock(&device.NetconfOpMutex, device.ReuseConnection, true) + if locked { + defer device.NetconfOpMutex.Unlock() + } + defer helpers.CloseNetconfConnection(ctx, device.NetconfClient, device.ReuseConnection) + + body := state.addDeletePathsXML(ctx, "") + + if err := helpers.EditConfig(ctx, device.NetconfClient, body, device.AutoCommit); err != nil { + resp.Diagnostics.AddError("Client Error", err.Error()) + return + } + } + } + } + + tflog.Debug(ctx, fmt.Sprintf("%s: Delete finished successfully", state.Id.ValueString())) + + resp.State.RemoveResource(ctx) +} + +// End of section. //template:end delete + +// Section below is generated&owned by "gen/generator.go". //template:begin import + +func (r *CryptoEngineResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + idParts := strings.Split(req.ID, ",") + idParts = helpers.RemoveEmptyStrings(idParts) + + if len(idParts) != 0 && len(idParts) != 1 { + expectedIdentifier := "Expected import identifier with format: ''" + expectedIdentifier += " or ''" + resp.Diagnostics.AddError( + "Unexpected Import Identifier", + fmt.Sprintf("%s. Got: %q", expectedIdentifier, req.ID), + ) + return + } + if len(idParts) == 1 { + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("device"), idParts[len(idParts)-1])...) + } + + // construct path for 'id' attribute + var state CryptoEngine + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), state.getPath())...) + + helpers.SetFlagImporting(ctx, true, resp.Private, &resp.Diagnostics) +} + +// End of section. //template:end import diff --git a/internal/provider/resource_iosxe_crypto_engine_test.go b/internal/provider/resource_iosxe_crypto_engine_test.go new file mode 100644 index 00000000..3492c9d3 --- /dev/null +++ b/internal/provider/resource_iosxe_crypto_engine_test.go @@ -0,0 +1,96 @@ +// 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 + +// Code generated by "gen/generator.go"; DO NOT EDIT. + +package provider + +// Section below is generated&owned by "gen/generator.go". //template:begin imports +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" +) + +// End of section. //template:end imports + +// Section below is generated&owned by "gen/generator.go". //template:begin testAcc + +func TestAccIosxeCryptoEngine(t *testing.T) { + var checks []resource.TestCheckFunc + checks = append(checks, resource.TestCheckResourceAttr("iosxe_crypto_engine.test", "compliance_shield_disable", "true")) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccIosxeCryptoEngineConfig_minimum(), + }, + { + Config: testAccIosxeCryptoEngineConfig_all(), + Check: resource.ComposeTestCheckFunc(checks...), + }, + { + ResourceName: "iosxe_crypto_engine.test", + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: iosxeCryptoEngineImportStateIdFunc("iosxe_crypto_engine.test"), + ImportStateVerifyIgnore: []string{}, + Check: resource.ComposeTestCheckFunc(checks...), + }, + }, + }) +} + +// End of section. //template:end testAcc + +// Section below is generated&owned by "gen/generator.go". //template:begin importStateIdFunc + +func iosxeCryptoEngineImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + + return fmt.Sprintf(""), nil + } +} + +// End of section. //template:end importStateIdFunc + +// Section below is generated&owned by "gen/generator.go". //template:begin testPrerequisites +// End of section. //template:end testPrerequisites + +// Section below is generated&owned by "gen/generator.go". //template:begin testAccConfigMinimal + +func testAccIosxeCryptoEngineConfig_minimum() string { + config := `resource "iosxe_crypto_engine" "test" {` + "\n" + config += `}` + "\n" + return config +} + +// End of section. //template:end testAccConfigMinimal + +// Section below is generated&owned by "gen/generator.go". //template:begin testAccConfigAll + +func testAccIosxeCryptoEngineConfig_all() string { + config := `resource "iosxe_crypto_engine" "test" {` + "\n" + config += ` compliance_shield_disable = true` + "\n" + config += `}` + "\n" + return config +} + +// End of section. //template:end testAccConfigAll diff --git a/templates/guides/changelog.md.tmpl b/templates/guides/changelog.md.tmpl index 665e52f1..3f8e22a9 100644 --- a/templates/guides/changelog.md.tmpl +++ b/templates/guides/changelog.md.tmpl @@ -7,6 +7,43 @@ description: |- # Changelog +## 0.11.0 + +- Add `iosxe_crypto_engine` resource and data source for crypto engine compliance shield disable configuration +- Fix `iosxe_yang` resource payload ordering with NETCONF, [link](https://github.com/CiscoDevNet/terraform-provider-iosxe/issues/372) +- Add `ip_igmp_version` attribute to `iosxe_interface_ethernet`, `iosxe_interface_loopback`, `iosxe_interface_port_channel`, `iosxe_interface_port_channel_subinterface`, `iosxe_interface_tunnel`, and `iosxe_interface_vlan` resources and data sources +- Add `ip_default_gateway` attribute to `iosxe_system` resource and data source for default gateway configuration on non-routing devices +- Add `device_classifier` attribute to `iosxe_system` resource and data source for endpoint device classification +- Add `table_maps` attribute to `iosxe_system` resource and data source for QoS table map configuration with DSCP/CoS value translation +- Enhance `set_communities` attribute documentation in `iosxe_route_map` to clarify support for well-known BGP community values (internet, local-AS, no-advertise, no-export, gshut) +- Add `route_map` attribute to `iosxe_bgp_l2vpn_evpn_neighbor` resource and data source +- Add `import_path_selection_all` and `ipv4_unicast_aggregate_addresses.summary_only` attributes to `iosxe_bgp_address_family_ipv4_vrf` resource and data source +- BREAKING CHANGE: Rename `evpn_instance` to `evpn_instance_legacy` and `evpn_instance_vni` to `evpn_instance_vni_legacy` in `iosxe_vlan_configuration` resource and data source +- Add `evpn_instance`, `evpn_instance_vni`, and `evpn_instance_protected` attributes to `iosxe_vlan_configuration` resource and data source +- Add `evpn_instance_profile` and `evpn_instance_profile_protected` attributes to `iosxe_vlan_configuration` resource and data source +- Add `ttl` attribute to `iosxe_flow_exporter` resource and data source +- Add `match_routing_vrf_input`, `match_vxlan_vnid`, `match_vxlan_vtep_input`, and `match_vxlan_vtep_output` attributes to `iosxe_flow_record` resource and data source +- Add `register_source_interface_loopback` attributes to `iosxe_pim` resource and data source +- Add `iosxe_bgp_address_family_ipv4_mvpn` resource and data source +- Add `iosxe_bgp_ipv4_mvpn_neighbor` resource and data source +- Add `vlan_based_multicast_advertise` attribute to `iosxe_evpn_instance` resource and data source +- Add `multicast_advertise` attribute to `iosxe_evpn` resource and data source +- Add `carrier_delay_msec` and `hold_queues` attributes to `iosxe_interface_ethernet` resource and data source +- Add `iosxe_pim_ipv6` resource and data source +- Add `iosxe_interface_pim_ipv6` resource and data source +- Add `iosxe_multicast` resource and data source +- Add `deadtime` attribute to AAA group server radius in `iosxe_aaa` resource and data source +- Add `key_encryption`, `automate_tester_ignore_auth_port`, and `automate_tester_idle_time` attributes to `iosxe_radius` resource and data source +- Add `authentication_mac_move_permit` and `authentication_mac_move_deny_uncontrolled` attributes to `iosxe_system` resource and data source +- Add `dot1x` and `dot1x_default_*` attributes to `iosxe_aaa_accounting` resource and data source +- BREAKING CHANGE: Rename `iosxe_tacacs_server` resource and data source to `iosxe_tacacs` +- Add `port` attribute to `iosxe_tacacs` resource and data source +- BREAKING CHANGE: Add new `iosxe_tacacs_server` resource and data source +- Add `enable_default_group_legacy`, `enable_default_enable_legacy`, `enable_default_line_legacy` and `enable_default_none_legacy` attributes to `iosxe_aaa_authentication` resource and data source +- Add `iosxe_isis` resource and data source +- Add `iosxe_interface_isis` resource and data source +- Add `ip_router_isis` attribute to `iosxe_interface_ethernet`, `iosxe_interface_loopback`, `iosxe_interface_port_channel_subinterface`, `iosxe_interface_port_channel`, `iosxe_interface_tunnel` and `iosxe_interface_vlan` resources and data sources + ## 0.10.2 - Fix issue with incorrect reading of lists via NETCONF