diff --git a/assets/terraform/internal/manager/entry_test.go b/assets/terraform/internal/manager/entry_test.go index 6e6a6af6..a2849828 100644 --- a/assets/terraform/internal/manager/entry_test.go +++ b/assets/terraform/internal/manager/entry_test.go @@ -247,6 +247,63 @@ var _ = Describe("Entry", func() { Expect(client.list()).To(MatchEntries(planEntries)) }) }) + + Context("when renaming an existing entry", func() { + It("should rename the entry on the server", func() { + stateEntries := []*MockEntryObject{{Name: "1", Value: "A"}, {Name: "2", Value: "B"}, {Name: "3", Value: "C"}} + planEntries := []*MockEntryObject{{Name: "1", Value: "A"}, {Name: "two", Value: "B"}, {Name: "3", Value: "C"}} + + processed, err := sdk.UpdateMany(ctx, location, []string{}, stateEntries, planEntries) + + Expect(err).ToNot(HaveOccurred()) + Expect(processed).To(MatchEntries(planEntries)) + Expect(client.list()).To(MatchEntries(planEntries)) + + Expect(client.MultiConfigOpers).To(HaveLen(1)) + Expect(client.MultiConfigOpers[0]).To(ContainElement( + MultiConfigOper{Operation: MultiConfigOperRename, EntryName: "2", NewName: "two"}, + )) + }) + }) + + Context("with combined rename, delete, and add operations", func() { + BeforeEach(func() { + existing = []*MockEntryObject{ + {Name: "1", Value: "A"}, + {Name: "2", Value: "B"}, + {Name: "3", Value: "C"}, + {Name: "4", Value: "D"}, + } + }) + + It("should correctly apply all changes", func() { + stateEntries := []*MockEntryObject{ + {Name: "1", Value: "A"}, + {Name: "2", Value: "B"}, + {Name: "3", Value: "C"}, + {Name: "4", Value: "D"}, + } + planEntries := []*MockEntryObject{ + {Name: "one", Value: "A"}, // renamed from "1" + {Name: "2", Value: "B"}, // unchanged + {Name: "three", Value: "C"}, // renamed from "3" + {Name: "5", Value: "E"}, // added (and "4" deleted) + } + + processed, err := sdk.UpdateMany(ctx, location, []string{}, stateEntries, planEntries) + + Expect(err).ToNot(HaveOccurred()) + Expect(processed).To(HaveLen(4)) + Expect(processed).To(MatchEntries(planEntries)) + + Expect(client.MultiConfigOpers[0]).To(ContainElements([]MultiConfigOper{ + {Operation: MultiConfigOperRename, EntryName: "1", NewName: "one"}, + {Operation: MultiConfigOperRename, EntryName: "3", NewName: "three"}, + {Operation: MultiConfigOperDelete, EntryName: "4"}, + {Operation: MultiConfigOperEdit, EntryName: "5"}, + })) + }) + }) }) Context("Delete()", func() { diff --git a/assets/terraform/test/resource_addresses_test.go b/assets/terraform/test/resource_addresses_test.go index 8fb8d87c..836a3bae 100644 --- a/assets/terraform/test/resource_addresses_test.go +++ b/assets/terraform/test/resource_addresses_test.go @@ -68,7 +68,7 @@ func (o *expectServerAddressObjects) CheckState(ctx context.Context, req statech } } -func TestAccAddresses(t *testing.T) { +func TestAccAddresses_Basic(t *testing.T) { t.Parallel() nameSuffix := acctest.RandStringFromCharSet(6, acctest.CharSetAlphaNum) @@ -115,6 +115,9 @@ func TestAccAddresses(t *testing.T) { fmt.Sprintf("%s-fqdn", prefix): config.ObjectVariable(map[string]config.Variable{ "fqdn": config.StringVariable("example.com"), }), + fmt.Sprintf("%s-ip-netmask-2", prefix): config.ObjectVariable(map[string]config.Variable{ + "ip_netmask": config.StringVariable("10.0.0.1/32"), + }), }), }, ConfigStateChecks: []statecheck.StateCheck{ @@ -161,9 +164,18 @@ func TestAccAddresses(t *testing.T) { "ip_wildcard": knownvalue.Null(), "fqdn": knownvalue.StringExact("example.com"), }), + fmt.Sprintf("%s-ip-netmask-2", prefix): knownvalue.ObjectExact(map[string]knownvalue.Check{ + "tags": knownvalue.Null(), + "description": knownvalue.Null(), + "disable_override": knownvalue.Null(), + "ip_netmask": knownvalue.StringExact("10.0.0.1/32"), + "ip_range": knownvalue.Null(), + "ip_wildcard": knownvalue.Null(), + "fqdn": knownvalue.Null(), + }), }), ), - ExpectServerAddressObjects(*location, prefix, []string{"ip-netmask", "ip-range", "ip-wildcard", "fqdn"}), + ExpectServerAddressObjects(*location, prefix, []string{"ip-netmask", "ip-range", "ip-wildcard", "fqdn", "ip-netmask-2"}), }, }, { @@ -178,6 +190,9 @@ func TestAccAddresses(t *testing.T) { fmt.Sprintf("%s-fqdn2", prefix): config.ObjectVariable(map[string]config.Variable{ "fqdn": config.StringVariable("example.com"), }), + fmt.Sprintf("%s-ip-netmask-2", prefix): config.ObjectVariable(map[string]config.Variable{ + "ip_netmask": config.StringVariable("10.0.0.2/32"), + }), }), }, ConfigStateChecks: []statecheck.StateCheck{ @@ -204,9 +219,18 @@ func TestAccAddresses(t *testing.T) { "ip_wildcard": knownvalue.Null(), "fqdn": knownvalue.StringExact("example.com"), }), + fmt.Sprintf("%s-ip-netmask-2", prefix): knownvalue.ObjectExact(map[string]knownvalue.Check{ + "tags": knownvalue.Null(), + "description": knownvalue.Null(), + "disable_override": knownvalue.Null(), + "ip_netmask": knownvalue.StringExact("10.0.0.2/32"), + "ip_range": knownvalue.Null(), + "ip_wildcard": knownvalue.Null(), + "fqdn": knownvalue.Null(), + }), }), ), - ExpectServerAddressObjects(*location, prefix, []string{"ip-range", "fqdn2"}), + ExpectServerAddressObjects(*location, prefix, []string{"ip-range", "fqdn2", "ip-netmask-2"}), }, }, { @@ -423,3 +447,156 @@ func TestAccAddresses_MultipleResources(t *testing.T) { }, }) } + +const testAccAddresses_Rename_Tmpl = ` +variable prefix { type = string } +variable "addresses" { + type = map(object({ + ip_netmask = string + })) +} + +resource "panos_device_group" "example" { + location = { panorama = {} } + name = var.prefix +} + +resource "panos_addresses" "addresses" { + location = { device_group = { name = panos_device_group.example.name } } + + addresses = { for name, value in var.addresses : name => { + ip_netmask = value.ip_netmask + }} +} +` + +func TestAccAddresses_Rename(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 + + addressCheck := func(name string, ipNetmask string) knownvalue.Check { + return knownvalue.ObjectExact(map[string]knownvalue.Check{ + "tags": knownvalue.Null(), + "description": knownvalue.Null(), + "disable_override": knownvalue.Null(), + "ip_netmask": knownvalue.StringExact(ipNetmask), + "ip_range": knownvalue.Null(), + "ip_wildcard": knownvalue.Null(), + "fqdn": knownvalue.Null(), + }) + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProviders, + Steps: []resource.TestStep{ + // Step 1: Create 4 addresses + { + Config: testAccAddresses_Rename_Tmpl, + ConfigVariables: map[string]config.Variable{ + "prefix": config.StringVariable(prefix), + "addresses": config.MapVariable(map[string]config.Variable{ + fmt.Sprintf("%s-addr-1", prefix): config.ObjectVariable(map[string]config.Variable{ + "ip_netmask": config.StringVariable("10.0.0.1/32"), + }), + fmt.Sprintf("%s-addr-2", prefix): config.ObjectVariable(map[string]config.Variable{ + "ip_netmask": config.StringVariable("10.0.0.2/32"), + }), + fmt.Sprintf("%s-addr-3", prefix): config.ObjectVariable(map[string]config.Variable{ + "ip_netmask": config.StringVariable("10.0.0.3/32"), + }), + fmt.Sprintf("%s-addr-4", prefix): config.ObjectVariable(map[string]config.Variable{ + "ip_netmask": config.StringVariable("10.0.0.4/32"), + }), + }), + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "panos_addresses.addresses", + tfjsonpath.New("addresses"), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + fmt.Sprintf("%s-addr-1", prefix): addressCheck("addr-1", "10.0.0.1/32"), + fmt.Sprintf("%s-addr-2", prefix): addressCheck("addr-2", "10.0.0.2/32"), + fmt.Sprintf("%s-addr-3", prefix): addressCheck("addr-3", "10.0.0.3/32"), + fmt.Sprintf("%s-addr-4", prefix): addressCheck("addr-4", "10.0.0.4/32"), + }), + ), + ExpectServerAddressObjects(*location, prefix, []string{"addr-1", "addr-2", "addr-3", "addr-4"}), + }, + }, + // Step 2: Rename addr-1 -> addr-1-renamed, addr-3 -> addr-3-renamed + { + Config: testAccAddresses_Rename_Tmpl, + ConfigVariables: map[string]config.Variable{ + "prefix": config.StringVariable(prefix), + "addresses": config.MapVariable(map[string]config.Variable{ + fmt.Sprintf("%s-addr-1-renamed", prefix): config.ObjectVariable(map[string]config.Variable{ + "ip_netmask": config.StringVariable("10.0.0.1/32"), + }), + fmt.Sprintf("%s-addr-2", prefix): config.ObjectVariable(map[string]config.Variable{ + "ip_netmask": config.StringVariable("10.0.0.2/32"), + }), + fmt.Sprintf("%s-addr-3-renamed", prefix): config.ObjectVariable(map[string]config.Variable{ + "ip_netmask": config.StringVariable("10.0.0.3/32"), + }), + fmt.Sprintf("%s-addr-4", prefix): config.ObjectVariable(map[string]config.Variable{ + "ip_netmask": config.StringVariable("10.0.0.4/32"), + }), + }), + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "panos_addresses.addresses", + tfjsonpath.New("addresses"), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + fmt.Sprintf("%s-addr-1-renamed", prefix): addressCheck("addr-1-renamed", "10.0.0.1/32"), + fmt.Sprintf("%s-addr-2", prefix): addressCheck("addr-2", "10.0.0.2/32"), + fmt.Sprintf("%s-addr-3-renamed", prefix): addressCheck("addr-3-renamed", "10.0.0.3/32"), + fmt.Sprintf("%s-addr-4", prefix): addressCheck("addr-4", "10.0.0.4/32"), + }), + ), + ExpectServerAddressObjects(*location, prefix, []string{"addr-1-renamed", "addr-2", "addr-3-renamed", "addr-4"}), + }, + }, + // Step 3: Swap — addr-1-renamed gets addr-3-renamed's value and vice versa. + { + Config: testAccAddresses_Rename_Tmpl, + ConfigVariables: map[string]config.Variable{ + "prefix": config.StringVariable(prefix), + "addresses": config.MapVariable(map[string]config.Variable{ + fmt.Sprintf("%s-addr-1-renamed", prefix): config.ObjectVariable(map[string]config.Variable{ + "ip_netmask": config.StringVariable("10.0.0.3/32"), + }), + fmt.Sprintf("%s-addr-2", prefix): config.ObjectVariable(map[string]config.Variable{ + "ip_netmask": config.StringVariable("10.0.0.2/32"), + }), + fmt.Sprintf("%s-addr-3-renamed", prefix): config.ObjectVariable(map[string]config.Variable{ + "ip_netmask": config.StringVariable("10.0.0.1/32"), + }), + fmt.Sprintf("%s-addr-4", prefix): config.ObjectVariable(map[string]config.Variable{ + "ip_netmask": config.StringVariable("10.0.0.4/32"), + }), + }), + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "panos_addresses.addresses", + tfjsonpath.New("addresses"), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + fmt.Sprintf("%s-addr-1-renamed", prefix): addressCheck("addr-1-renamed", "10.0.0.3/32"), + fmt.Sprintf("%s-addr-2", prefix): addressCheck("addr-2", "10.0.0.2/32"), + fmt.Sprintf("%s-addr-3-renamed", prefix): addressCheck("addr-3-renamed", "10.0.0.1/32"), + fmt.Sprintf("%s-addr-4", prefix): addressCheck("addr-4", "10.0.0.4/32"), + }), + ), + ExpectServerAddressObjects(*location, prefix, []string{"addr-1-renamed", "addr-2", "addr-3-renamed", "addr-4"}), + }, + }, + }, + }) +} diff --git a/assets/terraform/test/resource_dynamic_user_group_test.go b/assets/terraform/test/resource_dynamic_user_group_test.go index 6998a496..0ab365a0 100644 --- a/assets/terraform/test/resource_dynamic_user_group_test.go +++ b/assets/terraform/test/resource_dynamic_user_group_test.go @@ -207,7 +207,7 @@ resource "panos_administrative_tag" "tag2" { } resource "panos_dynamic_user_group" "example" { - depends_on = [panos_administrative_tag.tag1, panos_administrative_tag.tag2] + depends_on = [panos_administrative_tag.tag1, panos_administrative_tag.tag2, panos_device_group.example] location = var.location name = var.prefix diff --git a/assets/terraform/test/resource_firewall_device_test.go b/assets/terraform/test/resource_firewall_device_test.go new file mode 100644 index 00000000..53e6d92d --- /dev/null +++ b/assets/terraform/test/resource_firewall_device_test.go @@ -0,0 +1,165 @@ +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/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +) + +func TestAccFirewallDevice_Basic(t *testing.T) { + t.Parallel() + + // Generate a serial number matching PAN-OS format: 00 + 13 digits + suffix := acctest.RandStringFromCharSet(13, "0123456789") + serialNumber := fmt.Sprintf("00%s", suffix) + + location := config.ObjectVariable(map[string]config.Variable{ + "panorama": config.ObjectVariable(map[string]config.Variable{}), + }) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProviders, + Steps: []resource.TestStep{ + { + Config: firewallDevice_Basic_Tmpl, + ConfigVariables: map[string]config.Variable{ + "serial_number": config.StringVariable(serialNumber), + "location": location, + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "panos_firewall_device.example", + tfjsonpath.New("name"), + knownvalue.StringExact(serialNumber), + ), + statecheck.ExpectKnownValue( + "panos_firewall_device.example", + tfjsonpath.New("auto_push"), + knownvalue.Bool(true), + ), + statecheck.ExpectKnownValue( + "panos_firewall_device.example", + tfjsonpath.New("disable_config_backup"), + knownvalue.Bool(false), + ), + statecheck.ExpectKnownValue( + "panos_firewall_device.example", + tfjsonpath.New("hostname"), + knownvalue.StringExact("fw.example.com"), + ), + statecheck.ExpectKnownValue( + "panos_firewall_device.example", + tfjsonpath.New("ip"), + knownvalue.StringExact("192.0.2.1"), + ), + statecheck.ExpectKnownValue( + "panos_firewall_device.example", + tfjsonpath.New("to_sw_version"), + knownvalue.StringExact("11.0.0"), + ), + statecheck.ExpectKnownValue( + "panos_firewall_device.example", + tfjsonpath.New("vsys"), + knownvalue.Null(), + ), + }, + }, + }, + }) +} + +const firewallDevice_Basic_Tmpl = ` +variable "serial_number" { type = string } +variable "location" { type = any } + +resource "panos_firewall_device" "example" { + location = var.location + name = var.serial_number + + auto_push = true + disable_config_backup = false + hostname = "fw.example.com" + ip = "192.0.2.1" + to_sw_version = "11.0.0" +} +` + +func TestAccFirewallDevice_Vsys(t *testing.T) { + t.Parallel() + + // Generate a serial number matching PAN-OS format: 00 + 13 digits + suffix := acctest.RandStringFromCharSet(13, "0123456789") + serialNumber := fmt.Sprintf("00%s", suffix) + + location := config.ObjectVariable(map[string]config.Variable{ + "panorama": config.ObjectVariable(map[string]config.Variable{}), + }) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProviders, + Steps: []resource.TestStep{ + { + Config: firewallDevice_Vsys_Tmpl, + ConfigVariables: map[string]config.Variable{ + "serial_number": config.StringVariable(serialNumber), + "location": location, + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "panos_firewall_device.example", + tfjsonpath.New("name"), + knownvalue.StringExact(serialNumber), + ), + statecheck.ExpectKnownValue( + "panos_firewall_device.example", + tfjsonpath.New("vsys"), + knownvalue.ListExact([]knownvalue.Check{ + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "name": knownvalue.StringExact("vsys1"), + "tags": knownvalue.ListExact([]knownvalue.Check{ + knownvalue.StringExact("tag1"), + knownvalue.StringExact("tag2"), + }), + }), + knownvalue.ObjectExact(map[string]knownvalue.Check{ + "name": knownvalue.StringExact("vsys2"), + "tags": knownvalue.ListExact([]knownvalue.Check{ + knownvalue.StringExact("tag3"), + }), + }), + }), + ), + }, + }, + }, + }) +} + +const firewallDevice_Vsys_Tmpl = ` +variable "serial_number" { type = string } +variable "location" { type = any } + +resource "panos_firewall_device" "example" { + location = var.location + name = var.serial_number + + vsys = [ + { + name = "vsys1" + tags = ["tag1", "tag2"] + }, + { + name = "vsys2" + tags = ["tag3"] + } + ] +} +` diff --git a/pkg/translate/imports.go b/pkg/translate/imports.go index fcd3529d..c4177663 100644 --- a/pkg/translate/imports.go +++ b/pkg/translate/imports.go @@ -1,6 +1,8 @@ package translate import ( + "strings" + "github.com/paloaltonetworks/pan-os-codegen/pkg/imports" "github.com/paloaltonetworks/pan-os-codegen/pkg/properties" ) @@ -33,8 +35,10 @@ func RenderImports(spec *properties.Normalization, templateTypes ...string) (str case "location": manager.AddStandardImport("fmt", "") manager.AddSdkImport("github.com/PaloAltoNetworks/pango/errors", "") - manager.AddSdkImport("github.com/PaloAltoNetworks/pango/util", "") manager.AddSdkImport("github.com/PaloAltoNetworks/pango/version", "") + if locationsHaveEntryXpath(spec) { + manager.AddSdkImport("github.com/PaloAltoNetworks/pango/util", "") + } if spec.ResourceXpathVariablesWithChecks(true) { manager.AddStandardImport("strings", "") } @@ -66,3 +70,16 @@ func RenderImports(spec *properties.Normalization, templateTypes ...string) (str return manager.RenderImports() } + +// locationsHaveEntryXpath returns true if any location has an XPath element +// containing "Entry", which requires the pango/util import for AsEntryXpath. +func locationsHaveEntryXpath(spec *properties.Normalization) bool { + for _, location := range spec.Locations { + for _, xpath := range location.Xpath { + if strings.Contains(xpath, "Entry") { + return true + } + } + } + return false +} diff --git a/pkg/translate/terraform_provider/entity_generators.go b/pkg/translate/terraform_provider/entity_generators.go index 9ff4bfba..2cdcd5f6 100644 --- a/pkg/translate/terraform_provider/entity_generators.go +++ b/pkg/translate/terraform_provider/entity_generators.go @@ -142,6 +142,17 @@ func (g *GenerateTerraformProvider) generateTerraformEntityTemplate(resourceTyp return g.executeTemplate(template, spec, terraformProvider, resourceTyp, schemaTyp, names) } +func locationVariablesWithDefaults(locations map[string]*properties.Location) bool { + for _, location := range locations { + for _, variable := range location.Vars { + if variable.Default != "" { + return true + } + } + } + return false +} + // GenerateTerraformResource generates a Terraform resource template. func (g *GenerateTerraformProvider) GenerateTerraformResource(resourceTyp properties.ResourceType, spec *properties.Normalization, terraformProvider *properties.TerraformProviderFile) error { names := NewNameProvider(spec, resourceTyp) @@ -325,7 +336,9 @@ func (g *GenerateTerraformProvider) GenerateTerraformResource(resourceTyp proper if len(spec.Locations) > 0 { terraformProvider.ImportManager.AddHashicorpImport("github.com/hashicorp/terraform-plugin-framework/diag", "") - terraformProvider.ImportManager.AddHashicorpImport("github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault", "") + if locationVariablesWithDefaults(spec.Locations) { + terraformProvider.ImportManager.AddHashicorpImport("github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault", "") + } if resourceTyp != properties.ResourceCustom { terraformProvider.ImportManager.AddHashicorpImport("github.com/hashicorp/terraform-plugin-log/tflog", "") } @@ -468,7 +481,9 @@ func (g *GenerateTerraformProvider) GenerateTerraformDataSource(resourceTyp prop terraformProvider.ImportManager.AddHashicorpImport("github.com/hashicorp/terraform-plugin-framework/attr", "") terraformProvider.ImportManager.AddHashicorpImport("github.com/hashicorp/terraform-plugin-framework/diag", "") terraformProvider.ImportManager.AddHashicorpImport("github.com/hashicorp/terraform-plugin-framework/resource/schema", "rsschema") - terraformProvider.ImportManager.AddHashicorpImport("github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault", "") + if locationVariablesWithDefaults(spec.Locations) { + terraformProvider.ImportManager.AddHashicorpImport("github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault", "") + } names := NewNameProvider(spec, resourceTyp) funcMap := template.FuncMap{ diff --git a/specs/panorama/devices.yaml b/specs/panorama/devices.yaml new file mode 100644 index 00000000..1146674f --- /dev/null +++ b/specs/panorama/devices.yaml @@ -0,0 +1,115 @@ +name: mgt-config-devices +terraform_provider_config: + description: Firewall Devices + skip_resource: false + skip_datasource: false + resource_type: entry + resource_variants: + - singular + suffix: firewall_device + plural_description: "" +go_sdk_config: + skip: false + package: + - panorama + - devices +panos_xpath: + path: + - mgt-config + - devices + vars: [] +locations: + - name: panorama + xpath: + path: + - config + vars: [] + description: Located in a panorama. + validators: [] + required: false + read_only: false +entries: + - name: name + description: "" + validators: [] +spec: + params: + - name: auto-push + type: bool + profiles: + - xpath: + - auto-push + validators: [] + spec: {} + description: "" + required: false + - name: disable-config-backup + type: bool + profiles: + - xpath: + - disable-config-backup + validators: [] + spec: {} + description: Enable config back up for this device + required: false + - name: hostname + type: string + profiles: + - xpath: + - hostname + validators: [] + spec: {} + description: ip address + required: false + - name: ip + type: string + profiles: + - xpath: + - ip + validators: [] + spec: {} + description: ip address + required: false + - name: to-sw-version + type: string + profiles: + - xpath: + - to-sw-version + validators: + - type: length + spec: + max: 64 + spec: {} + description: Automatically upgrade software to this version for new deployments + required: false + - name: vsys + type: list + profiles: + - xpath: + - vsys + - entry + type: entry + validators: [] + spec: + type: object + items: + type: object + spec: + params: + - name: tags + type: list + profiles: + - xpath: + - tags + type: member + validators: [] + spec: + type: string + items: + type: string + description: "" + required: false + variants: [] + description: "" + required: false + variants: []