From ad19b26658b5d34e1eb014e937bde09a67fbe5ea Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Mon, 25 Oct 2021 14:45:38 +0200 Subject: [PATCH 01/24] r/system: add ports block argument Fixes #294 --- CHANGELOG.md | 2 + junos/resource_system.go | 139 ++++++++++++++++++++++++++++ junos/resource_system_test.go | 12 +++ website/docs/r/system.html.markdown | 22 +++++ 4 files changed, 175 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 113a7256..04cec276 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ENHANCEMENTS: +* resource/`junos_system`: add `ports` block argument (Fixes #294) + BUG FIXES: ## 1.21.1 (October 22, 2021) diff --git a/junos/resource_system.go b/junos/resource_system.go index 60452da1..b334760c 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, @@ -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_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/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) From 1367f5089a1387376b74c4aaef4ba66f4a7779d9 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Mon, 25 Oct 2021 16:13:56 +0200 Subject: [PATCH 02/24] r/application: add term block argument Fixes #296 --- CHANGELOG.md | 2 + junos/resource_application.go | 182 +++++++++++++++++++++++ junos/resource_application_test.go | 62 ++++++++ website/docs/r/application.html.markdown | 33 ++++ 4 files changed, 279 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 113a7256..e389cd08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ENHANCEMENTS: +* resource/`junos_application`: add `term` block argument (Fixes #296) + BUG FIXES: ## 1.21.1 (October 22, 2021) diff --git a/junos/resource_application.go b/junos/resource_application.go index 7a3ae2ad..3cb8bc13 100644 --- a/junos/resource_application.go +++ b/junos/resource_application.go @@ -10,6 +10,7 @@ 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 { @@ -23,6 +24,7 @@ type applicationOptions struct { rpcProgramNumber string sourcePort string uuid string + term []map[string]interface{} } func resourceApplication() *schema.Resource { @@ -78,6 +80,78 @@ func resourceApplication() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "term": { + Type: schema.TypeList, + Optional: true, + ConflictsWith: []string{ + "application_protocol", + "destination_port", + "inactivity_timeout", + "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), + }, + "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, @@ -295,6 +369,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 +387,46 @@ 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 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 @@ -342,6 +467,27 @@ 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, + "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 +497,39 @@ 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 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) @@ -387,6 +566,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..82993570 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 + } +} ` } @@ -71,5 +94,44 @@ resource "junos_application" "testacc_app2" { rpc_program_number = "0-0" 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" + 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/website/docs/r/application.html.markdown b/website/docs/r/application.html.markdown index 7341f647..a8449ecd 100644 --- a/website/docs/r/application.html.markdown +++ b/website/docs/r/application.html.markdown @@ -45,6 +45,39 @@ 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). +- **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. From 32c35a3b41ea4ec26da251ed37b946b30aab2c9c Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Tue, 26 Oct 2021 13:35:46 +0200 Subject: [PATCH 03/24] deps: bump github.com/jeremmfr/go-netconf to v0.4.1 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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= From fac83eed08d18b5538df938b289c5c45f018095a Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Wed, 27 Oct 2021 21:11:44 +0200 Subject: [PATCH 04/24] r/group_dual_system: add validation for `system.0.backup_router_address` and list of string for `system.0.backup_router_destination` is now unordered --- CHANGELOG.md | 2 ++ junos/resource_group_dual_system.go | 11 ++++++----- junos/resource_group_dual_system_test.go | 4 ++-- website/docs/r/group_dual_system.html.markdown | 4 ++-- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 113a7256..ef5c31aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ENHANCEMENTS: +* resource/`junos_group_dual_system`: add validation on `system.0.backup_router_address` and list of string for `system.0.backup_router_destination` is now unordered + BUG FIXES: ## 1.21.1 (October 22, 2021) diff --git a/junos/resource_group_dual_system.go b/junos/resource_group_dual_system.go index b565e49b..9a90f4cc 100644 --- a/junos/resource_group_dual_system.go +++ b/junos/resource_group_dual_system.go @@ -155,11 +155,12 @@ 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}, }, @@ -474,8 +475,8 @@ 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) } } diff --git a/junos/resource_group_dual_system_test.go b/junos/resource_group_dual_system_test.go index 62961e2a..2f3c566f 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"), ), }, { diff --git a/website/docs/r/group_dual_system.html.markdown b/website/docs/r/group_dual_system.html.markdown index 3e5bd8f8..fb2a9794 100644 --- a/website/docs/r/group_dual_system.html.markdown +++ b/website/docs/r/group_dual_system.html.markdown @@ -65,8 +65,8 @@ 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. --- From 006ee2c17c1308d054d690ae3b8fe4497002b726 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Wed, 27 Oct 2021 21:17:54 +0200 Subject: [PATCH 05/24] r/group_dual_system: add inet6_backup_router_address/destination arguments add `system.0.inet6_backup_router_address` and `system.0.inet6_backup_router_destination` arguments Fixes #302 --- CHANGELOG.md | 2 +- junos/resource_group_dual_system.go | 30 +++++++++++++++++-- junos/resource_group_dual_system_test.go | 10 ++++++- .../docs/r/group_dual_system.html.markdown | 4 +++ 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef5c31aa..b6f95750 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ENHANCEMENTS: -* resource/`junos_group_dual_system`: add validation on `system.0.backup_router_address` and list of string for `system.0.backup_router_destination` is now unordered +* 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) BUG FIXES: diff --git a/junos/resource_group_dual_system.go b/junos/resource_group_dual_system.go index 9a90f4cc..7fc880b5 100644 --- a/junos/resource_group_dual_system.go +++ b/junos/resource_group_dual_system.go @@ -164,6 +164,16 @@ func resourceGroupDualSystem() *schema.Resource { Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, + "inet6_backup_router_address": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.IsIPv6Address, + }, + "inet6_backup_router_destination": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, }, }, }, @@ -478,6 +488,12 @@ func setGroupDualSystem(d *schema.ResourceData, m interface{}, jnprSess *Netconf 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) + } } return sess.configSet(configSet, jnprSess) @@ -587,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 { @@ -601,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 2f3c566f..8e67726a 100644 --- a/junos/resource_group_dual_system_test.go +++ b/junos/resource_group_dual_system_test.go @@ -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/website/docs/r/group_dual_system.html.markdown b/website/docs/r/group_dual_system.html.markdown index fb2a9794..bdbdd283 100644 --- a/website/docs/r/group_dual_system.html.markdown +++ b/website/docs/r/group_dual_system.html.markdown @@ -67,6 +67,10 @@ The following arguments are supported: IPv4 address backup 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. --- From e47a1d1e6e9582f3d887bdb1180f259091826778 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Sat, 30 Oct 2021 15:58:54 +0200 Subject: [PATCH 06/24] r/interface_logical: add dhcp and dhcpv6_client block arguments inside family_inet and family_inet6 block arguments Fixes parts of #301 --- junos/resource_interface_logical.go | 460 +++++++++++++++++- junos/resource_interface_logical_test.go | 123 +++++ .../docs/r/interface_logical.html.markdown | 89 ++++ 3 files changed, 668 insertions(+), 4 deletions(-) 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/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) From 8ab88ab59a113b830983e9c1aaa1b84084fb31b3 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Sat, 30 Oct 2021 15:59:51 +0200 Subject: [PATCH 07/24] d/interface_logical: add dhcp and dhcpv6_client block attributes inside family_inet and family_inet6 block attributes --- junos/data_source_interface_logical.go | 140 +++++++++++++++++- .../docs/d/interface_logical.html.markdown | 76 ++++++++++ 2 files changed, 215 insertions(+), 1 deletion(-) 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/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) From 5141be0669e4a56cb37af438164c2b5e400afabc Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Wed, 3 Nov 2021 14:05:31 +0100 Subject: [PATCH 08/24] r/security_idp_custom_attack,services_rpm_probe: fix validation of IPv6 address * 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 --- CHANGELOG.md | 3 +++ junos/func_common.go | 16 ++++++++++++++++ junos/resource_security_idp_custom_attack.go | 6 +++--- junos/resource_services_rpm_probe.go | 10 +++++----- 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 475040f4..8061def4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ ENHANCEMENTS: 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/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/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, }, }, }, From 65ec7896fe9de6e20f8532839ce7d9f8a7dcafd9 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Fri, 5 Nov 2021 13:46:41 +0100 Subject: [PATCH 09/24] [docs] fix minor spelling errors --- website/docs/index.html.markdown | 2 +- website/docs/r/aggregate_route.html.markdown | 6 +++--- website/docs/r/application.html.markdown | 6 +++--- website/docs/r/interface.html.markdown | 4 ++-- website/docs/r/ospf_area.html.markdown | 6 +++--- website/docs/r/policyoptions_as_path.html.markdown | 6 +++--- website/docs/r/policyoptions_as_path_group.html.markdown | 6 +++--- website/docs/r/security_idp_custom_attack.html.markdown | 2 +- .../docs/r/security_idp_custom_attack_group.html.markdown | 2 +- website/docs/r/security_idp_policy.html.markdown | 2 +- website/docs/r/security_ike_gateway.html.markdown | 2 +- website/docs/r/security_ike_policy.html.markdown | 2 +- website/docs/r/security_ike_proposal.html.markdown | 2 +- website/docs/r/security_ipsec_policy.html.markdown | 2 +- website/docs/r/security_ipsec_proposal.html.markdown | 2 +- website/docs/r/vlan.html.markdown | 2 +- 16 files changed, 27 insertions(+), 27 deletions(-) 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/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 a8449ecd..f6bac46f 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" 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/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/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). From fac9619c04deeaec4512d16983b79a3d525128b8 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Fri, 5 Nov 2021 13:50:01 +0100 Subject: [PATCH 10/24] upgrade golang linter & exclude errors from varnamelen --- .github/workflows/linters.yml | 2 +- .golangci.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) 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 From c40dcef6c8836fbed0db68649bf56440ddcdbbc4 Mon Sep 17 00:00:00 2001 From: Jeremy Muriel Date: Mon, 8 Nov 2021 09:17:50 +0100 Subject: [PATCH 11/24] add access_address_assignment_pool resource Fixes parts of #301 --- junos/provider.go | 1 + ...resource_access_address_assignment_pool.go | 1359 +++++++++++++++++ ...rce_access_address_assignment_pool_test.go | 210 +++ ...cess_address_assignment_pool.html.markdown | 244 +++ website/junos.erb | 3 + 5 files changed, 1817 insertions(+) create mode 100644 junos/resource_access_address_assignment_pool.go create mode 100644 junos/resource_access_address_assignment_pool_test.go create mode 100644 website/docs/r/access_address_assignment_pool.html.markdown diff --git a/junos/provider.go b/junos/provider.go index 79e84df3..a4fa6ead 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(), 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/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/junos.erb b/website/junos.erb index c9714c84..be8fd0c7 100644 --- a/website/junos.erb +++ b/website/junos.erb @@ -18,6 +18,9 @@ > Resources