diff --git a/assets/pango/errors/panos.go b/assets/pango/errors/panos.go index 16a604bc..b8a80ae3 100644 --- a/assets/pango/errors/panos.go +++ b/assets/pango/errors/panos.go @@ -7,6 +7,7 @@ import ( "strings" ) +var ObjectExists = stderr.New("object already exists") var InvalidFilterError = stderr.New("filter is improperly formatted") var NameNotSpecifiedError = stderr.New("name is not specified") var NoLocationSpecifiedError = stderr.New("no location specified") @@ -151,3 +152,17 @@ func (e *errorCheck) CodeError() string { return fmt.Sprintf("(%d) Unknown failure code, operation failed", e.Code) } } + +type InvalidXpathComponentError struct { + Message string +} + +func (o InvalidXpathComponentError) Error() string { + return o.Message +} + +func NewInvalidXpathComponentError(message string) *InvalidXpathComponentError { + return &InvalidXpathComponentError{ + Message: message, + } +} diff --git a/assets/pango/example/main.go b/assets/pango/example/main.go index ee9037c0..9d9e19f0 100644 --- a/assets/pango/example/main.go +++ b/assets/pango/example/main.go @@ -2,7 +2,6 @@ package main import ( "context" - "encoding/xml" "fmt" "log" @@ -671,8 +670,8 @@ func checkSecurityPolicyRules(c *pango.Client, ctx context.Context) { log.Printf("Security policy rule '%s:%s' with description '%s' read by id", *securityPolicyRuleReply.Uuid, securityPolicyRuleReply.Name, *securityPolicyRuleReply.Description) // SECURITY POLICY RULE - UPDATE 2 - securityPolicyRuleEntry.Description = util.String("changed by id description") - securityPolicyRuleReply, err = securityPolicyRuleApi.UpdateById(ctx, *securityPolicyRuleLocation, securityPolicyRuleEntry, *securityPolicyRuleReply.Uuid) + securityPolicyRuleEntry.Description = util.String("changed by name description") + securityPolicyRuleReply, err = securityPolicyRuleApi.Update(ctx, *securityPolicyRuleLocation, securityPolicyRuleEntry, securityPolicyRuleReply.Name) if err != nil { log.Printf("Failed to update security policy rule: %s", err) return @@ -1038,21 +1037,6 @@ func checkService(c *pango.Client, ctx context.Context) { return } - readDescription := "" - if serviceReply.Description != nil { - readDescription = *serviceReply.Description - } - - keys := make([]string, 0, len(serviceReply.Misc)) - xmls := make([]string, 0, len(serviceReply.Misc)) - for key := range serviceReply.Misc { - keys = append(keys, key) - data, _ := xml.Marshal(serviceReply.Misc[key]) - xmls = append(xmls, string(data)) - } - log.Printf("Service '%s=%s, description: %s misc XML: %s, misc keys: %s' read", - serviceReply.Name, *serviceReply.Protocol.Tcp.Port, readDescription, xmls, keys) - // SERVICE - UPDATE 3 serviceReply.Description = util.String("some text changed now") @@ -1061,21 +1045,6 @@ func checkService(c *pango.Client, ctx context.Context) { log.Printf("Failed to update object: %s", err) return } - - readDescription = "" - if serviceReply.Description != nil { - readDescription = *serviceReply.Description - } - - keys = make([]string, 0, len(serviceReply.Misc)) - xmls = make([]string, 0, len(serviceReply.Misc)) - for key := range serviceReply.Misc { - keys = append(keys, key) - data, _ := xml.Marshal(serviceReply.Misc[key]) - xmls = append(xmls, string(data)) - } - log.Printf("Service '%s=%s, description: %s misc XML: %s, misc keys: %s' update", - serviceReply.Name, *serviceReply.Protocol.Tcp.Port, readDescription, xmls, keys) } func checkNtp(c *pango.Client, ctx context.Context) { diff --git a/assets/pango/tests/marshalling_test.go b/assets/pango/tests/marshalling_test.go new file mode 100644 index 00000000..868fa835 --- /dev/null +++ b/assets/pango/tests/marshalling_test.go @@ -0,0 +1,196 @@ +package tests + +import ( + "encoding/xml" + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/PaloAltoNetworks/pango/generic" + "github.com/PaloAltoNetworks/pango/objects/application" + "github.com/PaloAltoNetworks/pango/version" +) + +var _ = Describe("XML Marshalling and Unmarshalling tests", func() { + version, _ := version.New("11.0.0") + var specifier application.Specifier + var normalizer application.Normalizer + + JustBeforeEach(func() { + specifier, normalizer, _ = application.Versioning(version) + }) + Context("When unmarshalling XML document with no unknown nodes", func() { + Context("within top-level object", func() { + It("should return an Entry object with empty Misc field", func() { + data := []byte(``) + err := xml.Unmarshal(data, &normalizer) + Expect(err).To(Not(HaveOccurred())) + + entries, err := normalizer.Normalize() + Expect(err).To(Not(HaveOccurred())) + Expect(entries).To(HaveLen(1)) + + Expect(entries[0].Name).To(Equal("app-1")) + Expect(entries[0].Misc).To(HaveLen(0)) + }) + }) + Context("within child object", func() { + It("should return an Entry object with empty Misc field within child", func() { + data := []byte(`test-protocol`) + + marshalled := []byte(fmt.Sprintf("%s", string(data))) + err := xml.Unmarshal(marshalled, &normalizer) + Expect(err).To(Not(HaveOccurred())) + + entries, err := normalizer.Normalize() + Expect(err).To(Not(HaveOccurred())) + Expect(entries).To(HaveLen(1)) + + Expect(entries[0].Name).To(Equal("app-1")) + Expect(entries[0].Misc).To(HaveLen(0)) + + Expect(entries[0].Default.IdentByIpProtocol).To(Not(BeNil())) + Expect(*entries[0].Default.IdentByIpProtocol).To(Equal("test-protocol")) + Expect(entries[0].Default.Misc).To(HaveLen(0)) + + specified, err := specifier(entries[0]) + Expect(err).To(Not(HaveOccurred())) + + marshalled, err = xml.Marshal(specified) + Expect(err).To(Not(HaveOccurred())) + Expect(marshalled).To(Equal(data)) + }) + }) + Context("within a list child object", func() { + It("should return an Entry object with empty Misc field within a list child", func() { + data := []byte(`yes`) + + marshalled := []byte(fmt.Sprintf("%s", string(data))) + err := xml.Unmarshal(marshalled, &normalizer) + Expect(err).To(Not(HaveOccurred())) + + entries, err := normalizer.Normalize() + Expect(err).To(Not(HaveOccurred())) + Expect(entries).To(HaveLen(1)) + + Expect(entries[0].Name).To(Equal("app-1")) + Expect(entries[0].Misc).To(HaveLen(0)) + + Expect(entries[0].Signature).To(HaveLen(1)) + Expect(entries[0].Signature[0].OrderFree).To(Not(BeNil())) + Expect(*entries[0].Signature[0].OrderFree).To(BeTrue()) + Expect(entries[0].Signature[0].Misc).To(HaveLen(0)) + + specified, err := specifier(entries[0]) + Expect(err).To(Not(HaveOccurred())) + + marshalled, err = xml.Marshal(specified) + Expect(err).To(Not(HaveOccurred())) + Expect(marshalled).To(Equal(data)) + }) + }) + }) + Context("When unmarshalling XML document with no unknown nodes", func() { + var expectedXmlNodes []generic.Xml + + JustBeforeEach(func() { + fakeTextString := "fake-text" + expectedXmlNodes = []generic.Xml{ + { + XMLName: xml.Name{Local: "fake-node"}, + Text: []byte("fake-text"), + TrimmedText: &fakeTextString, + }, + } + }) + Context("within top-level object", func() { + It("should return an Entry object with a non-empty Misc field", func() { + data := []byte(`fake-text`) + err := xml.Unmarshal(data, &normalizer) + Expect(err).To(Not(HaveOccurred())) + + entries, err := normalizer.Normalize() + Expect(err).To(Not(HaveOccurred())) + Expect(entries).To(HaveLen(1)) + + Expect(entries[0].Name).To(Equal("address-1")) + Expect(entries[0].Misc).To(HaveExactElements(expectedXmlNodes)) + }) + }) + Context("within child object", func() { + It("should return an Entry object with a non-empty Misc field within child", func() { + data := []byte(`test-protocolfake-text`) + + marshalled := []byte(fmt.Sprintf("%s", string(data))) + err := xml.Unmarshal(marshalled, &normalizer) + Expect(err).To(Not(HaveOccurred())) + + entries, err := normalizer.Normalize() + Expect(err).To(Not(HaveOccurred())) + Expect(entries).To(HaveLen(1)) + + Expect(entries[0].Name).To(Equal("app-1")) + Expect(entries[0].Misc).To(HaveLen(0)) + + Expect(entries[0].Default.IdentByIpProtocol).To(Not(BeNil())) + Expect(*entries[0].Default.IdentByIpProtocol).To(Equal("test-protocol")) + Expect(entries[0].Default.Misc).To(HaveExactElements(expectedXmlNodes)) + + specified, err := specifier(entries[0]) + Expect(err).To(Not(HaveOccurred())) + + marshalled, err = xml.Marshal(specified) + Expect(err).To(Not(HaveOccurred())) + Expect(marshalled).To(Equal(data)) + }) + }) + Context("within a list child object", func() { + It("should return an Entry object with a non-empty Misc field within a list child, one for each element", func() { + data := []byte(`yesfake-text1nofake-text2`) + + marshalled := []byte(fmt.Sprintf("%s", string(data))) + err := xml.Unmarshal(marshalled, &normalizer) + Expect(err).To(Not(HaveOccurred())) + + entries, err := normalizer.Normalize() + Expect(err).To(Not(HaveOccurred())) + Expect(entries).To(HaveLen(1)) + + Expect(entries[0].Name).To(Equal("app-1")) + Expect(entries[0].Misc).To(HaveLen(0)) + + Expect(entries[0].Signature).To(HaveLen(2)) + Expect(entries[0].Signature[0].OrderFree).To(Not(BeNil())) + Expect(*entries[0].Signature[0].OrderFree).To(BeTrue()) + + fakeTextString := "fake-text1" + expectedXmlNodes = []generic.Xml{ + { + XMLName: xml.Name{Local: "fake-node"}, + Text: []byte(fakeTextString), + TrimmedText: &fakeTextString, + }, + } + Expect(entries[0].Signature[0].Misc).To(HaveExactElements()) + + fakeTextString = "fake-text2" + expectedXmlNodes = []generic.Xml{ + { + XMLName: xml.Name{Local: "fake-node"}, + Text: []byte(fakeTextString), + TrimmedText: &fakeTextString, + }, + } + Expect(entries[0].Signature[1].Misc).To(HaveExactElements()) + + specified, err := specifier(entries[0]) + Expect(err).To(Not(HaveOccurred())) + + marshalled, err = xml.Marshal(specified) + Expect(err).To(Not(HaveOccurred())) + Expect(marshalled).To(Equal(data)) + }) + }) + }) +}) diff --git a/assets/pango/tests/tests_suite_test.go b/assets/pango/tests/tests_suite_test.go new file mode 100644 index 00000000..82f8ddbe --- /dev/null +++ b/assets/pango/tests/tests_suite_test.go @@ -0,0 +1,18 @@ +package tests + +import ( + "log/slog" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestMovement(t *testing.T) { + handler := slog.NewTextHandler(GinkgoWriter, &slog.HandlerOptions{ + Level: slog.LevelDebug, + }) + slog.SetDefault(slog.New(handler)) + RegisterFailHandler(Fail) + RunSpecs(t, "Translate Suite") +} diff --git a/assets/pango/util/util.go b/assets/pango/util/util.go index b7e0b736..b48f5fd5 100644 --- a/assets/pango/util/util.go +++ b/assets/pango/util/util.go @@ -109,7 +109,7 @@ func AsXpath(i interface{}) string { } // AsEntryXpath returns the given values as an entry xpath segment. -func AsEntryXpath(vals []string) string { +func AsEntryXpath(vals ...string) string { if len(vals) == 0 || (len(vals) == 1 && vals[0] == "") { return "entry" } @@ -160,18 +160,18 @@ func TemplateXpathPrefix(tmpl, ts string) []string { return []string{ "config", "devices", - AsEntryXpath([]string{"localhost.localdomain"}), + AsEntryXpath("localhost.localdomain"), "template", - AsEntryXpath([]string{tmpl}), + AsEntryXpath(tmpl), } } return []string{ "config", "devices", - AsEntryXpath([]string{"localhost.localdomain"}), + AsEntryXpath("localhost.localdomain"), "template-stack", - AsEntryXpath([]string{ts}), + AsEntryXpath(ts), } } @@ -185,9 +185,9 @@ func DeviceGroupXpathPrefix(dg string) []string { return []string{ "config", "devices", - AsEntryXpath([]string{"localhost.localdomain"}), + AsEntryXpath("localhost.localdomain"), "device-group", - AsEntryXpath([]string{dg}), + AsEntryXpath(dg), } } @@ -202,9 +202,9 @@ func VsysXpathPrefix(vsys string) []string { return []string{ "config", "devices", - AsEntryXpath([]string{"localhost.localdomain"}), + AsEntryXpath("localhost.localdomain"), "vsys", - AsEntryXpath([]string{vsys}), + AsEntryXpath(vsys), } } diff --git a/assets/pango/util/util_test.go b/assets/pango/util/util_test.go index 08126d0d..736f09b0 100644 --- a/assets/pango/util/util_test.go +++ b/assets/pango/util/util_test.go @@ -106,9 +106,9 @@ func BenchmarkAsXpath(b *testing.B) { p := []string{ "config", "devices", - AsEntryXpath([]string{"localhost.localdomain"}), + AsEntryXpath("localhost.localdomain"), "vsys", - AsEntryXpath([]string{"vsys1"}), + AsEntryXpath("vsys1"), "import", "network", } @@ -124,7 +124,7 @@ func BenchmarkAsEntryXpathMultiple(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _ = AsEntryXpath(v) + _ = AsEntryXpath(v...) } } @@ -159,7 +159,7 @@ func TestAsEntryXpath(t *testing.T) { for _, tc := range testCases { t.Run(tc.r, func(t *testing.T) { - if AsEntryXpath(tc.v) != tc.r { + if AsEntryXpath(tc.v...) != tc.r { t.Fail() } }) diff --git a/assets/terraform/examples/resources/panos_ethernet_layer3_subinterface/resource.tf b/assets/terraform/examples/resources/panos_ethernet_layer3_subinterface/resource.tf new file mode 100644 index 00000000..abf80561 --- /dev/null +++ b/assets/terraform/examples/resources/panos_ethernet_layer3_subinterface/resource.tf @@ -0,0 +1,325 @@ +resource "panos_template" "example" { + location = { panorama = {} } + name = "example-tmpl" +} + +resource "panos_ethernet_interface" "parent" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + name = "ethernet1/1" + layer3 = {} +} + +resource "panos_ethernet_layer3_subinterface" "example1" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.1" + tag = 1 + comment = "Basic subinterface" + netflow_profile = "NetflowProfile1" + mtu = 1500 + adjust_tcp_mss = { enable = true, ipv4_mss_adjustment = 1300, ipv6_mss_adjustment = 1300 } + arp = [{ name = "192.168.0.1", hw_address = "00:1a:2b:3c:4d:5e" }] + bonjour = { enable = true, group_id = 5, ttl_check = true } + decrypt_forward = true + df_ignore = true + ndp_proxy = { enabled = true, address = [{ name = "10.0.0.1", negate = false }] } + ip = [{ name = "192.168.1.1", sdwan_gateway = "192.168.1.1" }] +} + +resource "panos_ethernet_layer3_subinterface" "example2" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.2" + tag = 2 + comment = "IPv6 GUA subinterface" + ipv6 = { + enabled = true + inherited = { + assign_addr = [ + { + name = "gua_config" + type = { + gua = { + enable_on_interface = true + prefix_pool = "my-gua-pool" + } + } + } + ] + } + } +} + +resource "panos_ethernet_layer3_subinterface" "example3" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.3" + tag = 3 + comment = "IPv6 ULA subinterface" + ipv6 = { + enabled = true + inherited = { + assign_addr = [ + { + name = "ula_config" + type = { + ula = { + enable_on_interface = true + address = "fd00:1234:5678::/48" + } + } + } + ] + } + } +} + +resource "panos_ethernet_layer3_subinterface" "example4" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.4" + tag = 4 + comment = "SDWAN DDNS subinterface" + sdwan_link_settings = { + enable = true + sdwan_interface_profile = "SdwanProfile1" + upstream_nat = { + enable = true + ddns = {} + } + } +} + +resource "panos_ethernet_layer3_subinterface" "example5" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.5" + tag = 5 + comment = "SDWAN Static IP FQDN subinterface" + sdwan_link_settings = { + enable = true + sdwan_interface_profile = "SdwanProfile1" + upstream_nat = { + enable = true + static_ip = { + fqdn = "example.com" + } + } + } +} + +resource "panos_ethernet_layer3_subinterface" "example6" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.6" + tag = 6 + comment = "SDWAN Static IP Address subinterface" + sdwan_link_settings = { + enable = true + sdwan_interface_profile = "SdwanProfile1" + upstream_nat = { + enable = true + static_ip = { + ip_address = "203.0.113.1" + } + } + } +} + +resource "panos_ethernet_layer3_subinterface" "example7" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.7" + tag = 7 + comment = "DHCP Client subinterface" + dhcp_client = { + create_default_route = true + default_route_metric = 10 + enable = true + send_hostname = { + enable = true + hostname = "dhcp-client-hostname" + } + } + interface_management_profile = "dhcp-client-profile" + ipv6 = { enabled = false } + sdwan_link_settings = { enable = false } +} + +resource "panos_ethernet_layer3_subinterface" "example8" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.8" + tag = 8 + comment = "IPv6 DHCP Client subinterface" + ipv6 = { + enabled = true + dhcp_client = { + accept_ra_route = true + default_route_metric = 10 + enable = true + neighbor_discovery = { + dad_attempts = 1 + enable_dad = true + enable_ndp_monitor = true + ns_interval = 1000 + reachable_time = 30000 + } + preference = "high" + prefix_delegation = { + enable = { + yes = { + pfx_pool_name = "prefix-pool-1" + prefix_len = 64 + prefix_len_hint = true + } + } + } + v6_options = { + duid_type = "duid-type-llt" + enable = { + yes = { + non_temp_addr = true + temp_addr = false + } + } + rapid_commit = true + support_srvr_reconfig = true + } + } + } +} + +resource "panos_ethernet_layer3_subinterface" "example9" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.9" + tag = 9 + comment = "IPv6 Neighbor Discovery subinterface" + ipv6 = { + enabled = true + neighbor_discovery = { + dad_attempts = 1 + enable_dad = true + enable_ndp_monitor = true + ns_interval = 1000 + reachable_time = 30000 + neighbor = [ + { + name = "2001:DB8::1/128" + hw_address = "00:1a:2b:3c:4d:5e" + } + ] + router_advertisement = { + enable = true + enable_consistency_check = true + hop_limit = "64" + lifetime = 1800 + link_mtu = "1500" + managed_flag = true + max_interval = 600 + min_interval = 200 + other_flag = true + reachable_time = "0" + retransmission_timer = "0" + router_preference = "Medium" + dns_support = { + enable = true + server = [ + { + name = "2001:DB8::1/128" + lifetime = 1200 + } + ] + suffix = [ + { + name = "suffix1" + lifetime = 1200 + } + ] + } + } + } + } +} + +resource "panos_ethernet_layer3_subinterface" "example10" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.10" + tag = 10 + comment = "PPPoE subinterface" + pppoe = { + access_concentrator = "ac-1" + authentication = "auto" + create_default_route = true + default_route_metric = 10 + enable = true + passive = { + enable = true + } + password = "pppoe-password" + service = "pppoe-service" + static_address = { + ip = "192.168.2.1" + } + username = "pppoe-user" + } + ipv6 = { enabled = false } + sdwan_link_settings = { enable = false } +} diff --git a/assets/terraform/go.mod b/assets/terraform/go.mod index 228c2683..0c555e8b 100644 --- a/assets/terraform/go.mod +++ b/assets/terraform/go.mod @@ -13,7 +13,6 @@ require ( github.com/hashicorp/terraform-plugin-testing v1.12.0 github.com/onsi/ginkgo/v2 v2.22.2 github.com/onsi/gomega v1.36.2 - golang.org/x/sync v0.13.0 ) require ( @@ -63,6 +62,7 @@ require ( golang.org/x/crypto v0.37.0 // indirect golang.org/x/mod v0.24.0 // indirect golang.org/x/net v0.37.0 // indirect + golang.org/x/sync v0.13.0 // indirect golang.org/x/sys v0.32.0 // indirect golang.org/x/text v0.24.0 // indirect golang.org/x/tools v0.28.0 // indirect diff --git a/assets/terraform/internal/manager/config.go b/assets/terraform/internal/manager/config.go index be7c003d..c3c05a80 100644 --- a/assets/terraform/internal/manager/config.go +++ b/assets/terraform/internal/manager/config.go @@ -9,6 +9,7 @@ import ( sdkerrors "github.com/PaloAltoNetworks/pango/errors" "github.com/PaloAltoNetworks/pango/util" "github.com/PaloAltoNetworks/pango/version" + "github.com/PaloAltoNetworks/pango/xmlapi" ) type TFConfigObject[E any] interface { @@ -18,13 +19,14 @@ type TFConfigObject[E any] interface { type SDKConfigService[C any, L ConfigLocation] interface { Create(context.Context, L, C) (C, error) - Update(context.Context, L, C) (C, error) - Read(context.Context, L, string) (C, error) + CreateWithXpath(context.Context, string, C) error + UpdateWithXpath(context.Context, string, C) error + ReadWithXpath(context.Context, string, string) (C, error) Delete(context.Context, L, C) error } type ConfigLocation interface { - Xpath(version.Number) ([]string, error) + XpathWithComponents(version.Number, ...string) ([]string, error) } type ConfigObjectManager[C any, L ConfigLocation, S SDKConfigService[C, L]] struct { @@ -41,16 +43,41 @@ func NewConfigObjectManager[C any, L ConfigLocation, S SDKConfigService[C, L]](c } } -func (o *ConfigObjectManager[C, L, S]) Create(ctx context.Context, location L, config C) (C, error) { - return o.service.Create(ctx, location, config) +func (o *ConfigObjectManager[C, L, S]) Create(ctx context.Context, location L, components []string, config C) (C, error) { + xpath, err := location.XpathWithComponents(o.client.Versioning(), components...) + if err != nil { + return *new(C), err + } + + err = o.service.CreateWithXpath(ctx, util.AsXpath(xpath[:len(xpath)-1]), config) + if err != nil { + return *new(C), err + } + + return o.service.ReadWithXpath(ctx, util.AsXpath(xpath), "get") } -func (o *ConfigObjectManager[C, L, S]) Update(ctx context.Context, location L, config C) (C, error) { - return o.service.Update(ctx, location, config) +func (o *ConfigObjectManager[C, L, S]) Update(ctx context.Context, location L, components []string, config C) (C, error) { + xpath, err := location.XpathWithComponents(o.client.Versioning(), components...) + if err != nil { + return *new(C), err + } + + err = o.service.UpdateWithXpath(ctx, util.AsXpath(xpath), config) + if err != nil { + return *new(C), err + } + + return o.service.ReadWithXpath(ctx, util.AsXpath(xpath), "get") } -func (o *ConfigObjectManager[C, L, S]) Read(ctx context.Context, location L) (C, error) { - obj, err := o.service.Read(ctx, location, "get") +func (o *ConfigObjectManager[C, L, S]) Read(ctx context.Context, location L, components []string) (C, error) { + xpath, err := location.XpathWithComponents(o.client.Versioning(), components...) + if err != nil { + return *new(C), err + } + + obj, err := o.service.ReadWithXpath(ctx, util.AsXpath(xpath), "get") if err != nil && sdkerrors.IsObjectNotFound(err) { return obj, ErrObjectNotFound } @@ -59,5 +86,23 @@ func (o *ConfigObjectManager[C, L, S]) Read(ctx context.Context, location L) (C, } func (o *ConfigObjectManager[C, L, S]) Delete(ctx context.Context, location L, config C) error { - return o.service.Delete(ctx, location, config) + deletes := xmlapi.NewChunkedMultiConfig(1, 1) + + xpath, err := location.XpathWithComponents(o.client.Versioning()) + if err != nil { + return err + } + + deletes.Add(&xmlapi.Config{ + Action: "delete", + Xpath: util.AsXpath(xpath), + Target: o.client.GetTarget(), + }) + + _, _, _, err = o.client.MultiConfig(ctx, deletes, false, nil) + if err != nil { + return &Error{err: err, message: "sdk error while deleting"} + } + + return nil } diff --git a/assets/terraform/internal/manager/entry.go b/assets/terraform/internal/manager/entry.go index 14427563..8992e8fe 100644 --- a/assets/terraform/internal/manager/entry.go +++ b/assets/terraform/internal/manager/entry.go @@ -33,15 +33,14 @@ type EntryObject interface { } type EntryLocation interface { - XpathWithEntryName(version.Number, string) ([]string, error) + XpathWithComponents(version.Number, ...string) ([]string, error) } type SDKEntryService[E EntryObject, L EntryLocation] interface { - Create(context.Context, L, E) (E, error) - List(context.Context, L, string, string, string) ([]E, error) - Read(context.Context, L, string, string) (E, error) - Update(context.Context, L, E, string) (E, error) - Delete(context.Context, L, ...string) error + CreateWithXpath(context.Context, string, E) error + ReadWithXpath(context.Context, string, string) (E, error) + ListWithXpath(context.Context, string, string, string, string) ([]E, error) + UpdateWithXpath(context.Context, string, E, string) error } type EntryObjectManager[E EntryObject, L EntryLocation, S SDKEntryService[E, L]] struct { @@ -62,8 +61,13 @@ func NewEntryObjectManager[E EntryObject, L EntryLocation, S SDKEntryService[E, } } -func (o *EntryObjectManager[E, L, S]) ReadMany(ctx context.Context, location L, entries []E) ([]E, error) { - existing, err := o.service.List(ctx, location, "get", "", "") +func (o *EntryObjectManager[E, L, S]) ReadMany(ctx context.Context, location L, components []string, entries []E) ([]E, error) { + xpath, err := location.XpathWithComponents(o.client.Versioning(), append(components, util.AsEntryXpath(""))...) + if err != nil { + return nil, err + } + + existing, err := o.service.ListWithXpath(ctx, util.AsXpath(xpath), "get", "", "") if err != nil { if sdkerrors.IsObjectNotFound(err) { return nil, ErrObjectNotFound @@ -84,8 +88,13 @@ func (o *EntryObjectManager[E, L, S]) ReadMany(ctx context.Context, location L, return filtered, nil } -func (o *EntryObjectManager[E, L, S]) Read(ctx context.Context, location L, name string) (E, error) { - object, err := o.service.Read(ctx, location, name, "get") +func (o *EntryObjectManager[E, L, S]) Read(ctx context.Context, location L, parentComponents []string, name string) (E, error) { + xpath, err := location.XpathWithComponents(o.client.Versioning(), append(parentComponents, util.AsEntryXpath(name))...) + if err != nil { + return *new(E), &Error{err: err, message: "failed to read entry from the server"} + } + + object, err := o.service.ReadWithXpath(ctx, util.AsXpath(xpath), "get") if err != nil { if sdkerrors.IsObjectNotFound(err) { return *new(E), ErrObjectNotFound @@ -96,25 +105,42 @@ func (o *EntryObjectManager[E, L, S]) Read(ctx context.Context, location L, name return object, nil } -func (o *EntryObjectManager[E, T, S]) Create(ctx context.Context, location T, entry E) (E, error) { - existing, err := o.service.List(ctx, location, "get", "", "") - if err != nil && !sdkerrors.IsObjectNotFound(err) { +func (o *EntryObjectManager[E, L, S]) Create(ctx context.Context, location L, parentComponents []string, entry E) (E, error) { + xpath, err := location.XpathWithComponents(o.client.Versioning(), append(parentComponents, util.AsEntryXpath(entry.EntryName()))...) + if err != nil { return *new(E), err } - for _, elt := range existing { - if elt.EntryName() == entry.EntryName() { - return *new(E), ErrConflict - } + _, err = o.service.ReadWithXpath(ctx, util.AsXpath(xpath), "get") + if err == nil { + return *new(E), &Error{err: ErrConflict, message: fmt.Sprintf("entry '%s' already exists", entry.EntryName())} } - obj, err := o.service.Create(ctx, location, entry) - return obj, err + if err != nil && !sdkerrors.IsObjectNotFound(err) { + return *new(E), &Error{err: err, message: "failed to read existing entry from the server"} + } + + err = o.service.CreateWithXpath(ctx, util.AsXpath(xpath[:len(xpath)-1]), entry) + if err != nil { + return *new(E), &Error{err: err, message: "failed to create entry on the server"} + } + + obj, err := o.service.ReadWithXpath(ctx, util.AsXpath(xpath), "get") + if err != nil { + return *new(E), &Error{err: err, message: "failed to read created entry from the server"} + } + + return obj, nil } -func (o *EntryObjectManager[E, L, S]) CreateMany(ctx context.Context, location L, entries []E) ([]E, error) { +func (o *EntryObjectManager[E, L, S]) CreateMany(ctx context.Context, location L, parentComponents []string, entries []E) ([]E, error) { + xpath, err := location.XpathWithComponents(o.client.Versioning(), append(parentComponents, util.AsEntryXpath(""))...) + if err != nil { + return nil, err + } + // First, check if none of the entries from the plan already exist on the server - existing, err := o.service.List(ctx, location, "get", "", "") + existing, err := o.service.ListWithXpath(ctx, util.AsXpath(xpath), "get", "", "") if err != nil && !sdkerrors.IsObjectNotFound(err) { return nil, &Error{err: err, message: "failed to list existing entries on the server"} } @@ -130,7 +156,8 @@ func (o *EntryObjectManager[E, L, S]) CreateMany(ctx context.Context, location L updates := xmlapi.NewChunkedMultiConfig(len(existing), o.batchSize) for _, elt := range entries { - path, err := location.XpathWithEntryName(o.client.Versioning(), elt.EntryName()) + components := append(parentComponents, util.AsEntryXpath(elt.EntryName())) + path, err := location.XpathWithComponents(o.client.Versioning(), components...) if err != nil { return nil, &Error{err: err, message: "failed to create xpath for an existing entry"} } @@ -154,7 +181,12 @@ func (o *EntryObjectManager[E, L, S]) CreateMany(ctx context.Context, location L } } - existing, err = o.service.List(ctx, location, "get", "", "") + xpath, err = location.XpathWithComponents(o.client.Versioning(), append(parentComponents, util.AsEntryXpath(""))...) + if err != nil { + return nil, err + } + + existing, err = o.service.ListWithXpath(ctx, util.AsXpath(xpath), "get", "", "") if err != nil && !sdkerrors.IsObjectNotFound(err) { return nil, &Error{err: err, message: "failed to list existing entries on the server"} } @@ -170,20 +202,38 @@ func (o *EntryObjectManager[E, L, S]) CreateMany(ctx context.Context, location L return filtered, nil } -func (o *EntryObjectManager[E, T, S]) Delete(ctx context.Context, location T, names []string) error { - err := o.service.Delete(ctx, location, names...) - if err != nil { - if sdkerrors.IsObjectNotFound(err) { - return ErrObjectNotFound - } else { - return &Error{err: err, message: "sdk error while deleting"} +func (o *EntryObjectManager[E, T, S]) Delete(ctx context.Context, location T, parentComponents []string, names []string) error { + deletes := xmlapi.NewChunkedMultiConfig(o.batchSize, len(names)) + + for _, elt := range names { + components := append(parentComponents, util.AsEntryXpath(elt)) + xpath, err := location.XpathWithComponents(o.client.Versioning(), components...) + if err != nil { + return err } + + deletes.Add(&xmlapi.Config{ + Action: "delete", + Xpath: util.AsXpath(xpath), + Target: o.client.GetTarget(), + }) + } + + _, _, _, err := o.client.MultiConfig(ctx, deletes, false, nil) + if err != nil { + return &Error{err: err, message: "sdk error while deleting"} } + return nil } -func (o *EntryObjectManager[E, L, S]) Update(ctx context.Context, location L, entry E, name string) (E, error) { - updated, err := o.service.Update(ctx, location, entry, name) +func (o *EntryObjectManager[E, L, S]) Update(ctx context.Context, location L, components []string, entry E, name string) (E, error) { + xpath, err := location.XpathWithComponents(o.client.Versioning(), append(components, util.AsEntryXpath(name))...) + if err != nil { + return *new(E), err + } + + err = o.service.UpdateWithXpath(ctx, util.AsXpath(xpath), entry, name) if err != nil { if sdkerrors.IsObjectNotFound(err) { return *new(E), ErrObjectNotFound @@ -192,10 +242,10 @@ func (o *EntryObjectManager[E, L, S]) Update(ctx context.Context, location L, en } } - return updated, nil + return o.service.ReadWithXpath(ctx, util.AsXpath(xpath), "get") } -func (o *EntryObjectManager[E, L, S]) UpdateMany(ctx context.Context, location L, stateEntries []E, planEntries []E) ([]E, error) { +func (o *EntryObjectManager[E, L, S]) UpdateMany(ctx context.Context, location L, components []string, stateEntries []E, planEntries []E) ([]E, error) { stateEntriesByName := o.entriesByName(stateEntries, entryUnknown) planEntriesByName := o.entriesByName(planEntries, entryUnknown) renamedEntries := make(map[string]struct{}) @@ -272,7 +322,12 @@ func (o *EntryObjectManager[E, L, S]) UpdateMany(ctx context.Context, location L } } - existing, err := o.service.List(ctx, location, "get", "", "") + xpath, err := location.XpathWithComponents(o.client.Versioning(), append(components, util.AsEntryXpath(""))...) + if err != nil { + return nil, err + } + + existing, err := o.service.ListWithXpath(ctx, util.AsXpath(xpath), "get", "", "") if err != nil && !sdkerrors.IsObjectNotFound(err) { return nil, &Error{err: err, message: "failed to get a list of existing entries from the server"} } @@ -296,7 +351,8 @@ func (o *EntryObjectManager[E, L, S]) UpdateMany(ctx context.Context, location L return nil, &Error{err: ErrConflict, message: "entry with a matching name already exists"} } - path, err := location.XpathWithEntryName(o.client.Versioning(), existingEntryName) + components := append(components, util.AsEntryXpath(existingEntryName)) + path, err := location.XpathWithComponents(o.client.Versioning(), components...) if err != nil { return nil, &Error{err: err, message: "failed to create xpath for an existing entry"} } @@ -331,7 +387,7 @@ func (o *EntryObjectManager[E, L, S]) UpdateMany(ctx context.Context, location L } for _, elt := range processedStateEntriesByName { - path, err := location.XpathWithEntryName(o.client.Versioning(), elt.Entry.EntryName()) + path, err := location.XpathWithComponents(o.client.Versioning(), append(components, util.AsEntryXpath(elt.Entry.EntryName()))...) if err != nil { return nil, &Error{err: err, message: "failed to create xpath for entry"} } @@ -384,7 +440,12 @@ func (o *EntryObjectManager[E, L, S]) UpdateMany(ctx context.Context, location L } } - existing, err = o.service.List(ctx, location, "get", "", "") + xpath, err = location.XpathWithComponents(o.client.Versioning(), append(components, util.AsEntryXpath(""))...) + if err != nil { + return nil, err + } + + existing, err = o.service.ListWithXpath(ctx, util.AsXpath(xpath), "get", "", "") if err != nil && !sdkerrors.IsObjectNotFound(err) { return nil, fmt.Errorf("Failed to list remote entries: %w", err) } diff --git a/assets/terraform/internal/manager/entry_import.go b/assets/terraform/internal/manager/entry_import.go index e98a23fe..7d5d8a65 100644 --- a/assets/terraform/internal/manager/entry_import.go +++ b/assets/terraform/internal/manager/entry_import.go @@ -2,27 +2,35 @@ package manager import ( "context" + "errors" + "fmt" sdkerrors "github.com/PaloAltoNetworks/pango/errors" + "github.com/PaloAltoNetworks/pango/util" + "github.com/PaloAltoNetworks/pango/xmlapi" ) type SDKImportableEntryService[E EntryObject, L EntryLocation, IL ImportLocation] interface { - Create(context.Context, L, []IL, E) (E, error) - Read(context.Context, L, string, string) (E, error) + CreateWithXpath(context.Context, string, E) error + ReadWithXpath(context.Context, string, string) (E, error) List(context.Context, L, string, string, string) ([]E, error) - Update(context.Context, L, E, string) (E, error) + UpdateWithXpath(context.Context, string, E, string) error Delete(context.Context, L, []IL, ...string) error + ImportToLocations(context.Context, L, []IL, string) error + UnimportFromLocations(context.Context, L, []IL, []string) error } type ImportableEntryObjectManager[E EntryObject, L EntryLocation, IL ImportLocation, IS SDKImportableEntryService[E, L, IL]] struct { + batchSize int service IS client SDKClient specifier func(E) (any, error) matcher func(E, E) bool } -func NewImportableEntryObjectManager[E EntryObject, L EntryLocation, IL ImportLocation, IS SDKImportableEntryService[E, L, IL]](client SDKClient, service IS, specifier func(E) (any, error), matcher func(E, E) bool) *ImportableEntryObjectManager[E, L, IL, IS] { +func NewImportableEntryObjectManager[E EntryObject, L EntryLocation, IL ImportLocation, IS SDKImportableEntryService[E, L, IL]](client SDKClient, service IS, batchSize int, specifier func(E) (any, error), matcher func(E, E) bool) *ImportableEntryObjectManager[E, L, IL, IS] { return &ImportableEntryObjectManager[E, L, IL, IS]{ + batchSize: batchSize, service: service, client: client, specifier: specifier, @@ -34,44 +42,91 @@ func (o *ImportableEntryObjectManager[E, L, IL, IS]) ReadMany(ctx context.Contex return nil, &Error{err: ErrInternal, message: "called ReadMany on an importable singular resource"} } -func (o *ImportableEntryObjectManager[E, L, IL, IS]) Read(ctx context.Context, location L, name string) (E, error) { - object, err := o.service.Read(ctx, location, name, "get") +func (o *ImportableEntryObjectManager[E, L, IL, IS]) Read(ctx context.Context, location L, components []string, name string) (E, error) { + xpath, err := location.XpathWithComponents(o.client.Versioning(), append(components, util.AsEntryXpath(name))...) if err != nil { - return *new(E), ErrObjectNotFound + return *new(E), err + } + + object, err := o.service.ReadWithXpath(ctx, util.AsXpath(xpath), "get") + if err != nil { + if sdkerrors.IsObjectNotFound(err) { + return *new(E), ErrObjectNotFound + } + return *new(E), &Error{err: err} } return object, nil } -func (o *ImportableEntryObjectManager[E, L, IL, IS]) Create(ctx context.Context, location L, importLocs []IL, entry E) (E, error) { - existing, err := o.service.List(ctx, location, "get", "", "") - if err != nil && !sdkerrors.IsObjectNotFound(err) { +func (o *ImportableEntryObjectManager[E, L, IL, IS]) Create(ctx context.Context, location L, components []string, entry E) (E, error) { + name := entry.EntryName() + + _, err := o.Read(ctx, location, components, name) + if err == nil { + return *new(E), &Error{err: ErrConflict, message: fmt.Sprintf("entry '%s' already exists", name)} + } + + if err != nil && !errors.Is(err, ErrObjectNotFound) { return *new(E), err } - for _, elt := range existing { - if elt.EntryName() == entry.EntryName() { - return *new(E), ErrConflict - } + xpath, err := location.XpathWithComponents(o.client.Versioning(), append(components, util.AsEntryXpath(name))...) + if err != nil { + return *new(E), err + } + + err = o.service.CreateWithXpath(ctx, util.AsXpath(xpath[:len(xpath)-1]), entry) + if err != nil { + return *new(E), err } - obj, err := o.service.Create(ctx, location, importLocs, entry) - return obj, err + return o.Read(ctx, location, components, name) } -func (o *ImportableEntryObjectManager[E, L, IL, IS]) Update(ctx context.Context, location L, entry E, name string) (E, error) { - updated, err := o.service.Update(ctx, location, entry, name) +func (o *ImportableEntryObjectManager[E, L, IL, IS]) Update(ctx context.Context, location L, components []string, entry E, name string) (E, error) { + xpath, err := location.XpathWithComponents(o.client.Versioning(), append(components, util.AsEntryXpath(entry.EntryName()))...) if err != nil { return *new(E), &Error{err: err, message: "error during Update call"} } - return updated, nil + err = o.service.UpdateWithXpath(ctx, util.AsXpath(xpath), entry, name) + if err != nil { + return *new(E), &Error{err: err, message: "error during Update call"} + } + + return o.service.ReadWithXpath(ctx, util.AsXpath(xpath), "get") } -func (o *ImportableEntryObjectManager[E, L, IL, IS]) Delete(ctx context.Context, location L, importLocations []IL, names []string, exhaustive ExhaustiveType) error { - err := o.service.Delete(ctx, location, importLocations, names...) +func (o *ImportableEntryObjectManager[E, L, IL, IS]) Delete(ctx context.Context, location L, importLocations []IL, components []string, names []string) error { + deletes := xmlapi.NewChunkedMultiConfig(o.batchSize, len(names)) + + for _, elt := range names { + components := append(components, util.AsEntryXpath(elt)) + xpath, err := location.XpathWithComponents(o.client.Versioning(), components...) + if err != nil { + return err + } + + deletes.Add(&xmlapi.Config{ + Action: "delete", + Xpath: util.AsXpath(xpath), + Target: o.client.GetTarget(), + }) + } + + _, _, _, err := o.client.MultiConfig(ctx, deletes, false, nil) if err != nil { return &Error{err: err, message: "sdk error while deleting"} } + return nil } + +func (o *ImportableEntryObjectManager[E, L, IL, IS]) ImportToLocations(ctx context.Context, location L, importLocs []IL, entry string) error { + return o.service.ImportToLocations(ctx, location, importLocs, entry) +} + +func (o *ImportableEntryObjectManager[E, L, IL, IS]) UnimportFromLocations(ctx context.Context, location L, importLocs []IL, entry string) error { + return o.service.UnimportFromLocations(ctx, location, importLocs, []string{entry}) +} diff --git a/assets/terraform/internal/manager/entry_test.go b/assets/terraform/internal/manager/entry_test.go index 0fd67ca2..f4f27ad4 100644 --- a/assets/terraform/internal/manager/entry_test.go +++ b/assets/terraform/internal/manager/entry_test.go @@ -14,8 +14,8 @@ var _ = Expect var _ = Describe("Entry", func() { existing := []*MockEntryObject{{Name: "1", Value: "A"}, {Name: "2", Value: "B"}, {Name: "3", Value: "C"}} var client *MockEntryClient[*MockEntryObject] - var service manager.SDKEntryService[*MockEntryObject, MockLocation] - var sdk *manager.EntryObjectManager[*MockEntryObject, MockLocation, manager.SDKEntryService[*MockEntryObject, MockLocation]] + var service *MockEntryService[*MockEntryObject, MockLocation] + var sdk *manager.EntryObjectManager[*MockEntryObject, MockLocation, *MockEntryService[*MockEntryObject, MockLocation]] var batchSize int location := MockLocation{} @@ -26,13 +26,14 @@ var _ = Describe("Entry", func() { batchSize = 500 client = NewMockEntryClient(existing) service = NewMockEntryService[*MockEntryObject, MockLocation](client) - sdk = manager.NewEntryObjectManager(client, service, batchSize, MockEntrySpecifier, MockEntryMatcher) + sdk = manager.NewEntryObjectManager[*MockEntryObject, MockLocation, *MockEntryService[*MockEntryObject, MockLocation]](client, service, batchSize, MockEntrySpecifier, MockEntryMatcher) }) Context("Read()", func() { When("reading entry that does not exist", func() { It("should return nil object and ErrObjectNotFound error", func() { - object, err := sdk.Read(ctx, location, "4") + object, err := sdk.Read(ctx, location, []string{}, "4") + Expect(err).To(HaveOccurred()) Expect(err).To(MatchError(MatchRegexp("Object not found"))) Expect(object).To(BeNil()) @@ -40,7 +41,7 @@ var _ = Describe("Entry", func() { }) When("reading entry that exists", func() { It("should return nil error and the existing entry", func() { - object, err := sdk.Read(ctx, location, "1") + object, err := sdk.Read(ctx, location, []string{}, "1") Expect(err).ToNot(HaveOccurred()) Expect(object.Name).To(Equal("1")) }) @@ -51,7 +52,7 @@ var _ = Describe("Entry", func() { When("no entries are in the state", func() { It("should return an empty list of entries", func() { entries := []*MockEntryObject{} - processed, err := sdk.ReadMany(ctx, location, entries) + processed, err := sdk.ReadMany(ctx, location, []string{}, entries) Expect(err).ToNot(HaveOccurred()) Expect(processed).To(HaveLen(0)) }) @@ -59,7 +60,7 @@ var _ = Describe("Entry", func() { When("there are entries in the state", func() { It("should return a list of entries from the server that match state entries", func() { entries := []*MockEntryObject{{Name: "1"}, {Name: "2"}} - processed, err := sdk.ReadMany(ctx, location, entries) + processed, err := sdk.ReadMany(ctx, location, []string{}, entries) Expect(err).ToNot(HaveOccurred()) Expect(processed).To(HaveLen(2)) @@ -74,7 +75,7 @@ var _ = Describe("Entry", func() { Context("when the entry already exists on the server", func() { It("should return an error back to the caller", func() { entry := &MockEntryObject{Name: "1", Value: "A"} - processed, err := sdk.Create(ctx, location, entry) + processed, err := sdk.Create(ctx, location, []string{entry.EntryName()}, entry) Expect(err).To(MatchError(MatchRegexp("already exists"))) Expect(processed).To(BeNil()) @@ -83,7 +84,7 @@ var _ = Describe("Entry", func() { Context("when there is no conflict between plan and remote state", func() { It("should return a pointer to the created object", func() { entry := &MockEntryObject{Name: "4", Value: "D"} - processed, err := sdk.Create(ctx, location, entry) + processed, err := sdk.Create(ctx, location, []string{entry.EntryName()}, entry) Expect(err).ToNot(HaveOccurred()) Expect(processed).ToNot(BeNil()) Expect(processed.EntryName()).To(Equal(entry.Name)) @@ -98,7 +99,7 @@ var _ = Describe("Entry", func() { Context("and some entries already exist on the server", func() { It("should return an error about conflict", func() { entries := []*MockEntryObject{{Name: "1", Value: "A"}, {Name: "4", Value: "D"}} - processed, err := sdk.CreateMany(ctx, location, entries) + processed, err := sdk.CreateMany(ctx, location, []string{}, entries) Expect(err).To(MatchError(manager.ErrConflict)) Expect(processed).To(BeNil()) @@ -111,7 +112,7 @@ var _ = Describe("Entry", func() { Context("and the list of entries is empty", func() { It("should not make any changes", func() { entries := []*MockEntryObject{} - processed, err := sdk.CreateMany(ctx, location, entries) + processed, err := sdk.CreateMany(ctx, location, []string{}, entries) Expect(err).ToNot(HaveOccurred()) Expect(processed).To(BeEmpty()) @@ -124,7 +125,7 @@ var _ = Describe("Entry", func() { Context("and there are new entries on the list", func() { It("should add those entries to the server and return only those new entries", func() { entries := []*MockEntryObject{{Name: "4", Value: "D"}, {Name: "5", Value: "E"}} - processed, err := sdk.CreateMany(ctx, location, entries) + processed, err := sdk.CreateMany(ctx, location, []string{}, entries) Expect(err).ToNot(HaveOccurred()) Expect(processed).To(HaveExactElements(entries)) @@ -144,7 +145,7 @@ var _ = Describe("Entry", func() { Context("when entries from the plan are missing from the server", func() { It("should recreate them, and return back list of all managed entries", func() { expected := append(existing, &MockEntryObject{Name: "4", Value: "D"}) - processed, err := sdk.UpdateMany(ctx, location, existing, expected) + processed, err := sdk.UpdateMany(ctx, location, []string{}, existing, expected) Expect(err).ToNot(HaveOccurred()) Expect(processed).To(HaveExactElements(expected)) @@ -157,7 +158,7 @@ var _ = Describe("Entry", func() { It("should properly remove deleted entries from the server and return back updated list", func() { stateEntries := []*MockEntryObject{{Name: "1", Value: "A"}, {Name: "2", Value: "B"}, {Name: "3", Value: "C"}} planEntries := []*MockEntryObject{{Name: "1", Value: "A"}, {Name: "3", Value: "C"}} - processed, err := sdk.UpdateMany(ctx, location, stateEntries, planEntries) + processed, err := sdk.UpdateMany(ctx, location, []string{}, stateEntries, planEntries) Expect(err).ToNot(HaveOccurred()) Expect(processed).To(HaveExactElements(planEntries)) @@ -170,7 +171,7 @@ var _ = Describe("Entry", func() { Context("when entries from the plan are missing from the server", func() { It("should not delete anything from the server", func() { entries := []string{"4"} - err := sdk.Delete(ctx, location, entries) + err := sdk.Delete(ctx, location, []string{}, entries) Expect(err).ToNot(HaveOccurred()) Expect(client.list()).To(HaveExactElements(existing)) diff --git a/assets/terraform/internal/manager/entry_utils_test.go b/assets/terraform/internal/manager/entry_utils_test.go index 1ac2c3ed..7d3bd6b2 100644 --- a/assets/terraform/internal/manager/entry_utils_test.go +++ b/assets/terraform/internal/manager/entry_utils_test.go @@ -3,8 +3,10 @@ package manager_test import ( "container/list" "context" + "log/slog" "net/http" "net/url" + "strings" sdkerrors "github.com/PaloAltoNetworks/pango/errors" "github.com/PaloAltoNetworks/pango/version" @@ -18,24 +20,24 @@ type MockEntryObject struct { Value string } -func (o *MockEntryObject) EntryName() string { - return o.Name +func (o *MockEntryObject) EntryUuid() *string { + panic("called EntryUuid on MockEntryObject") } -func (o *MockEntryObject) SetEntryName(name string) { - o.Name = name +func (o *MockEntryObject) SetEntryUuid(value *string) { + panic("called SetEntryUuid on MockEntryObject") } -func (o *MockEntryObject) EntryUuid() *string { - panic("mock entry object EntryUuid() called") +func (o *MockEntryObject) EntryName() string { + return o.Name } -func (o *MockEntryObject) SetEntryUuid(uuid *string) { - panic("mock entry object SetEntryUuid() called") +func (o *MockEntryObject) SetEntryName(name string) { + o.Name = name } func (o *MockEntryObject) DeepCopy() any { - return &MockUuidObject{ + return &MockEntryObject{ Name: o.Name, Value: o.Value, } @@ -43,17 +45,23 @@ func (o *MockEntryObject) DeepCopy() any { type MockEntryClient[E manager.UuidObject] struct { Initial *list.List + Current *list.List MultiConfigOpers []MultiConfigOper } func NewMockEntryClient[E manager.UuidObject](initial []E) *MockEntryClient[E] { l := list.New() + c := list.New() + for _, elt := range initial { - l.PushBack(elt) + entry := interface{}(elt).(*MockEntryObject) + l.PushBack(entry) + c.PushBack(entry.DeepCopy()) } return &MockEntryClient[E]{ Initial: l, + Current: c, } } @@ -83,14 +91,16 @@ func (o *MockEntryClient[E]) ChunkedMultiConfig(ctx context.Context, updates *xm } func (o *MockEntryClient[E]) MultiConfig(ctx context.Context, updates *xmlapi.MultiConfig, arg1 bool, arg2 url.Values) ([]byte, *http.Response, *xmlapi.MultiConfigResponse, error) { - o.MultiConfigOpers, _ = MultiConfig[E](updates, &o.Initial, multiConfigEntry, 0) + o.MultiConfigOpers, _ = MultiConfig[E](updates, &o.Current, multiConfigEntry, 0) return nil, nil, nil, nil } func (o *MockEntryClient[E]) list() []E { var entries []E - for e := o.Initial.Front(); e != nil; e = e.Next() { + slog.Debug("MockEntryClient list()", "o.Current", o.Current, "o.Current.Front()", o.Current.Front()) + for e := o.Current.Front(); e != nil; e = e.Next() { + slog.Debug("MockEntryClient list()", "entry", e.Value.(E).EntryName()) entries = append(entries, e.Value.(E)) } @@ -101,28 +111,41 @@ type MockEntryService[E manager.UuidObject, L manager.EntryLocation] struct { client *MockEntryClient[E] } +func (o *MockEntryService[E, L]) CreateWithXpath(ctx context.Context, xpath string, entry E) error { + _, err := o.Create(ctx, *new(L), entry) + return err +} + func (o *MockEntryService[E, L]) Create(ctx context.Context, location L, entry E) (E, error) { o.client.Initial.PushBack(entry) return entry, nil } -func (o *MockEntryService[E, L]) Update(ctx context.Context, location L, entry E, name string) (E, error) { +func (o *MockEntryService[E, L]) UpdateWithXpath(ctx context.Context, xpath string, entry E, name string) error { for e := o.client.Initial.Front(); e != nil; e = e.Next() { eltEntry := e.Value.(E) if entry.EntryName() == eltEntry.EntryName() { e.Value = entry - return entry, nil + return nil } } - return *new(E), sdkerrors.Panos{Code: 7, Msg: "Object not found"} + return sdkerrors.Panos{Code: 7, Msg: "Object not found"} } -func (o *MockEntryService[E, L]) List(ctx context.Context, location L, action string, filter string, quote string) ([]E, error) { +func (o *MockEntryService[E, L]) ListWithXpath(ctx context.Context, xpath string, action string, filter string, quote string) ([]E, error) { return o.client.list(), nil } +func (o *MockEntryService[E, L]) ReadWithXpath(ctx context.Context, xpath string, action string) (E, error) { + components := strings.Split(xpath, "/") + name := components[len(components)-1] + name = strings.TrimPrefix(name, "entry[@name='") + name = strings.TrimSuffix(name, "']") + return o.Read(ctx, *new(L), name, action) +} + func (o *MockEntryService[E, L]) Read(ctx context.Context, location L, name string, action string) (E, error) { for e := o.client.Initial.Front(); e != nil; e = e.Next() { entry := e.Value.(E) @@ -134,24 +157,6 @@ func (o *MockEntryService[E, L]) Read(ctx context.Context, location L, name stri return *new(E), sdkerrors.Panos{Code: 7, Msg: "Object not found"} } -func (o *MockEntryService[E, L]) Delete(ctx context.Context, location L, names ...string) error { - namesMap := make(map[string]struct{}, len(names)) - for _, elt := range names { - namesMap[elt] = struct{}{} - } - - var next *list.Element - for e := o.client.Initial.Front(); e != nil; e = next { - next = e.Next() - entry := e.Value.(E) - if _, found := namesMap[entry.EntryName()]; found { - o.client.Initial.Remove(e) - } - } - - return nil -} - func NewMockEntryService[E manager.UuidObject, L manager.EntryLocation](client *MockEntryClient[E]) *MockEntryService[E, L] { return &MockEntryService[E, L]{ client: client, diff --git a/assets/terraform/internal/manager/utils_test.go b/assets/terraform/internal/manager/utils_test.go index 9cf76065..e8de4f26 100644 --- a/assets/terraform/internal/manager/utils_test.go +++ b/assets/terraform/internal/manager/utils_test.go @@ -22,8 +22,8 @@ func (o MockLocation) IsValid() error { panic("unimplemented") } -func (o MockLocation) XpathWithEntryName(version version.Number, name string) ([]string, error) { - return []string{"some", "location", name}, nil +func (o MockLocation) XpathWithComponents(version version.Number, components ...string) ([]string, error) { + return []string{"some", "location", components[0]}, nil } func (o MockLocation) XpathWithUuid(version version.Number, uuid string) ([]string, error) { @@ -116,6 +116,8 @@ func MultiConfig[E sdkmanager.UuidObject](updates *xmlapi.MultiConfig, existingP for _, oper := range updates.Operations { xpathParts := strings.Split(oper.Xpath, "/") entryName := xpathParts[len(xpathParts)-1] + entryName = strings.TrimPrefix(entryName, "entry[@name='") + entryName = strings.TrimSuffix(entryName, "']") op := oper.XMLName.Local operEntry := MultiConfigOper{ @@ -123,6 +125,8 @@ func MultiConfig[E sdkmanager.UuidObject](updates *xmlapi.MultiConfig, existingP EntryName: entryName, } + slog.Debug("MultiConfig", "operEntry", operEntry) + switch MultiConfigOperType(op) { case MultiConfigOperSet, MultiConfigOperEdit: entry := oper.Data.(E) @@ -147,7 +151,12 @@ func MultiConfig[E sdkmanager.UuidObject](updates *xmlapi.MultiConfig, existingP idx += 1 } case MultiConfigOperDelete: - fixIndices(entriesByName[entryName].Idx) + entry, found := entriesByName[entryName] + if !found { + continue + } + + fixIndices(entry.Idx) delete(entriesByName, entryName) idx -= 1 case MultiConfigOperMove: @@ -229,45 +238,33 @@ func MultiConfig[E sdkmanager.UuidObject](updates *xmlapi.MultiConfig, existingP panic(fmt.Sprintf("UNKNOWN OPERATION: %s", op)) } - for idx := range entries { - entries[idx] = nil - } + opers = append(opers, operEntry) + } - for _, elt := range entriesByName { - if elt.State == entryOk { - if entries[elt.Idx] != nil { - var formattedEntries []string - idx := 1 - for _, elt := range entriesByName { - formattedEntries = append( - formattedEntries, - fmt.Sprintf("%d:{Entry:%s State:%s Idx:%d}", idx, elt.Entry.EntryName(), elt.State, elt.Idx), - ) - idx += 1 - } - formattedString := fmt.Sprintf("map[%s]", strings.Join(formattedEntries, " ")) - slog.Debug("Seen elements with duplicated indices", "entries", formattedString) - panic(fmt.Sprintf("element with idx %d already seen, problem with movement logic", elt.Idx)) - } - entries[elt.Idx] = &elt - } + for idx := range entries { + entries[idx] = nil + } + + for _, elt := range entriesByName { + if elt.State != entryOk { + continue } - var orderedEntryNames []string - transformed := list.New() - for _, elt := range entries { - if elt == nil { - continue + if entries[elt.Idx] != nil { + var formattedEntries []string + idx := 1 + for _, elt := range entriesByName { + formattedEntries = append( + formattedEntries, + fmt.Sprintf("%d:{Entry:%s State:%s Idx:%d}", idx, elt.Entry.EntryName(), elt.State, elt.Idx), + ) + idx += 1 } - - orderedEntryNames = append(orderedEntryNames, elt.Entry.EntryName()) - transformed.PushBack(elt.Entry) - + formattedString := fmt.Sprintf("map[%s]", strings.Join(formattedEntries, " ")) + slog.Debug("Seen elements with duplicated indices", "entries", formattedString) + panic(fmt.Sprintf("element with idx %d already seen, problem with movement logic", elt.Idx)) } - - slog.Debug("MultiConfig", "orderedEntryNames", orderedEntryNames) - - opers = append(opers, operEntry) + entries[elt.Idx] = &elt } transformed := list.New() diff --git a/assets/terraform/internal/manager/uuid.go b/assets/terraform/internal/manager/uuid.go index eaea56fd..9134455b 100644 --- a/assets/terraform/internal/manager/uuid.go +++ b/assets/terraform/internal/manager/uuid.go @@ -30,7 +30,7 @@ type UuidObject interface { } type UuidLocation interface { - XpathWithEntryName(version.Number, string) ([]string, error) + XpathWithComponents(version.Number, ...string) ([]string, error) } type SDKUuidService[E UuidObject, L UuidLocation] interface { @@ -219,7 +219,7 @@ func (o *UuidObjectManager[E, L, S]) moveNonExhaustive(ctx context.Context, loca return nil } -func (o *UuidObjectManager[E, L, S]) CreateMany(ctx context.Context, location L, planEntries []E, exhaustive ExhaustiveType, sdkPosition movement.Position) ([]E, error) { +func (o *UuidObjectManager[E, L, S]) CreateMany(ctx context.Context, location L, parentComponents []string, planEntries []E, exhaustive ExhaustiveType, sdkPosition movement.Position) ([]E, error) { var diags diag.Diagnostics planEntriesByName := o.entriesByName(planEntries, entryUnknown) @@ -237,9 +237,10 @@ func (o *UuidObjectManager[E, L, S]) CreateMany(ctx context.Context, location L, switch exhaustive { case Exhaustive: for _, elt := range existing { - path, err := location.XpathWithEntryName(o.client.Versioning(), elt.EntryName()) + components := append(parentComponents, util.AsEntryXpath(elt.EntryName())) + path, err := location.XpathWithComponents(o.client.Versioning(), components...) if err != nil { - return nil, ErrMarshaling + return nil, err } updates.Add(&xmlapi.Config{ @@ -257,9 +258,9 @@ func (o *UuidObjectManager[E, L, S]) CreateMany(ctx context.Context, location L, } for _, elt := range planEntries { - path, err := location.XpathWithEntryName(o.client.Versioning(), elt.EntryName()) + path, err := location.XpathWithComponents(o.client.Versioning(), util.AsEntryXpath(elt.EntryName())) if err != nil { - return nil, ErrMarshaling + return nil, err } xmlEntry, err := o.specifier(elt) @@ -279,7 +280,7 @@ func (o *UuidObjectManager[E, L, S]) CreateMany(ctx context.Context, location L, if len(updates.Operations) > 0 { _, err := o.client.ChunkedMultiConfig(ctx, updates, false, nil) if err != nil { - cleanupErr := o.cleanUpIncompleteUpdate(ctx, location, planEntriesByName, exhaustive) + cleanupErr := o.cleanUpIncompleteUpdate(ctx, location, parentComponents, planEntriesByName, exhaustive) return nil, errors.Join(fmt.Errorf("failed to create entries on the server: %w", err), cleanupErr) } } @@ -312,7 +313,7 @@ func (o *UuidObjectManager[E, L, S]) CreateMany(ctx context.Context, location L, return entries, nil } -func (o *UuidObjectManager[E, L, S]) UpdateMany(ctx context.Context, location L, stateEntries []E, planEntries []E, exhaustive ExhaustiveType, position movement.Position) ([]E, error) { +func (o *UuidObjectManager[E, L, S]) UpdateMany(ctx context.Context, location L, parentComponents []string, stateEntries []E, planEntries []E, exhaustive ExhaustiveType, position movement.Position) ([]E, error) { stateEntriesByName := o.entriesByName(stateEntries, entryUnknown) planEntriesByName := o.entriesByName(planEntries, entryUnknown) if len(planEntriesByName) != len(planEntries) { @@ -414,7 +415,7 @@ func (o *UuidObjectManager[E, L, S]) UpdateMany(ctx context.Context, location L, // state to find any required updates. for _, existingEntry := range existing { existingEntryName := existingEntry.EntryName() - path, err := location.XpathWithEntryName(o.client.Versioning(), existingEntryName) + path, err := location.XpathWithComponents(o.client.Versioning(), util.AsEntryXpath(existingEntryName)) if err != nil { return nil, &Error{err: err, message: "failed to create xpath for an existing entry"} } @@ -486,7 +487,8 @@ func (o *UuidObjectManager[E, L, S]) UpdateMany(ctx context.Context, location L, createOps := make([]*xmlapi.Config, len(planEntries)) for _, elt := range processedStateEntries { - path, err := location.XpathWithEntryName(o.client.Versioning(), elt.Entry.EntryName()) + components := append(parentComponents, util.AsEntryXpath(elt.Entry.EntryName())) + path, err := location.XpathWithComponents(o.client.Versioning(), components...) if err != nil { return nil, &Error{err: err, message: "failed to create xpath for an existing entry"} } @@ -547,7 +549,7 @@ func (o *UuidObjectManager[E, L, S]) UpdateMany(ctx context.Context, location L, if len(updates.Operations) > 0 { if _, err := o.client.ChunkedMultiConfig(ctx, updates, false, nil); err != nil { - cleanupErr := o.cleanUpIncompleteUpdate(ctx, location, processedStateEntries, exhaustive) + cleanupErr := o.cleanUpIncompleteUpdate(ctx, location, parentComponents, processedStateEntries, exhaustive) return nil, errors.Join(&Error{err: err, message: "failed to execute MultiConfig command"}, cleanupErr) } } @@ -639,29 +641,32 @@ func (o *UuidObjectManager[E, L, S]) ReadMany(ctx context.Context, location L, s return common, false, nil } -func (o *UuidObjectManager[E, L, S]) Delete(ctx context.Context, location L, entryNames []string, exhaustive ExhaustiveType) error { +func (o *UuidObjectManager[E, L, S]) Delete(ctx context.Context, location L, parentComponents []string, names []string, exhaustive ExhaustiveType) error { + deletes := xmlapi.NewChunkedMultiConfig(o.batchSize, len(names)) - var batches [][]string - for i := 0; i < len(entryNames); i += o.batchSize { - end := i + o.batchSize - if end > len(entryNames) { - end = len(entryNames) + for _, elt := range names { + components := append(parentComponents, util.AsEntryXpath(elt)) + xpath, err := location.XpathWithComponents(o.client.Versioning(), components...) + if err != nil { + return err } - batches = append(batches, entryNames[i:end]) + deletes.Add(&xmlapi.Config{ + Action: "delete", + Xpath: util.AsXpath(xpath), + Target: o.client.GetTarget(), + }) } - for _, elt := range batches { - err := o.service.Delete(ctx, location, elt...) - if err != nil { - return &Error{err: err, message: "sdk error while deleting"} - } + _, _, _, err := o.client.MultiConfig(ctx, deletes, false, nil) + if err != nil { + return &Error{err: err, message: "sdk error while deleting"} } return nil } -func (o *UuidObjectManager[E, L, S]) cleanUpIncompleteUpdate(ctx context.Context, location L, entries map[string]uuidObjectWithState[E], exhaustive ExhaustiveType) error { +func (o *UuidObjectManager[E, L, S]) cleanUpIncompleteUpdate(ctx context.Context, location L, parentComponents []string, entries map[string]uuidObjectWithState[E], exhaustive ExhaustiveType) error { var names []string for _, elt := range entries { if elt.State == entryMissing { @@ -669,5 +674,5 @@ func (o *UuidObjectManager[E, L, S]) cleanUpIncompleteUpdate(ctx context.Context } } - return o.Delete(ctx, location, names, exhaustive) + return o.Delete(ctx, location, parentComponents, names, exhaustive) } diff --git a/assets/terraform/internal/manager/uuid_test.go b/assets/terraform/internal/manager/uuid_test.go index 06190575..01653297 100644 --- a/assets/terraform/internal/manager/uuid_test.go +++ b/assets/terraform/internal/manager/uuid_test.go @@ -67,7 +67,7 @@ var _ = Describe("Server", func() { It("CreateMany() should create new entries on the server, and return them with uuid set", func() { entries := []*MockUuidObject{{Name: "1", Value: "A"}} - processed, err := manager.CreateMany(ctx, location, entries, sdkmanager.Exhaustive, movement.PositionFirst{}) + processed, err := manager.CreateMany(ctx, location, []string{}, entries, sdkmanager.Exhaustive, movement.PositionFirst{}) Expect(err).ToNot(HaveOccurred()) Expect(processed).To(HaveLen(1)) @@ -91,7 +91,7 @@ var _ = Describe("Server", func() { }) It("should not create any entries and return an error", func() { - processed, err := manager.CreateMany(ctx, location, entries, mode, position) + processed, err := manager.CreateMany(ctx, location, []string{}, entries, mode, position) Expect(err).To(MatchError(sdkmanager.ErrConflict)) Expect(processed).To(BeNil()) @@ -102,7 +102,7 @@ var _ = Describe("Server", func() { Context("and all entries being created are new to the server", func() { It("should create those entries in the correct position", func() { - processed, err := manager.CreateMany(ctx, location, entries, sdkmanager.NonExhaustive, movement.PositionFirst{}) + processed, err := manager.CreateMany(ctx, location, []string{}, entries, sdkmanager.NonExhaustive, movement.PositionFirst{}) Expect(err).ToNot(HaveOccurred()) Expect(processed).To(HaveLen(2)) @@ -123,7 +123,7 @@ var _ = Describe("Server", func() { }) It("should not return any error and overwrite all entries on the server", func() { - processed, err := manager.CreateMany(ctx, location, entries, mode, position) + processed, err := manager.CreateMany(ctx, location, []string{}, entries, mode, position) Expect(err).ToNot(HaveOccurred()) @@ -172,7 +172,7 @@ var _ = Describe("Server", func() { Expect(processed).To(HaveLen(3)) Expect(processed).NotTo(MatchEntries(entries)) - processed, err = manager.UpdateMany(ctx, location, entries, entries, sdkmanager.NonExhaustive, movement.PositionFirst{}) + processed, err = manager.UpdateMany(ctx, location, []string{}, entries, entries, sdkmanager.NonExhaustive, movement.PositionFirst{}) Expect(err).ToNot(HaveOccurred()) Expect(processed).To(HaveLen(3)) @@ -187,7 +187,7 @@ var _ = Describe("Server", func() { It("should create new entries on the top of the list", func() { entries := []*MockUuidObject{{Name: "4", Value: "D"}, {Name: "5", Value: "E"}, {Name: "6", Value: "F"}} - processed, err := manager.CreateMany(ctx, location, entries, sdkmanager.NonExhaustive, movement.PositionFirst{}) + processed, err := manager.CreateMany(ctx, location, []string{}, entries, sdkmanager.NonExhaustive, movement.PositionFirst{}) Expect(err).ToNot(HaveOccurred()) Expect(processed).To(HaveLen(3)) @@ -205,7 +205,7 @@ var _ = Describe("Server", func() { It("should create new entries on the bottom of the list", func() { entries := []*MockUuidObject{{Name: "4", Value: "D"}, {Name: "5", Value: "E"}, {Name: "6", Value: "F"}} - processed, err := manager.CreateMany(ctx, location, entries, sdkmanager.NonExhaustive, movement.PositionLast{}) + processed, err := manager.CreateMany(ctx, location, []string{}, entries, sdkmanager.NonExhaustive, movement.PositionLast{}) Expect(err).ToNot(HaveOccurred()) Expect(processed).To(HaveLen(3)) @@ -223,7 +223,7 @@ var _ = Describe("Server", func() { It("should create new entries directly after first existing element", func() { entries := []*MockUuidObject{{Name: "4", Value: "D"}, {Name: "5", Value: "E"}, {Name: "6", Value: "F"}} - processed, err := manager.CreateMany(ctx, location, entries, sdkmanager.NonExhaustive, movement.PositionAfter{Directly: true, Pivot: initial[0].Name}) + processed, err := manager.CreateMany(ctx, location, []string{}, entries, sdkmanager.NonExhaustive, movement.PositionAfter{Directly: true, Pivot: initial[0].Name}) Expect(err).ToNot(HaveOccurred()) Expect(processed).To(HaveLen(3)) @@ -248,7 +248,7 @@ var _ = Describe("Server", func() { pivot := initial[2].Name // "3" position = movement.PositionBefore{Directly: true, Pivot: pivot} - processed, err := manager.CreateMany(ctx, location, entries, sdkmanager.NonExhaustive, position) + processed, err := manager.CreateMany(ctx, location, []string{}, entries, sdkmanager.NonExhaustive, position) Expect(err).ToNot(HaveOccurred()) Expect(processed).To(HaveLen(3)) @@ -267,7 +267,7 @@ var _ = Describe("Server", func() { Context("and there is a duplicate entry within a list", func() { It("should properly raise an error", func() { entries := []*MockUuidObject{{Name: "4", Value: "D"}, {Name: "4", Value: "D"}} - _, err := manager.CreateMany(ctx, location, entries, sdkmanager.NonExhaustive, movement.PositionFirst{}) + _, err := manager.CreateMany(ctx, location, []string{}, entries, sdkmanager.NonExhaustive, movement.PositionFirst{}) Expect(err).To(MatchError(sdkmanager.ErrPlanConflict)) }) @@ -295,13 +295,13 @@ var _ = Describe("Server", func() { var position movement.Position position = movement.PositionFirst{} - _, err := manager.CreateMany(ctx, location, entries, sdkmanager.NonExhaustive, position) + _, err := manager.CreateMany(ctx, location, []string{}, entries, sdkmanager.NonExhaustive, position) Expect(err).ToNot(HaveOccurred()) entries = []*MockUuidObject{{Name: "99", Value: "ZZ"}} position = movement.PositionLast{} - _, err = manager.CreateMany(ctx, location, entries, sdkmanager.NonExhaustive, position) + _, err = manager.CreateMany(ctx, location, []string{}, entries, sdkmanager.NonExhaustive, position) Expect(err).ToNot(HaveOccurred()) current := client.list() @@ -313,7 +313,7 @@ var _ = Describe("Server", func() { entries = []*MockUuidObject{{Name: "2", Value: "B"}, {Name: "3", Value: "C"}} position = movement.PositionAfter{Pivot: "1", Directly: true} - _, err = manager.CreateMany(ctx, location, entries, sdkmanager.NonExhaustive, position) + _, err = manager.CreateMany(ctx, location, []string{}, entries, sdkmanager.NonExhaustive, position) Expect(err).ToNot(HaveOccurred()) current = client.list() @@ -330,7 +330,7 @@ var _ = Describe("Server", func() { entries = []*MockUuidObject{{Name: "4", Value: "D"}, {Name: "5", Value: "E"}} position = movement.PositionAfter{Pivot: "1", Directly: false} - _, err = manager.CreateMany(ctx, location, entries, sdkmanager.NonExhaustive, position) + _, err = manager.CreateMany(ctx, location, []string{}, entries, sdkmanager.NonExhaustive, position) Expect(err).ToNot(HaveOccurred()) current = client.list() @@ -349,7 +349,7 @@ var _ = Describe("Server", func() { entries = []*MockUuidObject{{Name: "6", Value: "F"}, {Name: "7", Value: "G"}} position = movement.PositionBefore{Pivot: "99", Directly: true} - _, err = manager.CreateMany(ctx, location, entries, sdkmanager.NonExhaustive, position) + _, err = manager.CreateMany(ctx, location, []string{}, entries, sdkmanager.NonExhaustive, position) Expect(err).ToNot(HaveOccurred()) current = client.list() @@ -370,7 +370,7 @@ var _ = Describe("Server", func() { entries = []*MockUuidObject{{Name: "8", Value: "H"}, {Name: "9", Value: "I"}} position = movement.PositionBefore{Pivot: "99", Directly: false} - _, err = manager.CreateMany(ctx, location, entries, sdkmanager.NonExhaustive, position) + _, err = manager.CreateMany(ctx, location, []string{}, entries, sdkmanager.NonExhaustive, position) Expect(err).ToNot(HaveOccurred()) current = client.list() diff --git a/assets/terraform/internal/manager/uuid_utils_test.go b/assets/terraform/internal/manager/uuid_utils_test.go index 9574d579..4d0b4e01 100644 --- a/assets/terraform/internal/manager/uuid_utils_test.go +++ b/assets/terraform/internal/manager/uuid_utils_test.go @@ -212,7 +212,7 @@ func (o *MockUuidService[E, T]) MoveGroup(ctx context.Context, location MockLoca updates := xmlapi.NewMultiConfig(len(movements)) for _, elt := range movements { - path, err := location.XpathWithEntryName(o.client.Versioning(), elt.Movable.EntryName()) + path, err := location.XpathWithComponents(o.client.Versioning(), elt.Movable.EntryName()) if err != nil { return err } diff --git a/assets/terraform/test/resource_address_group_test.go b/assets/terraform/test/resource_address_group_test.go index 12c83414..b1a26f43 100644 --- a/assets/terraform/test/resource_address_group_test.go +++ b/assets/terraform/test/resource_address_group_test.go @@ -1,22 +1,15 @@ package provider_test import ( - "context" "fmt" "testing" - "golang.org/x/sync/errgroup" - - sdkErrors "github.com/PaloAltoNetworks/pango/errors" - addressGroup "github.com/PaloAltoNetworks/pango/objects/address/group" - "github.com/hashicorp/terraform-plugin-testing/compare" "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" ) @@ -31,10 +24,6 @@ func TestAccPanosAddressGroup(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccCheckPanosAddressGroupDestroy( - fmt.Sprintf("%s-%s", resourceName, nameSuffix), - fmt.Sprintf("%s-base-%s", resourceName, nameSuffix), - ), Steps: []resource.TestStep{ { Config: makeAddressGroupConfig(resourceName), @@ -140,37 +129,3 @@ func makeAddressGroupConfig(label string) string { return fmt.Sprintf(confiTpl, label, label, label) } - -func testAccCheckPanosAddressGroupDestroy(entryNames ...string) func(s *terraform.State) error { - return func(s *terraform.State) error { - api := addressGroup.NewService(sdkClient) - location := addressGroup.NewSharedLocation() - - g := new(errgroup.Group) - - for _, addrGroupName := range entryNames { - g.Go(func() error { - ctx := context.TODO() - - reply, err := api.Read(ctx, *location, addrGroupName, "show") - if err != nil && !sdkErrors.IsObjectNotFound(err) { - return fmt.Errorf("reading address group entry %s via sdk: %v", addrGroupName, err) - } - - if reply != nil { - if reply.EntryName() == addrGroupName { - return fmt.Errorf("address group object still exists: %s", addrGroupName) - } - } - - return nil - }) - } - - if err := g.Wait(); err != nil { - return fmt.Errorf("checking destroy of address objects: %v", err) - } - - return nil - } -} diff --git a/assets/terraform/test/resource_addresses_test.go b/assets/terraform/test/resource_addresses_test.go index db32fed7..389a1c92 100644 --- a/assets/terraform/test/resource_addresses_test.go +++ b/assets/terraform/test/resource_addresses_test.go @@ -2,33 +2,84 @@ package provider_test import ( "context" - "errors" "fmt" "strings" "testing" - sdkErrors "github.com/PaloAltoNetworks/pango/errors" + sdkerrors "github.com/PaloAltoNetworks/pango/errors" "github.com/PaloAltoNetworks/pango/objects/address" - "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" ) +type expectServerAddressObjects struct { + Location address.Location + Prefix string + Addresses []string +} + +func ExpectServerAddressObjects(location address.Location, prefix string, addresses []string) *expectServerAddressObjects { + return &expectServerAddressObjects{ + Location: location, + Prefix: prefix, + Addresses: addresses, + } +} + +func (o *expectServerAddressObjects) CheckState(ctx context.Context, req statecheck.CheckStateRequest, resp *statecheck.CheckStateResponse) { + service := address.NewService(sdkClient) + + objects, err := service.List(ctx, o.Location, "get", "", "") + if err != nil && !sdkerrors.IsObjectNotFound(err) { + resp.Error = err + return + } + + objectsByName := make(map[string]int) + for _, elt := range o.Addresses { + objectsByName[fmt.Sprintf("%s-%s", o.Prefix, elt)] = 0 + } + + for _, elt := range objects { + _, found := objectsByName[elt.Name] + if !found { + objectsByName[elt.Name] = -1 + } else { + objectsByName[elt.Name] = 1 + } + } + + var errors []string + for name, state := range objectsByName { + switch state { + case -1: + errors = append(errors, fmt.Sprintf("%s: unexpected", name)) + case 0: + errors = append(errors, fmt.Sprintf("%s: missing", name)) + } + } + + if errors != nil { + resp.Error = fmt.Errorf("Unexpected server state: %s", strings.Join(errors, ", ")) + } +} + func TestAccAddresses(t *testing.T) { t.Parallel() nameSuffix := acctest.RandStringFromCharSet(6, acctest.CharSetAlphaNum) prefix := fmt.Sprintf("test-acc-%s", nameSuffix) + location := address.NewDeviceGroupLocation() + location.DeviceGroup.DeviceGroup = prefix + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccAddressesDestroy(prefix), Steps: []resource.TestStep{ { Config: testAccAddressesResourceTmpl, @@ -112,6 +163,7 @@ func TestAccAddresses(t *testing.T) { }), }), ), + ExpectServerAddressObjects(*location, prefix, []string{"ip-netmask", "ip-range", "ip-wildcard", "fqdn"}), }, }, { @@ -123,7 +175,7 @@ func TestAccAddresses(t *testing.T) { "description": config.StringVariable("description"), "ip_range": config.StringVariable("172.16.0.1-172.16.0.255"), }), - fmt.Sprintf("%s-fqdn", prefix): config.ObjectVariable(map[string]config.Variable{ + fmt.Sprintf("%s-fqdn2", prefix): config.ObjectVariable(map[string]config.Variable{ "fqdn": config.StringVariable("example.com"), }), }), @@ -143,7 +195,7 @@ func TestAccAddresses(t *testing.T) { "ip_wildcard": knownvalue.Null(), "fqdn": knownvalue.Null(), }), - fmt.Sprintf("%s-fqdn", prefix): knownvalue.ObjectExact(map[string]knownvalue.Check{ + fmt.Sprintf("%s-fqdn2", prefix): knownvalue.ObjectExact(map[string]knownvalue.Check{ "tags": knownvalue.Null(), "description": knownvalue.Null(), "disable_override": knownvalue.Null(), @@ -154,6 +206,7 @@ func TestAccAddresses(t *testing.T) { }), }), ), + ExpectServerAddressObjects(*location, prefix, []string{"ip-range", "fqdn2"}), }, }, { @@ -161,6 +214,9 @@ func TestAccAddresses(t *testing.T) { ConfigVariables: map[string]config.Variable{ "prefix": config.StringVariable(prefix), "addresses": config.MapVariable(map[string]config.Variable{})}, + ConfigStateChecks: []statecheck.StateCheck{ + ExpectServerAddressObjects(*location, prefix, []string{}), + }, }, { Config: testAccAddressesResourceTmpl, @@ -188,8 +244,14 @@ variable "addresses" { })) } +resource "panos_device_group" "example" { + location = { panorama = {} } + + name = var.prefix +} + resource "panos_administrative_tag" "tag" { - location = { shared = {} } + location = { device_group = { name = panos_device_group.example.name } } name = format("%s-tag", var.prefix) } @@ -197,7 +259,7 @@ resource "panos_administrative_tag" "tag" { resource "panos_addresses" "addresses" { depends_on = [ resource.panos_administrative_tag.tag ] - location = { shared = {} } + location = { device_group = { name = panos_device_group.example.name } } addresses = { for name, value in var.addresses : name => { disable_override = value.disable_override, @@ -210,34 +272,3 @@ resource "panos_addresses" "addresses" { }} } ` - -func testAccAddressesDestroy(prefix string) func(s *terraform.State) error { - return func(s *terraform.State) error { - api := address.NewService(sdkClient) - ctx := context.TODO() - - location := address.NewSharedLocation() - - entries, err := api.List(ctx, *location, "get", "", "") - if err != nil && !sdkErrors.IsObjectNotFound(err) { - return fmt.Errorf("listing interface management entries via sdk: %v", err) - } - - var leftEntries []string - for _, elt := range entries { - if strings.HasPrefix(elt.Name, prefix) { - leftEntries = append(leftEntries, elt.Name) - } - } - - if len(leftEntries) > 0 { - err := fmt.Errorf("terraform failed to remove entries from the server") - delErr := api.Delete(ctx, *location, leftEntries...) - if delErr != nil { - return errors.Join(err, delErr) - } - } - - return nil - } -} diff --git a/assets/terraform/test/resource_admin_role_test.go b/assets/terraform/test/resource_admin_role_test.go index 900eb7ea..89955139 100644 --- a/assets/terraform/test/resource_admin_role_test.go +++ b/assets/terraform/test/resource_admin_role_test.go @@ -1,21 +1,14 @@ package provider_test import ( - "context" - "errors" "fmt" - "strings" "testing" - "github.com/PaloAltoNetworks/pango/device/adminrole" - sdkErrors "github.com/PaloAltoNetworks/pango/errors" - "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" ) @@ -27,7 +20,6 @@ func TestAccAdminRoleDevice(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccAdminRoleDestroy(prefix), Steps: []resource.TestStep{ { Config: adminRoleResource1, @@ -3212,7 +3204,6 @@ func TestAccAdminRoleVsys(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccAdminRoleDestroy(prefix), Steps: []resource.TestStep{ { Config: adminRoleResource2, @@ -6355,39 +6346,3 @@ resource "panos_admin_role" "role" { } } ` - -func testAccAdminRoleDestroy(prefix string) func(s *terraform.State) error { - return func(s *terraform.State) error { - api := adminrole.NewService(sdkClient) - ctx := context.TODO() - - location := adminrole.NewTemplateLocation() - location.Template = &adminrole.TemplateLocation{ - Template: fmt.Sprintf("%s-tmpl", prefix), - PanoramaDevice: "localhost.localdomain", - } - - entries, err := api.List(ctx, *location, "get", "", "") - if err != nil && !sdkErrors.IsObjectNotFound(err) { - return fmt.Errorf("error while listing entries via sdk: %v", err) - } - - var leftEntries []string - for _, elt := range entries { - if strings.HasPrefix(elt.Name, prefix) { - leftEntries = append(leftEntries, elt.Name) - } - } - - if len(leftEntries) > 0 { - err := fmt.Errorf("terraform failed to remove entries from the server") - delErr := api.Delete(ctx, *location, leftEntries...) - if delErr != nil { - return errors.Join(err, delErr) - } - return err - } - - return nil - } -} diff --git a/assets/terraform/test/resource_administrative_tag_test.go b/assets/terraform/test/resource_administrative_tag_test.go index ba72315b..c381ef5c 100644 --- a/assets/terraform/test/resource_administrative_tag_test.go +++ b/assets/terraform/test/resource_administrative_tag_test.go @@ -2,22 +2,16 @@ package provider_test import ( "bytes" - "context" "fmt" - "strings" "testing" "text/template" - sdkerrors "github.com/PaloAltoNetworks/pango/errors" - tag "github.com/PaloAltoNetworks/pango/objects/admintag" - "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" ) @@ -112,23 +106,3 @@ func makeAdministrativeTagConfig(resourceName string) string { return buf.String() } - -func administrativeTagCheckDestroy(prefix string, location tag.Location) func(s *terraform.State) error { - return func(s *terraform.State) error { - service := tag.NewService(sdkClient) - ctx := context.TODO() - - tags, err := service.List(ctx, location, "get", "", "") - if err != nil && !sdkerrors.IsObjectNotFound(err) { - return err - } - - for _, elt := range tags { - if strings.HasPrefix(elt.Name, prefix) { - return DanglingObjectsError - } - } - - return nil - } -} diff --git a/assets/terraform/test/resource_aggregate_interface_test.go b/assets/terraform/test/resource_aggregate_interface_test.go index 895460c1..76fcb660 100644 --- a/assets/terraform/test/resource_aggregate_interface_test.go +++ b/assets/terraform/test/resource_aggregate_interface_test.go @@ -1,20 +1,14 @@ package provider_test import ( - "context" - "errors" "fmt" "testing" - sdkErrors "github.com/PaloAltoNetworks/pango/errors" - "github.com/PaloAltoNetworks/pango/network/interface/aggregate" - "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" ) @@ -28,9 +22,6 @@ func TestAccAggregateInterface_DecryptMirror(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccCheckPanosEthernetInterfaceDestroy( - prefix, interfaceName, - ), Steps: []resource.TestStep{ { Config: aggregateInterfaceDecryptMirror1, @@ -99,9 +90,6 @@ func TestAccAggregateInterface_HA(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccCheckPanosEthernetInterfaceDestroy( - prefix, interfaceName, - ), Steps: []resource.TestStep{ { Config: aggregateInterfaceHa1, @@ -220,9 +208,6 @@ func TestAccAggregateInterface_Layer2(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccCheckPanosEthernetInterfaceDestroy( - prefix, interfaceName, - ), Steps: []resource.TestStep{ { Config: aggregateInterfaceLayer21, @@ -330,9 +315,6 @@ func TestAccAggregateInterface_Layer3_1(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccCheckPanosEthernetInterfaceDestroy( - prefix, interfaceName, - ), Steps: []resource.TestStep{ { Config: aggregateInterfaceLayer31, @@ -513,9 +495,6 @@ func TestAccAggregateInterface_Layer3_2(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccCheckPanosEthernetInterfaceDestroy( - prefix, interfaceName, - ), Steps: []resource.TestStep{ { Config: aggregateInterfaceLayer32, @@ -662,9 +641,6 @@ func TestAccAggregateInterface_Layer3_3(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccCheckPanosEthernetInterfaceDestroy( - prefix, interfaceName, - ), Steps: []resource.TestStep{ { Config: aggregateInterfaceLayer33, @@ -1179,9 +1155,6 @@ func TestAccAggregateInterface_VirtualWire(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccCheckPanosEthernetInterfaceDestroy( - prefix, interfaceName, - ), Steps: []resource.TestStep{ { Config: aggregateInterfaceVirtualWire1, @@ -1248,29 +1221,3 @@ resource "panos_aggregate_interface" "iface" { } } ` - -func testAccCheckPanosAggregateInterfaceDestroy(prefix string, entry string) func(s *terraform.State) error { - return func(s *terraform.State) error { - api := aggregate.NewService(sdkClient) - ctx := context.TODO() - - location := aggregate.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 - } -} diff --git a/assets/terraform/test/resource_application_group_test.go b/assets/terraform/test/resource_application_group_test.go index 30bb3586..ea49880e 100644 --- a/assets/terraform/test/resource_application_group_test.go +++ b/assets/terraform/test/resource_application_group_test.go @@ -2,7 +2,6 @@ package provider_test import ( "context" - "errors" "fmt" "strings" "testing" @@ -10,13 +9,11 @@ import ( sdkerrors "github.com/PaloAltoNetworks/pango/errors" "github.com/PaloAltoNetworks/pango/objects/application/group" - // "github.com/hashicorp/terraform-plugin-testing/compare" "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" ) @@ -61,7 +58,6 @@ func TestAccPanosApplicationGroup(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccCheckPanosApplicationGroupDestroy(prefix), Steps: []resource.TestStep{ { Config: applicationGroupTmpl, @@ -130,35 +126,3 @@ resource "panos_device_group" "dg" { name = format("%s-dg1", var.prefix) } ` - -func testAccCheckPanosApplicationGroupDestroy(prefix string) func(s *terraform.State) error { - return func(s *terraform.State) error { - api := group.NewService(sdkClient) - location := group.NewDeviceGroupLocation() - location.DeviceGroup.DeviceGroup = fmt.Sprintf("%s-dg1", prefix) - - ctx := context.TODO() - existing, err := api.List(ctx, *location, "get", "", "") - if err != nil && !sdkerrors.IsObjectNotFound(err) { - return err - } - - var dangling []string - for _, elt := range existing { - if strings.HasPrefix(elt.Name, prefix) { - dangling = append(dangling, elt.Name) - } - } - - if len(dangling) > 0 { - err = fmt.Errorf("Some entries were left after terraform teardown") - deleteErr := api.Delete(ctx, *location, dangling...) - if deleteErr != nil { - err = errors.Join(err, deleteErr) - } - return err - } - - return nil - } -} diff --git a/assets/terraform/test/resource_application_test.go b/assets/terraform/test/resource_application_test.go index 22bff368..bdc5f797 100644 --- a/assets/terraform/test/resource_application_test.go +++ b/assets/terraform/test/resource_application_test.go @@ -1,21 +1,14 @@ package provider_test import ( - "context" - "errors" "fmt" - "strings" "testing" - sdkErrors "github.com/PaloAltoNetworks/pango/errors" - "github.com/PaloAltoNetworks/pango/objects/application" - "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" ) @@ -28,7 +21,6 @@ func TestAccApplication(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccApplicationDestroy(prefix), Steps: []resource.TestStep{ { Config: testApplicationTmpl, @@ -316,34 +308,3 @@ resource "panos_application" "app4" { } ` - -func testAccApplicationDestroy(prefix string) func(s *terraform.State) error { - return func(s *terraform.State) error { - api := application.NewService(sdkClient) - ctx := context.TODO() - - location := application.NewSharedLocation() - - entries, err := api.List(ctx, *location, "get", "", "") - if err != nil && !sdkErrors.IsObjectNotFound(err) { - return fmt.Errorf("listing interface management entries via sdk: %v", err) - } - - var leftEntries []string - for _, elt := range entries { - if strings.HasPrefix(elt.Name, prefix) { - leftEntries = append(leftEntries, elt.Name) - } - } - - if len(leftEntries) > 0 { - err := fmt.Errorf("terraform failed to remove entries from the server") - delErr := api.Delete(ctx, *location, leftEntries...) - if delErr != nil { - return errors.Join(err, delErr) - } - } - - return nil - } -} diff --git a/assets/terraform/test/resource_custom_url_category_test.go b/assets/terraform/test/resource_custom_url_category_test.go index b7bc0048..01dcee87 100644 --- a/assets/terraform/test/resource_custom_url_category_test.go +++ b/assets/terraform/test/resource_custom_url_category_test.go @@ -1,21 +1,14 @@ package provider_test import ( - "context" - "errors" "fmt" - "strings" "testing" - sdkErrors "github.com/PaloAltoNetworks/pango/errors" - category "github.com/PaloAltoNetworks/pango/objects/profiles/customurlcategory" - "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" ) @@ -28,7 +21,6 @@ func TestAccCustomUrlCategory(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccCustomUrlCategoryDestroy(prefix), Steps: []resource.TestStep{ { Config: testAccCustomUrlCategoryResourceTmpl, @@ -101,34 +93,3 @@ resource "panos_custom_url_category" "category" { list = var.list } ` - -func testAccCustomUrlCategoryDestroy(prefix string) func(s *terraform.State) error { - return func(s *terraform.State) error { - api := category.NewService(sdkClient) - ctx := context.TODO() - - location := category.NewSharedLocation() - - entries, err := api.List(ctx, *location, "get", "", "") - if err != nil && !sdkErrors.IsObjectNotFound(err) { - return fmt.Errorf("listing interface management entries via sdk: %v", err) - } - - var leftEntries []string - for _, elt := range entries { - if strings.HasPrefix(elt.Name, prefix) { - leftEntries = append(leftEntries, elt.Name) - } - } - - if len(leftEntries) > 0 { - err := fmt.Errorf("terraform failed to remove entries from the server") - delErr := api.Delete(ctx, *location, leftEntries...) - if delErr != nil { - return errors.Join(err, delErr) - } - } - - return nil - } -} diff --git a/assets/terraform/test/resource_decryption_policy_test.go b/assets/terraform/test/resource_decryption_policy_test.go index 14c37430..91ef5de5 100644 --- a/assets/terraform/test/resource_decryption_policy_test.go +++ b/assets/terraform/test/resource_decryption_policy_test.go @@ -2,12 +2,10 @@ package provider_test import ( "context" - "errors" "fmt" "strings" "testing" - sdkerrors "github.com/PaloAltoNetworks/pango/errors" "github.com/PaloAltoNetworks/pango/policies/rules/decryption" "github.com/hashicorp/terraform-plugin-testing/config" @@ -16,7 +14,6 @@ import ( "github.com/hashicorp/terraform-plugin-testing/knownvalue" "github.com/hashicorp/terraform-plugin-testing/plancheck" "github.com/hashicorp/terraform-plugin-testing/statecheck" - "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" ) @@ -226,7 +223,6 @@ func TestAccDecryptionPolicyExtended(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: decryptionPolicyCheckDestroy(prefix), Steps: []resource.TestStep{ { Config: decryptionPolicyExtendedResource1Tmpl, @@ -336,7 +332,6 @@ func TestAccPanosDecryptionPolicyOrdering(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: decryptionPolicyCheckDestroy(prefix), Steps: []resource.TestStep{ { Config: decryptionPolicyOrderTmpl, @@ -422,37 +417,3 @@ resource "panos_decryption_policy" "rules" { ] } ` - -func decryptionPolicyCheckDestroy(prefix string) func(s *terraform.State) error { - return func(s *terraform.State) error { - service := decryption.NewService(sdkClient) - ctx := context.TODO() - - location := decryption.NewDeviceGroupLocation() - location.DeviceGroup.DeviceGroup = fmt.Sprintf("%s-dg", prefix) - - rules, err := service.List(ctx, *location, "get", "", "") - if err != nil && !sdkerrors.IsObjectNotFound(err) { - return err - } - - var danglingNames []string - for _, elt := range rules { - if strings.HasPrefix(elt.Name, prefix) { - danglingNames = append(danglingNames, elt.Name) - } - } - - if len(danglingNames) > 0 { - err := DanglingObjectsError - delErr := service.Delete(ctx, *location, danglingNames...) - if delErr != nil { - err = errors.Join(err, delErr) - } - - return err - } - - return nil - } -} diff --git a/assets/terraform/test/resource_device_group_test.go b/assets/terraform/test/resource_device_group_test.go index e4817b9a..aaead490 100644 --- a/assets/terraform/test/resource_device_group_test.go +++ b/assets/terraform/test/resource_device_group_test.go @@ -1,21 +1,14 @@ package provider_test import ( - "context" - "errors" "fmt" - "strings" "testing" - sdkErrors "github.com/PaloAltoNetworks/pango/errors" - "github.com/PaloAltoNetworks/pango/panorama/devicegroup" - "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" ) @@ -28,7 +21,6 @@ func TestAccDeviceGroup(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccDeviceGroupDestroy(prefix), Steps: []resource.TestStep{ { Config: testAccDeviceGroupResourceTmpl, @@ -95,35 +87,3 @@ resource "panos_device_group" "dg" { authorization_code = "code" } ` - -func testAccDeviceGroupDestroy(prefix string) func(s *terraform.State) error { - return func(s *terraform.State) error { - api := devicegroup.NewService(sdkClient) - ctx := context.TODO() - - location := devicegroup.NewPanoramaLocation() - - entries, err := api.List(ctx, *location, "get", "", "") - if err != nil && !sdkErrors.IsObjectNotFound(err) { - return fmt.Errorf("listing interface management entries via sdk: %v", err) - } - - var leftEntries []string - for _, elt := range entries { - if strings.HasPrefix(elt.Name, prefix) { - leftEntries = append(leftEntries, elt.Name) - } - } - - if len(leftEntries) > 0 { - err := fmt.Errorf("terraform failed to remove entries from the server") - delErr := api.Delete(ctx, *location, leftEntries...) - if delErr != nil { - return errors.Join(err, delErr) - } - return err - } - - return nil - } -} diff --git a/assets/terraform/test/resource_ethernet_interface_test.go b/assets/terraform/test/resource_ethernet_interface_test.go index d54d4a09..61b4cbc8 100644 --- a/assets/terraform/test/resource_ethernet_interface_test.go +++ b/assets/terraform/test/resource_ethernet_interface_test.go @@ -1,19 +1,14 @@ package provider_test import ( - "context" "fmt" "testing" - sdkErrors "github.com/PaloAltoNetworks/pango/errors" - "github.com/PaloAltoNetworks/pango/network/interface/ethernet" - "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" ) @@ -28,10 +23,6 @@ func TestAccPanosEthernetInterface_Layer3(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccCheckPanosEthernetInterfaceDestroy( - interfaceName, - fmt.Sprintf("%s-%s", templateName, nameSuffix), - ), Steps: []resource.TestStep{ { Config: makePanosEthernetInterface_Layer3(resName), @@ -118,26 +109,3 @@ func makePanosEthernetInterface_Layer3(label string) string { return fmt.Sprintf(configTpl, label) } - -func testAccCheckPanosEthernetInterfaceDestroy(entryName, templateName string) func(s *terraform.State) error { - return func(s *terraform.State) error { - api := ethernet.NewService(sdkClient) - ctx := context.TODO() - - location := ethernet.NewTemplateLocation() - location.Template.Template = templateName - - reply, err := api.Read(ctx, *location, entryName, "show") - if err != nil && !sdkErrors.IsObjectNotFound(err) { - return fmt.Errorf("reading ethernet entry via sdk: %v", err) - } - - if reply != nil { - if reply.EntryName() == entryName { - return fmt.Errorf("ethernet object still exists: %s", entryName) - } - } - - return nil - } -} diff --git a/assets/terraform/test/resource_ethernet_layer3_subinterface_test.go b/assets/terraform/test/resource_ethernet_layer3_subinterface_test.go new file mode 100644 index 00000000..07b3b329 --- /dev/null +++ b/assets/terraform/test/resource_ethernet_layer3_subinterface_test.go @@ -0,0 +1,1297 @@ +package provider_test + +import ( + "fmt" + "testing" + + "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/plancheck" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +) + +func TestAccEthernetLayer3Subinterface_Basic(t *testing.T) { + t.Parallel() + + nameSuffix := acctest.RandStringFromCharSet(6, acctest.CharSetAlphaNum) + prefix := fmt.Sprintf("test-acc-%s", nameSuffix) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProviders, + Steps: []resource.TestStep{ + { + Config: ethernetLayer3Subinterface_1, + ConfigVariables: map[string]config.Variable{ + "prefix": config.StringVariable(prefix), + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("name"), + knownvalue.StringExact("ethernet1/1.1"), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("tag"), + knownvalue.Int64Exact(1), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("parent"), + knownvalue.StringExact("ethernet1/1"), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("comment"), + knownvalue.StringExact("Test subinterface with all top-level parameters"), + ), + // statecheck.ExpectKnownValue( + // "panos_ethernet_layer3_subinterface.subinterface", + // tfjsonpath.New("netflow_profile"), + // knownvalue.StringExact("NetflowProfile1"), + // ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("mtu"), + knownvalue.Int64Exact(1500), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("adjust_tcp_mss"), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "enable": knownvalue.Bool(true), + "ipv4_mss_adjustment": knownvalue.Int64Exact(100), + "ipv6_mss_adjustment": knownvalue.Int64Exact(150), + }), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("arp"), + knownvalue.ListExact([]knownvalue.Check{ + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "name": knownvalue.StringExact("192.168.0.1"), + "hw_address": knownvalue.StringExact("00:1a:2b:3c:4d:5e"), + }), + }), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("bonjour"), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "enable": knownvalue.Bool(true), + "group_id": knownvalue.Int64Exact(5), + "ttl_check": knownvalue.Bool(true), + }), + ), + // statecheck.ExpectKnownValue( + // "panos_ethernet_layer3_subinterface.subinterface", + // tfjsonpath.New("ddns_config"), + // knownvalue.ObjectExact(map[string]knownvalue.Check{ + // "ddns_cert_profile": knownvalue.StringExact("cert-profile-1"), + // "ddns_enabled": knownvalue.Bool(true), + // "ddns_hostname": knownvalue.StringExact("test-hostname"), + // "ddns_ip": knownvalue.ListExact([]knownvalue.Check{knownvalue.StringExact("10.0.0.1")}), + // "ddns_ipv6": knownvalue.ListExact([]knownvalue.Check{knownvalue.StringExact("2001:db8::1")}), + // "ddns_update_interval": knownvalue.Int64Exact(24), + // "ddns_vendor": knownvalue.StringExact("dyndns"), + // "ddns_vendor_config": knownvalue.ListExact([]knownvalue.Check{ + // knownvalue.ObjectExact(map[string]knownvalue.Check{ + // "name": knownvalue.StringExact("vendor_config_1"), + // "value": knownvalue.StringExact("config-value-1"), + // }), + // }), + // }), + // ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("df_ignore"), + knownvalue.Bool(true), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("interface_management_profile"), + knownvalue.StringExact(prefix), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("ip"), + knownvalue.ListExact([]knownvalue.Check{ + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "name": knownvalue.StringExact("192.168.1.1"), + "sdwan_gateway": knownvalue.StringExact("192.168.1.1"), + }), + }), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("ndp_proxy"), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "enabled": knownvalue.Bool(true), + "address": knownvalue.ListExact([]knownvalue.Check{ + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "name": knownvalue.StringExact("ndp_proxy_1"), + "negate": knownvalue.Bool(false), + }), + }), + }), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("ipv6"), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "enabled": knownvalue.Bool(true), + "address": knownvalue.ListExact([]knownvalue.Check{ + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "name": knownvalue.StringExact("2001:db8:85a3::8a2e:370:7334"), + "enable_on_interface": knownvalue.Bool(true), + "prefix": knownvalue.ObjectExact(map[string]knownvalue.Check{}), + "anycast": knownvalue.ObjectExact(map[string]knownvalue.Check{}), + "advertise": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "enable": knownvalue.Bool(true), + "valid_lifetime": knownvalue.StringExact("2592000"), + "preferred_lifetime": knownvalue.StringExact("604800"), + "onlink_flag": knownvalue.Bool(true), + "auto_config_flag": knownvalue.Bool(true), + }), + }), + }), + "interface_id": knownvalue.StringExact("EUI-64"), + "dhcp_client": knownvalue.Null(), + "inherited": knownvalue.Null(), + "neighbor_discovery": knownvalue.Null(), + }), + ), + // statecheck.ExpectKnownValue( + // "panos_ethernet_layer3_subinterface.subinterface", + // tfjsonpath.New("sdwan_link_settings"), + // knownvalue.ObjectExact(map[string]knownvalue.Check{ + // "enable": knownvalue.Bool(true), + // "sdwan_interface_profile": knownvalue.StringExact("SdwanProfile1"), + // "upstream_nat": knownvalue.ObjectExact(map[string]knownvalue.Check{ + // "enable": knownvalue.Bool(true), + // "ddns": knownvalue.ObjectExact(map[string]knownvalue.Check{}), + // }), + // }), + // ), + }, + }, + { + Config: ethernetLayer3Subinterface_1, + ConfigVariables: map[string]config.Variable{ + "prefix": config.StringVariable(prefix), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + }, + }, + }) +} + +func TestAccEthernetLayer3Subinterface_DHCP_Client(t *testing.T) { + t.Parallel() + + nameSuffix := acctest.RandStringFromCharSet(6, acctest.CharSetAlphaNum) + prefix := fmt.Sprintf("test-acc-%s", nameSuffix) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProviders, + Steps: []resource.TestStep{ + { + Config: ethernetLayer3Subinterface_DHCP_Client, + ConfigVariables: map[string]config.Variable{ + "prefix": config.StringVariable(prefix), + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("name"), + knownvalue.StringExact("ethernet1/1.1"), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("tag"), + knownvalue.Int64Exact(1), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("parent"), + knownvalue.StringExact("ethernet1/1"), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("comment"), + knownvalue.StringExact("Test subinterface with DHCP client configuration"), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("dhcp_client"), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "create_default_route": knownvalue.Bool(true), + "default_route_metric": knownvalue.Int64Exact(10), + "enable": knownvalue.Bool(true), + "send_hostname": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "enable": knownvalue.Bool(true), + "hostname": knownvalue.StringExact("dhcp-client-hostname"), + }), + }), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("ipv6").AtMapKey("enabled"), + knownvalue.Bool(false), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("sdwan_link_settings").AtMapKey("enable"), + knownvalue.Bool(false), + ), + }, + }, + { + Config: ethernetLayer3Subinterface_DHCP_Client, + ConfigVariables: map[string]config.Variable{ + "prefix": config.StringVariable(prefix), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + }, + }, + }) +} + +func TestAccEthernetLayer3Subinterface_PPPoE(t *testing.T) { + t.Parallel() + + nameSuffix := acctest.RandStringFromCharSet(6, acctest.CharSetAlphaNum) + prefix := fmt.Sprintf("test-acc-%s", nameSuffix) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProviders, + Steps: []resource.TestStep{ + { + Config: ethernetLayer3Subinterface_PPPoE, + ConfigVariables: map[string]config.Variable{ + "prefix": config.StringVariable(prefix), + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("pppoe"), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "access_concentrator": knownvalue.StringExact("ac-1"), + "authentication": knownvalue.StringExact("auto"), + "create_default_route": knownvalue.Bool(true), + "default_route_metric": knownvalue.Int64Exact(10), + "enable": knownvalue.Bool(true), + "passive": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "enable": knownvalue.Bool(true), + }), + "password": knownvalue.StringExact("pppoe-password"), + "service": knownvalue.StringExact("pppoe-service"), + "static_address": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "ip": knownvalue.StringExact("192.168.2.1"), + }), + "username": knownvalue.StringExact("pppoe-user"), + }), + ), + // Additional checks for other relevant attributes + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("ipv6").AtMapKey("enabled"), + knownvalue.Bool(false), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("sdwan_link_settings").AtMapKey("enable"), + knownvalue.Bool(false), + ), + }, + }, + { + Config: ethernetLayer3Subinterface_PPPoE, + ConfigVariables: map[string]config.Variable{ + "prefix": config.StringVariable(prefix), + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectEmptyPlan(), + }, + }, + }, + }, + }) +} + +func TestAccEthernetLayer3Subinterface_IPv6_DHCP_Client(t *testing.T) { + t.Parallel() + + nameSuffix := acctest.RandStringFromCharSet(6, acctest.CharSetAlphaNum) + prefix := fmt.Sprintf("test-acc-%s", nameSuffix) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProviders, + Steps: []resource.TestStep{ + { + Config: ethernetLayer3Subinterface_IPv6_DHCP_Client, + ConfigVariables: map[string]config.Variable{ + "prefix": config.StringVariable(prefix), + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("name"), + knownvalue.StringExact("ethernet1/1.1"), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("tag"), + knownvalue.Int64Exact(1), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("comment"), + knownvalue.StringExact("Test subinterface with IPv6 DHCP client configuration"), + ), + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("ipv6"), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "enabled": knownvalue.Bool(true), + "dhcp_client": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "accept_ra_route": knownvalue.Bool(true), + "default_route_metric": knownvalue.Int64Exact(10), + "enable": knownvalue.Bool(true), + "neighbor_discovery": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "dad_attempts": knownvalue.Int64Exact(1), + "enable_dad": knownvalue.Bool(true), + "enable_ndp_monitor": knownvalue.Bool(true), + "ns_interval": knownvalue.Int64Exact(1000), + "reachable_time": knownvalue.Int64Exact(30000), + "dns_server": knownvalue.Null(), + "dns_suffix": knownvalue.Null(), + "neighbor": knownvalue.Null(), + }), + "preference": knownvalue.StringExact("high"), + "prefix_delegation": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "enable": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "yes": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "pfx_pool_name": knownvalue.StringExact("prefix-pool-1"), + "prefix_len": knownvalue.Int64Exact(64), + "prefix_len_hint": knownvalue.Bool(true), + }), + "no": knownvalue.Null(), + }), + }), + "v6_options": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "duid_type": knownvalue.StringExact("duid-type-llt"), + "enable": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "yes": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "non_temp_addr": knownvalue.Bool(true), + "temp_addr": knownvalue.Bool(false), + }), + "no": knownvalue.Null(), + }), + "rapid_commit": knownvalue.Bool(true), + "support_srvr_reconfig": knownvalue.Bool(true), + }), + }), + "address": knownvalue.Null(), + "inherited": knownvalue.Null(), + "interface_id": knownvalue.StringExact("EUI-64"), + "neighbor_discovery": knownvalue.Null(), + }), + ), + }, + }, + }, + }) +} + +func TestAccEthernetLayer3Subinterface_IPv6_Neighbor_Discovery(t *testing.T) { + t.Parallel() + + nameSuffix := acctest.RandStringFromCharSet(6, acctest.CharSetAlphaNum) + prefix := fmt.Sprintf("test-acc-%s", nameSuffix) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProviders, + Steps: []resource.TestStep{ + { + Config: ethernetLayer3Subinterface_IPv6_Neighbor_Discovery, + ConfigVariables: map[string]config.Variable{ + "prefix": config.StringVariable(prefix), + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("ipv6"), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "enabled": knownvalue.Bool(true), + "neighbor_discovery": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "dad_attempts": knownvalue.Int64Exact(1), + "enable_dad": knownvalue.Bool(true), + "enable_ndp_monitor": knownvalue.Bool(true), + "ns_interval": knownvalue.Int64Exact(1000), + "reachable_time": knownvalue.Int64Exact(30000), + "router_advertisement": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "dns_support": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "enable": knownvalue.Bool(true), + "server": knownvalue.ListExact([]knownvalue.Check{ + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "name": knownvalue.StringExact("2001:DB8::1/128"), + "lifetime": knownvalue.Int64Exact(1200), + }), + }), + "suffix": knownvalue.ListExact([]knownvalue.Check{ + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "name": knownvalue.StringExact("suffix1"), + "lifetime": knownvalue.Int64Exact(1200), + }), + }), + }), + "enable": knownvalue.Bool(true), + "enable_consistency_check": knownvalue.Bool(true), + "hop_limit": knownvalue.StringExact("64"), + "lifetime": knownvalue.Int64Exact(1800), + "link_mtu": knownvalue.StringExact("1500"), + "managed_flag": knownvalue.Bool(false), + "max_interval": knownvalue.Int64Exact(600), + "min_interval": knownvalue.Int64Exact(200), + "other_flag": knownvalue.Bool(false), + "reachable_time": knownvalue.StringExact("0"), + "retransmission_timer": knownvalue.StringExact("0"), + "router_preference": knownvalue.StringExact("Medium"), + }), + "neighbor": knownvalue.ListExact([]knownvalue.Check{ + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "name": knownvalue.StringExact("2001:DB8::1/128"), + "hw_address": knownvalue.StringExact("00:1a:2b:3c:4d:5e"), + }), + }), + }), + "interface_id": knownvalue.StringExact("EUI-64"), + "address": knownvalue.Null(), + "dhcp_client": knownvalue.Null(), + "inherited": knownvalue.Null(), + }), + ), + }, + }, + }, + }) +} + +func TestAccEthernetLayer3Subinterface_IPv6_GUA(t *testing.T) { + t.Parallel() + + nameSuffix := acctest.RandStringFromCharSet(6, acctest.CharSetAlphaNum) + prefix := fmt.Sprintf("test-acc-%s", nameSuffix) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProviders, + Steps: []resource.TestStep{ + { + Config: ethernetLayer3Subinterface_IPv6_GUA, + ConfigVariables: map[string]config.Variable{ + "prefix": config.StringVariable(prefix), + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("ipv6").AtMapKey("inherited").AtMapKey("assign_addr").AtSliceIndex(0), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "name": knownvalue.StringExact("gua_config"), + "type": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "gua": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "enable_on_interface": knownvalue.Bool(true), + "advertise": knownvalue.Null(), + "prefix_pool": knownvalue.Null(), + "pool_type": knownvalue.Null(), + }), + "ula": knownvalue.Null(), + }), + }), + ), + }, + }, + }, + }) +} + +func TestAccEthernetLayer3Subinterface_IPv6_ULA(t *testing.T) { + t.Parallel() + + nameSuffix := acctest.RandStringFromCharSet(6, acctest.CharSetAlphaNum) + prefix := fmt.Sprintf("test-acc-%s", nameSuffix) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProviders, + Steps: []resource.TestStep{ + { + Config: ethernetLayer3Subinterface_IPv6_ULA, + ConfigVariables: map[string]config.Variable{ + "prefix": config.StringVariable(prefix), + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("ipv6").AtMapKey("inherited").AtMapKey("assign_addr").AtSliceIndex(0), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "name": knownvalue.StringExact("ula_config"), + "type": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "ula": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "enable_on_interface": knownvalue.Bool(true), + "address": knownvalue.StringExact("fd00:1234:5678::/48"), + "advertise": knownvalue.Null(), + "anycast": knownvalue.Null(), + "prefix": knownvalue.Null(), + }), + "gua": knownvalue.Null(), + }), + }), + ), + }, + }, + }, + }) +} + +func TestAccEthernetLayer3Subinterface_SDWAN_DDNS(t *testing.T) { + t.Parallel() + t.Skip("Missing resource implementation") + + nameSuffix := acctest.RandStringFromCharSet(6, acctest.CharSetAlphaNum) + prefix := fmt.Sprintf("test-acc-%s", nameSuffix) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProviders, + Steps: []resource.TestStep{ + { + Config: ethernetLayer3Subinterface_SDWAN_DDNS, + ConfigVariables: map[string]config.Variable{ + "prefix": config.StringVariable(prefix), + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("sdwan_link_settings"), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "enable": knownvalue.Bool(true), + "upstream_nat": knownvalue.ObjectExact(map[string]knownvalue.Check{ + "enable": knownvalue.Bool(true), + "ddns": knownvalue.ObjectExact(map[string]knownvalue.Check{}), + "static_ip": knownvalue.Null(), + }), + }), + ), + }, + }, + }, + }) +} + +func TestAccEthernetLayer3Subinterface_SDWAN_StaticIP_FQDN(t *testing.T) { + t.Parallel() + t.Skip("Missing resource implementation") + + nameSuffix := acctest.RandStringFromCharSet(6, acctest.CharSetAlphaNum) + prefix := fmt.Sprintf("test-acc-%s", nameSuffix) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProviders, + Steps: []resource.TestStep{ + { + Config: ethernetLayer3Subinterface_SDWAN_StaticIP_FQDN, + ConfigVariables: map[string]config.Variable{ + "prefix": config.StringVariable(prefix), + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("sdwan_link_settings").AtMapKey("upstream_nat").AtMapKey("static_ip"), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "fqdn": knownvalue.StringExact("example.com"), + "ip_address": knownvalue.Null(), + }), + ), + }, + }, + }, + }) +} + +func TestAccEthernetLayer3Subinterface_SDWAN_StaticIP_IPAddress(t *testing.T) { + t.Parallel() + t.Skip("Missing resource implementation") + + nameSuffix := acctest.RandStringFromCharSet(6, acctest.CharSetAlphaNum) + prefix := fmt.Sprintf("test-acc-%s", nameSuffix) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProviders, + Steps: []resource.TestStep{ + { + Config: ethernetLayer3Subinterface_SDWAN_StaticIP_IPAddress, + ConfigVariables: map[string]config.Variable{ + "prefix": config.StringVariable(prefix), + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "panos_ethernet_layer3_subinterface.subinterface", + tfjsonpath.New("sdwan_link_settings").AtMapKey("upstream_nat").AtMapKey("static_ip"), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "ip_address": knownvalue.StringExact("203.0.113.1"), + "fqdn": knownvalue.Null(), + }), + ), + }, + }, + }, + }) +} + +const ethernetLayer3Subinterface_1 = ` +variable "prefix" { type = string } + +resource "panos_template" "example" { + location = { panorama = {} } + name = "${var.prefix}-tmpl" +} + +resource "panos_ethernet_interface" "parent" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + name = "ethernet1/1" + layer3 = {} +} + +resource "panos_interface_management_profile" "profile" { + location = { template = { name = panos_template.example.name } } + + name = var.prefix +} + +resource "panos_ethernet_layer3_subinterface" "subinterface" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.1" + tag = 1 + comment = "Test subinterface with all top-level parameters" + #netflow_profile = "NetflowProfile1" + mtu = 1500 + + adjust_tcp_mss = { + enable = true + ipv4_mss_adjustment = 100 + ipv6_mss_adjustment = 150 + } + + arp = [ + { + name = "192.168.0.1" + hw_address = "00:1a:2b:3c:4d:5e" + } + ] + + bonjour = { + enable = true + group_id = 5 + ttl_check = true + } + + #ddns_config = { + # ddns_cert_profile = "cert-profile-1" + # ddns_enabled = true + # ddns_hostname = "test-hostname" + # ddns_ip = ["10.0.0.1"] + # ddns_ipv6 = ["2001:db8::1"] + # ddns_update_interval = 24 + # ddns_vendor = "dyndns" + # ddns_vendor_config = [ + # { + # name = "vendor_config_1" + # value = "config-value-1" + # } + # ] + #} + + #decrypt_forward = true + + df_ignore = true + + interface_management_profile = panos_interface_management_profile.profile.name + + ip = [ + { + name = "192.168.1.1" + sdwan_gateway = "192.168.1.1" + } + ] + + ndp_proxy = { + enabled = true + address = [ + { + name = "ndp_proxy_1" + negate = false + } + ] + } + + ipv6 = { + enabled = true + address = [ + { + name = "2001:db8:85a3::8a2e:370:7334" + enable_on_interface = true + prefix = {} + anycast = {} + advertise = { + enable = true + valid_lifetime = "2592000" + preferred_lifetime = "604800" + onlink_flag = true + auto_config_flag = true + } + } + ] + + interface_id = "EUI-64" + } + + #sdwan_link_settings = { + # enable = true + # sdwan_interface_profile = "SdwanProfile1" + # upstream_nat = { + # enable = true + # ddns = {} + # } + #} +} +` + +const ethernetLayer3Subinterface_PPPoE = ` +variable "prefix" { type = string } + +resource "panos_template" "example" { + location = { panorama = {} } + name = "${var.prefix}-tmpl" +} + +resource "panos_ethernet_interface" "parent" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + name = "ethernet1/1" + layer3 = {} +} + +resource "panos_ethernet_layer3_subinterface" "subinterface" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.1" + tag = 1 + comment = "Test subinterface with PPPoE configuration" + mtu = 1500 + + pppoe = { + access_concentrator = "ac-1" + authentication = "auto" + create_default_route = true + default_route_metric = 10 + enable = true + passive = { + enable = true + } + password = "pppoe-password" + service = "pppoe-service" + static_address = { + ip = "192.168.2.1" + } + username = "pppoe-user" + } + + // Disable other configurations to focus on PPPoE + ipv6 = { + enabled = false + } + + sdwan_link_settings = { + enable = false + } +} +` + +const ethernetLayer3Subinterface_DHCP_Client = ` +variable "prefix" { type = string } + +resource "panos_template" "example" { + location = { panorama = {} } + name = "${var.prefix}-tmpl" +} + +resource "panos_ethernet_interface" "parent" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + name = "ethernet1/1" + layer3 = {} +} + +resource "panos_ethernet_layer3_subinterface" "subinterface" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.1" + tag = 1 + comment = "Test subinterface with DHCP client configuration" + + dhcp_client = { + create_default_route = true + default_route_metric = 10 + enable = true + send_hostname = { + enable = true + hostname = "dhcp-client-hostname" + } + } + + // Explicitly disable IPv6 and SDWAN to focus on DHCP client + ipv6 = { + enabled = false + } + + sdwan_link_settings = { + enable = false + } +} +` + +const ethernetLayer3Subinterface_IPv6_GUA = ` +variable "prefix" { type = string } + +resource "panos_template" "example" { + location = { panorama = {} } + name = "${var.prefix}-tmpl" +} + +resource "panos_ethernet_interface" "parent" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + name = "ethernet1/1" + layer3 = {} +} + +resource "panos_ethernet_layer3_subinterface" "subinterface" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.1" + tag = 1 + + ipv6 = { + inherited = { + assign_addr = [{ + name = "gua_config" + type = { + gua = { + enable_on_interface = true + #prefix_pool = "my-gua-pool" + } + } + }] + } + } +} +` + +const ethernetLayer3Subinterface_IPv6_DHCP_Client = ` +variable "prefix" { type = string } + +resource "panos_template" "example" { + location = { panorama = {} } + name = "${var.prefix}-tmpl" +} + +resource "panos_ethernet_interface" "parent" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + name = "ethernet1/1" + layer3 = {} +} + +resource "panos_ethernet_layer3_subinterface" "subinterface" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.1" + tag = 1 + comment = "Test subinterface with IPv6 DHCP client configuration" + + ipv6 = { + enabled = true + dhcp_client = { + accept_ra_route = true + default_route_metric = 10 + enable = true + neighbor_discovery = { + dad_attempts = 1 + enable_dad = true + enable_ndp_monitor = true + ns_interval = 1000 + reachable_time = 30000 + } + preference = "high" + prefix_delegation = { + enable = { + yes = { + pfx_pool_name = "prefix-pool-1" + prefix_len = 64 + prefix_len_hint = true + } + } + } + v6_options = { + duid_type = "duid-type-llt" + enable = { + yes = { + non_temp_addr = true + temp_addr = false + } + } + rapid_commit = true + support_srvr_reconfig = true + } + } + } +} +` + +const ethernetLayer3Subinterface_IPv6_Neighbor_Discovery = ` +variable "prefix" { type = string } + +resource "panos_template" "example" { + location = { panorama = {} } + name = "${var.prefix}-tmpl" +} + +resource "panos_ethernet_interface" "parent" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + name = "ethernet1/1" + layer3 = {} +} + +resource "panos_ethernet_layer3_subinterface" "subinterface" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.1" + tag = 1 + comment = "Test subinterface with IPv6 Neighbor Discovery configuration" + mtu = 1500 + + ipv6 = { + enabled = true + neighbor_discovery = { + dad_attempts = 1 + enable_dad = true + enable_ndp_monitor = true + neighbor = [{ + name = "2001:DB8::1/128" + hw_address = "00:1a:2b:3c:4d:5e" + }] + ns_interval = 1000 + reachable_time = 30000 + router_advertisement = { + dns_support = { + enable = true + server = [ + { + name = "2001:DB8::1/128" + lifetime = 1200 + } + ] + suffix = [ + { + name = "suffix1" + lifetime = 1200 + } + ] + } + enable = true + enable_consistency_check = true + hop_limit = "64" + lifetime = 1800 + link_mtu = "1500" + managed_flag = false + max_interval = 600 + min_interval = 200 + other_flag = false + reachable_time = "0" + retransmission_timer = "0" + router_preference = "Medium" + } + } + } +} +` + +const ethernetLayer3Subinterface_IPv6_ULA = ` +variable "prefix" { type = string } + +resource "panos_template" "example" { + location = { panorama = {} } + name = "${var.prefix}-tmpl" +} + +resource "panos_ethernet_interface" "parent" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + name = "ethernet1/1" + layer3 = {} +} + +resource "panos_ethernet_layer3_subinterface" "subinterface" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.1" + tag = 1 + + ipv6 = { + inherited = { + assign_addr = [ + { + name = "ula_config" + type = { + ula = { + enable_on_interface = true + address = "fd00:1234:5678::/48" + } + } + } + ] + } + } +} +` + +const ethernetLayer3Subinterface_SDWAN_DDNS = ` +variable "prefix" { type = string } + +resource "panos_template" "example" { + location = { panorama = {} } + name = "${var.prefix}-tmpl" +} + +resource "panos_ethernet_interface" "parent" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + name = "ethernet1/1" + layer3 = {} +} + +resource "panos_ethernet_layer3_subinterface" "subinterface" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.1" + tag = 1 + + sdwan_link_settings = { + enable = true + upstream_nat = { + enable = true + ddns = {} + } + } +} +` + +const ethernetLayer3Subinterface_SDWAN_StaticIP_FQDN = ` +variable "prefix" { type = string } + +resource "panos_template" "example" { + location = { panorama = {} } + name = "${var.prefix}-tmpl" +} + +resource "panos_ethernet_interface" "parent" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + name = "ethernet1/1" + layer3 = {} +} + +resource "panos_ethernet_layer3_subinterface" "subinterface" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.1" + tag = 1 + + sdwan_link_settings = { + enable = true + upstream_nat = { + enable = true + static_ip = { + fqdn = "example.com" + } + } + } +} +` + +const ethernetLayer3Subinterface_SDWAN_StaticIP_IPAddress = ` +variable "prefix" { type = string } + +resource "panos_template" "example" { + location = { panorama = {} } + name = "${var.prefix}-tmpl" +} + +resource "panos_ethernet_interface" "parent" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + name = "ethernet1/1" + layer3 = {} +} + +resource "panos_ethernet_layer3_subinterface" "subinterface" { + location = { + template = { + vsys = "vsys1" + name = panos_template.example.name + } + } + + parent = panos_ethernet_interface.parent.name + name = "ethernet1/1.1" + tag = 1 + + sdwan_link_settings = { + enable = true + upstream_nat = { + enable = true + static_ip = { + ip_address = "203.0.113.1" + } + } + } +} +` diff --git a/assets/terraform/test/resource_external_dynamic_list_test.go b/assets/terraform/test/resource_external_dynamic_list_test.go index 6fa52e36..3eb70c22 100644 --- a/assets/terraform/test/resource_external_dynamic_list_test.go +++ b/assets/terraform/test/resource_external_dynamic_list_test.go @@ -1,19 +1,14 @@ package provider_test import ( - "context" "fmt" "testing" - sdkErrors "github.com/PaloAltoNetworks/pango/errors" - "github.com/PaloAltoNetworks/pango/objects/extdynlist" - "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" ) @@ -25,7 +20,6 @@ func TestAccPanosExternalDynamicList_1(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccExternalDynamicListCheckDestroy(prefix), Steps: []resource.TestStep{ { Config: externalDynamicList1Tmpl, @@ -89,7 +83,6 @@ func TestAccPanosExternalDynamicList_2(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccExternalDynamicListCheckDestroy(prefix), Steps: []resource.TestStep{ { Config: externalDynamicList2Tmpl, @@ -150,7 +143,6 @@ func TestAccPanosExternalDynamicList_3(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccExternalDynamicListCheckDestroy(prefix), Steps: []resource.TestStep{ { Config: externalDynamicList3Tmpl, @@ -211,7 +203,6 @@ func TestAccPanosExternalDynamicList_4(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccExternalDynamicListCheckDestroy(prefix), Steps: []resource.TestStep{ { Config: externalDynamicList4Tmpl, @@ -275,7 +266,6 @@ func TestAccPanosExternalDynamicList_5(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccExternalDynamicListCheckDestroy(prefix), Steps: []resource.TestStep{ { Config: externalDynamicList5Tmpl, @@ -324,7 +314,6 @@ func TestAccPanosExternalDynamicList_6(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccExternalDynamicListCheckDestroy(prefix), Steps: []resource.TestStep{ { Config: externalDynamicList6Tmpl, @@ -373,7 +362,6 @@ func TestAccPanosExternalDynamicList_7(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccExternalDynamicListCheckDestroy(prefix), Steps: []resource.TestStep{ { Config: externalDynamicList7Tmpl, @@ -653,29 +641,3 @@ resource "panos_external_dynamic_list" "list" { } } ` - -func testAccExternalDynamicListCheckDestroy(prefix string) func(s *terraform.State) error { - return func(s *terraform.State) error { - api := extdynlist.NewService(sdkClient) - location := extdynlist.NewDeviceGroupLocation() - - location.DeviceGroup.DeviceGroup = fmt.Sprintf("%s-dg", prefix) - - ctx := context.TODO() - - entry := fmt.Sprintf("%s-list", prefix) - - reply, err := api.Read(ctx, *location, entry, "show") - if err != nil && !sdkErrors.IsObjectNotFound(err) { - return fmt.Errorf("reading external dynamic list entry %s via sdk: %w", entry, err) - } - - if reply != nil { - if reply.EntryName() == entry { - return fmt.Errorf("external dynamic list object still exists: %s", entry) - } - } - - return nil - } -} diff --git a/assets/terraform/test/resource_ike_crypto_profile_test.go b/assets/terraform/test/resource_ike_crypto_profile_test.go index adf4c9c7..9ea82933 100644 --- a/assets/terraform/test/resource_ike_crypto_profile_test.go +++ b/assets/terraform/test/resource_ike_crypto_profile_test.go @@ -1,20 +1,14 @@ package provider_test import ( - "context" - "errors" "fmt" "testing" - sdkErrors "github.com/PaloAltoNetworks/pango/errors" - "github.com/PaloAltoNetworks/pango/objects/profiles/ikecrypto" - "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" ) @@ -27,7 +21,6 @@ func TestAccIkeCryptoProfile_1(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccIkeCryptoProfileDestroy(prefix), Steps: []resource.TestStep{ { Config: ikeCryptoProfile1, @@ -172,30 +165,3 @@ resource "panos_ike_crypto_profile" "profile4" { } } ` - -func testAccIkeCryptoProfileDestroy(prefix string) func(s *terraform.State) error { - return func(s *terraform.State) error { - entry := fmt.Sprintf("%s-profile", prefix) - api := ikecrypto.NewService(sdkClient) - ctx := context.TODO() - - location := ikecrypto.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 - } -} diff --git a/assets/terraform/test/resource_interface_management_profile_test.go b/assets/terraform/test/resource_interface_management_profile_test.go index 4341a251..6b2df5f2 100644 --- a/assets/terraform/test/resource_interface_management_profile_test.go +++ b/assets/terraform/test/resource_interface_management_profile_test.go @@ -1,21 +1,14 @@ package provider_test import ( - "context" - "errors" "fmt" - "strings" "testing" - sdkErrors "github.com/PaloAltoNetworks/pango/errors" - "github.com/PaloAltoNetworks/pango/network/profiles/interface_management" - "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" ) @@ -35,7 +28,6 @@ func TestAccInterfaceManagementProfile(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccInterfaceManagementProfileDestroy(prefix, templateName), Steps: []resource.TestStep{ { Config: interfaceManagementProfileResourceTmpl, @@ -166,39 +158,3 @@ resource "panos_interface_management_profile" "profile" { permitted_ips = var.permitted_ips } ` - -func testAccInterfaceManagementProfileDestroy(prefix string, template string) func(s *terraform.State) error { - return func(s *terraform.State) error { - api := interface_management.NewService(sdkClient) - ctx := context.TODO() - - location := interface_management.NewTemplateLocation() - location.Template = &interface_management.TemplateLocation{ - Template: template, - NgfwDevice: "localhost.localdomain", - PanoramaDevice: "localhost.localdomain", - } - - entries, err := api.List(ctx, *location, "get", "", "") - if err != nil && !sdkErrors.IsObjectNotFound(err) { - return fmt.Errorf("listing interface management entries via sdk: %v", err) - } - - var leftEntries []string - for _, elt := range entries { - if strings.HasPrefix(elt.Name, prefix) { - leftEntries = append(leftEntries, elt.Name) - } - } - - if len(leftEntries) > 0 { - err := fmt.Errorf("terraform failed to remove entries from the server") - delErr := api.Delete(ctx, *location, leftEntries...) - if delErr != nil { - return errors.Join(err, delErr) - } - } - - return nil - } -} diff --git a/assets/terraform/test/resource_ipsec_crypto_profile_test.go b/assets/terraform/test/resource_ipsec_crypto_profile_test.go index 07611910..5f381da5 100644 --- a/assets/terraform/test/resource_ipsec_crypto_profile_test.go +++ b/assets/terraform/test/resource_ipsec_crypto_profile_test.go @@ -1,20 +1,14 @@ package provider_test import ( - "context" - "errors" "fmt" "testing" - sdkErrors "github.com/PaloAltoNetworks/pango/errors" - "github.com/PaloAltoNetworks/pango/objects/profiles/ipseccrypto" - "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" ) @@ -27,7 +21,6 @@ func TestAccIpsecCryptoProfile_1(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccIpsecCryptoProfileDestroy(prefix), Steps: []resource.TestStep{ { Config: ipsecCryptoProfile1, @@ -227,30 +220,3 @@ resource "panos_ipsec_crypto_profile" "profile4" { } } ` - -func testAccIpsecCryptoProfileDestroy(prefix string) func(s *terraform.State) error { - return func(s *terraform.State) error { - entry := fmt.Sprintf("%s-profile", prefix) - api := ipseccrypto.NewService(sdkClient) - ctx := context.TODO() - - location := ipseccrypto.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 - } -} diff --git a/assets/terraform/test/resource_log_forwarding_test.go b/assets/terraform/test/resource_log_forwarding_test.go index c95b22ae..ced4065d 100644 --- a/assets/terraform/test/resource_log_forwarding_test.go +++ b/assets/terraform/test/resource_log_forwarding_test.go @@ -1,21 +1,14 @@ package provider_test import ( - "context" - "errors" "fmt" - "strings" "testing" - sdkErrors "github.com/PaloAltoNetworks/pango/errors" - "github.com/PaloAltoNetworks/pango/objects/profiles/logforwarding" - "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" ) @@ -27,7 +20,6 @@ func TestAccLogForwarding(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccLogForwardingDestroy(prefix), Steps: []resource.TestStep{ { Config: logForwardingResource1, @@ -336,39 +328,3 @@ resource "panos_log_forwarding_profile" "profile" { }] } ` - -func testAccLogForwardingDestroy(prefix string) func(s *terraform.State) error { - return func(s *terraform.State) error { - api := logforwarding.NewService(sdkClient) - ctx := context.TODO() - - location := logforwarding.NewDeviceGroupLocation() - location.DeviceGroup = &logforwarding.DeviceGroupLocation{ - DeviceGroup: fmt.Sprintf("%s-dg", prefix), - PanoramaDevice: "localhost.localdomain", - } - - entries, err := api.List(ctx, *location, "get", "", "") - if err != nil && !sdkErrors.IsObjectNotFound(err) { - return fmt.Errorf("error while listing entries via sdk: %v", err) - } - - var leftEntries []string - for _, elt := range entries { - if strings.HasPrefix(elt.Name, prefix) { - leftEntries = append(leftEntries, elt.Name) - } - } - - if len(leftEntries) > 0 { - err := fmt.Errorf("terraform failed to remove entries from the server") - delErr := api.Delete(ctx, *location, leftEntries...) - if delErr != nil { - return errors.Join(err, delErr) - } - return err - } - - return nil - } -} diff --git a/assets/terraform/test/resource_loopback_interface_test.go b/assets/terraform/test/resource_loopback_interface_test.go index fa5e22ef..5dc73e0f 100644 --- a/assets/terraform/test/resource_loopback_interface_test.go +++ b/assets/terraform/test/resource_loopback_interface_test.go @@ -1,20 +1,14 @@ package provider_test import ( - "context" - "errors" "fmt" "testing" - sdkErrors "github.com/PaloAltoNetworks/pango/errors" - "github.com/PaloAltoNetworks/pango/network/interface/loopback" - "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" ) @@ -28,9 +22,6 @@ func TestAccLoopbackInterface(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccCheckPanosLoopbackInterfaceDestroy( - prefix, interfaceName, - ), Steps: []resource.TestStep{ { Config: loopbackInterfaceResource1, @@ -143,29 +134,3 @@ resource "panos_loopback_interface" "iface" { } } ` - -func testAccCheckPanosLoopbackInterfaceDestroy(prefix string, entry string) func(s *terraform.State) error { - return func(s *terraform.State) error { - api := loopback.NewService(sdkClient) - ctx := context.TODO() - - location := loopback.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 - } -} diff --git a/assets/terraform/test/resource_nat_policy_test.go b/assets/terraform/test/resource_nat_policy_test.go index b9e860b5..43c11add 100644 --- a/assets/terraform/test/resource_nat_policy_test.go +++ b/assets/terraform/test/resource_nat_policy_test.go @@ -9,7 +9,6 @@ import ( "testing" "text/template" - sdkerrors "github.com/PaloAltoNetworks/pango/errors" "github.com/PaloAltoNetworks/pango/policies/rules/nat" "github.com/hashicorp/terraform-plugin-testing/config" @@ -18,7 +17,6 @@ import ( "github.com/hashicorp/terraform-plugin-testing/knownvalue" "github.com/hashicorp/terraform-plugin-testing/plancheck" "github.com/hashicorp/terraform-plugin-testing/statecheck" - "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" ) @@ -353,7 +351,6 @@ func TestAccNatPolicyExtended(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: natPolicyCheckDestroy(prefix), Steps: []resource.TestStep{ { Config: natPolicyExtendedResource1Tmpl, @@ -448,7 +445,6 @@ func TestAccNatPolicyExtended(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: natPolicyCheckDestroy(prefix), Steps: []resource.TestStep{ { Config: natPolicyExtendedResource2Tmpl, @@ -531,7 +527,6 @@ func TestAccNatPolicyExtended(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: natPolicyCheckDestroy(prefix), Steps: []resource.TestStep{ { Config: natPolicyExtendedResource3Tmpl, @@ -578,7 +573,6 @@ func TestAccNatPolicyExtended(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: natPolicyCheckDestroy(prefix), Steps: []resource.TestStep{ { Config: natPolicyExtendedResource4Tmpl, @@ -679,10 +673,8 @@ func TestAccPanosNatPolicyOrdering(t *testing.T) { PreCheck: func() { testAccPreCheck(t) natPolicyPreCheck(prefix, sdkLocation) - }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: natPolicyCheckDestroy(prefix), Steps: []resource.TestStep{ { Config: makeNatPolicyConfig(prefix), @@ -851,84 +843,5 @@ func natPolicyPreCheck(prefix string, location nat.Location) { if err != nil { panic(fmt.Sprintf("natPolicyPreCheck failed: %s", err)) } - } } - -func natPolicyCheckDestroy(prefix string) func(s *terraform.State) error { - return func(s *terraform.State) error { - service := nat.NewService(sdkClient) - ctx := context.TODO() - - location := nat.NewDeviceGroupLocation() - location.DeviceGroup.DeviceGroup = fmt.Sprintf("%s-dg", prefix) - - rules, err := service.List(ctx, *location, "get", "", "") - if err != nil && !sdkerrors.IsObjectNotFound(err) { - return err - } - - var danglingNames []string - for _, elt := range rules { - if strings.HasPrefix(elt.Name, prefix) { - danglingNames = append(danglingNames, elt.Name) - } - } - - if len(danglingNames) > 0 { - err := DanglingObjectsError - delErr := service.Delete(ctx, *location, danglingNames...) - if delErr != nil { - err = errors.Join(err, delErr) - } - - return err - } - - return nil - } -} - -func init() { - resource.AddTestSweepers("pango_nat_policy", &resource.Sweeper{ - Name: "pango_nat_policy", - F: func(typ string) error { - service := nat.NewService(sdkClient) - - var deviceTyp deviceType - switch typ { - case "panorama": - deviceTyp = devicePanorama - case "firewall": - deviceTyp = deviceFirewall - default: - panic("invalid device type") - } - - for _, rulebase := range []string{"pre-rulebase", "post-rulebase"} { - location, _ := natPolicyLocationByDeviceType(deviceTyp, rulebase) - ctx := context.TODO() - objects, err := service.List(ctx, location, "get", "", "") - if err != nil && !sdkerrors.IsObjectNotFound(err) { - return fmt.Errorf("Failed to list NAT rules during sweep: %w", err) - } - - var names []string - for _, elt := range objects { - if strings.HasPrefix(elt.Name, "test-acc") { - names = append(names, elt.Name) - } - } - - if len(names) > 0 { - err = service.Delete(ctx, location, names...) - if err != nil { - return fmt.Errorf("Failed to delete NAT rules during sweep: %w", err) - } - } - } - - return nil - }, - }) -} diff --git a/assets/terraform/test/resource_panorama_template_test.go b/assets/terraform/test/resource_panorama_template_test.go index 635cd3f6..7b4b9eed 100644 --- a/assets/terraform/test/resource_panorama_template_test.go +++ b/assets/terraform/test/resource_panorama_template_test.go @@ -1,19 +1,14 @@ package provider_test import ( - "context" "fmt" "testing" - sdkErrors "github.com/PaloAltoNetworks/pango/errors" - "github.com/PaloAltoNetworks/pango/panorama/template" - "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" ) @@ -27,7 +22,6 @@ func TestAccPanosTemplate_RequiredInputs(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccCheckPanoramaTemplateDestroy(templateName), Steps: []resource.TestStep{ { Config: makePanosTemplateConfig(resourceName), @@ -62,24 +56,3 @@ func makePanosTemplateConfig(label string) string { ` return fmt.Sprintf(configTpl, label) } - -func testAccCheckPanoramaTemplateDestroy(name string) func(s *terraform.State) error { - return func(s *terraform.State) error { - api := template.NewService(sdkClient) - location := template.NewPanoramaLocation() - ctx := context.TODO() - - reply, err := api.Read(ctx, *location, name, "show") - if err != nil && !sdkErrors.IsObjectNotFound(err) { - return fmt.Errorf("reading template entry via sdk: %v", err) - } - - if reply != nil { - if reply.EntryName() == name { - return fmt.Errorf("template object still exists: %s", name) - } - } - - return nil - } -} diff --git a/assets/terraform/test/resource_panorama_template_variable_test.go b/assets/terraform/test/resource_panorama_template_variable_test.go index 9e5dbd4f..67804d5a 100644 --- a/assets/terraform/test/resource_panorama_template_variable_test.go +++ b/assets/terraform/test/resource_panorama_template_variable_test.go @@ -1,19 +1,15 @@ package provider_test import ( - "context" "fmt" "testing" - sdkErrors "github.com/PaloAltoNetworks/pango/errors" - "github.com/PaloAltoNetworks/pango/panorama/template_variable" "github.com/hashicorp/terraform-plugin-testing/compare" "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" ) @@ -77,11 +73,7 @@ func TestAccPanosPanoramaTemplateVariable(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccPanosPanoramaTemplateVariableDestroy( - "$tempvar-"+nameSuffix, - fmt.Sprintf("%s_%s", templateName, nameSuffix), - ), - Steps: templateVariableTypeEntries, + Steps: templateVariableTypeEntries, }) } @@ -119,26 +111,3 @@ func makePanoramaTemplateVariableConfig(label string) string { return fmt.Sprintf(configTpl, label, label, label) } - -func testAccPanosPanoramaTemplateVariableDestroy(entryName, templateName string) func(s *terraform.State) error { - return func(s *terraform.State) error { - api := template_variable.NewService(sdkClient) - ctx := context.TODO() - - location := template_variable.NewTemplateLocation() - location.Template.Template = templateName - - reply, err := api.Read(ctx, *location, entryName, "show") - if err != nil && !sdkErrors.IsObjectNotFound(err) { - return fmt.Errorf("reading template variable entry via sdk: %v", err) - } - - if reply != nil { - if reply.EntryName() == entryName { - return fmt.Errorf("template object still exists: %s", entryName) - } - } - - return nil - } -} diff --git a/assets/terraform/test/resource_security_policy_rules_test.go b/assets/terraform/test/resource_security_policy_rules_test.go index 87baa2e0..9e13b00a 100644 --- a/assets/terraform/test/resource_security_policy_rules_test.go +++ b/assets/terraform/test/resource_security_policy_rules_test.go @@ -4,22 +4,17 @@ import ( "context" "encoding/base64" "encoding/json" - "errors" "fmt" "regexp" "strings" "testing" - sdkerrors "github.com/PaloAltoNetworks/pango/errors" "github.com/PaloAltoNetworks/pango/policies/rules/security" "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/plancheck" - //"github.com/PaloAltoNetworks/terraform-provider-panos/internal/provider" - //"github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-testing/statecheck" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" @@ -90,7 +85,6 @@ func TestAccSecurityPolicyRulesImport(t *testing.T) { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: securityPolicyRulesCheckDestroy(prefix), Steps: []resource.TestStep{ { Config: securityPolicyRulesImportInitial, @@ -334,7 +328,6 @@ func TestAccSecurityPolicyRulesPositioning(t *testing.T) { }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: securityPolicyRulesCheckDestroy(prefix), Steps: []resource.TestStep{ { Config: securityPolicyRulesPositionFirst, @@ -704,7 +697,6 @@ func TestAccSecurityPolicyRulesPositionAsVariable(t *testing.T) { }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: securityPolicyRulesCheckDestroy(prefix), Steps: []resource.TestStep{ { Config: securityPolicyRulesPositionAsVariableTmpl, @@ -815,53 +807,6 @@ func securityPolicyRulesPreCheck(prefix string) { } } -func securityPolicyRulesCheckDestroy(prefix string) func(s *terraform.State) error { - return func(s *terraform.State) error { - - location := security.NewDeviceGroupLocation() - location.DeviceGroup.DeviceGroup = fmt.Sprintf("%s-dg", prefix) - - service := security.NewService(sdkClient) - ctx := context.TODO() - - rules, err := service.List(ctx, *location, "get", "", "") - if err != nil && !sdkerrors.IsObjectNotFound(err) { - return err - } - - var danglingNames []string - - seededRule := func(name string) bool { - seeded := []string{"rule-0", "rule-1", "rule-99"} - for _, elt := range seeded { - if strings.HasSuffix(name, elt) { - return true - } - } - - return false - } - - for _, elt := range rules { - if strings.HasPrefix(elt.Name, prefix) && !seededRule(elt.Name) { - danglingNames = append(danglingNames, elt.Name) - } - } - - if len(danglingNames) > 0 { - err := fmt.Errorf("%w: %s", DanglingObjectsError, strings.Join(danglingNames, ", ")) - delErr := service.Delete(ctx, *location, danglingNames...) - if delErr != nil { - err = errors.Join(err, delErr) - } - - return err - } - - return nil - } -} - func mergeConfigs(configs ...string) string { return strings.Join(configs, "\n") } diff --git a/assets/terraform/test/resource_security_policy_test.go b/assets/terraform/test/resource_security_policy_test.go index ae599a1e..e38a641c 100644 --- a/assets/terraform/test/resource_security_policy_test.go +++ b/assets/terraform/test/resource_security_policy_test.go @@ -2,13 +2,11 @@ package provider_test import ( "context" - "errors" "fmt" "regexp" "strings" "testing" - sdkerrors "github.com/PaloAltoNetworks/pango/errors" "github.com/PaloAltoNetworks/pango/policies/rules/security" "github.com/hashicorp/terraform-plugin-testing/config" @@ -17,7 +15,6 @@ import ( "github.com/hashicorp/terraform-plugin-testing/knownvalue" "github.com/hashicorp/terraform-plugin-testing/plancheck" "github.com/hashicorp/terraform-plugin-testing/statecheck" - "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" ) @@ -555,7 +552,6 @@ func TestAccSecurityPolicyOrdering(t *testing.T) { }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: securityPolicyCheckDestroy(prefix), Steps: []resource.TestStep{ { Config: securityPolicyOrderingTmpl, @@ -683,37 +679,3 @@ func securityPolicyPreCheck(prefix string) { } } - -func securityPolicyCheckDestroy(prefix string) func(s *terraform.State) error { - return func(s *terraform.State) error { - service := security.NewService(sdkClient) - ctx := context.TODO() - - location := security.NewDeviceGroupLocation() - location.DeviceGroup.DeviceGroup = fmt.Sprintf("%s-dg", prefix) - - rules, err := service.List(ctx, *location, "get", "", "") - if err != nil && !sdkerrors.IsObjectNotFound(err) { - return err - } - - var danglingNames []string - for _, elt := range rules { - if strings.HasPrefix(elt.Name, prefix) { - danglingNames = append(danglingNames, elt.Name) - } - } - - if len(danglingNames) > 0 { - err := DanglingObjectsError - delErr := service.Delete(ctx, *location, danglingNames...) - if delErr != nil { - err = errors.Join(err, delErr) - } - - return err - } - - return nil - } -} diff --git a/assets/terraform/test/resource_security_profile_test.go b/assets/terraform/test/resource_security_profile_test.go index 3309412e..aeb816be 100644 --- a/assets/terraform/test/resource_security_profile_test.go +++ b/assets/terraform/test/resource_security_profile_test.go @@ -2,7 +2,6 @@ package provider_test import ( "context" - "errors" "fmt" "strings" "testing" @@ -15,7 +14,6 @@ import ( "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" ) @@ -28,7 +26,6 @@ func TestAccSecurityProfileGroup(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccSecurityProfileGroupDestroy(prefix), Steps: []resource.TestStep{ { Config: securityProfileGroupTmpl, @@ -178,35 +175,3 @@ func (o *securityProfileGroupExpectNoEntriesInLocation) CheckState(ctx context.C resp.Error = fmt.Errorf("delete of the resource didn't remove it from the server") } } - -func testAccSecurityProfileGroupDestroy(prefix string) func(s *terraform.State) error { - return func(s *terraform.State) error { - service := secgroup.NewService(sdkClient) - - location := secgroup.NewDeviceGroupLocation() - location.DeviceGroup.DeviceGroup = fmt.Sprintf("%s-dg1", prefix) - - ctx := context.TODO() - entries, err := service.List(ctx, *location, "get", "", "") - if err != nil && !sdkerrors.IsObjectNotFound(err) { - return fmt.Errorf("failed to list existing entries via sdk: %w", err) - } - - var leftEntries []string - for _, elt := range entries { - if strings.HasPrefix(elt.Name, prefix) { - leftEntries = append(leftEntries, elt.Name) - } - } - - if len(leftEntries) > 0 { - err := fmt.Errorf("terraform failed to remove entries from the server") - delErr := service.Delete(ctx, *location, leftEntries...) - if delErr != nil { - return errors.Join(err, delErr) - } - } - - return nil - } -} diff --git a/assets/terraform/test/resource_service_group_test.go b/assets/terraform/test/resource_service_group_test.go index 9280f078..3d6227dd 100644 --- a/assets/terraform/test/resource_service_group_test.go +++ b/assets/terraform/test/resource_service_group_test.go @@ -1,21 +1,14 @@ package provider_test import ( - "context" - "errors" "fmt" - "strings" "testing" - sdkerrors "github.com/PaloAltoNetworks/pango/errors" - servicegroup "github.com/PaloAltoNetworks/pango/objects/service/group" - "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" ) @@ -28,7 +21,6 @@ func TestAccPanosServiceGroup(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccCheckPanosServiceGroupDestroy(prefix), Steps: []resource.TestStep{ { Config: testAccPanosServiceGroupTmpl, @@ -111,34 +103,3 @@ resource "panos_service_group" "group2" { members = var.groups["group2"].members } ` - -func testAccCheckPanosServiceGroupDestroy(prefix string) func(s *terraform.State) error { - return func(s *terraform.State) error { - svc := servicegroup.NewService(sdkClient) - ctx := context.TODO() - location := servicegroup.NewSharedLocation() - - entries, err := svc.List(ctx, *location, "get", "", "") - if err != nil && !sdkerrors.IsObjectNotFound(err) { - return fmt.Errorf("Failed to list service group entries: %w", err) - } - - err = nil - var dangling []string - for _, elt := range entries { - if strings.HasPrefix(elt.Name, prefix) { - dangling = append(dangling, elt.Name) - err = errors.Join(err, fmt.Errorf("service group entry not deleted properly: %s", elt.Name)) - } - } - - if len(dangling) > 0 { - deleteErr := svc.Delete(ctx, *location, dangling...) - if deleteErr != nil && !sdkerrors.IsObjectNotFound(deleteErr) { - err = errors.Join(err, fmt.Errorf("failed to delete service group entries: %w", deleteErr)) - } - } - - return err - } -} diff --git a/assets/terraform/test/resource_service_test.go b/assets/terraform/test/resource_service_test.go index 6f61afae..370002d6 100644 --- a/assets/terraform/test/resource_service_test.go +++ b/assets/terraform/test/resource_service_test.go @@ -1,21 +1,14 @@ package provider_test import ( - "context" - "errors" "fmt" - "strings" "testing" - sdkErrors "github.com/PaloAltoNetworks/pango/errors" - "github.com/PaloAltoNetworks/pango/objects/service" - "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" ) @@ -28,7 +21,6 @@ func TestAccService(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccServiceDestroy(prefix), Steps: []resource.TestStep{ { Config: testAccServiceResourceTmpl, @@ -276,34 +268,3 @@ resource "panos_service" "svc4" { protocol = var.services["svc4"].protocol } ` - -func testAccServiceDestroy(prefix string) func(s *terraform.State) error { - return func(s *terraform.State) error { - api := service.NewService(sdkClient) - ctx := context.TODO() - - location := service.NewSharedLocation() - - entries, err := api.List(ctx, *location, "get", "", "") - if err != nil && !sdkErrors.IsObjectNotFound(err) { - return fmt.Errorf("listing interface management entries via sdk: %v", err) - } - - var leftEntries []string - for _, elt := range entries { - if strings.HasPrefix(elt.Name, prefix) { - leftEntries = append(leftEntries, elt.Name) - } - } - - if len(leftEntries) > 0 { - err := fmt.Errorf("terraform failed to remove entries from the server") - delErr := api.Delete(ctx, *location, leftEntries...) - if delErr != nil { - return errors.Join(err, delErr) - } - } - - return nil - } -} diff --git a/assets/terraform/test/resource_url_filtering_security_profile_test.go b/assets/terraform/test/resource_url_filtering_security_profile_test.go index fccae012..a91b0c87 100644 --- a/assets/terraform/test/resource_url_filtering_security_profile_test.go +++ b/assets/terraform/test/resource_url_filtering_security_profile_test.go @@ -2,7 +2,6 @@ package provider_test import ( "context" - "errors" "fmt" "strings" "testing" @@ -15,7 +14,6 @@ import ( "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" ) @@ -26,7 +24,6 @@ func TestAccUrlFilteringSecurityProfile(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccUrlFilteringSecurityProfileDestroy(prefix), Steps: []resource.TestStep{ { Config: urlFilteringSecurityProfile1Tmpl, @@ -293,35 +290,3 @@ func (o *urlFilteringSecurityProfileExpectNoEntriesInLocation) CheckState(ctx co resp.Error = fmt.Errorf("delete of the resource didn't remove it from the server") } } - -func testAccUrlFilteringSecurityProfileDestroy(prefix string) func(s *terraform.State) error { - return func(s *terraform.State) error { - service := urlfiltering.NewService(sdkClient) - - location := urlfiltering.NewDeviceGroupLocation() - location.DeviceGroup.DeviceGroup = fmt.Sprintf("%s-dg1", prefix) - - ctx := context.TODO() - entries, err := service.List(ctx, *location, "get", "", "") - if err != nil && !sdkerrors.IsObjectNotFound(err) { - return fmt.Errorf("failed to list existing entries via sdk: %w", err) - } - - var leftEntries []string - for _, elt := range entries { - if strings.HasPrefix(elt.Name, prefix) { - leftEntries = append(leftEntries, elt.Name) - } - } - - if len(leftEntries) > 0 { - err := fmt.Errorf("terraform failed to remove entries from the server") - delErr := service.Delete(ctx, *location, leftEntries...) - if delErr != nil { - return errors.Join(err, delErr) - } - } - - return nil - } -} diff --git a/assets/terraform/test/resource_virtual_router_test.go b/assets/terraform/test/resource_virtual_router_test.go index ec88fa86..07c2b873 100644 --- a/assets/terraform/test/resource_virtual_router_test.go +++ b/assets/terraform/test/resource_virtual_router_test.go @@ -1,18 +1,14 @@ package provider_test import ( - "context" "fmt" "testing" - sdkErrors "github.com/PaloAltoNetworks/pango/errors" - "github.com/PaloAltoNetworks/pango/network/virtual_router" "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" ) @@ -26,10 +22,6 @@ func TestAccPanosVirtualRouter_RequiredInputs(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccCheckPanosVirtualRouterDestroy( - fmt.Sprintf("%s-%s", resName, nameSuffix), - fmt.Sprintf("%s-%s", templateName, nameSuffix), - ), Steps: []resource.TestStep{ { Config: makePanosVirtualRouterConfig(resName), @@ -65,10 +57,6 @@ func TestAccPanosVirtualRouter_WithEthernetInterface(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccCheckPanosVirtualRouterDestroy( - fmt.Sprintf("%s-%s", resName, nameSuffix), - fmt.Sprintf("%s-%s", templateName, nameSuffix), - ), Steps: []resource.TestStep{ { Config: makePanosVirtualRouterConfig(resName), @@ -182,26 +170,3 @@ func makePanosVirtualRouterConfig(label string) string { return fmt.Sprintf(configTpl, label) } - -func testAccCheckPanosVirtualRouterDestroy(entryName, templateName string) func(s *terraform.State) error { - return func(s *terraform.State) error { - api := virtual_router.NewService(sdkClient) - ctx := context.TODO() - - location := virtual_router.NewTemplateLocation() - location.Template.Template = templateName - - reply, err := api.Read(ctx, *location, entryName, "show") - if err != nil && !sdkErrors.IsObjectNotFound(err) { - return fmt.Errorf("reading virtual router entry via sdk: %v", err) - } - - if reply != nil { - if reply.EntryName() == entryName { - return fmt.Errorf("virtual router object still exists: %s", entryName) - } - } - - return nil - } -} diff --git a/assets/terraform/test/resource_vlan_interface_test.go b/assets/terraform/test/resource_vlan_interface_test.go index d565086b..1f8d813b 100644 --- a/assets/terraform/test/resource_vlan_interface_test.go +++ b/assets/terraform/test/resource_vlan_interface_test.go @@ -1,20 +1,14 @@ package provider_test import ( - "context" - "errors" "fmt" "testing" - sdkErrors "github.com/PaloAltoNetworks/pango/errors" - "github.com/PaloAltoNetworks/pango/network/interface/vlan" - "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" ) @@ -28,9 +22,6 @@ func TestAccVlanInterface_1(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccCheckPanosVlanInterfaceDestroy( - prefix, interfaceName, - ), Steps: []resource.TestStep{ { Config: vlanInterfaceResource1, @@ -355,9 +346,6 @@ func TestAccVlanInterface_2(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccCheckPanosVlanInterfaceDestroy( - prefix, interfaceName, - ), Steps: []resource.TestStep{ { Config: vlanInterfaceResource2, @@ -534,9 +522,6 @@ func TestAccVlanInterface_3(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccCheckPanosVlanInterfaceDestroy( - prefix, interfaceName, - ), Steps: []resource.TestStep{ { Config: vlanInterfaceResource3, @@ -792,29 +777,3 @@ resource "panos_vlan_interface" "iface" { } } ` - -func testAccCheckPanosVlanInterfaceDestroy(prefix string, entry string) func(s *terraform.State) error { - return func(s *terraform.State) error { - api := vlan.NewService(sdkClient) - ctx := context.TODO() - - location := vlan.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 - } -} diff --git a/assets/terraform/test/resource_vulnerability_security_profile_test.go b/assets/terraform/test/resource_vulnerability_security_profile_test.go index b3adfca8..42376d58 100644 --- a/assets/terraform/test/resource_vulnerability_security_profile_test.go +++ b/assets/terraform/test/resource_vulnerability_security_profile_test.go @@ -1,10 +1,7 @@ -// In TestAccVulnerabilitySecurityProfile function implement all checks indicated by "MISSING" comment - package provider_test import ( "context" - "errors" "fmt" "strings" "testing" @@ -17,7 +14,6 @@ import ( "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" ) @@ -30,7 +26,6 @@ func TestAccVulnerabilitySecurityProfile(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccVulnerabilitySecurityProfileDestroy(prefix), Steps: []resource.TestStep{ { Config: vulnerabilitySecurityProfileTmpl, @@ -269,35 +264,3 @@ func (o *vulnerabilitySecurityProfileExpectNoEntriesInLocation) CheckState(ctx c resp.Error = fmt.Errorf("delete of the resource didn't remove it from the server") } } - -func testAccVulnerabilitySecurityProfileDestroy(prefix string) func(s *terraform.State) error { - return func(s *terraform.State) error { - service := vulnerability.NewService(sdkClient) - - location := vulnerability.NewDeviceGroupLocation() - location.DeviceGroup.DeviceGroup = fmt.Sprintf("%s-dg1", prefix) - - ctx := context.TODO() - entries, err := service.List(ctx, *location, "get", "", "") - if err != nil && !sdkerrors.IsObjectNotFound(err) { - return fmt.Errorf("failed to list existing entries via sdk: %w", err) - } - - var leftEntries []string - for _, elt := range entries { - if strings.HasPrefix(elt.Name, prefix) { - leftEntries = append(leftEntries, elt.Name) - } - } - - if len(leftEntries) > 0 { - err := fmt.Errorf("terraform failed to remove entries from the server") - delErr := service.Delete(ctx, *location, leftEntries...) - if delErr != nil { - return errors.Join(err, delErr) - } - } - - return nil - } -} diff --git a/assets/terraform/test/resource_zone_test.go b/assets/terraform/test/resource_zone_test.go index 3bbf881d..ece3b2da 100644 --- a/assets/terraform/test/resource_zone_test.go +++ b/assets/terraform/test/resource_zone_test.go @@ -1,21 +1,14 @@ package provider_test import ( - "context" - "errors" "fmt" - "strings" "testing" - sdkErrors "github.com/PaloAltoNetworks/pango/errors" - "github.com/PaloAltoNetworks/pango/network/zone" - "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" ) @@ -29,7 +22,6 @@ func TestAccZone(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccZoneDestroy(prefix, fmt.Sprintf("%s-%s", prefix, suffix)), Steps: []resource.TestStep{ { Config: zoneResourceTmpl, @@ -78,7 +70,6 @@ func TestAccZone(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccZoneDestroy(prefix, fmt.Sprintf("%s-%s", prefix, suffix)), Steps: []resource.TestStep{ { Config: zoneResourceTmpl, @@ -96,7 +87,6 @@ func TestAccZone(t *testing.T) { // resource.Test(t, resource.TestCase{ // PreCheck: func() { testAccPreCheck(t) }, // ProtoV6ProviderFactories: testAccProviders, - // CheckDestroy: testAccZoneDestroy(prefix, fmt.Sprintf("%s-%s", prefix, suffix)), // Steps: []resource.TestStep{ // { // Config: zoneResourceTmpl, @@ -114,7 +104,6 @@ func TestAccZone(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, ProtoV6ProviderFactories: testAccProviders, - CheckDestroy: testAccZoneDestroy(prefix, fmt.Sprintf("%s-%s", prefix, suffix)), Steps: []resource.TestStep{ { Config: zoneResourceTmpl, @@ -142,7 +131,6 @@ func TestAccZone(t *testing.T) { // resource.Test(t, resource.TestCase{ // PreCheck: func() { testAccPreCheck(t) }, // ProtoV6ProviderFactories: testAccProviders, - // CheckDestroy: testAccZoneDestroy(prefix, fmt.Sprintf("%s-%s", prefix, suffix)), // Steps: []resource.TestStep{ // { // Config: zoneResourceTmpl, @@ -238,40 +226,3 @@ resource "panos_zone" "zone" { network = local.network } ` - -func testAccZoneDestroy(prefix string, template string) func(s *terraform.State) error { - return func(s *terraform.State) error { - api := zone.NewService(sdkClient) - ctx := context.TODO() - - location := zone.NewTemplateLocation() - location.Template = &zone.TemplateLocation{ - Template: template, - Vsys: "vsys1", - NgfwDevice: "localhost.localdomain", - PanoramaDevice: "localhost.localdomain", - } - - entries, err := api.List(ctx, *location, "get", "", "") - if err != nil && !sdkErrors.IsObjectNotFound(err) { - return fmt.Errorf("error while listing entries via sdk: %v", err) - } - - var leftEntries []string - for _, elt := range entries { - if strings.HasPrefix(elt.Name, prefix) { - leftEntries = append(leftEntries, elt.Name) - } - } - - if len(leftEntries) > 0 { - err := fmt.Errorf("terraform failed to remove entries from the server") - delErr := api.Delete(ctx, *location, leftEntries...) - if delErr != nil { - return errors.Join(err, delErr) - } - } - - return nil - } -} diff --git a/pkg/generate/generator.go b/pkg/generate/generator.go index 7111eea6..dbf65400 100644 --- a/pkg/generate/generator.go +++ b/pkg/generate/generator.go @@ -250,17 +250,17 @@ func writeContentToFile(content *bytes.Buffer, file *os.File) error { func (c *Creator) parseTemplate(templateName string) (*template.Template, error) { templatePath := filepath.Join(c.TemplatesDir, templateName) funcMap := template.FuncMap{ - "Map": codegentmpl.TemplateMap, - "renderImports": translate.RenderImports, + "Map": codegentmpl.TemplateMap, + "renderImports": func(templateTypes ...string) (string, error) { + return translate.RenderImports(c.Spec, templateTypes...) + }, "RenderEntryImportStructs": func() (string, error) { return translate.RenderEntryImportStructs(c.Spec) }, "packageName": translate.PackageName, "locationType": translate.LocationType, "specParamType": translate.SpecParamType, - "xmlParamType": translate.XmlParamType, "xmlName": translate.XmlName, + "xmlParamType": translate.XmlParamType, "xmlTag": translate.XmlTag, - "specifyEntryAssignment": translate.SpecifyEntryAssignmentTmpl, - "normalizeAssignment": translate.NormalizeAssignmentTmpl, "specMatchesFunction": translate.SpecMatchesFunction, "nestedSpecMatchesFunction": translate.NestedSpecMatchesFunction, "omitEmpty": translate.OmitEmpty, @@ -271,13 +271,32 @@ func (c *Creator) parseTemplate(templateName string) (*template.Template, error) "subtract": func(a, b int) int { return a - b }, - "generateEntryXpath": translate.GenerateEntryXpath, - "nestedSpecs": translate.NestedSpecs, + "generateEntryXpath": translate.GenerateEntryXpath, + "RenderApiStructs": func(spec *properties.Normalization) (string, error) { + return translate.RenderEntryApiStructs(spec) + }, + "RenderXmlStructs": func(spec *properties.Normalization) (string, error) { + return translate.RenderEntryXmlStructs(spec) + }, + "RenderXmlContainerNormalizers": func(spec *properties.Normalization) (string, error) { + return translate.RenderXmlContainerNormalizers(spec) + }, + "RenderXmlContainerSpecifiers": func(spec *properties.Normalization) (string, error) { + return translate.RenderXmlContainerSpecifiers(spec) + }, + "RenderToXmlMarshallers": func(spec *properties.Normalization) (string, error) { + return translate.RenderToXmlMarshalers(spec) + }, + "RenderSpecMatchers": func(spec *properties.Normalization) (string, error) { + return translate.RenderSpecMatchers(spec) + }, "createGoSuffixFromVersion": translate.CreateGoSuffixFromVersionTmpl, + "paramNotSkipped": translate.ParamNotSkippedTmpl, "paramSupportedInVersion": translate.ParamSupportedInVersionTmpl, "xmlPathSuffixes": translate.XmlPathSuffixes, "underscore": naming.Underscore, "camelCase": naming.CamelCase, + "lowerCamelCase": naming.LowerCamelCase, } return template.New(templateName).Funcs(funcMap).ParseFiles(templatePath) } diff --git a/pkg/naming/names.go b/pkg/naming/names.go index 222942c6..dab0b8c7 100644 --- a/pkg/naming/names.go +++ b/pkg/naming/names.go @@ -92,6 +92,13 @@ func CamelCase(prefix, value, suffix string, capitalizeFirstRune bool) string { return builder.String() } +func LowerCamelCase(value string) string { + r := []rune(value) + r[0] = unicode.ToLower(r[0]) + + return string(r) +} + // writeRuneAndApplyChangesForCamelCase contains logic to check all conditions for create CamelCase names and writes rune value. func writeRuneAndApplyChangesForCamelCase(runeValue int32, builder *strings.Builder, isFirstCharacter *bool, hasFirstRuneBeenCapitalized *bool, capitalizeFirstRune *bool) { if shouldResetFirstCharacterFlag(runeValue) { diff --git a/pkg/properties/namevariant.go b/pkg/properties/namevariant.go new file mode 100644 index 00000000..f54c6819 --- /dev/null +++ b/pkg/properties/namevariant.go @@ -0,0 +1,48 @@ +package properties + +import ( + "strings" + + "github.com/paloaltonetworks/pan-os-codegen/pkg/naming" +) + +type NameVariant struct { + Original string + Underscore string + CamelCase string + LowerCamelCase string +} + +func NewNameVariant(name string) *NameVariant { + return &NameVariant{ + Original: name, + Underscore: naming.Underscore("", name, ""), + CamelCase: naming.CamelCase("", name, "", true), + LowerCamelCase: naming.CamelCase("", name, "", false), + } +} + +func (o NameVariant) Components() []string { + return strings.Split(o.Original, "-") +} + +func (o NameVariant) IsEmpty() bool { + return o.Original == "" +} + +func (o NameVariant) WithSuffix(suffix *NameVariant) *NameVariant { + if o.Original == "" { + return NewNameVariant(suffix.Original) + } else { + return NewNameVariant(o.Original + "-" + suffix.Original) + } +} + +func (o NameVariant) WithLiteralSuffix(suffix string) *NameVariant { + return &NameVariant{ + Original: o.Original + suffix, + CamelCase: o.CamelCase + suffix, + LowerCamelCase: o.LowerCamelCase + suffix, + Underscore: o.Underscore + suffix, + } +} diff --git a/pkg/properties/namevariant_test.go b/pkg/properties/namevariant_test.go new file mode 100644 index 00000000..8a76061d --- /dev/null +++ b/pkg/properties/namevariant_test.go @@ -0,0 +1,22 @@ +package properties_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" +) + +var _ = Describe("NameVariant", func() { + Context("When creating name variant from empty string", func() { + It("should return empty string for all variants", func() { + variant := properties.NewNameVariant("") + Expect(variant).To(Equal(&properties.NameVariant{ + Original: "", + LowerCamelCase: "", + CamelCase: "", + Underscore: "", + })) + }) + }) +}) diff --git a/pkg/properties/normalized.go b/pkg/properties/normalized.go index ed6c89e9..6819df2f 100644 --- a/pkg/properties/normalized.go +++ b/pkg/properties/normalized.go @@ -1,17 +1,19 @@ package properties import ( + "bytes" "fmt" "io/fs" "maps" "path/filepath" + "regexp" "runtime" "slices" "strconv" "strings" + "text/template" "github.com/paloaltonetworks/pan-os-codegen/pkg/content" - "github.com/paloaltonetworks/pan-os-codegen/pkg/naming" "github.com/paloaltonetworks/pan-os-codegen/pkg/schema/object" "github.com/paloaltonetworks/pan-os-codegen/pkg/schema/parameter" "github.com/paloaltonetworks/pan-os-codegen/pkg/schema/validator" @@ -24,7 +26,7 @@ type Normalization struct { TerraformProviderConfig TerraformProviderConfig `json:"terraform_provider_config" yaml:"terraform_provider_config"` GoSdkSkip bool `json:"go_sdk_skip" yaml:"go_sdk_skip"` GoSdkPath []string `json:"go_sdk_path" yaml:"go_sdk_path"` - XpathSuffix []string `json:"xpath_suffix" yaml:"xpath_suffix"` + PanosXpath PanosXpath `json:"panos_xpath" yaml:"panos_xpath"` Locations map[string]*Location `json:"locations" yaml:"locations"` Entry *Entry `json:"entry" yaml:"entry"` Imports []Import `json:"imports" yaml:"imports"` @@ -33,6 +35,11 @@ type Normalization struct { Const map[string]*Const `json:"const" yaml:"const"` } +type PanosXpath struct { + Path []string `yaml:"path"` + Variables []object.PanosXpathVariable `yaml:"vars"` +} + type Import struct { Variant *NameVariant Type *NameVariant @@ -82,25 +89,10 @@ type TerraformProviderConfig struct { Suffix string `json:"suffix" yaml:"suffix"` PluralSuffix string `json:"plural_suffix" yaml:"plural_suffix"` PluralName string `json:"plural_name" yaml:"plural_name"` + PluralType object.TerraformPluralType `json:"plural_type" yaml:"plural_type"` PluralDescription string `json:"plural_description" yaml:"plural_description"` } -type NameVariant struct { - Original string - Underscore string - CamelCase string - LowerCamelCase string -} - -func NewNameVariant(name string) *NameVariant { - return &NameVariant{ - Original: name, - Underscore: naming.Underscore("", name, ""), - CamelCase: naming.CamelCase("", name, "", true), - LowerCamelCase: naming.CamelCase("", name, "", false), - } -} - type Location struct { Name *NameVariant SpecOrder int @@ -182,6 +174,7 @@ type SpecParam struct { SpecOrder int `json:"-" yaml:"-"` Description string `json:"description" yaml:"description"` TerraformProviderConfig *SpecParamTerraformProviderConfig `json:"terraform_provider_config" yaml:"terraform_provider_config"` + GoSdkConfig *SpecParamGoSdkConfig `json:"gosdk_config" yaml:"gosdk_config"` IsNil bool `json:"-" yaml:"-"` Type string `json:"type" yaml:"type"` Default string `json:"default" yaml:"default"` @@ -197,14 +190,19 @@ type SpecParam struct { Spec *Spec `json:"spec" yaml:"spec"` } +type SpecParamGoSdkConfig struct { + Skip *bool `json:"skip" yaml:"skip"` +} + type SpecParamTerraformProviderConfig struct { - Name *string `json:"name" yaml:"name"` - Type *string `json:"type" yaml:"type"` - Private *bool `json:"ignored" yaml:"private"` - Sensitive *bool `json:"sensitive" yaml:"sensitive"` - Computed *bool `json:"computed" yaml:"computed"` - Required *bool `json:"required" yaml:"required"` - VariantCheck *string `json:"variant_check" yaml:"variant_check"` + Name *string `json:"name" yaml:"name"` + Type *string `json:"type" yaml:"type"` + Private *bool `json:"ignored" yaml:"private"` + Sensitive *bool `json:"sensitive" yaml:"sensitive"` + Computed *bool `json:"computed" yaml:"computed"` + Required *bool `json:"required" yaml:"required"` + VariantCheck *string `json:"variant_check" yaml:"variant_check"` + XpathVariable *string `json:"xpath_variable" yaml:"xpath_variable"` } type SpecParamLength struct { @@ -425,6 +423,14 @@ func (o *SpecParam) HasPrivateParameters() bool { return false } +func (o *SpecParam) IsTerraformOnly() bool { + if o.GoSdkConfig != nil && o.GoSdkConfig.Skip != nil { + return *o.GoSdkConfig.Skip + } + + return false +} + func (o *SpecParam) IsPrivateParameter() bool { if o.TerraformProviderConfig != nil && o.TerraformProviderConfig.Private != nil { return *o.TerraformProviderConfig.Private @@ -569,6 +575,7 @@ func schemaParameterToSpecParameter(schemaSpec *parameter.Parameter) (*SpecParam } var terraformProviderConfig *SpecParamTerraformProviderConfig + var goSdkConfig *SpecParamGoSdkConfig if schemaSpec.CodegenOverrides != nil { var variantCheck *string if schemaSpec.CodegenOverrides.Terraform.VariantCheck != nil { @@ -577,13 +584,18 @@ func schemaParameterToSpecParameter(schemaSpec *parameter.Parameter) (*SpecParam } terraformProviderConfig = &SpecParamTerraformProviderConfig{ - Name: schemaSpec.CodegenOverrides.Terraform.Name, - Type: schemaSpec.CodegenOverrides.Terraform.Type, - Private: schemaSpec.CodegenOverrides.Terraform.Private, - Sensitive: schemaSpec.CodegenOverrides.Terraform.Sensitive, - Computed: schemaSpec.CodegenOverrides.Terraform.Computed, - Required: schemaSpec.CodegenOverrides.Terraform.Required, - VariantCheck: variantCheck, + Name: schemaSpec.CodegenOverrides.Terraform.Name, + Type: schemaSpec.CodegenOverrides.Terraform.Type, + Private: schemaSpec.CodegenOverrides.Terraform.Private, + Sensitive: schemaSpec.CodegenOverrides.Terraform.Sensitive, + Computed: schemaSpec.CodegenOverrides.Terraform.Computed, + Required: schemaSpec.CodegenOverrides.Terraform.Required, + VariantCheck: variantCheck, + XpathVariable: schemaSpec.CodegenOverrides.Terraform.XpathVariable, + } + + goSdkConfig = &SpecParamGoSdkConfig{ + Skip: schemaSpec.CodegenOverrides.GoSdk.Skip, } } @@ -593,6 +605,7 @@ func schemaParameterToSpecParameter(schemaSpec *parameter.Parameter) (*SpecParam IsNil: paramTypeIsNil, Default: defaultVal, Required: schemaSpec.Required, + GoSdkConfig: goSdkConfig, TerraformProviderConfig: terraformProviderConfig, Hashing: specHashing, Profiles: profiles, @@ -665,6 +678,12 @@ func schemaToSpec(object object.Object) (*Normalization, error) { for _, elt := range object.TerraformConfig.ResourceVariants { resourceVariants = append(resourceVariants, TerraformResourceVariant(elt)) } + + panosXpath := PanosXpath{ + Path: object.PanosXpath.Path, + Variables: object.PanosXpath.Variables, + } + spec := &Normalization{ Name: object.DisplayName, TerraformProviderConfig: TerraformProviderConfig{ @@ -679,13 +698,14 @@ func schemaToSpec(object object.Object) (*Normalization, error) { Suffix: object.TerraformConfig.Suffix, PluralSuffix: object.TerraformConfig.PluralSuffix, PluralName: object.TerraformConfig.PluralName, + PluralType: object.TerraformConfig.PluralType, PluralDescription: object.TerraformConfig.PluralDescription, }, - Locations: make(map[string]*Location), - GoSdkSkip: object.GoSdkConfig.Skip, - GoSdkPath: object.GoSdkConfig.Package, - XpathSuffix: object.XpathSuffix, - Version: object.Version, + Locations: make(map[string]*Location), + GoSdkSkip: object.GoSdkConfig.Skip, + GoSdkPath: object.GoSdkConfig.Package, + PanosXpath: panosXpath, + Version: object.Version, Spec: &Spec{ Params: make(map[string]*SpecParam), OneOf: make(map[string]*SpecParam), @@ -1042,6 +1062,217 @@ func (spec *Normalization) Sanity() error { return nil } +func (spec *Normalization) XpathSuffix() []string { + return spec.PanosXpath.Path +} + +func (spec *Normalization) ResourceXpathVariablesWithChecks(checkEntryName bool) bool { + for _, elt := range spec.PanosXpath.Variables { + if elt.Name == "name" && !checkEntryName { + continue + } + + if elt.Spec.Type != "value" { + return true + } + } + + return false +} + +func (spec *Normalization) ResourceXpathVariablesCount() int { + if spec.HasEntryName() && len(spec.PanosXpath.Variables) == 0 { + return 1 + } + + return len(spec.PanosXpath.Variables) +} + +const attributesFromXpathComponentsTmpl = ` +{{- range .Components }} + {{ if eq .Type "value" }} + {{ $.Target }}.{{ .Name }} = types.StringValue(components[{{ .Idx }}]) + {{- else if eq .Type "entry" }} +{ + component := components[{{ .Idx }}] + component = strings.TrimPrefix(component, "entry[@name='") + component = strings.TrimSuffix(component, "']") + {{ $.Target }}.{{ .Name.CamelCase }} = types.StringValue(component) +} + {{- end }} +{{- end }} +` + +func (spec *Normalization) AttributesFromXpathComponents(target string) (string, error) { + type componentCtx struct { + Type object.PanosXpathVariableType + Name *NameVariant + Idx int + } + + type componentsCtx struct { + Target string + Components []componentCtx + } + + var components []componentCtx + for idx, elt := range spec.PanosXpath.Variables { + if elt.Name == "name" { + continue + } + + param, err := spec.ParameterForPanosXpathVariable(elt) + if err != nil { + return "", err + } + + components = append(components, componentCtx{ + Type: elt.Spec.Type, + Name: param.Name, + Idx: idx, + }) + } + + data := componentsCtx{ + Target: target, + Components: components, + } + + tmpl := template.Must(template.New("attributes-from-xpath-components").Parse(attributesFromXpathComponentsTmpl)) + + var buf bytes.Buffer + err := tmpl.Execute(&buf, data) + if err != nil { + panic(err) + } + + return buf.String(), nil +} + +const resourceXpathAssignmentsTmpl = ` +{{- range .Variables }} + {{- if or (eq .Type "entry") (eq .Type "value") }} + ans = append(ans, components[{{ .Idx }}]) + {{- else if eq .Type "static" }} + ans = append(ans, "{{ .Name.Original }}") + {{- end }} +{{- end }} +` + +func (spec *Normalization) ResourceXpathAssignments() (string, error) { + data := xpathVariablesCtx{ + Variables: createXpathVariablesContext(spec), + } + + tmpl := template.Must(template.New("resource-xpath-assignments").Parse(resourceXpathAssignmentsTmpl)) + + var buf bytes.Buffer + err := tmpl.Execute(&buf, data) + if err != nil { + panic(err) + } + + return buf.String(), nil +} + +const xpathVariableChecksTmpl = ` +{{- range .Variables }} + {{- if eq .Type "entry" }} +{ + component := components[{{ .Idx }}] + {{- if .AllowEmpty }} + if component != "entry" { + {{- end }} + if !strings.HasPrefix(component, "entry[@name=\"]") && !strings.HasPrefix(component, "entry[@name='") { + return nil, errors.NewInvalidXpathComponentError(fmt.Sprintf("{{ .Name.CamelCase }} must be formatted as entry: %s", component)) + } + + if !strings.HasSuffix(component, "\"]") && !strings.HasSuffix(component, "']") { + return nil, errors.NewInvalidXpathComponentError(fmt.Sprintf("{{ .Name.CamelCase }} must be formatted as entry: %s", component)) + } + {{- if .AllowEmpty }} + } + {{- end }} +} + {{- end }} +{{- end }} +` + +type xpathVariableCtx struct { + Type object.PanosXpathVariableType + Name *NameVariant + AllowEmpty bool + Idx int +} + +type xpathVariablesCtx struct { + Variables []xpathVariableCtx +} + +func createXpathVariablesContext(spec *Normalization) []xpathVariableCtx { + var variables []xpathVariableCtx + + variablesByName := make(map[string]object.PanosXpathVariable) + for _, elt := range spec.PanosXpath.Variables { + variablesByName[elt.Name] = elt + } + + var idx int + for _, elt := range spec.PanosXpath.Path { + if strings.HasPrefix(elt, "$") { + variable, found := variablesByName[elt[1:]] + if !found { + panic("couldn't match panos xpath variable to path item") + } + + var allowEmpty bool + var name *NameVariant + if variable.Name == "name" { + allowEmpty = true + name = NewNameVariant("name") + } else { + param, err := spec.ParameterForPanosXpathVariable(variable) + if err != nil { + panic(fmt.Sprintf("couldn't find matching param for xpath variable: %s", err.Error())) + } + name = param.Name + } + + variables = append(variables, xpathVariableCtx{ + Type: variable.Spec.Type, + AllowEmpty: allowEmpty, + Name: name, + Idx: idx, + }) + + idx += 1 + } else { + variables = append(variables, xpathVariableCtx{ + Type: object.PanosXpathVariableStatic, + Name: NewNameVariant(elt), + }) + } + } + + return variables +} + +func (spec *Normalization) ResourceXpathVariableChecks() (string, error) { + data := xpathVariablesCtx{ + Variables: createXpathVariablesContext(spec), + } + + var buf bytes.Buffer + tmpl := template.Must(template.New("xpath-variable-checks").Parse(xpathVariableChecksTmpl)) + + err := tmpl.Execute(&buf, data) + if err != nil { + panic(err) + } + + return buf.String(), nil +} + // Validate validations for specification (normalization) e.g. check if XPath contain /. func (spec *Normalization) Validate() []error { var checks []error @@ -1049,7 +1280,7 @@ func (spec *Normalization) Validate() []error { if strings.Contains(spec.TerraformProviderConfig.Suffix, "panos_") { checks = append(checks, fmt.Errorf("suffix for Terraform provider cannot contain `panos_`")) } - for _, suffix := range spec.XpathSuffix { + for _, suffix := range spec.XpathSuffix() { if strings.Contains(suffix, "/") { checks = append(checks, fmt.Errorf("XPath cannot contain /")) } @@ -1287,6 +1518,53 @@ func (spec *Normalization) HasPrivateParameters() bool { return false } +func resolveXpath(xpath []string, spec *Spec) (*SpecParam, error) { + fmt.Printf("xpath[0]: %s\n", xpath[0]) + elt := xpath[0] + if elt == "spec" { + elt = xpath[1] + } + + var stack map[string]*SpecParam + if strings.HasPrefix(elt, "params") { + stack = spec.Params + } else if strings.HasPrefix(elt, "variants") { + stack = spec.OneOf + } else { + return nil, fmt.Errorf("failed to resolve variable xpath: %s", elt) + } + + getParamNameFromXpathElt := func(elt string) string { + nameRegex := regexp.MustCompile(`.+("|')(?P.+)("|')`) + regexNames := nameRegex.SubexpNames() + matches := nameRegex.FindStringSubmatch(elt) + + return matches[nameRegex.SubexpIndex(regexNames[2])] + } + + name := getParamNameFromXpathElt(elt) + + needle, found := stack[name] + if !found { + return nil, fmt.Errorf("failed to revolve variable xpath: %s", elt) + } + + if len(xpath) == 1 { + return needle, nil + } + + if needle.Spec == nil { + return nil, fmt.Errorf("failed to revolve variable xpath: %s", elt) + } + + return resolveXpath(xpath[1:], needle.Spec) +} + +func (spec *Normalization) ParameterForPanosXpathVariable(variable object.PanosXpathVariable) (*SpecParam, error) { + xpath := strings.Split(variable.Spec.Xpath, "/") + return resolveXpath(xpath[1:], spec.Spec) +} + func supportedVersions(params map[string]*SpecParam, versions []string) []string { for _, param := range params { for _, profile := range param.Profiles { diff --git a/pkg/properties/properties_suite_test.go b/pkg/properties/properties_suite_test.go new file mode 100644 index 00000000..e59e4a36 --- /dev/null +++ b/pkg/properties/properties_suite_test.go @@ -0,0 +1,18 @@ +package properties_test + +import ( + "log/slog" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestMovement(t *testing.T) { + handler := slog.NewTextHandler(GinkgoWriter, &slog.HandlerOptions{ + Level: slog.LevelDebug, + }) + slog.SetDefault(slog.New(handler)) + RegisterFailHandler(Fail) + RunSpecs(t, "Properties Suite") +} diff --git a/pkg/schema/object/object.go b/pkg/schema/object/object.go index a973bdef..60fe00d2 100644 --- a/pkg/schema/object/object.go +++ b/pkg/schema/object/object.go @@ -1,6 +1,8 @@ package object import ( + "fmt" + "gopkg.in/yaml.v3" "github.com/paloaltonetworks/pan-os-codegen/pkg/schema/imports" @@ -25,6 +27,14 @@ const ( TerraformResourcePlural TerraformResourceVariant = "plural" ) +type TerraformPluralType string + +const ( + TerraformPluralListType TerraformPluralType = "list" + TerraformPluralMapType TerraformPluralType = "map" + TerraformPluralSetType TerraformPluralType = "set" +) + type TerraformConfig struct { Description string `yaml:"description"` Epheneral bool `yaml:"ephemeral"` @@ -37,6 +47,7 @@ type TerraformConfig struct { Suffix string `yaml:"suffix"` PluralSuffix string `yaml:"plural_suffix"` PluralName string `yaml:"plural_name"` + PluralType TerraformPluralType `yaml:"plural_type"` PluralDescription string `yaml:"plural_description"` } @@ -57,10 +68,33 @@ type Spec struct { Variants []*parameter.Parameter `yaml:"variants"` } +type PanosXpathVariableType string + +const ( + PanosXpathVariableStatic PanosXpathVariableType = "static" + PanosXpathVariableValue PanosXpathVariableType = "value" + PanosXpathVariableEntry PanosXpathVariableType = "entry" +) + +type PanosXpathVariableSpec struct { + Type PanosXpathVariableType `yaml:"type"` + Xpath string `yaml:"xpath"` +} + +type PanosXpathVariable struct { + Name string `yaml:"name"` + Spec PanosXpathVariableSpec `yaml:"spec"` +} + +type PanosXpath struct { + Path []string `yaml:"path"` + Variables []PanosXpathVariable `yaml:"vars"` +} + type Object struct { Name string `yaml:"-"` DisplayName string `yaml:"name"` - XpathSuffix []string `yaml:"xpath_suffix"` + PanosXpath PanosXpath `yaml:"panos_xpath"` TerraformConfig *TerraformConfig `yaml:"terraform_provider_config"` Version string `yaml:"version"` GoSdkConfig *GoSdkConfig `yaml:"go_sdk_config"` @@ -70,6 +104,47 @@ type Object struct { Spec *Spec `yaml:"spec"` } +func (o *Object) UnmarshalYAML(n *yaml.Node) error { + type O Object + type S struct { + *O `yaml:",inline"` + } + + obj := &S{O: (*O)(o)} + if err := n.Decode(obj); err != nil { + return err + } + + switch obj.TerraformConfig.ResourceType { + case TerraformResourceEntry, TerraformResourceUuid: + if obj.PanosXpath.Path[len(obj.PanosXpath.Path)-1] != "$name" { + obj.PanosXpath.Path = append(obj.PanosXpath.Path, "$name") + obj.PanosXpath.Variables = append(obj.PanosXpath.Variables, PanosXpathVariable{ + Name: "name", + Spec: PanosXpathVariableSpec{ + Type: PanosXpathVariableEntry, + Xpath: "/params[@name=\"name\"]", + }, + }) + } + case TerraformResourceCustom, TerraformResourceConfig: + } + + if obj.TerraformConfig.PluralType == "" { + switch obj.TerraformConfig.ResourceType { + case TerraformResourceUuid: + obj.TerraformConfig.PluralType = TerraformPluralListType + case TerraformResourceEntry: + obj.TerraformConfig.PluralType = TerraformPluralMapType + case TerraformResourceConfig, TerraformResourceCustom: + } + } else if obj.TerraformConfig.ResourceType == TerraformResourceUuid && obj.TerraformConfig.PluralType != "list" { + return fmt.Errorf("failed to unmarshal yaml spec: plural_type must be list for uuid resource types") + } + + return nil +} + func NewFromBytes(name string, objectBytes []byte) (*Object, error) { var object Object diff --git a/pkg/schema/parameter/parameter.go b/pkg/schema/parameter/parameter.go index 79cc847d..af133a4d 100644 --- a/pkg/schema/parameter/parameter.go +++ b/pkg/schema/parameter/parameter.go @@ -3,6 +3,7 @@ package parameter import ( "fmt" "log/slog" + "strings" "gopkg.in/yaml.v3" @@ -50,17 +51,23 @@ const ( VariantCheckExactlyOneOf VariantCheckType = "ExactlyOneOf" ) +type CodegenOverridesGoSdk struct { + Skip *bool `yaml:"skip"` +} + type CodegenOverridesTerraform struct { - Name *string `yaml:"name"` - Type *string `yaml:"type"` - Private *bool `yaml:"private"` - Sensitive *bool `yaml:"sensitive"` - Computed *bool `yaml:"computed"` - Required *bool `yaml:"required"` - VariantCheck *VariantCheckType `yaml:"variant_check"` + Name *string `yaml:"name"` + Type *string `yaml:"type"` + Private *bool `yaml:"private"` + Sensitive *bool `yaml:"sensitive"` + Computed *bool `yaml:"computed"` + Required *bool `yaml:"required"` + VariantCheck *VariantCheckType `yaml:"variant_check"` + XpathVariable *string `yaml:"xpath_variable"` } type CodegenOverrides struct { + GoSdk CodegenOverridesGoSdk `yaml:"gosdk"` Terraform CodegenOverridesTerraform `yaml:"terraform"` } @@ -149,6 +156,9 @@ func (p *Parameter) UnmarshalYAML(n *yaml.Node) error { return errors.NewSchemaError(fmt.Sprintf("unsupported parameter type: '%s'", p.Type)) } + // Escape value of a description, making sure all backslashes are handled properly + obj.Description = strings.ReplaceAll(obj.Description, "\\", "\\\\") + // Finally, decode obj.Spec (which is yaml.Node type) into the parameter // spec structure return obj.Spec.Decode(p.Spec) diff --git a/pkg/translate/assignments.go b/pkg/translate/assignments.go index f1f1d6ff..4729bb3d 100644 --- a/pkg/translate/assignments.go +++ b/pkg/translate/assignments.go @@ -1,86 +1,9 @@ package translate import ( - "fmt" - "log" - "strings" - "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" - "github.com/paloaltonetworks/pan-os-codegen/pkg/version" ) -func NormalizeAssignmentTmpl(objectType string, param *properties.SpecParam, v any) (string, error) { - if v != nil { - typed, ok := v.(version.Version) - if !ok { - return "", fmt.Errorf("Failed to cast version to version.Version: '%T'", v) - } - return NormalizeAssignment(objectType, param, &typed), nil - } - return NormalizeAssignment(objectType, param, nil), nil -} - -// NormalizeAssignment generates a string, which contains entry/config assignment in Normalize() function -// in entry.tmpl/config.tmpl template. If param contains nested specs, then recursively are executed -// internal functions, which are creating entry assignment. -func NormalizeAssignment(objectType string, param *properties.SpecParam, version *version.Version) string { - return prepareAssignment(objectType, param, "util.MemToStr", "util.EntToStr", "util.AsBool", "", "", version) -} - -func SpecifyEntryAssignmentTmpl(objectType string, param *properties.SpecParam, v any) (string, error) { - - if v != nil { - typed, ok := v.(version.Version) - if !ok { - return "", fmt.Errorf("Failed to cast version to version.Version: '%T'", v) - } - return SpecifyEntryAssignment(objectType, param, &typed), nil - } - - return SpecifyEntryAssignment(objectType, param, nil), nil -} - -// SpecifyEntryAssignment generates a string, which contains entry/config assignment in SpecifyEntry() function -// in entry.tmpl/config.tmpl template. If param contains nested specs, then recursively are executed -// internal functions, which are creating entry assignment. -func SpecifyEntryAssignment(objectType string, param *properties.SpecParam, v *version.Version) string { - return prepareAssignment(objectType, param, "util.StrToMem", "util.StrToEnt", "util.YesNo", "", "Xml", v) -} - -func prepareAssignment(objectType string, param *properties.SpecParam, listFunction, entryFunction, boolFunction, prefix, suffix string, version *version.Version) string { - var builder strings.Builder - - if ParamSupportedInVersion(param, version) { - var isNestedListHack bool - if param.Type == "list" { - isNestedListHack = true - } - switch { - case param.Spec != nil: - appendSpecObjectAssignment(param, nil, objectType, version, - listFunction, entryFunction, boolFunction, prefix, suffix, &builder, isNestedListHack) - case isParamListAndProfileTypeIsMember(param): - appendFunctionAssignment(param, objectType, listFunction, "", &builder) - case isParamListAndProfileTypeIsSingleEntry(param): - appendFunctionAssignment(param, objectType, entryFunction, "", &builder) - case param.Type == "bool": - appendFunctionAssignment(param, objectType, boolFunction, - useBoolFunctionForAdditionalArguments(suffix, param), &builder) - default: - appendSimpleAssignment(param, objectType, &builder) - } - } - - return builder.String() -} - -func useBoolFunctionForAdditionalArguments(suffix string, param *properties.SpecParam) string { - if suffix == "Xml" && param.Default != "" { - return fmt.Sprintf("util.Bool(%s)", param.Default) - } - return "nil" -} - func isParamListAndProfileTypeIsMember(param *properties.SpecParam) bool { return param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "member" } @@ -92,293 +15,3 @@ func isParamListAndProfileTypeIsSingleEntry(param *properties.SpecParam) bool { func isParamListAndProfileTypeIsExtendedEntry(param *properties.SpecParam) bool { return param != nil && param.Type == "list" && param.Profiles != nil && len(param.Profiles) > 0 && param.Profiles[0].Type == "entry" && param.Items != nil && param.Items.Type != "string" } - -func appendSimpleAssignment(param *properties.SpecParam, objectType string, builder *strings.Builder) { - builder.WriteString(fmt.Sprintf("%s.%s = o.%s", objectType, param.Name.CamelCase, param.Name.CamelCase)) -} - -func appendFunctionAssignment(param *properties.SpecParam, objectType string, functionName, additionalArguments string, builder *strings.Builder) { - if additionalArguments != "" { - builder.WriteString(fmt.Sprintf("%s.%s = %s(o.%s, %s)", objectType, param.Name.CamelCase, functionName, param.Name.CamelCase, additionalArguments)) - } else { - builder.WriteString(fmt.Sprintf("%s.%s = %s(o.%s)", objectType, param.Name.CamelCase, functionName, param.Name.CamelCase)) - } -} - -func appendSpecObjectAssignment(param, parentParam *properties.SpecParam, objectType string, version *version.Version, listFunction, entryFunction, boolFunction, prefix, suffix string, builder *strings.Builder, isNestedListHack bool) { - defineNestedObject([]*properties.SpecParam{param}, param, parentParam, objectType, version, listFunction, entryFunction, boolFunction, prefix, suffix, builder) - - if parentParam == nil && param.Type == "list" && param.Items.Type == "entry" { - builder.WriteString(fmt.Sprintf("%s.%s = nested%sCol\n", objectType, param.Name.CamelCase, param.Name.CamelCase)) - } else { - builder.WriteString(fmt.Sprintf("%s.%s = nested%s\n", objectType, param.Name.CamelCase, param.Name.CamelCase)) - } - - if isNestedListHack { - builder.WriteString("}\n") - } -} - -func defineNestedObject(parent []*properties.SpecParam, param, parentParam *properties.SpecParam, objectType string, version *version.Version, listFunction, entryFunction, boolFunction, prefix, suffix string, builder *strings.Builder) { - - var isNestedListHack bool - if parentParam == nil && param.Type == "list" && param.Items.Type == "entry" { - isNestedListHack = true - } - - declareRootOfNestedObject(parent, builder, version, prefix, suffix, isNestedListHack) - - if ParamSupportedInVersion(param, version) { - startIfBlockForParamNotNil(parent, param, parentParam, builder) - - switch { - case param.Spec != nil: - assignEmptyStructForNestedObject(parent, builder, param, objectType, version, prefix, suffix) - defineNestedObjectForChildParams(parent, param.Spec.SortedParams(), param, objectType, version, listFunction, entryFunction, boolFunction, prefix, suffix, builder) - defineNestedObjectForChildParams(parent, param.Spec.SortedOneOf(), param, objectType, version, listFunction, entryFunction, boolFunction, prefix, suffix, builder) - case isParamListAndProfileTypeIsMember(param): - assignFunctionForNestedObject(parent, listFunction, "", builder, param, parentParam) - case isParamListAndProfileTypeIsSingleEntry(param): - assignFunctionForNestedObject(parent, entryFunction, "", builder, param, parentParam) - case param.Type == "bool": - assignFunctionForNestedObject(parent, boolFunction, - useBoolFunctionForAdditionalArguments(suffix, param), builder, param, parentParam) - default: - assignValueForNestedObject(parent, builder, param, parentParam) - } - - finishNestedObjectIfBlock(parent, param, builder) - } -} - -func startIfBlockForParamNotNil(parents []*properties.SpecParam, param *properties.SpecParam, parentParam *properties.SpecParam, builder *strings.Builder) { - grandparent := parents[0] - - if grandparent != param && grandparent.Type == "list" && grandparent.Items.Type == "entry" { - if isParamName(param) { - builder.WriteString(fmt.Sprintf("if o%s != \"\" {\n", - renderNestedVariableName(parents, true, true, false))) - } else { - builder.WriteString(fmt.Sprintf("if o%s != nil {\n", - renderNestedVariableName(parents, true, true, false))) - } - } else { - if isParamName(param) { - builder.WriteString(fmt.Sprintf("if o%s != \"\" {\n", - renderNestedVariableName(parents, true, true, true))) - } else { - builder.WriteString(fmt.Sprintf("if o%s != nil {\n", - renderNestedVariableName(parents, true, true, true))) - } - } - -} - -func finishNestedObjectIfBlock(parent []*properties.SpecParam, param *properties.SpecParam, builder *strings.Builder) { - if len(parent) == 1 && parent[0].Type == "list" && parent[0].Items.Type == "entry" { - if isParamListAndProfileTypeIsExtendedEntry(param) { - builder.WriteString(fmt.Sprintf("nested%sCol = append(nested%sCol, nested%s)\n", - renderNestedVariableName(parent, true, true, false), - renderNestedVariableName(parent, true, true, false), - renderNestedVariableName(parent, false, false, false))) - } - } else { - if isParamListAndProfileTypeIsExtendedEntry(param) { - builder.WriteString(fmt.Sprintf("nested%s = append(nested%s, nested%s)\n", - renderNestedVariableName(parent, true, true, false), - renderNestedVariableName(parent, true, true, false), - renderNestedVariableName(parent, false, false, false))) - } - } - - builder.WriteString("}\n") -} - -func isParamName(param *properties.SpecParam) bool { - return param.Required && param.Name.CamelCase == "Name" -} - -func declareRootOfNestedObject(parent []*properties.SpecParam, builder *strings.Builder, version *version.Version, prefix, suffix string, isNestedListHack bool) { - var vstr string - if suffix != "" { - vstr = CreateGoSuffixFromVersion(version) - } - if isNestedListHack { - builder.WriteString(fmt.Sprintf("var nested%sCol []%s%s%s%s\n", - renderNestedVariableName(parent, true, true, false), prefix, - renderNestedVariableName(parent, false, false, false), suffix, - vstr)) - } else if len(parent) == 1 { - builder.WriteString(fmt.Sprintf("var nested%s *%s%s%s%s\n", - renderNestedVariableName(parent, true, true, false), prefix, - renderNestedVariableName(parent, false, false, false), suffix, - vstr)) - } -} - -func assignEmptyStructForNestedObject(parent []*properties.SpecParam, builder *strings.Builder, param *properties.SpecParam, objectType string, version *version.Version, prefix, suffix string) { - if isParamListAndProfileTypeIsExtendedEntry(param) { - createListAndLoopForNestedEntry(parent, param, builder, prefix, suffix, version) - miscForUnknownXmlWithExtendedEntry(parent, objectType, builder, suffix) - } else { - createStructForParamWithSpec(parent, builder, prefix, suffix, version) - miscForUnknownXmlWithSpec(parent, builder, suffix, objectType) - } - builder.WriteString("}\n") -} - -func createStructForParamWithSpec(parent []*properties.SpecParam, builder *strings.Builder, prefix string, suffix string, version *version.Version) { - var vstr string - if suffix != "" { - vstr = CreateGoSuffixFromVersion(version) - } - builder.WriteString(fmt.Sprintf("nested%s = &%s%s%s%s{}\n", - renderNestedVariableName(parent, true, true, false), prefix, - renderNestedVariableName(parent, false, false, false), suffix, - vstr)) -} - -func createListAndLoopForNestedEntry(parent []*properties.SpecParam, param *properties.SpecParam, builder *strings.Builder, prefix string, suffix string, version *version.Version) { - var vstr string - if suffix != "" { - vstr = CreateGoSuffixFromVersion(version) - } - - if len(parent) == 1 && parent[0].Type == "list" && parent[0].Items.Type == "entry" { - builder.WriteString(fmt.Sprintf("nested%sCol = []%s%s%s%s{}\n", - renderNestedVariableName(parent, true, true, false), prefix, - renderNestedVariableName(parent, false, false, false), suffix, - vstr)) - } else { - builder.WriteString(fmt.Sprintf("nested%s = []%s%s%s%s{}\n", - renderNestedVariableName(parent, true, true, false), prefix, - renderNestedVariableName(parent, false, false, false), suffix, - vstr)) - } - - startFromDot := true - if len(parent) >= 2 && parent[0].Type == "list" && parent[0].Items.Type == "entry" { - startFromDot = false - } - - builder.WriteString(fmt.Sprintf("for _, o%s := range o%s { \n", - renderNestedVariableName(parent, false, false, false), - renderNestedVariableName(parent, true, true, startFromDot))) - builder.WriteString(fmt.Sprintf("nested%s := %s%s%s%s{}\n", - renderNestedVariableName(parent, false, false, false), - prefix, renderNestedVariableName(parent, false, false, false), suffix, - vstr)) -} - -func miscForUnknownXmlWithSpec(parent []*properties.SpecParam, builder *strings.Builder, suffix string, objectType string) { - if suffix == "Xml" { - builder.WriteString(fmt.Sprintf("if _, ok := o.Misc[\"%s\"]; ok {\n", - renderNestedVariableName(parent, false, false, false))) - builder.WriteString(fmt.Sprintf("nested%s.Misc = o.Misc[\"%s\"]\n", - renderNestedVariableName(parent, true, true, false), - renderNestedVariableName(parent, false, false, false), - )) - } else { - startsWithDot := true - if parent[0].Type == "list" && parent[0].Items.Type == "entry" { - startsWithDot = false - } - - builder.WriteString(fmt.Sprintf("if o%s.Misc != nil {\n", - renderNestedVariableName(parent, true, true, startsWithDot))) - builder.WriteString(fmt.Sprintf("%s.Misc[\"%s\"] = o%s.Misc\n", - objectType, renderNestedVariableName(parent, false, false, false), - renderNestedVariableName(parent, true, true, startsWithDot), - )) - } -} - -func miscForUnknownXmlWithExtendedEntry(parent []*properties.SpecParam, objectType string, builder *strings.Builder, suffix string) { - if suffix == "Xml" { - builder.WriteString(fmt.Sprintf("if _, ok := o.Misc[\"%s\"]; ok {\n", - renderNestedVariableName(parent, false, false, false))) - builder.WriteString(fmt.Sprintf("nested%s.Misc = o.Misc[\"%s\"]\n", - renderNestedVariableName(parent, false, false, false), - renderNestedVariableName(parent, false, false, false), - )) - } else { - builder.WriteString(fmt.Sprintf("if o%s.Misc != nil {\n", - renderNestedVariableName(parent, false, false, false))) - builder.WriteString(fmt.Sprintf("%s.Misc[\"%s\"] = o%s.Misc\n", - objectType, - renderNestedVariableName(parent, false, false, false), - renderNestedVariableName(parent, false, false, false), - )) - } -} - -var _ = log.Printf - -func assignValueForNestedObject(parent []*properties.SpecParam, builder *strings.Builder, param, parentParam *properties.SpecParam) { - if parent[0] != param && parent[0].Type == "list" && parent[0].Items.Type == "entry" { - builder.WriteString(fmt.Sprintf("nested%s = o%s\n", - renderNestedVariableName(parent, true, true, false), - renderNestedVariableName(parent, true, true, false))) - } else { - builder.WriteString(fmt.Sprintf("nested%s = o%s\n", - renderNestedVariableName(parent, true, true, false), - renderNestedVariableName(parent, true, true, true))) - } -} - -func assignFunctionForNestedObject(parent []*properties.SpecParam, functionName, additionalArguments string, builder *strings.Builder, param, parentParam *properties.SpecParam) { - - var startWithDot bool - if parent[0] != param && parent[0].Type == "list" && parent[0].Items.Type == "entry" { - startWithDot = false - } else { - startWithDot = true - } - if additionalArguments != "" { - builder.WriteString(fmt.Sprintf("nested%s = %s(o%s, %s)\n", - renderNestedVariableName(parent, true, true, false), functionName, - renderNestedVariableName(parent, true, true, startWithDot), additionalArguments)) - } else { - builder.WriteString(fmt.Sprintf("nested%s = %s(o%s)\n", - renderNestedVariableName(parent, true, true, false), functionName, - renderNestedVariableName(parent, true, true, startWithDot))) - } -} - -func defineNestedObjectForChildParams(parent []*properties.SpecParam, params []*properties.SpecParam, parentParam *properties.SpecParam, objectType string, version *version.Version, listFunction, entryFunction, boolFunction, prefix, suffix string, builder *strings.Builder) { - for _, param := range params { - defineNestedObject(append(parent, param), param, parentParam, objectType, version, listFunction, entryFunction, boolFunction, prefix, suffix, builder) - if isParamListAndProfileTypeIsExtendedEntry(param) { - builder.WriteString("}\n") - } - } -} - -func renderNestedVariableName(params []*properties.SpecParam, useDot, searchForParamWithEntry, startFromDot bool) string { - var builder strings.Builder - - indexOfLastParamWithExtendedEntry := 0 - - if searchForParamWithEntry && len(params) > 2 { - for i := len(params) - 2; i >= 0; i-- { - if isParamListAndProfileTypeIsExtendedEntry(params[i]) { - indexOfLastParamWithExtendedEntry = i - break - } - } - } - - for i, param := range params { - if useDot && startFromDot && i == 0 && (!searchForParamWithEntry || - searchForParamWithEntry && i >= indexOfLastParamWithExtendedEntry) { - builder.WriteString(".") - } - builder.WriteString(param.Name.CamelCase) - if useDot && i < len(params)-1 && (!searchForParamWithEntry || - searchForParamWithEntry && i >= indexOfLastParamWithExtendedEntry) { - builder.WriteString(".") - } - } - - return builder.String() -} diff --git a/pkg/translate/assignments_test.go b/pkg/translate/assignments_test.go deleted file mode 100644 index 3a4c0d62..00000000 --- a/pkg/translate/assignments_test.go +++ /dev/null @@ -1,298 +0,0 @@ -package translate - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" -) - -func TestSpecifyEntryAssignmentForFlatStructure(t *testing.T) { - // given - paramTypeString := properties.SpecParam{ - Name: &properties.NameVariant{ - CamelCase: "Description", - Underscore: "description", - }, - Profiles: []*properties.SpecParamProfile{ - { - Type: "string", - Xpath: []string{"description"}, - }, - }, - } - paramTypeListString := properties.SpecParam{ - Type: "list", - Name: &properties.NameVariant{ - CamelCase: "Tags", - Underscore: "tags", - }, - Profiles: []*properties.SpecParamProfile{ - { - Type: "member", - Xpath: []string{"tags"}, - }, - }, - } - - // when - calculatedAssignmentString := SpecifyEntryAssignment("entry", ¶mTypeString, nil) - calculatedAssignmentListString := SpecifyEntryAssignment("entry", ¶mTypeListString, nil) - - // then - assert.Equal(t, "entry.Description = o.Description", calculatedAssignmentString) - assert.Equal(t, "entry.Tags = util.StrToMem(o.Tags)", calculatedAssignmentListString) -} - -func TestSpecifyEntryAssignmentForNestedObject(t *testing.T) { - // given - spec := properties.Spec{ - Params: map[string]*properties.SpecParam{ - "a": { - Name: &properties.NameVariant{ - Underscore: "a", - CamelCase: "A", - }, - Spec: &properties.Spec{ - Params: map[string]*properties.SpecParam{ - "b": { - Name: &properties.NameVariant{ - Underscore: "b", - CamelCase: "B", - }, - Spec: &properties.Spec{ - Params: map[string]*properties.SpecParam{ - "c": { - Name: &properties.NameVariant{ - Underscore: "c", - CamelCase: "C", - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - expectedAssignmentString := `var nestedA *AXml -if o.A != nil { -nestedA = &AXml{} -if _, ok := o.Misc["A"]; ok { -nestedA.Misc = o.Misc["A"] -} -if o.A.B != nil { -nestedA.B = &ABXml{} -if _, ok := o.Misc["AB"]; ok { -nestedA.B.Misc = o.Misc["AB"] -} -if o.A.B.C != nil { -nestedA.B.C = o.A.B.C -} -} -} -entry.A = nestedA -` - // when - calculatedAssignmentString := SpecifyEntryAssignment("entry", spec.Params["a"], nil) - - // then - assert.Equal(t, expectedAssignmentString, calculatedAssignmentString) -} - -func TestNormalizeAssignmentForNestedObject(t *testing.T) { - // given - spec := properties.Spec{ - Params: map[string]*properties.SpecParam{ - "a": { - Name: &properties.NameVariant{ - Underscore: "a", - CamelCase: "A", - }, - Spec: &properties.Spec{ - Params: map[string]*properties.SpecParam{ - "b": { - Name: &properties.NameVariant{ - Underscore: "b", - CamelCase: "B", - }, - Spec: &properties.Spec{ - Params: map[string]*properties.SpecParam{ - "c": { - Name: &properties.NameVariant{ - Underscore: "c", - CamelCase: "C", - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - expectedAssignmentString := `var nestedA *A -if o.A != nil { -nestedA = &A{} -if o.A.Misc != nil { -entry.Misc["A"] = o.A.Misc -} -if o.A.B != nil { -nestedA.B = &AB{} -if o.A.B.Misc != nil { -entry.Misc["AB"] = o.A.B.Misc -} -if o.A.B.C != nil { -nestedA.B.C = o.A.B.C -} -} -} -entry.A = nestedA -` - // when - calculatedAssignmentString := NormalizeAssignment("entry", spec.Params["a"], nil) - - // then - assert.Equal(t, expectedAssignmentString, calculatedAssignmentString) -} - -func TestPrepareAssignment(t *testing.T) { - // given - objectType := "entry" - param := &properties.SpecParam{ - Name: &properties.NameVariant{ - CamelCase: "Description", - Underscore: "description", - }, - Type: "list", - Items: &properties.SpecParamItems{ - Type: "string", - }, - Profiles: []*properties.SpecParamProfile{ - { - Type: "member", - Xpath: []string{"description"}, - }, - }, - } - listFunction := "util.StrToMem" - entryFunction := "" - boolFunction := "util.Bool" - prefix := "" - suffix := "Xml" - - expectedAssignment := `entry.Description = util.StrToMem(o.Description)` - - // when - calculatedAssignment := prepareAssignment(objectType, param, listFunction, entryFunction, boolFunction, prefix, suffix, nil) - - // then - assert.Equal(t, expectedAssignment, calculatedAssignment) -} - -func TestNestedVariableNameWithoutEntry(t *testing.T) { - // given - spec := properties.Spec{ - Params: map[string]*properties.SpecParam{ - "a": { - Name: &properties.NameVariant{ - Underscore: "a", - CamelCase: "A", - }, - Spec: &properties.Spec{ - Params: map[string]*properties.SpecParam{ - "b": { - Name: &properties.NameVariant{ - Underscore: "b", - CamelCase: "B", - }, - Spec: &properties.Spec{ - Params: map[string]*properties.SpecParam{ - "c": { - Name: &properties.NameVariant{ - Underscore: "c", - CamelCase: "C", - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - expectedNestedVariableName := `A.B.C` - params := []*properties.SpecParam{} - params = append(params, - spec.Params["a"], - spec.Params["a"].Spec.Params["b"], - spec.Params["a"].Spec.Params["b"].Spec.Params["c"], - ) - - // when - calculatedNestedVariableName := renderNestedVariableName(params, true, true, false) - - // then - assert.Equal(t, expectedNestedVariableName, calculatedNestedVariableName) -} - -func TestNestedVariableNameWithEntry(t *testing.T) { - // given - spec := properties.Spec{ - Params: map[string]*properties.SpecParam{ - "a": { - Name: &properties.NameVariant{ - Underscore: "a", - CamelCase: "A", - }, - Spec: &properties.Spec{ - Params: map[string]*properties.SpecParam{ - "b": { - Name: &properties.NameVariant{ - Underscore: "b", - CamelCase: "B", - }, - Type: "list", - Items: &properties.SpecParamItems{ - Type: "entry", - }, - Profiles: []*properties.SpecParamProfile{ - { - Xpath: []string{"test", "entry"}, - Type: "entry", - }, - }, - Spec: &properties.Spec{ - Params: map[string]*properties.SpecParam{ - "c": { - Name: &properties.NameVariant{ - Underscore: "c", - CamelCase: "C", - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - expectedNestedVariableName := `AB.C` - params := []*properties.SpecParam{} - params = append(params, - spec.Params["a"], - spec.Params["a"].Spec.Params["b"], - spec.Params["a"].Spec.Params["b"].Spec.Params["c"], - ) - - // when - calculatedNestedVariableName := renderNestedVariableName(params, true, true, false) - - // then - assert.Equal(t, expectedNestedVariableName, calculatedNestedVariableName) -} diff --git a/pkg/translate/funcs_test.go b/pkg/translate/funcs_test.go index cabccc0a..6310a2c5 100644 --- a/pkg/translate/funcs_test.go +++ b/pkg/translate/funcs_test.go @@ -12,10 +12,10 @@ func TestGenerateEntryXpath(t *testing.T) { // given // when - asEntryXpath, _ := GenerateEntryXpath("util.AsEntryXpath([]string{", "})", "DeviceGroup", "{{ Entry $panorama_device }}") + asEntryXpath, _ := GenerateEntryXpath("util.AsEntryXpath(", ")", "DeviceGroup", "{{ Entry $panorama_device }}") // then - assert.Equal(t, "util.AsEntryXpath([]string{o.DeviceGroup.PanoramaDevice}),", asEntryXpath) + assert.Equal(t, "util.AsEntryXpath(o.DeviceGroup.PanoramaDevice),", asEntryXpath) } func TestSpecMatchesFunction(t *testing.T) { diff --git a/pkg/translate/imports.go b/pkg/translate/imports.go index f6932929..8e854052 100644 --- a/pkg/translate/imports.go +++ b/pkg/translate/imports.go @@ -2,10 +2,11 @@ package translate import ( "github.com/paloaltonetworks/pan-os-codegen/pkg/imports" + "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" ) // RenderImports render string, which contains import required in entry, location or service template. -func RenderImports(templateTypes ...string) (string, error) { +func RenderImports(spec *properties.Normalization, templateTypes ...string) (string, error) { manager := imports.NewManager() for _, templateType := range templateTypes { @@ -30,6 +31,9 @@ func RenderImports(templateTypes ...string) (string, error) { manager.AddSdkImport("github.com/PaloAltoNetworks/pango/errors", "") manager.AddSdkImport("github.com/PaloAltoNetworks/pango/util", "") manager.AddSdkImport("github.com/PaloAltoNetworks/pango/version", "") + if spec.ResourceXpathVariablesWithChecks(true) { + manager.AddStandardImport("strings", "") + } case "service": manager.AddStandardImport("context", "") manager.AddStandardImport("fmt", "") diff --git a/pkg/translate/imports_test.go b/pkg/translate/imports_test.go index caf28de4..f9f81e1b 100644 --- a/pkg/translate/imports_test.go +++ b/pkg/translate/imports_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/stretchr/testify/assert" + + "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" ) func TestRenderImports(t *testing.T) { @@ -18,7 +20,13 @@ import ( )` // when - actualImports, _ := RenderImports("location") + spec := &properties.Normalization{ + PanosXpath: properties.PanosXpath{ + Path: []string{"test"}, + }, + } + + actualImports, _ := RenderImports(spec, "location") // then assert.NotNil(t, actualImports) diff --git a/pkg/translate/structs.go b/pkg/translate/structs.go index 21602424..1c584d97 100644 --- a/pkg/translate/structs.go +++ b/pkg/translate/structs.go @@ -12,6 +12,20 @@ import ( "github.com/paloaltonetworks/pan-os-codegen/pkg/version" ) +type structType string + +const ( + structXmlType structType = "xml" + structApiType structType = "api" +) + +var xmlNameVariant = &properties.NameVariant{ + Original: "xml-name", + LowerCamelCase: "xmlName", + CamelCase: "XMLName", + Underscore: "xml_name", +} + // LocationType function used in template location.tmpl to generate location type name. func LocationType(location *properties.Location, pointer bool) string { prefix := "" @@ -21,85 +35,6 @@ func LocationType(location *properties.Location, pointer bool) string { return fmt.Sprintf("%s%sLocation", prefix, location.Name.CamelCase) } -// NestedSpecs goes through all params and one ofs (recursively) and returns map of all nested specs. -func NestedSpecs(spec *properties.Spec) (map[string]NestedSpec, error) { - nestedSpecs := make(map[string]NestedSpec) - - fmt.Println("HELLO1") - if spec == nil { - panic("spec == nil") - } - - checkNestedSpecs([]string{}, spec, nestedSpecs) - fmt.Println("END1") - - return nestedSpecs, nil -} - -type NestedSpec struct { - ParentIsList bool - Spec *properties.Spec -} - -func checkNestedSpecs(parent []string, spec *properties.Spec, nestedSpecs map[string]NestedSpec) { - for _, param := range spec.SortedParams() { - paramKey := append(parent, param.Name.CamelCase) - updateNestedSpecs(paramKey, param, nestedSpecs) - if len(param.Profiles) > 0 && param.Profiles[0].Type == "entry" && param.Items != nil && param.Items.Type == "entry" { - nested, modified := addNameAsParamForNestedSpec(paramKey, nestedSpecs) - nested.ParentIsList = true - if modified { - nested.Spec.HackFixInjectedNameSpecOrder() - } - } - } - for _, param := range spec.SortedOneOf() { - paramKey := append(parent, param.Name.CamelCase) - updateNestedSpecs(paramKey, param, nestedSpecs) - if len(param.Profiles) > 0 && param.Profiles[0].Type == "entry" && param.Items != nil && param.Items.Type == "entry" { - nested, modified := addNameAsParamForNestedSpec(paramKey, nestedSpecs) - nested.ParentIsList = true - if modified { - nested.Spec.HackFixInjectedNameSpecOrder() - } - } - } -} - -func updateNestedSpecs(parent []string, param *properties.SpecParam, nestedSpecs map[string]NestedSpec) { - if param.Spec != nil { - nestedSpecs[strings.Join(parent, "")] = NestedSpec{ - Spec: param.Spec, - } - - checkNestedSpecs(parent, param.Spec, nestedSpecs) - } -} - -func addNameAsParamForNestedSpec(parent []string, nestedSpecs map[string]NestedSpec) (*NestedSpec, bool) { - nested := nestedSpecs[strings.Join(parent, "")] - if _, found := nested.Spec.Params["name"]; found { - return &nested, false - } - - nested.Spec.Params["name"] = &properties.SpecParam{ - Name: &properties.NameVariant{ - Underscore: "name", - CamelCase: "Name", - }, - SpecOrder: 0, - Type: "string", - Required: true, - Profiles: []*properties.SpecParamProfile{ - { - Xpath: []string{"name"}, - }, - }, - } - - return &nested, true -} - const importLocationStructTmpl = ` type ImportLocation interface { XpathForLocation(version.Number, util.ILocation) ([]string, error) @@ -301,7 +236,7 @@ func createImportLocationSpecsForLocation(location properties.ImportLocation) im for _, elt := range location.XpathElements { if strings.HasPrefix(elt, "$") { variableName := elt[1:] - asEntryXpath := fmt.Sprintf("util.AsEntryXpath([]string{o.%s})", variablesByName[variableName].Name.LowerCamelCase) + asEntryXpath := fmt.Sprintf("util.AsEntryXpath(o.%s)", variablesByName[variableName].Name.LowerCamelCase) elements = append(elements, asEntryXpath) } else { elements = append(elements, fmt.Sprintf("\"%s\"", elt)) @@ -378,24 +313,39 @@ func SpecParamType(parent string, param *properties.SpecParam) string { return fmt.Sprintf("%s%s", prefix, calculatedType) } -// XmlParamType return param type (it can be nested spec) (for struct based on spec from YAML files). -func XmlParamType(parent string, param *properties.SpecParam) string { - prefix := determinePrefix(param, true) - - calculatedType := "" - if param.Spec != nil { - calculatedType = calculateNestedXmlSpecType(parent, param) +// ParamType return param type (it can be nested spec) (for struct based on spec from YAML files). +func ParamType(structTyp structType, parentName *properties.NameVariant, param *properties.SpecParam, suffix string) string { + var calculatedType string + if param.Type == "" || isParamListAndProfileTypeIsExtendedEntry(param) { + typ := calculateNestedXmlSpecType(structTyp, parentName, param, suffix) + if structTyp == structXmlType { + calculatedType = typ.LowerCamelCase + } else { + calculatedType = typ.CamelCase + } } else if isParamListAndProfileTypeIsMember(param) { - calculatedType = "util.MemberType" + if structTyp == structXmlType { + calculatedType = "util.Member" + } else { + calculatedType = "string" + } } else if isParamListAndProfileTypeIsSingleEntry(param) { - calculatedType = "util.EntryType" - } else if param.Type == "bool" { + if structTyp == structXmlType { + calculatedType = "util.Entry" + } else { + calculatedType = calculateNestedXmlSpecType(structTyp, parentName, param, suffix).CamelCase + } + } else if param.Type == "bool" && structTyp == structXmlType { calculatedType = "string" } else { calculatedType = param.Type } - return fmt.Sprintf("%s%s", prefix, calculatedType) + return calculatedType +} + +func XmlParamType(parent string, param *properties.SpecParam) string { + return ParamType(structXmlType, properties.NewNameVariant(parent), param, "") } func determinePrefix(param *properties.SpecParam, useMemberOrEntryTypeStruct bool) string { @@ -422,13 +372,28 @@ func calculateNestedSpecType(parent string, param *properties.SpecParam) string return fmt.Sprintf("%s%s", parent, naming.CamelCase("", param.Name.CamelCase, "", true)) } -func calculateNestedXmlSpecType(parent string, param *properties.SpecParam) string { - return fmt.Sprintf("%s%sXml", parent, naming.CamelCase("", param.Name.CamelCase, "", true)) +func calculateNestedXmlSpecType(structTyp structType, parentName *properties.NameVariant, param *properties.SpecParam, suffix string) *properties.NameVariant { + var typ *properties.NameVariant + if parentName.IsEmpty() { + typ = param.Name + } else { + typ = parentName.WithSuffix(param.Name) + } + + if structTyp == structXmlType { + typ = typ.WithSuffix(properties.NewNameVariant("xml")).WithLiteralSuffix(suffix) + } + + return typ } // XmlName creates a string with xml name (e.g. `description`). func XmlName(param *properties.SpecParam) string { if len(param.Profiles) > 0 { + // FIXME: lists of objects have an extra "entry" element on their xpath + if param.Type == "list" && param.Items.Type == "entry" { + return param.Profiles[0].Xpath[0] + } return strings.Join(param.Profiles[0].Xpath, ">") } @@ -484,6 +449,14 @@ func CreateGoSuffixFromVersion(v *version.Version) string { return fmt.Sprintf("_%s", strings.ReplaceAll(v.String(), ".", "_")) } +func ParamNotSkippedTmpl(param *properties.SpecParam) bool { + if param.GoSdkConfig != nil && param.GoSdkConfig.Skip != nil { + return !*param.GoSdkConfig.Skip + } + + return true +} + func ParamSupportedInVersionTmpl(param *properties.SpecParam, deviceVersion any) (bool, error) { if deviceVersion == nil { return true, nil @@ -513,7 +486,7 @@ func checkIfDeviceVersionSupportedByProfile(param *properties.SpecParam, deviceV return true } - log.Printf("Param: %s, Version: %s, MinVersion: %s, MaxVersion: %s", param.Name.CamelCase, deviceVersion, profile.MinVersion.String(), profile.MaxVersion.String()) + log.Printf("Param: %s, deviceVersion: %s, MinVersion: %s, MaxVersion: %s", param.Name.CamelCase, deviceVersion, profile.MinVersion.String(), profile.MaxVersion.String()) if deviceVersion.GreaterThanOrEqualTo(*profile.MinVersion) && deviceVersion.LesserThan(*profile.MaxVersion) { return true @@ -521,3 +494,732 @@ func checkIfDeviceVersionSupportedByProfile(param *properties.SpecParam, deviceV } return false } + +type entryStructFieldContext struct { + Name *properties.NameVariant + IsInternal bool + Required bool + FieldType string + Type string + ItemsType string + XmlType string + XmlContainerType string + ItemsXmlType string + Tags string + version *version.Version +} + +func (o entryStructFieldContext) FinalType() string { + switch o.FieldType { + case "list-entry", "list-member": + return o.ItemsType + case "object", "simple": + if o.Required { + return o.Type + } else { + return "*" + o.Type + } + case "internal": + return o.Type + default: + panic(fmt.Sprintf("unreachable FieldType '%s' for '%s'", o.FieldType, o.Name.CamelCase)) + } +} + +func (o entryStructFieldContext) FinalXmlType() string { + switch o.FieldType { + case "list-entry": + if o.XmlContainerType != "" { + return "*" + o.XmlContainerType + } else { + return o.ItemsXmlType + } + case "list-member": + return "*" + o.ItemsXmlType + case "object", "simple": + if o.Required { + return o.XmlType + } else { + return "*" + o.XmlType + } + case "internal": + return o.XmlType + default: + panic(fmt.Sprintf("unreachable FieldType '%s' for '%s'", o.FieldType, o.Name.CamelCase)) + } +} + +type entryStructContext struct { + TopLevel bool + IsXmlContainer bool + Fields []entryStructFieldContext + + version *version.Version + name *properties.NameVariant +} + +func (o entryStructContext) versionSuffix() string { + if o.version == nil { + return "" + } + + return fmt.Sprintf("_%s", strings.ReplaceAll(o.version.String(), ".", "_")) +} + +func (o entryStructContext) StructName() string { + return o.name.CamelCase +} + +func (o entryStructContext) XmlStructName() string { + return o.name.LowerCamelCase + "Xml" + o.versionSuffix() +} + +func (o entryStructContext) XmlContainerStructName() string { + return o.name.LowerCamelCase + "XmlContainer" + o.versionSuffix() +} + +func (o entryStructContext) SpecifierFuncName(suffix string) string { + return "specify" + suffix + o.versionSuffix() +} + +func getTypesForParam(structTyp structType, parent *properties.NameVariant, param *properties.SpecParam, version *version.Version, overrideForXmlContainer bool) (string, string, string) { + var versionSuffix string + if version != nil { + versionSuffix = fmt.Sprintf("_%s", strings.ReplaceAll(version.String(), ".", "_")) + } + + if structTyp == structXmlType { + typ := ParamType(structXmlType, parent, param, versionSuffix) + var itemsType string + if param.Type == "list" && param.Items.Type == "string" { + itemsType = "util.MemberType" + } else if param.Type == "list" && param.Items.Type == "entry" { + itemsType = "[]" + typ + } + var xmlContainerType string + if overrideForXmlContainer { + xmlContainerType = parent.WithSuffix(param.Name).WithSuffix(properties.NewNameVariant("container")).WithSuffix(properties.NewNameVariant("xml")).WithLiteralSuffix(versionSuffix).LowerCamelCase + } + return typ, itemsType, xmlContainerType + } else { + typ := ParamType(structApiType, parent, param, "") + var itemsType string + if param.Type == "list" && param.Items.Type == "string" { + itemsType = "[]string" + } else if param.Type == "list" && param.Items.Type == "entry" { + itemsType = "[]" + typ + } + return typ, itemsType, "" + } +} + +func getFieldTypeForParam(param *properties.SpecParam) string { + if param.Type == "" { + return "object" + } + + if param.Type == "list" && param.Items.Type == "string" { + return "list-member" + } + + if param.Type == "list" && param.Items.Type == "entry" { + return "list-entry" + } + + return "simple" +} + +func createStructSpecForXmlListContainer(prefix *properties.NameVariant, param *properties.SpecParam, version *version.Version) []entryStructContext { + typ, itemsType, _ := getTypesForParam(structApiType, prefix, param, version, false) + xmlType, itemsXmlType, _ := getTypesForParam(structXmlType, prefix, param, version, false) + fieldType := "list-entry" + + fields := []entryStructFieldContext{ + { + Name: properties.NewNameVariant("entries"), + Required: false, + FieldType: fieldType, + Type: typ, + ItemsType: itemsType, + XmlType: xmlType, + ItemsXmlType: itemsXmlType, + Tags: "`xml:\"entry\"`", + version: version, + }, + } + + return []entryStructContext{{ + IsXmlContainer: true, + Fields: fields, + name: prefix.WithSuffix(param.Name).WithSuffix(properties.NewNameVariant("container")), + version: version, + }} +} + +func createEntryXmlStructSpecsForParameter(structTyp structType, parentPrefix *properties.NameVariant, param *properties.SpecParam, version *version.Version) []entryStructContext { + var fields []entryStructFieldContext + var entries []entryStructContext + + if param.Type == "list" && param.Items.Type == "entry" { + if structTyp == structXmlType { + fields = append(fields, entryStructFieldContext{ + IsInternal: true, + FieldType: "internal", + Name: xmlNameVariant, + XmlType: "xml.Name", + Tags: "`xml:\"entry\"`", + }) + } + fields = append(fields, entryStructFieldContext{ + Name: properties.NewNameVariant("name"), + Required: true, + FieldType: "simple", + Type: "string", + XmlType: "string", + Tags: "`xml:\"name,attr\"`", + }) + } + + processParameter := func(prefix *properties.NameVariant, param *properties.SpecParam) { + if param.GoSdkConfig != nil && param.GoSdkConfig.Skip != nil && *param.GoSdkConfig.Skip { + return + } + + if !ParamSupportedInVersion(param, version) { + return + } + + var overrideTypeForXmlContainer bool + if structTyp == structXmlType && param.Type == "list" && param.Items.Type == "entry" { + overrideTypeForXmlContainer = true + entries = append(entries, createStructSpecForXmlListContainer(prefix, param, version)...) + } + + typ, itemsType, _ := getTypesForParam(structApiType, prefix, param, version, overrideTypeForXmlContainer) + xmlType, itemsXmlType, xmlContainerType := getTypesForParam(structXmlType, prefix, param, version, overrideTypeForXmlContainer) + fieldType := getFieldTypeForParam(param) + + fields = append(fields, entryStructFieldContext{ + Name: param.Name, + Required: param.Required, + FieldType: fieldType, + Type: typ, + ItemsType: itemsType, + XmlType: xmlType, + XmlContainerType: xmlContainerType, + ItemsXmlType: itemsXmlType, + Tags: XmlTag(param), + version: version, + }) + + if param.Type == "" || (param.Type == "list" && param.Items.Type == "entry") { + entries = append(entries, createEntryXmlStructSpecsForParameter(structTyp, prefix, param, version)...) + } + + } + + prefixName := parentPrefix.WithSuffix(param.Name) + for _, elt := range param.Spec.SortedParams() { + processParameter(prefixName, elt) + } + + for _, elt := range param.Spec.SortedOneOf() { + processParameter(prefixName, elt) + } + + fields = append(fields, entryStructFieldContext{ + Name: properties.NewNameVariant("misc"), + FieldType: "internal", + Type: "[]generic.Xml", + XmlType: "[]generic.Xml", + Tags: "`xml:\",any\"`", + }) + + name := parentPrefix.WithSuffix(param.Name) + entries = append([]entryStructContext{{ + Fields: fields, + name: name, + version: version, + }}, entries...) + + return entries +} + +func creasteStructSpecsForNormalization(structTyp structType, parentPrefix *properties.NameVariant, spec *properties.Normalization, version *version.Version) []entryStructContext { + var entries []entryStructContext + var fields []entryStructFieldContext + + if structTyp == structXmlType { + var xmlTags string + switch spec.TerraformProviderConfig.ResourceType { + case properties.TerraformResourceEntry, properties.TerraformResourceUuid: + xmlTags = "`xml:\"entry\"`" + case properties.TerraformResourceConfig: + xmlTags = "`xml:\"system\"`" + case properties.TerraformResourceCustom: + fallthrough + default: + panic(fmt.Sprintf("unreachable resource type: '%s'", spec.TerraformProviderConfig.ResourceType)) + } + + fields = append(fields, entryStructFieldContext{ + IsInternal: true, + FieldType: "internal", + Name: xmlNameVariant, + XmlType: "xml.Name", + Tags: xmlTags, + }) + } + + switch spec.TerraformProviderConfig.ResourceType { + case properties.TerraformResourceEntry, properties.TerraformResourceUuid: + fields = append(fields, entryStructFieldContext{ + Name: properties.NewNameVariant("name"), + Required: true, + FieldType: "simple", + Type: "string", + XmlType: "string", + Tags: "`xml:\"name,attr\"`", + }) + case properties.TerraformResourceConfig: + case properties.TerraformResourceCustom: + fallthrough + default: + panic(fmt.Sprintf("unreachable resource type: '%s'", spec.TerraformProviderConfig.ResourceType)) + } + + processParameter := func(prefix *properties.NameVariant, param *properties.SpecParam) { + if param.GoSdkConfig != nil && param.GoSdkConfig.Skip != nil && *param.GoSdkConfig.Skip { + return + } + + if !ParamSupportedInVersion(param, version) { + return + } + + var overrideTypeForXmlContainer bool + if structTyp == structXmlType && param.Type == "list" && param.Items.Type == "entry" { + overrideTypeForXmlContainer = true + entries = append(entries, createStructSpecForXmlListContainer(prefix, param, version)...) + } + + typ, itemsType, _ := getTypesForParam(structApiType, prefix, param, version, overrideTypeForXmlContainer) + xmlType, itemsXmlType, xmlContainerType := getTypesForParam(structXmlType, prefix, param, version, overrideTypeForXmlContainer) + fieldType := getFieldTypeForParam(param) + + fields = append(fields, entryStructFieldContext{ + Name: param.Name, + Required: param.Required, + FieldType: fieldType, + Type: typ, + ItemsType: itemsType, + XmlType: xmlType, + XmlContainerType: xmlContainerType, + ItemsXmlType: itemsXmlType, + Tags: XmlTag(param), + version: version, + }) + + if param.Type == "" || (param.Type == "list" && param.Items.Type == "entry") { + entries = append(entries, createEntryXmlStructSpecsForParameter(structTyp, properties.NewNameVariant(""), param, version)...) + } + } + + for _, elt := range spec.Spec.SortedParams() { + processParameter(parentPrefix, elt) + } + + for _, elt := range spec.Spec.SortedOneOf() { + processParameter(parentPrefix, elt) + } + + fields = append(fields, entryStructFieldContext{ + Name: properties.NewNameVariant("misc"), + FieldType: "internal", + Type: "[]generic.Xml", + XmlType: "[]generic.Xml", + Tags: "`xml:\",any\"`", + }) + + var name *properties.NameVariant + switch spec.TerraformProviderConfig.ResourceType { + case properties.TerraformResourceEntry, properties.TerraformResourceUuid: + name = properties.NewNameVariant("entry") + case properties.TerraformResourceConfig: + name = properties.NewNameVariant("config") + case properties.TerraformResourceCustom: + fallthrough + default: + panic(fmt.Sprintf("unreachable resource type: %v", spec.TerraformProviderConfig.ResourceType)) + } + + entries = append([]entryStructContext{{ + TopLevel: true, + Fields: fields, + name: name, + version: version, + }}, entries...) + + return entries +} + +func createStructSpecs(structTyp structType, spec *properties.Normalization, version *version.Version) []entryStructContext { + return creasteStructSpecsForNormalization(structTyp, properties.NewNameVariant(""), spec, version) +} + +const apiStructsTmpl = ` +{{- range .Specs }} +{{- $spec := . }} +type {{ .StructName }} struct{ + {{- range .Fields }} + {{- if .IsInternal }}{{ continue }}{{ end }} + {{ .Name.CamelCase }} {{ .FinalType }} + {{- end }} +} +{{- end }} +` + +func RenderEntryApiStructs(spec *properties.Normalization) (string, error) { + tmpl := template.Must(template.New("render-entry-api-structs").Parse(apiStructsTmpl)) + + specs := createStructSpecs(structApiType, spec, nil) + type context struct { + Specs []entryStructContext + } + + data := context{Specs: specs} + + var builder strings.Builder + if err := tmpl.Execute(&builder, data); err != nil { + return "", err + } + + return builder.String(), nil +} + +const xmlStructsTmpl = ` +{{- range .Specs }} +{{- $spec := . }} +type {{ .XmlStructName }} struct{ + {{- range .Fields }} + {{ .Name.CamelCase }} {{ .FinalXmlType }} {{ .Tags }} + {{- end }} +} +{{- end }} +` + +func RenderEntryXmlStructs(spec *properties.Normalization) (string, error) { + tmpl := template.Must(template.New("render-entry-xml-structs").Parse(xmlStructsTmpl)) + + specs := createStructSpecs(structXmlType, spec, nil) + for _, elt := range spec.SupportedVersionRanges() { + specs = append(specs, createStructSpecs(structXmlType, spec, &elt.Minimum)...) + } + + type context struct { + Specs []entryStructContext + } + + data := context{Specs: specs} + + var builder strings.Builder + if err := tmpl.Execute(&builder, data); err != nil { + return "", err + } + + return builder.String(), nil +} + +const structToXmlMarshalersTmpl = ` +{{- range .Specs }} + {{- if .IsXmlContainer }}{{ continue }}{{ end }} +func (o *{{ .XmlStructName }}) MarshalFromObject(s {{ .StructName }}) { + {{- range .Fields }} + {{- if .IsInternal }}{{ continue }}{{- end }} + {{- if eq .FieldType "object" }} + if s.{{ .Name.CamelCase }} != nil { + var obj {{ .XmlType }} + obj.MarshalFromObject(*s.{{ .Name.CamelCase }}) + o.{{ .Name.CamelCase }} = &obj + } + {{- else if eq .FieldType "list-member" }} + if s.{{ .Name.CamelCase }} != nil { + o.{{ .Name.CamelCase }} = util.StrToMem(s.{{ .Name.CamelCase }}) + } + {{- else if eq .FieldType "list-entry" }} + if s.{{ .Name.CamelCase }} != nil { + var objs {{ .ItemsXmlType }} + for _, elt := range s.{{ .Name.CamelCase }} { + var obj {{ .XmlType }} + obj.MarshalFromObject(elt) + objs = append(objs, obj) + } + o.{{ .Name.CamelCase }} = &{{ .XmlContainerType }}{ Entries: objs } + } + {{- else if and (eq .FieldType "simple") (eq .Type "bool") }} + o.{{ .Name.CamelCase }} = util.YesNo(s.{{ .Name.CamelCase }}, nil) + {{- else }} + o.{{ .Name.CamelCase }} = s.{{ .Name.CamelCase }} + {{- end }} + {{- end }} +} + +func (o {{ .XmlStructName }}) UnmarshalToObject() *{{ .StructName }} { + {{- range .Fields }} + {{- if .IsInternal }}{{ continue }}{{- end }} + {{- if eq .FieldType "object" }} + var {{ .Name.LowerCamelCase }}Val {{ .FinalType }} + if o.{{ .Name.CamelCase }} != nil { + {{ .Name.LowerCamelCase }}Val = o.{{ .Name.CamelCase }}.UnmarshalToObject() + } + {{- else if eq .FieldType "list-member" }} + var {{ .Name.LowerCamelCase }}Val {{ .FinalType }} + if o.{{ .Name.CamelCase }} != nil { + {{ .Name.LowerCamelCase }}Val = util.MemToStr(o.{{ .Name.CamelCase }}) + } + {{- else if eq .FieldType "list-entry" }} + var {{ .Name.LowerCamelCase }}Val {{ .FinalType }} + if o.{{ .Name.CamelCase }} != nil { + for _, elt := range o.{{ .Name.CamelCase }}.Entries { + {{ .Name.LowerCamelCase }}Val = append({{ .Name.LowerCamelCase }}Val, *elt.UnmarshalToObject()) + } + } + {{- end }} + {{- end }} + + result := &{{ .StructName }}{ + {{- range .Fields }} + {{- if .IsInternal }}{{- continue }}{{- end }} + {{- if or (eq .FieldType "list-member") (eq .FieldType "list-entry") (eq .FieldType "object") }} + {{ .Name.CamelCase }}: {{ .Name.LowerCamelCase }}Val, + {{- else if and (eq .FieldType "simple") (eq .Type "bool") }} + {{ .Name.CamelCase }}: util.AsBool(o.{{ .Name.CamelCase }}, nil), + {{- else }} + {{ .Name.CamelCase }}: o.{{ .Name.CamelCase }}, + {{- end }} + {{- end }} + } + return result +} +{{- end }} +` + +func RenderToXmlMarshalers(spec *properties.Normalization) (string, error) { + tmpl := template.Must(template.New("render-to-xml-marsrhallers").Parse(structToXmlMarshalersTmpl)) + + specs := createStructSpecs(structXmlType, spec, nil) + for _, elt := range spec.SupportedVersionRanges() { + specs = append(specs, createStructSpecs(structXmlType, spec, &elt.Minimum)...) + } + type context struct { + EntryOrConfig string + Specs []entryStructContext + } + + entryOrConfig := "Entry" + if spec.TerraformProviderConfig.ResourceType == properties.TerraformResourceConfig { + entryOrConfig = "Config" + } + + data := context{ + EntryOrConfig: entryOrConfig, + Specs: specs, + } + + var builder strings.Builder + if err := tmpl.Execute(&builder, data); err != nil { + return "", err + } + + return builder.String(), nil +} + +const xmlContainerNormalizersTmpl = ` +{{- range .Specs }} +{{- if not .TopLevel }}{{ continue }}{{ end }} +func (o *{{ .XmlContainerStructName }}) Normalize() ([]*{{ $.EntryOrConfig }}, error) { + entries := make([]*{{ $.EntryOrConfig }}, 0, len(o.Answer)) + for _, elt := range o.Answer { + entries = append(entries, elt.UnmarshalToObject()) + } + + return entries, nil +} +{{- end }} +` + +func RenderXmlContainerNormalizers(spec *properties.Normalization) (string, error) { + tmpl := template.Must(template.New("render-xml-container-normalizers").Parse(xmlContainerNormalizersTmpl)) + + specs := createStructSpecs(structXmlType, spec, nil) + for _, elt := range spec.SupportedVersionRanges() { + specs = append(specs, createStructSpecs(structXmlType, spec, &elt.Minimum)...) + } + type context struct { + EntryOrConfig string + Specs []entryStructContext + } + + entryOrConfig := "Entry" + if spec.TerraformProviderConfig.ResourceType == properties.TerraformResourceConfig { + entryOrConfig = "Config" + } + + data := context{ + EntryOrConfig: entryOrConfig, + Specs: specs, + } + + var builder strings.Builder + if err := tmpl.Execute(&builder, data); err != nil { + return "", err + } + + return builder.String(), nil +} + +const xmlContainerSpecifiersTmpl = ` +{{- range .Specs }} +{{- if not .TopLevel }}{{ continue }}{{ end }} +func {{ .SpecifierFuncName $.EntryOrConfig }}(source *{{ $.EntryOrConfig }}) (any, error) { + var obj {{ .XmlStructName }} + obj.MarshalFromObject(*source) + return obj, nil +} +{{- end }} +` + +func RenderXmlContainerSpecifiers(spec *properties.Normalization) (string, error) { + tmpl := template.Must(template.New("render-xml-container-specifiers").Parse(xmlContainerSpecifiersTmpl)) + + specs := createStructSpecs(structXmlType, spec, nil) + for _, elt := range spec.SupportedVersionRanges() { + specs = append(specs, createStructSpecs(structXmlType, spec, &elt.Minimum)...) + } + type context struct { + EntryOrConfig string + Specs []entryStructContext + } + + entryOrConfig := "Entry" + if spec.TerraformProviderConfig.ResourceType == properties.TerraformResourceConfig { + entryOrConfig = "Config" + } + + data := context{ + EntryOrConfig: entryOrConfig, + Specs: specs, + } + + var builder strings.Builder + if err := tmpl.Execute(&builder, data); err != nil { + return "", err + } + + return builder.String(), nil +} + +const specMatchersTmpl = ` +func SpecMatches(a, b *{{ .EntryOrConfig }}) bool { + if a == nil && b == nil { + return true + } + + if (a == nil && b != nil) || (a != nil && b == nil) { + return false + } + + return a.matches(b) +} + +{{- range .Specs }} + {{ if .IsXmlContainer }}{{ continue }}{{ end }} + {{ $spec := . }} +func (o *{{ .StructName }}) matches(other *{{ .StructName }}) bool { + if o == nil && other == nil { + return true + } + + if (o == nil && other != nil) || (o != nil && other == nil) { + return false + } + + {{- range .Fields }} + {{- if .IsInternal }}{{ continue }}{{ end }} + {{- if and $spec.TopLevel (eq .Name.CamelCase "Name") }}{{ continue }}{{ end }} + {{- if eq .Name.CamelCase "Misc" }}{{ continue }}{{ end }} + {{- if eq .FieldType "object" }} + if !o.{{ .Name.CamelCase }}.matches(other.{{ .Name.CamelCase }}) { + return false + } + {{- else if eq .FieldType "list-entry" }} + if len(o.{{ .Name.CamelCase }}) != len(other.{{ .Name.CamelCase }}) { + return false + } + for idx := range o.{{ .Name.CamelCase }} { + if !o.{{ .Name.CamelCase }}[idx].matches(&other.{{ .Name.CamelCase }}[idx]) { + return false + } + } + {{- else if eq .FieldType "list-member" }} + if !util.OrderedListsMatch(o.{{ .Name.CamelCase}}, other.{{ .Name.CamelCase }}) { + return false + } + {{- else if and (eq .Type "string") (eq .Required false)}} + if !util.StringsMatch(o.{{ .Name.CamelCase }}, other.{{ .Name.CamelCase }}) { + return false + } + {{- else if and (eq .Type "int64") (eq .Required false)}} + if !util.Ints64Match(o.{{ .Name.CamelCase }}, other.{{ .Name.CamelCase }}) { + return false + } + {{- else if and (eq .Type "int64") (eq .Required false)}} + if !util.Ints64Match(o.{{ .Name.CamelCase }}, other.{{ .Name.CamelCase }}) { + return false + } + {{- else if and (eq .Type "bool") (eq .Required false)}} + if !util.BoolsMatch(o.{{ .Name.CamelCase }}, other.{{ .Name.CamelCase }}) { + return false + } + {{- else if and (eq .Type "float64") (eq .Required false)}} + if !util.FloatsMatch(o.{{ .Name.CamelCase }}, other.{{ .Name.CamelCase }}) { + return false + } + {{- else }} + if o.{{ .Name.CamelCase }} != other.{{ .Name.CamelCase }} { + return false + } + {{- end }} + {{- end }} + + return true +} +{{- end }} +` + +func RenderSpecMatchers(spec *properties.Normalization) (string, error) { + tmpl := template.Must(template.New("render-spec-matchers").Parse(specMatchersTmpl)) + + specs := createStructSpecs(structApiType, spec, nil) + type context struct { + EntryOrConfig string + Specs []entryStructContext + } + + entryOrConfig := "Entry" + if spec.TerraformProviderConfig.ResourceType == properties.TerraformResourceConfig { + entryOrConfig = "Config" + } + + data := context{ + EntryOrConfig: entryOrConfig, + Specs: specs, + } + + var builder strings.Builder + if err := tmpl.Execute(&builder, data); err != nil { + return "", err + } + + return builder.String(), nil +} diff --git a/pkg/translate/structs_test.go b/pkg/translate/structs_test.go index f8b94c3d..a3c9daa7 100644 --- a/pkg/translate/structs_test.go +++ b/pkg/translate/structs_test.go @@ -4,6 +4,8 @@ import ( "os" "testing" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" "github.com/stretchr/testify/assert" "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" @@ -82,39 +84,912 @@ func TestOmitEmpty(t *testing.T) { assert.Contains(t, omitEmptyLocations, "") } -func TestXmlParamType(t *testing.T) { - // given - paramTypeRequiredString := properties.SpecParam{ - Type: "string", - Required: true, - Profiles: []*properties.SpecParamProfile{ - { - Type: "string", - Xpath: []string{"description"}, +var _ = Describe("ParamType", func() { + Context("When generating XML types for basic types", func() { + structTyp := structXmlType + parent := properties.NewNameVariant("") + suffix := "" + Context("when parameter is not a list", func() { + It("should return a normal go type for required params", func() { + param := &properties.SpecParam{ + Name: properties.NewNameVariant("test-param"), + Required: true, + Type: "string", + } + Expect(ParamType(structTyp, parent, param, suffix)).To(Equal("string")) + }) + It("should return a pointer to a go type for optional params", func() { + param := &properties.SpecParam{ + Name: properties.NewNameVariant("test-param"), + Required: false, + Type: "string", + } + Expect(ParamType(structTyp, parent, param, suffix)).To(Equal("string")) + }) + It("should return string as type for properties with bool type", func() { + param := &properties.SpecParam{ + Name: properties.NewNameVariant("test-param"), + Required: false, + Type: "bool", + } + Expect(ParamType(structTyp, parent, param, suffix)).To(Equal("string")) + }) + }) + Context("when parameter is a list", func() { + It("should return a custom pango type for member lists", func() { + param := &properties.SpecParam{ + Name: properties.NewNameVariant("test-param"), + Required: true, + Type: "list", + Profiles: []*properties.SpecParamProfile{{Type: "member"}}, + Items: &properties.SpecParamItems{ + Type: "string", + }, + } + Expect(ParamType(structTyp, parent, param, suffix)).To(Equal("util.Member")) + }) + It("should return a custom pango type for optional member lists", func() { + param := &properties.SpecParam{ + Name: properties.NewNameVariant("test-param"), + Required: false, + Type: "list", + Profiles: []*properties.SpecParamProfile{{Type: "member"}}, + Items: &properties.SpecParamItems{ + Type: "string", + }, + } + Expect(ParamType(structTyp, parent, param, suffix)).To(Equal("util.Member")) + }) + }) + }) + Context("When generating XML types for custom types", func() { + structTyp := structXmlType + Context("when parent is an empty name", func() { + parent := properties.NewNameVariant("") + Context("and suffix is empty", func() { + suffix := "" + It("should return a correct XML custom type", func() { + param := &properties.SpecParam{ + Name: properties.NewNameVariant("test-param"), + Required: true, + Type: "", + } + Expect(ParamType(structTyp, parent, param, suffix)).To(Equal("testParamXml")) + }) + }) + Context("and suffix is non-empty", func() { + suffix := "_10_2" + It("should return a correct XML custom type", func() { + param := &properties.SpecParam{ + Name: properties.NewNameVariant("test-param"), + Required: true, + Type: "", + } + Expect(ParamType(structTyp, parent, param, suffix)).To(Equal("testParamXml_10_2")) + }) + }) + }) + Context("when parent is non-empty name", func() { + parent := properties.NewNameVariant("parent-param") + Context("and suffix is empty", func() { + suffix := "" + It("should return a correct XML custom type prefixed with parent name", func() { + param := &properties.SpecParam{ + Name: properties.NewNameVariant("test-param"), + Required: true, + Type: "", + } + Expect(ParamType(structTyp, parent, param, suffix)).To(Equal("parentParamTestParamXml")) + }) + }) + Context("and suffix is non-empty", func() { + suffix := "_10_2" + It("should return a correct XML custom type", func() { + param := &properties.SpecParam{ + Name: properties.NewNameVariant("test-param"), + Required: true, + Type: "", + } + Expect(ParamType(structTyp, parent, param, suffix)).To(Equal("parentParamTestParamXml_10_2")) + }) + }) + }) + }) + Context("When generating XML types for custom type lists", func() { + structTyp := structXmlType + Context("when parent is an empty name", func() { + parent := properties.NewNameVariant("") + Context("and suffix is empty", func() { + suffix := "" + It("should return a correct XML custom type", func() { + param := &properties.SpecParam{ + Name: properties.NewNameVariant("test-param"), + Required: true, + Type: "list", + Profiles: []*properties.SpecParamProfile{{Type: "entry"}}, + Items: &properties.SpecParamItems{ + Type: "object", + }, + } + Expect(ParamType(structTyp, parent, param, suffix)).To(Equal("testParamXml")) + }) + }) + Context("and suffix is non-empty", func() { + suffix := "_10_2" + It("should return a correct XML custom type", func() { + param := &properties.SpecParam{ + Name: properties.NewNameVariant("test-param"), + Required: true, + Type: "list", + Profiles: []*properties.SpecParamProfile{{Type: "entry"}}, + Items: &properties.SpecParamItems{ + Type: "object", + }, + } + Expect(ParamType(structTyp, parent, param, suffix)).To(Equal("testParamXml_10_2")) + }) + }) + }) + Context("when parent is non-empty name", func() { + parent := properties.NewNameVariant("parent-param") + Context("and suffix is empty", func() { + suffix := "" + It("should return a correct XML custom type prefixed with parent name", func() { + param := &properties.SpecParam{ + Name: properties.NewNameVariant("test-param"), + Required: true, + Type: "list", + Profiles: []*properties.SpecParamProfile{{Type: "entry"}}, + Items: &properties.SpecParamItems{ + Type: "object", + }, + } + Expect(ParamType(structTyp, parent, param, suffix)).To(Equal("parentParamTestParamXml")) + }) + }) + Context("and suffix is non-empty", func() { + suffix := "_10_2" + It("should return a correct XML custom type", func() { + param := &properties.SpecParam{ + Name: properties.NewNameVariant("test-param"), + Required: true, + Type: "list", + Profiles: []*properties.SpecParamProfile{{Type: "entry"}}, + Items: &properties.SpecParamItems{ + Type: "object", + }, + } + Expect(ParamType(structTyp, parent, param, suffix)).To(Equal("parentParamTestParamXml_10_2")) + }) + }) + }) + }) +}) + +var _ = Describe("createEntryXmlStructSpecsForParameter", func() { + Context("when creating struct context for a parameter", func() { + It("should return a proper struct context", func() { + parent := properties.NewNameVariant("parent-param") + param := &properties.SpecParam{ + Name: properties.NewNameVariant("child-param"), + Type: "", + Profiles: []*properties.SpecParamProfile{{Type: "entry", Xpath: []string{"child-param"}}}, + Spec: &properties.Spec{ + Params: map[string]*properties.SpecParam{ + "grandchild-param": { + Name: properties.NewNameVariant("grandchild-param"), + Type: "", + Profiles: []*properties.SpecParamProfile{{Type: "entry", Xpath: []string{"grandchild-param"}}}, + Spec: &properties.Spec{}, + }, + }, + }, + } + + result := createEntryXmlStructSpecsForParameter(structXmlType, parent, param, nil) + Expect(result).To(HaveLen(2)) + + Expect(result[0].StructName()).To(Equal("ParentParamChildParam")) + Expect(result[0].XmlStructName()).To(Equal("parentParamChildParamXml")) + Expect(result[0].Fields[0]).To(Equal(entryStructFieldContext{ + Name: properties.NewNameVariant("grandchild-param"), + FieldType: "object", + Type: "ParentParamChildParamGrandchildParam", + XmlType: "parentParamChildParamGrandchildParamXml", + Tags: "`xml:\"grandchild-param,omitempty\"`", + })) + Expect(result[0].Fields[0].FinalType()).To(Equal("*ParentParamChildParamGrandchildParam")) + Expect(result[0].Fields[0].FinalXmlType()).To(Equal("*parentParamChildParamGrandchildParamXml")) + Expect(result[1].StructName()).To(Equal("ParentParamChildParamGrandchildParam")) + Expect(result[1].XmlStructName()).To(Equal("parentParamChildParamGrandchildParamXml")) + }) + }) + Context("when creating struct context for a simple type list parameter", func() { + It("should return a proper struct context", func() { + parent := properties.NewNameVariant("") + param := &properties.SpecParam{ + Name: properties.NewNameVariant("parent-param"), + Type: "", + Profiles: []*properties.SpecParamProfile{{Type: "entry", Xpath: []string{"child-param"}}}, + Spec: &properties.Spec{ + Params: map[string]*properties.SpecParam{ + "child-param": { + Name: properties.NewNameVariant("child-param"), + Type: "list", + Profiles: []*properties.SpecParamProfile{{Type: "member", Xpath: []string{"child-param"}}}, + Items: &properties.SpecParamItems{ + Type: "string", + }, + }, + }, + }, + } + + result := createEntryXmlStructSpecsForParameter(structXmlType, parent, param, nil) + Expect(result).To(HaveLen(1)) + + Expect(result[0].StructName()).To(Equal("ParentParam")) + Expect(result[0].XmlStructName()).To(Equal("parentParamXml")) + Expect(result[0].Fields[0]).To(Equal(entryStructFieldContext{ + Name: properties.NewNameVariant("child-param"), + FieldType: "list-member", + Type: "string", + ItemsType: "[]string", + XmlType: "util.Member", + ItemsXmlType: "util.MemberType", + Tags: "`xml:\"child-param,omitempty\"`", + })) + }) + }) + Context("when creating struct context for a complex type list parameter", func() { + It("should return a proper struct context", func() { + parent := properties.NewNameVariant("") + param := &properties.SpecParam{ + Name: properties.NewNameVariant("parent-param"), + Type: "", + Profiles: []*properties.SpecParamProfile{{Type: "entry", Xpath: []string{"child-param"}}}, + Spec: &properties.Spec{ + Params: map[string]*properties.SpecParam{ + "child-param": { + Name: properties.NewNameVariant("child-param"), + SpecOrder: 0, + Type: "list", + Profiles: []*properties.SpecParamProfile{{Type: "entry", Xpath: []string{"child-param"}}}, + Items: &properties.SpecParamItems{ + Type: "entry", + }, + Spec: &properties.Spec{ + Params: map[string]*properties.SpecParam{ + "grandchild-param": { + Name: properties.NewNameVariant("grandchild-param"), + Type: "int64", + Profiles: []*properties.SpecParamProfile{{Xpath: []string{"grandchild-param"}}}, + }, + }, + }, + }, + }, + }, + } + + result := createEntryXmlStructSpecsForParameter(structXmlType, parent, param, nil) + Expect(result).To(HaveLen(3)) + + Expect(result[0].StructName()).To(Equal("ParentParam")) + Expect(result[0].XmlStructName()).To(Equal("parentParamXml")) + Expect(result[0].Fields[0]).To(Equal(entryStructFieldContext{ + Name: properties.NewNameVariant("child-param"), + FieldType: "list-entry", + Type: "ParentParamChildParam", + ItemsType: "[]ParentParamChildParam", + XmlType: "parentParamChildParamXml", + XmlContainerType: "parentParamChildParamContainerXml", + ItemsXmlType: "[]parentParamChildParamXml", + Tags: "`xml:\"child-param,omitempty\"`", + })) + Expect(result[0].Fields[0].FinalXmlType()).To(Equal("*parentParamChildParamContainerXml")) + + Expect(result[1].IsXmlContainer).To(BeTrue()) + Expect(result[1].XmlStructName()).To(Equal("parentParamChildParamContainerXml")) + Expect(result[1].Fields).To(HaveExactElements([]entryStructFieldContext{ + { + Name: properties.NewNameVariant("entries"), + FieldType: "list-entry", + Type: "ParentParamChildParam", + ItemsType: "[]ParentParamChildParam", + XmlType: "parentParamChildParamXml", + ItemsXmlType: "[]parentParamChildParamXml", + Tags: "`xml:\"entry\"`", + }, + })) + + Expect(result[2].StructName()).To(Equal("ParentParamChildParam")) + Expect(result[2].XmlStructName()).To(Equal("parentParamChildParamXml")) + Expect(result[2].Fields).To(HaveExactElements([]entryStructFieldContext{ + { + Name: xmlNameVariant, + IsInternal: true, + Required: false, + FieldType: "internal", + Type: "", + ItemsType: "", + XmlType: "xml.Name", + ItemsXmlType: "", + Tags: "`xml:\"entry\"`", + }, + { + Name: properties.NewNameVariant("name"), + Required: true, + FieldType: "simple", + Type: "string", + ItemsType: "", + XmlType: "string", + ItemsXmlType: "", + Tags: "`xml:\"name,attr\"`", + }, + { + Name: properties.NewNameVariant("grandchild-param"), + FieldType: "simple", + Type: "int64", + ItemsType: "", + XmlType: "int64", + ItemsXmlType: "", + Tags: "`xml:\"grandchild-param,omitempty\"`", + }, + { + Name: properties.NewNameVariant("misc"), + FieldType: "internal", + Type: "[]generic.Xml", + XmlType: "[]generic.Xml", + Tags: "`xml:\",any\"`", + }, + })) + }) + }) + Context("when creating struct context for parameter with versioned children", func() { + parent := properties.NewNameVariant("") + It("should return all specs when nil version is passed", func() { + param := &properties.SpecParam{ + Name: properties.NewNameVariant("parent-param"), + Type: "", + Profiles: []*properties.SpecParamProfile{{Type: "entry", Xpath: []string{"child-param"}}}, + Spec: &properties.Spec{ + Params: map[string]*properties.SpecParam{ + "child-param1": { + Name: properties.NewNameVariant("child-param1"), + SpecOrder: 0, + Type: "", + Profiles: []*properties.SpecParamProfile{ + { + Type: "entry", + Xpath: []string{"child-param1"}, + }, + }, + Spec: &properties.Spec{}, + }, + "child-param2": { + Name: properties.NewNameVariant("child-param2"), + SpecOrder: 1, + Type: "", + Profiles: []*properties.SpecParamProfile{ + { + Type: "entry", + Xpath: []string{"child-param2"}, + }, + }, + Spec: &properties.Spec{}, + }, + }, + }, + } + + result := createEntryXmlStructSpecsForParameter(structXmlType, parent, param, nil) + Expect(result).To(HaveLen(3)) + + Expect(result[0].StructName()).To(Equal("ParentParam")) + Expect(result[0].XmlStructName()).To(Equal("parentParamXml")) + Expect(result[0].Fields).To(HaveExactElements([]entryStructFieldContext{ + { + Name: properties.NewNameVariant("child-param1"), + FieldType: "object", + Type: "ParentParamChildParam1", + XmlType: "parentParamChildParam1Xml", + Tags: "`xml:\"child-param1,omitempty\"`", + }, + { + Name: properties.NewNameVariant("child-param2"), + FieldType: "object", + Type: "ParentParamChildParam2", + XmlType: "parentParamChildParam2Xml", + Tags: "`xml:\"child-param2,omitempty\"`", + }, + { + Name: properties.NewNameVariant("misc"), + FieldType: "internal", + Type: "[]generic.Xml", + XmlType: "[]generic.Xml", + Tags: "`xml:\",any\"`", + }, + })) + + Expect(result[0].Fields[0].FinalType()).To(Equal("*ParentParamChildParam1")) + Expect(result[0].Fields[0].FinalXmlType()).To(Equal("*parentParamChildParam1Xml")) + + Expect(result[0].Fields[1].FinalType()).To(Equal("*ParentParamChildParam2")) + Expect(result[0].Fields[1].FinalXmlType()).To(Equal("*parentParamChildParam2Xml")) + + Expect(result[1].StructName()).To(Equal("ParentParamChildParam1")) + Expect(result[1].XmlStructName()).To(Equal("parentParamChildParam1Xml")) + Expect(result[2].StructName()).To(Equal("ParentParamChildParam2")) + Expect(result[2].XmlStructName()).To(Equal("parentParamChildParam2Xml")) + }) + It("should only return relevant specs when version is non-nill", func() { + param := &properties.SpecParam{ + Name: properties.NewNameVariant("parent-param"), + Type: "", + Profiles: []*properties.SpecParamProfile{{Type: "entry", Xpath: []string{"child-param"}}}, + Spec: &properties.Spec{ + Params: map[string]*properties.SpecParam{ + "child-param1": { + Name: properties.NewNameVariant("child-param1"), + SpecOrder: 0, + Type: "", + Profiles: []*properties.SpecParamProfile{ + { + Type: "entry", + Xpath: []string{"child-param1"}, + }, + }, + Spec: &properties.Spec{}, + }, + "child-param2": { + Name: properties.NewNameVariant("child-param2"), + SpecOrder: 1, + Type: "", + Profiles: []*properties.SpecParamProfile{ + { + Type: "entry", + Xpath: []string{"child-param2"}, + MinVersion: version.MustNewVersionFromString("11.0.0"), + MaxVersion: version.MustNewVersionFromString("11.0.5"), + }, + }, + Spec: &properties.Spec{}, + }, + }, + }, + } + paramVersion := version.MustNewVersionFromString("10.0.0") + result := createEntryXmlStructSpecsForParameter(structXmlType, parent, param, paramVersion) + Expect(result).To(HaveLen(2)) + + Expect(result[0].StructName()).To(Equal("ParentParam")) + Expect(result[0].XmlStructName()).To(Equal("parentParamXml_10_0_0")) + Expect(result[0].Fields).To(HaveExactElements([]entryStructFieldContext{ + { + Name: properties.NewNameVariant("child-param1"), + FieldType: "object", + Type: "ParentParamChildParam1", + XmlType: "parentParamChildParam1Xml_10_0_0", + Tags: "`xml:\"child-param1,omitempty\"`", + version: paramVersion, + }, + { + Name: properties.NewNameVariant("misc"), + FieldType: "internal", + Type: "[]generic.Xml", + XmlType: "[]generic.Xml", + Tags: "`xml:\",any\"`", + }, + })) + + Expect(result[1].StructName()).To(Equal("ParentParamChildParam1")) + Expect(result[1].XmlStructName()).To(Equal("parentParamChildParam1Xml_10_0_0")) + }) + }) +}) + +var _ = Describe("createStructSpecs", func() { + Context("when generating xml struct specs for a spec without parameters", func() { + It("should return a list of struct specs with a single element", func() { + spec := &properties.Normalization{ + TerraformProviderConfig: properties.TerraformProviderConfig{ + ResourceType: properties.TerraformResourceEntry, + }, + Name: "test-spec", + Spec: &properties.Spec{}, + } + + result := createStructSpecs(structXmlType, spec, nil) + Expect(result).To(HaveLen(1)) + + Expect(result[0].StructName()).To(Equal("Entry")) + Expect(result[0].XmlStructName()).To(Equal("entryXml")) + Expect(result[0].Fields[0].Name.CamelCase).To(Equal("XMLName")) + Expect(result[0].Fields).To(HaveExactElements([]entryStructFieldContext{ + { + Name: xmlNameVariant, + FieldType: "internal", + IsInternal: true, + XmlType: "xml.Name", + Tags: "`xml:\"entry\"`", + }, + { + Name: properties.NewNameVariant("name"), + Required: true, + FieldType: "simple", + Type: "string", + XmlType: "string", + Tags: "`xml:\"name,attr\"`", + }, + { + Name: properties.NewNameVariant("misc"), + FieldType: "internal", + Type: "[]generic.Xml", + XmlType: "[]generic.Xml", + Tags: "`xml:\",any\"`", + }, + })) + }) + }) + Context("when generating struct specs for a spec with entry lists", func() { + spec := &properties.Normalization{ + TerraformProviderConfig: properties.TerraformProviderConfig{ + ResourceType: properties.TerraformResourceEntry, }, - }, - } - paramTypeListString := properties.SpecParam{ - Type: "list", - Items: &properties.SpecParamItems{ - Type: "string", - }, - Profiles: []*properties.SpecParamProfile{ - { - Type: "member", - Xpath: []string{"tag"}, + Name: "test-spec", + Spec: &properties.Spec{ + Params: map[string]*properties.SpecParam{ + "param-one": { + Name: properties.NewNameVariant("param-one"), + Profiles: []*properties.SpecParamProfile{ + { + Type: "entry", + Xpath: []string{"param-one"}, + }, + }, + Type: "list", + Items: &properties.SpecParamItems{ + Type: "entry", + }, + Spec: &properties.Spec{ + Params: map[string]*properties.SpecParam{ + "param-two": { + Name: properties.NewNameVariant("param-two"), + Profiles: []*properties.SpecParamProfile{ + { + Xpath: []string{"param-two"}, + }, + }, + Type: "int64", + }, + }, + }, + }, + }, }, - }, - } + } + Context("and the struct type is structApiType", func() { + It("should create a parent spec and a single child spec", func() { + result := createStructSpecs(structApiType, spec, nil) - // when - calculatedTypeRequiredString := XmlParamType("", ¶mTypeRequiredString) - calculatedTypeListString := XmlParamType("", ¶mTypeListString) + Expect(result).To(HaveLen(2)) + Expect(result[0].name).To(Equal(properties.NewNameVariant("entry"))) + Expect(result[0].Fields).To(Equal([]entryStructFieldContext{ + { + Name: properties.NewNameVariant("name"), + Required: true, + FieldType: "simple", + Type: "string", + XmlType: "string", + Tags: "`xml:\"name,attr\"`", + }, + { + Name: properties.NewNameVariant("param-one"), + FieldType: "list-entry", + Type: "ParamOne", + ItemsType: "[]ParamOne", + XmlType: "paramOneXml", + ItemsXmlType: "[]paramOneXml", + Tags: "`xml:\"param-one,omitempty\"`", + }, + { + Name: properties.NewNameVariant("misc"), + FieldType: "internal", + Type: "[]generic.Xml", + XmlType: "[]generic.Xml", + Tags: "`xml:\",any\"`", + }, + })) + Expect(result[1].name).To(Equal(properties.NewNameVariant("param-one"))) + Expect(result[1].Fields).To(Equal([]entryStructFieldContext{ + { + Name: properties.NewNameVariant("name"), + Required: true, + FieldType: "simple", + Type: "string", + XmlType: "string", + Tags: "`xml:\"name,attr\"`", + }, + { + Name: properties.NewNameVariant("param-two"), + FieldType: "simple", + Type: "int64", + ItemsType: "", + XmlType: "int64", + ItemsXmlType: "", + Tags: "`xml:\"param-two,omitempty\"`", + }, + { + Name: properties.NewNameVariant("misc"), + FieldType: "internal", + Type: "[]generic.Xml", + XmlType: "[]generic.Xml", + Tags: "`xml:\",any\"`", + }, + })) + }) + }) + Context("and the struct type is structXmlType", func() { + It("should create a parent spec and two child specs", func() { + result := createStructSpecs(structXmlType, spec, nil) - // then - assert.Equal(t, "string", calculatedTypeRequiredString) - assert.Equal(t, "*util.MemberType", calculatedTypeListString) -} + Expect(result).To(HaveLen(3)) + Expect(result[0].name).To(Equal(properties.NewNameVariant("entry"))) + Expect(result[0].Fields).To(Equal([]entryStructFieldContext{ + { + Name: xmlNameVariant, + FieldType: "internal", + IsInternal: true, + XmlType: "xml.Name", + Tags: "`xml:\"entry\"`", + }, + { + Name: properties.NewNameVariant("name"), + Required: true, + FieldType: "simple", + Type: "string", + XmlType: "string", + Tags: "`xml:\"name,attr\"`", + }, + { + Name: properties.NewNameVariant("param-one"), + FieldType: "list-entry", + Type: "ParamOne", + ItemsType: "[]ParamOne", + XmlType: "paramOneXml", + XmlContainerType: "paramOneContainerXml", + ItemsXmlType: "[]paramOneXml", + Tags: "`xml:\"param-one,omitempty\"`", + }, + { + Name: properties.NewNameVariant("misc"), + FieldType: "internal", + Type: "[]generic.Xml", + XmlType: "[]generic.Xml", + Tags: "`xml:\",any\"`", + }, + })) + + Expect(result[1].name).To(Equal(properties.NewNameVariant("param-one-container"))) + Expect(result[1].Fields).To(Equal([]entryStructFieldContext{ + { + Name: properties.NewNameVariant("entries"), + FieldType: "list-entry", + Type: "ParamOne", + ItemsType: "[]ParamOne", + XmlType: "paramOneXml", + ItemsXmlType: "[]paramOneXml", + Tags: "`xml:\"entry\"`", + }, + })) + + Expect(result[2].name).To(Equal(properties.NewNameVariant("param-one"))) + Expect(result[2].Fields).To(Equal([]entryStructFieldContext{ + { + Name: xmlNameVariant, + FieldType: "internal", + IsInternal: true, + XmlType: "xml.Name", + Tags: "`xml:\"entry\"`", + }, + { + Name: properties.NewNameVariant("name"), + Required: true, + FieldType: "simple", + Type: "string", + XmlType: "string", + Tags: "`xml:\"name,attr\"`", + }, + { + Name: properties.NewNameVariant("param-two"), + FieldType: "simple", + Type: "int64", + ItemsType: "", + XmlType: "int64", + ItemsXmlType: "", + Tags: "`xml:\"param-two,omitempty\"`", + }, + { + Name: properties.NewNameVariant("misc"), + FieldType: "internal", + Type: "[]generic.Xml", + XmlType: "[]generic.Xml", + Tags: "`xml:\",any\"`", + }, + })) + }) + }) + }) + Context("when generating xml struct specs for a spec with one object parameter", func() { + It("should return a list of two specs", func() { + version11_0_0 := version.MustNewVersionFromString("11.0.0") + version11_0_3 := version.MustNewVersionFromString("11.0.3") + spec := &properties.Normalization{ + TerraformProviderConfig: properties.TerraformProviderConfig{ + ResourceType: properties.TerraformResourceEntry, + }, + Name: "test-spec", + Spec: &properties.Spec{ + Params: map[string]*properties.SpecParam{ + "param-one": { + Name: properties.NewNameVariant("param-one"), + Profiles: []*properties.SpecParamProfile{ + { + Type: "entry", + Xpath: []string{"param-one"}, + MinVersion: version11_0_3, + MaxVersion: version.MustNewVersionFromString("11.0.5"), + }, + }, + Type: "bool", + }, + }, + OneOf: map[string]*properties.SpecParam{ + "param-two": { + Name: properties.NewNameVariant("param-two"), + Type: "", + Profiles: []*properties.SpecParamProfile{ + { + Type: "entry", + Xpath: []string{"param-two"}, + }, + }, + Items: &properties.SpecParamItems{ + Type: "object", + }, + Spec: &properties.Spec{ + Params: map[string]*properties.SpecParam{ + "param-three": { + Name: properties.NewNameVariant("param-three"), + Type: "", + Profiles: []*properties.SpecParamProfile{ + { + Type: "entry", + Xpath: []string{"param-three"}, + MinVersion: version11_0_0, + MaxVersion: version.MustNewVersionFromString("11.0.5"), + }, + }, + Items: &properties.SpecParamItems{ + Type: "object", + }, + Spec: &properties.Spec{ + Params: map[string]*properties.SpecParam{ + "param-four": { + Name: properties.NewNameVariant("param-four"), + Profiles: []*properties.SpecParamProfile{ + { + Xpath: []string{"param-four"}, + }, + }, + Type: "int64", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + result := createStructSpecs(structXmlType, spec, nil) + Expect(result).To(HaveLen(3)) + + Expect(result[0].XmlStructName()).To(Equal("entryXml")) + Expect(result[1].XmlStructName()).To(Equal("paramTwoXml")) + Expect(result[2].XmlStructName()).To(Equal("paramTwoParamThreeXml")) + + result = createStructSpecs(structXmlType, spec, version11_0_3) + Expect(result).To(HaveLen(3)) + Expect(result[0].XmlStructName()).To(Equal("entryXml_11_0_3")) + Expect(result[1].XmlStructName()).To(Equal("paramTwoXml_11_0_3")) + Expect(result[2].XmlStructName()).To(Equal("paramTwoParamThreeXml_11_0_3")) + + Expect(result[0].Fields).To(HaveExactElements([]entryStructFieldContext{ + { + Name: xmlNameVariant, + FieldType: "internal", + IsInternal: true, + XmlType: "xml.Name", + Tags: "`xml:\"entry\"`", + }, + { + Name: properties.NewNameVariant("name"), + Required: true, + FieldType: "simple", + Type: "string", + XmlType: "string", + Tags: "`xml:\"name,attr\"`", + }, + { + Name: properties.NewNameVariant("param-one"), + FieldType: "simple", + Type: "bool", + XmlType: "string", + Tags: "`xml:\"param-one,omitempty\"`", + version: version11_0_3, + }, + { + Name: properties.NewNameVariant("param-two"), + FieldType: "object", + Type: "ParamTwo", + XmlType: "paramTwoXml_11_0_3", + Tags: "`xml:\"param-two,omitempty\"`", + version: version11_0_3, + }, + { + Name: properties.NewNameVariant("misc"), + FieldType: "internal", + Type: "[]generic.Xml", + XmlType: "[]generic.Xml", + Tags: "`xml:\",any\"`", + }, + })) + + result = createStructSpecs(structXmlType, spec, version11_0_0) + Expect(result).To(HaveLen(3)) + Expect(result[0].XmlStructName()).To(Equal("entryXml_11_0_0")) + Expect(result[1].XmlStructName()).To(Equal("paramTwoXml_11_0_0")) + Expect(result[2].XmlStructName()).To(Equal("paramTwoParamThreeXml_11_0_0")) + + Expect(result[0].Fields).To(HaveExactElements([]entryStructFieldContext{ + { + Name: xmlNameVariant, + FieldType: "internal", + IsInternal: true, + XmlType: "xml.Name", + Tags: "`xml:\"entry\"`", + }, + { + Name: properties.NewNameVariant("name"), + Required: true, + FieldType: "simple", + Type: "string", + XmlType: "string", + Tags: "`xml:\"name,attr\"`", + }, + { + Name: properties.NewNameVariant("param-two"), + FieldType: "object", + Type: "ParamTwo", + XmlType: "paramTwoXml_11_0_0", + Tags: "`xml:\"param-two,omitempty\"`", + version: version11_0_0, + }, + { + Name: properties.NewNameVariant("misc"), + FieldType: "internal", + Type: "[]generic.Xml", + XmlType: "[]generic.Xml", + Tags: "`xml:\",any\"`", + }, + })) + }) + }) +}) func TestXmlTag(t *testing.T) { // given @@ -163,39 +1038,6 @@ func TestXmlTag(t *testing.T) { assert.Equal(t, "`xml:\"uuid,attr,omitempty\"`", calculatedXmlTagUuid) } -func TestNestedSpecs(t *testing.T) { - // given - spec := properties.Spec{ - Params: map[string]*properties.SpecParam{ - "a": { - Name: properties.NewNameVariant("a"), - Spec: &properties.Spec{ - Params: map[string]*properties.SpecParam{ - "b": { - Name: properties.NewNameVariant("b"), - Spec: &properties.Spec{ - Params: map[string]*properties.SpecParam{ - "c": { - Name: properties.NewNameVariant("c"), - }, - }, - }, - }, - }, - }, - }, - }, - } - - // when - nestedSpecs, _ := NestedSpecs(&spec) - - // then - assert.NotNil(t, nestedSpecs) - assert.Contains(t, nestedSpecs, "A") - assert.Contains(t, nestedSpecs, "AB") -} - func TestCreateGoSuffixFromVersion(t *testing.T) { // given diff --git a/pkg/translate/terraform_provider/funcs.go b/pkg/translate/terraform_provider/funcs.go index f942de7b..8d689187 100644 --- a/pkg/translate/terraform_provider/funcs.go +++ b/pkg/translate/terraform_provider/funcs.go @@ -3,6 +3,7 @@ package terraform_provider import ( "fmt" "log" + "runtime/debug" "sort" "strings" "text/template" @@ -13,6 +14,7 @@ import ( "github.com/paloaltonetworks/pan-os-codegen/pkg/imports" "github.com/paloaltonetworks/pan-os-codegen/pkg/naming" "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" + "github.com/paloaltonetworks/pan-os-codegen/pkg/schema/object" ) type Entry struct { @@ -55,9 +57,14 @@ type spec struct { func renderSpecsForParams(params []*properties.SpecParam, parentNames []string) []parameterSpec { var specs []parameterSpec for _, elt := range params { + if elt.IsTerraformOnly() { + continue + } + if elt.IsPrivateParameter() { continue } + var encryptionSpec *parameterEncryptionSpec if elt.Hashing != nil { path := strings.Join(append(parentNames, elt.Name.Underscore), " | ") @@ -164,7 +171,7 @@ func generateFromTerraformToPangoParameter(resourceTyp properties.ResourceType, case properties.ResourceEntryPlural, properties.ResourceUuid, properties.ResourceUuidPlural: terraformPrefix = fmt.Sprintf("%s%s", terraformPrefix, pascalCase(prop.TerraformProviderConfig.PluralName)) var hasEntryName bool - if prop.Entry != nil && resourceTyp != properties.ResourceEntryPlural { + if prop.Entry != nil && (resourceTyp != properties.ResourceEntryPlural || prop.TerraformProviderConfig.PluralType != object.TerraformPluralMapType) { hasEntryName = true } specs = append(specs, spec{ @@ -638,6 +645,74 @@ func RenderCopyFromPangoFunctions(resourceTyp properties.ResourceType, pkgName s return processTemplate(copyFromPangoTmpl, "copy-from-pango", data, funcMap) } +const xpathComponentsGetterTmpl = ` +func (o *{{ .StructName }}Model) resourceXpathParentComponents() ([]string, error) { + var components []string +{{- range .Components }} + {{- if eq .Type "value" }} + components = append(components, (o.{{ .Name.CamelCase }}.ValueString())) + {{- else if eq .Type "entry" }} + components = append(components, pangoutil.AsEntryXpath(o.{{ .Name.CamelCase }}.ValueString())) + {{- end }} +{{- end }} + return components, nil +} +` + +func RenderXpathComponentsGetter(structName string, property *properties.Normalization) (string, error) { + defer func() { + if e := recover(); e != nil { + log.Printf("** PANIC: %v", e) + debug.PrintStack() + panic(e) + } + }() + + type componentSpec struct { + Type string + Name *properties.NameVariant + Variants []*properties.NameVariant + } + + var components []componentSpec + for _, elt := range property.PanosXpath.Variables { + if elt.Name == "name" { + continue + } + + xpathProperty, err := property.ParameterForPanosXpathVariable(elt) + if err != nil { + return "", err + } + + switch elt.Spec.Type { + case object.PanosXpathVariableValue: + components = append(components, componentSpec{ + Type: "value", + Name: xpathProperty.Name, + }) + case object.PanosXpathVariableEntry: + components = append(components, componentSpec{ + Type: "entry", + Name: xpathProperty.Name, + }) + case object.PanosXpathVariableStatic: + default: + panic(fmt.Sprintf("invalid panos xpath variable type: '%s'", elt.Spec.Type)) + } + } + + data := struct { + StructName string + Components []componentSpec + }{ + StructName: structName, + Components: components, + } + + return processTemplate(xpathComponentsGetterTmpl, "xpath-components", data, commonFuncMap) +} + const renderLocationTmpl = ` {{- range .Locations }} type {{ .StructName }} struct { @@ -1523,14 +1598,42 @@ func createSchemaSpecForEntryListModel(resourceTyp properties.ResourceType, sche listNameStr := spec.TerraformProviderConfig.PluralName listName := properties.NewNameVariant(listNameStr) + var listAttributeSchemaType string + switch spec.TerraformProviderConfig.PluralType { + case object.TerraformPluralListType: + listAttributeSchemaType = "ListNestedAttribute" + case object.TerraformPluralMapType: + listAttributeSchemaType = "MapNestedAttribute" + case object.TerraformPluralSetType: + listAttributeSchemaType = "SetNestedAttribute" + } + attributes = append(attributes, attributeCtx{ Package: packageName, Name: listName, Description: spec.TerraformProviderConfig.PluralDescription, Required: true, - SchemaType: "MapNestedAttribute", + SchemaType: listAttributeSchemaType, }) + for _, elt := range spec.PanosXpath.Variables { + if elt.Name == "name" { + continue + } + + param, err := spec.ParameterForPanosXpathVariable(elt) + if err != nil { + panic(fmt.Sprintf("couldn't find matching param for xpath variable: %s", err.Error())) + } + + attributes = append(attributes, attributeCtx{ + Package: packageName, + Name: param.Name, + Required: true, + SchemaType: "StringAttribute", + }) + } + var isResource bool if schemaTyp == properties.SchemaResource { isResource = true @@ -1624,9 +1727,9 @@ func createSchemaSpecForNormalization(resourceTyp properties.ResourceType, schem }) } - // We don't add name for resources of type ResourceEntryPlural, as those resources + // We don't add name for resources that have plurar type set to map, as those resources // handle names as map keys in the top-level model. - if spec.HasEntryName() && resourceTyp != properties.ResourceEntryPlural { + if spec.HasEntryName() && (resourceTyp != properties.ResourceEntryPlural || spec.TerraformProviderConfig.PluralType != object.TerraformPluralMapType) { name := properties.NewNameVariant("name") var description string @@ -1648,6 +1751,10 @@ func createSchemaSpecForNormalization(resourceTyp properties.ResourceType, schem continue } + if resourceTyp == properties.ResourceEntryPlural && elt.TerraformProviderConfig != nil && elt.TerraformProviderConfig.XpathVariable != nil { + continue + } + var functions []validatorFunctionCtx if len(elt.EnumValues) > 0 && schemaTyp == properties.SchemaResource { var values []string @@ -1685,6 +1792,10 @@ func createSchemaSpecForNormalization(resourceTyp properties.ResourceType, schem continue } + if resourceTyp == properties.ResourceEntryPlural && elt.TerraformProviderConfig != nil && elt.TerraformProviderConfig.XpathVariable != nil { + continue + } + var validators *validatorCtx if schemaTyp == properties.SchemaResource { validatorFn, found := validatorFns[elt.VariantGroupId] @@ -2384,13 +2495,41 @@ func createStructSpecForEntryListModel(resourceTyp properties.ResourceType, sche panic("unreachable") } + for _, elt := range spec.PanosXpath.Variables { + if elt.Name == "name" { + continue + } + + param, err := spec.ParameterForPanosXpathVariable(elt) + if err != nil { + panic(fmt.Sprintf("couldn't find matching param for xpath variable: %s", err.Error())) + } + + xmlTags := []string{fmt.Sprintf("`tfsdk:\"%s\"`", param.Name.Underscore)} + fields = append(fields, datasourceStructFieldSpec{ + Name: param.Name, + Type: "types.String", + Tags: xmlTags, + }) + } + listNameStr := spec.TerraformProviderConfig.PluralName listName := properties.NewNameVariant(listNameStr) + var listEltType string + switch spec.TerraformProviderConfig.PluralType { + case object.TerraformPluralMapType: + listEltType = "types.Map" + case object.TerraformPluralListType: + listEltType = "types.List" + case object.TerraformPluralSetType: + listEltType = "types.Set" + } + tag := fmt.Sprintf("`tfsdk:\"%s\"`", listName.Underscore) fields = append(fields, datasourceStructFieldSpec{ Name: listName, - Type: "types.Map", + Type: listEltType, Tags: []string{tag}, }) @@ -2475,7 +2614,7 @@ func createStructSpecForNormalization(resourceTyp properties.ResourceType, struc // We don't add name field for entry-style list resources, as they // represent lists as maps with name being a key. - if spec.HasEntryName() && resourceTyp != properties.ResourceEntryPlural { + if spec.HasEntryName() && (resourceTyp != properties.ResourceEntryPlural || spec.TerraformProviderConfig.PluralType != object.TerraformPluralMapType) { fields = append(fields, datasourceStructFieldSpec{ Name: properties.NewNameVariant("name"), Type: "types.String", @@ -2488,6 +2627,10 @@ func createStructSpecForNormalization(resourceTyp properties.ResourceType, struc continue } + if resourceTyp == properties.ResourceEntryPlural && elt.TerraformProviderConfig != nil && elt.TerraformProviderConfig.XpathVariable != nil { + continue + } + fields = append(fields, structFieldSpec(elt, structName, hackStructAsTypeObjects)) if elt.Type == "" || (elt.Type == "list" && elt.Items.Type == "entry") { structs = append(structs, dataSourceStructContextForParam(structName, elt, hackStructAsTypeObjects)...) @@ -2499,6 +2642,10 @@ func createStructSpecForNormalization(resourceTyp properties.ResourceType, struc continue } + if resourceTyp == properties.ResourceEntryPlural && elt.TerraformProviderConfig != nil && elt.TerraformProviderConfig.XpathVariable != nil { + continue + } + fields = append(fields, structFieldSpec(elt, structName, hackStructAsTypeObjects)) if elt.Type == "" || (elt.Type == "list" && elt.Items.Type == "entry") { structs = append(structs, dataSourceStructContextForParam(structName, elt, hackStructAsTypeObjects)...) @@ -2679,15 +2826,11 @@ func ResourceCreateFunction(resourceTyp properties.ResourceType, names *NameProv listAttributeVariant := properties.NewNameVariant(listAttribute) - var resourceIsMap bool - if resourceTyp == properties.ResourceEntryPlural { - resourceIsMap = true - } data := map[string]interface{}{ + "PluralType": paramSpec.TerraformProviderConfig.PluralType, "HasEncryptedResources": paramSpec.HasEncryptedResources(), "HasImports": len(paramSpec.Imports) > 0, "Exhaustive": exhaustive, - "ResourceIsMap": resourceIsMap, "ListAttribute": listAttributeVariant, "EntryOrConfig": paramSpec.EntryOrConfig(), "HasEntryName": paramSpec.HasEntryName(), @@ -2732,27 +2875,25 @@ func DataSourceReadFunction(resourceTyp properties.ResourceType, names *NameProv listAttributeVariant := properties.NewNameVariant(listAttribute) - var resourceIsMap bool - if resourceTyp == properties.ResourceEntryPlural { - resourceIsMap = true - } data := map[string]interface{}{ - "ResourceOrDS": "DataSource", - "ResourceIsMap": resourceIsMap, - "HasEncryptedResources": paramSpec.HasEncryptedResources(), - "ListAttribute": listAttributeVariant, - "Exhaustive": exhaustive, - "EntryOrConfig": paramSpec.EntryOrConfig(), - "HasEntryName": paramSpec.HasEntryName(), - "structName": names.StructName, - "resourceStructName": names.ResourceStructName, - "dataSourceStructName": names.DataSourceStructName, - "serviceName": naming.CamelCase("", serviceName, "", false), - "resourceSDKName": resourceSDKName, - "locations": paramSpec.OrderedLocations(), + "PluralType": paramSpec.TerraformProviderConfig.PluralType, + "ResourceXpathVariablesWithChecks": paramSpec.ResourceXpathVariablesWithChecks(false), + "ResourceOrDS": "DataSource", + "HasEncryptedResources": paramSpec.HasEncryptedResources(), + "ListAttribute": listAttributeVariant, + "Exhaustive": exhaustive, + "EntryOrConfig": paramSpec.EntryOrConfig(), + "HasEntryName": paramSpec.HasEntryName(), + "structName": names.StructName, + "resourceStructName": names.ResourceStructName, + "dataSourceStructName": names.DataSourceStructName, + "serviceName": naming.CamelCase("", serviceName, "", false), + "resourceSDKName": resourceSDKName, + "locations": paramSpec.OrderedLocations(), } funcMap := template.FuncMap{ + "AttributesFromXpathComponents": func(target string) (string, error) { return paramSpec.AttributesFromXpathComponents(target) }, "RenderLocationsPangoToState": func(source string, dest string) (string, error) { return RenderLocationsPangoToState(names, paramSpec, source, dest) }, @@ -2761,7 +2902,7 @@ func DataSourceReadFunction(resourceTyp properties.ResourceType, names *NameProv }, } - return processTemplate(tmpl, "resource-read-function", data, funcMap) + return processTemplate(tmpl, "datasource-read-function", data, funcMap) } func ResourceReadFunction(resourceTyp properties.ResourceType, names *NameProvider, serviceName string, paramSpec *properties.Normalization, resourceSDKName string) (string, error) { @@ -2795,27 +2936,25 @@ func ResourceReadFunction(resourceTyp properties.ResourceType, names *NameProvid listAttributeVariant := properties.NewNameVariant(listAttribute) - var resourceIsMap bool - if resourceTyp == properties.ResourceEntryPlural { - resourceIsMap = true - } data := map[string]interface{}{ - "ResourceOrDS": "Resource", - "ResourceIsMap": resourceIsMap, - "HasEncryptedResources": paramSpec.HasEncryptedResources(), - "ListAttribute": listAttributeVariant, - "Exhaustive": exhaustive, - "EntryOrConfig": paramSpec.EntryOrConfig(), - "HasEntryName": paramSpec.HasEntryName(), - "structName": names.StructName, - "datasourceStructName": names.DataSourceStructName, - "resourceStructName": names.ResourceStructName, - "serviceName": naming.CamelCase("", serviceName, "", false), - "resourceSDKName": resourceSDKName, - "locations": paramSpec.OrderedLocations(), + "PluralType": paramSpec.TerraformProviderConfig.PluralType, + "ResourceXpathVariablesWithChecks": paramSpec.ResourceXpathVariablesWithChecks(false), + "ResourceOrDS": "Resource", + "HasEncryptedResources": paramSpec.HasEncryptedResources(), + "ListAttribute": listAttributeVariant, + "Exhaustive": exhaustive, + "EntryOrConfig": paramSpec.EntryOrConfig(), + "HasEntryName": paramSpec.HasEntryName(), + "structName": names.StructName, + "datasourceStructName": names.DataSourceStructName, + "resourceStructName": names.ResourceStructName, + "serviceName": naming.CamelCase("", serviceName, "", false), + "resourceSDKName": resourceSDKName, + "locations": paramSpec.OrderedLocations(), } funcMap := template.FuncMap{ + "AttributesFromXpathComponents": func(target string) (string, error) { return paramSpec.AttributesFromXpathComponents(target) }, "RenderLocationsPangoToState": func(source string, dest string) (string, error) { return RenderLocationsPangoToState(names, paramSpec, source, dest) }, @@ -2858,14 +2997,9 @@ func ResourceUpdateFunction(resourceTyp properties.ResourceType, names *NameProv listAttributeVariant := properties.NewNameVariant(listAttribute) - var resourceIsMap bool - if resourceTyp == properties.ResourceEntryPlural { - resourceIsMap = true - } - data := map[string]interface{}{ + "PluralType": paramSpec.TerraformProviderConfig.PluralType, "HasEncryptedResources": paramSpec.HasEncryptedResources(), - "ResourceIsMap": resourceIsMap, "ListAttribute": listAttributeVariant, "Exhaustive": exhaustive, "EntryOrConfig": paramSpec.EntryOrConfig(), @@ -2897,7 +3031,7 @@ func ResourceDeleteFunction(resourceTyp properties.ResourceType, names *NameProv var tmpl string var listAttribute string - var exhaustive bool + var exhaustive string switch resourceTyp { case properties.ResourceEntry, properties.ResourceConfig: tmpl = resourceDeleteFunction @@ -2907,10 +3041,11 @@ func ResourceDeleteFunction(resourceTyp properties.ResourceType, names *NameProv case properties.ResourceUuid: tmpl = resourceDeleteManyFunction listAttribute = pascalCase(paramSpec.TerraformProviderConfig.PluralName) - exhaustive = true + exhaustive = "exhaustive" case properties.ResourceUuidPlural: tmpl = resourceDeleteManyFunction listAttribute = pascalCase(paramSpec.TerraformProviderConfig.PluralName) + exhaustive = "non-exhaustive" case properties.ResourceCustom: var err error tmpl, err = getCustomTemplateForFunction(paramSpec, "Delete") @@ -2921,14 +3056,9 @@ func ResourceDeleteFunction(resourceTyp properties.ResourceType, names *NameProv listAttributeVariant := properties.NewNameVariant(listAttribute) - var resourceIsMap bool - if resourceTyp == properties.ResourceEntryPlural { - resourceIsMap = true - } - data := map[string]interface{}{ + "PluralType": paramSpec.TerraformProviderConfig.PluralType, "HasEncryptedResources": paramSpec.HasEncryptedResources(), - "ResourceIsMap": resourceIsMap, "HasImports": len(paramSpec.Imports) > 0, "EntryOrConfig": paramSpec.EntryOrConfig(), "ListAttribute": listAttributeVariant, @@ -3051,14 +3181,73 @@ func createImportStateStructSpecs(resourceTyp properties.ResourceType, names *Na Tags: "`json:\"location\"`", }) + var resourceHasParent bool + if spec.ResourceXpathVariablesWithChecks(false) { + resourceHasParent = true + } + switch resourceTyp { case properties.ResourceEntry: + if resourceHasParent { + var xpathVariable *object.PanosXpathVariable + for _, elt := range spec.PanosXpath.Variables { + if elt.Name == "parent" { + xpathVariable = &elt + } + } + + if xpathVariable == nil { + panic("couldn't find parent variable for a child spec") + } + + parentParam, err := spec.ParameterForPanosXpathVariable(*xpathVariable) + if err != nil { + panic(fmt.Sprintf("couldn't find matching param for xpath variable: %s", err.Error())) + } + + fields = append(fields, importStateStructFieldSpec{ + Name: parentParam.Name.CamelCase, + Type: "types.String", + Tags: fmt.Sprintf("`json:\"%s\"`", parentParam.Name.Underscore), + }) + } + fields = append(fields, importStateStructFieldSpec{ Name: "Name", Type: "types.String", Tags: "`json:\"name\"`", }) - case properties.ResourceEntryPlural, properties.ResourceUuid: + case properties.ResourceEntryPlural: + if resourceHasParent { + var xpathVariable *object.PanosXpathVariable + for _, elt := range spec.PanosXpath.Variables { + if elt.Name == "parent" { + xpathVariable = &elt + } + } + + if xpathVariable == nil { + panic("couldn't find parent variable for a child spec") + } + + parentParam, err := spec.ParameterForPanosXpathVariable(*xpathVariable) + if err != nil { + panic(fmt.Sprintf("couldn't find matching param for xpath variable: %s", err.Error())) + } + + fields = append(fields, importStateStructFieldSpec{ + Name: parentParam.Name.CamelCase, + Type: "types.String", + Tags: fmt.Sprintf("`json:\"%s\"`", parentParam.Name.Underscore), + }) + } else { + fields = append(fields, importStateStructFieldSpec{ + Name: "Names", + Type: "types.List", + Tags: "`json:\"names\"`", + }) + } + case properties.ResourceUuid: fields = append(fields, importStateStructFieldSpec{ Name: "Names", Type: "types.List", @@ -3088,7 +3277,7 @@ func createImportStateStructSpecs(resourceTyp properties.ResourceType, names *Na return specs } -func createImportStateMarshallerSpecs(resourceTyp properties.ResourceType, names *NameProvider, _ *properties.Normalization) []marshallerSpec { +func createImportStateMarshallerSpecs(resourceTyp properties.ResourceType, names *NameProvider, spec *properties.Normalization) []marshallerSpec { var specs []marshallerSpec var fields []marshallerFieldSpec @@ -3100,14 +3289,74 @@ func createImportStateMarshallerSpecs(resourceTyp properties.ResourceType, names Tags: "`json:\"location\"`", }) + var resourceHasParent bool + if spec.ResourceXpathVariablesWithChecks(false) { + resourceHasParent = true + } + switch resourceTyp { case properties.ResourceEntry: + if resourceHasParent { + var xpathVariable *object.PanosXpathVariable + for _, elt := range spec.PanosXpath.Variables { + if elt.Name == "parent" { + xpathVariable = &elt + } + } + + if xpathVariable == nil { + panic("couldn't find parent variable for a child spec") + } + + parentParam, err := spec.ParameterForPanosXpathVariable(*xpathVariable) + if err != nil { + panic(fmt.Sprintf("couldn't find matching param for xpath variable: %s", err.Error())) + } + + fields = append(fields, marshallerFieldSpec{ + Name: parentParam.Name, + Type: "string", + Tags: fmt.Sprintf("`json:\"%s\"`", parentParam.Name.Underscore), + }) + } + fields = append(fields, marshallerFieldSpec{ Name: properties.NewNameVariant("name"), Type: "string", Tags: "`json:\"name\"`", }) - case properties.ResourceEntryPlural, properties.ResourceUuid: + case properties.ResourceEntryPlural: + if resourceHasParent { + var xpathVariable *object.PanosXpathVariable + for _, elt := range spec.PanosXpath.Variables { + if elt.Name == "parent" { + xpathVariable = &elt + } + } + + if xpathVariable == nil { + panic("couldn't find parent variable for a child spec") + } + + parentParam, err := spec.ParameterForPanosXpathVariable(*xpathVariable) + if err != nil { + panic(fmt.Sprintf("couldn't find matching param for xpath variable: %s", err.Error())) + } + + fields = append(fields, marshallerFieldSpec{ + Name: parentParam.Name, + Type: "string", + Tags: fmt.Sprintf("`json:\"%s\"`", parentParam.Name.Underscore), + }) + } else { + fields = append(fields, marshallerFieldSpec{ + Name: properties.NewNameVariant("names"), + Type: "types.List", + StructName: "[]string", + Tags: "`json:\"names\"`", + }) + } + case properties.ResourceUuid: fields = append(fields, marshallerFieldSpec{ Name: properties.NewNameVariant("names"), Type: "types.List", @@ -3163,30 +3412,81 @@ func ResourceImportStateFunction(resourceTyp properties.ResourceType, names *Nam type context struct { StructName string - ResourceIsMap bool + PluralType object.TerraformPluralType ResourceIsList bool HasPosition bool HasEntryName bool ListAttribute *properties.NameVariant ListStructName string PangoStructName string + HasParent bool + ParentAttribute *properties.NameVariant } data := context{ StructName: names.StructName, } + var resourceHasParent bool + if spec.ResourceXpathVariablesWithChecks(false) { + resourceHasParent = true + } + switch resourceTyp { case properties.ResourceEntry: + if resourceHasParent { + var xpathVariable *object.PanosXpathVariable + for _, elt := range spec.PanosXpath.Variables { + if elt.Name == "parent" { + xpathVariable = &elt + } + } + + if xpathVariable == nil { + panic("couldn't find parent variable for a child spec") + } + + parentParam, err := spec.ParameterForPanosXpathVariable(*xpathVariable) + if err != nil { + panic(fmt.Sprintf("couldn't find matching param for xpath variable: %s", err.Error())) + } + + data.ParentAttribute = parentParam.Name + data.HasParent = true + } data.HasEntryName = spec.HasEntryName() case properties.ResourceEntryPlural: - data.ResourceIsMap = true - listAttribute := properties.NewNameVariant(spec.TerraformProviderConfig.PluralName) - data.ListAttribute = listAttribute - data.ListStructName = fmt.Sprintf("%sResource%sObject", names.StructName, listAttribute.CamelCase) - data.PangoStructName = fmt.Sprintf("%s.Entry", names.PackageName) + if resourceHasParent { + var xpathVariable *object.PanosXpathVariable + for _, elt := range spec.PanosXpath.Variables { + if elt.Name == "parent" { + xpathVariable = &elt + } + } + + if xpathVariable == nil { + panic("couldn't find parent variable for a child spec") + } + + parentParam, err := spec.ParameterForPanosXpathVariable(*xpathVariable) + if err != nil { + panic(fmt.Sprintf("couldn't find matching param for xpath variable: %s", err.Error())) + } + + data.PluralType = spec.TerraformProviderConfig.PluralType + data.ParentAttribute = parentParam.Name + data.HasParent = true + + } else { + listAttribute := properties.NewNameVariant(spec.TerraformProviderConfig.PluralName) + data.PluralType = spec.TerraformProviderConfig.PluralType + data.ListAttribute = listAttribute + data.ListStructName = fmt.Sprintf("%sResource%sObject", names.StructName, listAttribute.CamelCase) + data.PangoStructName = fmt.Sprintf("%s.Entry", names.PackageName) + } case properties.ResourceUuid, properties.ResourceUuidPlural: data.ResourceIsList = true + data.PluralType = spec.TerraformProviderConfig.PluralType listAttribute := properties.NewNameVariant(spec.TerraformProviderConfig.PluralName) data.ListAttribute = properties.NewNameVariant(spec.TerraformProviderConfig.PluralName) data.ListStructName = fmt.Sprintf("%sResource%sObject", names.StructName, listAttribute.CamelCase) @@ -3213,6 +3513,8 @@ func RenderImportStateCreator(resourceTyp properties.ResourceType, names *NamePr ListAttribute *properties.NameVariant ListStructName string ResourceType properties.ResourceType + HasParent bool + ParentAttribute *properties.NameVariant } data := context{ @@ -3222,12 +3524,58 @@ func RenderImportStateCreator(resourceTyp properties.ResourceType, names *NamePr StructNamePrefix: names.StructName, } + var resourceHasParent bool + if spec.ResourceXpathVariablesWithChecks(false) { + resourceHasParent = true + } + switch resourceTyp { case properties.ResourceEntry: + if resourceHasParent { + var xpathVariable *object.PanosXpathVariable + for _, elt := range spec.PanosXpath.Variables { + if elt.Name == "parent" { + xpathVariable = &elt + } + } + + if xpathVariable == nil { + panic("couldn't find parent variable for a child spec") + } + + parentParam, err := spec.ParameterForPanosXpathVariable(*xpathVariable) + if err != nil { + panic(fmt.Sprintf("couldn't find matching param for xpath variable: %s", err.Error())) + } + + data.HasParent = true + data.ParentAttribute = parentParam.Name + } case properties.ResourceEntryPlural: - listAttribute := properties.NewNameVariant(spec.TerraformProviderConfig.PluralName) - data.ListAttribute = listAttribute - data.ListStructName = fmt.Sprintf("%sResource%sObject", names.StructName, listAttribute.CamelCase) + if resourceHasParent { + var xpathVariable *object.PanosXpathVariable + for _, elt := range spec.PanosXpath.Variables { + if elt.Name == "parent" { + xpathVariable = &elt + } + } + + if xpathVariable == nil { + panic("couldn't find parent variable for a child spec") + } + + parentParam, err := spec.ParameterForPanosXpathVariable(*xpathVariable) + if err != nil { + panic(fmt.Sprintf("couldn't find matching param for xpath variable: %s", err.Error())) + } + + data.HasParent = true + data.ParentAttribute = parentParam.Name + } else { + listAttribute := properties.NewNameVariant(spec.TerraformProviderConfig.PluralName) + data.ListAttribute = listAttribute + data.ListStructName = fmt.Sprintf("%sResource%sObject", names.StructName, listAttribute.CamelCase) + } case properties.ResourceUuid, properties.ResourceUuidPlural: listAttribute := properties.NewNameVariant(spec.TerraformProviderConfig.PluralName) data.ListAttribute = listAttribute diff --git a/pkg/translate/terraform_provider/template.go b/pkg/translate/terraform_provider/template.go index eebe2d64..ceff823e 100644 --- a/pkg/translate/terraform_provider/template.go +++ b/pkg/translate/terraform_provider/template.go @@ -374,7 +374,9 @@ func (r *{{ resourceStructName }}) Configure(ctx context.Context, req {{ tfresou resp.Diagnostics.AddError("Failed to configure SDK client", err.Error()) return } - r.manager = sdkmanager.NewImportableEntryObjectManager(r.client, {{ resourceSDKName }}.NewService(r.client), specifier, {{ resourceSDKName }}.SpecMatches) + + batchSize := providerData.MultiConfigBatchSize + r.manager = sdkmanager.NewImportableEntryObjectManager(r.client, {{ resourceSDKName }}.NewService(r.client), batchSize, specifier, {{ resourceSDKName }}.SpecMatches) {{- else if IsEntry }} specifier, _, err := {{ resourceSDKName }}.Versioning(r.client.Versioning()) if err != nil { @@ -382,7 +384,7 @@ func (r *{{ resourceStructName }}) Configure(ctx context.Context, req {{ tfresou return } batchSize := providerData.MultiConfigBatchSize - r.manager = sdkmanager.NewEntryObjectManager(r.client, {{ resourceSDKName }}.NewService(r.client), batchSize, specifier, {{ resourceSDKName }}.SpecMatches) + r.manager = sdkmanager.NewEntryObjectManager[*{{ resourceSDKName }}.Entry, {{ resourceSDKName }}.Location, *{{ resourceSDKName }}.Service](r.client, {{ resourceSDKName }}.NewService(r.client), batchSize, specifier, {{ resourceSDKName }}.SpecMatches) {{- else if IsUuid }} specifier, _, err := {{ resourceSDKName }}.Versioning(r.client.Versioning()) if err != nil { @@ -390,7 +392,7 @@ func (r *{{ resourceStructName }}) Configure(ctx context.Context, req {{ tfresou return } batchSize := providerData.MultiConfigBatchSize - r.manager = sdkmanager.NewUuidObjectManager(r.client, {{ resourceSDKName }}.NewService(r.client), batchSize, specifier, {{ resourceSDKName }}.SpecMatches) + r.manager = sdkmanager.NewUuidObjectManager[*{{ resourceSDKName }}.Entry, {{ resourceSDKName }}.Location, *{{ resourceSDKName }}.Service](r.client, {{ resourceSDKName }}.NewService(r.client), batchSize, specifier, {{ resourceSDKName }}.SpecMatches) {{- else if IsConfig }} specifier, _, err := {{ resourceSDKName }}.Versioning(r.client.Versioning()) if err != nil { @@ -407,6 +409,8 @@ func (r *{{ resourceStructName }}) Configure(ctx context.Context, req {{ tfresou {{ RenderCopyFromPangoFunctions }} +{{ RenderXpathComponentsGetter }} + {{- if FunctionSupported "Create" }} func (r *{{ resourceStructName }}) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { {{ ResourceCreateFunction resourceStructName serviceName}} @@ -493,6 +497,7 @@ type entryWithState struct { StateIdx int } +{{ if eq .PluralType "map" }} var elements map[string]{{ $resourceTFStructName }} resp.Diagnostics.Append(state.{{ .ListAttribute.CamelCase }}.ElementsAs(ctx, &elements, false)...) if resp.Diagnostics.HasError() { @@ -511,13 +516,38 @@ for name, elt := range elements { entries[idx] = entry idx++ } +{{ else if or (eq .PluralType "list") (eq .PluralType "set") }} +var elements []{{ $resourceTFStructName }} +resp.Diagnostics.Append(state.{{ .ListAttribute.CamelCase }}.ElementsAs(ctx, &elements, false)...) +if resp.Diagnostics.HasError() { + return +} -created, err := r.manager.CreateMany(ctx, location, entries) +entries := make([]*{{ $resourceSDKStructName }}, len(elements)) +for idx, elt := range elements { + var entry *{{ .resourceSDKName }}.{{ .EntryOrConfig }} + resp.Diagnostics.Append(elt.CopyToPango(ctx, &entry, {{ $ev }})...) + if resp.Diagnostics.HasError() { + return + } + entry.Name = elt.Name.ValueString() + entries[idx] = entry +} +{{- end }} + +components, err := state.resourceXpathParentComponents() +if err != nil { + resp.Diagnostics.AddError("Error creating resource xpath", err.Error()) + return +} + +created, err := r.manager.CreateMany(ctx, location, components, entries) if err != nil { resp.Diagnostics.AddError("Failed to create new entries", err.Error()) return } +{{ if eq .PluralType "map" }} for _, elt := range created { if _, found := elements[elt.Name]; !found { continue @@ -536,6 +566,37 @@ resp.Diagnostics.Append(map_diags...) if resp.Diagnostics.HasError() { return } +{{ else if or (eq .PluralType "list") (eq .PluralType "set") }} +elementsByName := make(map[string]int) +for idx, elt := range elements { + elementsByName[elt.Name.ValueString()] = idx +} + +for _, elt := range created { + idx, found := elementsByName[elt.Name] + if !found { + continue + } + + var object {{ $resourceTFStructName }} + resp.Diagnostics.Append(object.CopyFromPango(ctx, elt, {{ $ev }})...) + if resp.Diagnostics.HasError() { + return + } + elements[idx] = object +} + +var list_diags diag.Diagnostics + {{ if eq .PluralType "list" }} +state.{{ .ListAttribute.CamelCase }}, list_diags = types.ListValueFrom(ctx, state.getTypeFor("{{ .ListAttribute.Underscore }}"), elements) + {{ else if eq .PluralType "set" }} +state.{{ .ListAttribute.CamelCase }}, list_diags = types.SetValueFrom(ctx, state.getTypeFor("{{ .ListAttribute.Underscore }}"), elements) + {{- end }} +resp.Diagnostics.Append(list_diags...) +if resp.Diagnostics.HasError() { + return +} +{{- end }} {{- if .HasEncryptedResources }} ev_map, ev_diags := types.MapValueFrom(ctx, types.StringType, ev) @@ -591,14 +652,14 @@ for idx, elt := range elements { entries[idx] = entry } -{{- if .ResourceIsMap }} -processed, err := o.manager.CreateMany(ctx, location, entries) +components, err := state.resourceXpathParentComponents() if err != nil { - resp.Diagnostics.AddError("Error during CreateMany() call", err.Error()) + resp.Diagnostics.AddError("Error creating resource xpath", err.Error()) return } -{{- else if .Exhaustive }} -processed, err := r.manager.CreateMany(ctx, location, entries, sdkmanager.Exhaustive, movement.PositionFirst{}) + +{{- if .Exhaustive }} +processed, err := r.manager.CreateMany(ctx, location, components, entries, sdkmanager.Exhaustive, movement.PositionFirst{}) if err != nil { resp.Diagnostics.AddError("Error during CreateMany() call", err.Error()) return @@ -610,33 +671,12 @@ if resp.Diagnostics.HasError() { return } position := positionAttribute.CopyToPango() -processed, err := r.manager.CreateMany(ctx, location, entries, sdkmanager.NonExhaustive, position) +processed, err := r.manager.CreateMany(ctx, location, components, entries, sdkmanager.NonExhaustive, position) if err != nil { resp.Diagnostics.AddError("Error during CreateMany() call", err.Error()) return } {{- end }} - - -{{- if .ResourceIsMap }} -objects := make(map[string]{{ $resourceTFStructName }}, len(processed)) -for _, elt := range processed { - var object {{ $resourceTFStructName }} - copy_diags := object.CopyFromPango(ctx, elt, {{ $ev }}) - resp.Diagnostics.Append(copy_diags...) - if resp.Diagnostics.HasError() { - return - } - objects[elt.Name] = object -} - -var map_diags diag.Diagnostics -state.{{ .ListAttribute.CamelCase }}, map_diags = types.MapValueFrom(ctx, state.getTypeFor("{{ .ListAttribute.Underscore }}"), objects) -resp.Diagnostics.Append(list_diags...) -if resp.Diagnostics.HasError() { - return -} -{{- else }} objects := make([]{{ $resourceTFStructName }}, len(processed)) for idx, elt := range processed { var object {{ $resourceTFStructName }} @@ -654,7 +694,6 @@ resp.Diagnostics.Append(list_diags...) if resp.Diagnostics.HasError() { return } -{{- end }} {{- if .HasEncryptedResources }} ev_map, ev_diags := types.MapValueFrom(ctx, types.StringType, ev) @@ -720,17 +759,35 @@ const resourceCreateFunction = ` */ // Perform the operation. + + components, err := state.resourceXpathParentComponents() + if err != nil { + resp.Diagnostics.AddError("Error creating resource xpath", err.Error()) + return + } + {{- if .HasImports }} var importLocation {{ .resourceSDKName }}.ImportLocation {{ RenderImportLocationAssignment "state.Location" "importLocation" }} - created, err := r.manager.Create(ctx, location, []{{ .resourceSDKName }}.ImportLocation{importLocation}, obj) + created, err := r.manager.Create(ctx, location, components, obj) + if err != nil { + resp.Diagnostics.AddError("Error in create", err.Error()) + return + } + + err = r.manager.ImportToLocations(ctx, location, []{{ .resourceSDKName }}.ImportLocation{importLocation}, obj.Name) + if err != nil { + resp.Diagnostics.AddError("Failed to import resource into location", err.Error()) + return + } {{- else }} - created, err := r.manager.Create(ctx, location, obj) -{{- end }} + created, err := r.manager.Create(ctx, location, components, obj) if err != nil { resp.Diagnostics.AddError("Error in create", err.Error()) return } +{{- end }} + resp.Diagnostics.Append(state.CopyFromPango(ctx, created, {{ $ev }})...) if resp.Diagnostics.HasError() { @@ -745,10 +802,6 @@ const resourceCreateFunction = ` } {{- end }} -{{- if .HasEntryName }} - state.Name = types.StringValue(created.Name) -{{- end }} - // Done. resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) @@ -800,6 +853,7 @@ if resp.Diagnostics.HasError() { } {{- end }} +{{- if eq .PluralType "map" }} elements := make(map[string]{{ $resourceTFStructName }}) resp.Diagnostics.Append(state.{{ .ListAttribute.CamelCase }}.ElementsAs(ctx, &elements, false)...) if len(elements) == 0 || resp.Diagnostics.HasError() { @@ -816,8 +870,31 @@ for name, elt := range elements { entry.Name = name entries = append(entries, entry) } +{{- else if or (eq .PluralType "list") (eq .PluralType "set") }} +var elements []{{ $resourceTFStructName }} +resp.Diagnostics.Append(state.{{ .ListAttribute.CamelCase }}.ElementsAs(ctx, &elements, false)...) +if resp.Diagnostics.HasError() { + return +} + +entries := make([]*{{ $resourceSDKStructName }}, 0, len(elements)) +for _, elt := range elements { + var entry *{{ $resourceSDKStructName }} + resp.Diagnostics.Append(elt.CopyToPango(ctx, &entry, {{ $ev }})...) + if resp.Diagnostics.HasError() { + return + } + entries = append(entries, entry) +} +{{- end }} -readEntries, err := o.manager.ReadMany(ctx, location, entries) +components, err := state.resourceXpathParentComponents() +if err != nil { + resp.Diagnostics.AddError("Error creating resource xpath", err.Error()) + return +} + +readEntries, err := o.manager.ReadMany(ctx, location, components, entries) if err != nil { if errors.Is(err, sdkmanager.ErrObjectNotFound) { resp.State.RemoveResource(ctx) @@ -827,6 +904,7 @@ if err != nil { return } +{{- if eq .PluralType "map" }} objects := make(map[string]{{ $resourceTFStructName }}) for _, elt := range readEntries { var object {{ $resourceTFStructName }} @@ -836,13 +914,44 @@ for _, elt := range readEntries { } objects[elt.Name] = object } +{{- else if or (eq .PluralType "list") (eq .PluralType "set") }} +objects := make([]{{ $resourceTFStructName }}, len(readEntries)) +for idx, elt := range readEntries { + var object {{ $resourceTFStructName }} + resp.Diagnostics.Append(object.CopyFromPango(ctx, elt, {{ $ev }})...) + if resp.Diagnostics.HasError() { + return + } + objects[idx] = object +} +{{- end }} +{{ if eq .PluralType "map" }} var map_diags diag.Diagnostics state.{{ .ListAttribute.CamelCase }}, map_diags = types.MapValueFrom(ctx, state.getTypeFor("{{ .ListAttribute.Underscore }}"), objects) resp.Diagnostics.Append(map_diags...) if resp.Diagnostics.HasError() { return } +{{- else if eq .PluralType "list" }} +var list_diags diag.Diagnostics +state.{{ .ListAttribute.CamelCase }}, list_diags = types.ListValueFrom(ctx, state.getTypeFor("{{ .ListAttribute.Underscore }}"), objects) +resp.Diagnostics.Append(list_diags...) +if resp.Diagnostics.HasError() { + return +} +{{- else if eq .PluralType "set" }} +var list_diags diag.Diagnostics +state.{{ .ListAttribute.CamelCase }}, list_diags = types.SetValueFrom(ctx, state.getTypeFor("{{ .ListAttribute.Underscore }}"), objects) +resp.Diagnostics.Append(list_diags...) +if resp.Diagnostics.HasError() { + return +} +{{- end }} + +{{- if .ResourceXpathVariablesWithChecks }} +{{ AttributesFromXpathComponents "state" }} +{{- end }} resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) ` @@ -995,12 +1104,16 @@ const resourceReadFunction = ` {{- end }} }) + components, err := savestate.resourceXpathParentComponents() + if err != nil { + resp.Diagnostics.AddError("Error creating resource xpath", err.Error()) + return + } - // Perform the operation. {{- if .HasEntryName }} - object, err := o.manager.Read(ctx, location, savestate.Name.ValueString()) + object, err := o.manager.Read(ctx, location, components, savestate.Name.ValueString()) {{- else }} - object, err := o.manager.Read(ctx, location) + object, err := o.manager.Read(ctx, location, components) {{- end }} if err != nil { if errors.Is(err, sdkmanager.ErrObjectNotFound) { @@ -1032,6 +1145,7 @@ const resourceReadFunction = ` resp.Diagnostics.Append(ev_diags...) {{- end }} +{{ AttributesFromXpathComponents "state" }} // Done. resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) @@ -1063,6 +1177,17 @@ tflog.Info(ctx, "performing resource update", map[string]any{ "function": "Update", }) +{{ $ev := "nil" }} +{{ if .HasEncryptedResources }} + {{- $ev = "&ev" }} +ev := make(map[string]types.String, len(state.EncryptedValues.Elements())) +resp.Diagnostics.Append(state.EncryptedValues.ElementsAs(ctx, &ev, false)...) +if resp.Diagnostics.HasError() { + return +} +{{- end }} + +{{ if eq .PluralType "map" }} var elements map[string]{{ $resourceTFStructName }} resp.Diagnostics.Append(state.{{ .ListAttribute.CamelCase }}.ElementsAs(ctx, &elements, false)...) if resp.Diagnostics.HasError() { @@ -1072,17 +1197,40 @@ if resp.Diagnostics.HasError() { stateEntries := make([]*{{ $resourceSDKStructName }}, len(elements)) idx := 0 for name, elt := range elements { + var entry *{{ .resourceSDKName }}.{{ .EntryOrConfig }} + resp.Diagnostics.Append(elt.CopyToPango(ctx, &entry, {{ $ev }})...) + if resp.Diagnostics.HasError() { + return + } + entry.Name = name + stateEntries[idx] = entry + idx++ +} +{{ else if or (eq .PluralType "list") (eq .PluralType "set") }} +var elements []{{ $resourceTFStructName }} +resp.Diagnostics.Append(state.{{ .ListAttribute.CamelCase }}.ElementsAs(ctx, &elements, false)...) +if resp.Diagnostics.HasError() { + return +} + +stateEntries := make([]*{{ $resourceSDKStructName }}, len(elements)) +for idx, elt := range elements { var entry *{{ $resourceSDKStructName }} resp.Diagnostics.Append(elt.CopyToPango(ctx, &entry, nil)...) if resp.Diagnostics.HasError() { return } - entry.Name = name stateEntries[idx] = entry - idx++ } +{{- end }} -existing, err := r.manager.ReadMany(ctx, location, stateEntries) +components, err := state.resourceXpathParentComponents() +if err != nil { + resp.Diagnostics.AddError("Error creating resource xpath", err.Error()) + return +} + +existing, err := r.manager.ReadMany(ctx, location, components, stateEntries) if err != nil && !errors.Is(err, sdkmanager.ErrObjectNotFound) { resp.Diagnostics.AddError("Error while reading entries from the server", err.Error()) return @@ -1098,6 +1246,7 @@ if resp.Diagnostics.HasError() { return } +{{ if eq .PluralType "map" }} planEntries := make([]*{{ $resourceSDKStructName }}, len(elements)) idx = 0 for name, elt := range elements { @@ -1111,13 +1260,26 @@ for name, elt := range elements { planEntries[idx] = entry idx++ } +{{ else if or (eq .PluralType "list") (eq .PluralType "set") }} +var planEntries []*{{ $resourceSDKStructName }} +for _, elt := range elements { + existingEntry, _ := existingEntriesByName[elt.Name.ValueString()] + resp.Diagnostics.Append(elt.CopyToPango(ctx, &existingEntry, nil)...) + if resp.Diagnostics.HasError() { + return + } + + planEntries = append(planEntries, existingEntry) +} +{{- end }} -processed, err := r.manager.UpdateMany(ctx, location, stateEntries, planEntries) +processed, err := r.manager.UpdateMany(ctx, location, components, stateEntries, planEntries) if err != nil { resp.Diagnostics.AddError("Error while updating entries", err.Error()) return } +{{- if eq .PluralType "map" }} objects := make(map[string]*{{ $resourceTFStructName }}, len(processed)) for _, elt := range processed { var object {{ $resourceTFStructName }} @@ -1129,9 +1291,27 @@ for _, elt := range processed { objects[elt.Name] = &object } +{{- else if or (eq .PluralType "list") (eq .PluralType "set") }} +objects := make([]*{{ $resourceTFStructName }}, len(processed)) +for idx, elt := range processed { + var object {{ $resourceTFStructName }} + resp.Diagnostics.Append(object.CopyFromPango(ctx, elt, {{ $ev }})...) + if resp.Diagnostics.HasError() { + return + } + + objects[idx] = &object +} +{{- end }} var list_diags diag.Diagnostics +{{ if eq .PluralType "map" }} plan.{{ .ListAttribute.CamelCase }}, list_diags = types.MapValueFrom(ctx, state.getTypeFor("{{ .ListAttribute.Underscore }}"), objects) +{{ else if eq .PluralType "list" }} +plan.{{ .ListAttribute.CamelCase }}, list_diags = types.ListValueFrom(ctx, state.getTypeFor("{{ .ListAttribute.Underscore }}"), objects) +{{ else if eq .PluralType "set" }} +plan.{{ .ListAttribute.CamelCase }}, list_diags = types.SetValueFrom(ctx, state.getTypeFor("{{ .ListAttribute.Underscore }}"), objects) +{{- end }} resp.Diagnostics.Append(list_diags...) if resp.Diagnostics.HasError() { return @@ -1214,7 +1394,13 @@ for idx, elt := range elements { planEntries[idx] = entry } -processed, err := r.manager.UpdateMany(ctx, location, stateEntries, planEntries, {{ $exhaustive }}, position) +components, err := state.resourceXpathParentComponents() +if err != nil { + resp.Diagnostics.AddError("Error creating resource xpath", err.Error()) + return +} + +processed, err := r.manager.UpdateMany(ctx, location, components, stateEntries, planEntries, {{ $exhaustive }}, position) if err != nil { resp.Diagnostics.AddError("Failed to udpate entries", err.Error()) } @@ -1275,10 +1461,16 @@ const resourceUpdateFunction = ` return } + components, err := state.resourceXpathParentComponents() + if err != nil { + resp.Diagnostics.AddError("Error creating resource xpath", err.Error()) + return + } + {{- if .HasEntryName }} - obj, err := r.manager.Read(ctx, location, plan.Name.ValueString()) + obj, err := r.manager.Read(ctx, location, components, plan.Name.ValueString()) {{- else }} - obj, err := r.manager.Read(ctx, location) + obj, err := r.manager.Read(ctx, location, components) {{- end }} if err != nil { resp.Diagnostics.AddError("Error in update", err.Error()) @@ -1290,12 +1482,17 @@ const resourceUpdateFunction = ` return } - // Perform the operation. -{{- if .HasEntryName }} - updated, err := r.manager.Update(ctx, location, obj, obj.Name) -{{- else }} - updated, err := r.manager.Update(ctx, location, obj) -{{- end }} + components, err = plan.resourceXpathParentComponents() + if err != nil { + resp.Diagnostics.AddError("Error creating resource xpath", err.Error()) + return + } + +{{ if .HasEntryName }} + updated, err := r.manager.Update(ctx, location, components, obj, obj.Name) +{{ else }} + updated, err := r.manager.Update(ctx, location, components, obj) +{{ end }} if err != nil { resp.Diagnostics.AddError("Error in update", err.Error()) return @@ -1341,13 +1538,13 @@ tflog.Info(ctx, "performing resource delete", map[string]any{ "function": "Delete", }) -{{- if .ResourceIsMap }} +{{- if eq .PluralType "map" }} elements := make(map[string]{{ $resourceTFStructName }}, len(state.{{ .ListAttribute.CamelCase }}.Elements())) resp.Diagnostics.Append(state.{{ .ListAttribute.CamelCase }}.ElementsAs(ctx, &elements, false)...) if resp.Diagnostics.HasError() { return } -{{- else }} +{{- else if or (eq .PluralType "list") (eq .PluralType "set") }} var elements []{{ $resourceTFStructName }} resp.Diagnostics.Append(state.{{ .ListAttribute.CamelCase }}.ElementsAs(ctx, &elements, false)...) if resp.Diagnostics.HasError() { @@ -1359,22 +1556,28 @@ var location {{ .resourceSDKName }}.Location {{ RenderLocationsStateToPango "state.Location" "location" }} var names []string -{{- if .ResourceIsMap }} +{{- if eq .PluralType "map" }} for name, _ := range elements { names = append(names, name) } -{{- else }} +{{- else if or (eq .PluralType "list") (eq .PluralType "set") }} for _, elt := range elements { names = append(names, elt.Name.ValueString()) } {{- end }} -{{- if .ResourceIsMap }} -err := r.manager.Delete(ctx, location, names) -{{- else if .Exhaustive }} -err := r.manager.Delete(ctx, location, names, sdkmanager.Exhaustive) +components, err := state.resourceXpathParentComponents() +if err != nil { + resp.Diagnostics.AddError("Error creating resource xpath", err.Error()) + return +} + +{{- if eq .Exhaustive "exhaustive" }} +err = r.manager.Delete(ctx, location, components, names, sdkmanager.Exhaustive) +{{- else if eq .Exhaustive "non-exhaustive" }} +err = r.manager.Delete(ctx, location, components, names, sdkmanager.NonExhaustive) {{- else }} -err := r.manager.Delete(ctx, location, names, sdkmanager.NonExhaustive) +err = r.manager.Delete(ctx, location, components, names) {{- end }} if err != nil { resp.Diagnostics.AddError("error while deleting entries", err.Error()) @@ -1410,15 +1613,26 @@ const resourceDeleteFunction = ` {{ RenderLocationsStateToPango "state.Location" "location" }} {{- if .HasEntryName }} + components, err := state.resourceXpathParentComponents() + if err != nil { + resp.Diagnostics.AddError("Error creating resource xpath", err.Error()) + return + } {{- if .HasImports }} var importLocation {{ .resourceSDKName }}.ImportLocation {{ RenderImportLocationAssignment "state.Location" "importLocation" }} - err := r.manager.Delete(ctx, location, []{{ .resourceSDKName }}.ImportLocation{importLocation}, []string{state.Name.ValueString()}, sdkmanager.NonExhaustive) + err = r.manager.UnimportFromLocations(ctx, location, []{{ .resourceSDKName }}.ImportLocation{importLocation}, state.Name.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Error in delete", err.Error()) + return + } + err = r.manager.Delete(ctx, location, []{{ .resourceSDKName }}.ImportLocation{importLocation}, components, []string{state.Name.ValueString()}) {{- else }} - err := r.manager.Delete(ctx, location, []string{state.Name.ValueString()}) + err = r.manager.Delete(ctx, location, components, []string{state.Name.ValueString()}) {{- end }} if err != nil && !errors.Is(err, sdkmanager.ErrObjectNotFound) { resp.Diagnostics.AddError("Error in delete", err.Error()) + return } {{- else }} @@ -1436,6 +1650,7 @@ const resourceDeleteFunction = ` err := r.manager.Delete(ctx, location, obj) if err != nil && errors.Is(err, sdkmanager.ErrObjectNotFound) { resp.Diagnostics.AddError("Error in delete", err.Error()) + return } {{- end }} ` @@ -1608,6 +1823,25 @@ func {{ .FuncName }}(ctx context.Context, resource types.Object) ([]byte, error) Name: name, } {{- else if eq .ResourceType "entry-plural" }} + {{- if .HasParent }} + parentAttr, ok := attrs["{{ .ParentAttribute.Underscore }}"] + if !ok { + return nil, fmt.Errorf("{{ .ParentAttribute.Underscore }} attribute missing") + } + + var parent types.String + switch value := parentAttr.(type) { + case types.String: + parent = value + default: + return nil, fmt.Errorf("{{ .ParentAttribute.Underscore }} expected to be a map") + } + + importStruct := {{ .StructNamePrefix }}ImportState{ + Location: location, + {{ .ParentAttribute.CamelCase }}: parent, + } + {{- else }} itemsAttr, ok := attrs["{{ .ListAttribute.Underscore }}"] if !ok { return nil, fmt.Errorf("{{ .ListAttribute.Underscore }} attribute missing") @@ -1642,6 +1876,7 @@ func {{ .FuncName }}(ctx context.Context, resource types.Object) ([]byte, error) Location: location, Names: namesObj, } + {{- end }} {{- else if or (eq .ResourceType "uuid") }} itemsAttr, ok := attrs["{{ .ListAttribute.Underscore }}"] if !ok { @@ -1749,8 +1984,12 @@ const resourceImportStateFunctionTmpl = ` if resp.Diagnostics.HasError() { return } - -{{- if .ResourceIsMap }} +{{- if .HasParent }} + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("{{ .ParentAttribute.Underscore }}"), obj.{{ .ParentAttribute.CamelCase }})...) + {{- if eq .PluralType "" }} + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("name"), obj.Name)...) + {{- end }} +{{- else if eq .PluralType "map" }} names := make(map[string]*{{ .ListStructName }}) var objectNames []string @@ -1767,7 +2006,7 @@ const resourceImportStateFunctionTmpl = ` names[elt] = object } resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("{{ .ListAttribute.Underscore }}"), names)...) -{{- else if .ResourceIsList }} +{{- else if eq .PluralType "list" }} {{- if .HasPosition }} resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("position"), obj.Position)...) if resp.Diagnostics.HasError() { @@ -1851,6 +2090,8 @@ type {{ dataSourceStructName }}Filter struct { {{ RenderCopyFromPangoFunctions }} +{{ RenderXpathComponentsGetter }} + {{ RenderDataSourceSchema }} {{- if HasLocations }} @@ -1886,7 +2127,8 @@ func (d *{{ dataSourceStructName }}) Configure(_ context.Context, req datasource resp.Diagnostics.AddError("Failed to configure SDK client", err.Error()) return } - d.manager = sdkmanager.NewImportableEntryObjectManager(d.client, {{ resourceSDKName }}.NewService(d.client), specifier, {{ resourceSDKName }}.SpecMatches) + batchSize := providerData.MultiConfigBatchSize + d.manager = sdkmanager.NewImportableEntryObjectManager(d.client, {{ resourceSDKName }}.NewService(d.client), batchSize, specifier, {{ resourceSDKName }}.SpecMatches) {{- else if IsEntry }} specifier, _, err := {{ resourceSDKName }}.Versioning(d.client.Versioning()) if err != nil { @@ -1894,7 +2136,7 @@ func (d *{{ dataSourceStructName }}) Configure(_ context.Context, req datasource return } batchSize := providerData.MultiConfigBatchSize - d.manager = sdkmanager.NewEntryObjectManager(d.client, {{ resourceSDKName }}.NewService(d.client), batchSize, specifier, {{ resourceSDKName }}.SpecMatches) + d.manager = sdkmanager.NewEntryObjectManager[*{{ resourceSDKName }}.Entry, {{ resourceSDKName }}.Location, *{{ resourceSDKName }}.Service](d.client, {{ resourceSDKName }}.NewService(d.client), batchSize, specifier, {{ resourceSDKName }}.SpecMatches) {{- else if IsUuid }} specifier, _, err := {{ resourceSDKName }}.Versioning(d.client.Versioning()) if err != nil { @@ -1902,7 +2144,7 @@ func (d *{{ dataSourceStructName }}) Configure(_ context.Context, req datasource return } batchSize := providerData.MultiConfigBatchSize - d.manager = sdkmanager.NewUuidObjectManager(d.client, {{ resourceSDKName }}.NewService(d.client), batchSize, specifier, {{ resourceSDKName }}.SpecMatches) + d.manager = sdkmanager.NewUuidObjectManager[*{{ resourceSDKName }}.Entry, {{ resourceSDKName }}.Location, *{{ resourceSDKName }}.Service](d.client, {{ resourceSDKName }}.NewService(d.client), batchSize, specifier, {{ resourceSDKName }}.SpecMatches) {{- else if IsConfig }} specifier, _, err := {{ resourceSDKName }}.Versioning(d.client.Versioning()) if err != nil { diff --git a/pkg/translate/terraform_provider/terraform_provider_file.go b/pkg/translate/terraform_provider/terraform_provider_file.go index d1645882..272bb231 100644 --- a/pkg/translate/terraform_provider/terraform_provider_file.go +++ b/pkg/translate/terraform_provider/terraform_provider_file.go @@ -159,6 +159,16 @@ func (g *GenerateTerraformProvider) GenerateTerraformResource(resourceTyp proper panic("unreachable") }, + "IsResourcePlural": func() bool { + switch resourceTyp { + case properties.ResourceEntryPlural, properties.ResourceUuid, properties.ResourceUuidPlural: + return true + case properties.ResourceEntry, properties.ResourceConfig, properties.ResourceCustom: + return false + } + + panic("unreachable") + }, "tfresourcepkg": func() string { if spec.TerraformProviderConfig.Ephemeral { return "ephemeral" @@ -188,6 +198,9 @@ func (g *GenerateTerraformProvider) GenerateTerraformResource(resourceTyp proper "RenderCopyFromPangoFunctions": func() (string, error) { return RenderCopyFromPangoFunctions(resourceTyp, names.PackageName, names.ResourceStructName, spec) }, + "RenderXpathComponentsGetter": func() (string, error) { + return RenderXpathComponentsGetter(names.ResourceStructName, spec) + }, "ResourceCreateFunction": func(structName string, serviceName string) (string, error) { return ResourceCreateFunction(resourceTyp, names, serviceName, spec, terraformProvider, names.PackageName) }, @@ -234,6 +247,7 @@ func (g *GenerateTerraformProvider) GenerateTerraformResource(resourceTyp proper if !spec.TerraformProviderConfig.SkipResource { terraformProvider.ImportManager.AddStandardImport("context", "") terraformProvider.ImportManager.AddSdkImport("github.com/PaloAltoNetworks/pango", "") + if spec.TerraformProviderConfig.ResourceType != properties.TerraformResourceCustom { terraformProvider.ImportManager.AddOtherImport("github.com/PaloAltoNetworks/terraform-provider-panos/internal/manager", "sdkmanager") } @@ -256,6 +270,11 @@ func (g *GenerateTerraformProvider) GenerateTerraformResource(resourceTyp proper case properties.ResourceCustom, properties.ResourceConfig: } + switch resourceTyp { + case properties.ResourceEntry, properties.ResourceConfig: + case properties.ResourceEntryPlural, properties.ResourceUuid, properties.ResourceUuidPlural, properties.ResourceCustom: + } + // Generate Resource with entry style terraformProvider.ImportManager.AddStandardImport("fmt", "") @@ -283,6 +302,11 @@ func (g *GenerateTerraformProvider) GenerateTerraformResource(resourceTyp proper terraformProvider.ImportManager.AddHashicorpImport("github.com/hashicorp/terraform-plugin-log/tflog", "") } + if spec.ResourceXpathVariablesWithChecks(false) { + terraformProvider.ImportManager.AddStandardImport("strings", "") + terraformProvider.ImportManager.AddSdkImport("github.com/PaloAltoNetworks/pango/util", "pangoutil") + } + err := g.generateTerraformEntityTemplate(resourceTyp, schemaTyp, names, spec, terraformProvider, resourceObj, funcMap) if err != nil { return err @@ -302,6 +326,7 @@ func (g *GenerateTerraformProvider) GenerateTerraformResource(resourceTyp proper case properties.ResourceEntryPlural, properties.ResourceUuid: case properties.ResourceUuidPlural, properties.ResourceCustom: } + conditionallyAddValidators(terraformProvider.ImportManager, spec) conditionallyAddModifiers(terraformProvider.ImportManager, spec) conditionallyAddDefaults(terraformProvider.ImportManager, spec.Spec) @@ -322,6 +347,11 @@ func (g *GenerateTerraformProvider) GenerateTerraformResource(resourceTyp proper terraformProvider.ImportManager.AddHashicorpImport("github.com/hashicorp/terraform-plugin-log/tflog", "") } + if spec.ResourceXpathVariablesWithChecks(false) { + terraformProvider.ImportManager.AddStandardImport("strings", "") + terraformProvider.ImportManager.AddSdkImport("github.com/PaloAltoNetworks/pango/util", "pangoutil") + } + err := g.generateTerraformEntityTemplate(resourceTyp, schemaTyp, names, spec, terraformProvider, resourceObj, funcMap) if err != nil { return err @@ -355,8 +385,18 @@ func (g *GenerateTerraformProvider) GenerateTerraformDataSource(resourceTyp prop names := NewNameProvider(spec, resourceTyp) funcMap := template.FuncMap{ - "GoSDKSkipped": func() bool { return spec.GoSdkSkip }, - "IsEntry": func() bool { return spec.HasEntryName() && !spec.HasEntryUuid() }, + "GoSDKSkipped": func() bool { return spec.GoSdkSkip }, + "IsEntry": func() bool { return spec.HasEntryName() && !spec.HasEntryUuid() }, + "IsResourcePlural": func() bool { + switch resourceTyp { + case properties.ResourceEntryPlural, properties.ResourceUuid, properties.ResourceUuidPlural: + return true + case properties.ResourceEntry, properties.ResourceConfig, properties.ResourceCustom: + return false + } + + panic("unreachable") + }, "HasImports": func() bool { return len(spec.Imports) > 0 }, "HasLocations": func() bool { return len(spec.Locations) > 0 }, "IsCustom": func() bool { return spec.TerraformProviderConfig.ResourceType == properties.TerraformResourceCustom }, @@ -383,6 +423,9 @@ func (g *GenerateTerraformProvider) GenerateTerraformDataSource(resourceTyp prop "RenderCopyFromPangoFunctions": func() (string, error) { return RenderCopyFromPangoFunctions(resourceTyp, names.PackageName, names.DataSourceStructName, spec) }, + "RenderXpathComponentsGetter": func() (string, error) { + return RenderXpathComponentsGetter(names.DataSourceStructName, spec) + }, "RenderDataSourceStructs": func() (string, error) { return RenderDataSourceStructs(resourceTyp, names, spec) }, "RenderDataSourceSchema": func() (string, error) { return RenderDataSourceSchema(resourceTyp, names, spec, terraformProvider.ImportManager) diff --git a/pkg/translate/translate_suite_test.go b/pkg/translate/translate_suite_test.go new file mode 100644 index 00000000..05beb9eb --- /dev/null +++ b/pkg/translate/translate_suite_test.go @@ -0,0 +1,18 @@ +package translate_test + +import ( + "log/slog" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestMovement(t *testing.T) { + handler := slog.NewTextHandler(GinkgoWriter, &slog.HandlerOptions{ + Level: slog.LevelDebug, + }) + slog.SetDefault(slog.New(handler)) + RegisterFailHandler(Fail) + RunSpecs(t, "Translate Suite") +} diff --git a/pkg/version/version.go b/pkg/version/version.go index f0f610f9..8e937680 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -14,6 +14,15 @@ type Version struct { Hotfix string } +func MustNewVersionFromString(version string) *Version { + result, err := NewVersionFromString(version) + if err != nil { + panic(err) + } + + return &result +} + // NewVersionFromString creates a new Version value from a given string func NewVersionFromString(version string) (Version, error) { parts := strings.Split(version, ".") diff --git a/specs/device/adminrole.yaml b/specs/device/adminrole.yaml index 4e73e35b..7df47acc 100644 --- a/specs/device/adminrole.yaml +++ b/specs/device/adminrole.yaml @@ -14,8 +14,10 @@ go_sdk_config: package: - device - adminrole -xpath_suffix: -- admin-role +panos_xpath: + path: + - admin-role + vars: [] locations: - name: template xpath: @@ -2338,6 +2340,27 @@ spec: - value: disable description: '' required: false + - name: dhcp-syslog-server + type: enum + profiles: + - xpath: + - dhcp-syslog-server + min_version: 11.0.2 + max_version: 11.0.3 + validators: + - type: values + spec: + values: + - enable + - read-only + - disable + spec: + values: + - value: enable + - value: read-only + - value: disable + description: '' + required: false - name: dynamic-updates type: enum profiles: @@ -3339,27 +3362,6 @@ spec: - value: disable description: '' required: false - - name: dhcp-syslog-server - type: enum - profiles: - - xpath: - - dhcp-syslog-server - min_version: 11.0.2 - max_version: 11.0.3 - validators: - - type: values - spec: - values: - - enable - - read-only - - disable - spec: - values: - - value: enable - - value: read-only - - value: disable - description: '' - required: false variants: [] description: '' required: false @@ -5125,11 +5127,13 @@ spec: - value: disable description: '' required: false - - name: virtual-routers + - name: secure-web-gateway type: enum profiles: - xpath: - - virtual-routers + - secure-web-gateway + min_version: 11.0.2 + max_version: 11.0.3 validators: - type: values spec: @@ -5144,11 +5148,11 @@ spec: - value: disable description: '' required: false - - name: virtual-wires + - name: virtual-routers type: enum profiles: - xpath: - - virtual-wires + - virtual-routers validators: - type: values spec: @@ -5163,11 +5167,11 @@ spec: - value: disable description: '' required: false - - name: vlans + - name: virtual-wires type: enum profiles: - xpath: - - vlans + - virtual-wires validators: - type: values spec: @@ -5182,11 +5186,11 @@ spec: - value: disable description: '' required: false - - name: zones + - name: vlans type: enum profiles: - xpath: - - zones + - vlans validators: - type: values spec: @@ -5201,13 +5205,11 @@ spec: - value: disable description: '' required: false - - name: secure-web-gateway + - name: zones type: enum profiles: - xpath: - - secure-web-gateway - min_version: 11.0.2 - max_version: 11.0.3 + - zones validators: - type: values spec: @@ -6416,11 +6418,13 @@ spec: validators: [] spec: params: - - name: partial-save + - name: object-level-changes type: enum profiles: - xpath: - - partial-save + - object-level-changes + min_version: 11.0.2 + max_version: 11.0.3 validators: - type: values spec: @@ -6433,11 +6437,11 @@ spec: - value: disable description: '' required: false - - name: save-for-other-admins + - name: partial-save type: enum profiles: - xpath: - - save-for-other-admins + - partial-save validators: - type: values spec: @@ -6450,13 +6454,11 @@ spec: - value: disable description: '' required: false - - name: object-level-changes + - name: save-for-other-admins type: enum profiles: - xpath: - - object-level-changes - min_version: 11.0.2 - max_version: 11.0.3 + - save-for-other-admins validators: - type: values spec: @@ -6956,11 +6958,13 @@ spec: - value: disable description: '' required: false - - name: zones + - name: sdwan-interface-profiles type: enum profiles: - xpath: - - zones + - sdwan-interface-profiles + min_version: 11.0.2 + max_version: 11.0.3 validators: - type: values spec: @@ -6975,13 +6979,11 @@ spec: - value: disable description: '' required: false - - name: sdwan-interface-profiles + - name: zones type: enum profiles: - xpath: - - sdwan-interface-profiles - min_version: 11.0.2 - max_version: 11.0.3 + - zones validators: - type: values spec: @@ -8336,6 +8338,27 @@ spec: - value: disable description: '' required: false + - name: dhcp-syslog-server + type: enum + profiles: + - xpath: + - dhcp-syslog-server + min_version: 11.0.2 + max_version: 11.0.3 + validators: + - type: values + spec: + values: + - enable + - read-only + - disable + spec: + values: + - value: enable + - value: read-only + - value: disable + description: '' + required: false - name: local-user-database type: object profiles: @@ -9047,27 +9070,6 @@ spec: - value: disable description: '' required: false - - name: dhcp-syslog-server - type: enum - profiles: - - xpath: - - dhcp-syslog-server - min_version: 11.0.2 - max_version: 11.0.3 - validators: - - type: values - spec: - values: - - enable - - read-only - - disable - spec: - values: - - value: enable - - value: read-only - - value: disable - description: '' - required: false variants: [] description: '' required: false @@ -11319,11 +11321,13 @@ spec: validators: [] spec: params: - - name: partial-save + - name: object-level-changes type: enum profiles: - xpath: - - partial-save + - object-level-changes + min_version: 11.0.2 + max_version: 11.0.3 validators: - type: values spec: @@ -11336,11 +11340,11 @@ spec: - value: disable description: '' required: false - - name: save-for-other-admins + - name: partial-save type: enum profiles: - xpath: - - save-for-other-admins + - partial-save validators: - type: values spec: @@ -11353,13 +11357,11 @@ spec: - value: disable description: '' required: false - - name: object-level-changes + - name: save-for-other-admins type: enum profiles: - xpath: - - object-level-changes - min_version: 11.0.2 - max_version: 11.0.3 + - save-for-other-admins validators: - type: values spec: diff --git a/specs/device/api_key.yaml b/specs/device/api_key.yaml index 45e806d2..f2501955 100644 --- a/specs/device/api_key.yaml +++ b/specs/device/api_key.yaml @@ -11,7 +11,8 @@ terraform_provider_config: go_sdk_config: skip: true package: ["api_key"] -xpath_suffix: ["api-key"] +panos_xpath: + path: ["api-key"] locations: [] entries: [] imports: [] diff --git a/specs/device/dns.yaml b/specs/device/dns.yaml index 65a622ce..7b985531 100644 --- a/specs/device/dns.yaml +++ b/specs/device/dns.yaml @@ -15,7 +15,9 @@ go_sdk_config: - device - services - dns -xpath_suffix: [] +panos_xpath: + path: [] + vars: [] locations: - name: system xpath: diff --git a/specs/device/dynamic-updates.yaml b/specs/device/dynamic-updates.yaml index b05f956d..1f7d1a0e 100644 --- a/specs/device/dynamic-updates.yaml +++ b/specs/device/dynamic-updates.yaml @@ -14,7 +14,9 @@ go_sdk_config: package: - device - dynamicupdates -xpath_suffix: [] +panos_xpath: + path: [] + vars: [] locations: - name: system xpath: @@ -204,6 +206,7 @@ spec: variants: [] description: Everyday required: false + variant_group_id: 0 - name: hourly type: object profiles: @@ -245,6 +248,7 @@ spec: variants: [] description: Every hour required: false + variant_group_id: 0 - name: none type: object profiles: @@ -256,6 +260,7 @@ spec: variants: [] description: No schedule required: false + variant_group_id: 0 - name: weekly type: object profiles: @@ -324,6 +329,7 @@ spec: variants: [] description: Once a week required: false + variant_group_id: 0 description: '' required: false variants: [] @@ -409,6 +415,7 @@ spec: variants: [] description: Everyday required: false + variant_group_id: 0 - name: none type: object profiles: @@ -420,6 +427,7 @@ spec: variants: [] description: No schedule required: false + variant_group_id: 0 - name: weekly type: object profiles: @@ -488,6 +496,7 @@ spec: variants: [] description: Once a week required: false + variant_group_id: 0 description: '' required: false variants: [] @@ -551,6 +560,7 @@ spec: variants: [] description: Everyday required: false + variant_group_id: 0 - name: hourly type: object profiles: @@ -592,6 +602,7 @@ spec: variants: [] description: Every hour required: false + variant_group_id: 0 - name: none type: object profiles: @@ -603,6 +614,7 @@ spec: variants: [] description: No schedule required: false + variant_group_id: 0 - name: weekly type: object profiles: @@ -671,6 +683,7 @@ spec: variants: [] description: Once a week required: false + variant_group_id: 0 description: '' required: false variants: [] @@ -732,6 +745,7 @@ spec: variants: [] description: Everyday required: false + variant_group_id: 0 - name: hourly type: object profiles: @@ -773,6 +787,7 @@ spec: variants: [] description: Every hour required: false + variant_group_id: 0 - name: none type: object profiles: @@ -784,6 +799,7 @@ spec: variants: [] description: No schedule required: false + variant_group_id: 0 - name: weekly type: object profiles: @@ -852,6 +868,7 @@ spec: variants: [] description: Once a week required: false + variant_group_id: 0 description: '' required: false variants: [] @@ -1042,6 +1059,7 @@ spec: variants: [] description: Everyday required: false + variant_group_id: 0 - name: every-30-mins type: object profiles: @@ -1092,6 +1110,7 @@ spec: variants: [] description: Every 0, 30 minutes past the hour required: false + variant_group_id: 0 - name: hourly type: object profiles: @@ -1142,6 +1161,7 @@ spec: variants: [] description: Every hour required: false + variant_group_id: 0 - name: none type: object profiles: @@ -1153,6 +1173,7 @@ spec: variants: [] description: No schedule required: false + variant_group_id: 0 - name: weekly type: object profiles: @@ -1230,6 +1251,7 @@ spec: variants: [] description: Once a week required: false + variant_group_id: 0 description: '' required: false variants: [] @@ -1302,6 +1324,7 @@ spec: variants: [] description: Every 15 minutes required: false + variant_group_id: 0 - name: every-30-mins type: object profiles: @@ -1343,6 +1366,7 @@ spec: variants: [] description: Every 30 minutes required: false + variant_group_id: 0 - name: every-5-mins type: object profiles: @@ -1384,6 +1408,7 @@ spec: variants: [] description: Every 5 minutes required: false + variant_group_id: 0 - name: every-hour type: object profiles: @@ -1425,6 +1450,7 @@ spec: variants: [] description: Every hour required: false + variant_group_id: 0 - name: none type: object profiles: @@ -1436,6 +1462,7 @@ spec: variants: [] description: No schedule required: false + variant_group_id: 0 description: '' required: false variants: [] @@ -1508,6 +1535,7 @@ spec: variants: [] description: Every 0, 15, 30, 45 minutes past the hour required: false + variant_group_id: 0 - name: every-30-mins type: object profiles: @@ -1558,6 +1586,7 @@ spec: variants: [] description: Every 0, 30 minutes past the hour required: false + variant_group_id: 0 - name: every-hour type: object profiles: @@ -1608,6 +1637,7 @@ spec: variants: [] description: Every 0 minutes past the hour required: false + variant_group_id: 0 - name: every-min type: object profiles: @@ -1645,6 +1675,7 @@ spec: variants: [] description: Every minute required: false + variant_group_id: 0 - name: none type: object profiles: @@ -1656,6 +1687,7 @@ spec: variants: [] description: No schedule required: false + variant_group_id: 0 - name: real-time type: object profiles: @@ -1667,6 +1699,7 @@ spec: variants: [] description: Real-time Streaming required: false + variant_group_id: 0 description: '' required: false variants: [] diff --git a/specs/device/ntp.yaml b/specs/device/ntp.yaml index d08f1ba1..95db70f4 100644 --- a/specs/device/ntp.yaml +++ b/specs/device/ntp.yaml @@ -15,7 +15,9 @@ go_sdk_config: - device - services - ntp -xpath_suffix: [] +panos_xpath: + path: [] + vars: [] locations: - name: system xpath: diff --git a/specs/device/ssl-decrypt.yaml b/specs/device/ssl-decrypt.yaml index dd597f2a..01361279 100644 --- a/specs/device/ssl-decrypt.yaml +++ b/specs/device/ssl-decrypt.yaml @@ -14,7 +14,9 @@ go_sdk_config: package: - device - ssldecrypt -xpath_suffix: [] +panos_xpath: + path: [] + vars: [] locations: - name: panorama xpath: @@ -213,6 +215,58 @@ spec: type: string description: List of disabled predefined exclude certificates. required: false + - name: forward-trust-certificate-ecdsa + type: string + profiles: + - xpath: + - forward-trust-certificate + - ecdsa + validators: + - type: length + spec: + max: 63 + spec: {} + description: Forward trust ECDSA certificate. + required: false + - name: forward-trust-certificate-rsa + type: string + profiles: + - xpath: + - forward-trust-certificate + - rsa + validators: + - type: length + spec: + max: 63 + spec: {} + description: Forward trust RSA certificate. + required: false + - name: forward-untrust-certificate-ecdsa + type: string + profiles: + - xpath: + - forward-untrust-certificate + - ecdsa + validators: + - type: length + spec: + max: 63 + spec: {} + description: Forward untrust ECDSA certificate. + required: false + - name: forward-untrust-certificate-rsa + type: string + profiles: + - xpath: + - forward-untrust-certificate + - rsa + validators: + - type: length + spec: + max: 63 + spec: {} + description: Forward untrust RSA certificate. + required: false - name: root-ca-exclude-list type: list profiles: @@ -278,56 +332,4 @@ spec: type: string description: List of trusted root CAs. required: false - - name: forward-trust-certificate-ecdsa - type: string - profiles: - - xpath: - - forward-trust-certificate - - ecdsa - validators: - - type: length - spec: - max: 63 - spec: {} - description: Forward trust ECDSA certificate. - required: false - - name: forward-trust-certificate-rsa - type: string - profiles: - - xpath: - - forward-trust-certificate - - rsa - validators: - - type: length - spec: - max: 63 - spec: {} - description: Forward trust RSA certificate. - required: false - - name: forward-untrust-certificate-ecdsa - type: string - profiles: - - xpath: - - forward-untrust-certificate - - ecdsa - validators: - - type: length - spec: - max: 63 - spec: {} - description: Forward untrust ECDSA certificate. - required: false - - name: forward-untrust-certificate-rsa - type: string - profiles: - - xpath: - - forward-untrust-certificate - - rsa - validators: - - type: length - spec: - max: 63 - spec: {} - description: Forward untrust RSA certificate. - required: false variants: [] diff --git a/specs/network/ike-gateway.yaml b/specs/network/ike-gateway.yaml index 89bb1ce1..bc31b7a8 100644 --- a/specs/network/ike-gateway.yaml +++ b/specs/network/ike-gateway.yaml @@ -6,724 +6,730 @@ terraform_provider_config: resource_type: entry resource_variants: [] suffix: ike_gateway - plural_suffix: '' - plural_name: '' - plural_description: '' + plural_suffix: "" + plural_name: "" + plural_description: "" go_sdk_config: skip: false package: - - crypto - - ike - - gateway -xpath_suffix: -- network -- ike -- gateway + - crypto + - ike + - gateway +panos_xpath: + path: + - network + - ike + - gateway locations: -- 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 -- 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 + - 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 entries: -- name: name - description: '' - validators: [] + - name: name + description: "" + validators: [] imports: [] spec: params: - - name: authentication - type: object - profiles: - - xpath: - - authentication - validators: [] - spec: - params: [] - variants: - - name: certificate - type: object - profiles: + - name: authentication + type: object + profiles: - xpath: - - certificate - validators: [] - spec: - params: - - name: allow-id-payload-mismatch - type: bool - profiles: - - xpath: - - allow-id-payload-mismatch - validators: [] - spec: {} - description: Permit peer identification and certificate payload identification - mismatch - required: false - - name: certificate-profile - type: string + - authentication + validators: [] + spec: + params: [] + variants: + - name: certificate + type: object profiles: - - xpath: - - certificate-profile + - xpath: + - certificate validators: [] - spec: {} - description: Profile for certificate valdiation during IKE negotiation + spec: + params: + - name: allow-id-payload-mismatch + type: bool + profiles: + - xpath: + - allow-id-payload-mismatch + validators: [] + spec: {} + description: + Permit peer identification and certificate payload identification + mismatch + required: false + - name: certificate-profile + type: string + profiles: + - xpath: + - certificate-profile + validators: [] + spec: {} + description: Profile for certificate valdiation during IKE negotiation + required: false + - name: local-certificate + type: object + profiles: + - xpath: + - local-certificate + validators: [] + spec: + params: + - name: hash-and-url + type: object + profiles: + - xpath: + - hash-and-url + validators: [] + spec: + params: + - name: base-url + type: string + profiles: + - xpath: + - base-url + validators: + - type: length + spec: + max: 1024 + spec: {} + description: + The host and directory part of URL for local certificates(http + only) + required: false + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: Use hash-and-url for local certificate + required: false + variants: [] + description: "" + required: false + - name: name + type: string + profiles: + - xpath: + - name + validators: + - type: length + spec: + max: 255 + spec: {} + description: Local certificate name + required: false + variants: [] + description: "" + required: false + - name: strict-validation-revocation + type: bool + profiles: + - xpath: + - strict-validation-revocation + validators: [] + spec: {} + description: Enable strict validation of peer's extended key use + required: false + - name: use-management-as-source + type: bool + profiles: + - xpath: + - use-management-as-source + validators: [] + spec: {} + description: Use management interface IP as source to retrieve http certificates + required: false + variants: [] + description: Use RSA or ECDSA digital signature authentication required: false - - name: local-certificate + - name: pre-shared-key type: object profiles: - - xpath: - - local-certificate + - xpath: + - pre-shared-key validators: [] spec: params: - - name: hash-and-url - type: object - profiles: - - xpath: - - hash-and-url - validators: [] - spec: - params: - - name: base-url - type: string - profiles: + - name: key + type: string + profiles: - xpath: - - base-url - validators: + - key + validators: - type: length spec: - max: 1024 - spec: {} - description: The host and directory part of URL for local certificates(http - only) - required: false - - name: enable - type: bool - profiles: - - xpath: - - enable - validators: [] - spec: {} - description: Use hash-and-url for local certificate - required: false - variants: [] - description: '' - required: false - - name: name - type: string - profiles: - - xpath: - - name - validators: - - type: length - spec: - max: 255 - spec: {} - description: Local certificate name - required: false + max: 255 + spec: {} + description: the string used as pre-shared key + required: false variants: [] - description: '' + description: Use pre-shared key for mutual authentication required: false - - name: strict-validation-revocation - type: bool + description: Authentication method + required: false + - name: comment + type: string + profiles: + - xpath: + - comment + validators: + - type: length + spec: + min: 0 + max: 1023 + spec: {} + description: "" + required: false + - name: disabled + type: bool + profiles: + - xpath: + - disabled + validators: [] + spec: {} + description: Disable the IKE gateway + required: false + - name: ipv6 + type: bool + profiles: + - xpath: + - ipv6 + validators: [] + spec: {} + description: use IPv6 for the IKE gateway + required: false + - name: local-address + type: object + profiles: + - xpath: + - local-address + validators: [] + spec: + params: + - name: interface + type: string profiles: - - xpath: - - strict-validation-revocation + - xpath: + - interface validators: [] spec: {} - description: Enable strict validation of peer's extended key use + description: local gateway end-point required: false - - name: use-management-as-source - type: bool + variants: + - name: floating-ip + type: string profiles: - - xpath: - - use-management-as-source + - xpath: + - floating-ip validators: [] spec: {} - description: Use management interface IP as source to retrieve http certificates + description: Floating IP address in HA Active-Active configuration required: false - variants: [] - description: Use RSA or ECDSA digital signature authentication - required: false - - name: pre-shared-key - type: object - profiles: - - xpath: - - pre-shared-key - validators: [] - spec: - params: - - name: key + codegen_overrides: + terraform: + variant_check: ConflictsWith + - name: ip type: string profiles: - - xpath: - - key + - xpath: + - ip validators: - - type: length - spec: - max: 255 + - type: length + spec: + max: 63 spec: {} - description: the string used as pre-shared key + description: specify exact IP address if interface has multiple addresses required: false - variants: [] - description: Use pre-shared key for mutual authentication - required: false - description: Authentication method - required: false - - name: comment - type: string - profiles: - - xpath: - - comment - validators: - - type: length - spec: - min: 0 - max: 1023 - spec: {} - description: '' - required: false - - name: disabled - type: bool - profiles: - - xpath: - - disabled - validators: [] - spec: {} - description: Disable the IKE gateway - required: false - - name: ipv6 - type: bool - profiles: - - xpath: - - ipv6 - validators: [] - spec: {} - description: use IPv6 for the IKE gateway - required: false - - name: local-address - type: object - profiles: - - xpath: - - local-address - validators: [] - spec: - params: - - name: interface - type: string - profiles: - - xpath: - - interface - validators: [] - spec: {} - description: local gateway end-point - required: false - variants: - - name: floating-ip - type: string - profiles: - - xpath: - - floating-ip - validators: [] - spec: {} - description: Floating IP address in HA Active-Active configuration - required: false - codegen_overrides: - terraform: - variant_check: ConflictsWith - - name: ip - type: string - profiles: - - xpath: - - ip - validators: - - type: length - spec: - max: 63 - spec: {} - description: specify exact IP address if interface has multiple addresses - required: false - description: IKE gateway local IP configuration - required: false - - name: local-id - type: object - profiles: - - xpath: - - local-id - validators: [] - spec: - params: - - name: id - type: string - profiles: - - xpath: - - id - validators: - - type: length - spec: - min: 1 - max: 1024 - spec: {} - description: Local ID string - required: false - - name: type - type: string - profiles: - - xpath: - - type - validators: [] - spec: {} - description: '' - required: false - variants: [] - description: optionally how peer gateway will identify local gateway instead of - using IP address - required: false - - name: peer-address - type: object - profiles: - - xpath: - - peer-address - validators: [] - spec: - params: [] - variants: - - name: dynamic - type: object - profiles: - - xpath: - - dynamic - validators: [] - spec: - params: [] - variants: [] - description: peer gateway has dynamic IP address - required: false - - name: fqdn - type: string - profiles: - - xpath: - - fqdn - validators: - - type: length - spec: - max: 255 - spec: {} - description: peer gateway FQDN name - required: false - - name: ip - type: string - profiles: - - xpath: - - ip - validators: [] - spec: {} - description: peer gateway has static IP address - required: false - description: Peer gateway address - required: false - - name: peer-id - type: object - profiles: - - xpath: - - peer-id - validators: [] - spec: - params: - - name: id - type: string - profiles: - - xpath: - - id - validators: - - type: length - spec: - min: 1 - max: 1024 - spec: {} - description: Peer ID string - required: false - - name: matching - type: enum - profiles: - - xpath: - - matching - validators: - - type: values - spec: - values: - - exact - - wildcard - spec: - default: exact - values: - - value: exact - - value: wildcard - description: Enable peer ID wildcard match for certificate authentication - required: false - - name: type - type: string - profiles: + description: IKE gateway local IP configuration + required: false + - name: local-id + type: object + profiles: - xpath: - - type - validators: [] - spec: {} - description: '' - required: false - variants: [] - description: optionally how local gateway will identify peer gateway instead of - using IP address - required: false - - name: protocol - type: object - profiles: - - xpath: - - protocol - validators: [] - spec: - params: - - name: ikev1 - type: object - profiles: + - local-id + validators: [] + spec: + params: + - name: id + type: string + profiles: + - xpath: + - id + validators: + - type: length + spec: + min: 1 + max: 1024 + spec: {} + description: Local ID string + required: false + - name: type + type: string + profiles: + - xpath: + - type + validators: [] + spec: {} + description: "" + required: false + variants: [] + description: + optionally how peer gateway will identify local gateway instead of + using IP address + required: false + - name: peer-address + type: object + profiles: - xpath: - - ikev1 - validators: [] - spec: - params: - - name: dpd + - peer-address + validators: [] + spec: + params: [] + variants: + - name: dynamic type: object profiles: - - xpath: - - dpd + - xpath: + - dynamic validators: [] spec: - params: - - name: enable - type: bool - profiles: - - xpath: - - enable - validators: [] - spec: {} - description: Enable Dead-Peer-Detection - required: false - - name: interval - type: int64 - profiles: - - xpath: - - interval - validators: - - type: length - spec: - min: 2 - max: 100 + params: [] + variants: [] + description: peer gateway has dynamic IP address + required: false + - name: fqdn + type: string + profiles: + - xpath: + - fqdn + validators: + - type: length spec: - default: 5 - description: sending interval for probing packets (in seconds) - required: false - - name: retry - type: int64 - profiles: - - xpath: - - retry - validators: - - type: length - spec: - min: 2 - max: 100 + max: 255 + spec: {} + description: peer gateway FQDN name + required: false + - name: ip + type: string + profiles: + - xpath: + - ip + validators: [] + spec: {} + description: peer gateway has static IP address + required: false + description: Peer gateway address + required: false + - name: peer-id + type: object + profiles: + - xpath: + - peer-id + validators: [] + spec: + params: + - name: id + type: string + profiles: + - xpath: + - id + validators: + - type: length spec: - default: 5 - description: number of retries before disconnection - required: false - variants: [] - description: Dead-Peer-Detection settings + min: 1 + max: 1024 + spec: {} + description: Peer ID string required: false - - name: exchange-mode + - name: matching type: enum profiles: - - xpath: - - exchange-mode + - xpath: + - matching validators: - - type: values - spec: - values: - - auto - - main - - aggressive + - type: values + spec: + values: + - exact + - wildcard spec: - default: auto + default: exact values: - - value: auto - - value: main - - value: aggressive - description: Exchange mode + - value: exact + - value: wildcard + description: Enable peer ID wildcard match for certificate authentication required: false - - name: ike-crypto-profile + - name: type type: string profiles: - - xpath: - - ike-crypto-profile + - xpath: + - type validators: [] - spec: - default: default - description: IKE SA crypto profile name + spec: {} + description: "" required: false - variants: [] - description: IKEv1 setting - required: false - - name: ikev2 - type: object - profiles: + variants: [] + description: + optionally how local gateway will identify peer gateway instead of + using IP address + required: false + - name: protocol + type: object + profiles: - xpath: - - ikev2 - validators: [] - spec: - params: - - name: dpd + - protocol + validators: [] + spec: + params: + - name: ikev1 type: object profiles: - - xpath: - - dpd + - xpath: + - ikev1 validators: [] spec: params: - - name: enable - type: bool - profiles: - - xpath: - - enable - validators: [] - spec: {} - description: Enable sending empty information liveness check message - required: false - - name: interval - type: int64 - profiles: - - xpath: - - interval - validators: - - type: length + - name: dpd + type: object + profiles: + - xpath: + - dpd + validators: [] spec: - min: 2 - max: 100 - spec: - default: 5 - description: delay interval before sending probing packets (in seconds) - required: false + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: Enable Dead-Peer-Detection + required: false + - name: interval + type: int64 + profiles: + - xpath: + - interval + validators: + - type: length + spec: + min: 2 + max: 100 + spec: + default: 5 + description: sending interval for probing packets (in seconds) + required: false + - name: retry + type: int64 + profiles: + - xpath: + - retry + validators: + - type: length + spec: + min: 2 + max: 100 + spec: + default: 5 + description: number of retries before disconnection + required: false + variants: [] + description: Dead-Peer-Detection settings + required: false + - name: exchange-mode + type: enum + profiles: + - xpath: + - exchange-mode + validators: + - type: values + spec: + values: + - auto + - main + - aggressive + spec: + default: auto + values: + - value: auto + - value: main + - value: aggressive + description: Exchange mode + required: false + - name: ike-crypto-profile + type: string + profiles: + - xpath: + - ike-crypto-profile + validators: [] + spec: + default: default + description: IKE SA crypto profile name + required: false variants: [] - description: IKEv2 liveness check setting + description: IKEv1 setting required: false - - name: ike-crypto-profile - type: string + - name: ikev2 + type: object profiles: - - xpath: - - ike-crypto-profile + - xpath: + - ikev2 validators: [] spec: - default: default - description: IKE SA crypto profile name + params: + - name: dpd + type: object + profiles: + - xpath: + - dpd + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: Enable sending empty information liveness check message + required: false + - name: interval + type: int64 + profiles: + - xpath: + - interval + validators: + - type: length + spec: + min: 2 + max: 100 + spec: + default: 5 + description: delay interval before sending probing packets (in seconds) + required: false + variants: [] + description: IKEv2 liveness check setting + required: false + - name: ike-crypto-profile + type: string + profiles: + - xpath: + - ike-crypto-profile + validators: [] + spec: + default: default + description: IKE SA crypto profile name + required: false + - name: require-cookie + type: bool + profiles: + - xpath: + - require-cookie + validators: [] + spec: {} + description: Require cookie + required: false + variants: [] + description: IKEv2 setting required: false - - name: require-cookie - type: bool + - name: version + type: enum profiles: - - xpath: - - require-cookie - validators: [] - spec: {} - description: Require cookie + - xpath: + - version + validators: + - type: values + spec: + values: + - ikev1 + - ikev2 + - ikev2-preferred + spec: + default: ikev1 + values: + - value: ikev1 + - value: ikev2 + - value: ikev2-preferred + description: IKE protocol version required: false - variants: [] - description: IKEv2 setting - required: false - - name: version - type: enum - profiles: - - xpath: - - version - validators: - - type: values - spec: - values: - - ikev1 - - ikev2 - - ikev2-preferred - spec: - default: ikev1 - values: - - value: ikev1 - - value: ikev2 - - value: ikev2-preferred - description: IKE protocol version - required: false - variants: [] - description: IKE Protocol settings - required: false - - name: protocol-common - type: object - profiles: - - xpath: - - protocol-common - validators: [] - spec: - params: - - name: fragmentation - type: object - profiles: + variants: [] + description: IKE Protocol settings + required: false + - name: protocol-common + type: object + profiles: - xpath: - - fragmentation - validators: [] - spec: - params: - - name: enable - type: bool + - protocol-common + validators: [] + spec: + params: + - name: fragmentation + type: object profiles: - - xpath: - - enable + - xpath: + - fragmentation validators: [] - spec: {} - description: Enable IKE fragmentation + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: Enable IKE fragmentation + required: false + variants: [] + description: IKE fragmentation settings required: false - variants: [] - description: IKE fragmentation settings - required: false - - name: nat-traversal - type: object - profiles: - - xpath: - - nat-traversal - validators: [] - spec: - params: - - name: enable - type: bool + - name: nat-traversal + type: object profiles: - - xpath: - - enable + - xpath: + - nat-traversal validators: [] - spec: {} - description: Enable NAT-Traversal - required: false - - name: keep-alive-interval - type: int64 - profiles: - - xpath: - - keep-alive-interval - validators: - - type: length - spec: - min: 10 - max: 3600 spec: - default: 20 - description: sending interval for NAT keep-alive packets (in seconds) + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: Enable NAT-Traversal + required: false + - name: keep-alive-interval + type: int64 + profiles: + - xpath: + - keep-alive-interval + validators: + - type: length + spec: + min: 10 + max: 3600 + spec: + default: 20 + description: sending interval for NAT keep-alive packets (in seconds) + required: false + - name: udp-checksum-enable + type: bool + profiles: + - xpath: + - udp-checksum-enable + validators: [] + spec: {} + description: Enable UDP checksum + required: false + variants: [] + description: NAT-Traversal settings required: false - - name: udp-checksum-enable + - name: passive-mode type: bool profiles: - - xpath: - - udp-checksum-enable + - xpath: + - passive-mode validators: [] spec: {} - description: Enable UDP checksum + description: Enable passive mode (responder only) required: false - variants: [] - description: NAT-Traversal settings - required: false - - name: passive-mode - type: bool - profiles: - - xpath: - - passive-mode - validators: [] - spec: {} - description: Enable passive mode (responder only) - required: false - variants: [] - description: IKE Protocol settings common to IKEv1 and IKEv2 (IKEv2 to be supported - in the future) - required: false + variants: [] + description: + IKE Protocol settings common to IKEv1 and IKEv2 (IKEv2 to be supported + in the future) + required: false variants: [] diff --git a/specs/network/interface/aggregate.yaml b/specs/network/interface/aggregate.yaml index 3be99f58..bde440a6 100644 --- a/specs/network/interface/aggregate.yaml +++ b/specs/network/interface/aggregate.yaml @@ -16,10 +16,12 @@ go_sdk_config: - network - interface - aggregate -xpath_suffix: -- network -- interface -- aggregate-ethernet +panos_xpath: + path: + - network + - interface + - aggregate-ethernet + vars: [] locations: - name: shared xpath: @@ -155,6 +157,7 @@ spec: variants: [] description: Interface to mirror decrypted packet required: false + variant_group_id: 0 - name: ha type: object profiles: @@ -259,6 +262,7 @@ spec: variants: [] description: Interface for high-availability functions required: false + variant_group_id: 0 - name: layer2 type: object profiles: @@ -444,6 +448,7 @@ spec: variants: [] description: Layer2 interface required: false + variant_group_id: 0 - name: layer3 type: object profiles: @@ -941,135 +946,76 @@ spec: 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 - - name: neighbor-discovery + - name: dhcp-client type: object profiles: - xpath: - - neighbor-discovery + - dhcp-client + min_version: 11.0.2 + max_version: 11.0.3 validators: [] spec: params: - - name: dad-attempts - type: int64 - profiles: - - xpath: - - dad-attempts - validators: - - type: length - spec: - min: 0 - max: 10 - spec: - default: 1 - description: number of consecutive neighbor solicitation messages - sent for duplicate address detection - required: false - - name: enable-dad - type: bool - profiles: - - xpath: - - enable-dad - validators: [] - spec: {} - description: enable duplicate address detection - required: false - - name: enable-ndp-monitor + - name: accept-ra-route type: bool profiles: - xpath: - - enable-ndp-monitor + - accept-ra-route validators: [] spec: {} - description: enable ndp monitoring - required: false - - name: neighbor - type: list - profiles: - - xpath: - - neighbor - - entry - type: entry - validators: [] - spec: - type: object - items: - type: object - spec: - params: - - name: hw-address - type: string - profiles: - - xpath: - - hw-address - validators: [] - spec: {} - description: MAC address (format xx:xx:xx:xx:xx:xx) - required: false - variants: [] - description: '' + description: Accept Router Advertised Default Route required: false - - name: ns-interval + - name: default-route-metric type: int64 profiles: - xpath: - - ns-interval + - default-route-metric validators: - type: length spec: min: 1 - max: 3600 + max: 65535 spec: - default: 1 - description: interval (in seconds) between consecutive neighbor solicitation - messages + default: 10 + description: Metric of the default route created required: false - - name: reachable-time - type: int64 + - name: enable + type: bool profiles: - xpath: - - reachable-time - validators: - - type: length - spec: - min: 10 - max: 36000 - spec: - default: 30 - description: time (in seconds) that the Reachable status for a neighbor - can be maintained + - enable + validators: [] + spec: {} + description: Enable DHCPv6 Client required: false - - name: router-advertisement + - name: neighbor-discovery type: object profiles: - xpath: - - router-advertisement + - neighbor-discovery validators: [] spec: params: - - name: dns-support + - name: dad-attempts + type: int64 + profiles: + - xpath: + - dad-attempts + validators: + - type: length + spec: + min: 1 + max: 10 + spec: + default: 1 + description: number of consecutive neighbor solicitation messages + sent for duplicate address detection + required: false + - name: dns-server type: object profiles: - xpath: - - dns-support + - dns-server validators: [] spec: params: @@ -1082,729 +1028,429 @@ spec: spec: {} description: '' required: false - - name: server - type: list + - name: source + type: object profiles: - xpath: - - server - - entry - type: entry + - source validators: [] spec: - type: object - items: + params: [] + variants: + - name: dhcpv6 + type: object + profiles: + - xpath: + - dhcpv6 + validators: [] + spec: + params: [] + variants: [] + description: Source from DHCPv6 Server + required: false + - name: manual type: object + profiles: + - xpath: + - manual + validators: [] spec: params: - - name: lifetime - type: int64 + - name: server + type: list profiles: - xpath: - - lifetime - validators: - - type: length - spec: - min: 4 - max: 3600 + - server + - entry + type: entry + validators: [] spec: - default: 1200 - description: (4-3600) lifetime in seconds + type: object + items: + type: object + spec: + params: + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 4 + max: 3600 + spec: + default: 1200 + description: (4-3600) Lifetime in Seconds + required: false + variants: [] + description: '' required: false variants: [] + description: Configure manually + required: false + description: Either source from DHCPv6 Server or manually + configure + required: false + variants: [] + description: DNS Recursive Name Server + required: false + - name: dns-suffix + type: object + profiles: + - xpath: + - dns-suffix + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} description: '' required: false - - name: suffix - type: list + - name: source + type: object profiles: - xpath: - - suffix - - entry - type: entry + - source validators: [] spec: - type: object - items: + params: [] + variants: + - name: dhcpv6 + type: object + profiles: + - xpath: + - dhcpv6 + validators: [] + spec: + params: [] + variants: [] + description: Source from DHCPv6 Server + required: false + - name: manual type: object + profiles: + - xpath: + - manual + validators: [] spec: params: - - name: lifetime - type: int64 + - name: suffix + type: list profiles: - xpath: - - lifetime - validators: - - type: length - spec: - min: 4 - max: 3600 + - suffix + - entry + type: entry + validators: [] spec: - default: 1200 - description: (4-3600) lifetime in seconds + type: object + items: + type: object + spec: + params: + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 4 + max: 3600 + spec: + default: 1200 + description: (4-3600) lifetime in seconds + required: false + variants: [] + description: '' required: false variants: [] - description: '' + description: Configure manually + required: false + description: Either source from DHCPv6 Server or manually + configure required: false variants: [] - description: DNS configuration support + description: Domain Search List required: false - - name: enable + - name: enable-dad type: bool profiles: - xpath: - - enable + - enable-dad validators: [] spec: {} - description: '' + description: Enable Duplicate Address Detection required: false - - name: enable-consistency-check + - name: enable-ndp-monitor type: bool profiles: - xpath: - - enable-consistency-check + - enable-ndp-monitor validators: [] spec: {} - description: check consistency of RA messages from other routers. - required: false - - name: hop-limit - type: string - profiles: - - xpath: - - hop-limit - validators: [] - spec: - default: '64' - description: Current Hop Limit advertised in Router Advertisement - messages - required: false - - name: lifetime - type: int64 - profiles: - - xpath: - - lifetime - validators: - - type: length - spec: - min: 0 - max: 9000 - spec: - default: 1800 - description: Router Lifetime (in seconds) advertised in Router - Advertisement messages - required: false - - name: link-mtu - type: string - profiles: - - xpath: - - link-mtu - validators: [] - spec: - default: unspecified - description: value of MTU option in Router Advertisement messages, - upto 9216 in Jumbo-Frame mode, up to 1500 otherwise + description: Enable NDP Monitoring required: false - - name: managed-flag - type: bool + - name: neighbor + type: list profiles: - xpath: - - managed-flag + - neighbor + - entry + type: entry validators: [] - spec: {} - description: Set the Managed Configuration Flag (M-bit) in Router - Advertisement messages - required: false - - name: max-interval - type: int64 - profiles: - - xpath: - - max-interval - validators: - - type: length - spec: - min: 4 - max: 1800 spec: - default: 600 - description: Maximum interval (seconds) between consecutive unsolicited - Router Advertisement messages + type: object + items: + type: object + spec: + params: + - name: hw-address + type: string + profiles: + - xpath: + - hw-address + validators: [] + spec: {} + description: MAC address (format xx:xx:xx:xx:xx:xx) + required: false + variants: [] + description: '' required: false - - name: min-interval + - name: ns-interval type: int64 profiles: - xpath: - - min-interval + - ns-interval validators: - type: length spec: - min: 3 - max: 1350 + min: 1 + max: 3600 spec: - default: 200 - description: Minimum interval (seconds) between consecutive unsolicited - Router Advertisement messages - required: false - - name: other-flag - type: bool - profiles: - - xpath: - - other-flag - validators: [] - spec: {} - description: Set the Other Stateful Configuration Flag (O-bit) - in Router Advertisement messages + default: 1 + description: interval (in seconds) between consecutive neighbor + solicitation messages required: false - name: reachable-time - type: string + type: int64 profiles: - xpath: - reachable-time - validators: [] - spec: - default: unspecified - description: Reachable Time (in milliseconds) advertised in Router - Advertisement messages - required: false - - name: retransmission-timer - type: string - profiles: - - xpath: - - retransmission-timer - validators: [] - spec: - default: unspecified - description: Retransmission Timer (in milliseconds) advertised - in Router Advertisement messages - required: false - - name: router-preference - type: enum - profiles: - - xpath: - - router-preference validators: - - type: values + - type: length spec: - values: - - High - - Medium - - Low + min: 10 + max: 36000 spec: - default: Medium - values: - - value: High - - value: Medium - - value: Low - description: '' + default: 30 + description: time (in seconds) that the Reachable status for a + neighbor can be maintained required: false variants: [] - description: Router advertisement configuration - required: false - variants: [] - description: Neighbor Discovery configuration - required: false - - name: dhcp-client - type: object - profiles: - - xpath: - - dhcp-client - min_version: 11.0.2 - max_version: 11.0.3 - validators: [] - spec: - params: - - name: accept-ra-route - type: bool - profiles: - - xpath: - - accept-ra-route - validators: [] - spec: {} - description: Accept Router Advertised Default Route + description: Neighbor Discovery configuration required: false - - name: default-route-metric - type: int64 + - name: preference + type: enum profiles: - xpath: - - default-route-metric + - preference validators: - - type: length + - type: values spec: - min: 1 - max: 65535 + values: + - low + - medium + - high spec: - default: 10 - description: Metric of the default route created - required: false - - name: enable - type: bool - profiles: - - xpath: - - enable - validators: [] - spec: {} - description: Enable DHCPv6 Client + default: high + values: + - value: low + - value: medium + - value: high + description: Select Low/Medium/High required: false - - name: neighbor-discovery + - name: prefix-delegation type: object profiles: - xpath: - - neighbor-discovery + - prefix-delegation validators: [] spec: params: - - name: dad-attempts - type: int64 - profiles: - - xpath: - - dad-attempts - validators: - - type: length - spec: - min: 1 - max: 10 - spec: - default: 1 - description: number of consecutive neighbor solicitation messages - sent for duplicate address detection - required: false - - name: dns-server + - name: enable type: object profiles: - xpath: - - dns-server + - enable validators: [] spec: - params: - - name: enable - type: bool + params: [] + variants: + - name: 'no' + type: object profiles: - xpath: - - enable + - 'no' validators: [] - spec: {} - description: '' + spec: + params: [] + variants: [] + description: Disable Prefix Delegation required: false - - name: source + - name: 'yes' type: object profiles: - xpath: - - source + - 'yes' validators: [] spec: - params: [] - variants: - - name: dhcpv6 - type: object + params: + - name: pfx-pool-name + type: string profiles: - xpath: - - dhcpv6 - validators: [] + - pfx-pool-name + validators: + - type: length + spec: + max: 63 + spec: {} + description: Configure unique Prefix Pool Name + required: false + - name: prefix-len + type: int64 + profiles: + - xpath: + - prefix-len + validators: + - type: length + spec: + min: 0 + max: 128 spec: - params: [] - variants: [] - description: Source from DHCPv6 Server + default: 48 + description: Hint DHCP Prefix Length (bits) required: false - - name: manual - type: object + - name: prefix-len-hint + type: bool profiles: - xpath: - - manual + - prefix-len-hint validators: [] - spec: - params: - - name: server - type: list - profiles: - - xpath: - - server - - entry - type: entry - validators: [] - spec: - type: object - items: - type: object - spec: - params: - - name: lifetime - type: int64 - profiles: - - xpath: - - lifetime - validators: - - type: length - spec: - min: 4 - max: 3600 - spec: - default: 1200 - description: (4-3600) Lifetime in Seconds - required: false - variants: [] - description: '' - required: false - variants: [] - description: Configure manually + spec: {} + description: Send prefix length hint to server required: false - description: Either source from DHCPv6 Server or manually - configure + variants: [] + description: Enable Prefix Delegation required: false - variants: [] - description: DNS Recursive Name Server + description: Enable/Disable Prefix Delegation required: false - - name: dns-suffix + variants: [] + description: Configure Prefix Delegation Options + required: false + - name: v6-options + type: object + profiles: + - xpath: + - v6-options + validators: [] + spec: + params: + - name: duid-type + type: enum + profiles: + - xpath: + - duid-type + validators: + - type: values + spec: + values: + - duid-type-llt + - duid-type-ll + spec: + default: duid-type-llt + values: + - value: duid-type-llt + - value: duid-type-ll + description: Select DUID-LLT/DUID-LL + required: false + - name: enable type: object profiles: - xpath: - - dns-suffix + - enable validators: [] spec: - params: - - name: enable - type: bool + params: [] + variants: + - name: 'no' + type: object profiles: - xpath: - - enable + - 'no' validators: [] - spec: {} - description: '' + spec: + params: [] + variants: [] + description: Disable IPv6 Address required: false - - name: source + - name: 'yes' type: object profiles: - xpath: - - source + - 'yes' validators: [] spec: - params: [] - variants: - - name: dhcpv6 - type: object + params: + - name: non-temp-addr + type: bool profiles: - xpath: - - dhcpv6 + - non-temp-addr validators: [] - spec: - params: [] - variants: [] - description: Source from DHCPv6 Server + spec: {} + description: Request Non-Temporary Address Type required: false - - name: manual - type: object + - name: temp-addr + type: bool profiles: - xpath: - - manual + - temp-addr validators: [] - spec: - params: - - name: suffix - type: list - profiles: - - xpath: - - suffix - - entry - type: entry - validators: [] - spec: - type: object - items: - type: object - spec: - params: - - name: lifetime - type: int64 - profiles: - - xpath: - - lifetime - validators: - - type: length - spec: - min: 4 - max: 3600 - spec: - default: 1200 - description: (4-3600) lifetime in seconds - required: false - variants: [] - description: '' - required: false - variants: [] - description: Configure manually + spec: {} + description: Request Temporary Address Type required: false - description: Either source from DHCPv6 Server or manually - configure + variants: [] + description: Enable IPv6 Address required: false - variants: [] - description: Domain Search List + description: Enable/Disable IPv6 Address required: false - - name: enable-dad + - name: rapid-commit type: bool profiles: - xpath: - - enable-dad + - rapid-commit validators: [] spec: {} - description: Enable Duplicate Address Detection + description: Enable Rapid Commit required: false - - name: enable-ndp-monitor + - name: support-srvr-reconfig type: bool profiles: - xpath: - - enable-ndp-monitor + - support-srvr-reconfig validators: [] spec: {} - description: Enable NDP Monitoring - required: false - - name: neighbor - type: list - profiles: - - xpath: - - neighbor - - entry - type: entry - validators: [] - spec: - type: object - items: - type: object - spec: - params: - - name: hw-address - type: string - profiles: - - xpath: - - hw-address - validators: [] - spec: {} - description: MAC address (format xx:xx:xx:xx:xx:xx) - required: false - variants: [] - description: '' - required: false - - name: ns-interval - type: int64 - profiles: - - xpath: - - ns-interval - validators: - - type: length - spec: - min: 1 - max: 3600 - spec: - default: 1 - description: interval (in seconds) between consecutive neighbor - solicitation messages - required: false - - name: reachable-time - type: int64 - profiles: - - xpath: - - reachable-time - validators: - - type: length - spec: - min: 10 - max: 36000 - spec: - default: 30 - description: time (in seconds) that the Reachable status for a - neighbor can be maintained - required: false - variants: [] - description: Neighbor Discovery configuration - required: false - - name: preference - type: enum - profiles: - - xpath: - - preference - validators: - - type: values - spec: - values: - - low - - medium - - high - spec: - default: high - values: - - value: low - - value: medium - - value: high - description: Select Low/Medium/High - required: false - - name: prefix-delegation - type: object - profiles: - - xpath: - - prefix-delegation - validators: [] - spec: - params: - - name: enable - type: object - profiles: - - xpath: - - enable - validators: [] - spec: - params: [] - variants: - - name: 'no' - type: object - profiles: - - xpath: - - 'no' - validators: [] - spec: - params: [] - variants: [] - description: Disable Prefix Delegation - required: false - - name: 'yes' - type: object - profiles: - - xpath: - - 'yes' - validators: [] - spec: - params: - - name: pfx-pool-name - type: string - profiles: - - xpath: - - pfx-pool-name - validators: - - type: length - spec: - max: 63 - spec: {} - description: Configure unique Prefix Pool Name - required: false - - name: prefix-len - type: int64 - profiles: - - xpath: - - prefix-len - validators: - - type: length - spec: - min: 0 - max: 128 - spec: - default: 48 - description: Hint DHCP Prefix Length (bits) - required: false - - name: prefix-len-hint - type: bool - profiles: - - xpath: - - prefix-len-hint - validators: [] - spec: {} - description: Send prefix length hint to server - required: false - variants: [] - description: Enable Prefix Delegation - required: false - description: Enable/Disable Prefix Delegation - required: false - variants: [] - description: Configure Prefix Delegation Options - required: false - - name: v6-options - type: object - profiles: - - xpath: - - v6-options - validators: [] - spec: - params: - - name: duid-type - type: enum - profiles: - - xpath: - - duid-type - validators: - - type: values - spec: - values: - - duid-type-llt - - duid-type-ll - spec: - default: duid-type-llt - values: - - value: duid-type-llt - - value: duid-type-ll - description: Select DUID-LLT/DUID-LL - required: false - - name: enable - type: object - profiles: - - xpath: - - enable - validators: [] - spec: - params: [] - variants: - - name: 'no' - type: object - profiles: - - xpath: - - 'no' - validators: [] - spec: - params: [] - variants: [] - description: Disable IPv6 Address - required: false - - name: 'yes' - type: object - profiles: - - xpath: - - 'yes' - validators: [] - spec: - params: - - name: non-temp-addr - type: bool - profiles: - - xpath: - - non-temp-addr - validators: [] - spec: {} - description: Request Non-Temporary Address Type - required: false - - name: temp-addr - type: bool - profiles: - - xpath: - - temp-addr - validators: [] - spec: {} - description: Request Temporary Address Type - required: false - variants: [] - description: Enable IPv6 Address - required: false - description: Enable/Disable IPv6 Address - required: false - - name: rapid-commit - type: bool - profiles: - - xpath: - - rapid-commit - validators: [] - spec: {} - description: Enable Rapid Commit - required: false - - name: support-srvr-reconfig - type: bool - profiles: - - xpath: - - support-srvr-reconfig - validators: [] - spec: {} - description: Enable DHCPv6 Server Re-Configuration Support + description: Enable DHCPv6 Server Re-Configuration Support required: false variants: [] description: Configure DHCPv6 Options @@ -1812,6 +1458,15 @@ spec: variants: [] description: Configure DHCPv6 Client required: false + - name: enabled + type: bool + profiles: + - xpath: + - enabled + validators: [] + spec: {} + description: Enable IPv6 on the interface + required: false - name: inherited type: object profiles: @@ -2552,6 +2207,356 @@ spec: variants: [] description: Configure to inherit properties from another interface required: false + - name: interface-id + type: string + profiles: + - xpath: + - interface-id + validators: [] + spec: + default: EUI-64 + description: '' + required: false + - name: neighbor-discovery + type: object + profiles: + - xpath: + - neighbor-discovery + validators: [] + spec: + params: + - name: dad-attempts + type: int64 + profiles: + - xpath: + - dad-attempts + validators: + - type: length + spec: + min: 0 + max: 10 + spec: + default: 1 + description: number of consecutive neighbor solicitation messages + sent for duplicate address detection + required: false + - name: enable-dad + type: bool + profiles: + - xpath: + - enable-dad + validators: [] + spec: {} + description: enable duplicate address detection + required: false + - name: enable-ndp-monitor + type: bool + profiles: + - xpath: + - enable-ndp-monitor + validators: [] + spec: {} + description: enable ndp monitoring + required: false + - name: neighbor + type: list + profiles: + - xpath: + - neighbor + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: hw-address + type: string + profiles: + - xpath: + - hw-address + validators: [] + spec: {} + description: MAC address (format xx:xx:xx:xx:xx:xx) + required: false + variants: [] + description: '' + required: false + - name: ns-interval + type: int64 + profiles: + - xpath: + - ns-interval + validators: + - type: length + spec: + min: 1 + max: 3600 + spec: + default: 1 + description: interval (in seconds) between consecutive neighbor solicitation + messages + required: false + - name: reachable-time + type: int64 + profiles: + - xpath: + - reachable-time + validators: + - type: length + spec: + min: 10 + max: 36000 + spec: + default: 30 + description: time (in seconds) that the Reachable status for a neighbor + can be maintained + required: false + - name: router-advertisement + type: object + profiles: + - xpath: + - router-advertisement + validators: [] + spec: + params: + - name: dns-support + type: object + profiles: + - xpath: + - dns-support + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: '' + required: false + - name: server + type: list + profiles: + - xpath: + - server + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 4 + max: 3600 + spec: + default: 1200 + description: (4-3600) lifetime in seconds + required: false + variants: [] + description: '' + required: false + - name: suffix + type: list + profiles: + - xpath: + - suffix + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 4 + max: 3600 + spec: + default: 1200 + description: (4-3600) lifetime in seconds + required: false + variants: [] + description: '' + required: false + variants: [] + description: DNS configuration support + required: false + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: '' + required: false + - name: enable-consistency-check + type: bool + profiles: + - xpath: + - enable-consistency-check + validators: [] + spec: {} + description: check consistency of RA messages from other routers. + required: false + - name: hop-limit + type: string + profiles: + - xpath: + - hop-limit + validators: [] + spec: + default: '64' + description: Current Hop Limit advertised in Router Advertisement + messages + required: false + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 0 + max: 9000 + spec: + default: 1800 + description: Router Lifetime (in seconds) advertised in Router + Advertisement messages + required: false + - name: link-mtu + type: string + profiles: + - xpath: + - link-mtu + validators: [] + spec: + default: unspecified + description: value of MTU option in Router Advertisement messages, + upto 9216 in Jumbo-Frame mode, up to 1500 otherwise + required: false + - name: managed-flag + type: bool + profiles: + - xpath: + - managed-flag + validators: [] + spec: {} + description: Set the Managed Configuration Flag (M-bit) in Router + Advertisement messages + required: false + - name: max-interval + type: int64 + profiles: + - xpath: + - max-interval + validators: + - type: length + spec: + min: 4 + max: 1800 + spec: + default: 600 + description: Maximum interval (seconds) between consecutive unsolicited + Router Advertisement messages + required: false + - name: min-interval + type: int64 + profiles: + - xpath: + - min-interval + validators: + - type: length + spec: + min: 3 + max: 1350 + spec: + default: 200 + description: Minimum interval (seconds) between consecutive unsolicited + Router Advertisement messages + required: false + - name: other-flag + type: bool + profiles: + - xpath: + - other-flag + validators: [] + spec: {} + description: Set the Other Stateful Configuration Flag (O-bit) + in Router Advertisement messages + required: false + - name: reachable-time + type: string + profiles: + - xpath: + - reachable-time + validators: [] + spec: + default: unspecified + description: Reachable Time (in milliseconds) advertised in Router + Advertisement messages + required: false + - name: retransmission-timer + type: string + profiles: + - xpath: + - retransmission-timer + validators: [] + spec: + default: unspecified + description: Retransmission Timer (in milliseconds) advertised + in Router Advertisement messages + required: false + - name: router-preference + type: enum + profiles: + - xpath: + - router-preference + validators: + - type: values + spec: + values: + - High + - Medium + - Low + spec: + default: Medium + values: + - value: High + - value: Medium + - value: Low + description: '' + required: false + variants: [] + description: Router advertisement configuration + required: false + variants: [] + description: Neighbor Discovery configuration + required: false variants: [] description: Interface IPv6 configuration required: false @@ -2847,6 +2852,7 @@ spec: variants: [] description: DDNS derives upstream NAT IP required: false + variant_group_id: 0 - name: static-ip type: object profiles: @@ -2868,6 +2874,7 @@ spec: spec: {} description: Upstream NAT address FQDN name configuration required: false + variant_group_id: 0 - name: ip-address type: string profiles: @@ -2877,8 +2884,10 @@ spec: spec: {} description: Upstream NAT IP address required: false + variant_group_id: 0 description: Upstream NAT IP address required: false + variant_group_id: 0 description: Upstream NAT IP config required: false variants: [] @@ -2896,6 +2905,7 @@ spec: variants: [] description: Layer3 interface required: false + variant_group_id: 0 - name: virtual-wire type: object profiles: @@ -2968,3 +2978,4 @@ spec: variants: [] description: Virtual-wire interface required: false + variant_group_id: 0 diff --git a/specs/network/interface/ethernet.yaml b/specs/network/interface/ethernet.yaml index b78bd216..b47b79e7 100644 --- a/specs/network/interface/ethernet.yaml +++ b/specs/network/interface/ethernet.yaml @@ -16,10 +16,12 @@ go_sdk_config: - network - interface - ethernet -xpath_suffix: -- network -- interface -- ethernet +panos_xpath: + path: + - network + - interface + - ethernet + vars: [] locations: - name: shared xpath: @@ -388,6 +390,7 @@ spec: spec: {} description: Aggregate interface group required: false + variant_group_id: 0 - name: decrypt-mirror type: object profiles: @@ -399,6 +402,7 @@ spec: variants: [] description: Interface to mirror decrypted packet required: false + variant_group_id: 0 - name: ha type: object profiles: @@ -410,6 +414,7 @@ spec: variants: [] description: HA mode interface required: false + variant_group_id: 0 - name: layer2 type: object profiles: @@ -482,6 +487,7 @@ spec: variants: [] description: Layer2 interface required: false + variant_group_id: 0 - name: layer3 type: object profiles: @@ -607,6 +613,17 @@ spec: variants: [] description: Bonjour configuration required: false + - name: cluster-interconnect + type: bool + profiles: + - xpath: + - cluster-interconnect + min_version: 11.0.2 + max_version: 11.0.3 + validators: [] + spec: {} + description: '' + required: false - name: ddns-config type: object profiles: @@ -985,135 +1002,76 @@ spec: codegen_overrides: terraform: name: addresses - - 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 - - name: neighbor-discovery + - name: dhcp-client type: object profiles: - xpath: - - neighbor-discovery + - dhcp-client + min_version: 11.0.2 + max_version: 11.0.3 validators: [] spec: params: - - name: dad-attempts - type: int64 - profiles: - - xpath: - - dad-attempts - validators: - - type: length - spec: - min: 0 - max: 10 - spec: - default: 1 - description: Number of consecutive neighbor solicitation messages - sent for duplicate address detection - required: false - - name: enable-dad - type: bool - profiles: - - xpath: - - enable-dad - validators: [] - spec: {} - description: Enable Duplicate ADdress Detection (DAD) - required: false - - name: enable-ndp-monitor + - name: accept-ra-route type: bool profiles: - xpath: - - enable-ndp-monitor + - accept-ra-route validators: [] spec: {} - description: Enable NDP Monitoring - required: false - - name: neighbor - type: list - profiles: - - xpath: - - neighbor - - entry - type: entry - validators: [] - spec: - type: object - items: - type: object - spec: - params: - - name: hw-address - type: string - profiles: - - xpath: - - hw-address - validators: [] - spec: {} - description: MAC address (format xx:xx:xx:xx:xx:xx) - required: false - variants: [] - description: Static entries in neighbor cache + description: Accept Router Advertised Default Route required: false - - name: ns-interval + - name: default-route-metric type: int64 profiles: - xpath: - - ns-interval + - default-route-metric validators: - type: length spec: min: 1 - max: 3600 + max: 65535 spec: - default: 1 - description: Interval (in seconds) between consecutive neighbor solicitation - messages + default: 10 + description: Metric of the default route created required: false - - name: reachable-time - type: int64 + - name: enable + type: bool profiles: - xpath: - - reachable-time - validators: - - type: length - spec: - min: 10 - max: 36000 - spec: - default: 30 - description: Time (in seconds) that the Reachable status for a neighbor - can be maintained + - enable + validators: [] + spec: {} + description: Enable DHCPv6 Client required: false - - name: router-advertisement + - name: neighbor-discovery type: object profiles: - xpath: - - router-advertisement + - neighbor-discovery validators: [] spec: params: - - name: dns-support + - name: dad-attempts + type: int64 + profiles: + - xpath: + - dad-attempts + validators: + - type: length + spec: + min: 1 + max: 10 + spec: + default: 1 + description: Number of consecutive neighbor solicitation messages + sent for duplicate address detection + required: false + - name: dns-server type: object profiles: - xpath: - - dns-support + - dns-server validators: [] spec: params: @@ -1126,296 +1084,79 @@ spec: spec: {} description: '' required: false - - name: server - type: list + - name: source + type: object profiles: - xpath: - - server - - entry - type: entry + - source validators: [] spec: - type: object - items: + params: [] + variants: + - name: dhcpv6 type: object + profiles: + - xpath: + - dhcpv6 + validators: [] spec: - params: - - name: lifetime - type: int64 - profiles: - - xpath: - - lifetime - validators: - - type: length - spec: - min: 4 - max: 3600 - spec: - default: 1200 - description: Router Lifetime (in seconds) advertised - in Router Advertisement messages - required: false + params: [] variants: [] - description: '' - required: false - - name: suffix - type: list - profiles: - - xpath: - - suffix - - entry - type: entry - validators: [] - spec: - type: object - items: + description: Source from DHCPv6 Server + required: false + - name: manual type: object + profiles: + - xpath: + - manual + validators: [] spec: params: - - name: lifetime - type: int64 + - name: server + type: list profiles: - xpath: - - lifetime - validators: - - type: length - spec: - min: 4 - max: 3600 + - server + - entry + type: entry + validators: [] spec: - default: 1200 - description: Router Lifetime (in seconds) advertised - in Router Advertisement messages + type: object + items: + type: object + spec: + params: + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 4 + max: 3600 + spec: + default: 1200 + description: (4-3600) Lifetime in Seconds + required: false + variants: [] + description: '' required: false variants: [] - description: '' + description: Configure manually + required: false + description: Either source from DHCPv6 Server or manually + configure required: false variants: [] - description: DNS configuration support + description: DNS Recursive Name Server required: false - - name: enable - type: bool + - name: dns-suffix + type: object profiles: - xpath: - - enable - validators: [] - spec: {} - description: '' - required: false - - name: enable-consistency-check - type: bool - profiles: - - xpath: - - enable-consistency-check - validators: [] - spec: {} - description: check consistency of RA messages from other routers. - required: false - - name: hop-limit - type: string - profiles: - - xpath: - - hop-limit - validators: [] - spec: - default: '64' - description: Current Hop Limit advertised in Router Advertisement - messages - required: false - - name: lifetime - type: int64 - profiles: - - xpath: - - lifetime - validators: - - type: length - spec: - min: 0 - max: 9000 - spec: - default: 1800 - description: Router Lifetime (in seconds) advertised in Router - Advertisement messages - required: false - - name: link-mtu - type: string - profiles: - - xpath: - - link-mtu - validators: [] - spec: - default: unspecified - description: value of MTU option in Router Advertisement messages, - upto 9216 in Jumbo-Frame mode, up to 1500 otherwise - required: false - - name: managed-flag - type: bool - profiles: - - xpath: - - managed-flag - validators: [] - spec: {} - description: Set the Managed Configuration Flag (M-bit) in Router - Advertisement messages - required: false - - name: max-interval - type: int64 - profiles: - - xpath: - - max-interval - validators: - - type: length - spec: - min: 4 - max: 1800 - spec: - default: 600 - description: Maximum interval (seconds) between consecutive unsolicited - Router Advertisement messages - required: false - - name: min-interval - type: int64 - profiles: - - xpath: - - min-interval - validators: - - type: length - spec: - min: 3 - max: 1350 - spec: - default: 200 - description: Minimum interval (seconds) between consecutive unsolicited - Router Advertisement messages - required: false - - name: other-flag - type: bool - profiles: - - xpath: - - other-flag - validators: [] - spec: {} - description: Set the Other Stateful Configuration Flag (O-bit) - in Router Advertisement messages - required: false - - name: reachable-time - type: string - profiles: - - xpath: - - reachable-time - validators: [] - spec: - default: unspecified - description: Reachable Time (in milliseconds) advertised in Router - Advertisement messages - required: false - - name: retransmission-timer - type: string - profiles: - - xpath: - - retransmission-timer - validators: [] - spec: - default: unspecified - description: Retransmission Timer (in milliseconds) advertised - in Router Advertisement messages - required: false - - name: router-preference - type: enum - profiles: - - xpath: - - router-preference - validators: - - type: values - spec: - values: - - High - - Medium - - Low - spec: - default: Medium - values: - - value: High - - value: Medium - - value: Low - description: Router Preference - required: false - variants: [] - description: Router Advertisement configuration - required: false - variants: [] - description: Neighbor Discovery configuration - required: false - - name: dhcp-client - type: object - profiles: - - xpath: - - dhcp-client - min_version: 11.0.2 - max_version: 11.0.3 - validators: [] - spec: - params: - - name: accept-ra-route - type: bool - profiles: - - xpath: - - accept-ra-route - validators: [] - spec: {} - description: Accept Router Advertised Default Route - required: false - - name: default-route-metric - type: int64 - profiles: - - xpath: - - default-route-metric - validators: - - type: length - spec: - min: 1 - max: 65535 - spec: - default: 10 - description: Metric of the default route created - required: false - - name: enable - type: bool - profiles: - - xpath: - - enable - validators: [] - spec: {} - description: Enable DHCPv6 Client - required: false - - name: neighbor-discovery - type: object - profiles: - - xpath: - - neighbor-discovery - validators: [] - spec: - params: - - name: dad-attempts - type: int64 - profiles: - - xpath: - - dad-attempts - validators: - - type: length - spec: - min: 1 - max: 10 - spec: - default: 1 - description: Number of consecutive neighbor solicitation messages - sent for duplicate address detection - required: false - - name: dns-server - type: object - profiles: - - xpath: - - dns-server + - dns-suffix validators: [] spec: params: @@ -1456,11 +1197,11 @@ spec: validators: [] spec: params: - - name: server + - name: suffix type: list profiles: - xpath: - - server + - suffix - entry type: entry validators: [] @@ -1482,7 +1223,7 @@ spec: max: 3600 spec: default: 1200 - description: (4-3600) Lifetime in Seconds + description: (4-3600) lifetime in seconds required: false variants: [] description: '' @@ -1494,118 +1235,33 @@ spec: configure required: false variants: [] - description: DNS Recursive Name Server + description: Domain Search List required: false - - name: dns-suffix - type: object + - name: enable-dad + type: bool profiles: - xpath: - - dns-suffix - validators: [] - spec: - params: - - name: enable - type: bool - profiles: - - xpath: - - enable - validators: [] - spec: {} - description: '' - required: false - - name: source - type: object - profiles: - - xpath: - - source - validators: [] - spec: - params: [] - variants: - - name: dhcpv6 - type: object - profiles: - - xpath: - - dhcpv6 - validators: [] - spec: - params: [] - variants: [] - description: Source from DHCPv6 Server - required: false - - name: manual - type: object - profiles: - - xpath: - - manual - validators: [] - spec: - params: - - name: suffix - type: list - profiles: - - xpath: - - suffix - - entry - type: entry - validators: [] - spec: - type: object - items: - type: object - spec: - params: - - name: lifetime - type: int64 - profiles: - - xpath: - - lifetime - validators: - - type: length - spec: - min: 4 - max: 3600 - spec: - default: 1200 - description: (4-3600) lifetime in seconds - required: false - variants: [] - description: '' - required: false - variants: [] - description: Configure manually - required: false - description: Either source from DHCPv6 Server or manually - configure - required: false - variants: [] - description: Domain Search List - required: false - - name: enable-dad - type: bool - profiles: - - xpath: - - enable-dad - validators: [] - spec: {} - description: Enable Duplicate ADdress Detection (DAD) - required: false - - name: enable-ndp-monitor - type: bool - profiles: - - xpath: - - enable-ndp-monitor - validators: [] - spec: {} - description: Enable NDP Monitoring - required: false - - name: neighbor - type: list - profiles: - - xpath: - - neighbor - - entry - type: entry + - enable-dad + validators: [] + spec: {} + description: Enable Duplicate ADdress Detection (DAD) + required: false + - name: enable-ndp-monitor + type: bool + profiles: + - xpath: + - enable-ndp-monitor + validators: [] + spec: {} + description: Enable NDP Monitoring + required: false + - name: neighbor + type: list + profiles: + - xpath: + - neighbor + - entry + type: entry validators: [] spec: type: object @@ -1858,6 +1514,15 @@ spec: variants: [] description: Configure DHCPv6 Client required: false + - name: enabled + type: bool + profiles: + - xpath: + - enabled + validators: [] + spec: {} + description: Enable IPv6 on the interface + required: false - name: inherited type: object profiles: @@ -2407,199 +2072,551 @@ spec: variants: [] description: Static entries in neighbor cache required: false - - name: ns-interval + - name: ns-interval + type: int64 + profiles: + - xpath: + - ns-interval + validators: + - type: length + spec: + min: 1 + max: 3600 + spec: + default: 1 + description: Interval (in seconds) between consecutive neighbor + solicitation messages + required: false + - name: reachable-time + type: int64 + profiles: + - xpath: + - reachable-time + validators: + - type: length + spec: + min: 10 + max: 36000 + spec: + default: 30 + description: Time (in seconds) that the Reachable status for a + neighbor can be maintained + required: false + - name: router-advertisement + type: object + profiles: + - xpath: + - router-advertisement + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: '' + required: false + - name: enable-consistency-check + type: bool + profiles: + - xpath: + - enable-consistency-check + validators: [] + spec: {} + description: check consistency of RA messages from other routers. + required: false + - name: hop-limit + type: string + profiles: + - xpath: + - hop-limit + validators: [] + spec: + default: '64' + description: Current Hop Limit advertised in Router Advertisement + messages + required: false + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 0 + max: 9000 + spec: + default: 1800 + description: Router Lifetime (in seconds) advertised in Router + Advertisement messages + required: false + - name: link-mtu + type: string + profiles: + - xpath: + - link-mtu + validators: [] + spec: + default: unspecified + description: value of MTU option in Router Advertisement messages, + upto 9216 in Jumbo-Frame mode, up to 1500 otherwise + required: false + - name: managed-flag + type: bool + profiles: + - xpath: + - managed-flag + validators: [] + spec: {} + description: Set the Managed Configuration Flag (M-bit) in + Router Advertisement messages + required: false + - name: max-interval + type: int64 + profiles: + - xpath: + - max-interval + validators: + - type: length + spec: + min: 4 + max: 1800 + spec: + default: 600 + description: Maximum interval (seconds) between consecutive + unsolicited Router Advertisement messages + required: false + - name: min-interval + type: int64 + profiles: + - xpath: + - min-interval + validators: + - type: length + spec: + min: 3 + max: 1350 + spec: + default: 200 + description: Minimum interval (seconds) between consecutive + unsolicited Router Advertisement messages + required: false + - name: other-flag + type: bool + profiles: + - xpath: + - other-flag + validators: [] + spec: {} + description: Set the Other Stateful Configuration Flag (O-bit) + in Router Advertisement messages + required: false + - name: reachable-time + type: string + profiles: + - xpath: + - reachable-time + validators: [] + spec: + default: unspecified + description: Reachable Time (in milliseconds) advertised in + Router Advertisement messages + required: false + - name: retransmission-timer + type: string + profiles: + - xpath: + - retransmission-timer + validators: [] + spec: + default: unspecified + description: Retransmission Timer (in milliseconds) advertised + in Router Advertisement messages + required: false + - name: router-preference + type: enum + profiles: + - xpath: + - router-preference + validators: + - type: values + spec: + values: + - High + - Medium + - Low + spec: + default: Medium + values: + - value: High + - value: Medium + - value: Low + description: Router Preference + required: false + variants: [] + description: Router Advertisement configuration + required: false + variants: [] + description: Neighbor Discovery configuration + required: false + variants: [] + description: Configure to inherit properties from another interface + required: false + - name: interface-id + type: string + profiles: + - xpath: + - interface-id + validators: [] + spec: + default: EUI-64 + description: '' + required: false + - name: neighbor-discovery + type: object + profiles: + - xpath: + - neighbor-discovery + validators: [] + spec: + params: + - name: dad-attempts + type: int64 + profiles: + - xpath: + - dad-attempts + validators: + - type: length + spec: + min: 0 + max: 10 + spec: + default: 1 + description: Number of consecutive neighbor solicitation messages + sent for duplicate address detection + required: false + - name: enable-dad + type: bool + profiles: + - xpath: + - enable-dad + validators: [] + spec: {} + description: Enable Duplicate ADdress Detection (DAD) + required: false + - name: enable-ndp-monitor + type: bool + profiles: + - xpath: + - enable-ndp-monitor + validators: [] + spec: {} + description: Enable NDP Monitoring + required: false + - name: neighbor + type: list + profiles: + - xpath: + - neighbor + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: hw-address + type: string + profiles: + - xpath: + - hw-address + validators: [] + spec: {} + description: MAC address (format xx:xx:xx:xx:xx:xx) + required: false + variants: [] + description: Static entries in neighbor cache + required: false + - name: ns-interval + type: int64 + profiles: + - xpath: + - ns-interval + validators: + - type: length + spec: + min: 1 + max: 3600 + spec: + default: 1 + description: Interval (in seconds) between consecutive neighbor solicitation + messages + required: false + - name: reachable-time + type: int64 + profiles: + - xpath: + - reachable-time + validators: + - type: length + spec: + min: 10 + max: 36000 + spec: + default: 30 + description: Time (in seconds) that the Reachable status for a neighbor + can be maintained + required: false + - name: router-advertisement + type: object + profiles: + - xpath: + - router-advertisement + validators: [] + spec: + params: + - name: dns-support + type: object + profiles: + - xpath: + - dns-support + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: '' + required: false + - name: server + type: list + profiles: + - xpath: + - server + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 4 + max: 3600 + spec: + default: 1200 + description: Router Lifetime (in seconds) advertised + in Router Advertisement messages + required: false + variants: [] + description: '' + required: false + - name: suffix + type: list + profiles: + - xpath: + - suffix + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 4 + max: 3600 + spec: + default: 1200 + description: Router Lifetime (in seconds) advertised + in Router Advertisement messages + required: false + variants: [] + description: '' + required: false + variants: [] + description: DNS configuration support + required: false + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: '' + required: false + - name: enable-consistency-check + type: bool + profiles: + - xpath: + - enable-consistency-check + validators: [] + spec: {} + description: check consistency of RA messages from other routers. + required: false + - name: hop-limit + type: string + profiles: + - xpath: + - hop-limit + validators: [] + spec: + default: '64' + description: Current Hop Limit advertised in Router Advertisement + messages + required: false + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 0 + max: 9000 + spec: + default: 1800 + description: Router Lifetime (in seconds) advertised in Router + Advertisement messages + required: false + - name: link-mtu + type: string + profiles: + - xpath: + - link-mtu + validators: [] + spec: + default: unspecified + description: value of MTU option in Router Advertisement messages, + upto 9216 in Jumbo-Frame mode, up to 1500 otherwise + required: false + - name: managed-flag + type: bool + profiles: + - xpath: + - managed-flag + validators: [] + spec: {} + description: Set the Managed Configuration Flag (M-bit) in Router + Advertisement messages + required: false + - name: max-interval type: int64 profiles: - xpath: - - ns-interval + - max-interval validators: - type: length spec: - min: 1 - max: 3600 + min: 4 + max: 1800 spec: - default: 1 - description: Interval (in seconds) between consecutive neighbor - solicitation messages + default: 600 + description: Maximum interval (seconds) between consecutive unsolicited + Router Advertisement messages required: false - - name: reachable-time + - name: min-interval type: int64 profiles: - xpath: - - reachable-time + - min-interval validators: - type: length spec: - min: 10 - max: 36000 + min: 3 + max: 1350 spec: - default: 30 - description: Time (in seconds) that the Reachable status for a - neighbor can be maintained + default: 200 + description: Minimum interval (seconds) between consecutive unsolicited + Router Advertisement messages required: false - - name: router-advertisement - type: object + - name: other-flag + type: bool profiles: - xpath: - - router-advertisement + - other-flag + validators: [] + spec: {} + description: Set the Other Stateful Configuration Flag (O-bit) + in Router Advertisement messages + required: false + - name: reachable-time + type: string + profiles: + - xpath: + - reachable-time validators: [] spec: - params: - - name: enable - type: bool - profiles: - - xpath: - - enable - validators: [] - spec: {} - description: '' - required: false - - name: enable-consistency-check - type: bool - profiles: - - xpath: - - enable-consistency-check - validators: [] - spec: {} - description: check consistency of RA messages from other routers. - required: false - - name: hop-limit - type: string - profiles: - - xpath: - - hop-limit - validators: [] - spec: - default: '64' - description: Current Hop Limit advertised in Router Advertisement - messages - required: false - - name: lifetime - type: int64 - profiles: - - xpath: - - lifetime - validators: - - type: length - spec: - min: 0 - max: 9000 - spec: - default: 1800 - description: Router Lifetime (in seconds) advertised in Router - Advertisement messages - required: false - - name: link-mtu - type: string - profiles: - - xpath: - - link-mtu - validators: [] - spec: - default: unspecified - description: value of MTU option in Router Advertisement messages, - upto 9216 in Jumbo-Frame mode, up to 1500 otherwise - required: false - - name: managed-flag - type: bool - profiles: - - xpath: - - managed-flag - validators: [] - spec: {} - description: Set the Managed Configuration Flag (M-bit) in - Router Advertisement messages - required: false - - name: max-interval - type: int64 - profiles: - - xpath: - - max-interval - validators: - - type: length - spec: - min: 4 - max: 1800 - spec: - default: 600 - description: Maximum interval (seconds) between consecutive - unsolicited Router Advertisement messages - required: false - - name: min-interval - type: int64 - profiles: - - xpath: - - min-interval - validators: - - type: length - spec: - min: 3 - max: 1350 - spec: - default: 200 - description: Minimum interval (seconds) between consecutive - unsolicited Router Advertisement messages - required: false - - name: other-flag - type: bool - profiles: - - xpath: - - other-flag - validators: [] - spec: {} - description: Set the Other Stateful Configuration Flag (O-bit) - in Router Advertisement messages - required: false - - name: reachable-time - type: string - profiles: - - xpath: - - reachable-time - validators: [] - spec: - default: unspecified - description: Reachable Time (in milliseconds) advertised in - Router Advertisement messages - required: false - - name: retransmission-timer - type: string - profiles: - - xpath: - - retransmission-timer - validators: [] - spec: - default: unspecified - description: Retransmission Timer (in milliseconds) advertised - in Router Advertisement messages - required: false - - name: router-preference - type: enum - profiles: - - xpath: - - router-preference - validators: - - type: values - spec: - values: - - High - - Medium - - Low - spec: - default: Medium - values: - - value: High - - value: Medium - - value: Low - description: Router Preference - required: false - variants: [] - description: Router Advertisement configuration + default: unspecified + description: Reachable Time (in milliseconds) advertised in Router + Advertisement messages + required: false + - name: retransmission-timer + type: string + profiles: + - xpath: + - retransmission-timer + validators: [] + spec: + default: unspecified + description: Retransmission Timer (in milliseconds) advertised + in Router Advertisement messages + required: false + - name: router-preference + type: enum + profiles: + - xpath: + - router-preference + validators: + - type: values + spec: + values: + - High + - Medium + - Low + spec: + default: Medium + values: + - value: High + - value: Medium + - value: Low + description: Router Preference required: false variants: [] - description: Neighbor Discovery configuration + description: Router Advertisement configuration required: false variants: [] - description: Configure to inherit properties from another interface + description: Neighbor Discovery configuration required: false variants: [] description: Interface IPv6 configuration @@ -2945,6 +2962,7 @@ spec: variants: [] description: DDNS derives upstream NAT IP required: false + variant_group_id: 0 - name: static-ip type: object profiles: @@ -2966,6 +2984,7 @@ spec: spec: {} description: Upstream NAT address FQDN name configuration required: false + variant_group_id: 0 - name: ip-address type: string profiles: @@ -2975,47 +2994,39 @@ spec: spec: {} description: Upstream NAT IP address required: false + variant_group_id: 0 description: Upstream NAT IP address required: false + variant_group_id: 0 description: Upstream NAT IP config required: false variants: [] description: Sdwan related settings required: false - - name: untagged-sub-interface - type: bool - profiles: - - xpath: - - untagged-sub-interface - validators: [] - spec: {} - description: Enable untagged sub-interface - required: false - - name: cluster-interconnect + - name: traffic-interconnect type: bool profiles: - xpath: - - cluster-interconnect + - traffic-interconnect min_version: 11.0.2 max_version: 11.0.3 validators: [] spec: {} description: '' required: false - - name: traffic-interconnect + - name: untagged-sub-interface type: bool profiles: - xpath: - - traffic-interconnect - min_version: 11.0.2 - max_version: 11.0.3 + - untagged-sub-interface validators: [] spec: {} - description: '' + description: Enable untagged sub-interface required: false variants: [] description: Layer 3 interface required: false + variant_group_id: 0 - name: log-card type: object profiles: @@ -3072,6 +3083,7 @@ spec: variants: [] description: log card parameters required: false + variant_group_id: 0 - name: tap type: object profiles: @@ -3095,6 +3107,7 @@ spec: variants: [] description: Tap mode interface required: false + variant_group_id: 0 - name: virtual-wire type: object profiles: @@ -3199,3 +3212,4 @@ spec: variants: [] description: Virtual-wire interface required: false + variant_group_id: 0 diff --git a/specs/network/interface/loopback.yaml b/specs/network/interface/loopback.yaml index 736835d7..8873d770 100644 --- a/specs/network/interface/loopback.yaml +++ b/specs/network/interface/loopback.yaml @@ -16,11 +16,13 @@ go_sdk_config: - network - interface - loopback -xpath_suffix: -- network -- interface -- loopback -- units +panos_xpath: + path: + - network + - interface + - loopback + - units + vars: [] locations: - name: ngfw xpath: diff --git a/specs/network/interface/tunnel.yaml b/specs/network/interface/tunnel.yaml index b019aeb5..25c6025b 100644 --- a/specs/network/interface/tunnel.yaml +++ b/specs/network/interface/tunnel.yaml @@ -16,11 +16,13 @@ go_sdk_config: - network - interface - tunnel -xpath_suffix: -- network -- interface -- tunnel -- units +panos_xpath: + path: + - network + - interface + - tunnel + - units + vars: [] locations: - name: shared xpath: diff --git a/specs/network/interface/vlan.yaml b/specs/network/interface/vlan.yaml index 203cc2e7..01593489 100644 --- a/specs/network/interface/vlan.yaml +++ b/specs/network/interface/vlan.yaml @@ -16,11 +16,13 @@ go_sdk_config: - network - interface - vlan -xpath_suffix: -- network -- interface -- vlan -- units +panos_xpath: + path: + - network + - interface + - vlan + - units + vars: [] locations: - name: shared xpath: @@ -624,135 +626,76 @@ spec: 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 - - name: neighbor-discovery + - name: dhcp-client type: object profiles: - xpath: - - neighbor-discovery + - dhcp-client + min_version: 11.0.2 + max_version: 11.0.3 validators: [] spec: params: - - name: dad-attempts - type: int64 - profiles: - - xpath: - - dad-attempts - validators: - - type: length - spec: - min: 0 - max: 10 - spec: - default: 1 - description: number of consecutive neighbor solicitation messages sent - for duplicate address detection - required: false - - name: enable-dad - type: bool - profiles: - - xpath: - - enable-dad - validators: [] - spec: {} - description: enable duplicate address detection - required: false - - name: enable-ndp-monitor + - name: accept-ra-route type: bool profiles: - xpath: - - enable-ndp-monitor + - accept-ra-route validators: [] spec: {} - description: enable ndp monitoring - required: false - - name: neighbor - type: list - profiles: - - xpath: - - neighbor - - entry - type: entry - validators: [] - spec: - type: object - items: - type: object - spec: - params: - - name: hw-address - type: string - profiles: - - xpath: - - hw-address - validators: [] - spec: {} - description: MAC address (format xx:xx:xx:xx:xx:xx) - required: false - variants: [] - description: '' + description: Accept Router Advertised Default Route required: false - - name: ns-interval + - name: default-route-metric type: int64 profiles: - xpath: - - ns-interval + - default-route-metric validators: - type: length spec: min: 1 - max: 3600 + max: 65535 spec: - default: 1 - description: interval (in seconds) between consecutive neighbor solicitation - messages + default: 10 + description: Metric of the default route created required: false - - name: reachable-time - type: int64 + - name: enable + type: bool profiles: - xpath: - - reachable-time - validators: - - type: length - spec: - min: 10 - max: 36000 - spec: - default: 30 - description: time (in seconds) that the Reachable status for a neighbor - can be maintained + - enable + validators: [] + spec: {} + description: Enable DHCPv6 Client required: false - - name: router-advertisement + - name: neighbor-discovery type: object profiles: - xpath: - - router-advertisement + - neighbor-discovery validators: [] spec: params: - - name: dns-support + - name: dad-attempts + type: int64 + profiles: + - xpath: + - dad-attempts + validators: + - type: length + spec: + min: 1 + max: 10 + spec: + default: 1 + description: number of consecutive neighbor solicitation messages + sent for duplicate address detection + required: false + - name: dns-server type: object profiles: - xpath: - - dns-support + - dns-server validators: [] spec: params: @@ -765,727 +708,427 @@ spec: spec: {} description: '' required: false - - name: server - type: list + - name: source + type: object profiles: - xpath: - - server - - entry - type: entry + - source validators: [] spec: - type: object - items: + params: [] + variants: + - name: dhcpv6 + type: object + profiles: + - xpath: + - dhcpv6 + validators: [] + spec: + params: [] + variants: [] + description: Source from DHCPv6 Server + required: false + - name: manual type: object + profiles: + - xpath: + - manual + validators: [] spec: params: - - name: lifetime - type: int64 + - name: server + type: list profiles: - xpath: - - lifetime - validators: - - type: length - spec: - min: 4 - max: 3600 + - server + - entry + type: entry + validators: [] spec: - default: 1200 - description: (4-3600) lifetime in seconds + type: object + items: + type: object + spec: + params: + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 4 + max: 3600 + spec: + default: 1200 + description: (4-3600) Lifetime in Seconds + required: false + variants: [] + description: '' required: false variants: [] + description: Configure manually + required: false + description: Either source from DHCPv6 Server or manually configure + required: false + variants: [] + description: DNS Recursive Name Server + required: false + - name: dns-suffix + type: object + profiles: + - xpath: + - dns-suffix + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} description: '' required: false - - name: suffix - type: list + - name: source + type: object profiles: - xpath: - - suffix - - entry - type: entry + - source validators: [] spec: - type: object - items: + params: [] + variants: + - name: dhcpv6 + type: object + profiles: + - xpath: + - dhcpv6 + validators: [] + spec: + params: [] + variants: [] + description: Source from DHCPv6 Server + required: false + - name: manual type: object + profiles: + - xpath: + - manual + validators: [] spec: params: - - name: lifetime - type: int64 + - name: suffix + type: list profiles: - xpath: - - lifetime - validators: - - type: length - spec: - min: 4 - max: 3600 + - suffix + - entry + type: entry + validators: [] spec: - default: 1200 - description: (4-3600) lifetime in seconds + type: object + items: + type: object + spec: + params: + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 4 + max: 3600 + spec: + default: 1200 + description: (4-3600) lifetime in seconds + required: false + variants: [] + description: '' required: false variants: [] - description: '' + description: Configure manually + required: false + description: Either source from DHCPv6 Server or manually configure required: false variants: [] - description: DNS configuration support + description: Domain Search List required: false - - name: enable + - name: enable-dad type: bool profiles: - xpath: - - enable + - enable-dad validators: [] spec: {} - description: '' + description: Enable Duplicate Address Detection required: false - - name: enable-consistency-check + - name: enable-ndp-monitor type: bool profiles: - xpath: - - enable-consistency-check + - enable-ndp-monitor validators: [] spec: {} - description: check consistency of RA messages from other routers. + description: Enable NDP Monitoring required: false - - name: hop-limit - type: string + - name: neighbor + type: list profiles: - xpath: - - hop-limit + - neighbor + - entry + type: entry validators: [] spec: - default: '64' - description: Current Hop Limit advertised in Router Advertisement - messages + type: object + items: + type: object + spec: + params: + - name: hw-address + type: string + profiles: + - xpath: + - hw-address + validators: [] + spec: {} + description: MAC address (format xx:xx:xx:xx:xx:xx) + required: false + variants: [] + description: '' required: false - - name: lifetime + - name: ns-interval type: int64 profiles: - xpath: - - lifetime + - ns-interval validators: - type: length spec: - min: 0 - max: 9000 + min: 1 + max: 3600 spec: - default: 1800 - description: Router Lifetime (in seconds) advertised in Router Advertisement + default: 1 + description: interval (in seconds) between consecutive neighbor solicitation messages required: false - - name: link-mtu - type: string - profiles: - - xpath: - - link-mtu - validators: [] - spec: - default: unspecified - description: value of MTU option in Router Advertisement messages, - upto 9216 in Jumbo-Frame mode, up to 1500 otherwise - required: false - - name: managed-flag - type: bool - profiles: - - xpath: - - managed-flag - validators: [] - spec: {} - description: Set the Managed Configuration Flag (M-bit) in Router - Advertisement messages - required: false - - name: max-interval - type: int64 - profiles: - - xpath: - - max-interval - validators: - - type: length - spec: - min: 4 - max: 1800 - spec: - default: 600 - description: Maximum interval (seconds) between consecutive unsolicited - Router Advertisement messages - required: false - - name: min-interval - type: int64 - profiles: - - xpath: - - min-interval - validators: - - type: length - spec: - min: 3 - max: 1350 - spec: - default: 200 - description: Minimum interval (seconds) between consecutive unsolicited - Router Advertisement messages - required: false - - name: other-flag - type: bool - profiles: - - xpath: - - other-flag - validators: [] - spec: {} - description: Set the Other Stateful Configuration Flag (O-bit) in - Router Advertisement messages - required: false - name: reachable-time - type: string + type: int64 profiles: - xpath: - reachable-time - validators: [] - spec: - default: unspecified - description: Reachable Time (in milliseconds) advertised in Router - Advertisement messages - required: false - - name: retransmission-timer - type: string - profiles: - - xpath: - - retransmission-timer - validators: [] - spec: - default: unspecified - description: Retransmission Timer (in milliseconds) advertised in - Router Advertisement messages - required: false - - name: router-preference - type: enum - profiles: - - xpath: - - router-preference validators: - - type: values + - type: length spec: - values: - - High - - Medium - - Low + min: 10 + max: 36000 spec: - default: Medium - values: - - value: High - - value: Medium - - value: Low - description: '' + default: 30 + description: time (in seconds) that the Reachable status for a neighbor + can be maintained required: false variants: [] - description: Router advertisement configuration - required: false - variants: [] - description: Neighbor Discovery configuration - required: false - - name: dhcp-client - type: object - profiles: - - xpath: - - dhcp-client - min_version: 11.0.2 - max_version: 11.0.3 - validators: [] - spec: - params: - - name: accept-ra-route - type: bool - profiles: - - xpath: - - accept-ra-route - validators: [] - spec: {} - description: Accept Router Advertised Default Route + description: Neighbor Discovery configuration required: false - - name: default-route-metric - type: int64 + - name: preference + type: enum profiles: - xpath: - - default-route-metric + - preference validators: - - type: length + - type: values spec: - min: 1 - max: 65535 + values: + - low + - medium + - high spec: - default: 10 - description: Metric of the default route created - required: false - - name: enable - type: bool - profiles: - - xpath: - - enable - validators: [] - spec: {} - description: Enable DHCPv6 Client + default: high + values: + - value: low + - value: medium + - value: high + description: Select Low/Medium/High required: false - - name: neighbor-discovery + - name: prefix-delegation type: object profiles: - xpath: - - neighbor-discovery + - prefix-delegation validators: [] spec: params: - - name: dad-attempts - type: int64 + - name: enable + type: object profiles: - xpath: - - dad-attempts - validators: - - type: length - spec: - min: 1 - max: 10 - spec: - default: 1 - description: number of consecutive neighbor solicitation messages - sent for duplicate address detection - required: false - - name: dns-server - type: object - profiles: - - xpath: - - dns-server + - enable validators: [] spec: - params: - - name: enable - type: bool + params: [] + variants: + - name: 'no' + type: object profiles: - xpath: - - enable + - 'no' validators: [] - spec: {} - description: '' + spec: + params: [] + variants: [] + description: Disable Prefix Delegation required: false - - name: source + - name: 'yes' type: object profiles: - xpath: - - source + - 'yes' validators: [] spec: - params: [] - variants: - - name: dhcpv6 - type: object + params: + - name: pfx-pool-name + type: string profiles: - xpath: - - dhcpv6 - validators: [] + - pfx-pool-name + validators: + - type: length + spec: + max: 63 + spec: {} + description: Configure unique Prefix Pool Name + required: false + - name: prefix-len + type: int64 + profiles: + - xpath: + - prefix-len + validators: + - type: length + spec: + min: 0 + max: 128 spec: - params: [] - variants: [] - description: Source from DHCPv6 Server + default: 48 + description: Hint DHCP Prefix Length (bits) required: false - - name: manual - type: object + - name: prefix-len-hint + type: bool profiles: - xpath: - - manual + - prefix-len-hint validators: [] - spec: - params: - - name: server - type: list - profiles: - - xpath: - - server - - entry - type: entry - validators: [] - spec: - type: object - items: - type: object - spec: - params: - - name: lifetime - type: int64 - profiles: - - xpath: - - lifetime - validators: - - type: length - spec: - min: 4 - max: 3600 - spec: - default: 1200 - description: (4-3600) Lifetime in Seconds - required: false - variants: [] - description: '' - required: false - variants: [] - description: Configure manually + spec: {} + description: Send prefix length hint to server required: false - description: Either source from DHCPv6 Server or manually configure + variants: [] + description: Enable Prefix Delegation required: false - variants: [] - description: DNS Recursive Name Server + description: Enable/Disable Prefix Delegation required: false - - name: dns-suffix + variants: [] + description: Configure Prefix Delegation Options + required: false + - name: v6-options + type: object + profiles: + - xpath: + - v6-options + validators: [] + spec: + params: + - name: duid-type + type: enum + profiles: + - xpath: + - duid-type + validators: + - type: values + spec: + values: + - duid-type-llt + - duid-type-ll + spec: + default: duid-type-llt + values: + - value: duid-type-llt + - value: duid-type-ll + description: Select DUID-LLT/DUID-LL + required: false + - name: enable type: object profiles: - xpath: - - dns-suffix + - enable validators: [] spec: - params: - - name: enable - type: bool + params: [] + variants: + - name: 'no' + type: object profiles: - xpath: - - enable + - 'no' validators: [] - spec: {} - description: '' + spec: + params: [] + variants: [] + description: Disable IPv6 Address required: false - - name: source + - name: 'yes' type: object profiles: - xpath: - - source + - 'yes' validators: [] spec: - params: [] - variants: - - name: dhcpv6 - type: object + params: + - name: non-temp-addr + type: bool profiles: - xpath: - - dhcpv6 + - non-temp-addr validators: [] - spec: - params: [] - variants: [] - description: Source from DHCPv6 Server + spec: {} + description: Request Non-Temporary Address Type required: false - - name: manual - type: object + - name: temp-addr + type: bool profiles: - xpath: - - manual + - temp-addr validators: [] - spec: - params: - - name: suffix - type: list - profiles: - - xpath: - - suffix - - entry - type: entry - validators: [] - spec: - type: object - items: - type: object - spec: - params: - - name: lifetime - type: int64 - profiles: - - xpath: - - lifetime - validators: - - type: length - spec: - min: 4 - max: 3600 - spec: - default: 1200 - description: (4-3600) lifetime in seconds - required: false - variants: [] - description: '' - required: false - variants: [] - description: Configure manually + spec: {} + description: Request Temporary Address Type required: false - description: Either source from DHCPv6 Server or manually configure + variants: [] + description: Enable IPv6 Address required: false - variants: [] - description: Domain Search List + description: Enable/Disable IPv6 Address required: false - - name: enable-dad + - name: rapid-commit type: bool profiles: - xpath: - - enable-dad + - rapid-commit validators: [] spec: {} - description: Enable Duplicate Address Detection + description: Enable Rapid Commit required: false - - name: enable-ndp-monitor + - name: support-srvr-reconfig type: bool profiles: - xpath: - - enable-ndp-monitor + - support-srvr-reconfig validators: [] spec: {} - description: Enable NDP Monitoring - required: false - - name: neighbor - type: list - profiles: - - xpath: - - neighbor - - entry - type: entry - validators: [] - spec: - type: object - items: - type: object - spec: - params: - - name: hw-address - type: string - profiles: - - xpath: - - hw-address - validators: [] - spec: {} - description: MAC address (format xx:xx:xx:xx:xx:xx) - required: false - variants: [] - description: '' - required: false - - name: ns-interval - type: int64 - profiles: - - xpath: - - ns-interval - validators: - - type: length - spec: - min: 1 - max: 3600 - spec: - default: 1 - description: interval (in seconds) between consecutive neighbor solicitation - messages - required: false - - name: reachable-time - type: int64 - profiles: - - xpath: - - reachable-time - validators: - - type: length - spec: - min: 10 - max: 36000 - spec: - default: 30 - description: time (in seconds) that the Reachable status for a neighbor - can be maintained - required: false - variants: [] - description: Neighbor Discovery configuration - required: false - - name: preference - type: enum - profiles: - - xpath: - - preference - validators: - - type: values - spec: - values: - - low - - medium - - high - spec: - default: high - values: - - value: low - - value: medium - - value: high - description: Select Low/Medium/High - required: false - - name: prefix-delegation - type: object - profiles: - - xpath: - - prefix-delegation - validators: [] - spec: - params: - - name: enable - type: object - profiles: - - xpath: - - enable - validators: [] - spec: - params: [] - variants: - - name: 'no' - type: object - profiles: - - xpath: - - 'no' - validators: [] - spec: - params: [] - variants: [] - description: Disable Prefix Delegation - required: false - - name: 'yes' - type: object - profiles: - - xpath: - - 'yes' - validators: [] - spec: - params: - - name: pfx-pool-name - type: string - profiles: - - xpath: - - pfx-pool-name - validators: - - type: length - spec: - max: 63 - spec: {} - description: Configure unique Prefix Pool Name - required: false - - name: prefix-len - type: int64 - profiles: - - xpath: - - prefix-len - validators: - - type: length - spec: - min: 0 - max: 128 - spec: - default: 48 - description: Hint DHCP Prefix Length (bits) - required: false - - name: prefix-len-hint - type: bool - profiles: - - xpath: - - prefix-len-hint - validators: [] - spec: {} - description: Send prefix length hint to server - required: false - variants: [] - description: Enable Prefix Delegation - required: false - description: Enable/Disable Prefix Delegation - required: false - variants: [] - description: Configure Prefix Delegation Options - required: false - - name: v6-options - type: object - profiles: - - xpath: - - v6-options - validators: [] - spec: - params: - - name: duid-type - type: enum - profiles: - - xpath: - - duid-type - validators: - - type: values - spec: - values: - - duid-type-llt - - duid-type-ll - spec: - default: duid-type-llt - values: - - value: duid-type-llt - - value: duid-type-ll - description: Select DUID-LLT/DUID-LL - required: false - - name: enable - type: object - profiles: - - xpath: - - enable - validators: [] - spec: - params: [] - variants: - - name: 'no' - type: object - profiles: - - xpath: - - 'no' - validators: [] - spec: - params: [] - variants: [] - description: Disable IPv6 Address - required: false - - name: 'yes' - type: object - profiles: - - xpath: - - 'yes' - validators: [] - spec: - params: - - name: non-temp-addr - type: bool - profiles: - - xpath: - - non-temp-addr - validators: [] - spec: {} - description: Request Non-Temporary Address Type - required: false - - name: temp-addr - type: bool - profiles: - - xpath: - - temp-addr - validators: [] - spec: {} - description: Request Temporary Address Type - required: false - variants: [] - description: Enable IPv6 Address - required: false - description: Enable/Disable IPv6 Address - required: false - - name: rapid-commit - type: bool - profiles: - - xpath: - - rapid-commit - validators: [] - spec: {} - description: Enable Rapid Commit - required: false - - name: support-srvr-reconfig - type: bool - profiles: - - xpath: - - support-srvr-reconfig - validators: [] - spec: {} - description: Enable DHCPv6 Server Re-Configuration Support + description: Enable DHCPv6 Server Re-Configuration Support required: false variants: [] description: Configure DHCPv6 Options @@ -1493,6 +1136,15 @@ spec: variants: [] description: Configure DHCPv6 Client required: false + - name: enabled + type: bool + profiles: + - xpath: + - enabled + validators: [] + spec: {} + description: Enable IPv6 on the interface + required: false - name: inherited type: object profiles: @@ -2225,6 +1877,356 @@ spec: variants: [] description: Configure to inherit properties from another interface required: false + - name: interface-id + type: string + profiles: + - xpath: + - interface-id + validators: [] + spec: + default: EUI-64 + description: '' + required: false + - name: neighbor-discovery + type: object + profiles: + - xpath: + - neighbor-discovery + validators: [] + spec: + params: + - name: dad-attempts + type: int64 + profiles: + - xpath: + - dad-attempts + validators: + - type: length + spec: + min: 0 + max: 10 + spec: + default: 1 + description: number of consecutive neighbor solicitation messages sent + for duplicate address detection + required: false + - name: enable-dad + type: bool + profiles: + - xpath: + - enable-dad + validators: [] + spec: {} + description: enable duplicate address detection + required: false + - name: enable-ndp-monitor + type: bool + profiles: + - xpath: + - enable-ndp-monitor + validators: [] + spec: {} + description: enable ndp monitoring + required: false + - name: neighbor + type: list + profiles: + - xpath: + - neighbor + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: hw-address + type: string + profiles: + - xpath: + - hw-address + validators: [] + spec: {} + description: MAC address (format xx:xx:xx:xx:xx:xx) + required: false + variants: [] + description: '' + required: false + - name: ns-interval + type: int64 + profiles: + - xpath: + - ns-interval + validators: + - type: length + spec: + min: 1 + max: 3600 + spec: + default: 1 + description: interval (in seconds) between consecutive neighbor solicitation + messages + required: false + - name: reachable-time + type: int64 + profiles: + - xpath: + - reachable-time + validators: + - type: length + spec: + min: 10 + max: 36000 + spec: + default: 30 + description: time (in seconds) that the Reachable status for a neighbor + can be maintained + required: false + - name: router-advertisement + type: object + profiles: + - xpath: + - router-advertisement + validators: [] + spec: + params: + - name: dns-support + type: object + profiles: + - xpath: + - dns-support + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: '' + required: false + - name: server + type: list + profiles: + - xpath: + - server + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 4 + max: 3600 + spec: + default: 1200 + description: (4-3600) lifetime in seconds + required: false + variants: [] + description: '' + required: false + - name: suffix + type: list + profiles: + - xpath: + - suffix + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 4 + max: 3600 + spec: + default: 1200 + description: (4-3600) lifetime in seconds + required: false + variants: [] + description: '' + required: false + variants: [] + description: DNS configuration support + required: false + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: '' + required: false + - name: enable-consistency-check + type: bool + profiles: + - xpath: + - enable-consistency-check + validators: [] + spec: {} + description: check consistency of RA messages from other routers. + required: false + - name: hop-limit + type: string + profiles: + - xpath: + - hop-limit + validators: [] + spec: + default: '64' + description: Current Hop Limit advertised in Router Advertisement + messages + required: false + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 0 + max: 9000 + spec: + default: 1800 + description: Router Lifetime (in seconds) advertised in Router Advertisement + messages + required: false + - name: link-mtu + type: string + profiles: + - xpath: + - link-mtu + validators: [] + spec: + default: unspecified + description: value of MTU option in Router Advertisement messages, + upto 9216 in Jumbo-Frame mode, up to 1500 otherwise + required: false + - name: managed-flag + type: bool + profiles: + - xpath: + - managed-flag + validators: [] + spec: {} + description: Set the Managed Configuration Flag (M-bit) in Router + Advertisement messages + required: false + - name: max-interval + type: int64 + profiles: + - xpath: + - max-interval + validators: + - type: length + spec: + min: 4 + max: 1800 + spec: + default: 600 + description: Maximum interval (seconds) between consecutive unsolicited + Router Advertisement messages + required: false + - name: min-interval + type: int64 + profiles: + - xpath: + - min-interval + validators: + - type: length + spec: + min: 3 + max: 1350 + spec: + default: 200 + description: Minimum interval (seconds) between consecutive unsolicited + Router Advertisement messages + required: false + - name: other-flag + type: bool + profiles: + - xpath: + - other-flag + validators: [] + spec: {} + description: Set the Other Stateful Configuration Flag (O-bit) in + Router Advertisement messages + required: false + - name: reachable-time + type: string + profiles: + - xpath: + - reachable-time + validators: [] + spec: + default: unspecified + description: Reachable Time (in milliseconds) advertised in Router + Advertisement messages + required: false + - name: retransmission-timer + type: string + profiles: + - xpath: + - retransmission-timer + validators: [] + spec: + default: unspecified + description: Retransmission Timer (in milliseconds) advertised in + Router Advertisement messages + required: false + - name: router-preference + type: enum + profiles: + - xpath: + - router-preference + validators: + - type: values + spec: + values: + - High + - Medium + - Low + spec: + default: Medium + values: + - value: High + - value: Medium + - value: Low + description: '' + required: false + variants: [] + description: Router advertisement configuration + required: false + variants: [] + description: Neighbor Discovery configuration + required: false variants: [] description: Interface IPv6 configuration required: false diff --git a/specs/network/logical-router.yaml b/specs/network/logical-router.yaml index 08ab1ac2..8a2d1d6c 100644 --- a/specs/network/logical-router.yaml +++ b/specs/network/logical-router.yaml @@ -15,9 +15,11 @@ go_sdk_config: package: - network - logical_router -xpath_suffix: -- network -- logical-router +panos_xpath: + path: + - network + - logical-router + vars: [] locations: - name: vsys xpath: diff --git a/specs/network/profiles/anti-spyware-profile.yaml b/specs/network/profiles/anti-spyware-profile.yaml index 254effb1..28607082 100644 --- a/specs/network/profiles/anti-spyware-profile.yaml +++ b/specs/network/profiles/anti-spyware-profile.yaml @@ -16,9 +16,11 @@ go_sdk_config: - security - profiles - spyware -xpath_suffix: -- profiles -- spyware +panos_xpath: + path: + - profiles + - spyware + vars: [] locations: - name: shared xpath: @@ -62,7 +64,6 @@ locations: description: Located in a specific Virtual System devices: - ngfw - - panorama validators: [] required: false read_only: false @@ -292,6 +293,73 @@ spec: variants: [] description: '' required: false + - name: rtype-action + type: object + profiles: + - xpath: + - rtype-action + min_version: 11.0.2 + max_version: 11.0.3 + validators: [] + spec: + params: + - name: any + type: enum + profiles: + - xpath: + - any + validators: + - type: values + spec: + values: + - allow + - block + spec: + default: allow + values: + - value: allow + - value: block + description: ANY(255) + required: false + - name: https + type: enum + profiles: + - xpath: + - https + validators: + - type: values + spec: + values: + - allow + - block + spec: + default: allow + values: + - value: allow + - value: block + description: HTTPS(65) + required: false + - name: svcb + type: enum + profiles: + - xpath: + - svcb + validators: + - type: values + spec: + values: + - allow + - block + spec: + default: allow + values: + - value: allow + - value: block + description: SVCB(64) + required: false + variants: [] + description: Action for specific record types + required: false - name: sinkhole type: object profiles: @@ -370,73 +438,6 @@ spec: variants: [] description: '' required: false - - name: rtype-action - type: object - profiles: - - xpath: - - rtype-action - min_version: 11.0.2 - max_version: 11.0.3 - validators: [] - spec: - params: - - name: any - type: enum - profiles: - - xpath: - - any - validators: - - type: values - spec: - values: - - allow - - block - spec: - default: allow - values: - - value: allow - - value: block - description: ANY(255) - required: false - - name: https - type: enum - profiles: - - xpath: - - https - validators: - - type: values - spec: - values: - - allow - - block - spec: - default: allow - values: - - value: allow - - value: block - description: HTTPS(65) - required: false - - name: svcb - type: enum - profiles: - - xpath: - - svcb - validators: - - type: values - spec: - values: - - allow - - block - spec: - default: allow - values: - - value: allow - - value: block - description: SVCB(64) - required: false - variants: [] - description: Action for specific record types - required: false variants: [] description: '' required: false diff --git a/specs/network/profiles/interface-management-profile.yaml b/specs/network/profiles/interface-management-profile.yaml index c5edeca3..6a42408b 100644 --- a/specs/network/profiles/interface-management-profile.yaml +++ b/specs/network/profiles/interface-management-profile.yaml @@ -15,10 +15,12 @@ go_sdk_config: - network - profiles - interface_management -xpath_suffix: -- network -- profiles -- interface-management-profile +panos_xpath: + path: + - network + - profiles + - interface-management-profile + vars: [] locations: - name: ngfw xpath: diff --git a/specs/network/subinterface/ethernet/layer3.yaml b/specs/network/subinterface/ethernet/layer3.yaml new file mode 100644 index 00000000..38683048 --- /dev/null +++ b/specs/network/subinterface/ethernet/layer3.yaml @@ -0,0 +1,2632 @@ +name: ethernet-layer3-subinterface +terraform_provider_config: + description: Ethernet Layer3 Subinterface + skip_resource: false + skip_datasource: false + resource_type: entry + resource_variants: + - singular + suffix: ethernet_layer3_subinterface + plural_suffix: '' + plural_name: '' + plural_description: '' +go_sdk_config: + skip: false + package: + - network + - interface + - ethernet + - subinterface + - layer3 +panos_xpath: + path: + - network + - interface + - ethernet + - $parent + - layer3 + - units + - $name + vars: + - name: parent + spec: + type: entry + xpath: /params[@name="parent"] + - name: name + spec: + type: entry + xpath: /params[@name="name"] +locations: +- name: shared + xpath: + path: + - config + - shared + vars: [] + description: Panorama shared object + 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: +- variant: '' + type: template + locations: + - name: vsys + xpath: + path: + - vsys + - $vsys + - import + - network + - interface + vars: + - name: vsys + description: The vsys. + required: false + default: vsys1 + validators: [] + type: entry + description: '' + validators: [] + required: true + read_only: false +spec: + params: + - name: parent + type: string + profiles: + - xpath: [] + codegen_overrides: + gosdk: + skip: true + terraform: + xpath_variable: parent + - name: adjust-tcp-mss + type: object + profiles: + - xpath: + - adjust-tcp-mss + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: Set if TCP MSS value should be reduced based on mtu + required: false + - name: ipv4-mss-adjustment + type: int64 + profiles: + - xpath: + - ipv4-mss-adjustment + validators: + - type: length + spec: + min: 40 + max: 300 + spec: + default: 40 + description: IPv4 MSS adjustment size (in bytes) + required: false + - name: ipv6-mss-adjustment + type: int64 + profiles: + - xpath: + - ipv6-mss-adjustment + validators: + - type: length + spec: + min: 60 + max: 300 + spec: + default: 60 + description: IPv6 MSS adjustment size (in bytes) + required: false + variants: [] + description: TCP MSS adjustment configuration + required: false + - name: arp + type: list + profiles: + - xpath: + - arp + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: hw-address + type: string + profiles: + - xpath: + - hw-address + validators: [] + spec: {} + description: MAC address (format xx:xx:xx:xx:xx:xx) + required: false + variants: [] + description: '' + required: false + - 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: + max: 1024 + spec: {} + description: '' + required: false + - name: ddns-config + type: object + profiles: + - xpath: + - ddns-config + validators: [] + spec: + params: + - name: ddns-cert-profile + type: string + profiles: + - xpath: + - ddns-cert-profile + validators: [] + spec: {} + description: '' + required: false + - name: ddns-enabled + type: bool + profiles: + - xpath: + - ddns-enabled + validators: [] + spec: {} + description: '' + required: false + - name: ddns-hostname + type: string + profiles: + - xpath: + - ddns-hostname + validators: + - type: length + spec: + min: 1 + max: 255 + spec: {} + description: ddns hostname variable or real address + required: false + - name: ddns-ip + type: list + profiles: + - xpath: + - ddns-ip + type: member + validators: [] + spec: + type: string + items: + type: string + description: '' + required: false + - name: ddns-ipv6 + type: list + profiles: + - xpath: + - ddns-ipv6 + type: member + validators: [] + spec: + type: string + items: + type: string + description: '' + required: false + - name: ddns-update-interval + type: int64 + profiles: + - xpath: + - ddns-update-interval + validators: + - type: length + spec: + min: 1 + max: 30 + spec: + default: 1 + description: '' + required: false + - name: ddns-vendor + type: string + profiles: + - xpath: + - ddns-vendor + validators: + - type: length + spec: + max: 127 + spec: {} + description: Vendor and product type + required: false + - name: ddns-vendor-config + type: list + profiles: + - xpath: + - ddns-vendor-config + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: value + type: string + profiles: + - xpath: + - value + validators: + - type: length + spec: + max: 255 + spec: {} + description: '' + required: false + variants: [] + description: '' + required: false + variants: [] + description: '' + required: false + - name: decrypt-forward + type: bool + profiles: + - xpath: + - decrypt-forward + validators: [] + spec: {} + description: '' + required: false + - name: df-ignore + type: bool + profiles: + - xpath: + - df-ignore + validators: [] + spec: {} + description: '' + required: false + - name: dhcp-client + type: object + profiles: + - xpath: + - dhcp-client + validators: [] + spec: + params: + - name: create-default-route + type: bool + profiles: + - xpath: + - create-default-route + validators: [] + spec: {} + description: Automatically create default route pointing to default gateway + provided by server + required: false + - name: default-route-metric + type: int64 + profiles: + - xpath: + - default-route-metric + validators: + - type: length + spec: + min: 1 + max: 65535 + spec: + default: 10 + description: Metric of the default route created + required: false + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: '' + required: false + - name: send-hostname + type: object + profiles: + - xpath: + - send-hostname + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: '' + required: false + - name: hostname + type: string + profiles: + - xpath: + - hostname + validators: [] + spec: + default: system-hostname + description: Set Interface Hostname + required: false + variants: [] + description: Enable send firewall or custom hostname to DHCP server + required: false + variants: [] + description: dhcp client configuration + 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: + - name: sdwan-gateway + type: string + profiles: + - xpath: + - sdwan-gateway + validators: [] + spec: {} + description: Gateway IPv4 Address + required: false + 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 + - name: advertise + type: object + profiles: + - xpath: + - advertise + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: enable advertising this prefix in router advertisements + required: false + - name: valid-lifetime + type: string + profiles: + - xpath: + - valid-lifetime + validators: [] + spec: + default: '2592000' + description: Valid Lifetime (in seconds) of the prefix advertised + in Router Advertisement messages + required: false + - name: preferred-lifetime + type: string + profiles: + - xpath: + - preferred-lifetime + validators: [] + spec: + default: '604800' + description: Preferred Lifetime (in seconds) of the prefix advertised + in Router Advertisement messages + required: false + - name: onlink-flag + type: bool + profiles: + - xpath: + - onlink-flag + validators: [] + spec: {} + description: Set the On-Link Flag (L-bit) of the prefix in Router + Advertisement messages + required: false + - name: auto-config-flag + type: bool + profiles: + - xpath: + - auto-config-flag + validators: [] + spec: {} + description: Set the Auto Address Configuration Flag (A-bit) of + the prefix in Router Advertisement messages + required: false + variants: [] + description: configure router advertisement prefix option + required: false + variants: [] + description: '' + required: false + - name: dhcp-client + type: object + profiles: + - xpath: + - dhcp-client + min_version: 11.0.2 + max_version: 11.0.3 + validators: [] + spec: + params: + - name: accept-ra-route + type: bool + profiles: + - xpath: + - accept-ra-route + validators: [] + spec: {} + description: Accept Router Advertised Default Route + required: false + - name: default-route-metric + type: int64 + profiles: + - xpath: + - default-route-metric + validators: + - type: length + spec: + min: 1 + max: 65535 + spec: + default: 10 + description: Metric of the default route created + required: false + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: Enable DHCPv6 Client + required: false + - name: neighbor-discovery + type: object + profiles: + - xpath: + - neighbor-discovery + validators: [] + spec: + params: + - name: dad-attempts + type: int64 + profiles: + - xpath: + - dad-attempts + validators: + - type: length + spec: + min: 1 + max: 10 + spec: + default: 1 + description: number of consecutive neighbor solicitation messages + sent for duplicate address detection + required: false + - name: dns-server + type: object + profiles: + - xpath: + - dns-server + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: '' + required: false + - name: source + type: object + profiles: + - xpath: + - source + validators: [] + spec: + params: [] + variants: + - name: dhcpv6 + type: object + profiles: + - xpath: + - dhcpv6 + validators: [] + spec: + params: [] + variants: [] + description: Source from DHCPv6 Server + required: false + - name: manual + type: object + profiles: + - xpath: + - manual + validators: [] + spec: + params: + - name: server + type: list + profiles: + - xpath: + - server + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 4 + max: 3600 + spec: + default: 1200 + description: (4-3600) Lifetime in Seconds + required: false + variants: [] + description: '' + required: false + variants: [] + description: Configure manually + required: false + description: Either source from DHCPv6 Server or manually configure + required: false + variants: [] + description: DNS Recursive Name Server + required: false + - name: dns-suffix + type: object + profiles: + - xpath: + - dns-suffix + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: '' + required: false + - name: source + type: object + profiles: + - xpath: + - source + validators: [] + spec: + params: [] + variants: + - name: dhcpv6 + type: object + profiles: + - xpath: + - dhcpv6 + validators: [] + spec: + params: [] + variants: [] + description: Source from DHCPv6 Server + required: false + - name: manual + type: object + profiles: + - xpath: + - manual + validators: [] + spec: + params: + - name: suffix + type: list + profiles: + - xpath: + - suffix + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 4 + max: 3600 + spec: + default: 1200 + description: (4-3600) lifetime in seconds + required: false + variants: [] + description: '' + required: false + variants: [] + description: Configure manually + required: false + description: Either source from DHCPv6 Server or manually configure + required: false + variants: [] + description: Domain Search List + required: false + - name: enable-dad + type: bool + profiles: + - xpath: + - enable-dad + validators: [] + spec: {} + description: Enable Duplicate Address Detection + required: false + - name: enable-ndp-monitor + type: bool + profiles: + - xpath: + - enable-ndp-monitor + validators: [] + spec: {} + description: Enable NDP Monitoring + required: false + - name: neighbor + type: list + profiles: + - xpath: + - neighbor + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: hw-address + type: string + profiles: + - xpath: + - hw-address + validators: [] + spec: {} + description: MAC address (format xx:xx:xx:xx:xx:xx) + required: false + variants: [] + description: '' + required: false + - name: ns-interval + type: int64 + profiles: + - xpath: + - ns-interval + validators: + - type: length + spec: + min: 1 + max: 3600 + spec: + default: 1 + description: interval (in seconds) between consecutive neighbor solicitation + messages + required: false + - name: reachable-time + type: int64 + profiles: + - xpath: + - reachable-time + validators: + - type: length + spec: + min: 10 + max: 36000 + spec: + default: 30 + description: time (in seconds) that the Reachable status for a neighbor + can be maintained + required: false + variants: [] + description: Neighbor Discovery configuration + required: false + - name: preference + type: enum + profiles: + - xpath: + - preference + validators: + - type: values + spec: + values: + - low + - medium + - high + spec: + default: high + values: + - value: low + - value: medium + - value: high + description: Select Low/Medium/High + required: false + - name: prefix-delegation + type: object + profiles: + - xpath: + - prefix-delegation + validators: [] + spec: + params: + - name: enable + type: object + profiles: + - xpath: + - enable + validators: [] + spec: + params: [] + variants: + - name: 'no' + type: object + profiles: + - xpath: + - 'no' + validators: [] + spec: + params: [] + variants: [] + description: Disable Prefix Delegation + required: false + - name: 'yes' + type: object + profiles: + - xpath: + - 'yes' + validators: [] + spec: + params: + - name: pfx-pool-name + type: string + profiles: + - xpath: + - pfx-pool-name + validators: + - type: length + spec: + max: 63 + spec: {} + description: Configure unique Prefix Pool Name + required: false + - name: prefix-len + type: int64 + profiles: + - xpath: + - prefix-len + validators: + - type: length + spec: + min: 0 + max: 128 + spec: + default: 48 + description: Hint DHCP Prefix Length (bits) + required: false + - name: prefix-len-hint + type: bool + profiles: + - xpath: + - prefix-len-hint + validators: [] + spec: {} + description: Send prefix length hint to server + required: false + variants: [] + description: Enable Prefix Delegation + required: false + description: Enable/Disable Prefix Delegation + required: false + variants: [] + description: Configure Prefix Delegation Options + required: false + - name: v6-options + type: object + profiles: + - xpath: + - v6-options + validators: [] + spec: + params: + - name: duid-type + type: enum + profiles: + - xpath: + - duid-type + validators: + - type: values + spec: + values: + - duid-type-llt + - duid-type-ll + spec: + default: duid-type-llt + values: + - value: duid-type-llt + - value: duid-type-ll + description: Select DUID-LLT/DUID-LL + required: false + - name: enable + type: object + profiles: + - xpath: + - enable + validators: [] + spec: + params: [] + variants: + - name: 'no' + type: object + profiles: + - xpath: + - 'no' + validators: [] + spec: + params: [] + variants: [] + description: Disable IPv6 Address + required: false + - name: 'yes' + type: object + profiles: + - xpath: + - 'yes' + validators: [] + spec: + params: + - name: non-temp-addr + type: bool + profiles: + - xpath: + - non-temp-addr + validators: [] + spec: {} + description: Request Non-Temporary Address Type + required: false + - name: temp-addr + type: bool + profiles: + - xpath: + - temp-addr + validators: [] + spec: {} + description: Request Temporary Address Type + required: false + variants: [] + description: Enable IPv6 Address + required: false + description: Enable/Disable IPv6 Address + required: false + - name: rapid-commit + type: bool + profiles: + - xpath: + - rapid-commit + validators: [] + spec: {} + description: Enable Rapid Commit + required: false + - name: support-srvr-reconfig + type: bool + profiles: + - xpath: + - support-srvr-reconfig + validators: [] + spec: {} + description: Enable DHCPv6 Server Re-Configuration Support + required: false + variants: [] + description: Configure DHCPv6 Options + required: false + variants: [] + description: Configure DHCPv6 Client + required: false + - name: enabled + type: bool + profiles: + - xpath: + - enabled + validators: [] + spec: {} + description: Enable IPv6 on the interface + required: false + - name: inherited + type: object + profiles: + - xpath: + - inherited + min_version: 11.0.2 + max_version: 11.0.3 + validators: [] + spec: + params: + - name: assign-addr + type: list + profiles: + - xpath: + - assign-addr + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: type + type: object + profiles: + - xpath: + - type + validators: [] + spec: + params: [] + variants: + - name: gua + type: object + profiles: + - xpath: + - gua + validators: [] + spec: + params: + - name: enable-on-interface + type: bool + profiles: + - xpath: + - enable-on-interface + validators: [] + spec: {} + description: Enable on Interface + required: false + - name: prefix-pool + type: string + profiles: + - xpath: + - prefix-pool + validators: + - type: length + spec: + max: 63 + spec: {} + description: Prefix-Pool Name + required: false + - name: pool-type + type: object + profiles: + - xpath: + - pool-type + validators: [] + spec: + params: [] + variants: + - name: dynamic + type: object + profiles: + - xpath: + - dynamic + validators: [] + spec: + params: [] + variants: [] + description: Dynamically allocate Pool to member Interfaces + required: false + - name: dynamic-id + type: object + profiles: + - xpath: + - dynamic-id + validators: [] + spec: + params: + - name: identifier + type: int64 + profiles: + - xpath: + - identifier + validators: + - type: length + spec: + min: 0 + max: 4095 + spec: {} + description: Range [0-4095] must be unqiue for + this prefix-pool + required: false + variants: [] + description: Assign Id to selected delegate prefix + from the pool + required: false + description: Select Pool Allocation Method + required: false + - name: advertise + type: object + profiles: + - xpath: + - advertise + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: Enable advertising this prefix in router + advertisements + required: false + - name: onlink-flag + type: bool + profiles: + - xpath: + - onlink-flag + validators: [] + spec: {} + description: Set the On-Link Flag (L-bit) of the prefix + in Router Advertisement messages + required: false + - name: auto-config-flag + type: bool + profiles: + - xpath: + - auto-config-flag + validators: [] + spec: {} + description: Set the Auto Address Configuration Flag + (A-bit) of the prefix in Router Advertisement messages + required: false + variants: [] + description: Configure router advertisement prefix options + required: false + variants: [] + description: Select GUA (Global Unique Address)from Pool + required: false + - name: ula + type: object + profiles: + - xpath: + - ula + validators: [] + spec: + params: + - name: enable-on-interface + type: bool + profiles: + - xpath: + - enable-on-interface + validators: [] + spec: {} + description: Configure this address on Interface + required: false + - name: address + type: string + profiles: + - xpath: + - address + validators: [] + spec: {} + description: Configure ULA (Unique Local Address) + required: false + - name: prefix + type: bool + profiles: + - xpath: + - prefix + validators: [] + spec: {} + description: Use this as prefix to form full address with + interface id/EUI-64 + required: false + - name: anycast + type: bool + profiles: + - xpath: + - anycast + validators: [] + spec: {} + description: Anycast Address + required: false + - name: advertise + type: object + profiles: + - xpath: + - advertise + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: enable advertising this prefix in router + advertisements + required: false + - name: valid-lifetime + type: string + profiles: + - xpath: + - valid-lifetime + validators: [] + spec: + default: '2592000' + description: Valid Lifetime (in seconds) of the prefix + advertised in Router Advertisement messages + required: false + - name: preferred-lifetime + type: string + profiles: + - xpath: + - preferred-lifetime + validators: [] + spec: + default: '604800' + description: Preferred Lifetime (in seconds) of the + prefix advertised in Router advertisement messages + required: false + - name: onlink-flag + type: bool + profiles: + - xpath: + - onlink-flag + validators: [] + spec: {} + description: Set the On-Link Flag (L-bit) of the prefix + in Router Advertisement messages + required: false + - name: auto-config-flag + type: bool + profiles: + - xpath: + - auto-config-flag + validators: [] + spec: {} + description: Set the Auto Address Configuration Flag + (A-bit) of the prefix in Router Advertisement messages + required: false + variants: [] + description: Configure router advertisement prefix options + required: false + variants: [] + description: Configure ULA (Unique Local Address) + required: false + description: Select Address Source Type + required: false + variants: [] + description: '' + required: false + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: Enable Inherited Interface + required: false + - name: neighbor-discovery + type: object + profiles: + - xpath: + - neighbor-discovery + validators: [] + spec: + params: + - name: dad-attempts + type: int64 + profiles: + - xpath: + - dad-attempts + validators: + - type: length + spec: + min: 1 + max: 10 + spec: + default: 1 + description: Number of consecutive neighbor solicitation messages + sent for duplicate address detection + required: false + - name: dns-server + type: object + profiles: + - xpath: + - dns-server + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: '' + required: false + - name: source + type: object + profiles: + - xpath: + - source + validators: [] + spec: + params: [] + variants: + - name: dhcpv6 + type: object + profiles: + - xpath: + - dhcpv6 + validators: [] + spec: + params: + - name: prefix-pool + type: string + profiles: + - xpath: + - prefix-pool + validators: + - type: length + spec: + max: 63 + spec: {} + description: Prefix-Pool Name + required: false + variants: [] + description: Source from DHCPv6 Server + required: false + - name: manual + type: object + profiles: + - xpath: + - manual + validators: [] + spec: + params: + - name: server + type: list + profiles: + - xpath: + - server + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 4 + max: 3600 + spec: + default: 1200 + description: (4-3600) Lifetime in Seconds + required: false + variants: [] + description: '' + required: false + variants: [] + description: Configure manually + required: false + description: Either source from DHCPv6 Server or manually configure + required: false + variants: [] + description: DNS Recursive Name Server + required: false + - name: dns-suffix + type: object + profiles: + - xpath: + - dns-suffix + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: '' + required: false + - name: source + type: object + profiles: + - xpath: + - source + validators: [] + spec: + params: [] + variants: + - name: dhcpv6 + type: object + profiles: + - xpath: + - dhcpv6 + validators: [] + spec: + params: + - name: prefix-pool + type: string + profiles: + - xpath: + - prefix-pool + validators: + - type: length + spec: + max: 63 + spec: {} + description: Prefix-Pool Name + required: false + variants: [] + description: Source from DHCPv6 Server + required: false + - name: manual + type: object + profiles: + - xpath: + - manual + validators: [] + spec: + params: + - name: suffix + type: list + profiles: + - xpath: + - suffix + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 4 + max: 3600 + spec: + default: 1200 + description: (4-3600) lifetime in seconds + required: false + variants: [] + description: '' + required: false + variants: [] + description: Configure manually + required: false + description: Either source from DHCPv6 Server or manually configure + required: false + variants: [] + description: Domain Search List + required: false + - name: enable-dad + type: bool + profiles: + - xpath: + - enable-dad + validators: [] + spec: {} + description: Enable Duplicate Address Detection (DAD) + required: false + - name: enable-ndp-monitor + type: bool + profiles: + - xpath: + - enable-ndp-monitor + validators: [] + spec: {} + description: Enable NDP Monitoring + required: false + - name: neighbor + type: list + profiles: + - xpath: + - neighbor + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: hw-address + type: string + profiles: + - xpath: + - hw-address + validators: [] + spec: {} + description: MAC address (format xx:xx:xx:xx:xx:xx) + required: false + variants: [] + description: '' + required: false + - name: ns-interval + type: int64 + profiles: + - xpath: + - ns-interval + validators: + - type: length + spec: + min: 1 + max: 3600 + spec: + default: 1 + description: Interval (in seconds) between consecutive neighbor solicitation + messages + required: false + - name: reachable-time + type: int64 + profiles: + - xpath: + - reachable-time + validators: + - type: length + spec: + min: 10 + max: 36000 + spec: + default: 30 + description: Time (in seconds) that the Reachable status for a neighbor + can be maintained + required: false + - name: router-advertisement + type: object + profiles: + - xpath: + - router-advertisement + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: '' + required: false + - name: enable-consistency-check + type: bool + profiles: + - xpath: + - enable-consistency-check + validators: [] + spec: {} + description: check consistency of RA messages from other routers. + required: false + - name: hop-limit + type: string + profiles: + - xpath: + - hop-limit + validators: [] + spec: + default: '64' + description: Current Hop Limit advertised in Router Advertisement + messages + required: false + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 0 + max: 9000 + spec: + default: 1800 + description: Router Lifetime (in seconds) advertised in Router + Advertisement messages + required: false + - name: link-mtu + type: string + profiles: + - xpath: + - link-mtu + validators: [] + spec: + default: unspecified + description: value of MTU option in Router Advertisement messages, + upto 9216 in Jumbo-Frame mode, up to 1500 otherwise + required: false + - name: managed-flag + type: bool + profiles: + - xpath: + - managed-flag + validators: [] + spec: {} + description: Set the Managed Configuration Flag (M-bit) in Router + Advertisement messages + required: false + - name: max-interval + type: int64 + profiles: + - xpath: + - max-interval + validators: + - type: length + spec: + min: 4 + max: 1800 + spec: + default: 600 + description: Maximum interval (seconds) between consecutive unsolicited + Router Advertisement messages + required: false + - name: min-interval + type: int64 + profiles: + - xpath: + - min-interval + validators: + - type: length + spec: + min: 3 + max: 1350 + spec: + default: 200 + description: Minimum interval (seconds) between consecutive unsolicited + Router Advertisement messages + required: false + - name: other-flag + type: bool + profiles: + - xpath: + - other-flag + validators: [] + spec: {} + description: Set the Other Stateful Configuration Flag (O-bit) + in Router Advertisement messages + required: false + - name: reachable-time + type: string + profiles: + - xpath: + - reachable-time + validators: [] + spec: + default: unspecified + description: Reachable Time (in milliseconds) advertised in Router + Advertisement messages + required: false + - name: retransmission-timer + type: string + profiles: + - xpath: + - retransmission-timer + validators: [] + spec: + default: unspecified + description: Retransmission Timer (in milliseconds) advertised + in Router Advertisement messages + required: false + - name: router-preference + type: enum + profiles: + - xpath: + - router-preference + validators: + - type: values + spec: + values: + - High + - Medium + - Low + spec: + default: Medium + values: + - value: High + - value: Medium + - value: Low + description: '' + required: false + variants: [] + description: Router Advertisement configuration + required: false + variants: [] + description: Neighbor Discovery configuration + required: false + variants: [] + description: Configure to inherit properties from another interface + required: false + - name: interface-id + type: string + profiles: + - xpath: + - interface-id + validators: [] + spec: + default: EUI-64 + description: '' + required: false + - name: neighbor-discovery + type: object + profiles: + - xpath: + - neighbor-discovery + validators: [] + spec: + params: + - name: dad-attempts + type: int64 + profiles: + - xpath: + - dad-attempts + validators: + - type: length + spec: + min: 0 + max: 10 + spec: + default: 1 + description: number of consecutive neighbor solicitation messages sent + for duplicate address detection + required: false + - name: enable-dad + type: bool + profiles: + - xpath: + - enable-dad + validators: [] + spec: {} + description: enable duplicate address detection + required: false + - name: enable-ndp-monitor + type: bool + profiles: + - xpath: + - enable-ndp-monitor + validators: [] + spec: {} + description: enable ndp monitoring + required: false + - name: neighbor + type: list + profiles: + - xpath: + - neighbor + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: hw-address + type: string + profiles: + - xpath: + - hw-address + validators: [] + spec: {} + description: MAC address (format xx:xx:xx:xx:xx:xx) + required: false + variants: [] + description: '' + required: false + - name: ns-interval + type: int64 + profiles: + - xpath: + - ns-interval + validators: + - type: length + spec: + min: 1 + max: 3600 + spec: + default: 1 + description: interval (in seconds) between consecutive neighbor solicitation + messages + required: false + - name: reachable-time + type: int64 + profiles: + - xpath: + - reachable-time + validators: + - type: length + spec: + min: 10 + max: 36000 + spec: + default: 30 + description: time (in seconds) that the Reachable status for a neighbor + can be maintained + required: false + - name: router-advertisement + type: object + profiles: + - xpath: + - router-advertisement + validators: [] + spec: + params: + - name: dns-support + type: object + profiles: + - xpath: + - dns-support + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: '' + required: false + - name: server + type: list + profiles: + - xpath: + - server + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 4 + max: 3600 + spec: + default: 1200 + description: (4-3600) lifetime in seconds + required: false + variants: [] + description: '' + required: false + - name: suffix + type: list + profiles: + - xpath: + - suffix + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 4 + max: 3600 + spec: + default: 1200 + description: (4-3600) lifetime in seconds + required: false + variants: [] + description: '' + required: false + variants: [] + description: DNS configuration support + required: false + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: '' + required: false + - name: enable-consistency-check + type: bool + profiles: + - xpath: + - enable-consistency-check + validators: [] + spec: {} + description: check consistency of RA messages from other routers. + required: false + - name: hop-limit + type: string + profiles: + - xpath: + - hop-limit + validators: [] + spec: + default: '64' + description: Current Hop Limit advertised in Router Advertisement + messages + required: false + - name: lifetime + type: int64 + profiles: + - xpath: + - lifetime + validators: + - type: length + spec: + min: 0 + max: 9000 + spec: + default: 1800 + description: Router Lifetime (in seconds) advertised in Router Advertisement + messages + required: false + - name: link-mtu + type: string + profiles: + - xpath: + - link-mtu + validators: [] + spec: + default: unspecified + description: value of MTU option in Router Advertisement messages, + upto 9216 in Jumbo-Frame mode, up to 1500 otherwise + required: false + - name: managed-flag + type: bool + profiles: + - xpath: + - managed-flag + validators: [] + spec: {} + description: Set the Managed Configuration Flag (M-bit) in Router + Advertisement messages + required: false + - name: max-interval + type: int64 + profiles: + - xpath: + - max-interval + validators: + - type: length + spec: + min: 4 + max: 1800 + spec: + default: 600 + description: Maximum interval (seconds) between consecutive unsolicited + Router Advertisement messages + required: false + - name: min-interval + type: int64 + profiles: + - xpath: + - min-interval + validators: + - type: length + spec: + min: 3 + max: 1350 + spec: + default: 200 + description: Minimum interval (seconds) between consecutive unsolicited + Router Advertisement messages + required: false + - name: other-flag + type: bool + profiles: + - xpath: + - other-flag + validators: [] + spec: {} + description: Set the Other Stateful Configuration Flag (O-bit) in + Router Advertisement messages + required: false + - name: reachable-time + type: string + profiles: + - xpath: + - reachable-time + validators: [] + spec: + default: unspecified + description: Reachable Time (in milliseconds) advertised in Router + Advertisement messages + required: false + - name: retransmission-timer + type: string + profiles: + - xpath: + - retransmission-timer + validators: [] + spec: + default: unspecified + description: Retransmission Timer (in milliseconds) advertised in + Router Advertisement messages + required: false + - name: router-preference + type: enum + profiles: + - xpath: + - router-preference + validators: + - type: values + spec: + values: + - High + - Medium + - Low + spec: + default: Medium + values: + - value: High + - value: Medium + - value: Low + description: '' + required: false + variants: [] + description: Router advertisement configuration + required: false + variants: [] + description: Neighbor Discovery configuration + required: false + variants: [] + description: Interface IPv6 configuration + 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: ndp-proxy + type: object + profiles: + - xpath: + - ndp-proxy + validators: [] + spec: + params: + - name: address + type: list + profiles: + - xpath: + - address + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: negate + type: bool + profiles: + - xpath: + - negate + validators: [] + spec: {} + description: put the prefix or address on a block list + required: false + variants: [] + description: '' + required: false + - name: enabled + type: bool + profiles: + - xpath: + - enabled + validators: [] + spec: {} + description: Enable proxy NDP on the interface + required: false + variants: [] + description: proxy-ndp configuration + required: false + - name: netflow-profile + type: string + profiles: + - xpath: + - netflow-profile + validators: + - type: length + spec: + max: 63 + spec: {} + description: Netflow Server Profile + required: false + - name: pppoe + type: object + profiles: + - xpath: + - pppoe + min_version: 11.0.2 + max_version: 11.0.3 + validators: [] + spec: + params: + - name: access-concentrator + type: string + profiles: + - xpath: + - access-concentrator + validators: + - type: length + spec: + min: 1 + max: 255 + spec: {} + description: desired access concentrator. The valid characters are [a-zA-Z0-9._~!@#$%^*(){},:?/+=\-] + required: false + - name: authentication + type: enum + profiles: + - xpath: + - authentication + validators: + - type: values + spec: + values: + - CHAP + - PAP + - auto + spec: + values: + - value: CHAP + - value: PAP + - value: auto + description: authentication protocol + required: false + - name: create-default-route + type: bool + profiles: + - xpath: + - create-default-route + validators: [] + spec: {} + description: automatically create default route pointing to peer + required: false + - name: default-route-metric + type: int64 + profiles: + - xpath: + - default-route-metric + validators: + - type: length + spec: + min: 1 + max: 65535 + spec: + default: 10 + description: metric of the default route created + required: false + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: '' + required: false + - name: passive + type: object + profiles: + - xpath: + - passive + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: '' + required: false + variants: [] + description: devices awaits PPP request from peer + required: false + - name: password + type: string + profiles: + - xpath: + - password + validators: + - type: length + spec: + max: 255 + spec: {} + description: password for ppp autentication + required: false + codegen_overrides: + terraform: + sensitive: true + hashing: + type: solo + - name: service + type: string + profiles: + - xpath: + - service + validators: + - type: length + spec: + min: 1 + max: 255 + spec: {} + description: desired service. The valid characters are [a-zA-Z0-9._~!@#$%^*(){},:?/+=\-] + required: false + - name: static-address + type: object + profiles: + - xpath: + - static-address + validators: [] + spec: + params: + - name: ip + type: string + profiles: + - xpath: + - ip + validators: + - type: length + spec: + max: 63 + spec: {} + description: static ip address + required: false + variants: [] + description: use static interface address + required: false + - name: username + type: string + profiles: + - xpath: + - username + validators: + - type: length + spec: + min: 1 + max: 255 + spec: {} + description: username for ppp authentication. The valid characters are [a-zA-Z0-9._~!@#$%^*(){},:?/+=\-] + required: false + variants: [] + description: pppoe configuration + required: false + - name: sdwan-link-settings + type: object + profiles: + - xpath: + - sdwan-link-settings + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: Enable sdwan on this ethernet interface + required: false + - name: sdwan-interface-profile + type: string + profiles: + - xpath: + - sdwan-interface-profile + validators: + - type: length + spec: + max: 31 + spec: {} + description: Sdwan link characteristics + required: false + - name: upstream-nat + type: object + profiles: + - xpath: + - upstream-nat + validators: [] + spec: + params: + - name: enable + type: bool + profiles: + - xpath: + - enable + validators: [] + spec: {} + description: Enable upstream NAT IP config + required: false + variants: + - name: ddns + type: object + profiles: + - xpath: + - ddns + validators: [] + spec: + params: [] + variants: [] + description: DDNS derives upstream NAT IP + required: false + variant_group_id: 0 + - name: static-ip + type: object + profiles: + - xpath: + - static-ip + validators: [] + spec: + params: [] + variants: + - name: fqdn + type: string + profiles: + - xpath: + - fqdn + validators: + - type: length + spec: + max: 255 + spec: {} + description: Upstream NAT address FQDN name configuration + required: false + variant_group_id: 0 + - name: ip-address + type: string + profiles: + - xpath: + - ip-address + validators: [] + spec: {} + description: Upstream NAT IP address + required: false + variant_group_id: 0 + description: Upstream NAT IP address + required: false + variant_group_id: 0 + description: Upstream NAT IP config + required: false + variants: [] + description: Sdwan related settings + required: false + - name: tag + type: int64 + profiles: + - xpath: + - tag + validators: + - type: length + spec: + min: 1 + max: 4094 + spec: {} + description: 802.1q VLAN tag + required: false + variants: [] diff --git a/specs/network/tunnels/ipsec.yaml b/specs/network/tunnels/ipsec.yaml index b6935d37..0f614943 100644 --- a/specs/network/tunnels/ipsec.yaml +++ b/specs/network/tunnels/ipsec.yaml @@ -16,10 +16,12 @@ go_sdk_config: - network - tunnel - ipsec -xpath_suffix: -- network -- tunnel -- ipsec +panos_xpath: + path: + - network + - tunnel + - ipsec + vars: [] locations: - name: template xpath: @@ -185,6 +187,26 @@ spec: spec: {} description: allow GRE over IPSec required: false + - name: ipsec-mode + type: enum + profiles: + - xpath: + - ipsec-mode + min_version: 11.0.2 + max_version: 11.0.3 + validators: + - type: values + spec: + values: + - tunnel + - transport + spec: + default: tunnel + values: + - value: tunnel + - value: transport + description: '' + required: false - name: ipv6 type: bool profiles: @@ -250,26 +272,6 @@ spec: variants: [] description: Monitor tunnel status required: false - - name: ipsec-mode - type: enum - profiles: - - xpath: - - ipsec-mode - min_version: 11.0.2 - max_version: 11.0.3 - validators: - - type: values - spec: - values: - - tunnel - - transport - spec: - default: tunnel - values: - - value: tunnel - - value: transport - description: '' - required: false variants: - name: auto-key type: object @@ -605,6 +607,7 @@ spec: variants: [] description: IKE VPN options required: false + variant_group_id: 0 - name: global-protect-satellite type: object profiles: @@ -702,6 +705,7 @@ spec: variants: [] description: Floating IP address in HA Active-Active configuration required: false + variant_group_id: 0 - name: ip type: object profiles: @@ -732,6 +736,7 @@ spec: variants: [] description: specify exact IP address if interface has multiple addresses required: false + variant_group_id: 0 description: Satellite outgoing interface configuration required: false - name: portal-address @@ -782,6 +787,7 @@ spec: variants: [] description: Satellite side of Global Protect Satellite tunnel required: false + variant_group_id: 0 - name: manual-key type: object profiles: @@ -817,6 +823,7 @@ spec: spec: {} description: Floating IP address in HA Active-Active configuration required: false + variant_group_id: 0 - name: ip type: string profiles: @@ -829,6 +836,7 @@ spec: spec: {} description: specify exact IP address if interface has multiple addresses required: false + variant_group_id: 0 description: Tunnel local IP configuration required: false - name: local-spi @@ -902,6 +910,7 @@ spec: variants: [] description: key is 128 bit required: false + variant_group_id: 0 - name: sha1 type: object profiles: @@ -925,6 +934,7 @@ spec: variants: [] description: key is 160 bit required: false + variant_group_id: 0 - name: sha256 type: object profiles: @@ -948,6 +958,7 @@ spec: variants: [] description: key is 256 bit required: false + variant_group_id: 0 - name: sha384 type: object profiles: @@ -971,6 +982,7 @@ spec: variants: [] description: key is 384 bit required: false + variant_group_id: 0 - name: sha512 type: object profiles: @@ -994,8 +1006,10 @@ spec: variants: [] description: key is 512 bit required: false + variant_group_id: 0 description: AH options required: false + variant_group_id: 0 - name: esp type: object profiles: @@ -1036,6 +1050,7 @@ spec: variants: [] description: key is 128 bit required: false + variant_group_id: 0 - name: none type: object profiles: @@ -1047,6 +1062,7 @@ spec: variants: [] description: no authentication required: false + variant_group_id: 0 - name: sha1 type: object profiles: @@ -1070,6 +1086,7 @@ spec: variants: [] description: key is 160 bit required: false + variant_group_id: 0 - name: sha256 type: object profiles: @@ -1093,6 +1110,7 @@ spec: variants: [] description: key is 256 bit required: false + variant_group_id: 0 - name: sha384 type: object profiles: @@ -1116,6 +1134,7 @@ spec: variants: [] description: key is 384 bit required: false + variant_group_id: 0 - name: sha512 type: object profiles: @@ -1139,6 +1158,7 @@ spec: variants: [] description: key is 512 bit required: false + variant_group_id: 0 description: authentication algorithm required: false - name: encryption @@ -1194,5 +1214,7 @@ spec: variants: [] description: ESP options required: false + variant_group_id: 0 description: Manual-key options required: false + variant_group_id: 0 diff --git a/specs/network/virtual-router.yaml b/specs/network/virtual-router.yaml index b07a567b..a55ae18b 100644 --- a/specs/network/virtual-router.yaml +++ b/specs/network/virtual-router.yaml @@ -15,9 +15,11 @@ go_sdk_config: package: - network - virtual_router -xpath_suffix: -- network -- virtual-router +panos_xpath: + path: + - network + - virtual-router + vars: [] locations: - name: ngfw xpath: @@ -283,6 +285,7 @@ spec: variants: [] description: Re-balance load on path add/delete required: false + variant_group_id: 0 - name: ip-hash type: object profiles: @@ -326,6 +329,7 @@ spec: variants: [] description: Hash based on IP addresses and optionally port numbers required: false + variant_group_id: 0 - name: ip-modulo type: object profiles: @@ -337,6 +341,7 @@ spec: variants: [] description: IP addresses modulo number of path required: false + variant_group_id: 0 - name: weighted-round-robin type: object profiles: @@ -379,6 +384,7 @@ spec: variants: [] description: Weighted round robin required: false + variant_group_id: 0 description: load balancing algorithm required: false - name: enable @@ -994,6 +1000,7 @@ spec: variants: [] description: Configure device to act as candidate RP required: false + variant_group_id: 0 - name: static-rp type: object profiles: @@ -1051,6 +1058,7 @@ spec: variants: [] description: Configure device to act as a static RP required: false + variant_group_id: 0 description: Local Rendezvous Point configuration required: false variants: [] @@ -1730,6 +1738,7 @@ spec: spec: {} description: peer address configuration required: false + variant_group_id: 0 - name: fqdn type: string profiles: @@ -1742,6 +1751,7 @@ spec: spec: {} description: bgp peer FQDN address object configuration required: false + variant_group_id: 0 description: peer address configuration required: false - name: connection-options @@ -2114,6 +2124,7 @@ spec: variants: [] description: no change on AS path required: false + variant_group_id: 0 - name: prepend type: int64 profiles: @@ -2129,6 +2140,7 @@ spec: description: prepend local AS for specified number of times required: false + variant_group_id: 0 description: AS path update options required: false - name: community @@ -2151,6 +2163,7 @@ spec: variants: [] description: no change on communities required: false + variant_group_id: 0 - name: remove-all type: object profiles: @@ -2162,6 +2175,7 @@ spec: variants: [] description: remove all communities required: false + variant_group_id: 0 - name: remove-regex type: string profiles: @@ -2175,6 +2189,7 @@ spec: description: remove specified coummnity match regular expression required: false + variant_group_id: 0 - name: append type: list profiles: @@ -2188,6 +2203,7 @@ spec: type: string description: '' required: false + variant_group_id: 0 - name: overwrite type: list profiles: @@ -2201,6 +2217,7 @@ spec: type: string description: '' required: false + variant_group_id: 0 description: community update options required: false - name: extended-community @@ -2223,6 +2240,7 @@ spec: variants: [] description: no change on communities required: false + variant_group_id: 0 - name: remove-all type: object profiles: @@ -2234,6 +2252,7 @@ spec: variants: [] description: remove all communities required: false + variant_group_id: 0 - name: remove-regex type: string profiles: @@ -2247,6 +2266,7 @@ spec: description: remove specified coummnity match regular expression required: false + variant_group_id: 0 - name: append type: list profiles: @@ -2260,6 +2280,7 @@ spec: type: string description: '' required: false + variant_group_id: 0 - name: overwrite type: list profiles: @@ -2273,6 +2294,7 @@ spec: type: string description: '' required: false + variant_group_id: 0 description: extended community update options required: false variants: [] @@ -2415,6 +2437,7 @@ spec: spec: {} description: AS-path regular expression required: false + variant_group_id: 0 description: AS-path to match required: false - name: community @@ -2438,6 +2461,7 @@ spec: spec: {} description: AS-path regular expression required: false + variant_group_id: 0 description: community to match required: false - name: extended-community @@ -2461,6 +2485,7 @@ spec: spec: {} description: AS-path regular expression required: false + variant_group_id: 0 description: community to match required: false variants: [] @@ -2606,6 +2631,7 @@ spec: spec: {} description: AS-path regular expression required: false + variant_group_id: 0 description: AS-path to match required: false - name: community @@ -2629,6 +2655,7 @@ spec: spec: {} description: AS-path regular expression required: false + variant_group_id: 0 description: community to match required: false - name: extended-community @@ -2652,6 +2679,7 @@ spec: spec: {} description: AS-path regular expression required: false + variant_group_id: 0 description: community to match required: false variants: [] @@ -2838,6 +2866,7 @@ spec: spec: {} description: AS-path regular expression required: false + variant_group_id: 0 description: AS-path to match required: false - name: community @@ -2861,6 +2890,7 @@ spec: spec: {} description: AS-path regular expression required: false + variant_group_id: 0 description: community to match required: false - name: extended-community @@ -2884,6 +2914,7 @@ spec: spec: {} description: AS-path regular expression required: false + variant_group_id: 0 description: community to match required: false variants: [] @@ -3020,6 +3051,7 @@ spec: spec: {} description: AS-path regular expression required: false + variant_group_id: 0 description: AS-path to match required: false - name: community @@ -3043,6 +3075,7 @@ spec: spec: {} description: AS-path regular expression required: false + variant_group_id: 0 description: community to match required: false - name: extended-community @@ -3066,6 +3099,7 @@ spec: spec: {} description: AS-path regular expression required: false + variant_group_id: 0 description: community to match required: false variants: [] @@ -3238,6 +3272,7 @@ spec: spec: {} description: AS-path regular expression required: false + variant_group_id: 0 description: AS-path to match required: false - name: community @@ -3261,6 +3296,7 @@ spec: spec: {} description: AS-path regular expression required: false + variant_group_id: 0 description: community to match required: false - name: extended-community @@ -3284,6 +3320,7 @@ spec: spec: {} description: AS-path regular expression required: false + variant_group_id: 0 description: community to match required: false variants: [] @@ -3309,6 +3346,7 @@ spec: variants: [] description: '' required: false + variant_group_id: 0 - name: allow type: object profiles: @@ -3413,6 +3451,7 @@ spec: variants: [] description: no change on AS path required: false + variant_group_id: 0 - name: remove type: object profiles: @@ -3424,6 +3463,7 @@ spec: variants: [] description: remove matched AS path(s) required: false + variant_group_id: 0 - name: prepend type: int64 profiles: @@ -3439,6 +3479,7 @@ spec: description: prepend local AS for specified number of times required: false + variant_group_id: 0 - name: remove-and-prepend type: int64 profiles: @@ -3455,6 +3496,7 @@ spec: and prepend local AS for specified number of times required: false + variant_group_id: 0 description: AS path update options required: false - name: community @@ -3477,6 +3519,7 @@ spec: variants: [] description: no change on communities required: false + variant_group_id: 0 - name: remove-all type: object profiles: @@ -3488,6 +3531,7 @@ spec: variants: [] description: remove all communities required: false + variant_group_id: 0 - name: remove-regex type: string profiles: @@ -3501,6 +3545,7 @@ spec: description: remove specified coummnity match regular expression required: false + variant_group_id: 0 - name: append type: list profiles: @@ -3514,6 +3559,7 @@ spec: type: string description: '' required: false + variant_group_id: 0 - name: overwrite type: list profiles: @@ -3527,6 +3573,7 @@ spec: type: string description: '' required: false + variant_group_id: 0 description: community update options required: false - name: extended-community @@ -3549,6 +3596,7 @@ spec: variants: [] description: no change on communities required: false + variant_group_id: 0 - name: remove-all type: object profiles: @@ -3560,6 +3608,7 @@ spec: variants: [] description: remove all communities required: false + variant_group_id: 0 - name: remove-regex type: string profiles: @@ -3573,6 +3622,7 @@ spec: description: remove specified coummnity match regular expression required: false + variant_group_id: 0 - name: append type: list profiles: @@ -3586,6 +3636,7 @@ spec: type: string description: '' required: false + variant_group_id: 0 - name: overwrite type: list profiles: @@ -3599,6 +3650,7 @@ spec: type: string description: '' required: false + variant_group_id: 0 description: extended community update options required: false variants: [] @@ -3607,6 +3659,7 @@ spec: variants: [] description: '' required: false + variant_group_id: 0 description: rule action required: false variants: [] @@ -3773,6 +3826,7 @@ spec: spec: {} description: AS-path regular expression required: false + variant_group_id: 0 description: AS-path to match required: false - name: community @@ -3796,6 +3850,7 @@ spec: spec: {} description: AS-path regular expression required: false + variant_group_id: 0 description: community to match required: false - name: extended-community @@ -3819,6 +3874,7 @@ spec: spec: {} description: AS-path regular expression required: false + variant_group_id: 0 description: community to match required: false variants: [] @@ -3844,6 +3900,7 @@ spec: variants: [] description: '' required: false + variant_group_id: 0 - name: allow type: object profiles: @@ -3970,6 +4027,7 @@ spec: variants: [] description: no change on AS path required: false + variant_group_id: 0 - name: remove type: object profiles: @@ -3981,6 +4039,7 @@ spec: variants: [] description: remove matched AS path(s) required: false + variant_group_id: 0 description: AS path update options required: false - name: community @@ -4003,6 +4062,7 @@ spec: variants: [] description: no change on communities required: false + variant_group_id: 0 - name: remove-all type: object profiles: @@ -4014,6 +4074,7 @@ spec: variants: [] description: remove all communities required: false + variant_group_id: 0 - name: remove-regex type: string profiles: @@ -4027,6 +4088,7 @@ spec: description: remove specified coummnity match regular expression required: false + variant_group_id: 0 - name: append type: list profiles: @@ -4040,6 +4102,7 @@ spec: type: string description: '' required: false + variant_group_id: 0 - name: overwrite type: list profiles: @@ -4053,6 +4116,7 @@ spec: type: string description: '' required: false + variant_group_id: 0 description: community update options required: false - name: extended-community @@ -4075,6 +4139,7 @@ spec: variants: [] description: no change on communities required: false + variant_group_id: 0 - name: remove-all type: object profiles: @@ -4086,6 +4151,7 @@ spec: variants: [] description: remove all communities required: false + variant_group_id: 0 - name: remove-regex type: string profiles: @@ -4099,6 +4165,7 @@ spec: description: remove specified coummnity match regular expression required: false + variant_group_id: 0 - name: append type: list profiles: @@ -4112,6 +4179,7 @@ spec: type: string description: '' required: false + variant_group_id: 0 - name: overwrite type: list profiles: @@ -4125,6 +4193,7 @@ spec: type: string description: '' required: false + variant_group_id: 0 description: extended community update options required: false variants: [] @@ -4133,6 +4202,7 @@ spec: variants: [] description: '' required: false + variant_group_id: 0 description: rule action required: false variants: [] @@ -4732,6 +4802,7 @@ spec: variants: [] description: do summarization and advertise required: false + variant_group_id: 0 - name: suppress type: object profiles: @@ -4744,6 +4815,7 @@ spec: description: suppress summarization to be sent, make this subnet hidden from other areas required: false + variant_group_id: 0 description: '' required: false variants: [] @@ -4777,6 +4849,7 @@ spec: variants: [] description: do summarization and advertise required: false + variant_group_id: 0 - name: suppress type: object profiles: @@ -4789,6 +4862,7 @@ spec: description: suppress summarization to be sent, make this subnet hidden from other areas required: false + variant_group_id: 0 description: '' required: false - name: interface @@ -5182,6 +5256,7 @@ spec: spec: {} description: Simple password authentication required: false + variant_group_id: 0 - name: md5 type: list profiles: @@ -5220,6 +5295,7 @@ spec: variants: [] description: '' required: false + variant_group_id: 0 description: '' required: false - name: enable @@ -5691,6 +5767,7 @@ spec: variants: [] description: do summarization and advertise required: false + variant_group_id: 0 - name: suppress type: object profiles: @@ -5703,6 +5780,7 @@ spec: description: suppress summarization to be sent, make this subnet hidden from other areas required: false + variant_group_id: 0 description: '' required: false variants: [] @@ -5736,6 +5814,7 @@ spec: variants: [] description: do summarization and advertise required: false + variant_group_id: 0 - name: suppress type: object profiles: @@ -5748,6 +5827,7 @@ spec: description: suppress summarization to be sent, make this subnet hidden from other areas required: false + variant_group_id: 0 description: '' required: false - name: interface @@ -6208,6 +6288,7 @@ spec: variants: [] description: key is 128 bit required: false + variant_group_id: 0 - name: sha1 type: object profiles: @@ -6232,6 +6313,7 @@ spec: variants: [] description: key is 160 bit required: false + variant_group_id: 0 - name: sha256 type: object profiles: @@ -6256,6 +6338,7 @@ spec: variants: [] description: key is 256 bit required: false + variant_group_id: 0 - name: sha384 type: object profiles: @@ -6280,6 +6363,7 @@ spec: variants: [] description: key is 384 bit required: false + variant_group_id: 0 - name: sha512 type: object profiles: @@ -6304,6 +6388,7 @@ spec: variants: [] description: key is 512 bit required: false + variant_group_id: 0 - name: none type: object profiles: @@ -6315,6 +6400,7 @@ spec: variants: [] description: no authentication required: false + variant_group_id: 0 description: authentication algorithm required: false - name: encryption @@ -6368,6 +6454,7 @@ spec: variants: [] description: ESP options required: false + variant_group_id: 0 - name: ah type: object profiles: @@ -6401,6 +6488,7 @@ spec: variants: [] description: key is 128 bit required: false + variant_group_id: 0 - name: sha1 type: object profiles: @@ -6425,6 +6513,7 @@ spec: variants: [] description: key is 160 bit required: false + variant_group_id: 0 - name: sha256 type: object profiles: @@ -6449,6 +6538,7 @@ spec: variants: [] description: key is 256 bit required: false + variant_group_id: 0 - name: sha384 type: object profiles: @@ -6473,6 +6563,7 @@ spec: variants: [] description: key is 384 bit required: false + variant_group_id: 0 - name: sha512 type: object profiles: @@ -6497,8 +6588,10 @@ spec: variants: [] description: key is 512 bit required: false + variant_group_id: 0 description: AH options required: false + variant_group_id: 0 description: '' required: false - name: disable-transit-traffic @@ -7222,6 +7315,7 @@ spec: spec: {} description: Simple password authentication required: false + variant_group_id: 0 - name: md5 type: list profiles: @@ -7260,6 +7354,7 @@ spec: variants: [] description: '' required: false + variant_group_id: 0 description: '' required: false - name: enable @@ -7696,6 +7791,7 @@ spec: variants: [] description: Install route into unicast routing table required: false + variant_group_id: 0 - name: multicast type: object profiles: @@ -7708,6 +7804,7 @@ spec: description: Install route into multicast routing table, this will create multicast routing table if not exists required: false + variant_group_id: 0 - name: both type: object profiles: @@ -7720,6 +7817,7 @@ spec: description: Install route into both unicast and multicast routing table required: false + variant_group_id: 0 - name: no-install type: object profiles: @@ -7731,6 +7829,7 @@ spec: variants: [] description: Do not install route into forwarding table required: false + variant_group_id: 0 description: target routing table to install the route required: false - name: bfd @@ -8028,6 +8127,7 @@ spec: variants: [] description: Install route into unicast routing table required: false + variant_group_id: 0 - name: no-install type: object profiles: @@ -8039,6 +8139,7 @@ spec: variants: [] description: Do not install route into forwarding table required: false + variant_group_id: 0 description: target routing table to install the route required: false - name: bfd diff --git a/specs/network/zone.yaml b/specs/network/zone.yaml index 17130f8d..c8c85b9b 100644 --- a/specs/network/zone.yaml +++ b/specs/network/zone.yaml @@ -15,8 +15,10 @@ go_sdk_config: package: - network - zone -xpath_suffix: -- zone +panos_xpath: + path: + - zone + vars: [] locations: - name: vsys xpath: @@ -47,7 +49,6 @@ locations: description: Located in a specific Virtual System devices: - ngfw - - panorama validators: [] required: false read_only: false @@ -225,15 +226,6 @@ spec: spec: {} description: Log setting for forwarding scan logs required: false - - name: zone-protection-profile - type: string - profiles: - - xpath: - - zone-protection-profile - validators: [] - spec: {} - description: Zone protection profile - required: false - name: net-inspection type: bool profiles: @@ -245,6 +237,15 @@ spec: spec: {} description: '' required: false + - name: zone-protection-profile + type: string + profiles: + - xpath: + - zone-protection-profile + validators: [] + spec: {} + description: Zone protection profile + required: false variants: - name: external type: list @@ -259,6 +260,7 @@ spec: type: string description: '' required: false + variant_group_id: 0 - name: layer2 type: list profiles: @@ -272,6 +274,7 @@ spec: type: string description: '' required: false + variant_group_id: 0 - name: layer3 type: list profiles: @@ -285,6 +288,7 @@ spec: type: string description: '' required: false + variant_group_id: 0 - name: tap type: list profiles: @@ -298,6 +302,7 @@ spec: type: string description: '' required: false + variant_group_id: 0 - name: tunnel type: object profiles: @@ -309,6 +314,7 @@ spec: variants: [] description: Tunnel inspection zone required: false + variant_group_id: 0 - name: virtual-wire type: list profiles: @@ -322,6 +328,7 @@ spec: type: string description: '' required: false + variant_group_id: 0 description: Zone network configuration required: false - name: user-acl diff --git a/specs/objects/address-group.yaml b/specs/objects/address-group.yaml index 66a1d034..9d69e4e8 100644 --- a/specs/objects/address-group.yaml +++ b/specs/objects/address-group.yaml @@ -16,8 +16,10 @@ go_sdk_config: - objects - address - group -xpath_suffix: -- address-group +panos_xpath: + path: + - address-group + vars: [] locations: - name: shared xpath: @@ -61,7 +63,6 @@ locations: description: Located in a specific Virtual System devices: - ngfw - - panorama validators: [] required: false read_only: false @@ -169,6 +170,7 @@ spec: variants: [] description: '' required: false + variant_group_id: 0 - name: static type: list profiles: @@ -182,3 +184,4 @@ spec: type: string description: '' required: false + variant_group_id: 0 diff --git a/specs/objects/address.yaml b/specs/objects/address.yaml index 12eff43c..91c29571 100644 --- a/specs/objects/address.yaml +++ b/specs/objects/address.yaml @@ -16,8 +16,10 @@ go_sdk_config: package: - objects - address -xpath_suffix: -- address +panos_xpath: + path: + - address + vars: [] locations: - name: shared xpath: @@ -61,7 +63,6 @@ locations: description: Located in a specific Virtual System devices: - ngfw - - panorama validators: [] required: false read_only: false @@ -164,6 +165,7 @@ spec: spec: {} description: The FQDN value. required: false + variant_group_id: 0 - name: ip-netmask type: string profiles: @@ -173,6 +175,7 @@ spec: spec: {} description: The IP netmask value. required: false + variant_group_id: 0 - name: ip-range type: string profiles: @@ -182,6 +185,7 @@ spec: spec: {} description: The IP range value. required: false + variant_group_id: 0 - name: ip-wildcard type: string profiles: @@ -191,3 +195,4 @@ spec: spec: {} description: The IP wildcard value. required: false + variant_group_id: 0 diff --git a/specs/objects/administrative-tag.yaml b/specs/objects/administrative-tag.yaml index 4f3245dd..7231dc72 100644 --- a/specs/objects/administrative-tag.yaml +++ b/specs/objects/administrative-tag.yaml @@ -15,8 +15,10 @@ go_sdk_config: package: - objects - admintag -xpath_suffix: -- tag +panos_xpath: + path: + - tag + vars: [] locations: - name: shared xpath: @@ -60,7 +62,6 @@ locations: description: Located in a specific Virtual System devices: - ngfw - - panorama validators: [] required: false read_only: false diff --git a/specs/objects/application-group.yaml b/specs/objects/application-group.yaml index 0786e942..4a37721d 100644 --- a/specs/objects/application-group.yaml +++ b/specs/objects/application-group.yaml @@ -15,8 +15,10 @@ go_sdk_config: - objects - application - group -xpath_suffix: -- application-group +panos_xpath: + path: + - application-group + vars: [] locations: - name: shared xpath: @@ -60,7 +62,6 @@ locations: description: Located in a specific Virtual System devices: - ngfw - - panorama validators: [] required: false read_only: false diff --git a/specs/objects/application.yaml b/specs/objects/application.yaml index cb2b752a..78cc782f 100644 --- a/specs/objects/application.yaml +++ b/specs/objects/application.yaml @@ -14,8 +14,10 @@ go_sdk_config: package: - objects - application -xpath_suffix: -- application +panos_xpath: + path: + - application + vars: [] locations: - name: shared xpath: @@ -59,7 +61,6 @@ locations: description: Located in a specific Virtual System devices: - ngfw - - panorama validators: [] required: false read_only: false diff --git a/specs/objects/custom-url-category.yaml b/specs/objects/custom-url-category.yaml index f1f2a200..e3df0bce 100644 --- a/specs/objects/custom-url-category.yaml +++ b/specs/objects/custom-url-category.yaml @@ -16,9 +16,11 @@ go_sdk_config: - objects - profiles - customurlcategory -xpath_suffix: -- profiles -- custom-url-category +panos_xpath: + path: + - profiles + - custom-url-category + vars: [] locations: - name: shared xpath: @@ -62,7 +64,6 @@ locations: description: Located in a specific Virtual System devices: - ngfw - - panorama validators: [] required: false read_only: false diff --git a/specs/objects/ephemeral-vm-auth-key.yaml b/specs/objects/ephemeral-vm-auth-key.yaml index feddd542..3a682ba3 100644 --- a/specs/objects/ephemeral-vm-auth-key.yaml +++ b/specs/objects/ephemeral-vm-auth-key.yaml @@ -10,7 +10,8 @@ terraform_provider_config: go_sdk_config: skip: true package: ["vmauthkey"] # unused -xpath_suffix: ["vmauthkey"] # unused +panos_xpath: + path: ["vmauthkey"] # unused locations: [] entries: [] imports: [] diff --git a/specs/objects/external-dynamic-list.yaml b/specs/objects/external-dynamic-list.yaml index 0550127a..b7ee926f 100644 --- a/specs/objects/external-dynamic-list.yaml +++ b/specs/objects/external-dynamic-list.yaml @@ -15,8 +15,10 @@ go_sdk_config: package: - objects - extdynlist -xpath_suffix: -- external-list +panos_xpath: + path: + - external-list + vars: [] locations: - name: shared xpath: @@ -221,6 +223,7 @@ spec: variants: [] description: Everyday required: false + variant_group_id: 0 - name: five-minute type: object profiles: @@ -232,6 +235,7 @@ spec: variants: [] description: Every five minutes required: false + variant_group_id: 0 - name: hourly type: object profiles: @@ -243,6 +247,7 @@ spec: variants: [] description: Everyhour required: false + variant_group_id: 0 - name: monthly type: object profiles: @@ -281,6 +286,7 @@ spec: variants: [] description: Monthly required: false + variant_group_id: 0 - name: weekly type: object profiles: @@ -333,6 +339,7 @@ spec: variants: [] description: Once a week required: false + variant_group_id: 0 description: '' required: false - name: url @@ -468,6 +475,7 @@ spec: variants: [] description: Everyday required: false + variant_group_id: 0 - name: five-minute type: object profiles: @@ -479,6 +487,7 @@ spec: variants: [] description: Every five minutes required: false + variant_group_id: 0 - name: hourly type: object profiles: @@ -490,6 +499,7 @@ spec: variants: [] description: Everyhour required: false + variant_group_id: 0 - name: monthly type: object profiles: @@ -528,6 +538,7 @@ spec: variants: [] description: Monthly required: false + variant_group_id: 0 - name: weekly type: object profiles: @@ -580,6 +591,7 @@ spec: variants: [] description: Once a week required: false + variant_group_id: 0 description: '' required: false - name: url @@ -715,6 +727,7 @@ spec: variants: [] description: Everyday required: false + variant_group_id: 0 - name: five-minute type: object profiles: @@ -726,6 +739,7 @@ spec: variants: [] description: Every five minutes required: false + variant_group_id: 0 - name: hourly type: object profiles: @@ -737,6 +751,7 @@ spec: variants: [] description: Everyhour required: false + variant_group_id: 0 - name: monthly type: object profiles: @@ -775,6 +790,7 @@ spec: variants: [] description: Monthly required: false + variant_group_id: 0 - name: weekly type: object profiles: @@ -827,6 +843,7 @@ spec: variants: [] description: Once a week required: false + variant_group_id: 0 description: '' required: false - name: url @@ -962,6 +979,7 @@ spec: variants: [] description: Everyday required: false + variant_group_id: 0 - name: five-minute type: object profiles: @@ -973,6 +991,7 @@ spec: variants: [] description: Every five minutes required: false + variant_group_id: 0 - name: hourly type: object profiles: @@ -984,6 +1003,7 @@ spec: variants: [] description: Everyhour required: false + variant_group_id: 0 - name: monthly type: object profiles: @@ -1022,6 +1042,7 @@ spec: variants: [] description: Monthly required: false + variant_group_id: 0 - name: weekly type: object profiles: @@ -1074,6 +1095,7 @@ spec: variants: [] description: Once a week required: false + variant_group_id: 0 description: '' required: false - name: url @@ -1301,6 +1323,7 @@ spec: variants: [] description: Everyday required: false + variant_group_id: 0 - name: five-minute type: object profiles: @@ -1312,6 +1335,7 @@ spec: variants: [] description: Every five minutes required: false + variant_group_id: 0 - name: hourly type: object profiles: @@ -1323,6 +1347,7 @@ spec: variants: [] description: Everyhour required: false + variant_group_id: 0 - name: monthly type: object profiles: @@ -1361,6 +1386,7 @@ spec: variants: [] description: Monthly required: false + variant_group_id: 0 - name: weekly type: object profiles: @@ -1413,6 +1439,7 @@ spec: variants: [] description: Once a week required: false + variant_group_id: 0 description: '' required: false - name: url diff --git a/specs/objects/profiles/antivirus.yaml b/specs/objects/profiles/antivirus.yaml index 4ff772bb..6a144c9e 100644 --- a/specs/objects/profiles/antivirus.yaml +++ b/specs/objects/profiles/antivirus.yaml @@ -16,9 +16,11 @@ go_sdk_config: - objects - profiles - antivirus -xpath_suffix: -- profiles -- virus +panos_xpath: + path: + - profiles + - virus + vars: [] locations: - name: shared xpath: diff --git a/specs/objects/profiles/certificate.yaml b/specs/objects/profiles/certificate.yaml index 2577f9f2..98c7a3de 100644 --- a/specs/objects/profiles/certificate.yaml +++ b/specs/objects/profiles/certificate.yaml @@ -15,7 +15,10 @@ go_sdk_config: - device - profile - certificate -xpath_suffix: [] +panos_xpath: + path: + - certificate + vars: [] locations: - name: panorama xpath: @@ -204,6 +207,44 @@ entries: imports: [] spec: params: + - name: block-expired-certificate + type: bool + profiles: + - xpath: + - block-expired-cert + validators: [] + spec: {} + description: Whether to block a session if certificate status is expired. + required: false + - name: block-timeout-certificate + type: bool + profiles: + - xpath: + - block-timeout-cert + validators: [] + spec: {} + description: Whether to block a session if cert. status can't be retrieved within + timeout. + required: false + - name: block-unauthenticated-certificate + type: bool + profiles: + - xpath: + - block-unauthenticated-cert + validators: [] + spec: {} + description: Whether to block session if the certificate was not issued to the + authenticating device. + required: false + - name: block-unknown-certificate + type: bool + profiles: + - xpath: + - block-unknown-cert + validators: [] + spec: {} + description: Whether to block a session if cert. status is unknown. + required: false - name: certificate type: list profiles: @@ -259,44 +300,6 @@ spec: variants: [] description: CA Certificate to assign to the profile. required: false - - name: block-expired-certificate - type: bool - profiles: - - xpath: - - block-expired-cert - validators: [] - spec: {} - description: Whether to block a session if certificate status is expired. - required: false - - name: block-timeout-certificate - type: bool - profiles: - - xpath: - - block-timeout-cert - validators: [] - spec: {} - description: Whether to block a session if cert. status can't be retrieved within - timeout. - required: false - - name: block-unauthenticated-certificate - type: bool - profiles: - - xpath: - - block-unauthenticated-cert - validators: [] - spec: {} - description: Whether to block session if the certificate was not issued to the - authenticating device. - required: false - - name: block-unknown-certificate - type: bool - profiles: - - xpath: - - block-unknown-cert - validators: [] - spec: {} - description: Whether to block a session if cert. status is unknown. - required: false - name: certificate-status-timeout type: int64 profiles: diff --git a/specs/objects/profiles/file-blocking.yaml b/specs/objects/profiles/file-blocking.yaml index 1fe3ac1a..0332930d 100644 --- a/specs/objects/profiles/file-blocking.yaml +++ b/specs/objects/profiles/file-blocking.yaml @@ -15,9 +15,11 @@ go_sdk_config: - objects - profiles - fileblocking -xpath_suffix: -- profiles -- file-blocking +panos_xpath: + path: + - profiles + - file-blocking + vars: [] locations: - name: shared xpath: @@ -93,7 +95,6 @@ locations: description: Located in a specific Virtual System devices: - ngfw - - panorama validators: [] required: false read_only: false diff --git a/specs/objects/profiles/ike-crypto-profile.yaml b/specs/objects/profiles/ike-crypto-profile.yaml index a6c97733..db0ff675 100644 --- a/specs/objects/profiles/ike-crypto-profile.yaml +++ b/specs/objects/profiles/ike-crypto-profile.yaml @@ -15,11 +15,13 @@ go_sdk_config: - objects - profiles - ikecrypto -xpath_suffix: -- network -- ike -- crypto-profiles -- ike-crypto-profiles +panos_xpath: + path: + - network + - ike + - crypto-profiles + - ike-crypto-profiles + vars: [] locations: - name: ngfw xpath: diff --git a/specs/objects/profiles/ipsec-crypto-profile.yaml b/specs/objects/profiles/ipsec-crypto-profile.yaml index bd9f5cc1..652faa4b 100644 --- a/specs/objects/profiles/ipsec-crypto-profile.yaml +++ b/specs/objects/profiles/ipsec-crypto-profile.yaml @@ -15,11 +15,13 @@ go_sdk_config: - objects - profiles - ipseccrypto -xpath_suffix: -- network -- ike -- crypto-profiles -- ipsec-crypto-profiles +panos_xpath: + path: + - network + - ike + - crypto-profiles + - ipsec-crypto-profiles + vars: [] locations: - name: ngfw xpath: @@ -317,6 +319,7 @@ spec: variants: [] description: AH only required: false + variant_group_id: 0 - name: esp type: object profiles: @@ -375,3 +378,4 @@ spec: variants: [] description: ESP options required: false + variant_group_id: 0 diff --git a/specs/objects/profiles/log-forwarding.yaml b/specs/objects/profiles/log-forwarding.yaml index 81f44f38..acbd2414 100644 --- a/specs/objects/profiles/log-forwarding.yaml +++ b/specs/objects/profiles/log-forwarding.yaml @@ -15,9 +15,11 @@ go_sdk_config: - objects - profiles - logforwarding -xpath_suffix: -- log-settings -- profiles +panos_xpath: + path: + - log-settings + - profiles + vars: [] locations: - name: shared xpath: diff --git a/specs/objects/profiles/security-profile-group.yaml b/specs/objects/profiles/security-profile-group.yaml index cb824336..137ea183 100644 --- a/specs/objects/profiles/security-profile-group.yaml +++ b/specs/objects/profiles/security-profile-group.yaml @@ -15,8 +15,10 @@ go_sdk_config: - objects - profiles - secgroup -xpath_suffix: -- profile-group +panos_xpath: + path: + - profile-group + vars: [] locations: - name: shared xpath: diff --git a/specs/objects/profiles/ssl-tls-service.yaml b/specs/objects/profiles/ssl-tls-service.yaml index 3ed7bbc7..c6f892a6 100644 --- a/specs/objects/profiles/ssl-tls-service.yaml +++ b/specs/objects/profiles/ssl-tls-service.yaml @@ -16,8 +16,10 @@ go_sdk_config: - device - profile - ssltls -xpath_suffix: -- ssl-tls-service-profile +panos_xpath: + path: + - ssl-tls-service-profile + vars: [] locations: - name: shared xpath: @@ -51,7 +53,8 @@ locations: - template - $template - config - - shared + - devices + - $ngfw_device vars: - name: panorama_device description: Specific Panorama device @@ -64,13 +67,19 @@ locations: 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_vsys +- name: template-vsys xpath: path: - config @@ -128,7 +137,8 @@ locations: - template-stack - $template_stack - config - - shared + - devices + - $ngfw_device vars: - name: panorama_device description: Specific Panorama device @@ -137,11 +147,17 @@ locations: validators: [] type: entry - name: template_stack - description: The template stack + description: Specific Panorama template stack required: true validators: [] type: entry - description: Located in a specific template + - 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: [] @@ -226,33 +242,6 @@ spec: validators: [] spec: params: - - name: allow-authentication-sha1 - type: bool - profiles: - - xpath: - - auth-algo-sha1 - validators: [] - spec: {} - description: Allow authentication SHA1 - required: false - - name: allow-authentication-sha256 - type: bool - profiles: - - xpath: - - auth-algo-sha256 - validators: [] - spec: {} - description: Allow authentication SHA256 - required: false - - name: allow-authentication-sha384 - type: bool - profiles: - - xpath: - - auth-algo-sha384 - validators: [] - spec: {} - description: Allow authentication SHA384 - required: false - name: allow-algorithm-3des type: bool profiles: @@ -298,15 +287,6 @@ spec: spec: {} description: Allow algorithm AES-256-GCM required: false - - name: allow-algorithm-rc4 - type: bool - profiles: - - xpath: - - enc-algo-rc4 - validators: [] - spec: {} - description: Allow algorithm RC4 - required: false - name: allow-algorithm-dhe type: bool profiles: @@ -325,6 +305,15 @@ spec: spec: {} description: Allow algorithm ECDHE required: false + - name: allow-algorithm-rc4 + type: bool + profiles: + - xpath: + - enc-algo-rc4 + validators: [] + spec: {} + description: Allow algorithm RC4 + required: false - name: allow-algorithm-rsa type: bool profiles: @@ -334,6 +323,33 @@ spec: spec: {} description: Allow algorithm RSA required: false + - name: allow-authentication-sha1 + type: bool + profiles: + - xpath: + - auth-algo-sha1 + validators: [] + spec: {} + description: Allow authentication SHA1 + required: false + - name: allow-authentication-sha256 + type: bool + profiles: + - xpath: + - auth-algo-sha256 + validators: [] + spec: {} + description: Allow authentication SHA256 + required: false + - name: allow-authentication-sha384 + type: bool + profiles: + - xpath: + - auth-algo-sha384 + validators: [] + spec: {} + description: Allow authentication SHA384 + required: false - name: max-version type: enum profiles: diff --git a/specs/objects/profiles/url-filtering.yaml b/specs/objects/profiles/url-filtering.yaml index 98f49bcb..02295870 100644 --- a/specs/objects/profiles/url-filtering.yaml +++ b/specs/objects/profiles/url-filtering.yaml @@ -15,9 +15,11 @@ go_sdk_config: - objects - profiles - urlfiltering -xpath_suffix: -- profiles -- url-filtering +panos_xpath: + path: + - profiles + - url-filtering + vars: [] locations: - name: shared xpath: diff --git a/specs/objects/profiles/vulnerability.yaml b/specs/objects/profiles/vulnerability.yaml index 5faec089..03c3b453 100644 --- a/specs/objects/profiles/vulnerability.yaml +++ b/specs/objects/profiles/vulnerability.yaml @@ -15,9 +15,11 @@ go_sdk_config: - objects - profiles - vulnerability -xpath_suffix: -- profiles -- vulnerability +panos_xpath: + path: + - profiles + - vulnerability + vars: [] locations: - name: shared xpath: @@ -71,6 +73,17 @@ entries: imports: [] spec: params: + - name: cloud-inline-analysis + type: bool + profiles: + - xpath: + - cloud-inline-analysis + min_version: 11.0.2 + max_version: 11.0.3 + validators: [] + spec: {} + description: Enable cloud inline analysis + required: false - name: description type: string profiles: @@ -101,6 +114,79 @@ spec: - value: 'no' description: disable object override in child device groups required: false + - name: inline-exception-edl-url + type: list + profiles: + - xpath: + - inline-exception-edl-url + type: member + min_version: 11.0.2 + max_version: 11.0.3 + validators: [] + spec: + type: string + items: + type: string + description: '' + required: false + - name: inline-exception-ip-address + type: list + profiles: + - xpath: + - inline-exception-ip-address + type: member + min_version: 11.0.2 + max_version: 11.0.3 + validators: [] + spec: + type: string + items: + type: string + description: '' + required: false + - name: mica-engine-vulnerability-enabled + type: list + profiles: + - xpath: + - mica-engine-vulnerability-enabled + - entry + type: entry + min_version: 11.0.2 + max_version: 11.0.3 + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: inline-policy-action + type: enum + profiles: + - xpath: + - inline-policy-action + validators: + - type: values + spec: + values: + - alert + - allow + - reset-both + - reset-client + - reset-server + spec: + default: alert + values: + - value: alert + - value: allow + - value: reset-both + - value: reset-client + - value: reset-server + description: '' + required: false + variants: [] + description: '' + required: false - name: rules type: list profiles: @@ -585,88 +671,4 @@ spec: variants: [] description: '' required: false - - name: cloud-inline-analysis - type: bool - profiles: - - xpath: - - cloud-inline-analysis - min_version: 11.0.2 - max_version: 11.0.3 - validators: [] - spec: {} - description: Enable cloud inline analysis - required: false - - name: inline-exception-edl-url - type: list - profiles: - - xpath: - - inline-exception-edl-url - type: member - min_version: 11.0.2 - max_version: 11.0.3 - validators: [] - spec: - type: string - items: - type: string - description: '' - required: false - - name: inline-exception-ip-address - type: list - profiles: - - xpath: - - inline-exception-ip-address - type: member - min_version: 11.0.2 - max_version: 11.0.3 - validators: [] - spec: - type: string - items: - type: string - description: '' - required: false - - name: mica-engine-vulnerability-enabled - type: list - profiles: - - xpath: - - mica-engine-vulnerability-enabled - - entry - type: entry - min_version: 11.0.2 - max_version: 11.0.3 - validators: [] - spec: - type: object - items: - type: object - spec: - params: - - name: inline-policy-action - type: enum - profiles: - - xpath: - - inline-policy-action - validators: - - type: values - spec: - values: - - alert - - allow - - reset-both - - reset-client - - reset-server - spec: - default: alert - values: - - value: alert - - value: allow - - value: reset-both - - value: reset-client - - value: reset-server - description: '' - required: false - variants: [] - description: '' - required: false variants: [] diff --git a/specs/objects/profiles/wildfire-analysis.yaml b/specs/objects/profiles/wildfire-analysis.yaml index 3f2d26e6..0a0f3b19 100644 --- a/specs/objects/profiles/wildfire-analysis.yaml +++ b/specs/objects/profiles/wildfire-analysis.yaml @@ -15,9 +15,11 @@ go_sdk_config: - objects - profiles - wildfireanalysis -xpath_suffix: -- profiles -- wildfire-analysis +panos_xpath: + path: + - profiles + - wildfire-analysis + vars: [] locations: - name: shared xpath: diff --git a/specs/objects/service-group.yaml b/specs/objects/service-group.yaml index 37ba9b81..b45e44f5 100644 --- a/specs/objects/service-group.yaml +++ b/specs/objects/service-group.yaml @@ -16,8 +16,10 @@ go_sdk_config: - objects - service - group -xpath_suffix: -- service-group +panos_xpath: + path: + - service-group + vars: [] locations: - name: shared xpath: @@ -61,7 +63,6 @@ locations: description: Located in a specific Virtual System devices: - ngfw - - panorama validators: [] required: false read_only: false diff --git a/specs/objects/service.yaml b/specs/objects/service.yaml index 0be2d200..13a81a04 100644 --- a/specs/objects/service.yaml +++ b/specs/objects/service.yaml @@ -15,8 +15,10 @@ go_sdk_config: package: - objects - service -xpath_suffix: -- service +panos_xpath: + path: + - service + vars: [] locations: - name: shared xpath: @@ -60,7 +62,6 @@ locations: description: Located in a specific Virtual System devices: - ngfw - - panorama validators: [] required: false read_only: false @@ -148,33 +149,6 @@ spec: validators: [] spec: params: - - name: port - type: string - profiles: - - xpath: - - port - validators: - - type: length - spec: - max: 1023 - spec: {} - description: '' - required: false - codegen_overrides: - terraform: - name: destination-port - - name: source-port - type: string - profiles: - - xpath: - - source-port - validators: - - type: length - spec: - max: 1023 - spec: {} - description: '' - required: false - name: override type: object profiles: @@ -226,17 +200,6 @@ spec: variants: [] description: '' required: false - variants: [] - description: '' - required: false - - name: udp - type: object - profiles: - - xpath: - - udp - validators: [] - spec: - params: - name: port type: string profiles: @@ -264,6 +227,17 @@ spec: spec: {} description: '' required: false + variants: [] + description: '' + required: false + - name: udp + type: object + profiles: + - xpath: + - udp + validators: [] + spec: + params: - name: override type: object profiles: @@ -290,6 +264,33 @@ spec: variants: [] description: '' required: false + - name: port + type: string + profiles: + - xpath: + - port + validators: + - type: length + spec: + max: 1023 + spec: {} + description: '' + required: false + codegen_overrides: + terraform: + name: destination-port + - name: source-port + type: string + profiles: + - xpath: + - source-port + validators: + - type: length + spec: + max: 1023 + spec: {} + description: '' + required: false variants: [] description: '' required: false diff --git a/specs/panorama/device-group-parent.yaml b/specs/panorama/device-group-parent.yaml index de5727b2..807c0782 100644 --- a/specs/panorama/device-group-parent.yaml +++ b/specs/panorama/device-group-parent.yaml @@ -12,8 +12,9 @@ terraform_provider_config: go_sdk_config: skip: true package: ["device_group_parent"] -xpath_suffix: - - device-group +panos_xpath: + path: + - device-group locations: - name: "panorama" description: "Located in a specific Panorama." diff --git a/specs/panorama/device-group.yaml b/specs/panorama/device-group.yaml index f757d4e2..7b46c07a 100644 --- a/specs/panorama/device-group.yaml +++ b/specs/panorama/device-group.yaml @@ -1,12 +1,14 @@ name: "Device group" terraform_provider_config: + resource_type: entry suffix: "device_group" go_sdk_config: package: - "panorama" - "devicegroup" -xpath_suffix: - - "device-group" +panos_xpath: + path: + - "device-group" locations: - name: "panorama" description: "Located in a specific Panorama." diff --git a/specs/panorama/template-stack.yaml b/specs/panorama/template-stack.yaml index 0c0b4504..b5a1fced 100644 --- a/specs/panorama/template-stack.yaml +++ b/specs/panorama/template-stack.yaml @@ -1,12 +1,14 @@ name: "Template stack" terraform_provider_config: + resource_type: entry suffix: "template_stack" go_sdk_config: package: - "panorama" - "template_stack" -xpath_suffix: - - "template-stack" +panos_xpath: + path: + - "template-stack" locations: - name: "panorama" description: "Located in a specific Panorama." @@ -58,7 +60,7 @@ spec: items: type: string profiles: - - type: entry + - type: member xpath: ["devices"] - name: default-vsys description: "Default virtual system" diff --git a/specs/panorama/template-variable.yaml b/specs/panorama/template-variable.yaml index 2b1a2e0f..b22c5831 100644 --- a/specs/panorama/template-variable.yaml +++ b/specs/panorama/template-variable.yaml @@ -1,12 +1,14 @@ name: "Template Variable" terraform_provider_config: + resource_type: entry suffix: "template_variable" go_sdk_config: package: - "panorama" - "template_variable" -xpath_suffix: - - "variable" +panos_xpath: + path: + - "variable" locations: - name: "template" description: "Located in a specific template." diff --git a/specs/panorama/template.yaml b/specs/panorama/template.yaml index be078ac6..f1b52008 100644 --- a/specs/panorama/template.yaml +++ b/specs/panorama/template.yaml @@ -1,12 +1,14 @@ name: "Template" terraform_provider_config: + resource_type: entry suffix: "template" go_sdk_config: package: - "panorama" - "template" -xpath_suffix: - - "template" +panos_xpath: + path: + - "template" locations: - name: "panorama" description: "Located in a specific Panorama." diff --git a/specs/policies/decryption-policy.yaml b/specs/policies/decryption-policy.yaml index 7629bad2..291a5a8f 100644 --- a/specs/policies/decryption-policy.yaml +++ b/specs/policies/decryption-policy.yaml @@ -17,9 +17,11 @@ go_sdk_config: - policies - rules - decryption -xpath_suffix: -- decryption -- rules +panos_xpath: + path: + - decryption + - rules + vars: [] locations: - name: shared xpath: diff --git a/specs/policies/network-address-translation.yaml b/specs/policies/network-address-translation.yaml index 5363b37e..7001881d 100644 --- a/specs/policies/network-address-translation.yaml +++ b/specs/policies/network-address-translation.yaml @@ -17,9 +17,11 @@ go_sdk_config: - policies - rules - nat -xpath_suffix: -- nat -- rules +panos_xpath: + path: + - nat + - rules + vars: [] locations: - name: shared xpath: @@ -317,6 +319,7 @@ spec: spec: {} description: Floating IP address in HA Active-Active configuration required: false + variant_group_id: 0 - name: ip type: string profiles: @@ -327,8 +330,10 @@ spec: description: specify exact IP address if interface has multiple addresses required: false + variant_group_id: 0 description: Use interface address as translated address required: false + variant_group_id: 0 - name: translated-address type: list profiles: @@ -342,6 +347,7 @@ spec: type: string description: '' required: false + variant_group_id: 0 description: Fallback Dynamic IP and port translation required: false - name: translated-address @@ -360,6 +366,7 @@ spec: variants: [] description: Dynamic IP-only translation required: false + variant_group_id: 0 - name: dynamic-ip-and-port type: object profiles: @@ -399,6 +406,7 @@ spec: spec: {} description: Floating IP address in HA Active-Active configuration required: false + variant_group_id: 0 - name: ip type: string profiles: @@ -408,8 +416,10 @@ spec: spec: {} description: specify exact IP address if interface has multiple addresses required: false + variant_group_id: 0 description: Use interface address as translated address required: false + variant_group_id: 0 - name: translated-address type: list profiles: @@ -423,8 +433,10 @@ spec: type: string description: '' required: false + variant_group_id: 0 description: Dynamic IP and port translation required: false + variant_group_id: 0 - name: static-ip type: object profiles: @@ -464,6 +476,7 @@ spec: variants: [] description: static IP translation via IP shifting required: false + variant_group_id: 0 description: '' required: false - name: tag @@ -599,28 +612,6 @@ spec: validators: [] spec: params: - - name: translated-address - type: string - profiles: - - xpath: - - translated-address - validators: [] - spec: {} - description: '' - required: false - - name: translated-port - type: int64 - profiles: - - xpath: - - translated-port - validators: - - type: length - spec: - min: 1 - max: 65535 - spec: {} - description: '' - required: false - name: dns-rewrite type: object profiles: @@ -650,12 +641,36 @@ spec: variants: [] description: '' required: false + variant_group_id: 0 + - name: translated-address + type: string + profiles: + - xpath: + - translated-address + validators: [] + spec: {} + description: '' + required: false + - name: translated-port + type: int64 + profiles: + - xpath: + - translated-port + validators: + - type: length + spec: + min: 1 + max: 65535 + spec: {} + description: '' + required: false variants: [] description: '' required: false codegen_overrides: terraform: variant_check: ConflictsWith + variant_group_id: 0 - name: dynamic-destination-translation type: object profiles: @@ -716,3 +731,4 @@ spec: codegen_overrides: terraform: variant_check: ConflictsWith + variant_group_id: 0 diff --git a/specs/policies/security-policy-rule.yaml b/specs/policies/security-policy-rule.yaml index 4223219c..13f5a549 100644 --- a/specs/policies/security-policy-rule.yaml +++ b/specs/policies/security-policy-rule.yaml @@ -17,9 +17,11 @@ go_sdk_config: - policies - rules - security -xpath_suffix: -- security -- rules +panos_xpath: + path: + - security + - rules + vars: [] locations: - name: shared xpath: @@ -233,6 +235,16 @@ spec: spec: {} description: '' required: false + - name: disable-server-response-inspection + type: bool + profiles: + - xpath: + - option + - disable-server-response-inspection + validators: [] + spec: {} + description: Disable inspection of server side traffic + required: false - name: disabled type: bool profiles: @@ -778,14 +790,4 @@ spec: codegen_overrides: terraform: private: true - - name: disable-server-response-inspection - type: bool - profiles: - - xpath: - - option - - disable-server-response-inspection - validators: [] - spec: {} - description: Disable inspection of server side traffic - required: false variants: [] diff --git a/specs/schema/import.schema.json b/specs/schema/import.schema.json index 19626edb..415a8aef 100644 --- a/specs/schema/import.schema.json +++ b/specs/schema/import.schema.json @@ -14,8 +14,14 @@ "additionalProperties": false, "properties": { "name": { "type": "string" }, + "description": { "type": "string" }, + "read_only": { "type": "boolean" }, "required": { "type": "boolean", "default": false }, - "xpath": { "$ref": "xpath.schema.json" } + "xpath": { "$ref": "xpath.schema.json" }, + "validators": { + "type": "array", + "items": { "$ref": "validator.schema.json" } + } } } } diff --git a/specs/schema/object.schema.json b/specs/schema/object.schema.json index 68121838..87fe9c73 100644 --- a/specs/schema/object.schema.json +++ b/specs/schema/object.schema.json @@ -9,6 +9,7 @@ "name", "terraform_provider_config", "go_sdk_config", + "panos_xpath", "locations", "spec" ], @@ -17,7 +18,7 @@ "terraform_provider_config": { "type": "object", "additionalProperties": false, - "required": ["suffix"], + "required": ["suffix", "resource_type"], "properties": { "skip_datasource": { "type": "boolean" }, "skip_resource": { "type": "boolean" }, @@ -67,10 +68,16 @@ } } }, - "xpath_suffix": { - "type": "array", - "items": { - "type": "string" + "panos_xpath": { + "type": "object", + "required": ["path"], + "properties": { + "path": { + "type": "array", + "items": { + "type": "string" + } + } } }, "locations": { diff --git a/specs/schema/schema.json b/specs/schema/schema.json index c9f101ae..de3ef738 100644 --- a/specs/schema/schema.json +++ b/specs/schema/schema.json @@ -8,7 +8,7 @@ "name", "terraform_provider_config", "go_sdk_config", - "xpath_suffix", + "panos_xpath", "locations", "entries", "version", @@ -26,6 +26,13 @@ "type": "string" } } + }, + "panos_xpath": { + "type": "object", + "required": ["path"], + "properties": [ + "path": { "type": "array" } + ] } } } diff --git a/specs/schema/spec.schema.json b/specs/schema/spec.schema.json index ca825c4d..b84f5eac 100644 --- a/specs/schema/spec.schema.json +++ b/specs/schema/spec.schema.json @@ -21,6 +21,7 @@ "properties": { "name": { "type": "string" }, "description": { "type": "string" }, + "variant_group_id": { "type": "integer" }, "codegen_overrides": { "type": "object", "additionalProperties": false, diff --git a/templates/sdk/config.tmpl b/templates/sdk/config.tmpl index 1b90d258..8ebddb74 100644 --- a/templates/sdk/config.tmpl +++ b/templates/sdk/config.tmpl @@ -5,128 +5,12 @@ type configXmlContainer{{createGoSuffixFromVersion $.Version }} struct { } {{- end }} -{{- define "configXmlStructTmpl" }} -type configXml{{createGoSuffixFromVersion $.Version}} struct { - XMLName xml.Name `xml:"system"` - {{- range $_, $param := $.Spec.Params}} - {{- if paramSupportedInVersion $param $.Version}} - {{- if $param.Spec}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}}{{createGoSuffixFromVersion $.Version}} {{xmlTag $param}} - {{- else}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} - {{- end}} - {{- end}} - {{- end}} - - {{- range $_, $param := $.Spec.OneOf}} - {{- if paramSupportedInVersion $param $.Version}} - {{- if $param.Spec}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}}{{createGoSuffixFromVersion $.Version}} {{xmlTag $param}} - {{- else}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} - {{- end}} - {{- end}} - {{- end}} - - Misc []generic.Xml `xml:",any"` -} -{{- end }} - -{{- define "configXmlChildStructTmpl" }} -type {{ $.Name }}Xml{{ createGoSuffixFromVersion $.Version }} struct { -{{- range $_, $param := $.Spec.Params}} - {{- if paramSupportedInVersion $param $.Version}} - {{- if $param.Spec}} - {{$param.Name.CamelCase}} {{xmlParamType $.Name $param}}{{createGoSuffixFromVersion $.Version}} {{xmlTag $param}} - {{- else}} - {{$param.Name.CamelCase}} {{xmlParamType $.Name $param}} {{xmlTag $param}} - {{- end}} - {{- end}} -{{- end}} - -{{- range $_, $param := $.Spec.OneOf}} - {{- if paramSupportedInVersion $param $.Version}} - {{- if $param.Spec}} - {{$param.Name.CamelCase}} {{xmlParamType $.Name $param}}{{createGoSuffixFromVersion $.Version}} {{xmlTag $param}} - {{- else}} - {{$param.Name.CamelCase}} {{xmlParamType $.Name $param}} {{xmlTag $param}} - {{- end}} - {{- end}} -{{- end}} - - Misc []generic.Xml `xml:",any"` -} -{{- end }} - -{{- define "SpecifyConfigTmpl" }} -func specifyConfig{{createGoSuffixFromVersion $.Version}}(o *Config) (any, error) { - config := configXml{{createGoSuffixFromVersion $.Version}}{} - - {{- range $_, $param := $.Spec.Params}} - {{ specifyEntryAssignment "config" $param $.Version }} - {{- end}} - - {{- range $_, $param := $.Spec.OneOf}} - {{ specifyEntryAssignment "config" $param $.Version }} - {{- end}} - - config.Misc = o.Misc["Config"] - - return config, nil -} -{{- end }} - -{{- define "NormalizeTmpl" }} -func (c *configXmlContainer{{createGoSuffixFromVersion $.Version }}) Normalize() ([]*Config, error) { - configList := make([]*Config, 0, len(c.Answer)) - for _, o := range c.Answer { - config := &Config{ - Misc: make(map[string][]generic.Xml), - } - - {{- range $_, $param := $.Spec.Params}} - {{normalizeAssignment "config" $param $.Version}} - {{- end}} - - {{- range $_, $param := $.Spec.OneOf}} - {{normalizeAssignment "config" $param $.Version}} - {{- end}} - - config.Misc["Config"] = o.Misc - - configList = append(configList, config) -} - -return configList, nil -} -{{- end }} - {{- if not .Entry}} package {{packageName .GoSdkPath}} {{renderImports "config"}} - type Config{{createGoSuffixFromVersion nil}} struct { - {{- range $_, $param := $.Spec.SortedParams}} - {{$param.Name.CamelCase}} {{specParamType "" $param}} - {{- end}} - {{- range $_, $param := $.Spec.SortedOneOf}} - {{$param.Name.CamelCase}} {{specParamType "" $param}} - {{- end}} - - Misc map[string][]generic.Xml - } - - {{- range $name, $nested := nestedSpecs $.Spec }} - type {{$name}}{{createGoSuffixFromVersion nil}} struct { - {{- range $_, $param := $nested.Spec.SortedParams}} - {{$param.Name.CamelCase}} {{specParamType $name $param}} - {{- end}} - {{- range $_, $param := $nested.Spec.SortedOneOf}} - {{$param.Name.CamelCase}} {{specParamType $name $param}} - {{- end}} - } - {{- end}} + {{- RenderApiStructs $ }} {{- template "configXmlContainerStructTmpl" Map "Version" nil }} @@ -134,20 +18,13 @@ return configList, nil {{- template "configXmlContainerStructTmpl" Map "Version" $version.Minimum }} {{- end}} - {{- template "configXmlStructTmpl" Map "Spec" $.Spec "Version" nil }} - {{- range $version := .SupportedVersionRanges }} - {{- template "configXmlStructTmpl" Map "Version" $version.Minimum }} - {{- end}} + {{ RenderXmlContainerNormalizers $ }} - {{- range $name, $nested := nestedSpecs $.Spec }} - {{- template "configXmlChildStructTmpl" Map "Name" $name "ParentIsList" $nested.ParentIsList "Spec" $nested.Spec "Version" nil }} - {{- end }} + {{ RenderXmlContainerSpecifiers $ }} - {{- range $version := .SupportedVersionRanges }} - {{- range $name, $nested := nestedSpecs $.Spec }} - {{- template "configXmlChildStructTmpl" Map "Name" $name "ParentIsList" $nested.ParentIsList "Spec" $nested.Spec "Version" $version.Minimum }} - {{- end}} - {{- end}} + {{- RenderXmlStructs $ }} + + {{ RenderToXmlMarshallers $ }} func Versioning(vn version.Number) (Specifier, Normalizer, error) { {{- $numberOfVersions := len .SupportedVersions }} @@ -179,38 +56,5 @@ return configList, nil {{- end}} } - {{- template "SpecifyConfigTmpl" Map "Spec" $.Spec "Version" nil }} - - {{- range $version := .SupportedVersionRanges }} - {{- template "SpecifyConfigTmpl" Map "Spec" $.Spec "Version" nil }} - {{- end }} - - {{- template "NormalizeTmpl" Map "Spec" $.Spec "Version" nil }} - {{- range $version := .SupportedVersionRanges }} - {{- template "NormalizeTmpl" Map "Spec" $.Spec "Version" $version.Minimum }} - {{- end}} - - func SpecMatches(a, b *Config) bool { - if a == nil && b != nil || a != nil && b == nil { - return false - } else if a == nil && b == nil { - return true - } - - // Don't compare Name. - {{- range $_, $param := .Spec.SortedParams}} - if !{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { - return false - } - {{- end}} - {{- range $_, $param := .Spec.SortedOneOf}} - if !{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { - return false - } - {{- end}} - - return true - } - - {{nestedSpecMatchesFunction $.Spec}} + {{- RenderSpecMatchers $ }} {{- end}} diff --git a/templates/sdk/entry.tmpl b/templates/sdk/entry.tmpl index 764057af..aa41fedf 100644 --- a/templates/sdk/entry.tmpl +++ b/templates/sdk/entry.tmpl @@ -1,105 +1,3 @@ -{{- define "entryXmlStructTmpl" }} -type entryXml{{createGoSuffixFromVersion $.Version}} struct { - XMLName xml.Name `xml:"entry"` - Name string `xml:"name,attr"` - {{- range $_, $param := $.Spec.Params}} - {{- if paramSupportedInVersion $param $.Version}} - {{- if $param.Spec}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}}{{createGoSuffixFromVersion $.Version}} {{xmlTag $param}} - {{- else}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} - {{- end}} - {{- end}} - {{- end}} - - {{- range $_, $param := $.Spec.OneOf}} - {{- if paramSupportedInVersion $param $.Version}} - {{- if $param.Spec}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}}{{createGoSuffixFromVersion $.Version}} {{xmlTag $param}} - {{- else}} - {{$param.Name.CamelCase}} {{xmlParamType "" $param}} {{xmlTag $param}} - {{- end}} - {{- end}} - {{- end}} - - Misc []generic.Xml `xml:",any"` -} -{{- end }} - -{{- define "entryXmlChildStructTmpl" }} -type {{ .Name }}Xml{{createGoSuffixFromVersion $.Version}} struct { - {{- range $_, $param := $.Spec.Params}} - {{- if and ($.ParentIsList) (eq $param.Name.CamelCase "Name") }} - XMLName xml.Name `xml:"entry"` - {{- end}} - {{- if paramSupportedInVersion $param $.Version}} - {{- if $param.Spec}} - {{$param.Name.CamelCase}} {{xmlParamType $.Name $param}}{{createGoSuffixFromVersion $.Version}} {{xmlTag $param}} - {{- else}} - {{$param.Name.CamelCase}} {{xmlParamType $.Name $param}} {{xmlTag $param}} - {{- end}} - {{- end}} - {{- end}} - - {{- range $_, $param := $.Spec.OneOf}} - {{- if paramSupportedInVersion $param $.Version}} - {{- if $param.Spec}} - {{$param.Name.CamelCase}} {{xmlParamType $.Name $param}}{{createGoSuffixFromVersion $.Version}} {{xmlTag $param}} - {{- else}} - {{$param.Name.CamelCase}} {{xmlParamType $.Name $param}} {{xmlTag $param}} - {{- end}} - {{- end}} - {{- end}} - - Misc []generic.Xml `xml:",any"` -} -{{- end }} - -{{- define "SpecifyEntryTmpl" }} -func specifyEntry{{ createGoSuffixFromVersion $.Version }}(o *Entry) (any, error) { - entry := entryXml{{ createGoSuffixFromVersion $.Version }}{} - entry.Name = o.Name - - {{- range $_, $param := $.Spec.Params}} - {{ specifyEntryAssignment "entry" $param $.Version }} - {{- end}} - - {{- range $_, $param := $.Spec.OneOf}} - {{ specifyEntryAssignment "entry" $param $.Version }} - {{- end}} - - entry.Misc = o.Misc["Entry"] - - return entry, nil -} - -{{- end }} - -{{- define "NormalizeTmpl" }} -func (c *entryXmlContainer{{ createGoSuffixFromVersion $.Version }}) Normalize() ([]*Entry, error) { - entryList := make([]*Entry, 0, len(c.Answer)) - for _, o := range c.Answer { - entry := &Entry{ - Misc: make(map[string][]generic.Xml), - } - entry.Name = o.Name - {{- range $_, $param := $.Spec.Params}} - {{normalizeAssignment "entry" $param $.Version }} - {{- end}} - - {{- range $_, $param := $.Spec.OneOf}} - {{normalizeAssignment "entry" $param $.Version }} - {{- end}} - - entry.Misc["Entry"] = o.Misc - - entryList = append(entryList, entry) -} - -return entryList, nil -} -{{- end }} - {{- if .Entry}} package {{packageName .GoSdkPath}} @@ -110,35 +8,14 @@ return entryList, nil ) var ( - Suffix = []string{ + suffix = []string{ {{- $length := subtract (len .XpathSuffix) 1 }} {{- range $index, $suffix := .XpathSuffix}}" {{- $suffix}}"{{- if lt $index $length}},{{- end}} {{- end}}} ) - type Entry{{createGoSuffixFromVersion nil}} struct { - Name string - {{- range $_, $param := $.Spec.Params}} - {{$param.Name.CamelCase}} {{specParamType "" $param}} - {{- end}} - {{- range $_, $param := $.Spec.OneOf}} - {{$param.Name.CamelCase}} {{specParamType "" $param}} - {{- end}} - - Misc map[string][]generic.Xml - } - - {{ range $name, $nested := nestedSpecs $.Spec }} - type {{$name}}{{createGoSuffixFromVersion nil}} struct { - {{- range $param := $nested.Spec.SortedParams}} - {{$param.Name.CamelCase}} {{specParamType $name $param}} - {{- end}} - {{- range $param := $nested.Spec.SortedOneOf}} - {{$param.Name.CamelCase}} {{specParamType $name $param}} - {{- end}} - } - {{- end}} + {{ RenderApiStructs $ }} type entryXmlContainer{{createGoSuffixFromVersion nil}} struct { Answer []entryXml{{createGoSuffixFromVersion nil}} `xml:"entry"` @@ -150,20 +27,13 @@ return entryList, nil } {{- end}} - {{- template "entryXmlStructTmpl" Map "Spec" $.Spec "Version" nil }} - {{- range $version := .SupportedVersionRanges }} - {{- template "entryXmlStructTmpl" Map "Spec" $.Spec "Version" $version.Minimum }} - {{- end}} + {{ RenderXmlContainerNormalizers $ }} - {{- range $name, $nested := nestedSpecs $.Spec }} - {{- template "entryXmlChildStructTmpl" Map "Name" $name "ParentIsList" $nested.ParentIsList "Spec" $nested.Spec "Version" nil }} - {{- end }} + {{ RenderXmlContainerSpecifiers $ }} - {{- range $version := .SupportedVersionRanges }} - {{- range $name, $nested := nestedSpecs $.Spec }} - {{- template "entryXmlChildStructTmpl" Map "Name" $name "ParentIsList" $nested.ParentIsList "Spec" $nested.Spec "Version" $version.Minimum }} - {{- end }} - {{- end}} + {{ RenderXmlStructs $ }} + + {{ RenderToXmlMarshallers $ }} {{ range $name, $const := .Const}} const ( @@ -179,6 +49,7 @@ return entryList, nil } {{- range $_, $param := .Spec.SortedParams}} + {{- if not (paramNotSkipped $param) }}{{ continue }}{{- end }} if v == "{{$param.Name.Underscore}}" || v == "{{$param.Name.CamelCase}}" { return e.{{$param.Name.CamelCase}}, nil } @@ -211,41 +82,7 @@ return entryList, nil return specifyEntry, &entryXmlContainer{}, nil } - - {{- template "SpecifyEntryTmpl" Map "Spec" $.Spec "Version" nil }} - - {{ range $version := .SupportedVersionRanges }} - {{- template "SpecifyEntryTmpl" Map "Spec" $.Spec "Version" $version.Minimum }} - {{- end}} - - {{- template "NormalizeTmpl" Map "Spec" $.Spec "Version" nil }} - {{- range $version := .SupportedVersionRanges }} - {{- template "NormalizeTmpl" Map "Spec" $.Spec "Version" $version.Minimum }} - {{- end}} - - func SpecMatches(a, b *Entry) bool { - if a == nil && b != nil || a != nil && b == nil { - return false - } else if a == nil && b == nil { - return true - } - - // Don't compare Name. - {{- range $_, $param := .Spec.SortedParams}} - if !{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { - return false - } - {{- end}} - {{- range $_, $param := .Spec.SortedOneOf}} - if !{{specMatchesFunction $param}}(a.{{$param.Name.CamelCase}}, b.{{$param.Name.CamelCase}}) { - return false - } - {{- end}} - - return true - } - - {{nestedSpecMatchesFunction $.Spec}} + {{- RenderSpecMatchers $ }} func (o *Entry) EntryName() string { return o.Name diff --git a/templates/sdk/location.tmpl b/templates/sdk/location.tmpl index 0c8a9370..196424d6 100644 --- a/templates/sdk/location.tmpl +++ b/templates/sdk/location.tmpl @@ -74,7 +74,7 @@ switch { ans = []string{ {{- range $name, $xpath := $location.Xpath}} {{- if contains $xpath "Entry"}} - {{generateEntryXpath "util.AsEntryXpath([]string{" "})" $location.Name.CamelCase $xpath}} + {{generateEntryXpath "util.AsEntryXpath(" ")" $location.Name.CamelCase $xpath}} {{- else if contains $xpath "Object"}} {{generateEntryXpath "" "" $location.Name.CamelCase $xpath}} {{- else}} @@ -90,35 +90,21 @@ return nil, errors.NoLocationSpecifiedError return ans, nil } -{{- if .Entry}} - func (o Location) XpathWithEntryName(vn version.Number, name string) ([]string, error) { -{{- else}} - func (o Location) Xpath(vn version.Number) ([]string, error) { -{{- end}} - -ans, err := o.XpathPrefix(vn) -if err != nil { -return nil, err -} +func (o Location) XpathWithComponents(vn version.Number, components ...string) ([]string, error) { + if len(components) != {{ .ResourceXpathVariablesCount }} { + return nil, fmt.Errorf("invalid number of arguments for XpathWithComponents() call") + } -{{- if .Entry}} - ans = append(ans, Suffix...) - ans = append(ans, util.AsEntryXpath([]string{name})) -{{- end}} + {{ .ResourceXpathVariableChecks }} -return ans, nil -} + ans, err := o.XpathPrefix(vn) + if err != nil { + return nil, err + } -{{- if .Entry}} - func (o Location) XpathWithUuid(vn version.Number, uuid string) ([]string, error) { + {{- if ge .ResourceXpathVariablesCount 1 }} + {{ .ResourceXpathAssignments }} + {{- end }} - ans, err := o.XpathPrefix(vn) - if err != nil { - return nil, err - } - ans = append(ans, Suffix...) - ans = append(ans, util.AsUuidXpath(uuid)) - - return ans, nil - } -{{- end}} + return ans, nil +} diff --git a/templates/sdk/service.tmpl b/templates/sdk/service.tmpl index 954cfd41..89808bd3 100644 --- a/templates/sdk/service.tmpl +++ b/templates/sdk/service.tmpl @@ -55,73 +55,91 @@ client: client, } // Create adds new item, then returns the result. -{{- if and (.Entry) (.Imports)}} - func (s *Service) Create(ctx context.Context, loc Location, importLocations []ImportLocation, entry *Entry) (*Entry, error) { -{{- else if (.Entry) }} - func (s *Service) Create(ctx context.Context, loc Location, entry *Entry) (*Entry, error) { -{{- else }} - func (s *Service) Create(ctx context.Context, loc Location, config *Config) (*Config, error) { -{{- end}} + {{- if and (.Entry) (.Imports)}} +func (s *Service) Create(ctx context.Context, loc Location, importLocations []ImportLocation, entry *Entry) (*Entry, error) { + {{- else if (.Entry) }} +func (s *Service) Create(ctx context.Context, loc Location, entry *Entry) (*Entry, error) { + {{- else }} +func (s *Service) Create(ctx context.Context, loc Location, config *Config) (*Config, error) { + {{- end}} -{{- if .Entry}} - if entry.Name == "" { - return nil, errors.NameNotSpecifiedError - } -{{- end}} + {{- if .Entry}} + if entry.Name == "" { + return nil, errors.NameNotSpecifiedError + } + {{- end}} -vn := s.client.Versioning() + vn := s.client.Versioning() -specifier, _, err := Versioning(vn) -if err != nil { -return nil, err -} + {{- if .Entry}} + path, err := loc.XpathWithComponents(vn, util.AsEntryXpath(entry.Name)) + {{- else}} + path, err := loc.XpathWithComponents(vn) + {{- end}} + if err != nil { + return nil, err + } -{{- if .Entry}} - path, err := loc.XpathWithEntryName(vn, entry.Name) -{{- else}} - path, err := loc.Xpath(vn) -{{- end}} -if err != nil { -return nil, err -} + {{- if .Entry}} + err = s.CreateWithXpath(ctx, util.AsXpath(path[:len(path)-1]), entry) + {{- else}} + err = s.CreateWithXpath(ctx, util.AsXpath(path[:len(path)-1]), config) + {{- end}} + if err != nil { + return nil, err + } -{{- if .Entry}} - createSpec, err := specifier(entry) -{{- else}} - createSpec, err := specifier(config) -{{- end}} -if err != nil { -return nil, err -} + {{- if .Imports }} + err = s.ImportToLocations(ctx, loc, importLocations, entry.Name) + if err != nil { + return nil, err + } + {{- end }} -cmd := &xmlapi.Config{ -Action: "set", -Xpath: util.AsXpath(path[:len(path)-1]), -Element: createSpec, -Target: s.client.GetTarget(), + return s.ReadWithXpath(ctx, util.AsXpath(path), "get") } -if _, _, err = s.client.Communicate(ctx, cmd, false, nil); err != nil { -return nil, err -} -{{- if .Imports }} -err = s.importToLocations(ctx, loc, importLocations, entry.Name) -if err != nil { - return nil, err -} -{{- end }} + {{ $funcDef := "" }} + {{- if .Entry }} + {{ $funcDef = "CreateWithXpath(ctx context.Context, xpath string, entry *Entry) error" }} + {{- else }} + {{ $funcDef = "CreateWithXpath(ctx context.Context, xpath string, config *Config) error" }} + {{- end }} -{{- if .Entry}} - return s.Read(ctx, loc, entry.Name, "get") -{{- else}} - return s.Read(ctx, loc, "get") -{{- end}} +func (s *Service) {{ $funcDef }} { + vn := s.client.Versioning() + specifier, _, err := Versioning(vn) + if err != nil { + return err + } + + {{- if .Entry}} + createSpec, err := specifier(entry) + {{- else}} + createSpec, err := specifier(config) + {{- end}} + if err != nil { + return err + } + + cmd := &xmlapi.Config{ + Action: "set", + Xpath: xpath, + Element: createSpec, + Target: s.client.GetTarget(), + } + + if _, _, err = s.client.Communicate(ctx, cmd, false, nil); err != nil { + return err + } + + return nil } {{- if .Imports }} -func (s *Service) importToLocations(ctx context.Context, loc Location, importLocations []ImportLocation, entryName string) error { +func (s *Service) ImportToLocations(ctx context.Context, loc Location, importLocations []ImportLocation, entryName string) error { vn := s.client.Versioning() importToLocation := func(il ImportLocation) error { @@ -186,7 +204,7 @@ func (s *Service) importToLocations(ctx context.Context, loc Location, importLoc return nil } -func (s *Service) unimportFromLocations(ctx context.Context, loc Location, importLocations []ImportLocation, values []string) error { +func (s *Service) UnimportFromLocations(ctx context.Context, loc Location, importLocations []ImportLocation, values []string) error { vn := s.client.Versioning() valuesByName := make(map[string]bool) for _, elt := range values { @@ -259,115 +277,96 @@ func (s *Service) unimportFromLocations(ctx context.Context, loc Location, impor {{- if .Entry}} func (s *Service) Read(ctx context.Context, loc Location, name, action string) (*Entry, error) { {{- if $.Spec.Params.uuid}} - return s.read(ctx, loc, name, action, true, false) + return s.read(ctx, loc, name, action, true) {{- else}} - return s.read(ctx, loc, name, action, false) + return s.read(ctx, loc, name, action) {{- end}} } {{- if $.Spec.Params.uuid}} // ReadById returns the given config object with specified ID, using the specified action ("get" or "show"). func (s *Service) ReadById(ctx context.Context, loc Location, uuid, action string) (*Entry, error) { - return s.read(ctx, loc, uuid, action, false, false) + return s.read(ctx, loc, uuid, action, false) } {{- end}} {{- else}} func (s *Service) Read(ctx context.Context, loc Location, action string) (*Config, error) { - return s.read(ctx, loc, action, false) + return s.read(ctx, loc, action) } {{- end}} -// ReadFromConfig returns the given config object from the loaded XML config. -// Requires that client.LoadPanosConfig() has been invoked. -{{- if .Entry}} - func (s *Service) ReadFromConfig(ctx context.Context, loc Location, name string) (*Entry, error) { - {{- if $.Spec.Params.uuid}} - return s.read(ctx, loc, name, "", true, true) - {{- else}} - return s.read(ctx, loc, name, "", true) - {{- end}} - } - {{- if $.Spec.Params.uuid}} - // ReadFromConfigById returns the given config object with specified ID from the loaded XML config. - // Requires that client.LoadPanosConfig() has been invoked. - func (s *Service) ReadFromConfigById(ctx context.Context, loc Location, uuid string) (*Entry, error) { - return s.read(ctx, loc, uuid, "", false, true) - } - {{- end}} -{{- else}} - func (s *Service) ReadFromConfig(ctx context.Context, loc Location) (*Config, error) { - return s.read(ctx, loc, "", true) - } -{{- end}} + {{ $funcDef := "" }} + {{- if .Entry }} + {{ $funcDef = "ReadWithXpath(ctx context.Context, xpath string, action string) (*Entry, error)" }} + {{- else }} + {{ $funcDef = "ReadWithXpath(ctx context.Context, xpath string, action string) (*Config, error)" }} + {{- end }} +func (s *Service) {{ $funcDef }} { + vn := s.client.Versioning() + _, normalizer, err := Versioning(vn) + if err != nil { + return nil, err + } + + cmd := &xmlapi.Config{ + Action: action, + Xpath: xpath, + Target: s.client.GetTarget(), + } + + if _, _, err = s.client.Communicate(ctx, cmd, true, normalizer); err != nil { + if err.Error() == "No such node" && action == "show" { + return nil, errors.ObjectNotFound() + } + return nil, err + } + + list, err := normalizer.Normalize() + if err != nil { + return nil, err + } else if len(list) != 1 { + return nil, fmt.Errorf("expected to %q 1 entry, got %d", action, len(list)) + } -{{if .Entry}} + return list[0], nil +} + + {{if .Entry}} {{- if $.Spec.Params.uuid}} - func (s *Service) read(ctx context.Context, loc Location, value, action string, byName, usePanosConfig bool) (*Entry, error) { +func (s *Service) read(ctx context.Context, loc Location, value, action string, byName bool) (*Entry, error) { if byName && value == "" { - return nil, errors.NameNotSpecifiedError - } - if !byName && value == "" { - return nil, errors.UuidNotSpecifiedError + return nil, errors.NameNotSpecifiedError } + + if !byName && value == "" { + return nil, errors.UuidNotSpecifiedError + } {{- else}} - func (s *Service) read(ctx context.Context, loc Location, value, action string, usePanosConfig bool) (*Entry, error) { +func (s *Service) read(ctx context.Context, loc Location, value, action string) (*Entry, error) { if value == "" { return nil, errors.NameNotSpecifiedError } {{- end}} -{{- else}} - func (s *Service) read(ctx context.Context, loc Location, action string, usePanosConfig bool) (*Config, error) { -{{- end}} -vn := s.client.Versioning() -_, normalizer, err := Versioning(vn) -if err != nil { -return nil, err -} + {{- else}} +func (s *Service) read(ctx context.Context, loc Location, action string) (*Config, error) { + {{- end}} + vn := s.client.Versioning() -{{- if .Entry}} - var path []string + {{- if .Entry}} + var path []string + var err error {{- if $.Spec.Params.uuid}} - if byName { - path, err = loc.XpathWithEntryName(vn, value) - } else { - path, err = loc.XpathWithUuid(vn, value) - } + path, err = loc.XpathWithComponents(vn, value) {{- else}} - path, err = loc.XpathWithEntryName(vn, value) + path, err = loc.XpathWithComponents(vn, value) {{- end}} -{{- else}} - path, err := loc.Xpath(vn) -{{- end}} -if err != nil { -return nil, err -} - -if usePanosConfig { -if _, err = s.client.ReadFromConfig(ctx, path, true, normalizer); err != nil { -return nil, err -} -} else { -cmd := &xmlapi.Config{ -Action: action, -Xpath: util.AsXpath(path), -Target: s.client.GetTarget(), -} - -if _, _, err = s.client.Communicate(ctx, cmd, true, normalizer); err != nil { -if err.Error() == "No such node" && action == "show" { -return nil, errors.ObjectNotFound() -} -return nil, err -} -} - -list, err := normalizer.Normalize() -if err != nil { -return nil, err -} else if len(list) != 1 { -return nil, fmt.Errorf("expected to %q 1 entry, got %d", action, len(list)) -} + {{- else}} + path, err := loc.XpathWithComponents(vn) + {{- end}} + if err != nil { + return nil, err + } -return list[0], nil + return s.ReadWithXpath(ctx, util.AsXpath(path), action) } {{ $object := "Config" }} @@ -376,138 +375,127 @@ return list[0], nil {{- end }} {{- if .Entry }} - // Update updates the given config object, then returns the result. - func (s *Service) Update(ctx context.Context, loc Location, entry *{{ $object }}, name string) (*{{ $object }}, error) { - {{- if $.Spec.Params.uuid}} - return s.update(ctx, loc, entry, name, true) - {{- else}} - return s.update(ctx, loc, entry, name) - {{- end}} - } +func (s *Service) Update(ctx context.Context, loc Location, entry *{{ $object }}, name string) (*{{ $object }}, error) { + if entry.Name == "" { + return nil, errors.NameNotSpecifiedError + } - {{- if $.Spec.Params.uuid}} - // UpdateById updates the given config object, then returns the result. - func (s *Service) UpdateById(ctx context.Context, loc Location, entry *{{ $object }}, uuid string) (*{{ $object }}, error) { - return s.update(ctx, loc, entry, uuid, false) - } - {{- end}} + xpath, err := loc.XpathWithComponents(s.client.Versioning(), entry.Name) + if err != nil { + return nil, err + } - {{- if $.Spec.Params.uuid}} - func (s *Service) update(ctx context.Context, loc Location, entry *{{ $object }}, value string, byName bool) (*{{ $object }}, error) { - {{- if .Entry }} - if byName && entry.Name == "" { - return nil, errors.NameNotSpecifiedError - } - if !byName && value == "" { - return nil, errors.UuidNotSpecifiedError - } - {{- end }} - {{- else}} - func (s *Service) update(ctx context.Context, loc Location, entry *{{ $object }}, value string) (*{{ $object }}, error) { - {{- if .Entry }} - if entry.Name == "" { - return nil, errors.NameNotSpecifiedError - } - {{- end }} - {{- end}} + err = s.UpdateWithXpath(ctx, util.AsXpath(xpath), entry, name) + if err != nil { + return nil, err + } + + return s.ReadWithXpath(ctx, util.AsXpath(xpath), "get") +} +{{- else }} +func (s *Service) Update(ctx context.Context, loc Location, entry *{{ $object }}, name string) (*{{ $object }}, error) { + xpath, err := loc.XpathWithComponents(s.client.Versioning()) + if err != nil { + return nil, err + } + + err = s.UpdateWithXpath(ctx, util.AsXpath(xpath), entry) + if err != nil { + return nil, err + } + + return s.ReadWithXpath(ctx, util.AsXpath(xpath), "get") +} +{{- end }} + +{{- if .Entry }} +func (s *Service) UpdateWithXpath(ctx context.Context, xpath string, entry *{{ $object }}, name string) error { {{- else }} -func (s *Service) Update(ctx context.Context, loc Location, entry *{{ $object }}) (*{{ $object }}, error) { +func (s *Service) UpdateWithXpath(ctx context.Context, xpath string, entry *{{ $object }}) error { {{- end }} + vn := s.client.Versioning() + updates := xmlapi.NewMultiConfig(2) + specifier, _, err := Versioning(vn) + if err != nil { + return err + } - vn := s.client.Versioning() - updates := xmlapi.NewMultiConfig(2) - specifier, _, err := Versioning(vn) - if err != nil { - return nil, err - } + var old *{{ $object }} {{- if .Entry }} - var old *{{ $object }} - {{- if $.Spec.Params.uuid}} - if byName { - {{- end}} - if value != "" && value != entry.Name { - path, err := loc.XpathWithEntryName(vn, value) - if err != nil { - return nil, err - } + if name != "" && name != entry.Name { + old, err = s.ReadWithXpath(ctx, xpath, "get") + if err != nil { + return err + } - old, err = s.Read(ctx, loc, value, "get") + if old != nil { + return errors.ObjectExists + } - updates.Add(&xmlapi.Config{ - Action: "rename", - Xpath: util.AsXpath(path), - NewName: entry.Name, - Target: s.client.GetTarget(), - }) - } else { - old, err = s.Read(ctx, loc, entry.Name, "get") - } - {{- if $.Spec.Params.uuid}} - } else { - old, err = s.ReadById(ctx, loc, value, "get") - } - {{- end}} - if err != nil { - return nil, err - } else if old == nil { - return nil, fmt.Errorf("previous object doesn't exist for update") - } - {{- else }} - var old *{{ $object }} - old, err = s.Read(ctx, loc, "get") - if err != nil { - return nil, err - } else if old == nil { - return nil, fmt.Errorf("previous object doesn't exist for update") - } - {{- end }} + updates.Add(&xmlapi.Config{ + Action: "rename", + Xpath: util.AsXpath(xpath), + NewName: entry.Name, + Target: s.client.GetTarget(), + }) + } else { + old, err = s.ReadWithXpath(ctx, xpath, "get") + if err != nil { + return err + } - {{- if .Entry }} - if !SpecMatches(entry, old) { - path, err := loc.XpathWithEntryName(vn, entry.Name) - {{- else }} - if !SpecMatches(entry, old) { - path, err := loc.Xpath(vn) - {{- end }} - if err != nil { - return nil, err - } + if old == nil { + return errors.ObjectNotFound() + } + if SpecMatches(entry, old) { + return nil + } - updateSpec, err := specifier(entry) + updateSpec, err := specifier(entry) + if err != nil { + return err + } + + updates.Add(&xmlapi.Config{ + Action: "edit", + Xpath: util.AsXpath(xpath), + Element: updateSpec, + Target: s.client.GetTarget(), + }) + } + {{- else }} + old, err = s.ReadWithXpath(ctx, xpath, "get") if err != nil { - return nil, err - } + return err + } - updates.Add(&xmlapi.Config{ - Action: "edit", - Xpath: util.AsXpath(path), - Element: updateSpec, - Target: s.client.GetTarget(), - }) - } + if SpecMatches(entry, old) { + return nil + } - if len(updates.Operations) != 0 { - if _, _, _, err = s.client.MultiConfig(ctx, updates, false, nil); err != nil { - return nil, err - } - } + updateSpec, err := specifier(entry) + if err != nil { + return err + } - {{- if .Entry }} - {{- if $.Spec.Params.uuid}} - if byName { - return s.Read(ctx, loc, entry.Name, "get") - } else { - return s.ReadById(ctx, loc, value, "get") - } - {{- else}} - return s.Read(ctx, loc, entry.Name, "get") - {{- end}} - {{- else }} - return s.Read(ctx, loc, "get") + updates.Add(&xmlapi.Config{ + Action: "edit", + Xpath: util.AsXpath(xpath), + Element: updateSpec, + Target: s.client.GetTarget(), + }) {{- end }} + + if len(updates.Operations) != 0 { + if _, _, _, err = s.client.MultiConfig(ctx, updates, false, nil); err != nil { + return err + } + } + + return nil } // Delete deletes the given item. @@ -585,24 +573,16 @@ vn := s.client.Versioning() var err error deletes := xmlapi.NewMultiConfig(len(values)) {{- if .Imports }} - err = s.unimportFromLocations(ctx, loc, importLocations, values) + err = s.UnimportFromLocations(ctx, loc, importLocations, values) if err != nil { return err } {{- end }} for _, value := range values { var path []string - {{- if $.Spec.Params.uuid}} - if byName { - path, err = loc.XpathWithEntryName(vn, value) - } else { - path, err = loc.XpathWithUuid(vn, value) - } - {{- else}} - path, err = loc.XpathWithEntryName(vn, value) - {{- end}} + path, err = loc.XpathWithComponents(vn, util.AsEntryXpath(value)) {{- else}} - path, err := loc.Xpath(vn) + path, err := loc.XpathWithComponents(vn) {{- end}} if err != nil { return err @@ -725,85 +705,75 @@ func (s *Service) RemoveFromImport(ctx context.Context, loc Location, entry Entr {{- end}} {{- if .Entry}} - // List returns a list of objects using the given action ("get" or "show"). - // Params filter and quote are for client side filtering. - func (s *Service) List(ctx context.Context, loc Location, action, filter, quote string) ([]*Entry, error) { - return s.list(ctx, loc, action, filter, quote, false) - } - - // ListFromConfig returns a list of objects at the given location. - // Requires that client.LoadPanosConfig() has been invoked. - // Params filter and quote are for client side filtering. - func (s *Service) ListFromConfig(ctx context.Context, loc Location, filter, quote string) ([]*Entry, error) { - return s.list(ctx, loc, "", filter, quote, true) - } - - func (s *Service) list(ctx context.Context, loc Location, action, filter, quote string, usePanosConfig bool) ([]*Entry, error) { - var err error +// List returns a list of objects using the given action ("get" or "show"). +// Params filter and quote are for client side filtering. +func (s *Service) List(ctx context.Context, loc Location, action, filter, quote string) ([]*Entry, error) { + return s.list(ctx, loc, action, filter, quote) +} - var logic *filtering.Group - if filter != "" { - logic, err = filtering.Parse(filter, quote) - if err != nil { - return nil, err - } - } +func (s *Service) list(ctx context.Context, loc Location, action, filter, quote string) ([]*Entry, error) { + xpath, err := loc.XpathWithComponents(s.client.Versioning(), util.AsEntryXpath("")) + if err != nil { + return nil, err + } - vn := s.client.Versioning() + return s.ListWithXpath(ctx, util.AsXpath(xpath), action, filter, quote) +} - _, normalizer, err := Versioning(vn) - if err != nil { - return nil, err - } +func (s *Service) ListWithXpath(ctx context.Context, xpath string, action, filter, quote string) ([]*Entry, error) { + var logic *filtering.Group + if filter != "" { + var err error + logic, err = filtering.Parse(filter, quote) + if err != nil { + return nil, err + } + } - path, err := loc.XpathWithEntryName(vn, "") - if err != nil { - return nil, err - } + vn := s.client.Versioning() - if usePanosConfig { - if _, err = s.client.ReadFromConfig(ctx, path, false, normalizer); err != nil { - return nil, err - } - } else{ - cmd := &xmlapi.Config{ - Action: action, - Xpath: util.AsXpath(path), - Target: s.client.GetTarget(), - } + _, normalizer, err := Versioning(vn) + if err != nil { + return nil, err + } - if _, _, err = s.client.Communicate(ctx, cmd, true, normalizer); err != nil { - if err.Error() == "No such node" && action == "show" { - return nil, nil - } - return nil, err - } - } + cmd := &xmlapi.Config{ + Action: action, + Xpath: util.AsXpath(xpath), + Target: s.client.GetTarget(), + } - listing, err := normalizer.Normalize() - if err != nil || logic == nil { - return listing, err - } + if _, _, err = s.client.Communicate(ctx, cmd, true, normalizer); err != nil { + if err.Error() == "No such node" && action == "show" { + return nil, nil + } + return nil, err + } - filtered := make([]*Entry, 0, len(listing)) - for _, x := range listing { - ok, err := logic.Matches(x) - if err != nil { - return nil, err - } - if ok { - filtered = append(filtered, x) - } - } + listing, err := normalizer.Normalize() + if err != nil || logic == nil { + return listing, err + } - return filtered, nil - } + filtered := make([]*Entry, 0, len(listing)) + for _, x := range listing { + ok, err := logic.Matches(x) + if err != nil { + return nil, err + } + if ok { + filtered = append(filtered, x) + } + } + + return filtered, nil +} - {{- if $.Spec.Params.uuid}} - // MoveGroup arranges the given rules in the order specified. - // Any rule with a UUID specified is ignored. - // Only the rule names are considered for the purposes of the rule placement. - func (s *Service) MoveGroup(ctx context.Context, loc Location, position movement.Position, entries []*Entry, batchSize int) error { +{{- if $.Spec.Params.uuid}} +// MoveGroup arranges the given rules in the order specified. +// Any rule with a UUID specified is ignored. +// Only the rule names are considered for the purposes of the rule placement. +func (s *Service) MoveGroup(ctx context.Context, loc Location, position movement.Position, entries []*Entry, batchSize int) error { if len(entries) == 0 { return nil } @@ -823,7 +793,7 @@ func (s *Service) RemoveFromImport(ctx context.Context, loc Location, entry Entr updates := xmlapi.NewChunkedMultiConfig(len(movements), batchSize) for _, elt := range movements { - path, err := loc.XpathWithEntryName(s.client.Versioning(), elt.Movable.EntryName()) + path, err := loc.XpathWithComponents(s.client.Versioning(), util.AsEntryXpath(elt.Movable.EntryName())) if err != nil { return err } @@ -886,7 +856,7 @@ func (s *Service) RemoveFromImport(ctx context.Context, loc Location, entry Entr vn := s.client.Versioning() - path, err := loc.XpathWithEntryName(vn, name) + path, err := loc.XpathWithComponents(vn, name) if err != nil { return err } @@ -911,7 +881,7 @@ func (s *Service) RemoveFromImport(ctx context.Context, loc Location, entry Entr vn := s.client.Versioning() - path, err := loc.XpathWithEntryName(vn, name) + path, err := loc.XpathWithComponents(vn, name) if err != nil { return "", err }