diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index ff12572b..5ae107e5 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -10,7 +10,7 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v2 with: - version: v1.42 + version: v1.43 args: -c .golangci.yml -v markdown-lint: diff --git a/.golangci.yml b/.golangci.yml index 9246375e..df4f2db3 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -19,6 +19,7 @@ linters: - cyclop - forcetypeassert - gomoddirectives + - varnamelen linters-settings: gocognit: # minimal code complexity to report, 30 by default diff --git a/CHANGELOG.md b/CHANGELOG.md index 113a7256..4937bb48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,29 @@ ENHANCEMENTS: BUG FIXES: +## 1.22.0 (November 19, 2021) + +FEATURES: + +* add `junos_access_address_assignment_pool` resource (Fixes parts of [#301](https://github.com/jeremmfr/terraform-provider-junos/issues/301)) +* add `junos_interface_physical_disable` resource (Fixes [#305](https://github.com/jeremmfr/terraform-provider-junos/issues/305)) +* add `junos_interfaces_physical_present` data-source +* add `junos_system_services_dhcp_localserver_group` resource (Fixes parts of [#301](https://github.com/jeremmfr/terraform-provider-junos/issues/301)) + +ENHANCEMENTS: + +* resource/`junos_application`: add `term` block argument (Fixes [#296](https://github.com/jeremmfr/terraform-provider-junos/issues/296)), add `inactivity_timeout_never` argument (Fixes [#308](https://github.com/jeremmfr/terraform-provider-junos/issues/308)) +* resource/`junos_chassis_cluster`: add `control_ports` block argument (Fixes [#304](https://github.com/jeremmfr/terraform-provider-junos/issues/304)) +* resource/`junos_group_dual_system`: add `system.0.inet6_backup_router_address` and `system.0.inet6_backup_router_destination` arguments, add validation on `system.0.backup_router_address` and list of string for `system.0.backup_router_destination` is now unordered (Fixes [#302](https://github.com/jeremmfr/terraform-provider-junos/issues/302)) +* resource/`junos_interface_logical`: add `dhcp` and `dhcpv6_client` block arguments inside `family_inet` and `family_inet6` block arguments (Fixes parts of [#301](https://github.com/jeremmfr/terraform-provider-junos/issues/301)) +* data-source/`junos_interface_logical`: add `dhcp` and `dhcpv6_client` block attributes inside `family_inet` and `family_inet6` block attributes +* resource/`junos_system`: add `ports` block argument (Fixes [#294](https://github.com/jeremmfr/terraform-provider-junos/issues/294)) + +BUG FIXES: + +* resource/`junos_security_idp_custom_attack`: fix validation of IPv6 address for `destination_value`, `extension_header_destination_option_home_address_value` and `source_value` inside `protocol_ipv6` block +* resource/`junos_services_rpm_probe`: fix validation of IPv6 address for `inet6_source_address`, `rpm_scale.0.source_inet6_address_base`, `rpm_scale.0.source_inet6_step`, `rpm_scale.0.target_inet6_address_base` and `rpm_scale.0.target_inet6_step` inside `test` block + ## 1.21.1 (October 22, 2021) BUG FIXES: diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..7d6c7c4e --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +default: install + +.PHONY: install testacc testacc_srx testacc_router testacc_switch +# Install to use dev_overrides in provider_installation of Terraform +install: + go install +# Run acceptance tests +testacc: + cd junos ; TF_ACC=1 go test -v --timeout 0 -coverprofile=../coverage.out $(TESTARGS) + go tool cover -html=coverage.out +testacc/srx: + cd junos ; TESTACC_SRX=1 TF_ACC=1 go test -v --timeout 0 -coverprofile=../coverage_srx.out $(TESTARGS) + go tool cover -html=coverage_srx.out +testacc/router: + cd junos ; TESTACC_ROUTER=1 TF_ACC=1 go test -v --timeout 0 -coverprofile=../coverage_router.out $(TESTARGS) + go tool cover -html=coverage_router.out +testacc/switch: + cd junos ; TESTACC_SWITCH=1 TF_ACC=1 go test -v --timeout 0 -coverprofile=../coverage_switch.out $(TESTARGS) + go tool cover -html=coverage_switch.out diff --git a/go.mod b/go.mod index c3486e0d..06961ce3 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.16 require ( github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/terraform-plugin-sdk/v2 v2.8.0 - github.com/jeremmfr/go-netconf v0.4.0 + github.com/jeremmfr/go-netconf v0.4.1 github.com/jeremmfr/go-utils v0.3.0 github.com/jeremmfr/junosdecode v1.1.0 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 diff --git a/go.sum b/go.sum index fe443bf8..cabe173a 100644 --- a/go.sum +++ b/go.sum @@ -203,8 +203,8 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jeremmfr/go-netconf v0.4.0 h1:MYrbA5keH7bsBpgcTA0MVvIg6oiA2bgdsJkdE2F3I7Y= -github.com/jeremmfr/go-netconf v0.4.0/go.mod h1:Nd1B4xd6miAKQzNxe0m3wddyLdAeKK+N04MOAiVhE0c= +github.com/jeremmfr/go-netconf v0.4.1 h1:cFWw/x/3AI3XlmTft8G1qMI3+wwETQ2S1cbr0K2Ejl4= +github.com/jeremmfr/go-netconf v0.4.1/go.mod h1:Nd1B4xd6miAKQzNxe0m3wddyLdAeKK+N04MOAiVhE0c= github.com/jeremmfr/go-utils v0.3.0 h1:f69G5Z6EnHO2bNEW+aeWB22P/4/plS1DZ2UZK7gd+WI= github.com/jeremmfr/go-utils v0.3.0/go.mod h1:K0lGadiSvg9OKGJnW4Bs3t18/VApp/6x2+BV93Sts2M= github.com/jeremmfr/junosdecode v1.1.0 h1:Os8QeOzyL+BPuDZJMjyJgz4QPOgA8EChgKB2Ih5wwCc= diff --git a/junos/data_source_interface_logical.go b/junos/data_source_interface_logical.go index c36854ff..79b74494 100644 --- a/junos/data_source_interface_logical.go +++ b/junos/data_source_interface_logical.go @@ -156,6 +156,86 @@ func dataSourceInterfaceLogical() *schema.Resource { }, }, }, + "dhcp": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "client_identifier_ascii": { + Type: schema.TypeString, + Computed: true, + }, + "client_identifier_hexadecimal": { + Type: schema.TypeString, + Computed: true, + }, + "client_identifier_prefix_hostname": { + Type: schema.TypeBool, + Computed: true, + }, + "client_identifier_prefix_routing_instance_name": { + Type: schema.TypeBool, + Computed: true, + }, + "client_identifier_use_interface_description": { + Type: schema.TypeString, + Computed: true, + }, + "client_identifier_userid_ascii": { + Type: schema.TypeString, + Computed: true, + }, + "client_identifier_userid_hexadecimal": { + Type: schema.TypeString, + Computed: true, + }, + "force_discover": { + Type: schema.TypeBool, + Computed: true, + }, + "lease_time": { + Type: schema.TypeInt, + Computed: true, + }, + "lease_time_infinite": { + Type: schema.TypeBool, + Computed: true, + }, + "metric": { + Type: schema.TypeInt, + Computed: true, + }, + "no_dns_install": { + Type: schema.TypeBool, + Computed: true, + }, + "options_no_hostname": { + Type: schema.TypeBool, + Computed: true, + }, + "retransmission_attempt": { + Type: schema.TypeInt, + Computed: true, + }, + "retransmission_interval": { + Type: schema.TypeInt, + Computed: true, + }, + "server_address": { + Type: schema.TypeString, + Computed: true, + }, + "update_server": { + Type: schema.TypeBool, + Computed: true, + }, + "vendor_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, "filter_input": { Type: schema.TypeString, Computed: true, @@ -303,7 +383,65 @@ func dataSourceInterfaceLogical() *schema.Resource { }, "dad_disable": { Type: schema.TypeBool, - Optional: true, + Computed: true, + }, + "dhcpv6_client": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "client_identifier_duid_type": { + Type: schema.TypeString, + Computed: true, + }, + "client_type": { + Type: schema.TypeString, + Computed: true, + }, + "client_ia_type_na": { + Type: schema.TypeBool, + Computed: true, + }, + "client_ia_type_pd": { + Type: schema.TypeBool, + Computed: true, + }, + "no_dns_install": { + Type: schema.TypeBool, + Computed: true, + }, + "prefix_delegating_preferred_prefix_length": { + Type: schema.TypeInt, + Computed: true, + }, + "prefix_delegating_sub_prefix_length": { + Type: schema.TypeInt, + Computed: true, + }, + "rapid_commit": { + Type: schema.TypeBool, + Computed: true, + }, + "req_option": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "retransmission_attempt": { + Type: schema.TypeInt, + Computed: true, + }, + "update_router_advertisement_interface": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "update_server": { + Type: schema.TypeBool, + Computed: true, + }, + }, + }, }, "filter_input": { Type: schema.TypeString, diff --git a/junos/data_source_interfaces_physical_present.go b/junos/data_source_interfaces_physical_present.go new file mode 100644 index 00000000..31472fb3 --- /dev/null +++ b/junos/data_source_interfaces_physical_present.go @@ -0,0 +1,143 @@ +package junos + +import ( + "context" + "encoding/xml" + "fmt" + "regexp" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +type interfacesPresentOpts struct { + interfaceNames []string + interfaceStatuses []map[string]interface{} +} + +func dataSourceInterfacesPhysicalPresent() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceInterfacesPhysicalPresentRead, + Schema: map[string]*schema.Schema{ + "match_name": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if _, err := regexp.Compile(value); err != nil { + errors = append(errors, fmt.Errorf( + "%q for %q is not valid regexp", value, k)) + } + + return + }, + }, + "match_admin_up": { + Type: schema.TypeBool, + Optional: true, + }, + "match_oper_up": { + Type: schema.TypeBool, + Optional: true, + }, + "interface_names": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "interface_statuses": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "admin_status": { + Type: schema.TypeString, + Computed: true, + }, + "oper_status": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceInterfacesPhysicalPresentRead(ctx context.Context, + d *schema.ResourceData, m interface{}) diag.Diagnostics { + sess := m.(*Session) + jnprSess, err := sess.startNewSession() + if err != nil { + return diag.FromErr(err) + } + defer sess.closeSession(jnprSess) + mutex.Lock() + iPresent, err := searchInterfacesPhysicalPresent(d, m, jnprSess) + mutex.Unlock() + if err != nil { + return diag.FromErr(err) + } + if tfErr := d.Set("interface_names", iPresent.interfaceNames); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("interface_statuses", iPresent.interfaceStatuses); tfErr != nil { + panic(tfErr) + } + idString := "match=" + d.Get("match_name").(string) + if d.Get("match_admin_up").(bool) { + idString += idSeparator + "admin_up=true" + } + if d.Get("match_oper_up").(bool) { + idString += idSeparator + "oper_up=true" + } + d.SetId(idString) + + return nil +} + +func searchInterfacesPhysicalPresent( + d *schema.ResourceData, m interface{}, jnprSess *NetconfObject) (interfacesPresentOpts, error) { + sess := m.(*Session) + var result interfacesPresentOpts + replyData, err := sess.commandXML(rpcGetInterfaceInformationTerse, jnprSess) + if err != nil { + return result, err + } + var iface getInterfaceTerseReply + err = xml.Unmarshal([]byte(replyData), &iface.InterfaceInfo) + if err != nil { + return result, fmt.Errorf("failed to xml unmarshal reply data %s : %w", replyData, err) + } + for _, iFace := range iface.InterfaceInfo.PhysicalInterface { + if mName := d.Get("match_name").(string); mName != "" { + matched, err := regexp.MatchString(mName, strings.Trim(iFace.Name, " \n\t")) + if err != nil { + return result, fmt.Errorf("failed to regexp with %s : %w", mName, err) + } + if !matched { + continue + } + } + if d.Get("match_admin_up").(bool) && strings.Trim(iFace.AdminStatus, " \n\t") != "up" { + continue + } + if d.Get("match_oper_up").(bool) && strings.Trim(iFace.OperStatus, " \n\t") != "up" { + continue + } + result.interfaceNames = append(result.interfaceNames, strings.Trim(iFace.Name, " \n\t")) + result.interfaceStatuses = append(result.interfaceStatuses, map[string]interface{}{ + "name": strings.Trim(iFace.Name, " \n\t"), + "admin_status": strings.Trim(iFace.AdminStatus, " \n\t"), + "oper_status": strings.Trim(iFace.OperStatus, " \n\t"), + }) + } + + return result, nil +} diff --git a/junos/data_source_interfaces_physical_present_test.go b/junos/data_source_interfaces_physical_present_test.go new file mode 100644 index 00000000..86b95014 --- /dev/null +++ b/junos/data_source_interfaces_physical_present_test.go @@ -0,0 +1,198 @@ +package junos_test + +import ( + "os" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +// export TESTACC_INTERFACE= for choose interface available else it's ge-0/0/3. +func TestAccDataSourceInterfacesPhysicalPresent_basic(t *testing.T) { + var testaccInterface string + if os.Getenv("TESTACC_INTERFACE") != "" { + testaccInterface = os.Getenv("TESTACC_INTERFACE") + } else { + if os.Getenv("TESTACC_SWITCH") != "" { + testaccInterface = defaultInterfaceSwitchTestAcc + } else { + testaccInterface = defaultInterfaceTestAcc + } + } + if os.Getenv("TESTACC_SWITCH") != "" { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceInterfacesPhysicalPresentPreSwitch(testaccInterface), + }, + { + Config: testAccDataSourceInterfacesPhysicalPresentPreSwitch(testaccInterface), + Destroy: true, + }, + { + Config: testAccDataSourceInterfacesPhysicalPresentConfig(testaccInterface), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckTypeSetElemAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresent", + "interface_names.*", testaccInterface), + resource.TestCheckTypeSetElemAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresent", + "interface_names.*", "dsc"), + resource.TestCheckTypeSetElemAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresent", + "interface_names.*", "lo0"), + ), + }, + { + Config: testAccDataSourceInterfacesPhysicalPresentConfigMatch(testaccInterface), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckTypeSetElemAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresentEth", + "interface_names.*", testaccInterface), + resource.TestCheckResourceAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresentEth003", + "interface_names.#", "1"), + resource.TestCheckResourceAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresentEth003", + "interface_statuses.#", "1"), + resource.TestCheckResourceAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresentEth003", + "interface_statuses.0.name", testaccInterface), + resource.TestCheckResourceAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresentEth003", + "interface_statuses.0.admin_status", "up"), + resource.TestCheckResourceAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresentEth003AdmUp", + "interface_names.#", "1"), + resource.TestCheckResourceAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresentLo0", + "interface_names.#", "1"), + ), + }, + { + Config: testAccDataSourceInterfacesPhysicalPresentConfigMatch2(testaccInterface), + }, + { + Config: testAccDataSourceInterfacesPhysicalPresentConfigMatch2(testaccInterface), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresentEth003AdmUp", + "interface_names.#", "0"), + ), + }, + }, + PreventPostDestroyRefresh: true, + }) + } else { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceInterfacesPhysicalPresentPreSwitch(testaccInterface), + }, + { + Config: testAccDataSourceInterfacesPhysicalPresentPreSwitch(testaccInterface), + Destroy: true, + }, + { + Config: testAccDataSourceInterfacesPhysicalPresentConfig(testaccInterface), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckTypeSetElemAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresent", + "interface_names.*", testaccInterface), + resource.TestCheckTypeSetElemAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresent", + "interface_names.*", "dsc"), + resource.TestCheckTypeSetElemAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresent", + "interface_names.*", "lo0"), + ), + }, + { + Config: testAccDataSourceInterfacesPhysicalPresentConfigMatch(testaccInterface), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckTypeSetElemAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresentEth", + "interface_names.*", testaccInterface), + resource.TestCheckResourceAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresentEth003", + "interface_names.#", "1"), + resource.TestCheckResourceAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresentEth003", + "interface_statuses.#", "1"), + resource.TestCheckResourceAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresentEth003", + "interface_statuses.0.name", testaccInterface), + resource.TestCheckResourceAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresentEth003", + "interface_statuses.0.admin_status", "up"), + resource.TestCheckResourceAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresentEth003", + "interface_statuses.0.oper_status", "down"), + resource.TestCheckResourceAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresentEth003AdmUp", + "interface_names.#", "1"), + resource.TestCheckResourceAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresentEth003OperUp", + "interface_names.#", "0"), + resource.TestCheckResourceAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresentLo0", + "interface_names.#", "1"), + ), + }, + { + Config: testAccDataSourceInterfacesPhysicalPresentConfigMatch2(testaccInterface), + }, + { + Config: testAccDataSourceInterfacesPhysicalPresentConfigMatch2(testaccInterface), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.junos_interfaces_physical_present.testacc_dataIfacesPhysPresentEth003AdmUp", + "interface_names.#", "0"), + ), + }, + }, + PreventPostDestroyRefresh: true, + }) + } +} + +func testAccDataSourceInterfacesPhysicalPresentPreSwitch(interFace string) string { + return ` +resource junos_interface_physical testacc_dataIfacesPhysPresent { + name = "` + interFace + `" + description = "testacc_dataIfacesPhysPresent" +} +resource junos_interface_logical testacc_dataIfacesPhysPresent { + name = "${junos_interface_physical.testacc_dataIfacesPhysPresent.name}.0" + description = "testacc_dataIfacesPhysPresent" +} +` +} + +func testAccDataSourceInterfacesPhysicalPresentConfig(interFace string) string { + return ` +resource junos_interface_physical testacc_dataIfacesPhysPresent { + name = "` + interFace + `" + description = "testacc_dataIfacesPhysPresent" +} +data junos_interfaces_physical_present testacc_dataIfacesPhysPresent { +} +` +} + +func testAccDataSourceInterfacesPhysicalPresentConfigMatch(interFace string) string { + return ` +resource junos_interface_physical testacc_dataIfacesPhysPresent { + name = "` + interFace + `" + description = "testacc_dataIfacesPhysPresent" +} +data junos_interfaces_physical_present testacc_dataIfacesPhysPresentEth { + match_name = "^` + strings.Split(interFace, `-`)[0] + `-.*$" +} +data junos_interfaces_physical_present testacc_dataIfacesPhysPresentEth003 { + match_name = "^` + interFace + `$" +} +data junos_interfaces_physical_present testacc_dataIfacesPhysPresentEth003AdmUp { + match_name = "^` + interFace + `$" + match_admin_up = true +} +data junos_interfaces_physical_present testacc_dataIfacesPhysPresentEth003OperUp { + match_name = "^` + interFace + `$" + match_oper_up = true +} +data junos_interfaces_physical_present testacc_dataIfacesPhysPresentLo0 { + match_name = "^lo0$" + match_oper_up = true +} +` +} + +func testAccDataSourceInterfacesPhysicalPresentConfigMatch2(interFace string) string { + return ` +data junos_interfaces_physical_present testacc_dataIfacesPhysPresentEth003AdmUp { + match_name = "^` + interFace + `$" + match_admin_up = true +} +` +} diff --git a/junos/func_common.go b/junos/func_common.go index 769c1082..a0c4d4f7 100644 --- a/junos/func_common.go +++ b/junos/func_common.go @@ -327,3 +327,19 @@ func replaceTildeToHomeDir(path *string) error { return nil } + +func validateIsIPv6Address(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + + return warnings, errors + } + + ip := net.ParseIP(v) + if four, six := ip.To4(), ip.To16(); four != nil || six == nil { + errors = append(errors, fmt.Errorf("expected %s to contain a valid IPv6 address, got: %s", k, v)) + } + + return warnings, errors +} diff --git a/junos/netconf.go b/junos/netconf.go index 508fbbab..24aec64b 100644 --- a/junos/netconf.go +++ b/junos/netconf.go @@ -25,6 +25,8 @@ const ( rpcCandidateUnlock = "" rpcClearCandidate = "" rpcClose = "" + + rpcGetInterfaceInformationTerse = `` ) // NetconfObject : store Junos device info and session. @@ -60,6 +62,16 @@ type commitResults struct { Errors []netconf.RPCError `xml:"rpc-error"` } +type getInterfaceTerseReply struct { + InterfaceInfo struct { + PhysicalInterface []struct { + Name string `xml:"name"` + AdminStatus string `xml:"admin-status"` + OperStatus string `xml:"oper-status"` + } `xml:"physical-interface"` + } `xml:"interface-information"` +} + // netconfNewSession establishes a new connection to a NetconfObject device that we will use // to run our commands against. // Authentication methods are defined using the netconfAuthMethod struct, and are as follows: diff --git a/junos/provider.go b/junos/provider.go index 79e84df3..93406d4b 100644 --- a/junos/provider.go +++ b/junos/provider.go @@ -93,6 +93,7 @@ func Provider() *schema.Provider { }, }, ResourcesMap: map[string]*schema.Resource{ + "junos_access_address_assignment_pool": resourceAccessAddressAssignPool(), "junos_aggregate_route": resourceAggregateRoute(), "junos_application": resourceApplication(), "junos_application_set": resourceApplicationSet(), @@ -112,6 +113,7 @@ func Provider() *schema.Provider { "junos_interface": resourceInterface(), "junos_interface_logical": resourceInterfaceLogical(), "junos_interface_physical": resourceInterfacePhysical(), + "junos_interface_physical_disable": resourceInterfacePhysicalDisable(), "junos_interface_st0_unit": resourceInterfaceSt0Unit(), "junos_null_commit_file": resourceNullCommitFile(), "junos_ospf": resourceOspf(), @@ -180,15 +182,17 @@ func Provider() *schema.Provider { "junos_system_ntp_server": resourceSystemNtpServer(), "junos_system_radius_server": resourceSystemRadiusServer(), "junos_system_root_authentication": resourceSystemRootAuthentication(), + "junos_system_services_dhcp_localserver_group": resourceSystemServicesDhcpLocalServerGroup(), "junos_system_syslog_file": resourceSystemSyslogFile(), "junos_system_syslog_host": resourceSystemSyslogHost(), "junos_vlan": resourceVlan(), }, DataSourcesMap: map[string]*schema.Resource{ - "junos_interface": dataSourceInterface(), - "junos_interface_logical": dataSourceInterfaceLogical(), - "junos_interface_physical": dataSourceInterfacePhysical(), - "junos_system_information": dataSourceSystemInformation(), + "junos_interface": dataSourceInterface(), + "junos_interface_logical": dataSourceInterfaceLogical(), + "junos_interface_physical": dataSourceInterfacePhysical(), + "junos_interfaces_physical_present": dataSourceInterfacesPhysicalPresent(), + "junos_system_information": dataSourceSystemInformation(), }, ConfigureContextFunc: configureProvider, } diff --git a/junos/provider_test.go b/junos/provider_test.go index c4f7db3f..9206bb80 100644 --- a/junos/provider_test.go +++ b/junos/provider_test.go @@ -19,8 +19,9 @@ var ( ) const ( - defaultInterfaceTestAcc = "ge-0/0/3" - defaultInterfaceTestAcc2 = "ge-0/0/4" + defaultInterfaceTestAcc = "ge-0/0/3" + defaultInterfaceTestAcc2 = "ge-0/0/4" + defaultInterfaceSwitchTestAcc = "xe-0/0/3" ) func TestProvider(t *testing.T) { diff --git a/junos/resource_access_address_assignment_pool.go b/junos/resource_access_address_assignment_pool.go new file mode 100644 index 00000000..903cfa8c --- /dev/null +++ b/junos/resource_access_address_assignment_pool.go @@ -0,0 +1,1359 @@ +package junos + +import ( + "context" + "fmt" + "regexp" + "strconv" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + bchk "github.com/jeremmfr/go-utils/basiccheck" +) + +type accessAddressAssignPoolOptions struct { + activeDrain bool + holdDown bool + name string + link string + routingInstance string + family []map[string]interface{} +} + +func resourceAccessAddressAssignPool() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceAccessAddressAssignPoolCreate, + ReadContext: resourceAccessAddressAssignPoolRead, + UpdateContext: resourceAccessAddressAssignPoolUpdate, + DeleteContext: resourceAccessAddressAssignPoolDelete, + Importer: &schema.ResourceImporter{ + State: resourceAccessAddressAssignPoolImport, + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), + }, + "routing_instance": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: defaultWord, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), + }, + "family": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{inetWord, inet6Word}, false), + }, + "network": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.IsCIDRNetwork(0, 128), + }, + "dhcp_attributes": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "boot_file": { + Type: schema.TypeString, + Optional: true, + }, + "boot_server": { + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefAndDots), + }, + "dns_server": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "domain_name": { + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefAndDots), + }, + "exclude_prefix_len": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 128), + }, + "grace_period": { + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntBetween(0, 4294967295), + }, + "maximum_lease_time": { + Type: schema.TypeInt, + Optional: true, + ConflictsWith: []string{ + "family.0.dhcp_attributes.0.maximum_lease_time_infinite", + "family.0.dhcp_attributes.0.preferred_lifetime", + "family.0.dhcp_attributes.0.preferred_lifetime_infinite", + "family.0.dhcp_attributes.0.valid_lifetime", + "family.0.dhcp_attributes.0.valid_lifetime_infinite", + }, + Default: -1, + ValidateFunc: validation.IntBetween(0, 4294967295), + }, + "maximum_lease_time_infinite": { + Type: schema.TypeBool, + Optional: true, + ConflictsWith: []string{ + "family.0.dhcp_attributes.0.maximum_lease_time", + "family.0.dhcp_attributes.0.preferred_lifetime", + "family.0.dhcp_attributes.0.preferred_lifetime_infinite", + "family.0.dhcp_attributes.0.valid_lifetime", + "family.0.dhcp_attributes.0.valid_lifetime_infinite", + }, + }, + "name_server": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "netbios_node_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"b-node", "h-node", "m-node", "p-node"}, false), + }, + "next_server": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.IsIPv4Address, + }, + "option": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "option_match_82_circuit_id": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeString, + Required: true, + }, + "range": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "option_match_82_remote_id": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeString, + Required: true, + }, + "range": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "preferred_lifetime": { + Type: schema.TypeInt, + Optional: true, + ConflictsWith: []string{ + "family.0.dhcp_attributes.0.preferred_lifetime_infinite", + "family.0.dhcp_attributes.0.maximum_lease_time", + "family.0.dhcp_attributes.0.maximum_lease_time_infinite", + }, + Default: -1, + ValidateFunc: validation.IntBetween(0, 4294967295), + }, + "preferred_lifetime_infinite": { + Type: schema.TypeBool, + Optional: true, + ConflictsWith: []string{ + "family.0.dhcp_attributes.0.preferred_lifetime", + "family.0.dhcp_attributes.0.maximum_lease_time", + "family.0.dhcp_attributes.0.maximum_lease_time_infinite", + }, + }, + "propagate_ppp_settings": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "propagate_settings": { + Type: schema.TypeString, + Optional: true, + }, + "router": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "server_identifier": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.IsIPv4Address, + }, + "sip_server_inet_address": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "sip_server_inet_domain_name": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "sip_server_inet6_address": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "sip_server_inet6_domain_name": { + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefAndDots), + }, + "t1_percentage": { + Type: schema.TypeInt, + Optional: true, + ConflictsWith: []string{ + "family.0.dhcp_attributes.0.t1_renewal_time", + "family.0.dhcp_attributes.0.t2_rebinding_time", + }, + Default: -1, + ValidateFunc: validation.IntBetween(0, 100), + }, + "t1_renewal_time": { + Type: schema.TypeInt, + Optional: true, + ConflictsWith: []string{ + "family.0.dhcp_attributes.0.t1_percentage", + "family.0.dhcp_attributes.0.t2_percentage", + }, + Default: -1, + ValidateFunc: validation.IntBetween(0, 4294967295), + }, + "t2_percentage": { + Type: schema.TypeInt, + Optional: true, + ConflictsWith: []string{ + "family.0.dhcp_attributes.0.t1_renewal_time", + "family.0.dhcp_attributes.0.t2_rebinding_time", + }, + Default: -1, + ValidateFunc: validation.IntBetween(0, 100), + }, + "t2_rebinding_time": { + Type: schema.TypeInt, + Optional: true, + ConflictsWith: []string{ + "family.0.dhcp_attributes.0.t1_percentage", + "family.0.dhcp_attributes.0.t2_percentage", + }, + Default: -1, + ValidateFunc: validation.IntBetween(0, 4294967295), + }, + "tftp_server": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.IsIPv4Address, + }, + "valid_lifetime": { + Type: schema.TypeInt, + Optional: true, + ConflictsWith: []string{ + "family.0.dhcp_attributes.0.valid_lifetime_infinite", + "family.0.dhcp_attributes.0.maximum_lease_time", + "family.0.dhcp_attributes.0.maximum_lease_time_infinite", + }, + Default: -1, + ValidateFunc: validation.IntBetween(0, 4294967295), + }, + "valid_lifetime_infinite": { + Type: schema.TypeBool, + Optional: true, + ConflictsWith: []string{ + "family.0.dhcp_attributes.0.valid_lifetime", + "family.0.dhcp_attributes.0.maximum_lease_time", + "family.0.dhcp_attributes.0.maximum_lease_time_infinite", + }, + }, + "wins_server": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + "excluded_address": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "excluded_range": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 63, formatDefault), + }, + "low": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.IsIPAddress, + }, + "high": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.IsIPAddress, + }, + }, + }, + }, + "host": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 63, formatDefault), + }, + "hardware_address": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.IsMACAddress, + }, + "ip_address": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.IsIPv4Address, + }, + }, + }, + }, + "inet_range": { + Type: schema.TypeList, + Optional: true, + ConflictsWith: []string{"family.0.inet6_range"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 63, formatDefault), + }, + "low": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.IsIPv4Address, + }, + "high": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.IsIPv4Address, + }, + }, + }, + }, + "inet6_range": { + Type: schema.TypeList, + Optional: true, + ConflictsWith: []string{"family.0.inet_range"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 63, formatDefault), + }, + "low": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.IsCIDR, + }, + "high": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.IsCIDR, + }, + "prefix_length": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 128), + }, + }, + }, + }, + "xauth_attributes_primary_dns": { + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: validateIPMaskFunc(), + }, + "xauth_attributes_primary_wins": { + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: validateIPMaskFunc(), + }, + "xauth_attributes_secondary_dns": { + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: validateIPMaskFunc(), + }, + "xauth_attributes_secondary_wins": { + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: validateIPMaskFunc(), + }, + }, + }, + }, + "active_drain": { + Type: schema.TypeBool, + Optional: true, + }, + "hold_down": { + Type: schema.TypeBool, + Optional: true, + }, + "link": { + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), + }, + }, + } +} + +func resourceAccessAddressAssignPoolCreate(ctx context.Context, + d *schema.ResourceData, m interface{}) diag.Diagnostics { + sess := m.(*Session) + if sess.junosFakeCreateSetFile != "" { + if err := setAccessAddressAssignPool(d, m, nil); err != nil { + return diag.FromErr(err) + } + d.SetId(d.Get("name").(string) + idSeparator + d.Get("routing_instance").(string)) + + return nil + } + jnprSess, err := sess.startNewSession() + if err != nil { + return diag.FromErr(err) + } + defer sess.closeSession(jnprSess) + sess.configLock(jnprSess) + var diagWarns diag.Diagnostics + if d.Get("routing_instance").(string) != defaultWord { + instanceExists, err := checkRoutingInstanceExists(d.Get("routing_instance").(string), m, jnprSess) + if err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + if !instanceExists { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, + diag.FromErr(fmt.Errorf("routing instance %v doesn't exist", d.Get("routing_instance").(string)))...) + } + } + accessAddressAssignPoolExists, err := checkAccessAddressAssignPoolExists( + d.Get("name").(string), d.Get("routing_instance").(string), m, jnprSess) + if err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + if accessAddressAssignPoolExists { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr( + fmt.Errorf("access address-assignment pool %v already exists in routing-instance %s", + d.Get("name").(string), d.Get("routing_instance").(string)))...) + } + if err := setAccessAddressAssignPool(d, m, jnprSess); err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + warns, err := sess.commitConf("create resource junos_access_address_assignment_pool", jnprSess) + appendDiagWarns(&diagWarns, warns) + if err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + accessAddressAssignPoolExists, err = checkAccessAddressAssignPoolExists( + d.Get("name").(string), d.Get("routing_instance").(string), m, jnprSess) + if err != nil { + return append(diagWarns, diag.FromErr(err)...) + } + if accessAddressAssignPoolExists { + d.SetId(d.Get("name").(string) + idSeparator + d.Get("routing_instance").(string)) + } else { + return append(diagWarns, + diag.FromErr(fmt.Errorf("access address-assignment pool %v not exists in routing_instance %s after commit "+ + "=> check your config", d.Get("name").(string), d.Get("routing_instance").(string)))...) + } + + return append(diagWarns, resourceAccessAddressAssignPoolReadWJnprSess(d, m, jnprSess)...) +} + +func resourceAccessAddressAssignPoolRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + sess := m.(*Session) + jnprSess, err := sess.startNewSession() + if err != nil { + return diag.FromErr(err) + } + defer sess.closeSession(jnprSess) + + return resourceAccessAddressAssignPoolReadWJnprSess(d, m, jnprSess) +} + +func resourceAccessAddressAssignPoolReadWJnprSess( + d *schema.ResourceData, m interface{}, jnprSess *NetconfObject) diag.Diagnostics { + mutex.Lock() + accessAddressAssignPoolOptions, err := + readAccessAddressAssignPool(d.Get("name").(string), d.Get("routing_instance").(string), m, jnprSess) + mutex.Unlock() + if err != nil { + return diag.FromErr(err) + } + if accessAddressAssignPoolOptions.name == "" { + d.SetId("") + } else { + fillAccessAddressAssignPoolData(d, accessAddressAssignPoolOptions) + } + + return nil +} + +func resourceAccessAddressAssignPoolUpdate(ctx context.Context, + d *schema.ResourceData, m interface{}) diag.Diagnostics { + d.Partial(true) + sess := m.(*Session) + jnprSess, err := sess.startNewSession() + if err != nil { + return diag.FromErr(err) + } + defer sess.closeSession(jnprSess) + sess.configLock(jnprSess) + var diagWarns diag.Diagnostics + if err := delAccessAddressAssignPool(d.Get("name").(string), d.Get("routing_instance").(string), + m, jnprSess); err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + if err := setAccessAddressAssignPool(d, m, jnprSess); err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + warns, err := sess.commitConf("update resource junos_access_address_assignment_pool", jnprSess) + appendDiagWarns(&diagWarns, warns) + if err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + d.Partial(false) + + return append(diagWarns, resourceAccessAddressAssignPoolReadWJnprSess(d, m, jnprSess)...) +} + +func resourceAccessAddressAssignPoolDelete(ctx context.Context, + d *schema.ResourceData, m interface{}) diag.Diagnostics { + sess := m.(*Session) + jnprSess, err := sess.startNewSession() + if err != nil { + return diag.FromErr(err) + } + defer sess.closeSession(jnprSess) + sess.configLock(jnprSess) + var diagWarns diag.Diagnostics + if err := delAccessAddressAssignPool(d.Get("name").(string), d.Get("routing_instance").(string), + m, jnprSess); err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + warns, err := sess.commitConf("delete resource junos_access_address_assignment_pool", jnprSess) + appendDiagWarns(&diagWarns, warns) + if err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + + return diagWarns +} + +func resourceAccessAddressAssignPoolImport(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + sess := m.(*Session) + jnprSess, err := sess.startNewSession() + if err != nil { + return nil, err + } + defer sess.closeSession(jnprSess) + result := make([]*schema.ResourceData, 1) + idSplit := strings.Split(d.Id(), idSeparator) + if len(idSplit) < 2 { + return nil, fmt.Errorf("missing element(s) in id with separator %v", idSeparator) + } + accessAddressAssignPoolExists, err := checkAccessAddressAssignPoolExists(idSplit[0], idSplit[1], m, jnprSess) + if err != nil { + return nil, err + } + if !accessAddressAssignPoolExists { + return nil, fmt.Errorf("don't find access address-assignment pool with id '%v' (id must be "+ + ""+idSeparator+")", d.Id()) + } + accessAddressAssignPoolOptions, err := readAccessAddressAssignPool(idSplit[0], idSplit[1], m, jnprSess) + if err != nil { + return nil, err + } + fillAccessAddressAssignPoolData(d, accessAddressAssignPoolOptions) + + result[0] = d + + return result, nil +} + +func checkAccessAddressAssignPoolExists(name string, instance string, m interface{}, + jnprSess *NetconfObject) (bool, error) { + sess := m.(*Session) + var showConfig string + var err error + if instance == defaultWord { + showConfig, err = sess.command("show configuration"+ + " access address-assignment pool "+name+" | display set", jnprSess) + if err != nil { + return false, err + } + } else { + showConfig, err = sess.command("show configuration routing-instances "+instance+ + " access address-assignment pool "+name+" | display set", jnprSess) + if err != nil { + return false, err + } + } + + if showConfig == emptyWord { + return false, nil + } + + return true, nil +} + +func setAccessAddressAssignPool(d *schema.ResourceData, m interface{}, jnprSess *NetconfObject) error { + sess := m.(*Session) + configSet := make([]string, 0) + + var setPrefix string + if d.Get("routing_instance").(string) == defaultWord { + setPrefix = "set access address-assignment pool " + d.Get("name").(string) + " " + } else { + setPrefix = "set routing-instances " + d.Get("routing_instance").(string) + + " access address-assignment pool " + d.Get("name").(string) + " " + } + + for _, fi := range d.Get("family").([]interface{}) { + family := fi.(map[string]interface{}) + + configSetFamily, err := setAccessAddressAssignPoolFamily(family, setPrefix) + if err != nil { + return err + } + configSet = append(configSet, configSetFamily...) + } + if d.Get("active_drain").(bool) { + configSet = append(configSet, setPrefix+"active-drain") + } + if d.Get("hold_down").(bool) { + configSet = append(configSet, setPrefix+"hold-down") + } + if v := d.Get("link").(string); v != "" { + configSet = append(configSet, setPrefix+"link "+v) + } + + return sess.configSet(configSet, jnprSess) +} + +func setAccessAddressAssignPoolFamily(family map[string]interface{}, setPrefix string) ([]string, error) { + configSet := make([]string, 0) + + setPrefixFamily := setPrefix + "family inet " + + switch family["type"].(string) { + case inetWord: + configSet = append(configSet, setPrefixFamily+"network "+family["network"].(string)) + case inet6Word: + setPrefixFamily = setPrefix + "family inet6 " + configSet = append(configSet, setPrefixFamily+"prefix "+family["network"].(string)) + } + for _, da := range family["dhcp_attributes"].([]interface{}) { + dhcpAttr := da.(map[string]interface{}) + setPrefixDhcpAttr := setPrefixFamily + "dhcp-attributes " + + configSetDhcpAttributes, err := + setAccessAddressAssignPoolFamilyDhcpAttributes(dhcpAttr, family["type"].(string), setPrefixDhcpAttr) + if err != nil { + return configSet, err + } + configSet = append(configSet, configSetDhcpAttributes...) + } + for _, v := range sortSetOfString(family["excluded_address"].(*schema.Set).List()) { + switch family["type"].(string) { + case inetWord: + if _, errs := validation.IsIPv4Address(v, "family.0.excluded_address"); len(errs) > 0 { + return configSet, errs[0] + } + case inet6Word: + if _, errs := validateIsIPv6Address(v, "family.0.excluded_address"); len(errs) > 0 { + return configSet, errs[0] + } + } + configSet = append(configSet, setPrefixFamily+"excluded-address "+v) + } + excludedRangeNameList := make([]string, 0) + for _, v := range family["excluded_range"].([]interface{}) { + excludedRange := v.(map[string]interface{}) + if bchk.StringInSlice(excludedRange["name"].(string), excludedRangeNameList) { + return configSet, fmt.Errorf("multiple blocks excluded_range with the same name %s", excludedRange["name"].(string)) + } + excludedRangeNameList = append(excludedRangeNameList, excludedRange["name"].(string)) + configSet = append(configSet, + setPrefixFamily+"excluded-range "+excludedRange["name"].(string)+" low "+excludedRange["low"].(string)) + configSet = append(configSet, + setPrefixFamily+"excluded-range "+excludedRange["name"].(string)+" high "+excludedRange["high"].(string)) + } + hostNameList := make([]string, 0) + for _, v := range family["host"].([]interface{}) { + if family["type"].(string) == inet6Word { + return configSet, fmt.Errorf("host not compatible when type = inet6") + } + host := v.(map[string]interface{}) + if bchk.StringInSlice(host["name"].(string), hostNameList) { + return configSet, fmt.Errorf("multiple blocks host with the same name %s", host["name"].(string)) + } + hostNameList = append(hostNameList, host["name"].(string)) + configSet = append(configSet, + setPrefixFamily+"host "+host["name"].(string)+" hardware-address "+host["hardware_address"].(string)) + configSet = append(configSet, + setPrefixFamily+"host "+host["name"].(string)+" ip-address "+host["ip_address"].(string)) + } + rangeNameList := make([]string, 0) + switch family["type"].(string) { + case inetWord: + if len(family["inet6_range"].([]interface{})) > 0 { + return configSet, fmt.Errorf("inet6_range not compatible when type = inet") + } + for _, v := range family["inet_range"].([]interface{}) { + rangeBlck := v.(map[string]interface{}) + if bchk.StringInSlice(rangeBlck["name"].(string), rangeNameList) { + return configSet, fmt.Errorf("multiple blocks inet_range with the same name %s", rangeBlck["name"].(string)) + } + rangeNameList = append(rangeNameList, rangeBlck["name"].(string)) + configSet = append(configSet, + setPrefixFamily+"range "+rangeBlck["name"].(string)+" low "+rangeBlck["low"].(string)) + configSet = append(configSet, + setPrefixFamily+"range "+rangeBlck["name"].(string)+" high "+rangeBlck["high"].(string)) + } + case inet6Word: + if len(family["inet_range"].([]interface{})) > 0 { + return configSet, fmt.Errorf("inet_range not compatible when type = inet6") + } + for _, v := range family["inet6_range"].([]interface{}) { + rangeBlck := v.(map[string]interface{}) + if bchk.StringInSlice(rangeBlck["name"].(string), rangeNameList) { + return configSet, fmt.Errorf("multiple blocks inet6_range with the same name %s", rangeBlck["name"].(string)) + } + rangeNameList = append(rangeNameList, rangeBlck["name"].(string)) + switch { + case rangeBlck["prefix_length"].(int) != 0 && + (rangeBlck["low"].(string) != "" || rangeBlck["high"].(string) != ""): + return configSet, + fmt.Errorf("conflict between prefix_length and low/high in inet6_range %s", rangeBlck["name"].(string)) + case rangeBlck["prefix_length"].(int) != 0: + configSet = append(configSet, setPrefixFamily+"range "+rangeBlck["name"].(string)+ + " prefix-length "+strconv.Itoa(rangeBlck["prefix_length"].(int))) + case rangeBlck["low"].(string) != "" && rangeBlck["high"].(string) != "": + configSet = append(configSet, setPrefixFamily+"range "+rangeBlck["name"].(string)+ + " low "+rangeBlck["low"].(string)) + configSet = append(configSet, setPrefixFamily+"range "+rangeBlck["name"].(string)+ + " high "+rangeBlck["high"].(string)) + default: + return configSet, fmt.Errorf("missing prefix_length or low & high for inet6_range %s", rangeBlck["name"].(string)) + } + } + } + if v := family["xauth_attributes_primary_dns"].(string); v != "" { + if family["type"].(string) == inet6Word { + return configSet, fmt.Errorf("xauth_attributes_primary_dns not compatible when type = inet6") + } + if _, errs := validation.IsIPv4Address(strings.Split(v, "/")[0], ""); len(errs) > 0 { + return configSet, fmt.Errorf("%s is not a IPv4", v) + } + configSet = append(configSet, setPrefixFamily+"xauth-attributes primary-dns "+v) + } + if v := family["xauth_attributes_primary_wins"].(string); v != "" { + if family["type"].(string) == inet6Word { + return configSet, fmt.Errorf("xauth_attributes_primary_wins not compatible when type = inet6") + } + if _, errs := validation.IsIPv4Address(strings.Split(v, "/")[0], ""); len(errs) > 0 { + return configSet, fmt.Errorf("%s is not a IPv4", v) + } + configSet = append(configSet, setPrefixFamily+"xauth-attributes primary-wins "+v) + } + if v := family["xauth_attributes_secondary_dns"].(string); v != "" { + if family["type"].(string) == inet6Word { + return configSet, fmt.Errorf("xauth_attributes_secondary_dns not compatible when type = inet6") + } + if _, errs := validation.IsIPv4Address(strings.Split(v, "/")[0], ""); len(errs) > 0 { + return configSet, fmt.Errorf("%s is not a IPv4", v) + } + configSet = append(configSet, setPrefixFamily+"xauth-attributes secondary-dns "+v) + } + if v := family["xauth_attributes_secondary_wins"].(string); v != "" { + if family["type"].(string) == inet6Word { + return configSet, fmt.Errorf("xauth_attributes_secondary_wins not compatible when type = inet6") + } + if _, errs := validation.IsIPv4Address(strings.Split(v, "/")[0], ""); len(errs) > 0 { + return configSet, fmt.Errorf("%s is not a IPv4", v) + } + configSet = append(configSet, setPrefixFamily+"xauth-attributes secondary-wins "+v) + } + + return configSet, nil +} + +func setAccessAddressAssignPoolFamilyDhcpAttributes( + dhcpAttr map[string]interface{}, familyType, setPrefix string) ([]string, error) { + configSet := make([]string, 0) + + if v := dhcpAttr["boot_file"].(string); v != "" { + configSet = append(configSet, setPrefix+"boot-file \""+v+"\"") + } + if v := dhcpAttr["boot_server"].(string); v != "" { + configSet = append(configSet, setPrefix+"boot-server "+v) + } + for _, v := range dhcpAttr["dns_server"].([]interface{}) { + if familyType == inetWord { + return configSet, fmt.Errorf("dhcp_attributes.0.dns_server not compatible when type = inet") + } + if _, errs := validateIsIPv6Address(v, "dhcp_attributes.0.dns_server"); len(errs) > 0 { + return configSet, errs[0] + } + + configSet = append(configSet, setPrefix+"dns-server "+v.(string)) + } + if v := dhcpAttr["domain_name"].(string); v != "" { + configSet = append(configSet, setPrefix+"domain-name "+v) + } + if v := dhcpAttr["exclude_prefix_len"].(int); v != 0 { + if familyType == inetWord { + return configSet, fmt.Errorf("dhcp_attributes.0.exclude_prefix_len not compatible when type = inet") + } + configSet = append(configSet, setPrefix+"exclude-prefix-len "+strconv.Itoa(v)) + } + if v := dhcpAttr["grace_period"].(int); v != -1 { + configSet = append(configSet, setPrefix+"grace-period "+strconv.Itoa(v)) + } + if v := dhcpAttr["maximum_lease_time"].(int); v != -1 { + configSet = append(configSet, setPrefix+"maximum-lease-time "+strconv.Itoa(v)) + } + if dhcpAttr["maximum_lease_time_infinite"].(bool) { + configSet = append(configSet, setPrefix+"maximum-lease-time infinite") + } + for _, v := range dhcpAttr["name_server"].([]interface{}) { + if _, errs := validation.IsIPv4Address(v, "dhcp_attributes.0.name_server"); len(errs) > 0 { + return configSet, errs[0] + } + configSet = append(configSet, setPrefix+"name-server "+v.(string)) + } + if v := dhcpAttr["netbios_node_type"].(string); v != "" { + configSet = append(configSet, setPrefix+"netbios-node-type "+v) + } + if v := dhcpAttr["next_server"].(string); v != "" { + configSet = append(configSet, setPrefix+"next-server "+v) + } + for _, v := range sortSetOfString(dhcpAttr["option"].(*schema.Set).List()) { + r := regexp.MustCompile( + `^\d+ (array )?(byte|flag|hex-string|integer|ip-address|short|string|unsigned-integer|unsigned-short) .*$`) + if !r.MatchString(v) { + return configSet, fmt.Errorf("option '%s' is invalid, need to match "+ + "'^\\d+ (array )?(byte|flag|hex-string|integer|ip-address|short|string|unsigned-integer|unsigned-short) .*$'", v) + } + configSet = append(configSet, setPrefix+"option "+v) + } + optionMatch82CircuitIDValueList := make([]string, 0) + for _, v := range dhcpAttr["option_match_82_circuit_id"].([]interface{}) { + opt := v.(map[string]interface{}) + if bchk.StringInSlice(opt["value"].(string), optionMatch82CircuitIDValueList) { + return configSet, + fmt.Errorf("multiple blocks option_match_82_circuit_id with the same value %s", opt["value"].(string)) + } + optionMatch82CircuitIDValueList = append(optionMatch82CircuitIDValueList, opt["value"].(string)) + configSet = append(configSet, + setPrefix+"option-match option-82 "+ + "circuit-id \""+opt["value"].(string)+"\" range \""+opt["range"].(string)+"\"") + } + optionMatch82RemoteIDValueList := make([]string, 0) + for _, v := range dhcpAttr["option_match_82_remote_id"].([]interface{}) { + opt := v.(map[string]interface{}) + if bchk.StringInSlice(opt["value"].(string), optionMatch82RemoteIDValueList) { + return configSet, + fmt.Errorf("multiple blocks option_match_82_remote_id with the same value %s", opt["value"].(string)) + } + optionMatch82RemoteIDValueList = append(optionMatch82RemoteIDValueList, opt["value"].(string)) + configSet = append(configSet, + setPrefix+"option-match option-82 "+ + "remote-id \""+opt["value"].(string)+"\" range \""+opt["range"].(string)+"\"") + } + if v := dhcpAttr["preferred_lifetime"].(int); v != -1 { + if familyType == inetWord { + return configSet, fmt.Errorf("dhcp_attributes.0.preferred_lifetime not compatible when type = inet") + } + configSet = append(configSet, setPrefix+"preferred-lifetime "+strconv.Itoa(v)) + } + if dhcpAttr["preferred_lifetime_infinite"].(bool) { + if familyType == inetWord { + return configSet, fmt.Errorf("dhcp_attributes.0.preferred_lifetime_infinite not compatible when type = inet") + } + configSet = append(configSet, setPrefix+"preferred-lifetime infinite") + } + for _, v := range sortSetOfString(dhcpAttr["propagate_ppp_settings"].(*schema.Set).List()) { + configSet = append(configSet, setPrefix+"propagate-ppp-settings "+v) + } + if v := dhcpAttr["propagate_settings"].(string); v != "" { + configSet = append(configSet, setPrefix+"propagate-settings \""+v+"\"") + } + for _, v := range dhcpAttr["router"].([]interface{}) { + if _, errs := validation.IsIPv4Address(v.(string), "dhcp_attributes.0.router"); len(errs) > 0 { + return configSet, errs[0] + } + configSet = append(configSet, setPrefix+"router "+v.(string)) + } + if v := dhcpAttr["server_identifier"].(string); v != "" { + configSet = append(configSet, setPrefix+"server-identifier "+v) + } + for _, v := range dhcpAttr["sip_server_inet_address"].([]interface{}) { + if _, errs := validation.IsIPv4Address(v.(string), "dhcp_attributes.0.sip_server_inet_address"); len(errs) > 0 { + return configSet, errs[0] + } + configSet = append(configSet, setPrefix+"sip-server ip-address "+v.(string)) + } + for _, v := range dhcpAttr["sip_server_inet6_address"].([]interface{}) { + if familyType == inetWord { + return configSet, fmt.Errorf("dhcp_attributes.0.sip_server_inet6_address not compatible when type = inet") + } + if _, errs := validateIsIPv6Address(v.(string), "dhcp_attributes.0.sip_server_inet6_address"); len(errs) > 0 { + return configSet, errs[0] + } + configSet = append(configSet, setPrefix+"sip-server-address "+v.(string)) + } + for _, v := range dhcpAttr["sip_server_inet_domain_name"].([]interface{}) { + configSet = append(configSet, setPrefix+"sip-server name \""+v.(string)+"\"") + } + if v := dhcpAttr["sip_server_inet6_domain_name"].(string); v != "" { + if familyType == inetWord { + return configSet, fmt.Errorf("dhcp_attributes.0.sip_server_inet6_domain_name not compatible when type = inet") + } + configSet = append(configSet, setPrefix+"sip-server-domain-name \""+v+"\"") + } + if v := dhcpAttr["t1_percentage"].(int); v != -1 { + configSet = append(configSet, setPrefix+"t1-percentage "+strconv.Itoa(v)) + } + if v := dhcpAttr["t1_renewal_time"].(int); v != -1 { + configSet = append(configSet, setPrefix+"t1-renewal-time "+strconv.Itoa(v)) + } + if v := dhcpAttr["t2_percentage"].(int); v != -1 { + configSet = append(configSet, setPrefix+"t2-percentage "+strconv.Itoa(v)) + } + if v := dhcpAttr["t2_rebinding_time"].(int); v != -1 { + configSet = append(configSet, setPrefix+"t2-rebinding-time "+strconv.Itoa(v)) + } + if v := dhcpAttr["tftp_server"].(string); v != "" { + configSet = append(configSet, setPrefix+"tftp-server "+v) + } + if v := dhcpAttr["valid_lifetime"].(int); v != -1 { + if familyType == inetWord { + return configSet, fmt.Errorf("dhcp_attributes.0.valid_lifetime not compatible when type = inet") + } + configSet = append(configSet, setPrefix+"valid-lifetime "+strconv.Itoa(v)) + } + if dhcpAttr["valid_lifetime_infinite"].(bool) { + if familyType == inetWord { + return configSet, fmt.Errorf("dhcp_attributes.0.valid_lifetime_infinite not compatible when type = inet") + } + configSet = append(configSet, setPrefix+"valid-lifetime infinite") + } + for _, v := range dhcpAttr["wins_server"].([]interface{}) { + if _, errs := validation.IsIPv4Address(v.(string), "dhcp_attributes.0.wins_server"); len(errs) > 0 { + return configSet, errs[0] + } + configSet = append(configSet, setPrefix+"wins-server "+v.(string)) + } + if len(configSet) == 0 { + return configSet, fmt.Errorf("family.0.dhcp_attributes block is empty") + } + + return configSet, nil +} + +func readAccessAddressAssignPool(name string, instance string, m interface{}, + jnprSess *NetconfObject) (accessAddressAssignPoolOptions, error) { + sess := m.(*Session) + var confRead accessAddressAssignPoolOptions + var showConfig string + var err error + + if instance == defaultWord { + showConfig, err = sess.command("show configuration"+ + " access address-assignment pool "+name+" | display set relative", jnprSess) + } else { + showConfig, err = sess.command("show configuration routing-instances "+instance+ + " access address-assignment pool "+name+" | display set relative", jnprSess) + } + if err != nil { + return confRead, err + } + + if showConfig != emptyWord { + confRead.name = name + confRead.routingInstance = instance + for _, item := range strings.Split(showConfig, "\n") { + if strings.Contains(item, "") { + continue + } + if strings.Contains(item, "") { + break + } + itemTrim := strings.TrimPrefix(item, setLineStart) + switch { + case strings.HasPrefix(itemTrim, "family "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "family "), " ") + if len(confRead.family) == 0 { + confRead.family = append(confRead.family, map[string]interface{}{ + "type": itemTrimSplit[0], + "network": "", + "dhcp_attributes": make([]map[string]interface{}, 0), + "excluded_address": make([]string, 0), + "excluded_range": make([]map[string]interface{}, 0), + "host": make([]map[string]interface{}, 0), + "inet_range": make([]map[string]interface{}, 0), + "inet6_range": make([]map[string]interface{}, 0), + "xauth_attributes_primary_dns": "", + "xauth_attributes_primary_wins": "", + "xauth_attributes_secondary_dns": "", + "xauth_attributes_secondary_wins": "", + }) + } + if err := readAccessAddressAssignPoolFamily( + strings.TrimPrefix(itemTrim, "family "+itemTrimSplit[0]+" "), + confRead.family[0], + ); err != nil { + return confRead, err + } + case itemTrim == "active-drain": + confRead.activeDrain = true + case itemTrim == "hold-down": + confRead.holdDown = true + case strings.HasPrefix(itemTrim, "link "): + confRead.link = strings.TrimPrefix(itemTrim, "link ") + } + } + } + + return confRead, nil +} + +func readAccessAddressAssignPoolFamily(itemTrim string, family map[string]interface{}) error { + var err error + switch { + case strings.HasPrefix(itemTrim, "network "): + family["network"] = strings.TrimPrefix(itemTrim, "network ") + case strings.HasPrefix(itemTrim, "prefix "): + family["network"] = strings.TrimPrefix(itemTrim, "prefix ") + case strings.HasPrefix(itemTrim, "dhcp-attributes "): + if len(family["dhcp_attributes"].([]map[string]interface{})) == 0 { + family["dhcp_attributes"] = append(family["dhcp_attributes"].([]map[string]interface{}), map[string]interface{}{ + "boot_file": "", + "boot_server": "", + "dns_server": make([]string, 0), + "domain_name": "", + "exclude_prefix_len": 0, + "grace_period": -1, + "maximum_lease_time": -1, + "maximum_lease_time_infinite": false, + "name_server": make([]string, 0), + "netbios_node_type": "", + "next_server": "", + "option": make([]string, 0), + "option_match_82_circuit_id": make([]map[string]interface{}, 0), + "option_match_82_remote_id": make([]map[string]interface{}, 0), + "preferred_lifetime": -1, + "preferred_lifetime_infinite": false, + "propagate_ppp_settings": make([]string, 0), + "propagate_settings": "", + "router": make([]string, 0), + "server_identifier": "", + "sip_server_inet_address": make([]string, 0), + "sip_server_inet_domain_name": make([]string, 0), + "sip_server_inet6_address": make([]string, 0), + "sip_server_inet6_domain_name": "", + "t1_percentage": -1, + "t1_renewal_time": -1, + "t2_percentage": -1, + "t2_rebinding_time": -1, + "tftp_server": "", + "valid_lifetime": -1, + "valid_lifetime_infinite": false, + "wins_server": make([]string, 0), + }) + } + itemTrimAttr := strings.TrimPrefix(itemTrim, "dhcp-attributes ") + dhcpAttr := family["dhcp_attributes"].([]map[string]interface{})[0] + switch { + case strings.HasPrefix(itemTrimAttr, "boot-file "): + dhcpAttr["boot_file"] = strings.Trim(strings.TrimPrefix(itemTrimAttr, "boot-file "), "\"") + case strings.HasPrefix(itemTrimAttr, "boot-server "): + dhcpAttr["boot_server"] = strings.TrimPrefix(itemTrimAttr, "boot-server ") + case strings.HasPrefix(itemTrimAttr, "dns-server "): + dhcpAttr["dns_server"] = append(dhcpAttr["dns_server"].([]string), strings.TrimPrefix(itemTrimAttr, "dns-server ")) + case strings.HasPrefix(itemTrimAttr, "domain-name "): + dhcpAttr["domain_name"] = strings.TrimPrefix(itemTrimAttr, "domain-name ") + case strings.HasPrefix(itemTrimAttr, "exclude-prefix-len "): + dhcpAttr["exclude_prefix_len"], err = strconv.Atoi(strings.TrimPrefix(itemTrimAttr, "exclude-prefix-len ")) + case strings.HasPrefix(itemTrimAttr, "grace-period "): + dhcpAttr["grace_period"], err = strconv.Atoi(strings.TrimPrefix(itemTrimAttr, "grace-period ")) + case itemTrimAttr == "maximum-lease-time infinite": + dhcpAttr["maximum_lease_time_infinite"] = true + case strings.HasPrefix(itemTrimAttr, "maximum-lease-time "): + dhcpAttr["maximum_lease_time"], err = strconv.Atoi(strings.TrimPrefix(itemTrimAttr, "maximum-lease-time ")) + case strings.HasPrefix(itemTrimAttr, "name-server "): + dhcpAttr["name_server"] = append(dhcpAttr["name_server"].([]string), + strings.TrimPrefix(itemTrimAttr, "name-server ")) + case strings.HasPrefix(itemTrimAttr, "netbios-node-type "): + dhcpAttr["netbios_node_type"] = strings.TrimPrefix(itemTrimAttr, "netbios-node-type ") + case strings.HasPrefix(itemTrimAttr, "next-server "): + dhcpAttr["next_server"] = strings.TrimPrefix(itemTrimAttr, "next-server ") + case strings.HasPrefix(itemTrimAttr, "option "): + dhcpAttr["option"] = append(dhcpAttr["option"].([]string), strings.TrimPrefix(itemTrimAttr, "option ")) + case strings.HasPrefix(itemTrimAttr, "option-match option-82 circuit-id "): + itemTrimAttrSplit := strings.Split(strings.TrimPrefix(itemTrimAttr, "option-match option-82 circuit-id "), " ") + if len(itemTrimAttrSplit) < 3 { + return fmt.Errorf("can't find range from '%s'", itemTrimAttr) + } + dhcpAttr["option_match_82_circuit_id"] = append( + dhcpAttr["option_match_82_circuit_id"].([]map[string]interface{}), + map[string]interface{}{ + "value": itemTrimAttrSplit[0], + "range": itemTrimAttrSplit[2], + }, + ) + case strings.HasPrefix(itemTrimAttr, "option-match option-82 remote-id "): + itemTrimAttrSplit := strings.Split(strings.TrimPrefix(itemTrimAttr, "option-match option-82 remote-id "), " ") + if len(itemTrimAttrSplit) < 3 { + return fmt.Errorf("can't find range from '%s'", itemTrimAttr) + } + dhcpAttr["option_match_82_remote_id"] = append( + dhcpAttr["option_match_82_remote_id"].([]map[string]interface{}), + map[string]interface{}{ + "value": itemTrimAttrSplit[0], + "range": itemTrimAttrSplit[2], + }, + ) + case itemTrimAttr == "preferred-lifetime infinite": + dhcpAttr["preferred_lifetime_infinite"] = true + case strings.HasPrefix(itemTrimAttr, "preferred-lifetime "): + dhcpAttr["preferred_lifetime"], err = strconv.Atoi(strings.TrimPrefix(itemTrimAttr, "preferred-lifetime ")) + case strings.HasPrefix(itemTrimAttr, "propagate-ppp-settings "): + dhcpAttr["propagate_ppp_settings"] = append(dhcpAttr["propagate_ppp_settings"].([]string), + strings.TrimPrefix(itemTrimAttr, "propagate-ppp-settings ")) + case strings.HasPrefix(itemTrimAttr, "propagate-settings "): + dhcpAttr["propagate_settings"] = strings.TrimPrefix(itemTrimAttr, "propagate-settings ") + case strings.HasPrefix(itemTrimAttr, "router "): + dhcpAttr["router"] = append(dhcpAttr["router"].([]string), strings.TrimPrefix(itemTrimAttr, "router ")) + case strings.HasPrefix(itemTrimAttr, "server-identifier "): + dhcpAttr["server_identifier"] = strings.TrimPrefix(itemTrimAttr, "server-identifier ") + case strings.HasPrefix(itemTrimAttr, "sip-server ip-address "): + dhcpAttr["sip_server_inet_address"] = append(dhcpAttr["sip_server_inet_address"].([]string), + strings.TrimPrefix(itemTrimAttr, "sip-server ip-address ")) + case strings.HasPrefix(itemTrimAttr, "sip-server-address "): + dhcpAttr["sip_server_inet6_address"] = append(dhcpAttr["sip_server_inet6_address"].([]string), + strings.TrimPrefix(itemTrimAttr, "sip-server-address ")) + case strings.HasPrefix(itemTrimAttr, "sip-server name "): + dhcpAttr["sip_server_inet_domain_name"] = append(dhcpAttr["sip_server_inet_domain_name"].([]string), + strings.Trim(strings.TrimPrefix(itemTrimAttr, "sip-server name "), "\"")) + case strings.HasPrefix(itemTrimAttr, "sip-server-domain-name "): + dhcpAttr["sip_server_inet6_domain_name"] = + strings.Trim(strings.TrimPrefix(itemTrimAttr, "sip-server-domain-name "), "\"") + case strings.HasPrefix(itemTrimAttr, "t1-percentage "): + dhcpAttr["t1_percentage"], err = strconv.Atoi(strings.TrimPrefix(itemTrimAttr, "t1-percentage ")) + case strings.HasPrefix(itemTrimAttr, "t1-renewal-time "): + dhcpAttr["t1_renewal_time"], err = strconv.Atoi(strings.TrimPrefix(itemTrimAttr, "t1-renewal-time ")) + case strings.HasPrefix(itemTrimAttr, "t2-percentage "): + dhcpAttr["t2_percentage"], err = strconv.Atoi(strings.TrimPrefix(itemTrimAttr, "t2-percentage ")) + case strings.HasPrefix(itemTrimAttr, "t2-rebinding-time "): + dhcpAttr["t2_rebinding_time"], err = strconv.Atoi(strings.TrimPrefix(itemTrimAttr, "t2-rebinding-time ")) + case strings.HasPrefix(itemTrimAttr, "tftp-server "): + dhcpAttr["tftp_server"] = strings.TrimPrefix(itemTrimAttr, "tftp-server ") + case itemTrimAttr == "valid-lifetime infinite": + dhcpAttr["valid_lifetime_infinite"] = true + case strings.HasPrefix(itemTrimAttr, "valid-lifetime "): + dhcpAttr["valid_lifetime"], err = strconv.Atoi(strings.TrimPrefix(itemTrimAttr, "valid-lifetime ")) + case strings.HasPrefix(itemTrimAttr, "wins-server "): + dhcpAttr["wins_server"] = append(dhcpAttr["wins_server"].([]string), + strings.TrimPrefix(itemTrimAttr, "wins-server ")) + } + case strings.HasPrefix(itemTrim, "excluded-address "): + family["excluded_address"] = append(family["excluded_address"].([]string), + strings.TrimPrefix(itemTrim, "excluded-address ")) + case strings.HasPrefix(itemTrim, "excluded-range "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "excluded-range "), " ") + familyExcludedRange := map[string]interface{}{ + "name": itemTrimSplit[0], + "low": "", + "high": "", + } + family["excluded_range"] = copyAndRemoveItemMapList( + "name", familyExcludedRange, family["excluded_range"].([]map[string]interface{})) + itemTrimExcludedRange := strings.TrimPrefix(itemTrim, "excluded-range "+itemTrimSplit[0]+" ") + switch { + case strings.HasPrefix(itemTrimExcludedRange, "low "): + familyExcludedRange["low"] = strings.TrimPrefix(itemTrimExcludedRange, "low ") + case strings.HasPrefix(itemTrimExcludedRange, "high "): + familyExcludedRange["high"] = strings.TrimPrefix(itemTrimExcludedRange, "high ") + } + family["excluded_range"] = append(family["excluded_range"].([]map[string]interface{}), familyExcludedRange) + case strings.HasPrefix(itemTrim, "host "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "host "), " ") + familyHost := map[string]interface{}{ + "name": itemTrimSplit[0], + "hardware_address": "", + "ip_address": "", + } + family["host"] = copyAndRemoveItemMapList( + "name", familyHost, family["host"].([]map[string]interface{})) + itemTrimHost := strings.TrimPrefix(itemTrim, "host "+itemTrimSplit[0]+" ") + switch { + case strings.HasPrefix(itemTrimHost, "hardware-address "): + familyHost["hardware_address"] = strings.TrimPrefix(itemTrimHost, "hardware-address ") + case strings.HasPrefix(itemTrimHost, "ip-address "): + familyHost["ip_address"] = strings.TrimPrefix(itemTrimHost, "ip-address ") + } + family["host"] = append(family["host"].([]map[string]interface{}), familyHost) + case strings.HasPrefix(itemTrim, "range "): + if family["type"] == inetWord { + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "range "), " ") + familyInetRange := map[string]interface{}{ + "name": itemTrimSplit[0], + "low": "", + "high": "", + } + family["inet_range"] = copyAndRemoveItemMapList( + "name", familyInetRange, family["inet_range"].([]map[string]interface{})) + itemTrimRange := strings.TrimPrefix(itemTrim, "range "+itemTrimSplit[0]+" ") + switch { + case strings.HasPrefix(itemTrimRange, "low "): + familyInetRange["low"] = strings.TrimPrefix(itemTrimRange, "low ") + case strings.HasPrefix(itemTrimRange, "high "): + familyInetRange["high"] = strings.TrimPrefix(itemTrimRange, "high ") + } + family["inet_range"] = append(family["inet_range"].([]map[string]interface{}), familyInetRange) + } else if family["type"] == inet6Word { + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "range "), " ") + familyInet6Range := map[string]interface{}{ + "name": itemTrimSplit[0], + "low": "", + "high": "", + "prefix_length": 0, + } + family["inet6_range"] = copyAndRemoveItemMapList( + "name", familyInet6Range, family["inet6_range"].([]map[string]interface{})) + itemTrimRange := strings.TrimPrefix(itemTrim, "range "+itemTrimSplit[0]+" ") + switch { + case strings.HasPrefix(itemTrimRange, "low "): + familyInet6Range["low"] = strings.TrimPrefix(itemTrimRange, "low ") + case strings.HasPrefix(itemTrimRange, "high "): + familyInet6Range["high"] = strings.TrimPrefix(itemTrimRange, "high ") + case strings.HasPrefix(itemTrimRange, "prefix-length "): + familyInet6Range["prefix_length"], err = strconv.Atoi(strings.TrimPrefix(itemTrimRange, "prefix-length ")) + } + family["inet6_range"] = append(family["inet6_range"].([]map[string]interface{}), familyInet6Range) + } + case strings.HasPrefix(itemTrim, "xauth-attributes primary-dns "): + family["xauth_attributes_primary_dns"] = strings.TrimPrefix(itemTrim, "xauth-attributes primary-dns ") + case strings.HasPrefix(itemTrim, "xauth-attributes primary-wins "): + family["xauth_attributes_primary_wins"] = strings.TrimPrefix(itemTrim, "xauth-attributes primary-wins ") + case strings.HasPrefix(itemTrim, "xauth-attributes secondary-dns "): + family["xauth_attributes_secondary_dns"] = strings.TrimPrefix(itemTrim, "xauth-attributes secondary-dns ") + case strings.HasPrefix(itemTrim, "xauth-attributes secondary-wins "): + family["xauth_attributes_secondary_wins"] = strings.TrimPrefix(itemTrim, "xauth-attributes secondary-wins ") + } + if err != nil { + return fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + + return nil +} + +func delAccessAddressAssignPool(name string, instance string, m interface{}, jnprSess *NetconfObject) error { + sess := m.(*Session) + configSet := make([]string, 0, 1) + if instance == defaultWord { + configSet = append(configSet, "delete access address-assignment pool "+name) + } else { + configSet = append(configSet, "delete routing-instances "+instance+" access address-assignment pool "+name) + } + + return sess.configSet(configSet, jnprSess) +} + +func fillAccessAddressAssignPoolData( + d *schema.ResourceData, accessAddressAssignPoolOptions accessAddressAssignPoolOptions) { + if tfErr := d.Set("name", accessAddressAssignPoolOptions.name); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("routing_instance", accessAddressAssignPoolOptions.routingInstance); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("family", accessAddressAssignPoolOptions.family); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("active_drain", accessAddressAssignPoolOptions.activeDrain); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("hold_down", accessAddressAssignPoolOptions.holdDown); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("link", accessAddressAssignPoolOptions.link); tfErr != nil { + panic(tfErr) + } +} diff --git a/junos/resource_access_address_assignment_pool_test.go b/junos/resource_access_address_assignment_pool_test.go new file mode 100644 index 00000000..d37c99ac --- /dev/null +++ b/junos/resource_access_address_assignment_pool_test.go @@ -0,0 +1,210 @@ +package junos_test + +import ( + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccJunosAccessAddressAssignmentPool_basic(t *testing.T) { + if os.Getenv("TESTACC_SWITCH") == "" { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccJunosAccessAddressAssignmentPoolCreate(), + }, + { + Config: testAccJunosAccessAddressAssignmentPoolUpdate(), + }, + { + ResourceName: "junos_access_address_assignment_pool.testacc_accessAddAssP4", + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "junos_access_address_assignment_pool.testacc_accessAddAssP6_1", + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "junos_access_address_assignment_pool.testacc_accessAddAssP6_2", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + } +} + +func testAccJunosAccessAddressAssignmentPoolCreate() string { + return ` +resource "junos_access_address_assignment_pool" "testacc_accessAddAssP4" { + name = "testacc_accessAddAssP4" + family { + type = "inet" + network = "192.0.2.128/25" + } +} + +resource "junos_routing_instance" "testacc_accessAddAssP6" { + name = "testacc_accessAddAssP6" +} +resource "junos_access_address_assignment_pool" "testacc_accessAddAssP6_1" { + name = "testacc_accessAddAssP6_1" + routing_instance = junos_routing_instance.testacc_accessAddAssP6.name + family { + type = "inet6" + network = "fe80:0:0:b::/64" + } +} +` +} + +func testAccJunosAccessAddressAssignmentPoolUpdate() string { + return ` +resource junos_interface_logical "testacc_accessAddAssP4" { + name = "lo0.1" +} +resource "junos_access_address_assignment_pool" "testacc_accessAddAssP4" { + name = "testacc_accessAddAssP4" + family { + type = "inet" + network = "192.0.2.128/25" + excluded_address = [ + "192.0.2.201", + "192.0.2.200", + ] + excluded_range { + name = "excl1" + low = "192.0.2.208" + high = "192.0.2.210" + } + excluded_range { + name = "excl2" + low = "192.0.2.219" + high = "192.0.2.220" + } + host { + name = "host1" + hardware_address = "aa:bb:cc:dd:ee:ff" + ip_address = "192.0.2.211" + } + inet_range { + name = "range2" + low = "192.0.2.225" + high = "192.0.2.230" + } + inet_range { + name = "range1" + low = "192.0.2.200" + high = "192.0.2.220" + } + xauth_attributes_primary_dns = "192.0.2.53/32" + xauth_attributes_primary_wins = "192.0.2.54/32" + xauth_attributes_secondary_dns = "192.0.2.55/32" + xauth_attributes_secondary_wins = "192.0.2.56/32" + dhcp_attributes { + boot_file = "file.boot" + boot_server = "test.com" + domain_name = "test.com" + grace_period = 3600 + maximum_lease_time = 3600 + name_server = ["192.0.2.2"] + netbios_node_type = "b-node" + next_server = "192.0.2.3" + option = [ + "1 string a", + "2 flag true", + ] + option_match_82_circuit_id { + value = "bb" + range = "cc" + } + option_match_82_remote_id { + value = "dd" + range = "ee" + } + propagate_ppp_settings = [junos_interface_logical.testacc_accessAddAssP4.name, ] + propagate_settings = "ff" + router = ["192.0.2.121", "192.0.2.120"] + server_identifier = "192.0.2.6" + sip_server_inet_address = ["192.0.2.62", "192.0.2.61"] + sip_server_inet_domain_name = ["domain.name"] + t1_percentage = 50 + t2_percentage = 70 + tftp_server = "192.0.2.7" + wins_server = ["192.0.2.72", "192.0.2.71"] + } + } + active_drain = true + hold_down = true +} + +resource "junos_routing_instance" "testacc_accessAddAssP6" { + name = "testacc_accessAddAssP6" +} +resource "junos_access_address_assignment_pool" "testacc_accessAddAssP6_1" { + name = "testacc_accessAddAssP6_1" + routing_instance = junos_routing_instance.testacc_accessAddAssP6.name + family { + type = "inet6" + network = "fe80:0:0:b::/64" + excluded_address = [ + "fe80:0:0:b::bb", + "fe80:0:0:b::aa", + ] + inet6_range { + name = "range62" + low = "fe80:0:0:b:1::bbbb/80" + high = "fe80:0:0:b:1::cccc/80" + } + inet6_range { + name = "range6" + low = "fe80:0:0:b:2::bbbb/80" + high = "fe80:0:0:b:2::aaaa/80" + } + inet6_range { + name = "range_pref" + prefix_length = 100 + } + dhcp_attributes { + dns_server = ["fe80::1"] + exclude_prefix_len = "65" + maximum_lease_time_infinite = true + sip_server_inet6_address = ["fe80:0:0:b:b::cc", "fe80:0:0:b:b::dc"] + sip_server_inet6_domain_name = "domain2.name" + t1_renewal_time = 3600 + t2_rebinding_time = 3600 + } + } +} +resource "junos_access_address_assignment_pool" "testacc_accessAddAssP6_2" { + name = "testacc_accessAddAssP6_2" + routing_instance = junos_routing_instance.testacc_accessAddAssP6.name + family { + type = "inet6" + network = "fe80:0:0:a::/64" + dhcp_attributes { + preferred_lifetime = 7200 + valid_lifetime = 4800 + } + } + link = junos_access_address_assignment_pool.testacc_accessAddAssP6_1.name +} +resource "junos_access_address_assignment_pool" "testacc_accessAddAssP6_3" { + name = "testacc_accessAddAssP6_3" + routing_instance = junos_routing_instance.testacc_accessAddAssP6.name + family { + type = "inet6" + network = "fe80:0:0:c::/64" + dhcp_attributes { + preferred_lifetime_infinite = true + valid_lifetime_infinite = true + } + } +} +` +} diff --git a/junos/resource_application.go b/junos/resource_application.go index 7a3ae2ad..fa4f5ded 100644 --- a/junos/resource_application.go +++ b/junos/resource_application.go @@ -10,19 +10,22 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + bchk "github.com/jeremmfr/go-utils/basiccheck" ) type applicationOptions struct { - inactivityTimeout int - name string - applicationProtocol string - description string - destinationPort string - etherType string - protocol string - rpcProgramNumber string - sourcePort string - uuid string + inactivityTimeoutNever bool + inactivityTimeout int + name string + applicationProtocol string + description string + destinationPort string + etherType string + protocol string + rpcProgramNumber string + sourcePort string + uuid string + term []map[string]interface{} } func resourceApplication() *schema.Resource { @@ -60,9 +63,15 @@ func resourceApplication() *schema.Resource { `^0[xX][0-9a-fA-F]{4}$`), "must be in hex (example: 0x8906)"), }, "inactivity_timeout": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(4, 86400), + Type: schema.TypeInt, + Optional: true, + ConflictsWith: []string{"inactivity_timeout_never"}, + ValidateFunc: validation.IntBetween(4, 86400), + }, + "inactivity_timeout_never": { + Type: schema.TypeBool, + Optional: true, + ConflictsWith: []string{"inactivity_timeout"}, }, "protocol": { Type: schema.TypeString, @@ -78,6 +87,83 @@ func resourceApplication() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "term": { + Type: schema.TypeList, + Optional: true, + ConflictsWith: []string{ + "application_protocol", + "destination_port", + "inactivity_timeout", + "inactivity_timeout_never", + "protocol", + "rpc_program_number", + "source_port", + "uuid", + }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), + }, + "protocol": { + Type: schema.TypeString, + Required: true, + }, + "alg": { + Type: schema.TypeString, + Optional: true, + }, + "destination_port": { + Type: schema.TypeString, + Optional: true, + }, + "icmp_code": { + Type: schema.TypeString, + Optional: true, + }, + "icmp_type": { + Type: schema.TypeString, + Optional: true, + }, + "icmp6_code": { + Type: schema.TypeString, + Optional: true, + }, + "icmp6_type": { + Type: schema.TypeString, + Optional: true, + }, + "inactivity_timeout": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(4, 86400), + }, + "inactivity_timeout_never": { + Type: schema.TypeBool, + Optional: true, + }, + "rpc_program_number": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringMatch(regexp.MustCompile( + `^\d+(-\d+)?$`), "must be an integer or a range of integers"), + }, + "source_port": { + Type: schema.TypeString, + Optional: true, + }, + "uuid": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringMatch(regexp.MustCompile( + `^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`), + "must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"), + }, + }, + }, + }, "uuid": { Type: schema.TypeString, Optional: true, @@ -285,6 +371,8 @@ func setApplication(d *schema.ResourceData, m interface{}, jnprSess *NetconfObje } if v := d.Get("inactivity_timeout").(int); v != 0 { configSet = append(configSet, setPrefix+"inactivity-timeout "+strconv.Itoa(v)) + } else if d.Get("inactivity_timeout_never").(bool) { + configSet = append(configSet, setPrefix+"inactivity-timeout never") } if v := d.Get("protocol").(string); v != "" { configSet = append(configSet, setPrefix+"protocol "+v) @@ -295,6 +383,17 @@ func setApplication(d *schema.ResourceData, m interface{}, jnprSess *NetconfObje if v := d.Get("source_port").(string); v != "" { configSet = append(configSet, setPrefix+"source-port "+v) } + termName := make([]string, 0) + for _, v := range d.Get("term").([]interface{}) { + term := v.(map[string]interface{}) + if bchk.StringInSlice(term["name"].(string), termName) { + return fmt.Errorf("multiple blocks term with the same name %s", term["name"].(string)) + } + termName = append(termName, term["name"].(string)) + if err := setApplicationTerm(setPrefix, term, sess, jnprSess); err != nil { + return err + } + } if v := d.Get("uuid").(string); v != "" { configSet = append(configSet, setPrefix+"uuid "+v) } @@ -302,6 +401,52 @@ func setApplication(d *schema.ResourceData, m interface{}, jnprSess *NetconfObje return sess.configSet(configSet, jnprSess) } +func setApplicationTerm(setApp string, term map[string]interface{}, sess *Session, jnprSess *NetconfObject) error { + configSet := make([]string, 0) + setPrefix := setApp + "term " + term["name"].(string) + " " + + configSet = append(configSet, setPrefix) + configSet = append(configSet, setPrefix+"protocol "+term["protocol"].(string)) + if v := term["alg"].(string); v != "" { + configSet = append(configSet, setPrefix+"alg "+v) + } + if v := term["destination_port"].(string); v != "" { + configSet = append(configSet, setPrefix+"destination-port "+v) + } + if v := term["icmp_code"].(string); v != "" { + configSet = append(configSet, setPrefix+"icmp-code "+v) + } + if v := term["icmp_type"].(string); v != "" { + configSet = append(configSet, setPrefix+"icmp-type "+v) + } + if v := term["icmp6_code"].(string); v != "" { + configSet = append(configSet, setPrefix+"icmp6-code "+v) + } + if v := term["icmp6_type"].(string); v != "" { + configSet = append(configSet, setPrefix+"icmp6-type "+v) + } + if v := term["inactivity_timeout"].(int); v != 0 { + configSet = append(configSet, setPrefix+"inactivity-timeout "+strconv.Itoa(v)) + if term["inactivity_timeout_never"].(bool) { + return fmt.Errorf("conflict between 'inactivity_timeout' and 'inactivity_timeout_never' "+ + "in term %s", term["name"].(string)) + } + } else if term["inactivity_timeout_never"].(bool) { + configSet = append(configSet, setPrefix+"inactivity-timeout never") + } + if v := term["rpc_program_number"].(string); v != "" { + configSet = append(configSet, setPrefix+"rpc-program-number "+v) + } + if v := term["source_port"].(string); v != "" { + configSet = append(configSet, setPrefix+"source-port "+v) + } + if v := term["uuid"].(string); v != "" { + configSet = append(configSet, setPrefix+"uuid "+v) + } + + return sess.configSet(configSet, jnprSess) +} + func readApplication(application string, m interface{}, jnprSess *NetconfObject) (applicationOptions, error) { sess := m.(*Session) var confRead applicationOptions @@ -330,6 +475,8 @@ func readApplication(application string, m interface{}, jnprSess *NetconfObject) confRead.destinationPort = strings.TrimPrefix(itemTrim, "destination-port ") case strings.HasPrefix(itemTrim, "ether-type "): confRead.etherType = strings.TrimPrefix(itemTrim, "ether-type ") + case itemTrim == "inactivity-timeout never": + confRead.inactivityTimeoutNever = true case strings.HasPrefix(itemTrim, "inactivity-timeout "): var err error confRead.inactivityTimeout, err = strconv.Atoi(strings.TrimPrefix(itemTrim, "inactivity-timeout ")) @@ -342,6 +489,28 @@ func readApplication(application string, m interface{}, jnprSess *NetconfObject) confRead.rpcProgramNumber = strings.TrimPrefix(itemTrim, "rpc-program-number ") case strings.HasPrefix(itemTrim, "source-port "): confRead.sourcePort = strings.TrimPrefix(itemTrim, "source-port ") + case strings.HasPrefix(itemTrim, "term "): + itemTermList := strings.Split(strings.TrimPrefix(itemTrim, "term "), " ") + termOpts := map[string]interface{}{ + "name": itemTermList[0], + "protocol": "", + "alg": "", + "destination_port": "", + "icmp_code": "", + "icmp_type": "", + "icmp6_code": "", + "icmp6_type": "", + "inactivity_timeout": 0, + "inactivity_timeout_never": false, + "rpc_program_number": "", + "source_port": "", + "uuid": "", + } + confRead.term = copyAndRemoveItemMapList("name", termOpts, confRead.term) + if err := readApplicationTerm(strings.TrimPrefix(itemTrim, "term "+itemTermList[0]+" "), termOpts); err != nil { + return confRead, err + } + confRead.term = append(confRead.term, termOpts) case strings.HasPrefix(itemTrim, "uuid "): confRead.uuid = strings.TrimPrefix(itemTrim, "uuid ") } @@ -351,6 +520,41 @@ func readApplication(application string, m interface{}, jnprSess *NetconfObject) return confRead, nil } +func readApplicationTerm(itemTrim string, term map[string]interface{}) error { + switch { + case strings.HasPrefix(itemTrim, "protocol "): + term["protocol"] = strings.TrimPrefix(itemTrim, "protocol ") + case strings.HasPrefix(itemTrim, "alg "): + term["alg"] = strings.TrimPrefix(itemTrim, "alg ") + case strings.HasPrefix(itemTrim, "destination-port "): + term["destination_port"] = strings.TrimPrefix(itemTrim, "destination-port ") + case strings.HasPrefix(itemTrim, "icmp-code "): + term["icmp_code"] = strings.TrimPrefix(itemTrim, "icmp-code ") + case strings.HasPrefix(itemTrim, "icmp-type "): + term["icmp_type"] = strings.TrimPrefix(itemTrim, "icmp-type ") + case strings.HasPrefix(itemTrim, "icmp6-code "): + term["icmp6_code"] = strings.TrimPrefix(itemTrim, "icmp6-code ") + case strings.HasPrefix(itemTrim, "icmp6-type "): + term["icmp6_type"] = strings.TrimPrefix(itemTrim, "icmp6-type ") + case itemTrim == "inactivity-timeout never": + term["inactivity_timeout_never"] = true + case strings.HasPrefix(itemTrim, "inactivity-timeout "): + var err error + term["inactivity_timeout"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "inactivity-timeout ")) + if err != nil { + return fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + case strings.HasPrefix(itemTrim, "rpc-program-number "): + term["rpc_program_number"] = strings.TrimPrefix(itemTrim, "rpc-program-number ") + case strings.HasPrefix(itemTrim, "source-port "): + term["source_port"] = strings.TrimPrefix(itemTrim, "source-port ") + case strings.HasPrefix(itemTrim, "uuid "): + term["uuid"] = strings.TrimPrefix(itemTrim, "uuid ") + } + + return nil +} + func delApplication(d *schema.ResourceData, m interface{}, jnprSess *NetconfObject) error { sess := m.(*Session) configSet := make([]string, 0, 1) @@ -378,6 +582,9 @@ func fillApplicationData(d *schema.ResourceData, applicationOptions applicationO if tfErr := d.Set("inactivity_timeout", applicationOptions.inactivityTimeout); tfErr != nil { panic(tfErr) } + if tfErr := d.Set("inactivity_timeout_never", applicationOptions.inactivityTimeoutNever); tfErr != nil { + panic(tfErr) + } if tfErr := d.Set("protocol", applicationOptions.protocol); tfErr != nil { panic(tfErr) } @@ -387,6 +594,9 @@ func fillApplicationData(d *schema.ResourceData, applicationOptions applicationO if tfErr := d.Set("source_port", applicationOptions.sourcePort); tfErr != nil { panic(tfErr) } + if tfErr := d.Set("term", applicationOptions.term); tfErr != nil { + panic(tfErr) + } if tfErr := d.Set("uuid", applicationOptions.uuid); tfErr != nil { panic(tfErr) } diff --git a/junos/resource_application_test.go b/junos/resource_application_test.go index 2fe8d489..4abd3e57 100644 --- a/junos/resource_application_test.go +++ b/junos/resource_application_test.go @@ -38,6 +38,21 @@ func TestAccJunosApplication_basic(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + { + ResourceName: "junos_application.testacc_app3", + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "junos_application.testacc_app4", + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "junos_application.testacc_app5", + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -50,6 +65,14 @@ resource "junos_application" "testacc_app" { protocol = "tcp" destination_port = 22 } +resource "junos_application" "testacc_app3" { + name = "testacc_app3" + term { + name = "term_B" + protocol = "tcp" + destination_port = 22 + } +} ` } @@ -65,11 +88,52 @@ resource "junos_application" "testacc_app" { source_port = "1024-65535" } resource "junos_application" "testacc_app2" { - name = "testacc_app2" - protocol = "tcp" - ether_type = "0x0800" - rpc_program_number = "0-0" - uuid = "AAAAA0AA-B9B0-CCcc-DDDD-EEEffFFFAAAA" + name = "testacc_app2" + protocol = "tcp" + ether_type = "0x0800" + rpc_program_number = "0-0" + inactivity_timeout_never = true + uuid = "AAAAA0AA-B9B0-CCcc-DDDD-EEEffFFFAAAA" +} +resource "junos_application" "testacc_app3" { + name = "testacc_app3" + term { + name = "term_B" + protocol = "tcp" + destination_port = 22 + inactivity_timeout = 600 + source_port = "1024-65535" + } + term { + name = "term_ALG" + protocol = "tcp" + alg = "ssh" + } +} +resource "junos_application" "testacc_app4" { + name = "testacc_app4" + term { + name = "term_B" + protocol = "tcp" + rpc_program_number = "1-1" + inactivity_timeout_never = true + uuid = "BBBAA0AA-B9B0-CCcc-DDDD-EEEffFFFAAAA" + } +} +resource "junos_application" "testacc_app5" { + name = "testacc_app5" + term { + name = "term_I" + protocol = "icmp" + icmp_code = "1" + icmp_type = "echo-reply" + } + term { + name = "term_I6" + protocol = "icmp6" + icmp6_code = "1" + icmp6_type = "echo-reply" + } } ` } diff --git a/junos/resource_chassis_cluster.go b/junos/resource_chassis_cluster.go index 7a40034f..368f199b 100644 --- a/junos/resource_chassis_cluster.go +++ b/junos/resource_chassis_cluster.go @@ -18,6 +18,7 @@ type chassisClusterOptions struct { heartbeatInterval int heartbeatThreshold int rethCount int + controlPorts []map[string]interface{} redundancyGroup []map[string]interface{} fab0 []map[string]interface{} fab1 []map[string]interface{} @@ -150,6 +151,24 @@ func resourceChassisCluster() *schema.Resource { Type: schema.TypeBool, Optional: true, }, + "control_ports": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "fpc": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(0, 23), + }, + "port": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(0, 1), + }, + }, + }, + }, "heartbeat_interval": { Type: schema.TypeInt, Optional: true, @@ -401,6 +420,11 @@ func setChassisCluster(d *schema.ResourceData, m interface{}, jnprSess *NetconfO if d.Get("control_link_recovery").(bool) { configSet = append(configSet, setChassisluster+"control-link-recovery") } + for _, cp := range d.Get("control_ports").(*schema.Set).List() { + controlPort := cp.(map[string]interface{}) + configSet = append(configSet, setChassisluster+"control-ports fpc "+ + strconv.Itoa(controlPort["fpc"].(int))+" port "+strconv.Itoa(controlPort["port"].(int))) + } if v := d.Get("heartbeat_interval").(int); v != 0 { configSet = append(configSet, setChassisluster+"heartbeat-interval "+ strconv.Itoa(v)) @@ -544,6 +568,22 @@ func readChassisCluster(m interface{}, jnprSess *NetconfObject) (chassisClusterO } case itemTrim == "configuration-synchronize no-secondary-bootup-auto": confRead.configSyncNoSecBootAuto = true + case strings.HasPrefix(itemTrim, "control-ports fpc "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "control-ports fpc "), " ") + if len(itemTrimSplit) < 3 { + return confRead, fmt.Errorf("can't read values for control-ports fpc in '%s'", itemTrim) + } + controlPort := make(map[string]interface{}) + var err error + controlPort["fpc"], err = strconv.Atoi(itemTrimSplit[0]) + if err != nil { + return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrimSplit[0], err) + } + controlPort["port"], err = strconv.Atoi(itemTrimSplit[2]) + if err != nil { + return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrimSplit[2], err) + } + confRead.controlPorts = append(confRead.controlPorts, controlPort) case itemTrim == "control-link-recovery": confRead.controlLinkRecovery = true case strings.HasPrefix(itemTrim, "heartbeat-interval "): @@ -638,6 +678,9 @@ func fillChassisCluster(d *schema.ResourceData, chassisClusterOptions chassisClu chassisClusterOptions.configSyncNoSecBootAuto); tfErr != nil { panic(tfErr) } + if tfErr := d.Set("control_ports", chassisClusterOptions.controlPorts); tfErr != nil { + panic(tfErr) + } if tfErr := d.Set("control_link_recovery", chassisClusterOptions.controlLinkRecovery); tfErr != nil { panic(tfErr) } diff --git a/junos/resource_group_dual_system.go b/junos/resource_group_dual_system.go index b565e49b..7ef3fac1 100644 --- a/junos/resource_group_dual_system.go +++ b/junos/resource_group_dual_system.go @@ -155,11 +155,22 @@ func resourceGroupDualSystem() *schema.Resource { Optional: true, }, "backup_router_address": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.IsIPv4Address, }, "backup_router_destination": { - Type: schema.TypeList, + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "inet6_backup_router_address": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateIsIPv6Address, + }, + "inet6_backup_router_destination": { + Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, @@ -474,8 +485,14 @@ func setGroupDualSystem(d *schema.ResourceData, m interface{}, jnprSess *Netconf if v2 := system["backup_router_address"].(string); v2 != "" { configSet = append(configSet, setPrefix+" system backup-router "+v2) } - for _, v2 := range system["backup_router_destination"].([]interface{}) { - configSet = append(configSet, setPrefix+" system backup-router destination "+v2.(string)) + for _, v2 := range sortSetOfString(system["backup_router_destination"].(*schema.Set).List()) { + configSet = append(configSet, setPrefix+" system backup-router destination "+v2) + } + if v2 := system["inet6_backup_router_address"].(string); v2 != "" { + configSet = append(configSet, setPrefix+" system inet6-backup-router "+v2) + } + for _, v2 := range sortSetOfString(system["inet6_backup_router_destination"].(*schema.Set).List()) { + configSet = append(configSet, setPrefix+" system inet6-backup-router destination "+v2) } } @@ -586,9 +603,11 @@ func readGroupDualSystem(group string, m interface{}, jnprSess *NetconfObject) ( case strings.HasPrefix(itemTrim, "system"): if len(confRead.system) == 0 { confRead.system = append(confRead.system, map[string]interface{}{ - "host_name": "", - "backup_router_address": "", - "backup_router_destination": make([]string, 0), + "host_name": "", + "backup_router_address": "", + "backup_router_destination": make([]string, 0), + "inet6_backup_router_address": "", + "inet6_backup_router_destination": make([]string, 0), }) } switch { @@ -600,6 +619,12 @@ func readGroupDualSystem(group string, m interface{}, jnprSess *NetconfObject) ( strings.TrimPrefix(itemTrim, "system backup-router destination ")) case strings.HasPrefix(itemTrim, "system backup-router "): confRead.system[0]["backup_router_address"] = strings.TrimPrefix(itemTrim, "system backup-router ") + case strings.HasPrefix(itemTrim, "system inet6-backup-router destination "): + confRead.system[0]["inet6_backup_router_destination"] = append( + confRead.system[0]["inet6_backup_router_destination"].([]string), + strings.TrimPrefix(itemTrim, "system inet6-backup-router destination ")) + case strings.HasPrefix(itemTrim, "system inet6-backup-router "): + confRead.system[0]["inet6_backup_router_address"] = strings.TrimPrefix(itemTrim, "system inet6-backup-router ") } } } diff --git a/junos/resource_group_dual_system_test.go b/junos/resource_group_dual_system_test.go index 62961e2a..8e67726a 100644 --- a/junos/resource_group_dual_system_test.go +++ b/junos/resource_group_dual_system_test.go @@ -33,8 +33,8 @@ func TestAccJunosGroupDualSystem_basic(t *testing.T) { "system.0.backup_router_destination.#", "2"), resource.TestCheckResourceAttr("junos_group_dual_system.testacc_node0", "system.#", "1"), - resource.TestCheckResourceAttr("junos_group_dual_system.testacc_node0", - "system.0.backup_router_destination.1", "192.0.2.0/26"), + resource.TestCheckTypeSetElemAttr("junos_group_dual_system.testacc_node0", + "system.0.backup_router_destination.*", "192.0.2.0/26"), ), }, { @@ -79,6 +79,10 @@ resource "junos_group_dual_system" "testacc_node0" { backup_router_destination = [ "192.0.2.0/26", ] + inet6_backup_router_address = "fe80::1" + inet6_backup_router_destination = [ + "fe80:a::/48", + ] } } ` @@ -122,7 +126,11 @@ resource "junos_group_dual_system" "testacc_node0" { backup_router_destination = [ "192.0.2.64/26", "192.0.2.0/26", - + ] + inet6_backup_router_address = "fe80::1" + inet6_backup_router_destination = [ + "fe80:b::/48", + "fe80:a::/48", ] } } diff --git a/junos/resource_interface_logical.go b/junos/resource_interface_logical.go index 8e94ff50..7b68d8ae 100644 --- a/junos/resource_interface_logical.go +++ b/junos/resource_interface_logical.go @@ -64,8 +64,9 @@ func resourceInterfaceLogical() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "address": { - Type: schema.TypeList, - Optional: true, + Type: schema.TypeList, + Optional: true, + ConflictsWith: []string{"family_inet.0.dhcp"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "cidr_ip": { @@ -182,6 +183,109 @@ func resourceInterfaceLogical() *schema.Resource { }, }, }, + "dhcp": { + Type: schema.TypeList, + Optional: true, + ConflictsWith: []string{"family_inet.0.address"}, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "client_identifier_ascii": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"family_inet.0.dhcp.0.client_identifier_hexadecimal"}, + }, + "client_identifier_hexadecimal": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"family_inet.0.dhcp.0.client_identifier_ascii"}, + ValidateFunc: validation.StringMatch( + regexp.MustCompile(`^[0-9a-fA-F]+$`), + "must be hexadecimal digits (0-9, a-f, A-F)"), + }, + "client_identifier_prefix_hostname": { + Type: schema.TypeBool, + Optional: true, + }, + "client_identifier_prefix_routing_instance_name": { + Type: schema.TypeBool, + Optional: true, + }, + "client_identifier_use_interface_description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"device", "logical"}, false), + }, + "client_identifier_userid_ascii": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"family_inet.0.dhcp.0.client_identifier_userid_hexadecimal"}, + }, + "client_identifier_userid_hexadecimal": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"family_inet.0.dhcp.0.client_identifier_userid_ascii"}, + ValidateFunc: validation.StringMatch( + regexp.MustCompile(`^[0-9a-fA-F]+$`), + "must be hexadecimal digits (0-9, a-f, A-F)"), + }, + "force_discover": { + Type: schema.TypeBool, + Optional: true, + }, + "lease_time": { + Type: schema.TypeInt, + Optional: true, + ConflictsWith: []string{"family_inet.0.dhcp.0.lease_time_infinite"}, + ValidateFunc: validation.IntBetween(60, 2147483647), + }, + "lease_time_infinite": { + Type: schema.TypeBool, + Optional: true, + ConflictsWith: []string{"family_inet.0.dhcp.0.lease_time"}, + }, + "metric": { + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntBetween(0, 255), + }, + "no_dns_install": { + Type: schema.TypeBool, + Optional: true, + }, + "options_no_hostname": { + Type: schema.TypeBool, + Optional: true, + }, + "retransmission_attempt": { + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntBetween(0, 50000), + }, + "retransmission_interval": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(4, 64), + }, + "server_address": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.IsIPv4Address, + }, + "update_server": { + Type: schema.TypeBool, + Optional: true, + }, + "vendor_id": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 60), + }, + }, + }, + }, "filter_input": { Type: schema.TypeString, Optional: true, @@ -232,8 +336,9 @@ func resourceInterfaceLogical() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "address": { - Type: schema.TypeList, - Optional: true, + Type: schema.TypeList, + Optional: true, + ConflictsWith: []string{"family_inet6.0.dhcpv6_client"}, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "cidr_ip": { @@ -349,6 +454,81 @@ func resourceInterfaceLogical() *schema.Resource { Type: schema.TypeBool, Optional: true, }, + "dhcpv6_client": { + Type: schema.TypeList, + Optional: true, + ConflictsWith: []string{"family_inet6.0.address"}, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "client_identifier_duid_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"duid-ll", "duid-llt", "vendor"}, false), + }, + "client_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"autoconfig", "stateful"}, false), + }, + "client_ia_type_na": { + Type: schema.TypeBool, + Optional: true, + AtLeastOneOf: []string{ + "family_inet6.0.dhcpv6_client.0.client_ia_type_na", + "family_inet6.0.dhcpv6_client.0.client_ia_type_pd", + }, + }, + "client_ia_type_pd": { + Type: schema.TypeBool, + Optional: true, + AtLeastOneOf: []string{ + "family_inet6.0.dhcpv6_client.0.client_ia_type_na", + "family_inet6.0.dhcpv6_client.0.client_ia_type_pd", + }, + }, + "no_dns_install": { + Type: schema.TypeBool, + Optional: true, + }, + "prefix_delegating_preferred_prefix_length": { + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntBetween(0, 64), + }, + "prefix_delegating_sub_prefix_length": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 127), + }, + "rapid_commit": { + Type: schema.TypeBool, + Optional: true, + }, + "req_option": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "retransmission_attempt": { + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntBetween(0, 9), + }, + "update_router_advertisement_interface": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "update_server": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, "filter_input": { Type: schema.TypeString, Optional: true, @@ -823,6 +1003,9 @@ func setInterfaceLogical(d *schema.ResourceData, m interface{}, jnprSess *Netcon return err } configSet = append(configSet, configSetFamilyInet...) + for _, dhcp := range familyInet["dhcp"].([]interface{}) { + configSet = append(configSet, setFamilyInetDhcp(dhcp.(map[string]interface{}), setPrefix)...) + } if familyInet["filter_input"].(string) != "" { configSet = append(configSet, setPrefix+"family inet filter input "+ familyInet["filter_input"].(string)) @@ -865,6 +1048,9 @@ func setInterfaceLogical(d *schema.ResourceData, m interface{}, jnprSess *Netcon return err } configSet = append(configSet, configSetFamilyInet6...) + for _, dhcp := range familyInet6["dhcpv6_client"].([]interface{}) { + configSet = append(configSet, setFamilyInet6Dhcpv6Client(dhcp.(map[string]interface{}), setPrefix)...) + } if familyInet6["dad_disable"].(bool) { configSet = append(configSet, setPrefix+"family inet6 dad-disable") } @@ -959,6 +1145,7 @@ func readInterfaceLogical(interFace string, m interface{}, jnprSess *NetconfObje confRead.familyInet6 = append(confRead.familyInet6, map[string]interface{}{ "address": make([]map[string]interface{}, 0), "dad_disable": false, + "dhcpv6_client": make([]map[string]interface{}, 0), "filter_input": "", "filter_output": "", "mtu": 0, @@ -975,6 +1162,28 @@ func readInterfaceLogical(interFace string, m interface{}, jnprSess *NetconfObje if err != nil { return confRead, err } + case strings.HasPrefix(itemTrim, "family inet6 dhcpv6-client "): + if len(confRead.familyInet6[0]["dhcpv6_client"].([]map[string]interface{})) == 0 { + confRead.familyInet6[0]["dhcpv6_client"] = append( + confRead.familyInet6[0]["dhcpv6_client"].([]map[string]interface{}), map[string]interface{}{ + "client_identifier_duid_type": "", + "client_type": "", + "client_ia_type_na": false, + "client_ia_type_pd": false, + "no_dns_install": false, + "prefix_delegating_preferred_prefix_length": -1, + "prefix_delegating_sub_prefix_length": 0, + "rapid_commit": false, + "req_option": make([]string, 0), + "retransmission_attempt": -1, + "update_router_advertisement_interface": make([]string, 0), + "update_server": false, + }) + } + if err := readFamilyInet6Dhcpv6Client( + itemTrim, confRead.familyInet6[0]["dhcpv6_client"].([]map[string]interface{})[0]); err != nil { + return confRead, err + } case itemTrim == "family inet6 dad-disable": confRead.familyInet6[0]["dad_disable"] = true case strings.HasPrefix(itemTrim, "family inet6 filter input "): @@ -1011,6 +1220,7 @@ func readInterfaceLogical(interFace string, m interface{}, jnprSess *NetconfObje if len(confRead.familyInet) == 0 { confRead.familyInet = append(confRead.familyInet, map[string]interface{}{ "address": make([]map[string]interface{}, 0), + "dhcp": make([]map[string]interface{}, 0), "mtu": 0, "filter_input": "", "filter_output": "", @@ -1027,6 +1237,36 @@ func readInterfaceLogical(interFace string, m interface{}, jnprSess *NetconfObje if err != nil { return confRead, err } + case strings.HasPrefix(itemTrim, "family inet dhcp"): + if len(confRead.familyInet[0]["dhcp"].([]map[string]interface{})) == 0 { + confRead.familyInet[0]["dhcp"] = append( + confRead.familyInet[0]["dhcp"].([]map[string]interface{}), map[string]interface{}{ + "client_identifier_ascii": "", + "client_identifier_hexadecimal": "", + "client_identifier_prefix_hostname": false, + "client_identifier_prefix_routing_instance_name": false, + "client_identifier_use_interface_description": "", + "client_identifier_userid_ascii": "", + "client_identifier_userid_hexadecimal": "", + "force_discover": false, + "lease_time": 0, + "lease_time_infinite": false, + "metric": -1, + "no_dns_install": false, + "options_no_hostname": false, + "retransmission_attempt": -1, + "retransmission_interval": 0, + "server_address": "", + "update_server": false, + "vendor_id": "", + }) + } + if strings.HasPrefix(itemTrim, "family inet dhcp ") { + if err := readFamilyInetDhcp( + itemTrim, confRead.familyInet[0]["dhcp"].([]map[string]interface{})[0]); err != nil { + return confRead, err + } + } case strings.HasPrefix(itemTrim, "family inet filter input "): confRead.familyInet[0]["filter_input"] = strings.TrimPrefix(itemTrim, "family inet filter input ") case strings.HasPrefix(itemTrim, "family inet filter output "): @@ -1337,6 +1577,115 @@ func readFamilyInetAddress(item string, inetAddress []map[string]interface{}, return inetAddress, nil } +func readFamilyInetDhcp(item string, dhcp map[string]interface{}) error { + itemTrim := strings.TrimPrefix(item, "family inet dhcp ") + switch { + case strings.HasPrefix(itemTrim, "client-identifier ascii "): + dhcp["client_identifier_ascii"] = strings.Trim(strings.TrimPrefix(itemTrim, "client-identifier ascii "), "\"") + case strings.HasPrefix(itemTrim, "client-identifier hexadecimal "): + dhcp["client_identifier_hexadecimal"] = strings.TrimPrefix(itemTrim, "client-identifier hexadecimal ") + case itemTrim == "client-identifier prefix host-name": + dhcp["client_identifier_prefix_hostname"] = true + case itemTrim == "client-identifier prefix routing-instance-name": + dhcp["client_identifier_prefix_routing_instance_name"] = true + case strings.HasPrefix(itemTrim, "client-identifier use-interface-description "): + dhcp["client_identifier_use_interface_description"] = + strings.TrimPrefix(itemTrim, "client-identifier use-interface-description ") + case strings.HasPrefix(itemTrim, "client-identifier user-id ascii "): + dhcp["client_identifier_userid_ascii"] = + strings.Trim(strings.TrimPrefix(itemTrim, "client-identifier user-id ascii "), "\"") + case strings.HasPrefix(itemTrim, "client-identifier user-id hexadecimal "): + dhcp["client_identifier_userid_hexadecimal"] = strings.TrimPrefix(itemTrim, "client-identifier user-id hexadecimal ") + case itemTrim == "force-discover": + dhcp["force_discover"] = true + case itemTrim == "lease-time infinite": + dhcp["lease_time_infinite"] = true + case strings.HasPrefix(itemTrim, "lease-time "): + var err error + dhcp["lease_time"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "lease-time ")) + if err != nil { + return fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + case strings.HasPrefix(itemTrim, "metric "): + var err error + dhcp["metric"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "metric ")) + if err != nil { + return fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + case itemTrim == "no-dns-install": + dhcp["no_dns_install"] = true + case itemTrim == "options no-hostname": + dhcp["options_no_hostname"] = true + case strings.HasPrefix(itemTrim, "retransmission-attempt "): + var err error + dhcp["retransmission_attempt"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "retransmission-attempt ")) + if err != nil { + return fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + case strings.HasPrefix(itemTrim, "retransmission-interval "): + var err error + dhcp["retransmission_interval"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "retransmission-interval ")) + if err != nil { + return fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + case strings.HasPrefix(itemTrim, "server-address "): + dhcp["server_address"] = strings.TrimPrefix(itemTrim, "server-address ") + case itemTrim == "update-server": + dhcp["update_server"] = true + case strings.HasPrefix(itemTrim, "vendor-id "): + dhcp["vendor_id"] = strings.Trim(strings.TrimPrefix(itemTrim, "vendor-id "), "\"") + } + + return nil +} + +func readFamilyInet6Dhcpv6Client(item string, dhcp map[string]interface{}) error { + itemTrim := strings.TrimPrefix(item, "family inet6 dhcpv6-client ") + switch { + case strings.HasPrefix(itemTrim, "client-identifier duid-type "): + dhcp["client_identifier_duid_type"] = strings.TrimPrefix(itemTrim, "client-identifier duid-type ") + case strings.HasPrefix(itemTrim, "client-type "): + dhcp["client_type"] = strings.TrimPrefix(itemTrim, "client-type ") + case itemTrim == "client-ia-type ia-na": + dhcp["client_ia_type_na"] = true + case itemTrim == "client-ia-type ia-pd": + dhcp["client_ia_type_pd"] = true + case itemTrim == "no-dns-install": + dhcp["no_dns_install"] = true + case strings.HasPrefix(itemTrim, "prefix-delegating preferred-prefix-length "): + var err error + dhcp["prefix_delegating_preferred_prefix_length"], err = + strconv.Atoi(strings.TrimPrefix(itemTrim, "prefix-delegating preferred-prefix-length ")) + if err != nil { + return fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + case strings.HasPrefix(itemTrim, "prefix-delegating sub-prefix-length "): + var err error + dhcp["prefix_delegating_sub_prefix_length"], err = + strconv.Atoi(strings.TrimPrefix(itemTrim, "prefix-delegating sub-prefix-length ")) + if err != nil { + return fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + case itemTrim == "rapid-commit": + dhcp["rapid_commit"] = true + case strings.HasPrefix(itemTrim, "req-option "): + dhcp["req_option"] = append(dhcp["req_option"].([]string), strings.TrimPrefix(itemTrim, "req-option ")) + case strings.HasPrefix(itemTrim, "retransmission-attempt "): + var err error + dhcp["retransmission_attempt"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "retransmission-attempt ")) + if err != nil { + return fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + case strings.HasPrefix(itemTrim, "update-router-advertisement interface "): + dhcp["update_router_advertisement_interface"] = append(dhcp["update_router_advertisement_interface"].([]string), + strings.TrimPrefix(itemTrim, "update-router-advertisement interface ")) + case itemTrim == "update-server": + dhcp["update_server"] = true + } + + return nil +} + func setFamilyAddress(inetAddress map[string]interface{}, setPrefix string, family string) ([]string, error) { configSet := make([]string, 0) if family != inetWord && family != inet6Word { @@ -1468,6 +1817,109 @@ func setFamilyAddress(inetAddress map[string]interface{}, setPrefix string, fami return configSet, nil } +func setFamilyInetDhcp(dhcp map[string]interface{}, setPrefixInt string) []string { + configSet := make([]string, 0) + setPrefix := setPrefixInt + "family inet dhcp " + + configSet = append(configSet, setPrefix) + if v := dhcp["client_identifier_ascii"].(string); v != "" { + configSet = append(configSet, setPrefix+"client-identifier ascii \""+v+"\"") + } + if v := dhcp["client_identifier_hexadecimal"].(string); v != "" { + configSet = append(configSet, setPrefix+"client-identifier hexadecimal "+v) + } + if dhcp["client_identifier_prefix_hostname"].(bool) { + configSet = append(configSet, setPrefix+"client-identifier prefix host-name") + } + if dhcp["client_identifier_prefix_routing_instance_name"].(bool) { + configSet = append(configSet, setPrefix+"client-identifier prefix routing-instance-name") + } + if v := dhcp["client_identifier_use_interface_description"].(string); v != "" { + configSet = append(configSet, setPrefix+"client-identifier use-interface-description "+v) + } + if v := dhcp["client_identifier_userid_ascii"].(string); v != "" { + configSet = append(configSet, setPrefix+"client-identifier user-id ascii \""+v+"\"") + } + if v := dhcp["client_identifier_userid_hexadecimal"].(string); v != "" { + configSet = append(configSet, setPrefix+"client-identifier user-id hexadecimal "+v) + } + if dhcp["force_discover"].(bool) { + configSet = append(configSet, setPrefix+"force-discover") + } + if v := dhcp["lease_time"].(int); v != 0 { + configSet = append(configSet, setPrefix+"lease-time "+strconv.Itoa(v)) + } + if dhcp["lease_time_infinite"].(bool) { + configSet = append(configSet, setPrefix+"lease-time infinite") + } + if v := dhcp["metric"].(int); v != -1 { + configSet = append(configSet, setPrefix+"metric "+strconv.Itoa(v)) + } + if dhcp["no_dns_install"].(bool) { + configSet = append(configSet, setPrefix+"no-dns-install") + } + if dhcp["options_no_hostname"].(bool) { + configSet = append(configSet, setPrefix+"options no-hostname") + } + if v := dhcp["retransmission_attempt"].(int); v != -1 { + configSet = append(configSet, setPrefix+"retransmission-attempt "+strconv.Itoa(v)) + } + if v := dhcp["retransmission_interval"].(int); v != 0 { + configSet = append(configSet, setPrefix+"retransmission-interval "+strconv.Itoa(v)) + } + if v := dhcp["server_address"].(string); v != "" { + configSet = append(configSet, setPrefix+"server-address "+v) + } + if dhcp["update_server"].(bool) { + configSet = append(configSet, setPrefix+"update-server") + } + if v := dhcp["vendor_id"].(string); v != "" { + configSet = append(configSet, setPrefix+"vendor-id \""+v+"\"") + } + + return configSet +} + +func setFamilyInet6Dhcpv6Client(dhcp map[string]interface{}, setPrefixInt string) []string { + configSet := make([]string, 0) + setPrefix := setPrefixInt + "family inet6 dhcpv6-client " + + configSet = append(configSet, setPrefix+"client-identifier duid-type "+dhcp["client_identifier_duid_type"].(string)) + configSet = append(configSet, setPrefix+"client-type "+dhcp["client_type"].(string)) + if dhcp["client_ia_type_na"].(bool) { + configSet = append(configSet, setPrefix+"client-ia-type ia-na") + } + if dhcp["client_ia_type_pd"].(bool) { + configSet = append(configSet, setPrefix+"client-ia-type ia-pd") + } + if dhcp["no_dns_install"].(bool) { + configSet = append(configSet, setPrefix+"no-dns-install") + } + if v := dhcp["prefix_delegating_preferred_prefix_length"].(int); v != -1 { + configSet = append(configSet, setPrefix+"prefix-delegating preferred-prefix-length "+strconv.Itoa(v)) + } + if v := dhcp["prefix_delegating_sub_prefix_length"].(int); v != 0 { + configSet = append(configSet, setPrefix+"prefix-delegating sub-prefix-length "+strconv.Itoa(v)) + } + if dhcp["rapid_commit"].(bool) { + configSet = append(configSet, setPrefix+"rapid-commit") + } + for _, v := range sortSetOfString(dhcp["req_option"].(*schema.Set).List()) { + configSet = append(configSet, setPrefix+"req-option "+v) + } + if v := dhcp["retransmission_attempt"].(int); v != -1 { + configSet = append(configSet, setPrefix+"retransmission-attempt "+strconv.Itoa(v)) + } + for _, v := range sortSetOfString(dhcp["update_router_advertisement_interface"].(*schema.Set).List()) { + configSet = append(configSet, setPrefix+"update-router-advertisement interface "+v) + } + if dhcp["update_server"].(bool) { + configSet = append(configSet, setPrefix+"update-server") + } + + return configSet +} + func genFamilyInetAddress(address string) map[string]interface{} { return map[string]interface{}{ "cidr_ip": address, diff --git a/junos/resource_interface_logical_test.go b/junos/resource_interface_logical_test.go index 67bb5133..446cbd96 100644 --- a/junos/resource_interface_logical_test.go +++ b/junos/resource_interface_logical_test.go @@ -182,6 +182,15 @@ func TestAccJunosInterfaceLogical_basic(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + { + Config: testAccJunosInterfaceLogicalConfigUpdate2(testaccInterface), + }, + { + Config: testAccJunosInterfaceLogicalConfigUpdate3(testaccInterface), + }, + { + Config: testAccJunosInterfaceLogicalConfigUpdate4(testaccInterface), + }, }, }) } @@ -323,6 +332,9 @@ resource junos_interface_physical testacc_interface_logical_phy { vlan_tagging = true } resource junos_interface_logical testacc_interface_logical { + lifecycle { + create_before_destroy = true + } name = "${junos_interface_physical.testacc_interface_logical_phy.name}.100" vlan_id = 101 description = "testacc_interface_${junos_interface_physical.testacc_interface_logical_phy.name}.100" @@ -379,3 +391,114 @@ resource junos_interface_logical testacc_interface_logical { } `) } + +func testAccJunosInterfaceLogicalConfigUpdate2(interFace string) string { + return fmt.Sprintf(` +resource junos_interface_physical testacc_interface_logical_phy { + name = "` + interFace + `" + vlan_tagging = true +} +resource junos_interface_logical testacc_interface_logical { + name = "${junos_interface_physical.testacc_interface_logical_phy.name}.100" + vlan_id = 100 + family_inet { + dhcp {} + } + family_inet6 { + dhcpv6_client { + client_type = "stateful" + client_identifier_duid_type = "vendor" + client_ia_type_na = true + } + } +} +`) +} + +func testAccJunosInterfaceLogicalConfigUpdate3(interFace string) string { + return fmt.Sprintf(` +resource junos_interface_physical testacc_interface_logical_phy { + name = "` + interFace + `" + vlan_tagging = true +} +resource junos_interface_logical testacc_interface_logical { + name = "${junos_interface_physical.testacc_interface_logical_phy.name}.100" + vlan_id = 100 + family_inet { + dhcp { + client_identifier_ascii = "BBAA#1" + client_identifier_prefix_hostname = true + client_identifier_prefix_routing_instance_name = true + client_identifier_use_interface_description = "device" + client_identifier_userid_ascii = "BBCC#2" + force_discover = true + lease_time = 600 + metric = 0 + no_dns_install = true + options_no_hostname = true + retransmission_attempt = 0 + retransmission_interval = 4 + server_address = "192.0.2.1" + update_server = true + vendor_id = 2 + } + } + family_inet6 { + dhcpv6_client { + client_type = "stateful" + client_identifier_duid_type = "vendor" + client_ia_type_na = true + client_ia_type_pd = true + no_dns_install = true + prefix_delegating_preferred_prefix_length = 0 + prefix_delegating_sub_prefix_length = 5 + rapid_commit = true + req_option = ["fqdn"] + retransmission_attempt = 0 + update_router_advertisement_interface = [ + junos_interface_logical.testacc_interface_logical2.name, + ] + update_server = true + } + } +} +resource junos_interface_logical testacc_interface_logical2 { + name = "${junos_interface_physical.testacc_interface_logical_phy.name}.101" +} +`) +} + +func testAccJunosInterfaceLogicalConfigUpdate4(interFace string) string { + return fmt.Sprintf(` +resource junos_interface_physical testacc_interface_logical_phy { + name = "` + interFace + `" + vlan_tagging = true +} +resource junos_interface_logical testacc_interface_logical { + name = "${junos_interface_physical.testacc_interface_logical_phy.name}.100" + vlan_id = 100 + family_inet { + dhcp { + client_identifier_hexadecimal = "11BBee" + client_identifier_userid_hexadecimal = "00AAff" + lease_time_infinite = true + } + } + family_inet6 { + dhcpv6_client { + client_type = "stateful" + client_identifier_duid_type = "vendor" + client_ia_type_pd = true + req_option = ["fqdn", "domain"] + update_router_advertisement_interface = [ + junos_interface_logical.testacc_interface_logical2.name, + "${junos_interface_physical.testacc_interface_logical_phy.name}.100", + ] + } + } +} +resource junos_interface_logical testacc_interface_logical2 { + name = "${junos_interface_physical.testacc_interface_logical_phy.name}.101" +} +`) +} diff --git a/junos/resource_interface_physical.go b/junos/resource_interface_physical.go index 3817b379..fcdd6494 100644 --- a/junos/resource_interface_physical.go +++ b/junos/resource_interface_physical.go @@ -1544,8 +1544,10 @@ func readInterfacePhysicalParentEtherOpts(confRead *interfacePhysicalOptions, it func delInterfacePhysical(d *schema.ResourceData, m interface{}, jnprSess *NetconfObject) error { sess := m.(*Session) - if err := checkInterfacePhysicalContainsUnit(d.Get("name").(string), m, jnprSess); err != nil { + if containsUnit, err := checkInterfacePhysicalContainsUnit(d.Get("name").(string), m, jnprSess); err != nil { return err + } else if containsUnit { + return fmt.Errorf("interface %s is used for a logical unit interface", d.Get("name").(string)) } if err := sess.configSet([]string{"delete interfaces " + d.Get("name").(string)}, jnprSess); err != nil { return err @@ -1609,11 +1611,11 @@ func delInterfacePhysical(d *schema.ResourceData, m interface{}, jnprSess *Netco return nil } -func checkInterfacePhysicalContainsUnit(interFace string, m interface{}, jnprSess *NetconfObject) error { +func checkInterfacePhysicalContainsUnit(interFace string, m interface{}, jnprSess *NetconfObject) (bool, error) { sess := m.(*Session) showConfig, err := sess.command("show configuration interfaces "+interFace+" | display set relative", jnprSess) if err != nil { - return err + return false, err } for _, item := range strings.Split(showConfig, "\n") { if strings.Contains(item, "") { @@ -1627,11 +1629,11 @@ func checkInterfacePhysicalContainsUnit(interFace string, m interface{}, jnprSes continue } - return fmt.Errorf("interface %s is used for other son unit interface", interFace) + return true, nil } } - return nil + return false, nil } func delInterfaceNC(d *schema.ResourceData, m interface{}, jnprSess *NetconfObject) error { diff --git a/junos/resource_interface_physical_disable.go b/junos/resource_interface_physical_disable.go new file mode 100644 index 00000000..a93bfa51 --- /dev/null +++ b/junos/resource_interface_physical_disable.go @@ -0,0 +1,133 @@ +package junos + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceInterfacePhysicalDisable() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceInterfacePhysicalDisableCreate, + ReadContext: resourceInterfacePhysicalDisableRead, + DeleteContext: resourceInterfacePhysicalDisableDelete, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if strings.Count(value, ".") > 0 { + errors = append(errors, fmt.Errorf( + "%q in %q cannot have a dot", value, k)) + } + + return + }, + }, + }, + } +} + +func resourceInterfacePhysicalDisableCreate(ctx context.Context, + d *schema.ResourceData, m interface{}) diag.Diagnostics { + sess := m.(*Session) + if sess.junosFakeCreateSetFile != "" { + if err := addInterfacePhysicalNC(d.Get("name").(string), m, nil); err != nil { + return diag.FromErr(err) + } + d.SetId(d.Get("name").(string)) + + return nil + } + jnprSess, err := sess.startNewSession() + if err != nil { + return diag.FromErr(err) + } + defer sess.closeSession(jnprSess) + sess.configLock(jnprSess) + var diagWarns diag.Diagnostics + ncInt, emptyInt, err := checkInterfacePhysicalNCEmpty(d.Get("name").(string), m, jnprSess) + if err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + if !ncInt && !emptyInt { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(fmt.Errorf("interface %s is configured", d.Get("name").(string)))...) + } + if ncInt { + d.SetId(d.Get("name").(string)) + if errs := sess.configClear(jnprSess); len(errs) > 0 { + return diagWarns + } + + return nil + } + if emptyInt { + if containsUnit, err := checkInterfacePhysicalContainsUnit(d.Get("name").(string), m, jnprSess); err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } else if containsUnit { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr( + fmt.Errorf("interface %s is used for a logical unit interface", d.Get("name").(string)))...) + } + } + if err := addInterfacePhysicalNC(d.Get("name").(string), m, jnprSess); err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + warns, err := sess.commitConf("create resource junos_interface_physical_disable", jnprSess) + appendDiagWarns(&diagWarns, warns) + if err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + ncInt, _, err = checkInterfacePhysicalNCEmpty(d.Get("name").(string), m, jnprSess) + if err != nil { + return append(diagWarns, diag.FromErr(err)...) + } + if !ncInt { + return append(diagWarns, diag.FromErr(fmt.Errorf("interface %v always not disable after commit "+ + "=> check your config", d.Get("name").(string)))...) + } + d.SetId(d.Get("name").(string)) + + return nil +} + +func resourceInterfacePhysicalDisableRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + sess := m.(*Session) + jnprSess, err := sess.startNewSession() + if err != nil { + return diag.FromErr(err) + } + defer sess.closeSession(jnprSess) + mutex.Lock() + ncInt, _, err := checkInterfacePhysicalNCEmpty(d.Get("name").(string), m, jnprSess) + mutex.Unlock() + if err != nil { + return diag.FromErr(err) + } + if !ncInt { + d.SetId("") + } + + return nil +} + +func resourceInterfacePhysicalDisableDelete(ctx context.Context, + d *schema.ResourceData, m interface{}) diag.Diagnostics { + return nil +} diff --git a/junos/resource_interface_physical_disable_test.go b/junos/resource_interface_physical_disable_test.go new file mode 100644 index 00000000..441000f0 --- /dev/null +++ b/junos/resource_interface_physical_disable_test.go @@ -0,0 +1,84 @@ +package junos_test + +import ( + "os" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +// export TESTACC_INTERFACE= for choose interface available else it's ge-0/0/3. +func TestAccJunosInterfacePhysicalDisable_basic(t *testing.T) { + var testaccInterface string + if os.Getenv("TESTACC_INTERFACE") != "" { + testaccInterface = os.Getenv("TESTACC_INTERFACE") + } else { + if os.Getenv("TESTACC_SWITCH") != "" { + testaccInterface = defaultInterfaceSwitchTestAcc + } else { + testaccInterface = defaultInterfaceTestAcc + } + } + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccJunosInterfacePhysicalDisablePreConfigCreate(testaccInterface), + }, + { + Config: testAccJunosInterfacePhysicalDisablePreConfigCreate(testaccInterface), + Destroy: true, + }, + { + Config: testAccJunosInterfacePhysicalDisableConfigCreate(testaccInterface), + }, + { + Config: testAccJunosInterfacePhysicalDisableConfigConflict(testaccInterface), + ExpectNonEmptyPlan: true, + }, + { + Config: testAccJunosInterfacePhysicalDisableConfigConflict(testaccInterface), + ExpectError: regexp.MustCompile("interface " + testaccInterface + " is configured"), + }, + }, + }) +} + +func testAccJunosInterfacePhysicalDisablePreConfigCreate(interFace string) string { + return ` +resource junos_interface_physical testacc_interface_disable { + name = "` + interFace + `" + no_disable_on_destroy = true +} +resource junos_interface_logical testacc_interface_disable { + name = "${junos_interface_physical.testacc_interface_disable.name}.0" + description = "testacc_interface_disable" +} +` +} + +func testAccJunosInterfacePhysicalDisableConfigCreate(interFace string) string { + return ` +resource junos_interface_physical_disable testacc_interface_disable { + name = "` + interFace + `" +} +resource junos_interface_physical_disable testacc_interface_disable2 { + name = "` + interFace + `" +} +` +} + +func testAccJunosInterfacePhysicalDisableConfigConflict(interFace string) string { + return ` +resource junos_interface_physical testacc_interface_disable { + name = "` + interFace + `" + description = "testacc_interface_disable" + no_disable_on_destroy = true +} +resource junos_interface_physical_disable testacc_interface_disable { + name = "` + interFace + `" +} +` +} diff --git a/junos/resource_interface_test.go b/junos/resource_interface_test.go index 99abc46f..25f507d9 100644 --- a/junos/resource_interface_test.go +++ b/junos/resource_interface_test.go @@ -17,7 +17,7 @@ func TestAccJunosInterface_basic(t *testing.T) { testaccInterface = os.Getenv("TESTACC_INTERFACE") } else { if os.Getenv("TESTACC_SWITCH") != "" { - testaccInterface = "xe-0/0/3" + testaccInterface = defaultInterfaceSwitchTestAcc } else { testaccInterface = defaultInterfaceTestAcc } diff --git a/junos/resource_security_idp_custom_attack.go b/junos/resource_security_idp_custom_attack.go index 9d472cbd..04c7a3ea 100644 --- a/junos/resource_security_idp_custom_attack.go +++ b/junos/resource_security_idp_custom_attack.go @@ -480,7 +480,7 @@ func schemaSecurityIdpCustomAttackTypeSignature(chain bool) map[string]*schema.S "destination_value": { Type: schema.TypeString, Optional: true, - ValidateFunc: validation.IsIPv6Address, + ValidateFunc: validateIsIPv6Address, }, "extension_header_destination_option_home_address_match": { Type: schema.TypeString, @@ -490,7 +490,7 @@ func schemaSecurityIdpCustomAttackTypeSignature(chain bool) map[string]*schema.S "extension_header_destination_option_home_address_value": { Type: schema.TypeString, Optional: true, - ValidateFunc: validation.IsIPv6Address, + ValidateFunc: validateIsIPv6Address, }, "extension_header_destination_option_type_match": { Type: schema.TypeString, @@ -566,7 +566,7 @@ func schemaSecurityIdpCustomAttackTypeSignature(chain bool) map[string]*schema.S "source_value": { Type: schema.TypeString, Optional: true, - ValidateFunc: validation.IsIPv6Address, + ValidateFunc: validateIsIPv6Address, }, "traffic_class_match": { Type: schema.TypeString, diff --git a/junos/resource_services_rpm_probe.go b/junos/resource_services_rpm_probe.go index c709678d..9ad0aa53 100644 --- a/junos/resource_services_rpm_probe.go +++ b/junos/resource_services_rpm_probe.go @@ -86,7 +86,7 @@ func resourceServicesRpmProbe() *schema.Resource { "inet6_source_address": { Type: schema.TypeString, Optional: true, - ValidateFunc: validation.IsIPv6Address, + ValidateFunc: validateIsIPv6Address, }, "moving_average_size": { Type: schema.TypeInt, @@ -161,7 +161,7 @@ func resourceServicesRpmProbe() *schema.Resource { "source_inet6_address_base": { Type: schema.TypeString, Optional: true, - ValidateFunc: validation.IsIPv6Address, + ValidateFunc: validateIsIPv6Address, }, "source_inet6_count": { Type: schema.TypeInt, @@ -171,7 +171,7 @@ func resourceServicesRpmProbe() *schema.Resource { "source_inet6_step": { Type: schema.TypeString, Optional: true, - ValidateFunc: validation.IsIPv6Address, + ValidateFunc: validateIsIPv6Address, }, "target_address_base": { Type: schema.TypeString, @@ -191,7 +191,7 @@ func resourceServicesRpmProbe() *schema.Resource { "target_inet6_address_base": { Type: schema.TypeString, Optional: true, - ValidateFunc: validation.IsIPv6Address, + ValidateFunc: validateIsIPv6Address, }, "target_inet6_count": { Type: schema.TypeInt, @@ -201,7 +201,7 @@ func resourceServicesRpmProbe() *schema.Resource { "target_inet6_step": { Type: schema.TypeString, Optional: true, - ValidateFunc: validation.IsIPv6Address, + ValidateFunc: validateIsIPv6Address, }, }, }, diff --git a/junos/resource_system.go b/junos/resource_system.go index 60452da1..3a9b3287 100644 --- a/junos/resource_system.go +++ b/junos/resource_system.go @@ -39,6 +39,7 @@ type systemOptions struct { license []map[string]interface{} login []map[string]interface{} ntp []map[string]interface{} + ports []map[string]interface{} services []map[string]interface{} syslog []map[string]interface{} } @@ -529,6 +530,59 @@ func resourceSystem() *schema.Resource { }, }, }, + "ports": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "auxiliary_authentication_order": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "auxiliary_disable": { + Type: schema.TypeBool, + Optional: true, + }, + "auxiliary_insecure": { + Type: schema.TypeBool, + Optional: true, + }, + "auxiliary_logout_on_disconnect": { + Type: schema.TypeBool, + Optional: true, + }, + "auxiliary_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"ansi", "small-xterm", "vt100", "xterm"}, false), + }, + "console_authentication_order": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "console_disable": { + Type: schema.TypeBool, + Optional: true, + }, + "console_insecure": { + Type: schema.TypeBool, + Optional: true, + }, + "console_logout_on_disconnect": { + Type: schema.TypeBool, + Optional: true, + }, + "console_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"ansi", "small-xterm", "vt100", "xterm"}, false), + }, + }, + }, + }, "radius_options_attributes_nas_ipaddress": { Type: schema.TypeString, Optional: true, @@ -1035,7 +1089,7 @@ func setSystem(d *schema.ResourceData, m interface{}, jnprSess *NetconfObject) e configSet = append(configSet, setPrefixLicense+"renew interval "+ strconv.Itoa(license["renew_interval"].(int))) } - if !strings.HasPrefix(configSet[len(configSet)-1], setPrefixLicense) { + if len(configSet) == 0 || !strings.HasPrefix(configSet[len(configSet)-1], setPrefixLicense) { return fmt.Errorf("license block is empty") } } @@ -1095,6 +1149,45 @@ func setSystem(d *schema.ResourceData, m interface{}, jnprSess *NetconfObject) e return fmt.Errorf("ntp block is empty") } } + for _, p := range d.Get("ports").([]interface{}) { + if p == nil { + return fmt.Errorf("ports block is empty") + } + ports := p.(map[string]interface{}) + for _, v := range ports["auxiliary_authentication_order"].([]interface{}) { + configSet = append(configSet, setPrefix+"ports auxiliary authentication-order "+v.(string)) + } + if ports["auxiliary_disable"].(bool) { + configSet = append(configSet, setPrefix+"ports auxiliary disable") + } + if ports["auxiliary_insecure"].(bool) { + configSet = append(configSet, setPrefix+"ports auxiliary insecure") + } + if ports["auxiliary_logout_on_disconnect"].(bool) { + configSet = append(configSet, setPrefix+"ports auxiliary log-out-on-disconnect") + } + if v := ports["auxiliary_type"].(string); v != "" { + configSet = append(configSet, setPrefix+"ports auxiliary type "+v) + } + for _, v := range ports["console_authentication_order"].([]interface{}) { + configSet = append(configSet, setPrefix+"ports console authentication-order "+v.(string)) + } + if ports["console_disable"].(bool) { + configSet = append(configSet, setPrefix+"ports console disable") + } + if ports["console_insecure"].(bool) { + configSet = append(configSet, setPrefix+"ports console insecure") + } + if ports["console_logout_on_disconnect"].(bool) { + configSet = append(configSet, setPrefix+"ports console log-out-on-disconnect") + } + if v := ports["console_type"].(string); v != "" { + configSet = append(configSet, setPrefix+"ports console type "+v) + } + if len(configSet) == 0 || !strings.HasPrefix(configSet[len(configSet)-1], setPrefix+"ports ") { + return fmt.Errorf("ports block is empty") + } + } if v := d.Get("radius_options_attributes_nas_ipaddress").(string); v != "" { configSet = append(configSet, setPrefix+"radius-options attributes nas-ip-address "+v) } @@ -1631,6 +1724,7 @@ func delSystem(m interface{}, jnprSess *NetconfObject) error { listLinesToDelete = append(listLinesToDelete, "no-ping-time-stamp") listLinesToDelete = append(listLinesToDelete, "no-redirects") listLinesToDelete = append(listLinesToDelete, "no-redirects-ipv6") + listLinesToDelete = append(listLinesToDelete, "ports") listLinesToDelete = append(listLinesToDelete, "radius-options") listLinesToDelete = append(listLinesToDelete, listLinesServices()...) listLinesToDelete = append(listLinesToDelete, listLinesSyslog()...) @@ -1794,6 +1888,48 @@ func readSystem(m interface{}, jnprSess *NetconfObject) (systemOptions, error) { return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) } } + case strings.HasPrefix(itemTrim, "ports "): + if len(confRead.ports) == 0 { + confRead.ports = append(confRead.ports, map[string]interface{}{ + "auxiliary_authentication_order": make([]string, 0), + "auxiliary_disable": false, + "auxiliary_insecure": false, + "auxiliary_logout_on_disconnect": false, + "auxiliary_type": "", + "console_authentication_order": make([]string, 0), + "console_disable": false, + "console_insecure": false, + "console_logout_on_disconnect": false, + "console_type": "", + }) + } + itemTrimPorts := strings.TrimPrefix(itemTrim, "ports ") + switch { + case strings.HasPrefix(itemTrimPorts, "auxiliary authentication-order "): + confRead.ports[0]["auxiliary_authentication_order"] = append( + confRead.ports[0]["auxiliary_authentication_order"].([]string), + strings.TrimPrefix(itemTrimPorts, "auxiliary authentication-order ")) + case itemTrimPorts == "auxiliary disable": + confRead.ports[0]["auxiliary_disable"] = true + case itemTrimPorts == "auxiliary insecure": + confRead.ports[0]["auxiliary_insecure"] = true + case itemTrimPorts == "auxiliary log-out-on-disconnect": + confRead.ports[0]["auxiliary_logout_on_disconnect"] = true + case strings.HasPrefix(itemTrimPorts, "auxiliary type "): + confRead.ports[0]["auxiliary_type"] = strings.TrimPrefix(itemTrimPorts, "auxiliary type ") + case strings.HasPrefix(itemTrimPorts, "console authentication-order "): + confRead.ports[0]["console_authentication_order"] = append( + confRead.ports[0]["console_authentication_order"].([]string), + strings.TrimPrefix(itemTrimPorts, "console authentication-order ")) + case itemTrimPorts == "console disable": + confRead.ports[0]["console_disable"] = true + case itemTrimPorts == "console insecure": + confRead.ports[0]["console_insecure"] = true + case itemTrimPorts == "console log-out-on-disconnect": + confRead.ports[0]["console_logout_on_disconnect"] = true + case strings.HasPrefix(itemTrimPorts, "console type "): + confRead.ports[0]["console_type"] = strings.TrimPrefix(itemTrimPorts, "console type ") + } case strings.HasPrefix(itemTrim, "name-server "): confRead.nameServer = append(confRead.nameServer, strings.TrimPrefix(itemTrim, "name-server ")) case itemTrim == "no-multicast-echo": @@ -2585,6 +2721,9 @@ func fillSystem(d *schema.ResourceData, systemOptions systemOptions) { if tfErr := d.Set("ntp", systemOptions.ntp); tfErr != nil { panic(tfErr) } + if tfErr := d.Set("ports", systemOptions.ports); tfErr != nil { + panic(tfErr) + } if tfErr := d.Set("radius_options_attributes_nas_ipaddress", systemOptions.radiusOptionsAttributesNasIPAddress); tfErr != nil { panic(tfErr) diff --git a/junos/resource_system_services_dhcp_localserver_group.go b/junos/resource_system_services_dhcp_localserver_group.go new file mode 100644 index 00000000..d0c1a118 --- /dev/null +++ b/junos/resource_system_services_dhcp_localserver_group.go @@ -0,0 +1,2172 @@ +package junos + +import ( + "context" + "fmt" + "strconv" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + bchk "github.com/jeremmfr/go-utils/basiccheck" +) + +type systemServicesDhcpLocalServerGroupOptions struct { + dynamicProfileAggregateClients bool + reauthenticateLeaseRenewal bool + reauthenticateRemoteIDMismatch bool + remoteIDMismatchDisconnect bool + routeSuppressionAccess bool + routeSuppressionAccessInternal bool + routeSuppressionDestination bool + shortCycleProtectionLockoutMaxTime int + shortCycleProtectionLockoutMinTime int + accessProfile string + authenticationPassword string + dynamicProfile string + dynamicProfileUsePrimary string + dynamicProfileAggregateClientsAction string + livenessDetectionFailureAction string + name string + routingInstance string + serviceProfile string + version string + authenticationUsernameInclude []map[string]interface{} + interFace []map[string]interface{} + leaseTimeValidation []map[string]interface{} + livenessDetectionMethodBfd []map[string]interface{} + livenessDetectionMethodLayer2 []map[string]interface{} + overridesV4 []map[string]interface{} + overridesV6 []map[string]interface{} + reconfigure []map[string]interface{} +} + +func resourceSystemServicesDhcpLocalServerGroup() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceSystemServicesDhcpLocalServerGroupCreate, + ReadContext: resourceSystemServicesDhcpLocalServerGroupRead, + UpdateContext: resourceSystemServicesDhcpLocalServerGroupUpdate, + DeleteContext: resourceSystemServicesDhcpLocalServerGroupDelete, + Importer: &schema.ResourceImporter{ + State: resourceSystemServicesDhcpLocalServerGroupImport, + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), + }, + "routing_instance": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: defaultWord, + ValidateDiagFunc: validateNameObjectJunos([]string{}, 64, formatDefault), + }, + "version": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "v4", + ValidateFunc: validation.StringInSlice([]string{"v4", "v6"}, false), + }, + "access_profile": { + Type: schema.TypeString, + Optional: true, + }, + "authentication_password": { + Type: schema.TypeString, + Optional: true, + }, + "authentication_username_include": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "circuit_type": { + Type: schema.TypeBool, + Optional: true, + AtLeastOneOf: []string{ + "authentication_username_include.0.circuit_type", + "authentication_username_include.0.client_id", + "authentication_username_include.0.delimiter", + "authentication_username_include.0.domain_name", + "authentication_username_include.0.interface_description", + "authentication_username_include.0.interface_name", + "authentication_username_include.0.mac_address", + "authentication_username_include.0.option_60", + "authentication_username_include.0.option_82", + "authentication_username_include.0.relay_agent_interface_id", + "authentication_username_include.0.relay_agent_remote_id", + "authentication_username_include.0.relay_agent_subscriber_id", + "authentication_username_include.0.routing_instance_name", + "authentication_username_include.0.user_prefix", + "authentication_username_include.0.vlan_tags", + }, + }, + "client_id": { + Type: schema.TypeBool, + Optional: true, + }, + "delimiter": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 1), + }, + "domain_name": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + "interface_description": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"device", "logical"}, false), + }, + "interface_name": { + Type: schema.TypeBool, + Optional: true, + }, + "mac_address": { + Type: schema.TypeBool, + Optional: true, + }, + "option_60": { + Type: schema.TypeBool, + Optional: true, + }, + "option_82": { + Type: schema.TypeBool, + Optional: true, + }, + "option_82_circuit_id": { + Type: schema.TypeBool, + Optional: true, + RequiredWith: []string{"authentication_username_include.0.option_82"}, + }, + "option_82_remote_id": { + Type: schema.TypeBool, + Optional: true, + RequiredWith: []string{"authentication_username_include.0.option_82"}, + }, + "relay_agent_interface_id": { + Type: schema.TypeBool, + Optional: true, + }, + "relay_agent_remote_id": { + Type: schema.TypeBool, + Optional: true, + }, + "relay_agent_subscriber_id": { + Type: schema.TypeBool, + Optional: true, + }, + "routing_instance_name": { + Type: schema.TypeBool, + Optional: true, + }, + "user_prefix": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + "vlan_tags": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, + "dynamic_profile": { + Type: schema.TypeString, + Optional: true, + }, + "dynamic_profile_use_primary": { + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"dynamic_profile"}, + ConflictsWith: []string{"dynamic_profile_aggregate_clients"}, + }, + "dynamic_profile_aggregate_clients": { + Type: schema.TypeBool, + Optional: true, + RequiredWith: []string{"dynamic_profile"}, + }, + "dynamic_profile_aggregate_clients_action": { + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"dynamic_profile_aggregate_clients"}, + ValidateFunc: validation.StringInSlice([]string{"merge", "replace"}, false), + }, + "interface": { + Type: schema.TypeSet, + Optional: true, + AtLeastOneOf: []string{ + "access_profile", + "authentication_password", + "authentication_username_include", + "dynamic_profile", + "interface", + "lease_time_validation", + "liveness_detection_failure_action", + "liveness_detection_method_bfd", + "liveness_detection_method_layer2", + "overrides_v4", + "overrides_v6", + "reauthenticate_lease_renewal", + "reauthenticate_remote_id_mismatch", + "reconfigure", + "remote_id_mismatch_disconnect", + "route_suppression_access", + "route_suppression_access_internal", + "route_suppression_destination", + "service_profile", + "short_cycle_protection_lockout_max_time", + }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if strings.Count(value, ".") != 1 { + errors = append(errors, fmt.Errorf( + "%q in %q need to have 1 dot", value, k)) + } + + return + }, + }, + "access_profile": { + Type: schema.TypeString, + Optional: true, + }, + "dynamic_profile": { + Type: schema.TypeString, + Optional: true, + }, + "dynamic_profile_use_primary": { + Type: schema.TypeString, + Optional: true, + }, + "dynamic_profile_aggregate_clients": { + Type: schema.TypeBool, + Optional: true, + }, + "dynamic_profile_aggregate_clients_action": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"merge", "replace"}, false), + }, + "exclude": { + Type: schema.TypeBool, + Optional: true, + }, + "overrides_v4": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaSystemServicesDhcpLocalServerGroupOverridesV4(), + }, + }, + "overrides_v6": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaSystemServicesDhcpLocalServerGroupOverridesV6(), + }, + }, + "service_profile": { + Type: schema.TypeString, + Optional: true, + }, + "short_cycle_protection_lockout_max_time": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 86400), + }, + "short_cycle_protection_lockout_min_time": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 86400), + }, + "trace": { + Type: schema.TypeBool, + Optional: true, + }, + "upto": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if strings.Count(value, ".") != 1 { + errors = append(errors, fmt.Errorf( + "%q in %q need to have 1 dot", value, k)) + } + + return + }, + }, + }, + }, + }, + "lease_time_validation": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "lease_time_threshold": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(60, 2147483647), + }, + "violation_action": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"override-lease", "strict"}, false), + }, + }, + }, + }, + "liveness_detection_failure_action": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + "clear-binding", + "clear-binding-if-interface-up", + "log-only", + }, false), + }, + "liveness_detection_method_bfd": { + Type: schema.TypeList, + Optional: true, + ConflictsWith: []string{"liveness_detection_method_layer2"}, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "detection_time_threshold": { + Type: schema.TypeInt, + Optional: true, + AtLeastOneOf: []string{ + "liveness_detection_method_bfd.0.detection_time_threshold", + "liveness_detection_method_bfd.0.holddown_interval", + "liveness_detection_method_bfd.0.minimum_interval", + "liveness_detection_method_bfd.0.minimum_receive_interval", + "liveness_detection_method_bfd.0.multiplier", + "liveness_detection_method_bfd.0.no_adaptation", + "liveness_detection_method_bfd.0.session_mode", + "liveness_detection_method_bfd.0.transmit_interval_minimum", + "liveness_detection_method_bfd.0.transmit_interval_threshold", + "liveness_detection_method_bfd.0.version", + }, + Default: -1, + ValidateFunc: validation.IntBetween(0, 4294967295), + }, + "holddown_interval": { + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntBetween(0, 255000), + }, + "minimum_interval": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(30000, 255000), + }, + "minimum_receive_interval": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(30000, 255000), + }, + "multiplier": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 255), + }, + "no_adaptation": { + Type: schema.TypeBool, + Optional: true, + }, + "session_mode": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"automatic", "multihop", "single-hop"}, false), + }, + "transmit_interval_minimum": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(30000, 255000), + }, + "transmit_interval_threshold": { + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntBetween(0, 4294967295), + }, + "version": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"0", "1", "automatic"}, false), + }, + }, + }, + }, + "liveness_detection_method_layer2": { + Type: schema.TypeList, + Optional: true, + ConflictsWith: []string{"liveness_detection_method_bfd"}, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "max_consecutive_retries": { + Type: schema.TypeInt, + Optional: true, + AtLeastOneOf: []string{ + "liveness_detection_method_layer2.0.max_consecutive_retries", + "liveness_detection_method_layer2.0.transmit_interval", + }, + ValidateFunc: validation.IntBetween(3, 6), + }, + "transmit_interval": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(300, 1800), + }, + }, + }, + }, + "overrides_v4": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaSystemServicesDhcpLocalServerGroupOverridesV4(), + }, + }, + "overrides_v6": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: schemaSystemServicesDhcpLocalServerGroupOverridesV6(), + }, + }, + "reauthenticate_lease_renewal": { + Type: schema.TypeBool, + Optional: true, + }, + "reauthenticate_remote_id_mismatch": { + Type: schema.TypeBool, + Optional: true, + }, + "reconfigure": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "attempts": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 10), + }, + "clear_on_abort": { + Type: schema.TypeBool, + Optional: true, + }, + "support_option_pd_exclude": { + Type: schema.TypeBool, + Optional: true, + }, + "timeout": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 10), + }, + "token": { + Type: schema.TypeString, + Optional: true, + }, + "trigger_radius_disconnect": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, + "remote_id_mismatch_disconnect": { + Type: schema.TypeBool, + Optional: true, + }, + "route_suppression_access": { + Type: schema.TypeBool, + Optional: true, + }, + "route_suppression_access_internal": { + Type: schema.TypeBool, + Optional: true, + }, + "route_suppression_destination": { + Type: schema.TypeBool, + Optional: true, + }, + "service_profile": { + Type: schema.TypeString, + Optional: true, + }, + "short_cycle_protection_lockout_max_time": { + Type: schema.TypeInt, + Optional: true, + RequiredWith: []string{"short_cycle_protection_lockout_min_time"}, + ValidateFunc: validation.IntBetween(1, 86400), + }, + "short_cycle_protection_lockout_min_time": { + Type: schema.TypeInt, + Optional: true, + RequiredWith: []string{"short_cycle_protection_lockout_max_time"}, + ValidateFunc: validation.IntBetween(1, 86400), + }, + }, + } +} + +func schemaSystemServicesDhcpLocalServerGroupOverridesV4() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "allow_no_end_option": { + Type: schema.TypeBool, + Optional: true, + }, + "asymmetric_lease_time": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(600, 86400), + }, + "bootp_support": { + Type: schema.TypeBool, + Optional: true, + }, + "client_discover_match": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"incoming-interface", "option60-and-option82"}, false), + }, + "delay_offer_based_on": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "option": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"option-60", "option-77", "option-82"}, false), + }, + "compare": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"equals", "not-equals", "starts-with"}, false), + }, + "value_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"ascii", "hexadecimal"}, false), + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "delay_offer_delay_time": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 30), + }, + "delete_binding_on_renegotiation": { + Type: schema.TypeBool, + Optional: true, + }, + "dual_stack": { + Type: schema.TypeString, + Optional: true, + }, + "include_option_82_forcerenew": { + Type: schema.TypeBool, + Optional: true, + }, + "include_option_82_nak": { + Type: schema.TypeBool, + Optional: true, + }, + "interface_client_limit": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 500000), + }, + "process_inform": { + Type: schema.TypeBool, + Optional: true, + }, + "process_inform_pool": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + "protocol_attributes": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + } +} + +func schemaSystemServicesDhcpLocalServerGroupOverridesV6() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "always_add_option_dns_server": { + Type: schema.TypeBool, + Optional: true, + }, + "always_process_option_request_option": { + Type: schema.TypeBool, + Optional: true, + }, + "asymmetric_lease_time": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(600, 86400), + }, + "asymmetric_prefix_lease_time": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(600, 86400), + }, + "client_negotiation_match_incoming_interface": { + Type: schema.TypeBool, + Optional: true, + }, + "delay_advertise_based_on": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "option": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"option-15", "option-16", "option-18", "option-37"}, false), + }, + "compare": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"equals", "not-equals", "starts-with"}, false), + }, + "value_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"ascii", "hexadecimal"}, false), + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "delay_advertise_delay_time": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 30), + }, + "delegated_pool": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + "delete_binding_on_renegotiation": { + Type: schema.TypeBool, + Optional: true, + }, + "dual_stack": { + Type: schema.TypeString, + Optional: true, + }, + "interface_client_limit": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 500000), + }, + "multi_address_embedded_option_response": { + Type: schema.TypeBool, + Optional: true, + }, + "process_inform": { + Type: schema.TypeBool, + Optional: true, + }, + "process_inform_pool": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + "protocol_attributes": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 64), + }, + "rapid_commit": { + Type: schema.TypeBool, + Optional: true, + }, + "top_level_status_code": { + Type: schema.TypeBool, + Optional: true, + }, + } +} + +func resourceSystemServicesDhcpLocalServerGroupCreate(ctx context.Context, + d *schema.ResourceData, m interface{}) diag.Diagnostics { + sess := m.(*Session) + if sess.junosFakeCreateSetFile != "" { + if err := setSystemServicesDhcpLocalServerGroup(d, m, nil); err != nil { + return diag.FromErr(err) + } + d.SetId(d.Get("name").(string) + + idSeparator + d.Get("routing_instance").(string) + + idSeparator + d.Get("version").(string)) + + return nil + } + jnprSess, err := sess.startNewSession() + if err != nil { + return diag.FromErr(err) + } + defer sess.closeSession(jnprSess) + sess.configLock(jnprSess) + var diagWarns diag.Diagnostics + if d.Get("routing_instance").(string) != defaultWord { + instanceExists, err := checkRoutingInstanceExists(d.Get("routing_instance").(string), m, jnprSess) + if err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + if !instanceExists { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, + diag.FromErr(fmt.Errorf("routing instance %v doesn't exist", d.Get("routing_instance").(string)))...) + } + } + systemServicesDhcpLocalServerGroupExists, err := checkSystemServicesDhcpLocalServerGroupExists( + d.Get("name").(string), d.Get("routing_instance").(string), d.Get("version").(string), m, jnprSess) + if err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + if systemServicesDhcpLocalServerGroupExists { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + if d.Get("version").(string) == "v6" { + return append(diagWarns, diag.FromErr( + fmt.Errorf("system services dhcp-local-server dhcpv6 group %v already exists in routing-instance %s", + d.Get("name").(string), d.Get("routing_instance").(string)))...) + } + + return append(diagWarns, diag.FromErr( + fmt.Errorf("system services dhcp-local-server group %v already exists in routing-instance %s", + d.Get("name").(string), d.Get("routing_instance").(string)))...) + } + if err := setSystemServicesDhcpLocalServerGroup(d, m, jnprSess); err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + warns, err := sess.commitConf("create resource junos_system_services_dhcp_localserver_group", jnprSess) + appendDiagWarns(&diagWarns, warns) + if err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + systemServicesDhcpLocalServerGroupExists, err = checkSystemServicesDhcpLocalServerGroupExists( + d.Get("name").(string), d.Get("routing_instance").(string), d.Get("version").(string), m, jnprSess) + if err != nil { + return append(diagWarns, diag.FromErr(err)...) + } + if systemServicesDhcpLocalServerGroupExists { + d.SetId(d.Get("name").(string) + + idSeparator + d.Get("routing_instance").(string) + + idSeparator + d.Get("version").(string)) + } else { + if d.Get("version").(string) == "v6" { + return append(diagWarns, + diag.FromErr(fmt.Errorf("system services dhcp-local-server dhcpv6 group %v "+ + "not exists in routing_instance %s after commit => check your config", + d.Get("name").(string), d.Get("routing_instance").(string)))...) + } + + return append(diagWarns, + diag.FromErr(fmt.Errorf("system services dhcp-local-server group %v "+ + "not exists in routing_instance %s after commit => check your config", + d.Get("name").(string), d.Get("routing_instance").(string)))...) + } + + return append(diagWarns, resourceSystemServicesDhcpLocalServerGroupReadWJnprSess(d, m, jnprSess)...) +} + +func resourceSystemServicesDhcpLocalServerGroupRead(ctx context.Context, + d *schema.ResourceData, m interface{}) diag.Diagnostics { + sess := m.(*Session) + jnprSess, err := sess.startNewSession() + if err != nil { + return diag.FromErr(err) + } + defer sess.closeSession(jnprSess) + + return resourceSystemServicesDhcpLocalServerGroupReadWJnprSess(d, m, jnprSess) +} + +func resourceSystemServicesDhcpLocalServerGroupReadWJnprSess( + d *schema.ResourceData, m interface{}, jnprSess *NetconfObject) diag.Diagnostics { + mutex.Lock() + systemServicesDhcpLocalServerGroupOptions, err := readSystemServicesDhcpLocalServerGroup( + d.Get("name").(string), d.Get("routing_instance").(string), d.Get("version").(string), m, jnprSess) + mutex.Unlock() + if err != nil { + return diag.FromErr(err) + } + if systemServicesDhcpLocalServerGroupOptions.name == "" { + d.SetId("") + } else { + fillSystemServicesDhcpLocalServerGroupData(d, systemServicesDhcpLocalServerGroupOptions) + } + + return nil +} + +func resourceSystemServicesDhcpLocalServerGroupUpdate(ctx context.Context, + d *schema.ResourceData, m interface{}) diag.Diagnostics { + d.Partial(true) + sess := m.(*Session) + jnprSess, err := sess.startNewSession() + if err != nil { + return diag.FromErr(err) + } + defer sess.closeSession(jnprSess) + sess.configLock(jnprSess) + var diagWarns diag.Diagnostics + if err := delSystemServicesDhcpLocalServerGroup( + d.Get("name").(string), d.Get("routing_instance").(string), d.Get("version").(string), m, jnprSess); err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + if err := setSystemServicesDhcpLocalServerGroup(d, m, jnprSess); err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + warns, err := sess.commitConf("update resource junos_system_services_dhcp_localserver_group", jnprSess) + appendDiagWarns(&diagWarns, warns) + if err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + d.Partial(false) + + return append(diagWarns, resourceSystemServicesDhcpLocalServerGroupReadWJnprSess(d, m, jnprSess)...) +} + +func resourceSystemServicesDhcpLocalServerGroupDelete(ctx context.Context, + d *schema.ResourceData, m interface{}) diag.Diagnostics { + sess := m.(*Session) + jnprSess, err := sess.startNewSession() + if err != nil { + return diag.FromErr(err) + } + defer sess.closeSession(jnprSess) + sess.configLock(jnprSess) + var diagWarns diag.Diagnostics + if err := delSystemServicesDhcpLocalServerGroup( + d.Get("name").(string), d.Get("routing_instance").(string), d.Get("version").(string), m, jnprSess); err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + warns, err := sess.commitConf("delete resource junos_system_services_dhcp_localserver_group", jnprSess) + appendDiagWarns(&diagWarns, warns) + if err != nil { + appendDiagWarns(&diagWarns, sess.configClear(jnprSess)) + + return append(diagWarns, diag.FromErr(err)...) + } + + return diagWarns +} + +func resourceSystemServicesDhcpLocalServerGroupImport( + d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + sess := m.(*Session) + jnprSess, err := sess.startNewSession() + if err != nil { + return nil, err + } + defer sess.closeSession(jnprSess) + result := make([]*schema.ResourceData, 1) + idSplit := strings.Split(d.Id(), idSeparator) + if len(idSplit) < 3 { + return nil, fmt.Errorf("missing element(s) in id with separator %v", idSeparator) + } + if idSplit[2] != "v4" && idSplit[2] != "v6" { + return nil, fmt.Errorf("bad version '%s' in id, need to be 'v4' or 'v6' (id must be "+ + ""+idSeparator+""+idSeparator+")", idSplit[2]) + } + systemServicesDhcpLocalServerGroupExists, err := + checkSystemServicesDhcpLocalServerGroupExists(idSplit[0], idSplit[1], idSplit[2], m, jnprSess) + if err != nil { + return nil, err + } + if !systemServicesDhcpLocalServerGroupExists { + if idSplit[2] == "v6" { + return nil, fmt.Errorf("don't find system services dhcp-local-server dhcpv6 group with id '%v' (id must be "+ + ""+idSeparator+""+idSeparator+")", d.Id()) + } + + return nil, fmt.Errorf("don't find system services dhcp-local-server group with id '%v' (id must be "+ + ""+idSeparator+""+idSeparator+")", d.Id()) + } + systemServicesDhcpLocalServerGroupOptions, err := + readSystemServicesDhcpLocalServerGroup(idSplit[0], idSplit[1], idSplit[2], m, jnprSess) + if err != nil { + return nil, err + } + fillSystemServicesDhcpLocalServerGroupData(d, systemServicesDhcpLocalServerGroupOptions) + + result[0] = d + + return result, nil +} + +func checkSystemServicesDhcpLocalServerGroupExists(name, instance, version string, m interface{}, + jnprSess *NetconfObject) (bool, error) { + sess := m.(*Session) + var showConfig string + var err error + showCmd := "show configuration system services dhcp-local-server group " + name + if instance == defaultWord { + if version == "v6" { + showCmd = "show configuration system services dhcp-local-server dhcpv6 group " + name + } + } else { + if version == "v6" { + showCmd = "show configuration routing-instances " + instance + + " system services dhcp-local-server dhcpv6 group " + name + } else { + showCmd = "show configuration routing-instances " + instance + + " system services dhcp-local-server group " + name + } + } + showConfig, err = sess.command(showCmd+" | display set", jnprSess) + if err != nil { + return false, err + } + + if showConfig == emptyWord { + return false, nil + } + + return true, nil +} + +func setSystemServicesDhcpLocalServerGroup(d *schema.ResourceData, m interface{}, jnprSess *NetconfObject) error { + sess := m.(*Session) + configSet := make([]string, 0) + + setPrefix := "set system services dhcp-local-server group " + d.Get("name").(string) + " " + if d.Get("routing_instance").(string) == defaultWord { + if d.Get("version").(string) == "v6" { + setPrefix = "set system services dhcp-local-server dhcpv6 group " + d.Get("name").(string) + " " + } + } else { + if d.Get("version").(string) == "v6" { + setPrefix = "set routing-instances " + d.Get("routing_instance").(string) + + " system services dhcp-local-server dhcpv6 group " + d.Get("name").(string) + " " + } else { + setPrefix = "set routing-instances " + d.Get("routing_instance").(string) + + " system services dhcp-local-server group " + d.Get("name").(string) + " " + } + } + + if v := d.Get("access_profile").(string); v != "" { + configSet = append(configSet, setPrefix+"access-profile \""+v+"\"") + } + if v := d.Get("authentication_password").(string); v != "" { + configSet = append(configSet, setPrefix+"authentication password \""+v+"\"") + } + for _, vBlock := range d.Get("authentication_username_include").([]interface{}) { + authenticationUsernameInclude := vBlock.(map[string]interface{}) + if authenticationUsernameInclude["circuit_type"].(bool) { + configSet = append(configSet, setPrefix+"authentication username-include circuit-type") + } + if authenticationUsernameInclude["client_id"].(bool) { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("client_id not compatible when version = v4") + } + configSet = append(configSet, setPrefix+"authentication username-include client-id") + } + if v := authenticationUsernameInclude["delimiter"].(string); v != "" { + configSet = append(configSet, setPrefix+"authentication username-include delimiter \""+v+"\"") + } + if v := authenticationUsernameInclude["domain_name"].(string); v != "" { + configSet = append(configSet, setPrefix+"authentication username-include domain-name \""+v+"\"") + } + if v := authenticationUsernameInclude["interface_description"].(string); v != "" { + configSet = append(configSet, setPrefix+"authentication username-include interface-description "+v) + } + if authenticationUsernameInclude["interface_name"].(bool) { + configSet = append(configSet, setPrefix+"authentication username-include interface-name") + } + if authenticationUsernameInclude["mac_address"].(bool) { + configSet = append(configSet, setPrefix+"authentication username-include mac-address") + } + if authenticationUsernameInclude["option_60"].(bool) { + if d.Get("version").(string) == "v6" { + return fmt.Errorf("option_60 not compatible when version = v6") + } + configSet = append(configSet, setPrefix+"authentication username-include option-60") + } + if authenticationUsernameInclude["option_82"].(bool) { + if d.Get("version").(string) == "v6" { + return fmt.Errorf("option_82 not compatible when version = v6") + } + configSet = append(configSet, setPrefix+"authentication username-include option-82") + if authenticationUsernameInclude["option_82_circuit_id"].(bool) { + configSet = append(configSet, setPrefix+"authentication username-include option-82 circuit-id") + } + if authenticationUsernameInclude["option_82_remote_id"].(bool) { + configSet = append(configSet, setPrefix+"authentication username-include option-82 remote-id") + } + } else if authenticationUsernameInclude["option_82_circuit_id"].(bool) || + authenticationUsernameInclude["option_82_remote_id"].(bool) { + return fmt.Errorf("authentication_username_include.0.option_82 need to be true with " + + "option_82_circuit_id or option_82_remote_id") + } + if authenticationUsernameInclude["relay_agent_interface_id"].(bool) { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("relay_agent_interface_id not compatible when version = v4") + } + configSet = append(configSet, setPrefix+"authentication username-include relay-agent-interface-id") + } + if authenticationUsernameInclude["relay_agent_remote_id"].(bool) { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("relay_agent_remote_id not compatible when version = v4") + } + configSet = append(configSet, setPrefix+"authentication username-include relay-agent-remote-id") + } + if authenticationUsernameInclude["relay_agent_subscriber_id"].(bool) { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("relay_agent_subscriber_id not compatible when version = v4") + } + configSet = append(configSet, setPrefix+"authentication username-include relay-agent-subscriber-id") + } + if authenticationUsernameInclude["routing_instance_name"].(bool) { + configSet = append(configSet, setPrefix+"authentication username-include routing-instance-name") + } + if v := authenticationUsernameInclude["user_prefix"].(string); v != "" { + configSet = append(configSet, setPrefix+"authentication username-include user-prefix \""+v+"\"") + } + if authenticationUsernameInclude["vlan_tags"].(bool) { + configSet = append(configSet, setPrefix+"authentication username-include vlan-tags") + } + } + if dynProfile := d.Get("dynamic_profile").(string); dynProfile != "" { + configSet = append(configSet, setPrefix+"dynamic-profile \""+dynProfile+"\"") + if v := d.Get("dynamic_profile_use_primary").(string); v != "" { + configSet = append(configSet, setPrefix+"dynamic-profile use-primary \""+v+"\"") + } + if d.Get("dynamic_profile_aggregate_clients").(bool) { + configSet = append(configSet, setPrefix+"dynamic-profile aggregate-clients") + if v := d.Get("dynamic_profile_aggregate_clients_action").(string); v != "" { + configSet = append(configSet, setPrefix+"dynamic-profile aggregate-clients "+v) + } + } else if d.Get("dynamic_profile_aggregate_clients_action").(string) != "" { + return fmt.Errorf("dynamic_profile_aggregate_clients need to be true with " + + "dynamic_profile_aggregate_clients_action") + } + } else if d.Get("dynamic_profile_use_primary").(string) != "" || + d.Get("dynamic_profile_aggregate_clients").(bool) || + d.Get("dynamic_profile_aggregate_clients_action").(string) != "" { + return fmt.Errorf("dynamic_profile need to be set with " + + "dynamic_profile_use_primary, dynamic_profile_aggregate_clients " + + "and dynamic_profile_aggregate_clients_action") + } + interfaceNameList := make([]string, 0) + for _, v := range d.Get("interface").(*schema.Set).List() { + interFace := v.(map[string]interface{}) + if bchk.StringInSlice(interFace["name"].(string), interfaceNameList) { + return fmt.Errorf("multiple blocks interface with the same name %s", interFace["name"].(string)) + } + interfaceNameList = append(interfaceNameList, interFace["name"].(string)) + configSetInterface, err := + setSystemServicesDhcpLocalServerGroupInterface(interFace, setPrefix, d.Get("version").(string)) + if err != nil { + return err + } + configSet = append(configSet, configSetInterface...) + } + for _, lTVal := range d.Get("lease_time_validation").([]interface{}) { + configSet = append(configSet, setPrefix+"lease-time-validation") + if lTVal != nil { + leaseTimeValidation := lTVal.(map[string]interface{}) + if v := leaseTimeValidation["lease_time_threshold"].(int); v != 0 { + configSet = append(configSet, setPrefix+"lease-time-validation lease-time-threshold "+strconv.Itoa(v)) + } + if v := leaseTimeValidation["violation_action"].(string); v != "" { + configSet = append(configSet, setPrefix+"lease-time-validation violation-action "+v) + } + } + } + if v := d.Get("liveness_detection_failure_action").(string); v != "" { + configSet = append(configSet, setPrefix+"liveness-detection failure-action "+v) + } + for _, ldmBfd := range d.Get("liveness_detection_method_bfd").([]interface{}) { + liveDetectMethBfd := ldmBfd.(map[string]interface{}) + setPrefixLDMBfd := setPrefix + "liveness-detection method bfd " + if v := liveDetectMethBfd["detection_time_threshold"].(int); v != -1 { + configSet = append(configSet, setPrefixLDMBfd+"detection-time threshold "+strconv.Itoa(v)) + } + if v := liveDetectMethBfd["holddown_interval"].(int); v != -1 { + configSet = append(configSet, setPrefixLDMBfd+"holddown-interval "+strconv.Itoa(v)) + } + if v := liveDetectMethBfd["minimum_interval"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMBfd+"minimum-interval "+strconv.Itoa(v)) + } + if v := liveDetectMethBfd["minimum_receive_interval"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMBfd+"minimum-receive-interval "+strconv.Itoa(v)) + } + if v := liveDetectMethBfd["multiplier"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMBfd+"multiplier "+strconv.Itoa(v)) + } + if liveDetectMethBfd["no_adaptation"].(bool) { + configSet = append(configSet, setPrefixLDMBfd+"no-adaptation") + } + if v := liveDetectMethBfd["session_mode"].(string); v != "" { + configSet = append(configSet, setPrefixLDMBfd+"session-mode "+v) + } + if v := liveDetectMethBfd["transmit_interval_minimum"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMBfd+"transmit-interval minimum-interval "+strconv.Itoa(v)) + } + if v := liveDetectMethBfd["transmit_interval_threshold"].(int); v != -1 { + configSet = append(configSet, setPrefixLDMBfd+"transmit-interval threshold "+strconv.Itoa(v)) + } + if v := liveDetectMethBfd["version"].(string); v != "" { + configSet = append(configSet, setPrefixLDMBfd+"version "+v) + } + + if len(configSet) == 0 || !strings.HasPrefix(configSet[len(configSet)-1], setPrefixLDMBfd) { + return fmt.Errorf("liveness_detection_method_bfd block is empty") + } + } + for _, ldmLayer2 := range d.Get("liveness_detection_method_layer2").([]interface{}) { + if ldmLayer2 != nil { + liveDetectMethLayer2 := ldmLayer2.(map[string]interface{}) + setPrefixLDMLayer2 := setPrefix + "liveness-detection method layer2-liveness-detection " + if v := liveDetectMethLayer2["max_consecutive_retries"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMLayer2+"max-consecutive-retries "+strconv.Itoa(v)) + } + if v := liveDetectMethLayer2["transmit_interval"].(int); v != 0 { + configSet = append(configSet, setPrefixLDMLayer2+"transmit-interval "+strconv.Itoa(v)) + } + } else { + return fmt.Errorf("liveness_detection_method_layer2 block is empty") + } + } + for _, v := range d.Get("overrides_v4").([]interface{}) { + if d.Get("version").(string) == "v6" { + return fmt.Errorf("overrides_v4 not compatible if version = v6") + } + configSetOverrides, err := + setSystemServicesDhcpLocalServerGroupFamilyDhcpOverridesV4(v.(map[string]interface{}), setPrefix) + if err != nil { + return err + } + configSet = append(configSet, configSetOverrides...) + } + for _, v := range d.Get("overrides_v6").([]interface{}) { + if d.Get("version").(string) == "v4" { + return fmt.Errorf("overrides_v6 not compatible if version = v4") + } + configSetOverrides, err := + setSystemServicesDhcpLocalServerGroupFamilyDhcpOverridesV6(v.(map[string]interface{}), setPrefix) + if err != nil { + return err + } + configSet = append(configSet, configSetOverrides...) + } + if d.Get("reauthenticate_lease_renewal").(bool) { + configSet = append(configSet, setPrefix+"reauthenticate lease-renewal") + } + if d.Get("reauthenticate_remote_id_mismatch").(bool) { + configSet = append(configSet, setPrefix+"reauthenticate remote-id-mismatch") + } + for _, rec := range d.Get("reconfigure").([]interface{}) { + configSet = append(configSet, setPrefix+"reconfigure") + if rec != nil { + reconfigure := rec.(map[string]interface{}) + if v := reconfigure["attempts"].(int); v != 0 { + configSet = append(configSet, setPrefix+"reconfigure attempts "+strconv.Itoa(v)) + } + if reconfigure["clear_on_abort"].(bool) { + configSet = append(configSet, setPrefix+"reconfigure clear-on-abort") + } + if reconfigure["support_option_pd_exclude"].(bool) { + configSet = append(configSet, setPrefix+"reconfigure support-option-pd-exclude") + } + if v := reconfigure["timeout"].(int); v != 0 { + configSet = append(configSet, setPrefix+"reconfigure timeout "+strconv.Itoa(v)) + } + if v := reconfigure["token"].(string); v != "" { + configSet = append(configSet, setPrefix+"reconfigure token \""+v+"\"") + } + if reconfigure["trigger_radius_disconnect"].(bool) { + configSet = append(configSet, setPrefix+"reconfigure trigger radius-disconnect") + } + } + } + if d.Get("remote_id_mismatch_disconnect").(bool) { + configSet = append(configSet, setPrefix+"remote-id-mismatch disconnect") + } + if d.Get("route_suppression_access").(bool) { + configSet = append(configSet, setPrefix+"route-suppression access") + } + if d.Get("route_suppression_access_internal").(bool) { + configSet = append(configSet, setPrefix+"route-suppression access-internal") + } + if d.Get("route_suppression_destination").(bool) { + configSet = append(configSet, setPrefix+"route-suppression destination") + } + if v := d.Get("service_profile").(string); v != "" { + configSet = append(configSet, setPrefix+"service-profile \""+v+"\"") + } + if v := d.Get("short_cycle_protection_lockout_max_time").(int); v != 0 { + configSet = append(configSet, setPrefix+"short-cycle-protection lockout-max-time "+strconv.Itoa(v)) + } + if v := d.Get("short_cycle_protection_lockout_min_time").(int); v != 0 { + configSet = append(configSet, setPrefix+"short-cycle-protection lockout-min-time "+strconv.Itoa(v)) + } + + return sess.configSet(configSet, jnprSess) +} + +func setSystemServicesDhcpLocalServerGroupInterface( + interFace map[string]interface{}, setPrefixInterface, version string) ([]string, error) { + configSet := make([]string, 0) + + setPrefix := setPrefixInterface + "interface " + interFace["name"].(string) + " " + + configSet = append(configSet, setPrefix) + if v := interFace["access_profile"].(string); v != "" { + configSet = append(configSet, setPrefix+"access-profile \""+v+"\"") + } + if dynProfile := interFace["dynamic_profile"].(string); dynProfile != "" { + configSet = append(configSet, setPrefix+"dynamic-profile \""+dynProfile+"\"") + if interFace["dynamic_profile_use_primary"].(string) != "" && + interFace["dynamic_profile_aggregate_clients"].(bool) { + return configSet, fmt.Errorf("conflict between "+ + "dynamic_profile_use_primary and dynamic_profile_aggregate_clients in interface %s", interFace["name"].(string)) + } + if v := interFace["dynamic_profile_use_primary"].(string); v != "" { + configSet = append(configSet, setPrefix+"dynamic-profile use-primary \""+v+"\"") + } + if interFace["dynamic_profile_aggregate_clients"].(bool) { + configSet = append(configSet, setPrefix+"dynamic-profile aggregate-clients") + if v := interFace["dynamic_profile_aggregate_clients_action"].(string); v != "" { + configSet = append(configSet, setPrefix+"dynamic-profile aggregate-clients "+v) + } + } else if interFace["dynamic_profile_aggregate_clients_action"].(string) != "" { + return configSet, fmt.Errorf("dynamic_profile_aggregate_clients need to be true with "+ + "dynamic_profile_aggregate_clients_action in interface %s", interFace["name"].(string)) + } + } else if interFace["dynamic_profile_use_primary"].(string) != "" || + interFace["dynamic_profile_aggregate_clients"].(bool) || + interFace["dynamic_profile_aggregate_clients_action"].(string) != "" { + return configSet, fmt.Errorf("dynamic_profile need to be set with "+ + "dynamic_profile_use_primary, dynamic_profile_aggregate_clients "+ + "or dynamic_profile_aggregate_clients_action in interface %s", interFace["name"].(string)) + } + if interFace["exclude"].(bool) { + configSet = append(configSet, setPrefix+"exclude") + } + for _, v := range interFace["overrides_v4"].([]interface{}) { + if version == "v6" { + return configSet, fmt.Errorf("overrides_v4 not compatible if version = v6") + } + configSetOverrides, err := + setSystemServicesDhcpLocalServerGroupFamilyDhcpOverridesV4(v.(map[string]interface{}), setPrefix) + if err != nil { + return configSet, err + } + configSet = append(configSet, configSetOverrides...) + } + for _, v := range interFace["overrides_v6"].([]interface{}) { + if version == "v4" { + return configSet, fmt.Errorf("overrides_v6 not compatible if version = v4") + } + configSetOverrides, err := + setSystemServicesDhcpLocalServerGroupFamilyDhcpOverridesV6(v.(map[string]interface{}), setPrefix) + if err != nil { + return configSet, err + } + configSet = append(configSet, configSetOverrides...) + } + if v := interFace["service_profile"].(string); v != "" { + configSet = append(configSet, setPrefix+"service-profile \""+v+"\"") + } + if v := interFace["short_cycle_protection_lockout_max_time"].(int); v != 0 { + configSet = append(configSet, setPrefix+"short-cycle-protection lockout-max-time "+strconv.Itoa(v)) + } + if v := interFace["short_cycle_protection_lockout_min_time"].(int); v != 0 { + configSet = append(configSet, setPrefix+"short-cycle-protection lockout-min-time "+strconv.Itoa(v)) + } + if interFace["trace"].(bool) { + configSet = append(configSet, setPrefix+"trace") + } + if v := interFace["upto"].(string); v != "" { + configSet = append(configSet, setPrefix+"upto "+v) + } + + return configSet, nil +} + +func setSystemServicesDhcpLocalServerGroupFamilyDhcpOverridesV4( + overrides map[string]interface{}, setPrefix string) ([]string, error) { + configSet := make([]string, 0) + setPrefix += "overrides " + + if overrides["allow_no_end_option"].(bool) { + configSet = append(configSet, setPrefix+"allow-no-end-option") + } + if v := overrides["asymmetric_lease_time"].(int); v != 0 { + configSet = append(configSet, setPrefix+"asymmetric-lease-time "+strconv.Itoa(v)) + } + if overrides["bootp_support"].(bool) { + configSet = append(configSet, setPrefix+"bootp-support") + } + if v := overrides["client_discover_match"].(string); v != "" { + configSet = append(configSet, setPrefix+"client-discover-match "+v) + } + for _, dobason := range overrides["delay_offer_based_on"].(*schema.Set).List() { + delayOfferBasedOn := dobason.(map[string]interface{}) + configSet = append(configSet, setPrefix+"delay-offer based-on "+ + delayOfferBasedOn["option"].(string)+" "+delayOfferBasedOn["compare"].(string)+" "+ + delayOfferBasedOn["value_type"].(string)+" \""+delayOfferBasedOn["value"].(string)+"\"") + } + if len(overrides["delay_offer_based_on"].(*schema.Set).List()) == 0 && + overrides["delay_offer_delay_time"].(int) != 0 { + return configSet, fmt.Errorf("delay_offer_based_on need to be set with delay_offer_delay_time") + } + if v := overrides["delay_offer_delay_time"].(int); v != 0 { + configSet = append(configSet, setPrefix+"delay-offer delay-time "+strconv.Itoa(v)) + } + if overrides["delete_binding_on_renegotiation"].(bool) { + configSet = append(configSet, setPrefix+"delete-binding-on-renegotiation") + } + if v := overrides["dual_stack"].(string); v != "" { + configSet = append(configSet, setPrefix+"dual-stack \""+v+"\"") + } + if overrides["include_option_82_forcerenew"].(bool) { + configSet = append(configSet, setPrefix+"include-option-82 forcerenew") + } + if overrides["include_option_82_nak"].(bool) { + configSet = append(configSet, setPrefix+"include-option-82 nak") + } + if v := overrides["interface_client_limit"].(int); v != 0 { + configSet = append(configSet, setPrefix+"interface-client-limit "+strconv.Itoa(v)) + } + if overrides["process_inform"].(bool) { + configSet = append(configSet, setPrefix+"process-inform") + if v := overrides["process_inform_pool"].(string); v != "" { + configSet = append(configSet, setPrefix+"process-inform pool \""+v+"\"") + } + } else if overrides["process_inform_pool"].(string) != "" { + return configSet, fmt.Errorf("process_inform need to be true with process_inform_pool") + } + if v := overrides["protocol_attributes"].(string); v != "" { + configSet = append(configSet, setPrefix+"protocol-attributes \""+v+"\"") + } + + if len(configSet) == 0 { + return configSet, fmt.Errorf("an overrides_v4 block is empty") + } + + return configSet, nil +} + +func setSystemServicesDhcpLocalServerGroupFamilyDhcpOverridesV6( + overrides map[string]interface{}, setPrefix string) ([]string, error) { + configSet := make([]string, 0) + setPrefix += "overrides " + + if overrides["always_add_option_dns_server"].(bool) { + configSet = append(configSet, setPrefix+"always-add-option-dns-server") + } + if overrides["always_process_option_request_option"].(bool) { + configSet = append(configSet, setPrefix+"always-process-option-request-option") + } + if v := overrides["asymmetric_lease_time"].(int); v != 0 { + configSet = append(configSet, setPrefix+"asymmetric-lease-time "+strconv.Itoa(v)) + } + if v := overrides["asymmetric_prefix_lease_time"].(int); v != 0 { + configSet = append(configSet, setPrefix+"asymmetric-prefix-lease-time "+strconv.Itoa(v)) + } + if overrides["client_negotiation_match_incoming_interface"].(bool) { + configSet = append(configSet, setPrefix+"client-negotiation-match incoming-interface") + } + for _, dobason := range overrides["delay_advertise_based_on"].(*schema.Set).List() { + delayOfferBasedOn := dobason.(map[string]interface{}) + configSet = append(configSet, setPrefix+"delay-advertise based-on "+ + delayOfferBasedOn["option"].(string)+" "+delayOfferBasedOn["compare"].(string)+" "+ + delayOfferBasedOn["value_type"].(string)+" \""+delayOfferBasedOn["value"].(string)+"\"") + } + if len(overrides["delay_advertise_based_on"].(*schema.Set).List()) == 0 && + overrides["delay_advertise_delay_time"].(int) != 0 { + return configSet, fmt.Errorf("delay_offer_based_on need to be set with delay_offer_delay_time") + } + if v := overrides["delay_advertise_delay_time"].(int); v != 0 { + configSet = append(configSet, setPrefix+"delay-advertise delay-time "+strconv.Itoa(v)) + } + if v := overrides["delegated_pool"].(string); v != "" { + configSet = append(configSet, setPrefix+"delegated-pool \""+v+"\"") + } + if overrides["delete_binding_on_renegotiation"].(bool) { + configSet = append(configSet, setPrefix+"delete-binding-on-renegotiation") + } + if v := overrides["dual_stack"].(string); v != "" { + configSet = append(configSet, setPrefix+"dual-stack \""+v+"\"") + } + if v := overrides["interface_client_limit"].(int); v != 0 { + configSet = append(configSet, setPrefix+"interface-client-limit "+strconv.Itoa(v)) + } + if overrides["multi_address_embedded_option_response"].(bool) { + configSet = append(configSet, setPrefix+"multi-address-embedded-option-response") + } + if overrides["process_inform"].(bool) { + configSet = append(configSet, setPrefix+"process-inform") + if v := overrides["process_inform_pool"].(string); v != "" { + configSet = append(configSet, setPrefix+"process-inform pool \""+v+"\"") + } + } else if overrides["process_inform_pool"].(string) != "" { + return configSet, fmt.Errorf("process_inform need to be true with process_inform_pool") + } + if v := overrides["protocol_attributes"].(string); v != "" { + configSet = append(configSet, setPrefix+"protocol-attributes \""+v+"\"") + } + if overrides["rapid_commit"].(bool) { + configSet = append(configSet, setPrefix+"rapid-commit") + } + if overrides["top_level_status_code"].(bool) { + configSet = append(configSet, setPrefix+"top-level-status-code") + } + + if len(configSet) == 0 { + return configSet, fmt.Errorf("an overrides_v6 block is empty") + } + + return configSet, nil +} + +func readSystemServicesDhcpLocalServerGroup(name, instance, version string, m interface{}, + jnprSess *NetconfObject) (systemServicesDhcpLocalServerGroupOptions, error) { + sess := m.(*Session) + var confRead systemServicesDhcpLocalServerGroupOptions + var showConfig string + var err error + + showCmd := "show configuration system services dhcp-local-server group " + name + if instance == defaultWord { + if version == "v6" { + showCmd = "show configuration system services dhcp-local-server dhcpv6 group " + name + } + } else { + if version == "v6" { + showCmd = "show configuration routing-instances " + instance + + " system services dhcp-local-server dhcpv6 group " + name + } else { + showCmd = "show configuration routing-instances " + instance + + " system services dhcp-local-server group " + name + } + } + showConfig, err = sess.command(showCmd+" | display set relative", jnprSess) + if err != nil { + return confRead, err + } + + if showConfig != emptyWord { + confRead.name = name + confRead.routingInstance = instance + confRead.version = version + for _, item := range strings.Split(showConfig, "\n") { + if strings.Contains(item, "") { + continue + } + if strings.Contains(item, "") { + break + } + itemTrim := strings.TrimPrefix(item, setLineStart) + switch { + case strings.HasPrefix(itemTrim, "access-profile "): + confRead.accessProfile = strings.Trim(strings.TrimPrefix(itemTrim, "access-profile "), "\"") + case strings.HasPrefix(itemTrim, "authentication password "): + confRead.authenticationPassword = strings.Trim(strings.TrimPrefix(itemTrim, "authentication password "), "\"") + case strings.HasPrefix(itemTrim, "authentication username-include "): + if len(confRead.authenticationUsernameInclude) == 0 { + confRead.authenticationUsernameInclude = append(confRead.authenticationUsernameInclude, map[string]interface{}{ + "circuit_type": false, + "client_id": false, + "delimiter": "", + "domain_name": "", + "interface_description": "", + "interface_name": false, + "mac_address": false, + "option_60": false, + "option_82": false, + "option_82_circuit_id": false, + "option_82_remote_id": false, + "relay_agent_interface_id": false, + "relay_agent_remote_id": false, + "relay_agent_subscriber_id": false, + "routing_instance_name": false, + "user_prefix": "", + "vlan_tags": false, + }) + } + itemTrimAuthUserIncl := strings.TrimPrefix(itemTrim, "authentication username-include ") + switch { + case itemTrimAuthUserIncl == "circuit-type": + confRead.authenticationUsernameInclude[0]["circuit_type"] = true + case itemTrimAuthUserIncl == "client-id": + confRead.authenticationUsernameInclude[0]["client_id"] = true + case strings.HasPrefix(itemTrimAuthUserIncl, "delimiter "): + confRead.authenticationUsernameInclude[0]["delimiter"] = + strings.Trim(strings.TrimPrefix(itemTrimAuthUserIncl, "delimiter "), "\"") + case strings.HasPrefix(itemTrimAuthUserIncl, "domain-name "): + confRead.authenticationUsernameInclude[0]["domain_name"] = + strings.Trim(strings.TrimPrefix(itemTrimAuthUserIncl, "domain-name "), "\"") + case strings.HasPrefix(itemTrimAuthUserIncl, "interface-description "): + confRead.authenticationUsernameInclude[0]["interface_description"] = + strings.TrimPrefix(itemTrimAuthUserIncl, "interface-description ") + case itemTrimAuthUserIncl == "interface-name": + confRead.authenticationUsernameInclude[0]["interface_name"] = true + case itemTrimAuthUserIncl == "mac-address": + confRead.authenticationUsernameInclude[0]["mac_address"] = true + case itemTrimAuthUserIncl == "option-60": + confRead.authenticationUsernameInclude[0]["option_60"] = true + case itemTrimAuthUserIncl == "option-82 circuit-id": + confRead.authenticationUsernameInclude[0]["option_82_circuit_id"] = true + confRead.authenticationUsernameInclude[0]["option_82"] = true + case itemTrimAuthUserIncl == "option-82 remote-id": + confRead.authenticationUsernameInclude[0]["option_82_remote_id"] = true + confRead.authenticationUsernameInclude[0]["option_82"] = true + case itemTrimAuthUserIncl == "option-82": + confRead.authenticationUsernameInclude[0]["option_82"] = true + case itemTrimAuthUserIncl == "relay-agent-interface-id": + confRead.authenticationUsernameInclude[0]["relay_agent_interface_id"] = true + case itemTrimAuthUserIncl == "relay-agent-remote-id": + confRead.authenticationUsernameInclude[0]["relay_agent_remote_id"] = true + case itemTrimAuthUserIncl == "relay-agent-subscriber-id": + confRead.authenticationUsernameInclude[0]["relay_agent_subscriber_id"] = true + case itemTrimAuthUserIncl == "routing-instance-name": + confRead.authenticationUsernameInclude[0]["routing_instance_name"] = true + case strings.HasPrefix(itemTrimAuthUserIncl, "user-prefix "): + confRead.authenticationUsernameInclude[0]["user_prefix"] = + strings.Trim(strings.TrimPrefix(itemTrimAuthUserIncl, "user-prefix "), "\"") + case itemTrimAuthUserIncl == "vlan-tags": + confRead.authenticationUsernameInclude[0]["vlan_tags"] = true + } + case strings.HasPrefix(itemTrim, "dynamic-profile "): + switch { + case strings.HasPrefix(itemTrim, "dynamic-profile use-primary "): + confRead.dynamicProfileUsePrimary = + strings.Trim(strings.TrimPrefix(itemTrim, "dynamic-profile use-primary "), "\"") + case strings.HasPrefix(itemTrim, "dynamic-profile aggregate-clients"): + confRead.dynamicProfileAggregateClients = true + if strings.HasPrefix(itemTrim, "dynamic-profile aggregate-clients ") { + confRead.dynamicProfileAggregateClientsAction = + strings.TrimPrefix(itemTrim, "dynamic-profile aggregate-clients ") + } + default: + confRead.dynamicProfile = + strings.Trim(strings.TrimPrefix(itemTrim, "dynamic-profile "), "\"") + } + case strings.HasPrefix(itemTrim, "interface "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "interface "), " ") + interFace := map[string]interface{}{ + "name": itemTrimSplit[0], + "access_profile": "", + "dynamic_profile": "", + "dynamic_profile_use_primary": "", + "dynamic_profile_aggregate_clients": false, + "dynamic_profile_aggregate_clients_action": "", + "exclude": false, + "overrides_v4": make([]map[string]interface{}, 0), + "overrides_v6": make([]map[string]interface{}, 0), + "service_profile": "", + "short_cycle_protection_lockout_max_time": 0, + "short_cycle_protection_lockout_min_time": 0, + "trace": false, + "upto": "", + } + confRead.interFace = copyAndRemoveItemMapList("name", interFace, confRead.interFace) + itemTrimInterface := strings.TrimPrefix(itemTrim, "interface "+itemTrimSplit[0]) + if strings.HasPrefix(itemTrimInterface, " ") { + if err := readSystemServicesDhcpLocalServerGroupInterface( + strings.TrimPrefix(itemTrimInterface, " "), version, interFace); err != nil { + return confRead, err + } + } + confRead.interFace = append(confRead.interFace, interFace) + case strings.HasPrefix(itemTrim, "lease-time-validation"): + if len(confRead.leaseTimeValidation) == 0 { + confRead.leaseTimeValidation = append(confRead.leaseTimeValidation, map[string]interface{}{ + "lease_time_threshold": 0, + "violation_action": "", + }) + } + switch { + case strings.HasPrefix(itemTrim, "lease-time-validation lease-time-threshold "): + var err error + confRead.leaseTimeValidation[0]["lease_time_threshold"], err = + strconv.Atoi(strings.TrimPrefix(itemTrim, "lease-time-validation lease-time-threshold ")) + if err != nil { + return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + case strings.HasPrefix(itemTrim, "lease-time-validation violation-action "): + confRead.leaseTimeValidation[0]["violation_action"] = + strings.TrimPrefix(itemTrim, "lease-time-validation violation-action ") + } + case strings.HasPrefix(itemTrim, "liveness-detection failure-action "): + confRead.livenessDetectionFailureAction = strings.TrimPrefix(itemTrim, "liveness-detection failure-action ") + case strings.HasPrefix(itemTrim, "liveness-detection method bfd "): + if len(confRead.livenessDetectionMethodBfd) == 0 { + confRead.livenessDetectionMethodBfd = append(confRead.livenessDetectionMethodBfd, map[string]interface{}{ + "detection_time_threshold": -1, + "holddown_interval": -1, + "minimum_interval": 0, + "minimum_receive_interval": 0, + "multiplier": 0, + "no_adaptation": false, + "session_mode": "", + "transmit_interval_minimum": 0, + "transmit_interval_threshold": -1, + "version": "", + }) + } + itemTrimLiveDetMethBfd := strings.TrimPrefix(itemTrim, "liveness-detection method bfd ") + var err error + switch { + case strings.HasPrefix(itemTrimLiveDetMethBfd, "detection-time threshold "): + confRead.livenessDetectionMethodBfd[0]["detection_time_threshold"], err = + strconv.Atoi(strings.TrimPrefix(itemTrimLiveDetMethBfd, "detection-time threshold ")) + case strings.HasPrefix(itemTrimLiveDetMethBfd, "holddown-interval "): + confRead.livenessDetectionMethodBfd[0]["holddown_interval"], err = + strconv.Atoi(strings.TrimPrefix(itemTrimLiveDetMethBfd, "holddown-interval ")) + case strings.HasPrefix(itemTrimLiveDetMethBfd, "minimum-interval "): + confRead.livenessDetectionMethodBfd[0]["minimum_interval"], err = + strconv.Atoi(strings.TrimPrefix(itemTrimLiveDetMethBfd, "minimum-interval ")) + case strings.HasPrefix(itemTrimLiveDetMethBfd, "minimum-receive-interval "): + confRead.livenessDetectionMethodBfd[0]["minimum_receive_interval"], err = + strconv.Atoi(strings.TrimPrefix(itemTrimLiveDetMethBfd, "minimum-receive-interval ")) + case strings.HasPrefix(itemTrimLiveDetMethBfd, "multiplier "): + confRead.livenessDetectionMethodBfd[0]["multiplier"], err = + strconv.Atoi(strings.TrimPrefix(itemTrimLiveDetMethBfd, "multiplier ")) + case itemTrimLiveDetMethBfd == "no-adaptation": + confRead.livenessDetectionMethodBfd[0]["no_adaptation"] = true + case strings.HasPrefix(itemTrimLiveDetMethBfd, "session-mode "): + confRead.livenessDetectionMethodBfd[0]["session_mode"] = + strings.TrimPrefix(itemTrimLiveDetMethBfd, "session-mode ") + case strings.HasPrefix(itemTrimLiveDetMethBfd, "transmit-interval minimum-interval "): + confRead.livenessDetectionMethodBfd[0]["transmit_interval_minimum"], err = + strconv.Atoi(strings.TrimPrefix(itemTrimLiveDetMethBfd, "transmit-interval minimum-interval ")) + case strings.HasPrefix(itemTrimLiveDetMethBfd, "transmit-interval threshold "): + confRead.livenessDetectionMethodBfd[0]["transmit_interval_threshold"], err = + strconv.Atoi(strings.TrimPrefix(itemTrimLiveDetMethBfd, "transmit-interval threshold ")) + case strings.HasPrefix(itemTrimLiveDetMethBfd, "version "): + confRead.livenessDetectionMethodBfd[0]["version"] = + strings.TrimPrefix(itemTrimLiveDetMethBfd, "version ") + } + if err != nil { + return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + case strings.HasPrefix(itemTrim, "liveness-detection method layer2-liveness-detection "): + if len(confRead.livenessDetectionMethodLayer2) == 0 { + confRead.livenessDetectionMethodLayer2 = append(confRead.livenessDetectionMethodLayer2, map[string]interface{}{ + "max_consecutive_retries": 0, + "transmit_interval": 0, + }) + } + var err error + switch { + case strings.HasPrefix(itemTrim, "liveness-detection method layer2-liveness-detection max-consecutive-retries "): + confRead.livenessDetectionMethodLayer2[0]["max_consecutive_retries"], err = + strconv.Atoi(strings.TrimPrefix(itemTrim, + "liveness-detection method layer2-liveness-detection max-consecutive-retries ")) + case strings.HasPrefix(itemTrim, "liveness-detection method layer2-liveness-detection transmit-interval "): + confRead.livenessDetectionMethodLayer2[0]["transmit_interval"], err = + strconv.Atoi(strings.TrimPrefix(itemTrim, + "liveness-detection method layer2-liveness-detection transmit-interval ")) + } + if err != nil { + return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + case strings.HasPrefix(itemTrim, "overrides "): + if version == "v4" { + if len(confRead.overridesV4) == 0 { + confRead.overridesV4 = append(confRead.overridesV4, genSystemServicesDhcpLocalServerGroupOverridesV4()) + } + if err := readSystemServicesDhcpLocalServerGroupOverridesV4( + strings.TrimPrefix(itemTrim, "overrides "), + confRead.overridesV4[0], + ); err != nil { + return confRead, err + } + } else if version == "v6" { + if len(confRead.overridesV6) == 0 { + confRead.overridesV6 = append(confRead.overridesV6, genSystemServicesDhcpLocalServerGroupOverridesV6()) + } + if err := readSystemServicesDhcpLocalServerGroupOverridesV6( + strings.TrimPrefix(itemTrim, "overrides "), + confRead.overridesV6[0], + ); err != nil { + return confRead, err + } + } + case itemTrim == "reauthenticate lease-renewal": + confRead.reauthenticateLeaseRenewal = true + case itemTrim == "reauthenticate remote-id-mismatch": + confRead.reauthenticateRemoteIDMismatch = true + case strings.HasPrefix(itemTrim, "reconfigure"): + if len(confRead.reconfigure) == 0 { + confRead.reconfigure = append(confRead.reconfigure, map[string]interface{}{ + "attempts": 0, + "clear_on_abort": false, + "support_option_pd_exclude": false, + "timeout": 0, + "token": "", + "trigger_radius_disconnect": false, + }) + } + if strings.HasPrefix(itemTrim, "reconfigure ") { + var err error + switch { + case strings.HasPrefix(itemTrim, "reconfigure attempts "): + confRead.reconfigure[0]["attempts"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "reconfigure attempts ")) + case itemTrim == "reconfigure clear-on-abort": + confRead.reconfigure[0]["clear_on_abort"] = true + case itemTrim == "reconfigure support-option-pd-exclude": + confRead.reconfigure[0]["support_option_pd_exclude"] = true + case strings.HasPrefix(itemTrim, "reconfigure timeout "): + confRead.reconfigure[0]["timeout"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "reconfigure timeout ")) + case strings.HasPrefix(itemTrim, "reconfigure token "): + confRead.reconfigure[0]["token"] = strings.Trim(strings.TrimPrefix(itemTrim, "reconfigure token "), "\"") + case itemTrim == "reconfigure trigger radius-disconnect": + confRead.reconfigure[0]["trigger_radius_disconnect"] = true + } + if err != nil { + return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + } + case itemTrim == "remote-id-mismatch disconnect": + confRead.remoteIDMismatchDisconnect = true + case itemTrim == "route-suppression access": + confRead.routeSuppressionAccess = true + case itemTrim == "route-suppression access-internal": + confRead.routeSuppressionAccessInternal = true + case itemTrim == "route-suppression destination": + confRead.routeSuppressionDestination = true + case strings.HasPrefix(itemTrim, "service-profile "): + confRead.serviceProfile = strings.Trim(strings.TrimPrefix(itemTrim, "service-profile "), "\"") + case strings.HasPrefix(itemTrim, "short-cycle-protection lockout-max-time "): + var err error + confRead.shortCycleProtectionLockoutMaxTime, err = + strconv.Atoi(strings.TrimPrefix(itemTrim, "short-cycle-protection lockout-max-time ")) + if err != nil { + return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + case strings.HasPrefix(itemTrim, "short-cycle-protection lockout-min-time "): + var err error + confRead.shortCycleProtectionLockoutMinTime, err = + strconv.Atoi(strings.TrimPrefix(itemTrim, "short-cycle-protection lockout-min-time ")) + if err != nil { + return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + } + } + } + + return confRead, nil +} + +func readSystemServicesDhcpLocalServerGroupInterface(itemTrim, version string, interFace map[string]interface{}) error { + var err error + switch { + case strings.HasPrefix(itemTrim, "access-profile "): + interFace["access_profile"] = strings.Trim(strings.TrimPrefix(itemTrim, "access-profile "), "\"") + case strings.HasPrefix(itemTrim, "dynamic-profile "): + switch { + case strings.HasPrefix(itemTrim, "dynamic-profile use-primary "): + interFace["dynamic_profile_use_primary"] = + strings.Trim(strings.TrimPrefix(itemTrim, "dynamic-profile use-primary "), "\"") + case strings.HasPrefix(itemTrim, "dynamic-profile aggregate-clients"): + interFace["dynamic_profile_aggregate_clients"] = true + if strings.HasPrefix(itemTrim, "dynamic-profile aggregate-clients ") { + interFace["dynamic_profile_aggregate_clients_action"] = + strings.TrimPrefix(itemTrim, "dynamic-profile aggregate-clients ") + } + default: + interFace["dynamic_profile"] = strings.Trim(strings.TrimPrefix(itemTrim, "dynamic-profile "), "\"") + } + case itemTrim == "exclude": + interFace["exclude"] = true + case strings.HasPrefix(itemTrim, "overrides "): + if version == "v4" { + if len(interFace["overrides_v4"].([]map[string]interface{})) == 0 { + interFace["overrides_v4"] = append( + interFace["overrides_v4"].([]map[string]interface{}), + genSystemServicesDhcpLocalServerGroupOverridesV4(), + ) + } + if err := readSystemServicesDhcpLocalServerGroupOverridesV4( + strings.TrimPrefix(itemTrim, "overrides "), + interFace["overrides_v4"].([]map[string]interface{})[0], + ); err != nil { + return err + } + } else if version == "v6" { + if len(interFace["overrides_v6"].([]map[string]interface{})) == 0 { + interFace["overrides_v6"] = append( + interFace["overrides_v6"].([]map[string]interface{}), + genSystemServicesDhcpLocalServerGroupOverridesV6(), + ) + } + if err := readSystemServicesDhcpLocalServerGroupOverridesV6( + strings.TrimPrefix(itemTrim, "overrides "), + interFace["overrides_v6"].([]map[string]interface{})[0], + ); err != nil { + return err + } + } + case strings.HasPrefix(itemTrim, "service-profile "): + interFace["service_profile"] = strings.Trim(strings.TrimPrefix(itemTrim, "service-profile "), "\"") + case strings.HasPrefix(itemTrim, "short-cycle-protection lockout-max-time "): + interFace["short_cycle_protection_lockout_max_time"], err = + strconv.Atoi(strings.TrimPrefix(itemTrim, "short-cycle-protection lockout-max-time ")) + case strings.HasPrefix(itemTrim, "short-cycle-protection lockout-min-time "): + interFace["short_cycle_protection_lockout_min_time"], err = + strconv.Atoi(strings.TrimPrefix(itemTrim, "short-cycle-protection lockout-min-time ")) + case itemTrim == "trace": + interFace["trace"] = true + case strings.HasPrefix(itemTrim, "upto "): + interFace["upto"] = strings.TrimPrefix(itemTrim, "upto ") + } + if err != nil { + return fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + + return nil +} + +func genSystemServicesDhcpLocalServerGroupOverridesV4() map[string]interface{} { + return map[string]interface{}{ + "allow_no_end_option": false, + "asymmetric_lease_time": 0, + "bootp_support": false, + "client_discover_match": "", + "delay_offer_based_on": make([]map[string]interface{}, 0), + "delay_offer_delay_time": 0, + "delete_binding_on_renegotiation": false, + "dual_stack": "", + "include_option_82_forcerenew": false, + "include_option_82_nak": false, + "interface_client_limit": 0, + "process_inform": false, + "process_inform_pool": "", + "protocol_attributes": "", + } +} + +func genSystemServicesDhcpLocalServerGroupOverridesV6() map[string]interface{} { + return map[string]interface{}{ + "always_add_option_dns_server": false, + "always_process_option_request_option": false, + "asymmetric_lease_time": 0, + "asymmetric_prefix_lease_time": 0, + "client_negotiation_match_incoming_interface": false, + "delay_advertise_based_on": make([]map[string]interface{}, 0), + "delay_advertise_delay_time": 0, + "delegated_pool": "", + "delete_binding_on_renegotiation": false, + "dual_stack": "", + "interface_client_limit": 0, + "multi_address_embedded_option_response": false, + "process_inform": false, + "process_inform_pool": "", + "protocol_attributes": "", + "rapid_commit": false, + "top_level_status_code": false, + } +} + +func readSystemServicesDhcpLocalServerGroupOverridesV4(itemTrim string, overrides map[string]interface{}) error { + var err error + switch { + case itemTrim == "allow-no-end-option": + overrides["allow_no_end_option"] = true + case strings.HasPrefix(itemTrim, "asymmetric-lease-time "): + overrides["asymmetric_lease_time"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "asymmetric-lease-time ")) + case itemTrim == "bootp-support": + overrides["bootp_support"] = true + case strings.HasPrefix(itemTrim, "client-discover-match "): + overrides["client_discover_match"] = strings.TrimPrefix(itemTrim, "client-discover-match ") + case strings.HasPrefix(itemTrim, "delay-offer based-on "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "delay-offer based-on "), " ") + if len(itemTrimSplit) < 4 { + return fmt.Errorf("can't read line delay-offer based-on, not enough element") + } + overrides["delay_offer_based_on"] = append( + overrides["delay_offer_based_on"].([]map[string]interface{}), + map[string]interface{}{ + "option": itemTrimSplit[0], + "compare": itemTrimSplit[1], + "value_type": itemTrimSplit[2], + "value": strings.Trim(strings.TrimPrefix(itemTrim, + "delay-offer based-on "+itemTrimSplit[0]+" "+itemTrimSplit[1]+" "+itemTrimSplit[2]+" "), "\""), + }) + case strings.HasPrefix(itemTrim, "delay-offer delay-time "): + overrides["delay_offer_delay_time"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "delay-offer delay-time ")) + case itemTrim == "delete-binding-on-renegotiation": + overrides["delete_binding_on_renegotiation"] = true + case strings.HasPrefix(itemTrim, "dual-stack "): + overrides["dual_stack"] = strings.Trim(strings.TrimPrefix(itemTrim, "dual-stack "), "\"") + case itemTrim == "include-option-82 forcerenew": + overrides["include_option_82_forcerenew"] = true + case itemTrim == "include-option-82 nak": + overrides["include_option_82_nak"] = true + case strings.HasPrefix(itemTrim, "interface-client-limit "): + overrides["interface_client_limit"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "interface-client-limit ")) + case strings.HasPrefix(itemTrim, "process-inform pool "): + overrides["process_inform_pool"] = strings.Trim(strings.TrimPrefix(itemTrim, "process-inform pool "), "\"") + overrides["process_inform"] = true + case itemTrim == "process-inform": + overrides["process_inform"] = true + case strings.HasPrefix(itemTrim, "protocol-attributes "): + overrides["protocol_attributes"] = strings.Trim(strings.TrimPrefix(itemTrim, "protocol-attributes "), "\"") + } + if err != nil { + return fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + + return nil +} + +func readSystemServicesDhcpLocalServerGroupOverridesV6(itemTrim string, overrides map[string]interface{}) error { + var err error + switch { + case itemTrim == "always-add-option-dns-server": + overrides["always_add_option_dns_server"] = true + case itemTrim == "always-process-option-request-option": + overrides["always_process_option_request_option"] = true + case strings.HasPrefix(itemTrim, "asymmetric-lease-time "): + overrides["asymmetric_lease_time"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "asymmetric-lease-time ")) + case strings.HasPrefix(itemTrim, "asymmetric-prefix-lease-time "): + overrides["asymmetric_prefix_lease_time"], err = + strconv.Atoi(strings.TrimPrefix(itemTrim, "asymmetric-prefix-lease-time ")) + case itemTrim == "client-negotiation-match incoming-interface": + overrides["client_negotiation_match_incoming_interface"] = true + case strings.HasPrefix(itemTrim, "delay-advertise based-on "): + itemTrimSplit := strings.Split(strings.TrimPrefix(itemTrim, "delay-advertise based-on "), " ") + if len(itemTrimSplit) < 4 { + return fmt.Errorf("can't read line delay-advertise based-on, not enough element") + } + overrides["delay_advertise_based_on"] = append( + overrides["delay_advertise_based_on"].([]map[string]interface{}), + map[string]interface{}{ + "option": itemTrimSplit[0], + "compare": itemTrimSplit[1], + "value_type": itemTrimSplit[2], + "value": strings.Trim(strings.TrimPrefix(itemTrim, + "delay-advertise based-on "+itemTrimSplit[0]+" "+itemTrimSplit[1]+" "+itemTrimSplit[2]+" "), "\""), + }) + case strings.HasPrefix(itemTrim, "delay-advertise delay-time "): + overrides["delay_advertise_delay_time"], err = + strconv.Atoi(strings.TrimPrefix(itemTrim, "delay-advertise delay-time ")) + case strings.HasPrefix(itemTrim, "delegated-pool "): + overrides["delegated_pool"] = strings.Trim(strings.TrimPrefix(itemTrim, "delegated-pool "), "\"") + case itemTrim == "delete-binding-on-renegotiation": + overrides["delete_binding_on_renegotiation"] = true + case strings.HasPrefix(itemTrim, "dual-stack "): + overrides["dual_stack"] = strings.Trim(strings.TrimPrefix(itemTrim, "dual-stack "), "\"") + case strings.HasPrefix(itemTrim, "interface-client-limit "): + overrides["interface_client_limit"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "interface-client-limit ")) + case itemTrim == "multi-address-embedded-option-response": + overrides["multi_address_embedded_option_response"] = true + case strings.HasPrefix(itemTrim, "process-inform pool "): + overrides["process_inform_pool"] = strings.Trim(strings.TrimPrefix(itemTrim, "process-inform pool "), "\"") + overrides["process_inform"] = true + case itemTrim == "process-inform": + overrides["process_inform"] = true + case strings.HasPrefix(itemTrim, "protocol-attributes "): + overrides["protocol_attributes"] = strings.Trim(strings.TrimPrefix(itemTrim, "protocol-attributes "), "\"") + case itemTrim == "rapid-commit": + overrides["rapid_commit"] = true + case itemTrim == "top-level-status-code": + overrides["top_level_status_code"] = true + } + if err != nil { + return fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + + return nil +} + +func delSystemServicesDhcpLocalServerGroup( + name, instance, version string, m interface{}, jnprSess *NetconfObject) error { + sess := m.(*Session) + configSet := make([]string, 0, 1) + if instance == defaultWord { + if version == "v6" { + configSet = append(configSet, "delete system services dhcp-local-server dhcpv6 group "+name) + } else { + configSet = append(configSet, "delete system services dhcp-local-server group "+name) + } + } else { + if version == "v6" { + configSet = append(configSet, "delete routing-instances "+instance+ + " system services dhcp-local-server dhcpv6 group "+name) + } else { + configSet = append(configSet, "delete routing-instances "+instance+ + " system services dhcp-local-server group "+name) + } + } + + return sess.configSet(configSet, jnprSess) +} + +func fillSystemServicesDhcpLocalServerGroupData( + d *schema.ResourceData, systemServicesDhcpLocalServerGroupOptions systemServicesDhcpLocalServerGroupOptions) { + if tfErr := d.Set("name", + systemServicesDhcpLocalServerGroupOptions.name); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("routing_instance", + systemServicesDhcpLocalServerGroupOptions.routingInstance); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("version", + systemServicesDhcpLocalServerGroupOptions.version); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("access_profile", + systemServicesDhcpLocalServerGroupOptions.accessProfile); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("authentication_password", + systemServicesDhcpLocalServerGroupOptions.authenticationPassword); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("authentication_username_include", + systemServicesDhcpLocalServerGroupOptions.authenticationUsernameInclude); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("dynamic_profile", + systemServicesDhcpLocalServerGroupOptions.dynamicProfile); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("dynamic_profile_use_primary", + systemServicesDhcpLocalServerGroupOptions.dynamicProfileUsePrimary); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("dynamic_profile_aggregate_clients", + systemServicesDhcpLocalServerGroupOptions.dynamicProfileAggregateClients); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("dynamic_profile_aggregate_clients_action", + systemServicesDhcpLocalServerGroupOptions.dynamicProfileAggregateClientsAction); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("interface", + systemServicesDhcpLocalServerGroupOptions.interFace); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("lease_time_validation", + systemServicesDhcpLocalServerGroupOptions.leaseTimeValidation); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("liveness_detection_failure_action", + systemServicesDhcpLocalServerGroupOptions.livenessDetectionFailureAction); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("liveness_detection_method_bfd", + systemServicesDhcpLocalServerGroupOptions.livenessDetectionMethodBfd); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("liveness_detection_method_layer2", + systemServicesDhcpLocalServerGroupOptions.livenessDetectionMethodLayer2); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("overrides_v4", + systemServicesDhcpLocalServerGroupOptions.overridesV4); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("overrides_v6", + systemServicesDhcpLocalServerGroupOptions.overridesV6); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("reauthenticate_lease_renewal", + systemServicesDhcpLocalServerGroupOptions.reauthenticateLeaseRenewal); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("reauthenticate_remote_id_mismatch", + systemServicesDhcpLocalServerGroupOptions.reauthenticateRemoteIDMismatch); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("reconfigure", + systemServicesDhcpLocalServerGroupOptions.reconfigure); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("remote_id_mismatch_disconnect", + systemServicesDhcpLocalServerGroupOptions.remoteIDMismatchDisconnect); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("route_suppression_access", + systemServicesDhcpLocalServerGroupOptions.routeSuppressionAccess); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("route_suppression_access_internal", + systemServicesDhcpLocalServerGroupOptions.routeSuppressionAccessInternal); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("route_suppression_destination", + systemServicesDhcpLocalServerGroupOptions.routeSuppressionDestination); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("service_profile", + systemServicesDhcpLocalServerGroupOptions.serviceProfile); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("short_cycle_protection_lockout_max_time", + systemServicesDhcpLocalServerGroupOptions.shortCycleProtectionLockoutMaxTime); tfErr != nil { + panic(tfErr) + } + if tfErr := d.Set("short_cycle_protection_lockout_min_time", + systemServicesDhcpLocalServerGroupOptions.shortCycleProtectionLockoutMinTime); tfErr != nil { + panic(tfErr) + } +} diff --git a/junos/resource_system_services_dhcp_localserver_group_test.go b/junos/resource_system_services_dhcp_localserver_group_test.go new file mode 100644 index 00000000..bc29bfa9 --- /dev/null +++ b/junos/resource_system_services_dhcp_localserver_group_test.go @@ -0,0 +1,273 @@ +package junos_test + +import ( + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccJunosSystemServicesDhcpLocalserverGroup_basic(t *testing.T) { + if os.Getenv("TESTACC_ROUTER") != "" { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccJunosSystemServicesDhcpLocalserverGroupConfigCreate(), + }, + { + Config: testAccJunosSystemServicesDhcpLocalserverGroupConfigUpdate(), + }, + { + ResourceName: "junos_system_services_dhcp_localserver_group.testacc_dhcpgroup_v4_default", + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "junos_system_services_dhcp_localserver_group.testacc_dhcpgroup_v6_default", + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "junos_system_services_dhcp_localserver_group.testacc_dhcpgroup_v4_ri", + ImportState: true, + ImportStateVerify: true, + }, + { + ResourceName: "junos_system_services_dhcp_localserver_group.testacc_dhcpgroup_v6_ri", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + } +} + +func testAccJunosSystemServicesDhcpLocalserverGroupConfigCreate() string { + return ` +resource "junos_system_services_dhcp_localserver_group" "testacc_dhcpgroup_v4_default" { + name = "testacc_dhcpgroup_v4_default" + + dynamic_profile = "junos-default-profile" +} + +resource "junos_system_services_dhcp_localserver_group" "testacc_dhcpgroup_v6_default" { + name = "testacc_dhcpgroup_v6_default" + version = "v6" + + interface { + name = "ge-0/0/3.1" + upto = "ge-0/0/3.3" + exclude = true + } +} + +resource "junos_routing_instance" "testacc_dhcpgroup" { + name = "testacc_dhcpgroup" +} +resource "junos_system_services_dhcp_localserver_group" "testacc_dhcpgroup_v4_ri" { + name = "testacc_dhcpgroup_v4_ri" + routing_instance = junos_routing_instance.testacc_dhcpgroup.name + + interface { + name = "ge-0/0/3.0" + dynamic_profile = "junos-default-profile" + trace = true + } + interface { + name = "ge-0/0/3.1" + dynamic_profile = "junos-default-profile" + } +} +resource "junos_system_services_dhcp_localserver_group" "testacc_dhcpgroup_v6_ri" { + name = "testacc_dhcpgroup_v6_ri" + routing_instance = junos_routing_instance.testacc_dhcpgroup.name + version = "v6" + + short_cycle_protection_lockout_max_time = 2 + short_cycle_protection_lockout_min_time = 1 +} +` +} + +func testAccJunosSystemServicesDhcpLocalserverGroupConfigUpdate() string { + return ` +resource "junos_system_services_dhcp_localserver_group" "testacc_dhcpgroup_v4_default" { + name = "testacc_dhcpgroup_v4_default" + + dynamic_profile = "junos-default-profile" + authentication_password = "test1#1" + authentication_username_include { + circuit_type = true + delimiter = "#" + domain_name = "a domain" + interface_description = "logical" + interface_name = true + mac_address = true + option_60 = true + option_82 = true + option_82_circuit_id = true + option_82_remote_id = true + routing_instance_name = true + user_prefix = "user_#1_" + vlan_tags = true + } + reauthenticate_lease_renewal = true + reauthenticate_remote_id_mismatch = true + short_cycle_protection_lockout_max_time = 2 + short_cycle_protection_lockout_min_time = 1 +} + +resource "junos_system_services_dhcp_localserver_group" "testacc_dhcpgroup_v6_default" { + name = "testacc_dhcpgroup_v6_default" + version = "v6" + + authentication_username_include { + client_id = true + relay_agent_interface_id = true + relay_agent_remote_id = true + relay_agent_subscriber_id = true + } + dynamic_profile = "junos-default-profile" + dynamic_profile_use_primary = "junos-default-dhcp-profile" + interface { + name = "ge-0/0/3.1" + upto = "ge-0/0/3.3" + exclude = true + } + lease_time_validation {} + liveness_detection_method_layer2 { + max_consecutive_retries = 4 + transmit_interval = 305 + } + overrides_v6 { + always_add_option_dns_server = true + always_process_option_request_option = true + asymmetric_lease_time = 900 + asymmetric_prefix_lease_time = 1000 + client_negotiation_match_incoming_interface = true + delay_advertise_based_on { + option = "option-15" + compare = "equals" + value_type = "ascii" + value = "1#1 2" + } + delay_advertise_delay_time = 11 + delegated_pool = "test foo" + delete_binding_on_renegotiation = true + dual_stack = "dual-#stack" + interface_client_limit = 120 + multi_address_embedded_option_response = true + process_inform = true + protocol_attributes = "test#proto" + rapid_commit = true + top_level_status_code = true + } + route_suppression_access_internal = true +} + +resource "junos_routing_instance" "testacc_dhcpgroup" { + name = "testacc_dhcpgroup" +} +resource "junos_system_services_dhcp_localserver_group" "testacc_dhcpgroup_v4_ri" { + name = "testacc_dhcpgroup_v4_ri" + routing_instance = junos_routing_instance.testacc_dhcpgroup.name + + authentication_username_include { + option_82 = true + } + dynamic_profile = "junos-default-profile" + dynamic_profile_aggregate_clients = true + interface { + name = "ge-0/0/3.0" + dynamic_profile = "junos-default-profile" + dynamic_profile_use_primary = "junos-default-profile" + trace = true + } + interface { + name = "ge-0/0/3.1" + dynamic_profile = "junos-default-profile" + } + interface { + name = "ge-0/0/3.4" + dynamic_profile = "junos-default-profile" + dynamic_profile_aggregate_clients = true + dynamic_profile_aggregate_clients_action = "merge" + overrides_v4 { + allow_no_end_option = true + asymmetric_lease_time = 900 + bootp_support = true + client_discover_match = "option60-and-option82" + delete_binding_on_renegotiation = true + dual_stack = "dual-#stack" + include_option_82_forcerenew = true + include_option_82_nak = true + interface_client_limit = 120 + process_inform = true + protocol_attributes = "test#proto" + } + service_profile = "a_service#1" + short_cycle_protection_lockout_max_time = 2 + short_cycle_protection_lockout_min_time = 1 + } + lease_time_validation { + lease_time_threshold = 60099 + violation_action = "strict" + } + liveness_detection_method_bfd { + detection_time_threshold = 200000 + holddown_interval = 2 + minimum_interval = 30003 + minimum_receive_interval = 30004 + multiplier = 5 + no_adaptation = true + session_mode = "multihop" + transmit_interval_minimum = 30006 + transmit_interval_threshold = 30066 + version = "automatic" + } + liveness_detection_failure_action = "log-only" + overrides_v4 { + client_discover_match = "incoming-interface" + delay_offer_based_on { + option = "option-60" + compare = "equals" + value_type = "ascii" + value = "test #1" + } + delay_offer_delay_time = 2 + process_inform = true + process_inform_pool = "pool@21#" + } + reconfigure {} + route_suppression_destination = true + short_cycle_protection_lockout_max_time = 2 + short_cycle_protection_lockout_min_time = 1 +} +resource "junos_system_services_dhcp_localserver_group" "testacc_dhcpgroup_v6_ri" { + name = "testacc_dhcpgroup_v6_ri" + routing_instance = junos_routing_instance.testacc_dhcpgroup.name + version = "v6" + + + dynamic_profile = "junos-default-profile" + dynamic_profile_aggregate_clients = true + dynamic_profile_aggregate_clients_action = "merge" + + reconfigure { + attempts = 1 + clear_on_abort = true + support_option_pd_exclude = true + timeout = 2 + token = "tok en #" + trigger_radius_disconnect = true + } + remote_id_mismatch_disconnect = true + route_suppression_access = true + service_profile = "service-pro#2" + short_cycle_protection_lockout_max_time = 2 + short_cycle_protection_lockout_min_time = 1 +} +` +} diff --git a/junos/resource_system_test.go b/junos/resource_system_test.go index 283fce01..2401fedd 100644 --- a/junos/resource_system_test.go +++ b/junos/resource_system_test.go @@ -339,6 +339,18 @@ resource junos_system "testacc_system" { threshold_action = "accept" threshold_value = 30 } + ports { + auxiliary_authentication_order = ["password", "radius"] + auxiliary_disable = true + auxiliary_insecure = true + auxiliary_logout_on_disconnect = true + auxiliary_type = "vt100" + console_authentication_order = ["radius", "password"] + console_disable = true + console_insecure = true + console_logout_on_disconnect = true + console_type = "vt100" + } radius_options_attributes_nas_ipaddress = "192.0.2.12" radius_options_enhanced_accounting = true radius_options_password_protocol_mschapv2 = true diff --git a/website/docs/d/interface_logical.html.markdown b/website/docs/d/interface_logical.html.markdown index a24825ad..3608bece 100644 --- a/website/docs/d/interface_logical.html.markdown +++ b/website/docs/d/interface_logical.html.markdown @@ -50,6 +50,9 @@ The following attributes are exported: - **address** (Block List) List of address. See [below for nested schema](#address-attributes-for-family_inet). + - **dhcp** (Block) + Enable DHCP client and configuration. + See [below for nested schema](#dhcp-attributes-for-family_inet). - **filter_input** (String) Filter applied to received packets. - **filter_output** (String) @@ -70,6 +73,9 @@ The following attributes are exported: See [below for nested schema](#address-attributes-for-family_inet6). - **dad_disable** (Boolean) Disable duplicate-address-detection. + - **dhcpv6_client** (Block) + Enable DHCP client and configuration. + See [below for nested schema](#dhcpv6_client-attributes-for-family_inet6). - **filter_input** (String) Filter applied to received packets. - **filter_output** (String) @@ -151,6 +157,47 @@ The following attributes are exported: --- +### dhcp attributes for family_inet + +- **client_identifier_ascii** (String) + Client identifier as an ASCII string. +- **client_identifier_hexadecimal** (String) + Client identifier as a hexadecimal string. +- **client_identifier_prefix_hostname** (Boolean) + Add prefix router host name to client-id option. +- **client_identifier_prefix_routing_instance_name** (Boolean) + Add prefix routing instance name to client-id option. +- **client_identifier_use_interface_description** (Boolean) + Use the interface description. +- **client_identifier_userid_ascii** (String) + Add user id as an ASCII string to client-id option. +- **client_identifier_userid_hexadecimal** (String) + Add user id as a hexadecimal string to client-id option. +- **force_discover** (Boolean) + Send DHCPDISCOVER after DHCPREQUEST retransmission failure. +- **lease_time** (Number) + Lease time in seconds requested in DHCP client protocol packet (60..2147483647 seconds). +- **lease_time_infinite** (Boolean) + Lease never expires. +- **metric** (Number) + Client initiated default-route metric (0..255). +- **no_dns_install** (Boolean) + Do not install DNS information learned from DHCP server. +- **options_no_hostname** (Boolean) + Do not carry hostname (RFC option code is 12) in packet. +- **retransmission_attempt** (Number) + Number of attempts to retransmit the DHCP client protocol packet (0..50000). +- **retransmission_interval** (Number) + Number of seconds between successive retransmission (4..64 seconds). +- **server_address** (String) + DHCP Server-address. +- **update_server** (Boolean) + Propagate TCP/IP settings to DHCP server. +- **vendor_id** (String) + Vendor class id for the DHCP Client. + +--- + ### address attributes for family_inet6 - **cidr_ip** (String) @@ -175,6 +222,35 @@ block but without `authentication_key`, `authentication_type` and with --- +### dhcpv6_client attributes for family_inet6 + +- **client_identifier_duid_type** (String) + DUID identifying a client. +- **client_type** (String) + DHCPv6 client type. +- **client_ia_type_na** (Boolean) + DHCPv6 client identity association type Non-temporary Address. +- **client_ia_type_pd** (Boolean) + DHCPv6 client identity association type Prefix Address. +- **no_dns_install** (Boolean) + Do not install DNS information learned from DHCP server. +- **prefix_delegating_preferred_prefix_length** (Number) + Client preferred prefix length (0..64). +- **prefix_delegating_sub_prefix_length** (Number) + The sub prefix length for LAN interfaces (1..127). +- **rapid_commit** (Boolean) + Option is used to signal the use of the two message exchange for address assignment. +- **req_option** (Set of String) + DHCPV6 client requested option configuration. +- **retransmission_attempt** (Number) + Number of attempts to retransmit the DHCPV6 client protocol packet (0..9). +- **update_router_advertisement_interface** (Set of String) + Interfaces on which to delegate prefix. +- **update_server** (Boolean) + Propagate TCP/IP settings to DHCP server. + +--- + ### rpf_check attributes - **fail_filter** (String) diff --git a/website/docs/d/interfaces_physical_present.html.markdown b/website/docs/d/interfaces_physical_present.html.markdown new file mode 100644 index 00000000..9fd9b0af --- /dev/null +++ b/website/docs/d/interfaces_physical_present.html.markdown @@ -0,0 +1,50 @@ +--- +layout: "junos" +page_title: "Junos: junos_interfaces_physical_present" +sidebar_current: "docs-junos-data-source-interfaces-physical-present" +description: |- + Get list of all or filtered physical interfaces present on the Junos device and their admin/operational status. +--- + +# junos_interfaces_physical_present + +Get list of all of filtered physical interfaces present on the Junos device and their +admin/operational status. + +## Example Usage + +```hcl +# All interfaces that begin with 'ge-' +data junos_interfaces_physical_present "interfaces_ge" { + match_name = "^ge-.*$" +} +``` + +## Argument Reference + +The following arguments are supported: + +- **match_name** (Optional, String) + A regexp to apply filter on name. + Need to be a valid regexp. +- **match_admin_up** (Optional, Bool) + Filter on interfaces that have admin status `up`. +- **match_oper_up** (Optional, Bool) + Filter on interfaces that have operational status `up`. + +## Attributes Reference + +The following attributes are exported: + +- **id** (String) + An identifier for the resource. +- **interface_names** (List of String) + List of interface names found. +- **interface_statuses** (List of Block) + For each interface name. + - **name** (String) + Interface name. + - **admin_status** (String) + Admin status. + - **oper_status** (String) + Operational status. diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index ea2ff95c..733b5980 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -224,7 +224,7 @@ ge-0/0/3 { } ``` -and considers the interface available if the is this lines and only this lines on interface. +and considers the interface available if there is this lines and only this lines on interface. ## Number of ssh connections and netconf commands diff --git a/website/docs/r/access_address_assignment_pool.html.markdown b/website/docs/r/access_address_assignment_pool.html.markdown new file mode 100644 index 00000000..6447da27 --- /dev/null +++ b/website/docs/r/access_address_assignment_pool.html.markdown @@ -0,0 +1,244 @@ +--- +layout: "junos" +page_title: "Junos: junos_access_address_assignment_pool" +sidebar_current: "docs-junos-resource-access-address-assignment-pool" +description: |- + Create an access address-assignment pool +--- + +# junos_access_address_assignment_pool + +Provides an access address-assignment pool. + +## Example Usage + +```hcl +# Add an access address-assignment pool +resource junos_access_address_assignment_pool "demo_dhcp_pool" { + name = "demo_dhcp_pool" + family { + type = "inet" + network = "192.0.2.128/25" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +- **name** (Required, String, Forces new resource) + Address pool name. +- **routing_instance** (Optional, String, Forces new resource) + Routing instance for pool. + Need to be default or name of routing instance. + Defaults to `default` +- **family** (Required, Block) + Configure address family (`inet` or `inet6`). + See [below for nested schema](#family-arguments). +- **active_drain** (Optional, Boolean) + Notify client of pool active drain mode. +- **hold_down** (Optional, Boolean) + Place pool in passive drain mode. +- **link** (Optional, String) + Address pool link name. + +--- + +### family arguments + +- **type** (Required, String) + Type of family. + Need to be `inet` or `inet6`. +- **network** (Required, String) + Network address of pool. +- **dhcp_attributes** (Optional, Block) + DHCP options and match criteria. + See [below for nested schema](#dhcp_attributes-arguments). +- **excluded_address** (Optional, Set of String) + Excluded Addresses. + Need to be valid IP addresses. +- **excluded_range** (Optional, List of Block) + For each name of excluded address range to declare. + - **name** (Required, String) + Range name. + - **low** (Required, String) + Lower limit of excluded address range. + Need to be a valid IP address. + - **high** (Required, String) + Upper limit of excluded address range. + Need to be a valid IP address. +- **host** (Optional, List of Block) + For each name of host to declare. + `type` need to be `inet`. + - **name** (Required, String) + Hostname. + - **hardware_address** (Required, String) + Hardware address. + Need to be a valid MAC address. + - **Reserved address** (Required, String) + Hardware address. + Need to be a valid IPv4 address. +- **inet_range** (Optional, List of Block) + For each name of address range to declare. + `type` need to be `inet`. + - **name** (Required, String) + Range name. + - **low** (Required, String) + Lower limit of address range. + Need to be a valid IPv4 address. + - **high** (Required, String) + Upper limit of address range. + Need to be a valid IPv4 address. +- **inet6_range** (Optional, List of Block) + For each name of address range to declare. + Need to set one of `prefix_length` or `low` + `high`. + `type` need to be `inet6`. + - **name** (Required, String) + Range name. + - **low** (Optional, String) + Lower limit of IPv6 address range. + Need to be a valid IPv6 address with mask. + - **high** (Optional, String) + Upper limit of IPv6 address range. + Need to be a valid IPv6 address with mask. + - **prefix_length** (Optional, Number) + IPv6 delegated prefix length (1..128). +- **xauth_attributes_primary_dns** (Optional, String) + Specify the primary-dns IP address. + Need to be a valid IPv4 address with mask. + `type` need to be `inet`. +- **xauth_attributes_primary_wins** (Optional, String) + Specify the primary-wins IP address. + Need to be a valid IPv4 address with mask. + `type` need to be `inet`. +- **xauth_attributes_secondary_dns** (Optional, String) + Specify the secondary-dns IP address. + Need to be a valid IPv4 address with mask. + `type` need to be `inet`. +- **xauth_attributes_secondary_wins** (Optional, String) + Specify the secondary-wins IP address. + Need to be a valid IPv4 address with mask. + `type` need to be `inet`. + +--- + +### dhcp_attributes arguments + +- **boot_file** (Optional, String) + Boot filename advertised to clients. +- **boot_server** (Optional, String) + Boot server advertised to clients. +- **dns_server** (Optional, List of String) + IPv6 domain name servers available to the client. + `type` need to be `inet6`. +- **domain_name** (Optional, String) + Domain name advertised to clients. +- **exclude_prefix_len** (Optional, Number) + Length of IPv6 prefix to be excluded from delegated prefix (1..128). + `type` need to be `inet6`. +- **grace_period** (Optional, Number) + Grace period for leases (seconds). +- **maximum_lease_time** (Optional, Number) + Maximum lease time advertised to clients (seconds). + Conflict with `maximum_lease_time_infinite`, `preferred_lifetime*`, `valid_lifetime*`. +- **maximum_lease_time_infinite** (Optional, Boolean) + Lease time can be infinite. + Conflict with `maximum_lease_time`, `preferred_lifetime*`, `valid_lifetime*`. +- **name_server** (Optional, List of String) + IPv4 domain name servers available to the client. + Need to be valid IPv4 addresses. +- **netbios_node_type** (Optional, String) + Type of NETBIOS node advertised to clients. + Need to be `b-node`, `h-node`, `m-node` or `p-node`. +- **next_server** (Optional, String) + Next server that clients need to contact. + Need to be a valid IPv4 address. + +- **option** (Optional, String) + DHCP option. + Format need to match `^\d+ (array )?(byte|flag|hex-string|integer|ip-address|short|string|unsigned-integer|unsigned-short) .*$`. + +- **option_match_82_circuit_id** (Optional, List of Block) + For each value to declare, circuit ID portion of the option 82. + - **value** (Required, String) + Match value. + - **range** (Required, String) + Range name. +- **option_match_82_remote_id** (Optional, List of Block) + For each value to declare, remote ID portion of the option 82. + - **value** (Required, String) + Match value. + - **range** (Required, String) + Range name. +- **preferred_lifetime** (Optional, Number) + Preferred lifetime advertised to clients (seconds). + `type` need to be `inet6`. + Conflict with `preferred_lifetime_infinite`, `maximum_lease_time*`. +- **preferred_lifetime_infinite** (Optional, Boolean) + Lease time can be infinite. + `type` need to be `inet6`. + Conflict with `preferred_lifetime`, `maximum_lease_time*`. +- **propagate_ppp_settings** (Optional, Set of String) + PPP interface name for propagating DNS/WINS settings. +- **propagate_settings** (Optional, String) + Interface name for propagating TCP/IP Settings to pool. +- **router** (Optional, String) + Routers advertised to clients. + Need to be valid IPv4 addresses. +- **server_identifier** (Optional, String) + Server Identifier - IP address value. + Need to be a valid IPv4 address. +- **sip_server_inet_address** (Optional, List of String) + SIP servers list of IPv4 addresses available to the client. + Need to be valid IPv4 addresses. +- **sip_server_inet_domain_name** (Optional, List of String) + SIP server domain name available to clients. +- **sip_server_inet6_address** (Optional, List of String) + SIP Servers list of IPv6 addresses available to the client. + Need to be valid IPv6 addresses. + `type` need to be `inet6`. +- **sip_server_inet6_domain_name** (Optional, String) + SIP server domain name available to clients. + `type` need to be `inet6`. +- **t1_percentage** (Optional, Number) + T1 time as percentage of preferred lifetime or max lease (0..100 percent). + Conflict with `t(1|2)_(renewal|rebinding)_time`. +- **t1_renewal_time** (Optional, Number) + T1 renewal time (seconds). + Conflict with `t(1|2)_percentage`. +- **t2_percentage** (Optional, Number) + T2 time as percentage of preferred lifetime or max lease (0..100 percent). + Conflict with `t(1|2)_(renewal|rebinding)_time`. +- **t2_rebinding_time**(Optional, Number) + T2 rebinding time (seconds). + Conflict with `t(1|2)_percentage`. +- **tftp_server** (Optional, String) + TFTP server IP address advertised to clients. + Need to be a valid IPv4 address. +- **valid_lifetime** (Optional, Number) + Valid lifetime advertised to clients (seconds). + `type` need to be `inet6`. + Conflict with `valid_lifetime_infinite`, `maximum_lease_time*`. +- **valid_lifetime_infinite** (Optional, Boolean) + Lease time can be infinite. + `type` need to be `inet6`. + Conflict with `valid_lifetime`, `maximum_lease_time*`. +- **wins_server** (Optional, List of String) + WINS name servers. + Need to be valid IPv4 addresses. + +## Attributes Reference + +The following attributes are exported: + +- **id** (String) + An identifier for the resource with format `_-_`. + +## Import + +Junos aggregate route can be imported using an id made up of `_-_`, e.g. + +```shell +$ terraform import junos_access_address_assignment_pool.demo_dhcp_pool demo_dhcp_pool_-_default +``` diff --git a/website/docs/r/aggregate_route.html.markdown b/website/docs/r/aggregate_route.html.markdown index 13c0b678..46ca37f8 100644 --- a/website/docs/r/aggregate_route.html.markdown +++ b/website/docs/r/aggregate_route.html.markdown @@ -3,17 +3,17 @@ layout: "junos" page_title: "Junos: junos_aggregate_route" sidebar_current: "docs-junos-resource-aggregate-route" description: |- - Create a aggregate route for destination + Create an aggregate route for destination --- # junos_aggregate_route -Provides a aggregate route resource for destination. +Provides an aggregate route resource for destination. ## Example Usage ```hcl -# Add a aggregate route +# Add an aggregate route resource junos_aggregate_route "demo_aggregate_route" { destination = "192.0.2.0/25" routing_instance = "prod-vr" diff --git a/website/docs/r/application.html.markdown b/website/docs/r/application.html.markdown index 7341f647..dfd07d77 100644 --- a/website/docs/r/application.html.markdown +++ b/website/docs/r/application.html.markdown @@ -3,17 +3,17 @@ layout: "junos" page_title: "Junos: junos_application" sidebar_current: "docs-junos-resource-application" description: |- - Create a application + Create an application --- # junos_application -Provides a application resource. +Provides an application resource. ## Example Usage ```hcl -# Add a application +# Add an application resource junos_application "mysql" { name = "mysql" protocol = "tcp" @@ -37,7 +37,11 @@ The following arguments are supported: Match ether type. Must be in hex (example: 0x8906). - **inactivity_timeout** (Optional, Number) - Application-specific inactivity timeout (4..86400 seconds). + Application-specific inactivity timeout (4..86400 seconds). + Conflict with `inactivity_timeout_never`. +- **inactivity_timeout_never** (Optional, Boolean) + Disables inactivity timeout. + Conflict with `inactivity_timeout`. - **protocol** (Optional, String) Protocol used by application. - **rpc_program_number** (Optional, String) @@ -45,6 +49,43 @@ The following arguments are supported: Must be an integer or a range of integers. - **source_port** (Optional, String) Port(s) source used by application. +- **term** (Optional, Block List) + For each name of term to declare. + Conflict with `application_protocol`, `destination_port`, `inactivity_timeout`, `protocol`, + `rpc_program_number`, `source_port` and `uuid`. + See [below for nested schema](#term-arguments). +- **uuid** (Optional, String) + Match universal unique identifier for DCE RPC objects. + +### term arguments + +- **name** (Required, String) + Term name. +- **protocol** (Required, String) + Match IP protocol type. +- **alg** (Optional, String) + Application Layer Gateway. +- **destination_port** (Optional, String) + Match TCP/UDP destination port. +- **icmp_code** (Optional, String) + Match ICMP message code. +- **icmp_type** (Optional, String) + Match ICMP message type. +- **icmp6_code** (Optional, String) + Match ICMP6 message code. +- **icmp6_type** (Optional, String) + Match ICMP6 message type. +- **inactivity_timeout** (Optional, Number) + Application-specific inactivity timeout (4..86400 seconds). + Conflict with `inactivity_timeout_never`. +- **inactivity_timeout_never** (Optional, Boolean) + Disables inactivity timeout. + Conflict with `inactivity_timeout`. +- **rpc_program_number** (Optional, String) + Match range of RPC program numbers. + Must be an integer or a range of integers. +- **source_port** (Optional, String) + Match TCP/UDP source port. - **uuid** (Optional, String) Match universal unique identifier for DCE RPC objects. diff --git a/website/docs/r/chassis_cluster.html.markdown b/website/docs/r/chassis_cluster.html.markdown index 7560e2ff..eedfbb29 100644 --- a/website/docs/r/chassis_cluster.html.markdown +++ b/website/docs/r/chassis_cluster.html.markdown @@ -85,6 +85,14 @@ The following arguments are supported: Number of redundant ethernet interfaces (1..128) - **config_sync_no_secondary_bootup_auto** (Optional, Boolean) Disable auto configuration synchronize on secondary bootup. +- **control_ports** (Optional, Set of Block) + For each combination of block arguments, + enable the specific control port to use as a control link for the chassis cluster. + Only available for some higher end Juniper SRX devices. + - **fpc** (Required, Number) + Flexible PIC Concentrator (FPC) slot number. + - **port** (Required, Number) + Port number on which to configure the control port. - **control_link_recovery** (Optional, Boolean) Enable automatic control link recovery. - **heartbeat_interval** (Optional, Number) diff --git a/website/docs/r/group_dual_system.html.markdown b/website/docs/r/group_dual_system.html.markdown index 3e5bd8f8..bdbdd283 100644 --- a/website/docs/r/group_dual_system.html.markdown +++ b/website/docs/r/group_dual_system.html.markdown @@ -65,8 +65,12 @@ The following arguments are supported: Hostname. - **backup_router_address** (Optional, String) IPv4 address backup router. - - **backup_router_destination** (Optional, List of String) - Destinations network reachable through the router. + - **backup_router_destination** (Optional, Set of String) + Destinations network reachable through the IPv4 router. + - **inet6_backup_router_address** (Optional, String) + IPv6 address backup router. + - **inet6_backup_router_destination** (Optional, Set of String) + Destinations network reachable through the IPv6 router. --- diff --git a/website/docs/r/interface.html.markdown b/website/docs/r/interface.html.markdown index 08257bb2..09f38a25 100644 --- a/website/docs/r/interface.html.markdown +++ b/website/docs/r/interface.html.markdown @@ -3,12 +3,12 @@ layout: "junos" page_title: "Junos: junos_interface" sidebar_current: "docs-junos-resource-interface" description: |- - Create/configure a interface or interface unit + Create/configure an interface or interface unit --- # junos_interface -Provides a interface resource. +Provides an interface resource. !> **NOTE:** Since v1.11.0, this resource is **deprecated**. For more consistency, functionalities of this resource have been splitted in two new resource `junos_interface_physical` and `junos_interface_logical`. diff --git a/website/docs/r/interface_logical.html.markdown b/website/docs/r/interface_logical.html.markdown index 168bd20c..9613ca88 100644 --- a/website/docs/r/interface_logical.html.markdown +++ b/website/docs/r/interface_logical.html.markdown @@ -42,7 +42,12 @@ The following arguments are supported: Enable family inet and add configurations if specified. - **address** (Optional, Block List) For each ip address to declare. + Conflict with `dhcp`. See [below for nested schema](#address-arguments-for-family_inet). + - **dhcp** (Optional, Block) + Enable DHCP client and configuration. + Conflict with `address`. + See [below for nested schema](#dhcp-arguments-for-family_inet). - **filter_input** (Optional, String) Filter to be applied to received packets. - **filter_output** (Optional, String) @@ -60,9 +65,14 @@ The following arguments are supported: Enable family inet6 and add configurations if specified. - **address** (Optional, Block List) For each ipv6 address to declare. + Conflict with `dhcpv6_client`. See [below for nested schema](#address-arguments-for-family_inet6). - **dad_disable** (Optional, Boolean) Disable duplicate-address-detection. + - **dhcpv6_client** (Optional, Block) + Enable DHCP client and configuration. + Conflict with `address`. + See [below for nested schema](#dhcpv6_client-arguments-for-family_inet6). - **filter_input** (Optional, String) Filter to be applied to received packets. - **filter_output** (Optional, String) @@ -160,6 +170,52 @@ The following arguments are supported: --- +### dhcp arguments for family_inet + +- **client_identifier_ascii** (Optional, String) + Client identifier as an ASCII string. + Conflict witch `client_identifier_hexadecimal`. +- **client_identifier_hexadecimal** (Optional, String) + Client identifier as a hexadecimal string. + Conflict witch `client_identifier_ascii`. +- **client_identifier_prefix_hostname** (Optional, Boolean) + Add prefix router host name to client-id option. +- **client_identifier_prefix_routing_instance_name** (Optional, Boolean) + Add prefix routing instance name to client-id option. +- **client_identifier_use_interface_description** (Optional, Boolean) + Use the interface description. + Need to be `device` or `logical`. +- **client_identifier_userid_ascii** (Optional, String) + Add user id as an ASCII string to client-id option. +- **client_identifier_userid_hexadecimal** (Optional, String) + Add user id as a hexadecimal string to client-id option. +- **force_discover** (Optional, Boolean) + Send DHCPDISCOVER after DHCPREQUEST retransmission failure. +- **lease_time** (Optional, Number) + Lease time in seconds requested in DHCP client protocol packet (60..2147483647 seconds). + Conflict witch `lease_time_infinite`. +- **lease_time_infinite** (Optional, Boolean) + Lease never expires. + Conflict witch `lease_time`. +- **metric** (Optional, Number) + Client initiated default-route metric (0..255). +- **no_dns_install** (Optional, Boolean) + Do not install DNS information learned from DHCP server. +- **options_no_hostname** (Optional, Boolean) + Do not carry hostname (RFC option code is 12) in packet. +- **retransmission_attempt** (Optional, Number) + Number of attempts to retransmit the DHCP client protocol packet (0..50000). +- **retransmission_interval** (Optional, Number) + Number of seconds between successive retransmission (4..64 seconds). +- **server_address** (Optional, String) + DHCP Server-address. +- **update_server** (Optional, Boolean) + Propagate TCP/IP settings to DHCP server. +- **vendor_id** (Optional, String) + Vendor class id for the DHCP Client. + +--- + ### address arguments for family_inet6 - **cidr_ip** (Required, String) @@ -184,6 +240,39 @@ block but without `authentication_key`, `authentication_type` and with --- +### dhcpv6_client arguments for family_inet6 + +- **client_identifier_duid_type** (Required, String) + DUID identifying a client. + Need to be `duid-ll`, `duid-llt` or `vendor`. +- **client_type** (Required, String) + DHCPv6 client type. + Need to be `autoconfig` or `stateful`. +- **client_ia_type_na** (Optional, Boolean) + DHCPv6 client identity association type Non-temporary Address. + At least one of `client_ia_type_na`, `client_ia_type_pd` need to be true. +- **client_ia_type_pd** (Optional, Boolean) + DHCPv6 client identity association type Prefix Address. + At least one of `client_ia_type_na`, `client_ia_type_pd` need to be true. +- **no_dns_install** (Optional, Boolean) + Do not install DNS information learned from DHCP server. +- **prefix_delegating_preferred_prefix_length** (Optional, Number) + Client preferred prefix length (0..64). +- **prefix_delegating_sub_prefix_length** (Optional, Number) + The sub prefix length for LAN interfaces (1..127). +- **rapid_commit** (Optional, Boolean) + Option is used to signal the use of the two message exchange for address assignment. +- **req_option** (Optional, Set of String) + DHCPV6 client requested option configuration. +- **retransmission_attempt** (Optional, Number) + Number of attempts to retransmit the DHCPV6 client protocol packet (0..9). +- **update_router_advertisement_interface** (Optional, Set of String) + Interfaces on which to delegate prefix. +- **update_server** (Optional, Boolean) + Propagate TCP/IP settings to DHCP server. + +--- + ### rpf_check arguments - **fail_filter** (Optional, String) diff --git a/website/docs/r/interface_physical_disable.html.markdown b/website/docs/r/interface_physical_disable.html.markdown new file mode 100644 index 00000000..8eca0688 --- /dev/null +++ b/website/docs/r/interface_physical_disable.html.markdown @@ -0,0 +1,41 @@ +--- +layout: "junos" +page_title: "Junos: junos_interface_physical_disable" +sidebar_current: "docs-junos-resource-interface-physical-disable" +description: |- + Disable a not configured physical interface (same as when destroy junos_interface_physical resource) +--- + +# junos_interface_physical_disable + +Disable a not configured physical interface +(same as when destroy `junos_interface_physical` resource). +If the interface is configured or is used for a logical unit interface, the apply fails. + +This resource is useful for disable physical interfaces that have not already been used once +by the `junos_interface_physical` resource. + +Destroy this resource has no effect on the Junos configuration. + +## Example Usage + +```hcl +# Disable an interface +resource junos_interface_physical_disable "interface_demo" { + name = "ge-0/0/0" +} +``` + +## Argument Reference + +The following arguments are supported: + +- **name** (Required, String, Forces new resource) + Name of physical interface (without dot). + +## Attributes Reference + +The following attributes are exported: + +- **id** (String) + An identifier for the resource with format ``. diff --git a/website/docs/r/ospf_area.html.markdown b/website/docs/r/ospf_area.html.markdown index 57e26375..66e45692 100644 --- a/website/docs/r/ospf_area.html.markdown +++ b/website/docs/r/ospf_area.html.markdown @@ -3,17 +3,17 @@ layout: "junos" page_title: "Junos: junos_ospf_area" sidebar_current: "docs-junos-resource-ospf-area" description: |- - Create a ospf area + Create an ospf area --- # junos_ospf_area -Provides a ospf area resource. +Provides an ospf area resource. ## Example Usage ```hcl -# Add a ospf area +# Add an ospf area resource junos_ospf_area "demo_area" { area_id = "0.0.0.0" interface { diff --git a/website/docs/r/policyoptions_as_path.html.markdown b/website/docs/r/policyoptions_as_path.html.markdown index b919904c..ade3e4bc 100644 --- a/website/docs/r/policyoptions_as_path.html.markdown +++ b/website/docs/r/policyoptions_as_path.html.markdown @@ -3,17 +3,17 @@ layout: "junos" page_title: "Junos: junos_policyoptions_as_path" sidebar_current: "docs-junos-resource-policyoptions-as-path" description: |- - Create a as-path + Create an as-path --- # junos_policyoptions_as_path -Provides a as-path resource. +Provides an as-path resource. ## Example Usage ```hcl -# Add a as-path +# Add an as-path resource junos_policyoptions_as_path "github" { name = "github" path = ".* 36459" diff --git a/website/docs/r/policyoptions_as_path_group.html.markdown b/website/docs/r/policyoptions_as_path_group.html.markdown index 7803a7bf..72f56f88 100644 --- a/website/docs/r/policyoptions_as_path_group.html.markdown +++ b/website/docs/r/policyoptions_as_path_group.html.markdown @@ -3,17 +3,17 @@ layout: "junos" page_title: "Junos: junos_policyoptions_as_path_group" sidebar_current: "docs-junos-resource-policyoptions-as-path-group" description: |- - Create a as-path group + Create an as-path group --- # junos_policyoptions_as_path_group -Provides a as-path group resource. +Provides an as-path group resource. ## Example Usage ```hcl -# Add a as-path group +# Add an as-path group resource junos_policyoptions_as_path_group "via_century_link" { name = "viaCenturyLink" as_path { diff --git a/website/docs/r/security_idp_custom_attack.html.markdown b/website/docs/r/security_idp_custom_attack.html.markdown index e7a6fa4d..a59d9285 100644 --- a/website/docs/r/security_idp_custom_attack.html.markdown +++ b/website/docs/r/security_idp_custom_attack.html.markdown @@ -13,7 +13,7 @@ Provides a security idp custom-attack resource. ## Example Usage ```hcl -# Add a idp custom-attack +# Add an idp custom-attack resource junos_security_idp_custom_attack "demo_idp_custom_attack" { name = "SSH:BRUTE-FORCE-CUSTOM" recommended_action = "drop" diff --git a/website/docs/r/security_idp_custom_attack_group.html.markdown b/website/docs/r/security_idp_custom_attack_group.html.markdown index 195397b1..6347e990 100644 --- a/website/docs/r/security_idp_custom_attack_group.html.markdown +++ b/website/docs/r/security_idp_custom_attack_group.html.markdown @@ -13,7 +13,7 @@ Provides a security idp custom-attack-group resource. ## Example Usage ```hcl -# Add a idp custom-attack_group +# Add an idp custom-attack_group resource junos_security_idp_custom_attack_group "demo_idp_custom_attack_group" { name = "group_of_Attacks" member = ["custom_attack_1", "custom_attack_2"] diff --git a/website/docs/r/security_idp_policy.html.markdown b/website/docs/r/security_idp_policy.html.markdown index c85b9098..274f14f5 100644 --- a/website/docs/r/security_idp_policy.html.markdown +++ b/website/docs/r/security_idp_policy.html.markdown @@ -13,7 +13,7 @@ Provides a security idp policy resource. ## Example Usage ```hcl -# Add a idp policy +# Add an idp policy resource junos_security_idp_policy "demo_idp_policy" { name = "Idp-Policy" ips_rule { diff --git a/website/docs/r/security_ike_gateway.html.markdown b/website/docs/r/security_ike_gateway.html.markdown index 81b7a78e..5e490c2f 100644 --- a/website/docs/r/security_ike_gateway.html.markdown +++ b/website/docs/r/security_ike_gateway.html.markdown @@ -13,7 +13,7 @@ Provides a security ike gateway resource. ## Example Usage ```hcl -# Add a ike gateway +# Add an ike gateway resource junos_security_ike_gateway "demo_vpn_p1" { name = "first-vpn" address = ["192.0.2.1"] diff --git a/website/docs/r/security_ike_policy.html.markdown b/website/docs/r/security_ike_policy.html.markdown index e564b44c..074d5deb 100644 --- a/website/docs/r/security_ike_policy.html.markdown +++ b/website/docs/r/security_ike_policy.html.markdown @@ -13,7 +13,7 @@ Provides a security ike policy resource. ## Example Usage ```hcl -# Add a ike policy +# Add an ike policy resource junos_security_ike_policy "demo_vpn_policy" { name = "ike-policy" proposals = ["ike-proposal"] diff --git a/website/docs/r/security_ike_proposal.html.markdown b/website/docs/r/security_ike_proposal.html.markdown index 61b93222..358db4fc 100644 --- a/website/docs/r/security_ike_proposal.html.markdown +++ b/website/docs/r/security_ike_proposal.html.markdown @@ -13,7 +13,7 @@ Provides a security ike proposal resource. ## Example Usage ```hcl -# Add a ike proposal +# Add an ike proposal resource junos_security_ike_proposal "demo_vpn_proposal" { name = "ike-proposal" authentication_algorithm = "sha1" diff --git a/website/docs/r/security_ipsec_policy.html.markdown b/website/docs/r/security_ipsec_policy.html.markdown index 47985ec2..1bcd37f9 100644 --- a/website/docs/r/security_ipsec_policy.html.markdown +++ b/website/docs/r/security_ipsec_policy.html.markdown @@ -13,7 +13,7 @@ Provides a security ipsec policy resource. ## Example Usage ```hcl -# Add a ipsec policy +# Add an ipsec policy resource junos_security_ipsec_policy "demo_vpn_policy" { name = "ipsec-policy" proposals = ["ipsec-proposal"] diff --git a/website/docs/r/security_ipsec_proposal.html.markdown b/website/docs/r/security_ipsec_proposal.html.markdown index 7ea7cb3f..b59f59f4 100644 --- a/website/docs/r/security_ipsec_proposal.html.markdown +++ b/website/docs/r/security_ipsec_proposal.html.markdown @@ -13,7 +13,7 @@ Provides a security ipsec proposal resource. ## Example Usage ```hcl -# Add a ipsec proposal +# Add an ipsec proposal resource junos_security_ipsec_proposal "demo_vpn_proposal" { name = "ipsec-proposal" authentication_algorithm = "hmac-sha1-96" diff --git a/website/docs/r/system.html.markdown b/website/docs/r/system.html.markdown index b05d7efc..a28202d8 100644 --- a/website/docs/r/system.html.markdown +++ b/website/docs/r/system.html.markdown @@ -118,6 +118,28 @@ The following arguments are supported: - **threshold_value** (Optional, Number) Set the maximum threshold(sec) allowed for NTP adjustment (1..600). `threshold_action` needs to be set. +- **ports** (Optional, Block) + Declare `ports` configuration. + - **auxiliary_authentication_order** (Optional, List of String) + Order in which authentication methods are invoked on auxiliary port. + - **auxiliary_disable** (Optional, Boolean) + Disable console on auxiliary port. + - **auxiliary_insecure** (Optional, Boolean) + Disallow superuser access on auxiliary port. + - **auxiliary_logout_on_disconnect** (Optional, Boolean) + Log out the console session when cable is unplugged. + - **auxiliary_type** (Optional, String) + Terminal type on auxiliary port. + - **console_authentication_order** (Optional, List of String) + Order in which authentication methods are invoked on console port. + - **console_disable** (Optional, Boolean) + Disable console on console port. + - **console_insecure** (Optional, Boolean) + Disallow superuser access on console port. + - **console_logout_on_disconnect** (Optional, Boolean) + Log out the console session when cable is unplugged. + - **console_type** (Optional, String) + Terminal type on console port. - **radius_options_attributes_nas_ipaddress** (Optional, String) Value of NAS-IP-Address in outgoing RADIUS packets. - **radius_options_enhanced_accounting** (Optional, Boolean) diff --git a/website/docs/r/system_services_dhcp_localserver_group.html.markdown b/website/docs/r/system_services_dhcp_localserver_group.html.markdown new file mode 100644 index 00000000..03229241 --- /dev/null +++ b/website/docs/r/system_services_dhcp_localserver_group.html.markdown @@ -0,0 +1,352 @@ +--- +layout: "junos" +page_title: "Junos: junos_system_services_dhcp_localserver_group" +sidebar_current: "docs-junos-resource-system-services-dhcp-localserver-group" +description: |- + Create a DHCP (or DHCPv6) local server group +--- + +# junos_system_services_dhcp_localserver_group + +Provides a DHCP (or DHCPv6) local server group. + +## Example Usage + +```hcl +# Add a DHCP local server group +resource junos_system_services_dhcp_localserver_group "demo_dhcp_group" { + name = "demo_dhcp_group" + interface { + name = "ge-0/0/3.1" + } +} +# Add a DHCPv6 local server group +resource junos_system_services_dhcp_localserver_group "demo_dhcp_group_v6" { + name = "demo_dhcp_group" + version = "v6" + interface { + name = "ge-0/0/3.1" + } +} +``` + +## Argument Reference + +-> **Note:** At least one of arguments need to be set +(in addition to `name`, `routing_instance` and `version`). + +The following arguments are supported: + +- **name** (Required, String, Forces new resource) + Group name. +- **routing_instance** (Optional, String, Forces new resource) + Routing instance for pool. + Need to be default or name of routing instance. + Defaults to `default` +- **version** (Optional, String, Forces new resource) + Version for DHCP or DHCPv6. + Need to be `v4` or `v6`. +- **access_profile** (Optional, String) + Access profile to use for AAA services. +- **authentication_password** (Optional, String) + DHCP authentication, username password to use. +- **authentication_username_include** (Optional, Block) + DHCP authentication, add username options. + At least one of arguments of block need to be set. + - **circuit_type** (Optional, Boolean) + Include circuit type. + - **client_id** (Optional, Boolean) + Include client ID. + `version` need to be `v6`. + - **delimiter** (Optional, String) + Change delimiter/separator character. + One character maximum. + - **domain_name** (Optional, String) + Add domain name. + - **interface_description** (Optional, String) + Include interface description. + Need to be `device` or `logical`. + - **interface_name** (Optional, Boolean) + Include interface name. + - **mac_address** (Optional, Boolean) + Include MAC address. + - **option_60** (Optional, Boolean) + Include option 60. + `version` need to be `v4`. + - **option_82** (Optional, Boolean) + Include option 82. + `version` need to be `v4`. + - **option_82_circuit_id** (Optional, Boolean) + Include option 82 circuit-id (sub option 1). + `option_82` need to be true. + - **option_82_remote_id** (Optional, Boolean) + Include option 82 remote-id (sub option 2). + `option_82` need to be true. + - **relay_agent_interface_id** (Optional, Boolean) + Include the relay agent interface ID. + `version` need to be `v6`. + - **relay_agent_remote_id** (Optional, Boolean) + Include the relay agent remote ID. + `version` need to be `v6`. + - **relay_agent_subscriber_id** (Optional, Boolean) + Include the relay agent subscriber ID. + `version` need to be `v6`. + - **routing_instance_name** (Optional, Boolean) + Include routing instance name. + - **user_prefix** (Optional, String) + Add user defined prefix. + - **vlan_tags** (Optional, Boolean) + Include the vlan tag(s). +- **dynamic_profile** (Optional, String) + Dynamic profile to use. +- **dynamic_profile_use_primary** (Optional, String) + Dynamic profile to use on the primary interface. + `dynamic_profile` need to be set. + Conflict with `dynamic_profile_aggregate_clients`. +- **dynamic_profile_aggregate_clients** (Optional, Boolean) + Aggregate client profiles. +- **dynamic_profile_aggregate_clients_action** (Optional, String) + Merge or replace the client dynamic profiles. + Need to be `merge` or `replace`. + `dynamic_profile_aggregate_clients` need to be true. +- **interface** (Optional, Set of Block) + For each name of interface to declare. + See [below for nested schema](#interface-arguments). +- **lease_time_validation** (Optional, Block) + Configure lease time violation validation. + - **lease_time_threshold** (Optional, Number) + Threshold for lease time violation seconds (60..2147483647 seconds). + - **violation_action** (Optional, String) + Lease time validation violation action. + Need to be `override-lease` or `strict`. +- **liveness_detection_failure_action** (Optional, String) + Liveness detection failure action options. + Need to be `clear-binding`, `clear-binding-if-interface-up` or `log-only`. +- **liveness_detection_method_bfd** (Optional, Block) + Liveness detection method BFD options. + At least one of arguments of block need to be set. + Conflict with `liveness_detection_method_layer2`. + - **detection_time_threshold** (Optional, Number) + High detection-time triggering a trap (milliseconds). + - **holddown_interval** (Optional, Number) + Time to hold the session-UP notification to the client (0..255000 milliseconds). + - **minimum_interval** (Optional, Number) + Minimum transmit and receive interval (30000..255000 milliseconds). + - **minimum_receive_interval** (Optional, Number) + Minimum receive interval (30000..255000 milliseconds). + - **multiplier** (Optional, Number) + Detection time multiplier (1..255). + - **no_adaptation** (Optional, Boolean) + Disable adaptation. + - **session_mode** (Optional, String) + BFD single-hop or multihop session-mode. + Need to be `automatic`, `multihop` or `single-hop`. + - **transmit_interval_minimum** (Optional, Number) + Minimum transmit interval (30000..255000 milliseconds). + - **transmit_interval_threshold** (Optional, Number) + High transmit interval triggering a trap (milliseconds) + - **version** (Optional, String) + BFD protocol version number. + Need to be `0`, `1` or `automatic`. +- **liveness_detection_method_layer2** (Optional, Block) + Liveness detection method address resolution options. + At least one of arguments of block need to be set. + Conflict with `liveness_detection_method_bfd`. + - **max_consecutive_retries** (Optional, Number) + Retry attempts (3..6). + - **transmit_interval** (Optional, Number) + Transmit interval for address resolution (300..1800 seconds). +- **overrides_v4** (Optional, Block) + DHCP override processing. + See [below for nested schema](#overrides_v4-arguments). +- **overrides_v6** (Optional, Block) + DHCPv6 override processing. + See [below for nested schema](#overrides_v6-arguments). +- **reauthenticate_lease_renewal** (Optional, Boolean) + Reauthenticate on each renew, rebind, DISCOVER or SOLICIT. +- **reauthenticate_remote_id_mismatch** (Optional, Boolean) + Reauthenticate on remote-id mismatch for renew, rebind and re-negotiation. +- **reconfigure** (Optional, Block) + DHCP reconfigure processing. + - **attempts** (Optional, Number) + Number of reconfigure attempts before aborting (1..10). + - **clear_on_abort** (Optional, Boolean) + Delete client on reconfiguration abort. + - **support_option_pd_exclude** (Optional, Boolean) + Request prefix exclude option in reconfigure message + - **timeout** (Optional, Number) + Initial timeout value for retry (1..10). + - **token** (Optional, String) + Reconfigure token. + - **trigger_radius_disconnect** (Optional, Boolean) + Trigger DHCP reconfigure by radius initiated disconnect. +- **remote_id_mismatch_disconnect** (Optional, Boolean) + Disconnect session on remote-id mismatch. +- **route_suppression_access** (Optional, Boolean) + Suppress access route addition. +- **route_suppression_access_internal** (Optional, Boolean) + Suppress access-internal route addition. +- **route_suppression_destination** (Optional, Boolean) + Suppress destination route addition. +- **service_profile** (Optional, String) + Dynamic profile to use for default service activation. +- **short_cycle_protection_lockout_max_time** (Optional, Number) + Short cycle lockout max time in seconds (1..86400). + `short_cycle_protection_lockout_min_time` need to be set. +- **short_cycle_protection_lockout_min_time** (Optional, Number) + Short cycle lockout min time in seconds (1..86400). + `short_cycle_protection_lockout_max_time` need to be set. + +--- + +### interface arguments + +- **name** (Required, String) + Interface name. + Need to be a logical interface. +- **access_profile** (Optional, String) + Access profile to use for AAA services. +- **dynamic_profile** (Optional, String) + Dynamic profile to use. +- **dynamic_profile_use_primary** (Optional, String) + Dynamic profile to use on the primary interface. + `dynamic_profile` need to be set. + Conflict with `dynamic_profile_aggregate_clients`. +- **dynamic_profile_aggregate_clients** (Optional, Boolean) + Aggregate client profiles. +- **dynamic_profile_aggregate_clients_action** (Optional, String) + Merge or replace the client dynamic profiles. + Need to be `merge` or `replace`. + `dynamic_profile_aggregate_clients` need to be true. +- **exclude** (Optional, Boolean) + Exclude this interface range. +- **overrides_v4** (Optional, Block) + DHCP override processing. + See [below for nested schema](#overrides_v4-arguments). +- **overrides_v6** (Optional, Block) + DHCPv6 override processing. + See [below for nested schema](#overrides_v6-arguments). +- **service_profile** (Optional, String) + Dynamic profile to use for default service activation. +- **short_cycle_protection_lockout_max_time** (Optional, Number) + Short cycle lockout max time in seconds (1..86400). + `short_cycle_protection_lockout_min_time` need to be set. +- **short_cycle_protection_lockout_min_time** (Optional, Number) + Short cycle lockout min time in seconds (1..86400). + `short_cycle_protection_lockout_max_time` need to be set. +- **trace** (Optional, Boolean) + Enable tracing for this interface. +- **upto** (Optional, String) + Interface up to. + Need to be a logical interface. + +--- + +### overrides_v4 arguments + +- **allow_no_end_option** (Optional, Boolean) + Allow packets without end-of-option. +- **asymmetric_lease_time** (Optional, Number) + Use a reduced lease time for the client. In seconds (600..86400 seconds). +- **bootp_support** (Optional, Boolean) + Allow processing of bootp requests. +- **client_discover_match** (Optional, String) + Use incoming interface or option 60 and option 82 match criteria for DISCOVER PDU. + Need to be `incoming-interface` or `option60-and-option82`. +- **delay_offer_based_on** (Optional, Set of Block) + For each combination of block arguments, filter options for dhcp-server. + - **option** (Required, String) + Option. + Need to be `option-60`, `option-77` or `option-82`. + - **compare** (Required, String) + How to compare. + Need to be `equals`, `not-equals` or `starts-with`. + - **value_type** (Required, String) + Type of string. + Need to be `ascii` or `hexadecimal`. + - **value** (Required, String) + String to compare. +- **delay_offer_delay_time** (Optional, Number) + Time delay between discover and offer (1..30 seconds). + `delay_offer_based_on` need to be set at least one time. +- **delete_binding_on_renegotiation** (Optional, Boolean) + Delete binding on renegotiation. +- **dual_stack** (Optional, String) + Dual stack group to use. +- **include_option_82_forcerenew** (Optional, Boolean) + Include option-82 in FORCERENEW. +- **include_option_82_nak** (Optional, Boolean) + Include option-82 in NAK. +- **interface_client_limit** (Optional, Number) + Limit the number of clients allowed on an interface (1..500000). +- **process_inform** (Optional, Boolean) + Process INFORM PDUs. +- **process_inform_pool** (Optional, String) + Pool name for family inet. +- **protocol_attributes** (Optional, String) + DHCPv4 attributes to use as defined under access protocol-attributes. + +### overrides_v6 arguments + +- **always_add_option_dns_server** (Optional, Boolean) + Add option-23, DNS recursive name server in Advertise and Reply. +- **always_process_option_request_option** (Optional, Boolean) + Always process option even after address allocation failure. +- **asymmetric_lease_time** (Optional, Number) + Use a reduced lease time for the client. In seconds (600..86400 seconds). +- **asymmetric_prefix_lease_time** (Optional, Number) + Use a reduced prefix lease time for the client. In seconds (600..86400 seconds). +- **client_negotiation_match_incoming_interface** (Optional, Boolean) + Use incoming interface match criteria for SOLICIT PDU +- **delay_advertise_based_on** (Optional, Set of Block) + For each combination of block arguments, filter options for dhcp-server. + - **option** (Required, String) + Option. + Need to be `option-60`, `option-77` or `option-82`. + - **compare** (Required, String) + How to compare. + Need to be `equals`, `not-equals` or `starts-with`. + - **value_type** (Required, String) + Type of string. + Need to be `ascii` or `hexadecimal`. + - **value** (Required, String) + String to compare. +- **delay_advertise_delay_time** (Optional, Number) + Time delay between solicit and advertise (1..30 seconds). + `delay_advertise_based_on` need to be set at least one time. +- **delegated_pool** (Optional, String) + Delegated pool name for inet6. +- **delete_binding_on_renegotiation** (Optional, Boolean) + Delete binding on renegotiation. +- **dual_stack** (Optional, String) + Dual stack group to use. +- **interface_client_limit** (Optional, Number) + Limit the number of clients allowed on an interface (1..500000). +- **multi_address_embedded_option_response** (Optional, Boolean) + If the client requests multiple addresses place the options in each address. +- **process_inform** (Optional, Boolean) + Process INFORM PDUs. +- **process_inform_pool** (Optional, String) + Pool name for family inet. +- **protocol_attributes** (Optional, String) + DHCPv6 attributes to use as defined under access protocol-attributes. +- **rapid_commit** (Optional, Boolean) + Enable rapid commit processing. +- **top_level_status_code** (Optional, Boolean) + A top level status code option rather than encapsulated in IA for NoAddrsAvail in Advertise PDUs. + +## Attributes Reference + +The following attributes are exported: + +- **id** (String) + An identifier for the resource with format `_-__-_`. + +## Import + +Junos aggregate route can be imported using an id made up of +`_-__-_`, e.g. + +```shell +$ terraform import junos_system_services_dhcp_localserver_group.demo_dhcp_pool demo_dhcp_group_-_default_-_v4 +``` diff --git a/website/docs/r/vlan.html.markdown b/website/docs/r/vlan.html.markdown index 29d49aea..be5f8f87 100644 --- a/website/docs/r/vlan.html.markdown +++ b/website/docs/r/vlan.html.markdown @@ -52,7 +52,7 @@ The following arguments are supported: Conflict with `vlan_id_list`. - **vlan_id_list** (Optional, Set of String) List of vlan ID. - Can be a ID or range (exemple: 10-20). + Can be an ID or range (exemple: 10-20). Conflict with `vlan_id`. - **vxlan** (Optional, Block) Declare vxlan configuration (when Junos device supports it). diff --git a/website/junos.erb b/website/junos.erb index c9714c84..9f6ee379 100644 --- a/website/junos.erb +++ b/website/junos.erb @@ -18,6 +18,9 @@ > Resources