From 9814fdbbb70c3bbb32b32acc0b281a5346046cb3 Mon Sep 17 00:00:00 2001 From: Krzysztof Klimonda Date: Wed, 18 Dec 2024 17:55:18 +0100 Subject: [PATCH 1/2] feat(specs): Initial panos_tunnel_interface codegen spec --- specs/network/interface/tunnel.yaml | 345 ++++++++++++++++++++++++++++ 1 file changed, 345 insertions(+) create mode 100644 specs/network/interface/tunnel.yaml diff --git a/specs/network/interface/tunnel.yaml b/specs/network/interface/tunnel.yaml new file mode 100644 index 00000000..cb263317 --- /dev/null +++ b/specs/network/interface/tunnel.yaml @@ -0,0 +1,345 @@ +name: Tunnel interface +terraform_provider_config: + description: Tunnel Interface + skip_resource: false + skip_datasource: false + resource_type: entry + resource_variants: + - singular + suffix: tunnel_interface + plural_suffix: '' + plural_name: '' + plural_description: '' +go_sdk_config: + skip: false + package: + - network + - interface + - tunnel +xpath_suffix: +- network +- interface +- tunnel +- units +locations: +- name: shared + xpath: + path: + - config + - shared + vars: [] + description: Location in Shared Panorama + devices: + - panorama + - ngfw + validators: [] + required: false + read_only: false +- name: template + xpath: + path: + - config + - devices + - $panorama_device + - template + - $template + - config + - devices + - $ngfw_device + vars: + - name: panorama_device + description: Specific Panorama device + required: false + default: localhost.localdomain + validators: [] + type: entry + - name: template + description: Specific Panorama template + required: true + validators: [] + type: entry + - name: ngfw_device + description: The NGFW device + required: false + default: localhost.localdomain + validators: [] + type: entry + description: Located in a specific template + devices: + - panorama + validators: [] + required: false + read_only: false +- name: template-stack + xpath: + path: + - config + - devices + - $panorama_device + - template-stack + - $template_stack + - config + - devices + - $ngfw_device + vars: + - name: panorama_device + description: Specific Panorama device + required: false + default: localhost.localdomain + validators: [] + type: entry + - name: template_stack + description: Specific Panorama template stack + required: true + validators: [] + type: entry + - name: ngfw_device + description: The NGFW device + required: false + default: localhost.localdomain + validators: [] + type: entry + description: Located in a specific template stack + devices: + - panorama + validators: [] + required: false + read_only: false +- name: ngfw + xpath: + path: + - config + - devices + - $ngfw_device + vars: + - name: ngfw_device + description: The NGFW device + required: false + default: localhost.localdomain + validators: [] + type: entry + description: Located in a specific NGFW device + devices: + - ngfw + validators: [] + required: false + read_only: false +entries: +- name: name + description: '' + validators: [] +imports: [] +spec: + params: + - name: bonjour + type: object + profiles: + - xpath: + - bonjour + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: Set to support Bonjour service + required: false + - name: group-id + type: int64 + profiles: + - xpath: + - group-id + validators: + - type: length + spec: + min: 0 + max: 16 + spec: + default: 40 + description: 'default 0: NO-Group' + required: false + - name: ttl-check + type: bool + profiles: + - xpath: + - ttl-check + validators: [] + spec: {} + description: Set to check and update TTL + required: false + variants: [] + description: Bonjour configuration + required: false + - name: comment + type: string + profiles: + - xpath: + - comment + validators: + - type: length + spec: + min: 0 + max: 1023 + spec: {} + description: '' + required: false + - name: df-ignore + type: bool + profiles: + - xpath: + - df-ignore + validators: [] + spec: {} + description: '' + required: false + - name: interface-management-profile + type: string + profiles: + - xpath: + - interface-management-profile + validators: + - type: length + spec: + max: 31 + spec: {} + description: Interface management profile + required: false + - name: ip + type: list + profiles: + - xpath: + - ip + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: [] + variants: [] + description: '' + required: false + - name: ipv6 + type: object + profiles: + - xpath: + - ipv6 + validators: [] + spec: + params: + - name: address + type: list + profiles: + - xpath: + - address + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: enable-on-interface + type: bool + profiles: + - xpath: + - enable-on-interface + validators: [] + spec: {} + description: configure this address on interface + required: false + - name: prefix + type: object + profiles: + - xpath: + - prefix + validators: [] + spec: + params: [] + variants: [] + description: use this as prefix to form full address with interface + id/EUI-64 + required: false + - name: anycast + type: object + profiles: + - xpath: + - anycast + validators: [] + spec: + params: [] + variants: [] + description: anycast address + required: false + variants: [] + description: '' + required: false + - name: enabled + type: bool + profiles: + - xpath: + - enabled + validators: [] + spec: {} + description: Enable IPv6 on the interface + required: false + - name: interface-id + type: string + profiles: + - xpath: + - interface-id + validators: [] + spec: + default: EUI-64 + description: '' + required: false + variants: [] + description: Interface IPv6 configuration + required: false + - name: link-tag + type: string + profiles: + - xpath: + - link-tag + validators: + - type: length + spec: + max: 127 + spec: {} + description: '' + required: false + - name: mtu + type: int64 + profiles: + - xpath: + - mtu + validators: + - type: length + spec: + min: 576 + max: 9216 + spec: {} + description: Maximum Transfer Unit, up to 9216 in Jumbo-Frame mode, up to 1500 + otherwise + required: false + - name: netflow-profile + type: string + profiles: + - xpath: + - netflow-profile + validators: + - type: length + spec: + max: 63 + spec: {} + description: Netflow Server Profile + required: false + variants: [] From f33d6ebfd4b5b29e4957ce8fc90c4a390e142f8d Mon Sep 17 00:00:00 2001 From: kklimonda-cl Date: Tue, 14 Jan 2025 17:27:10 +0100 Subject: [PATCH 2/2] feat(tests): Initial panos_tunnel_interface acceptance tests (#272) --- .../test/resource_tunnel_interface_test.go | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 assets/terraform/test/resource_tunnel_interface_test.go diff --git a/assets/terraform/test/resource_tunnel_interface_test.go b/assets/terraform/test/resource_tunnel_interface_test.go new file mode 100644 index 00000000..17334354 --- /dev/null +++ b/assets/terraform/test/resource_tunnel_interface_test.go @@ -0,0 +1,173 @@ +package provider_test + +import ( + "context" + "errors" + "fmt" + "testing" + + sdkErrors "github.com/PaloAltoNetworks/pango/errors" + "github.com/PaloAltoNetworks/pango/network/interface/tunnel" + + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +) + +func TestAccTunnelInterface(t *testing.T) { + t.Parallel() + + interfaceName := "tunnel.1" + nameSuffix := acctest.RandStringFromCharSet(6, acctest.CharSetAlphaNum) + prefix := fmt.Sprintf("test-acc-%s", nameSuffix) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProviders, + CheckDestroy: testAccCheckPanosTunnelInterfaceDestroy( + prefix, interfaceName, + ), + Steps: []resource.TestStep{ + { + Config: loopbackInterfaceResource1, + ConfigVariables: map[string]config.Variable{ + "prefix": config.StringVariable(prefix), + "interface_name": config.StringVariable(interfaceName), + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "panos_tunnel_interface.iface", + tfjsonpath.New("name"), + knownvalue.StringExact("tunnel.1"), + ), + statecheck.ExpectKnownValue( + "panos_tunnel_interface.iface", + tfjsonpath.New("comment"), + knownvalue.StringExact("tunnel interface comment"), + ), + statecheck.ExpectKnownValue( + "panos_tunnel_interface.iface", + tfjsonpath.New("interface_management_profile"), + knownvalue.StringExact(fmt.Sprintf("%s-profile", prefix)), + ), + statecheck.ExpectKnownValue( + "panos_tunnel_interface.iface", + tfjsonpath.New("bonjour"), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "enable": knownvalue.Bool(true), + "group_id": knownvalue.Int64Exact(10), + "ttl_check": knownvalue.Bool(true), + }), + ), + statecheck.ExpectKnownValue( + "panos_tunnel_interface.iface", + tfjsonpath.New("ip"), + knownvalue.ListExact([]knownvalue.Check{ + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "name": knownvalue.StringExact("127.0.0.1"), + }), + }), + ), + statecheck.ExpectKnownValue( + "panos_tunnel_interface.iface", + tfjsonpath.New("ipv6"), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "enabled": knownvalue.Bool(true), + "interface_id": knownvalue.StringExact("100"), + "address": knownvalue.ListExact([]knownvalue.Check{ + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "name": knownvalue.StringExact("::1"), + "enable_on_interface": knownvalue.Bool(true), + "anycast": knownvalue.ObjectExact(nil), + "prefix": knownvalue.ObjectExact(nil), + }), + }), + }), + ), + }, + }, + }, + }) +} + +const loopbackInterfaceResource1 = ` +variable "prefix" { type = string } +variable "interface_name" { type = string } + +locals { + template_name = format("%s-tmpl", var.prefix) +} + +resource "panos_template" "template" { + location = { panorama = {} } + name = local.template_name +} + +resource "panos_interface_management_profile" "profile" { + location = { template = { name = panos_template.template.name }} + + name = format("%s-profile", var.prefix) +} + +resource "panos_tunnel_interface" "iface" { + location = { template = { name = panos_template.template.name } } + + name = var.interface_name + comment = "tunnel interface comment" + + df_ignore = true + interface_management_profile = panos_interface_management_profile.profile.name + #link_tag = "tag-1" + mtu = "9126" + #netflow_profile = format("%s-profile", var.prefix) + bonjour = { + enable = true + group_id = 10 + ttl_check = true + } + ip = [{ + name = "127.0.0.1" + }] + ipv6 = { + enabled = true + interface_id = "100" + address = [{ + name = "::1" + enable_on_interface = true + anycast = {} + prefix = {} + }] + + } +} +` + +func testAccCheckPanosTunnelInterfaceDestroy(prefix string, entry string) func(s *terraform.State) error { + return func(s *terraform.State) error { + api := tunnel.NewService(sdkClient) + ctx := context.TODO() + + location := tunnel.NewTemplateLocation() + location.Template.Template = fmt.Sprintf("%s-tmpl", prefix) + + reply, err := api.Read(ctx, *location, entry, "show") + if err != nil && !sdkErrors.IsObjectNotFound(err) { + return fmt.Errorf("reading ethernet entry via sdk: %v", err) + } + + if reply != nil { + err := fmt.Errorf("terraform didn't delete the server entry properly") + delErr := api.Delete(ctx, *location, entry) + if delErr != nil { + return errors.Join(err, delErr) + } + return err + } + + return nil + } +}