diff --git a/CHANGELOG.md b/CHANGELOG.md index b3749010..2594edf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ENHANCEMENTS: +* resource/`junos_ospf_area`: add multiple new arguments to `interface` block (Fixes #348, #356) + BUG FIXES: ## 1.24.1 (February 11, 2022) diff --git a/docs/resources/ospf_area.md b/docs/resources/ospf_area.md index fb09c2a5..164213e3 100644 --- a/docs/resources/ospf_area.md +++ b/docs/resources/ospf_area.md @@ -20,6 +20,8 @@ resource junos_ospf_area "demo_area" { ## Argument Reference +-> **Note** Some arguments are not compatible with version `v3` of ospf. + The following arguments are supported: - **area_id** (Required, String, Forces new resource) @@ -36,18 +38,147 @@ The following arguments are supported: For each interface or interface-range to declare. - **name** (Required, String) Name of interface or interface-range. + - **authentication_simple_password** (Optional, String, Sensitive) + Authentication key. + Conflict with `authentication_md5`. + - **authentication_md5** (Optional, Block List) + For each key_id, MD5 authentication key. + See [below for nested schema](#authentication_md5-arguments). + Conflict with `authentication_simple_password`. + - **bandwidth_based_metrics** (Optional, Block Set) + For each bandwidth, configure bandwidth based metrics. + See [below for nested schema](#bandwidth_based_metrics-arguments). + - **bfd_liveness_detection** (Optional, Block) + Bidirectional Forwarding Detection options. + See [below for nested schema](#bfd_liveness_detection-arguments). - **dead_interval** (Optional, Number) Dead interval (seconds). + - **demand_circuit** (Optional, Boolean) + Interface functions as a demand circuit. - **disable** (Optional, Boolean) Disable OSPF on this interface. + - **dynamic_neighbors** (Optional, Boolean) + Learn neighbors dynamically on a p2mp interface. + - **flood_reduction** (Optional, Boolean) + Enable flood reduction. - **hello_interval** (Optional, Number) Hello interval (seconds). + - **interface_type** (Optional, String) + Type of interface. + - **ipsec_sa** (Optional, String) + IPSec security association name. + - **ipv4_adjacency_segment_protected_type** (Optional, String) + Type to define adjacency SID is eligible for protection. + Need to be `dynamic`, `index` or `label`. + - **ipv4_adjacency_segment_protected_value** (Optional, String) + Value for index or label to define adjacency SID is eligible for protection. + - **ipv4_adjacency_segment_unprotected_type** (Optional, String) + Type to define adjacency SID uneligible for protection. + Need to be `dynamic`, `index` or `label`. + - **ipv4_adjacency_segment_unprotected_value** (Optional, String) + Value for index or label to define adjacency SID uneligible for protection. + - **link_protection** (Optional, Boolean) + Protect interface from link faults only. - **metric** (Optional, Number) Interface metric. + - **mtu** (Optional, Number) + Maximum OSPF packet size (128..65535). + - **neighbor** (Optional, Block Set) + For each address, configure NBMA neighbor. + See [below for nested schema](#neighbor-arguments). + - **no_advertise_adjacency_segment** (Optional, Boolean) + Do not advertise an adjacency segment for this interface. + - **no_eligible_backup** (Optional, Boolean) + Not eligible to backup traffic from protected interfaces. + - **no_eligible_remote_backup** (Optional, Boolean) + Not eligible for Remote-LFA backup traffic from protected interfaces. + - **no_interface_state_traps** (Optional, Boolean) + Do not send interface state change traps. + - **no_neighbor_down_notification** (Optional, Boolean) + Don't inform other protocols about neighbor down events. + - **node_link_protection** (Optional, Boolean) + Protect interface from both link and node faults. - **passive** (Optional, Boolean) Do not run OSPF, but advertise it. + - **passive_traffic_engineering_remote_node_id** (Optional, String) + Advertise TE link information, remote address of the link. + - **passive_traffic_engineering_remote_node_router_id** (Optional, String) + Advertise TE link information, TE Router-ID of the remote node. + - **poll_interval** (Optional, Number) + Poll interval for NBMA interfaces (1..65535). + - **priority** (Optional, Number) + Designated router priority (0..255). - **retransmit_interval** (Optional, Number) Retransmission interval (seconds). + - **secondary** (Optional, Boolean) + Treat interface as secondary. + - **strict_bfd** (Optional, Boolean) + Enable strict bfd over this interface + - **te_metric** (Optional, Number) + Traffic engineering metric (1..4294967295). + - **transit_delay** (Optional, Number) + Transit delay (seconds) (1..65535). + +--- + +### authentication_md5 arguments + +- **key_id** (Required, Number) + Key ID for MD5 authentication (0..255). +- **key** (Required, String, Sensitive) + MD5 authentication key value. +- **start_time** (Optional, String) + Start time for key transmission (YYYY-MM-DD.HH:MM:SS). + +--- + +### bandwidth_based_metrics arguments + +- **bandwidth** (Required, String) + Bandwidth threshold. + Format need to be `(\d)+(m|k|g)?` +- **metric** (Required, Number) + Metric associated with specified bandwidth (1..65535). + +--- + +### bfd_liveness_detection arguments + +- **authentication_algorithm** (Optional, String) + Authentication algorithm name. +- **authentication_key_chain** (Optional, String) + Authentication key chain name. +- **authentication_loose_check** (Optional, Boolean) + Verify authentication only if authentication is negotiated. +- **detection_time_threshold** (Optional, Number) + High detection-time triggering a trap (milliseconds). +- **full_neighbors_only** (Optional, Boolean) + Setup BFD sessions only to Full neighbors. +- **holddown_interval** (Optional, Number) + Time to hold the session-UP notification to the client (0..255000 milliseconds). +- **minimum_interval** (Optional, Number) + Minimum transmit and receive interval (1..255000 milliseconds). +- **minimum_receive_interval** (Optional, Number) + Minimum receive interval (1..255000 milliseconds). +- **multiplier** (Optional, Number) + Detection time multiplier (1..255). +- **no_adaptation** (Optional, Boolean) + Disable adaptation. +- **transmit_interval_minimum_interval** (Optional, Number) + Minimum transmit interval (1..255000 milliseconds). +- **transmit_interval_threshold** (Optional, Number) + High transmit interval triggering a trap (milliseconds). +- **version** (Optional, String) + BFD protocol version number. + +--- + +### neighbor arguments + +- **address** (Required, String) + Address of neighbor. +- **eligible** (Optional, Boolean) + Eligible to be DR on an NBMA network. ## Attributes Reference diff --git a/junos/constants.go b/junos/constants.go index 227c2502..ac1f7a5e 100644 --- a/junos/constants.go +++ b/junos/constants.go @@ -1,37 +1,39 @@ package junos const ( - idSeparator = "_-_" - defaultWord = "default" - evpnWord = "evpn" - inetWord = "inet" - inet6Word = "inet6" - mplsWord = "mpls" - emptyWord = "empty" - matchWord = "match" - permitWord = "permit" - thenWord = "then" - prefixWord = "prefix" - prefixNameWord = "prefix-name" - actionNoneWord = "none" - addWord = "add" - deleteWord = "delete" - setWord = "set" - setLineStart = setWord + " " - st0Word = "st0" - ospfV2 = "ospf" - ospfV3 = "ospf3" - activeW = "active" - asPathAtomicAggregate = "as-path atomic-aggregate" - passiveW = "passive" - discardW = "discard" - disableW = "disable" - dynamicDB = "dynamic-db" - preemptWord = "preempt" - flowControlWords = "flow-control" - noFlowControlWords = "no-flow-control" - loopbackWord = "loopback" - noLoopbackWord = "no-loopback" - actionCos = "class-of-service" - actionMarkDiffServ = "mark-diffserv" + idSeparator = "_-_" + defaultWord = "default" + evpnWord = "evpn" + inetWord = "inet" + inet6Word = "inet6" + mplsWord = "mpls" + emptyWord = "empty" + matchWord = "match" + permitWord = "permit" + thenWord = "then" + prefixWord = "prefix" + prefixNameWord = "prefix-name" + actionNoneWord = "none" + addWord = "add" + deleteWord = "delete" + setWord = "set" + setLineStart = setWord + " " + st0Word = "st0" + ospfV2 = "ospf" + ospfV3 = "ospf3" + activeW = "active" + asPathAtomicAggregate = "as-path atomic-aggregate" + passiveW = "passive" + discardW = "discard" + disableW = "disable" + dynamicDB = "dynamic-db" + preemptWord = "preempt" + flowControlWords = "flow-control" + noFlowControlWords = "no-flow-control" + loopbackWord = "loopback" + noLoopbackWord = "no-loopback" + actionCos = "class-of-service" + actionMarkDiffServ = "mark-diffserv" + authenticationLooseCheck = "authentication loose-check" + noAdaptation = "no-adaptation" ) diff --git a/junos/func_resource_bgp.go b/junos/func_resource_bgp.go index e3493aa5..653cccb6 100644 --- a/junos/func_resource_bgp.go +++ b/junos/func_resource_bgp.go @@ -445,7 +445,7 @@ func setBgpOptsBfd(setPrefix string, bfdLivenessDetection []interface{}, configSet = append(configSet, setPrefixBfd+"authentication key-chain "+bfdLD["authentication_key_chain"].(string)) } if bfdLD["authentication_loose_check"].(bool) { - configSet = append(configSet, setPrefixBfd+"authentication loose-check") + configSet = append(configSet, setPrefixBfd+authenticationLooseCheck) } if bfdLD["detection_time_threshold"].(int) != 0 { configSet = append(configSet, setPrefixBfd+"detection-time threshold "+ @@ -500,7 +500,7 @@ func readBgpOptsBfd(item string, bfdRead map[string]interface{}) error { bfdRead["authentication_algorithm"] = strings.TrimPrefix(itemTrim, "authentication algorithm ") case strings.HasPrefix(itemTrim, "authentication key-chain "): bfdRead["authentication_key_chain"] = strings.TrimPrefix(itemTrim, "authentication key-chain ") - case itemTrim == "authentication loose-check": + case itemTrim == authenticationLooseCheck: bfdRead["authentication_loose_check"] = true case strings.HasPrefix(itemTrim, "detection-time threshold "): var err error diff --git a/junos/resource_interface_physical.go b/junos/resource_interface_physical.go index 7e4597c7..32acffe3 100644 --- a/junos/resource_interface_physical.go +++ b/junos/resource_interface_physical.go @@ -1426,7 +1426,7 @@ func readInterfacePhysicalParentEtherOpts(confRead *interfacePhysicalOptions, it case strings.HasPrefix(itemTrimBfdLiveDet, "authentication key-chain "): parentEtherOptsBFDLiveDetect["authentication_key_chain"] = strings.TrimPrefix( itemTrimBfdLiveDet, "authentication key-chain ") - case itemTrimBfdLiveDet == "authentication loose-check": + case itemTrimBfdLiveDet == authenticationLooseCheck: parentEtherOptsBFDLiveDetect["authentication_loose_check"] = true case strings.HasPrefix(itemTrimBfdLiveDet, "detection-time threshold "): var err error @@ -1464,7 +1464,7 @@ func readInterfacePhysicalParentEtherOpts(confRead *interfacePhysicalOptions, it } case strings.HasPrefix(itemTrimBfdLiveDet, "neighbor "): parentEtherOptsBFDLiveDetect["neighbor"] = strings.TrimPrefix(itemTrimBfdLiveDet, "neighbor ") - case itemTrimBfdLiveDet == "no-adaptation": + case itemTrimBfdLiveDet == noAdaptation: parentEtherOptsBFDLiveDetect["no_adaptation"] = true case strings.HasPrefix(itemTrimBfdLiveDet, "transmit-interval minimum-interval "): var err error diff --git a/junos/resource_ospf_area.go b/junos/resource_ospf_area.go index 0c81819d..56d20f0a 100644 --- a/junos/resource_ospf_area.go +++ b/junos/resource_ospf_area.go @@ -3,6 +3,7 @@ package junos import ( "context" "fmt" + "regexp" "strconv" "strings" @@ -10,6 +11,7 @@ import ( "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" + jdecode "github.com/jeremmfr/junosdecode" ) type ospfAreaOptions struct { @@ -57,34 +59,279 @@ func resourceOspfArea() *schema.Resource { Type: schema.TypeString, Required: true, }, + "authentication_simple_password": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + }, + "authentication_md5": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key_id": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(0, 255), + }, + "key": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + }, + "start_time": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringMatch( + regexp.MustCompile(`^\d{4}\-\d\d?\-\d\d?\.\d{2}:\d{2}:\d{2}$`), + "must be in the format 'YYYY-MM-DD.HH:MM:SS'"), + }, + }, + }, + }, + "bandwidth_based_metrics": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bandwidth": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringMatch(regexp.MustCompile(`^(\d)+(m|k|g)?$`), + `must be a bandwidth ^(\d)+(m|k|g)?$`), + }, + "metric": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 65535), + }, + }, + }, + }, + "bfd_liveness_detection": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "authentication_algorithm": { + Type: schema.TypeString, + Optional: true, + }, + "authentication_key_chain": { + Type: schema.TypeString, + Optional: true, + }, + "authentication_loose_check": { + Type: schema.TypeBool, + Optional: true, + }, + "detection_time_threshold": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 4294967295), + }, + "full_neighbors_only": { + Type: schema.TypeBool, + Optional: true, + }, + "holddown_interval": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 255000), + }, + "minimum_interval": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 255000), + }, + "minimum_receive_interval": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 255000), + }, + "multiplier": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 255), + }, + "no_adaptation": { + Type: schema.TypeBool, + Optional: true, + }, + "transmit_interval_minimum_interval": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 255000), + }, + "transmit_interval_threshold": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 4294967295), + }, + "version": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, "dead_interval": { Type: schema.TypeInt, Optional: true, ValidateFunc: validation.IntBetween(1, 65535), }, + "demand_circuit": { + Type: schema.TypeBool, + Optional: true, + }, "disable": { Type: schema.TypeBool, Optional: true, }, + "dynamic_neighbors": { + Type: schema.TypeBool, + Optional: true, + }, + "flood_reduction": { + Type: schema.TypeBool, + Optional: true, + }, "hello_interval": { Type: schema.TypeInt, Optional: true, ValidateFunc: validation.IntBetween(1, 255), }, + "interface_type": { + Type: schema.TypeString, + Optional: true, + }, + "ipsec_sa": { + Type: schema.TypeString, + Optional: true, + }, + "ipv4_adjacency_segment_protected_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"dynamic", "index", "label"}, false), + }, + "ipv4_adjacency_segment_protected_value": { + Type: schema.TypeString, + Optional: true, + }, + "ipv4_adjacency_segment_unprotected_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"dynamic", "index", "label"}, false), + }, + "ipv4_adjacency_segment_unprotected_value": { + Type: schema.TypeString, + Optional: true, + }, + "link_protection": { + Type: schema.TypeBool, + Optional: true, + }, "metric": { Type: schema.TypeInt, Optional: true, ValidateFunc: validation.IntBetween(1, 65535), }, + "mtu": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(128, 65535), + }, + "neighbor": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "address": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.IsIPAddress, + }, + "eligible": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + }, + }, + }, + "no_advertise_adjacency_segment": { + Type: schema.TypeBool, + Optional: true, + }, + "no_eligible_backup": { + Type: schema.TypeBool, + Optional: true, + }, + "no_eligible_remote_backup": { + Type: schema.TypeBool, + Optional: true, + }, + "no_interface_state_traps": { + Type: schema.TypeBool, + Optional: true, + }, + "no_neighbor_down_notification": { + Type: schema.TypeBool, + Optional: true, + }, + "node_link_protection": { + Type: schema.TypeBool, + Optional: true, + }, "passive": { Type: schema.TypeBool, Optional: true, }, + "passive_traffic_engineering_remote_node_id": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.IsIPAddress, + }, + "passive_traffic_engineering_remote_node_router_id": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.IsIPAddress, + }, + "poll_interval": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 65535), + }, + "priority": { + Type: schema.TypeInt, + Optional: true, + Default: -1, + ValidateFunc: validation.IntBetween(0, 255), + }, "retransmit_interval": { Type: schema.TypeInt, Optional: true, ValidateFunc: validation.IntBetween(1, 65535), }, + "secondary": { + Type: schema.TypeBool, + Optional: true, + }, + "strict_bfd": { + Type: schema.TypeBool, + Optional: true, + }, + "te_metric": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 4294967295), + }, + "transit_delay": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 65535), + }, }, }, }, @@ -337,27 +584,207 @@ func setOspfArea(d *schema.ResourceData, m interface{}, jnprSess *NetconfObject) interfaceNameList = append(interfaceNameList, ospfInterface["name"].(string)) setPrefixInterface := setPrefix + "interface " + ospfInterface["name"].(string) + " " configSet = append(configSet, setPrefixInterface) - if ospfInterface["dead_interval"].(int) != 0 { - configSet = append(configSet, setPrefixInterface+"dead-interval "+ - strconv.Itoa(ospfInterface["dead_interval"].(int))) + if v := ospfInterface["authentication_simple_password"].(string); v != "" { + if len(ospfInterface["authentication_md5"].([]interface{})) != 0 { + return fmt.Errorf("conflict between 'authentication_simple_password' and 'authentication_md5'"+ + " in interface '%s'", ospfInterface["name"].(string)) + } + configSet = append(configSet, setPrefixInterface+"authentication simple-password \""+v+"\"") + } + authenticationMD5List := make([]int, 0) + for _, mAuthMD5 := range ospfInterface["authentication_md5"].([]interface{}) { + authMD5 := mAuthMD5.(map[string]interface{}) + if bchk.IntInSlice(authMD5["key_id"].(int), authenticationMD5List) { + return fmt.Errorf("multiple blocks authentication_md5 with the same key_id %d in interface with name %s", + authMD5["key_id"].(int), ospfInterface["name"].(string)) + } + authenticationMD5List = append(authenticationMD5List, authMD5["key_id"].(int)) + configSet = append(configSet, setPrefixInterface+"authentication md5 "+ + strconv.Itoa(authMD5["key_id"].(int))+" key \""+authMD5["key"].(string)+"\"") + if v := authMD5["start_time"].(string); v != "" { + configSet = append(configSet, setPrefixInterface+"authentication md5 "+ + strconv.Itoa(authMD5["key_id"].(int))+" start-time "+v) + } + } + bandwidthBasedMetricsList := make([]string, 0) + for _, mBandBaseMet := range ospfInterface["bandwidth_based_metrics"].(*schema.Set).List() { + bandwidthBaseMetrics := mBandBaseMet.(map[string]interface{}) + if bchk.StringInSlice(bandwidthBaseMetrics["bandwidth"].(string), bandwidthBasedMetricsList) { + return fmt.Errorf("multiple blocks bandwidth_based_metrics with the same bandwidth %s in interface with name %s", + bandwidthBaseMetrics["bandwidth"].(string), ospfInterface["name"].(string)) + } + bandwidthBasedMetricsList = append(bandwidthBasedMetricsList, bandwidthBaseMetrics["bandwidth"].(string)) + configSet = append(configSet, setPrefixInterface+"bandwidth-based-metrics bandwidth "+ + bandwidthBaseMetrics["bandwidth"].(string)+" metric "+strconv.Itoa(bandwidthBaseMetrics["metric"].(int))) + } + for _, mBFDLivDet := range ospfInterface["bfd_liveness_detection"].([]interface{}) { + if mBFDLivDet == nil { + return fmt.Errorf("bfd_liveness_detection block is empty in interface %s", ospfInterface["name"].(string)) + } + setPrefixBfd := setPrefixInterface + "bfd-liveness-detection " + bfdLiveDetect := mBFDLivDet.(map[string]interface{}) + if v := bfdLiveDetect["authentication_algorithm"].(string); v != "" { + configSet = append(configSet, setPrefixBfd+"authentication algorithm "+v) + } + if v := bfdLiveDetect["authentication_key_chain"].(string); v != "" { + configSet = append(configSet, setPrefixBfd+"authentication key-chain \""+v+"\"") + } + if bfdLiveDetect["authentication_loose_check"].(bool) { + configSet = append(configSet, setPrefixBfd+"authentication loose-check") + } + if v := bfdLiveDetect["detection_time_threshold"].(int); v != 0 { + configSet = append(configSet, setPrefixBfd+"detection-time threshold "+strconv.Itoa(v)) + } + if bfdLiveDetect["full_neighbors_only"].(bool) { + configSet = append(configSet, setPrefixBfd+"full-neighbors-only") + } + if v := bfdLiveDetect["holddown_interval"].(int); v != 0 { + configSet = append(configSet, setPrefixBfd+"holddown-interval "+strconv.Itoa(v)) + } + if v := bfdLiveDetect["minimum_interval"].(int); v != 0 { + configSet = append(configSet, setPrefixBfd+"minimum-interval "+strconv.Itoa(v)) + } + if v := bfdLiveDetect["minimum_receive_interval"].(int); v != 0 { + configSet = append(configSet, setPrefixBfd+"minimum-receive-interval "+strconv.Itoa(v)) + } + if v := bfdLiveDetect["multiplier"].(int); v != 0 { + configSet = append(configSet, setPrefixBfd+"multiplier "+strconv.Itoa(v)) + } + if bfdLiveDetect["no_adaptation"].(bool) { + configSet = append(configSet, setPrefixBfd+noAdaptation) + } + if v := bfdLiveDetect["transmit_interval_minimum_interval"].(int); v != 0 { + configSet = append(configSet, setPrefixBfd+""+ + "transmit-interval minimum-interval "+strconv.Itoa(v)) + } + if v := bfdLiveDetect["transmit_interval_threshold"].(int); v != 0 { + configSet = append(configSet, setPrefixBfd+""+ + "transmit-interval threshold "+strconv.Itoa(v)) + } + if v := bfdLiveDetect["version"].(string); v != "" { + configSet = append(configSet, setPrefixBfd+"version "+v) + } + if len(configSet) == 0 || !strings.HasPrefix(configSet[len(configSet)-1], setPrefixBfd) { + return fmt.Errorf("bfd_liveness_detection block is empty in interface %s", ospfInterface["name"].(string)) + } + } + if v := ospfInterface["dead_interval"].(int); v != 0 { + configSet = append(configSet, setPrefixInterface+"dead-interval "+strconv.Itoa(v)) + } + if ospfInterface["demand_circuit"].(bool) { + configSet = append(configSet, setPrefixInterface+"demand-circuit") } if ospfInterface["disable"].(bool) { configSet = append(configSet, setPrefixInterface+"disable") } - if ospfInterface["hello_interval"].(int) != 0 { - configSet = append(configSet, setPrefixInterface+"hello-interval "+ - strconv.Itoa(ospfInterface["hello_interval"].(int))) + if ospfInterface["dynamic_neighbors"].(bool) { + configSet = append(configSet, setPrefixInterface+"dynamic-neighbors") + } + if ospfInterface["flood_reduction"].(bool) { + configSet = append(configSet, setPrefixInterface+"flood-reduction") + } + if v := ospfInterface["hello_interval"].(int); v != 0 { + configSet = append(configSet, setPrefixInterface+"hello-interval "+strconv.Itoa(v)) + } + if v := ospfInterface["interface_type"].(string); v != "" { + configSet = append(configSet, setPrefixInterface+"interface-type "+v) + } + if v := ospfInterface["ipsec_sa"].(string); v != "" { + configSet = append(configSet, setPrefixInterface+"ipsec-sa \""+v+"\"") + } + if t := ospfInterface["ipv4_adjacency_segment_protected_type"].(string); t != "" { + if v := ospfInterface["ipv4_adjacency_segment_protected_value"].(string); v != "" { + configSet = append(configSet, setPrefixInterface+"ipv4-adjacency-segment protected "+t+" "+v) + } else { + configSet = append(configSet, setPrefixInterface+"ipv4-adjacency-segment protected "+t) + } + } else if ospfInterface["ipv4_adjacency_segment_protected_value"].(string) != "" { + return fmt.Errorf("ipv4_adjacency_segment_protected_type need to be set with " + + "ipv4_adjacency_segment_protected_value") + } + if t := ospfInterface["ipv4_adjacency_segment_unprotected_type"].(string); t != "" { + if v := ospfInterface["ipv4_adjacency_segment_unprotected_value"].(string); v != "" { + configSet = append(configSet, setPrefixInterface+"ipv4-adjacency-segment unprotected "+t+" "+v) + } else { + configSet = append(configSet, setPrefixInterface+"ipv4-adjacency-segment unprotected "+t) + } + } else if ospfInterface["ipv4_adjacency_segment_unprotected_value"].(string) != "" { + return fmt.Errorf("ipv4_adjacency_segment_unprotected_type need to be set with " + + "ipv4_adjacency_segment_unprotected_value") + } + if ospfInterface["link_protection"].(bool) { + configSet = append(configSet, setPrefixInterface+"link-protection") + } + if v := ospfInterface["metric"].(int); v != 0 { + configSet = append(configSet, setPrefixInterface+"metric "+strconv.Itoa(v)) + } + if v := ospfInterface["mtu"].(int); v != 0 { + configSet = append(configSet, setPrefixInterface+"mtu "+strconv.Itoa(v)) + } + neighborList := make([]string, 0) + for _, mNeighbor := range ospfInterface["neighbor"].(*schema.Set).List() { + neighbor := mNeighbor.(map[string]interface{}) + if bchk.StringInSlice(neighbor["address"].(string), neighborList) { + return fmt.Errorf("multiple blocks neighbor with the same address %s in interface with name %s", + neighbor["address"].(string), ospfInterface["name"].(string)) + } + neighborList = append(neighborList, neighbor["address"].(string)) + configSet = append(configSet, setPrefixInterface+"neighbor "+neighbor["address"].(string)) + if neighbor["eligible"].(bool) { + configSet = append(configSet, setPrefixInterface+"neighbor "+neighbor["address"].(string)+" eligible") + } + } + if ospfInterface["no_advertise_adjacency_segment"].(bool) { + configSet = append(configSet, setPrefixInterface+"no-advertise-adjacency-segment") + } + if ospfInterface["no_eligible_backup"].(bool) { + configSet = append(configSet, setPrefixInterface+"no-eligible-backup") + } + if ospfInterface["no_eligible_remote_backup"].(bool) { + configSet = append(configSet, setPrefixInterface+"no-eligible-remote-backup") } - if ospfInterface["metric"].(int) != 0 { - configSet = append(configSet, setPrefixInterface+"metric "+ - strconv.Itoa(ospfInterface["metric"].(int))) + if ospfInterface["no_interface_state_traps"].(bool) { + configSet = append(configSet, setPrefixInterface+"no-interface-state-traps") + } + if ospfInterface["no_neighbor_down_notification"].(bool) { + configSet = append(configSet, setPrefixInterface+"no-neighbor-down-notification") + } + if ospfInterface["node_link_protection"].(bool) { + configSet = append(configSet, setPrefixInterface+"node-link-protection") } if ospfInterface["passive"].(bool) { configSet = append(configSet, setPrefixInterface+"passive") + if v := ospfInterface["passive_traffic_engineering_remote_node_id"].(string); v != "" { + configSet = append(configSet, setPrefixInterface+"passive traffic-engineering remote-node-id "+v) + } + if v := ospfInterface["passive_traffic_engineering_remote_node_router_id"].(string); v != "" { + configSet = append(configSet, setPrefixInterface+"passive traffic-engineering remote-node-router-id "+v) + } + } else if ospfInterface["passive_traffic_engineering_remote_node_id"].(string) != "" || + ospfInterface["passive_traffic_engineering_remote_node_router_id"].(string) != "" { + return fmt.Errorf("passive need to be true with " + + "passive_traffic_engineering_remote_node_id and passive_traffic_engineering_remote_node_router_id") + } + if v := ospfInterface["poll_interval"].(int); v != 0 { + configSet = append(configSet, setPrefixInterface+"poll-interval "+strconv.Itoa(v)) + } + if v := ospfInterface["priority"].(int); v != -1 { + configSet = append(configSet, setPrefixInterface+"priority "+strconv.Itoa(v)) + } + if v := ospfInterface["retransmit_interval"].(int); v != 0 { + configSet = append(configSet, setPrefixInterface+"retransmit-interval "+strconv.Itoa(v)) + } + if ospfInterface["secondary"].(bool) { + configSet = append(configSet, setPrefixInterface+"secondary") + } + if ospfInterface["strict_bfd"].(bool) { + configSet = append(configSet, setPrefixInterface+"strict-bfd") } - if ospfInterface["retransmit_interval"].(int) != 0 { - configSet = append(configSet, setPrefixInterface+"retransmit-interval "+ - strconv.Itoa(ospfInterface["retransmit_interval"].(int))) + if v := ospfInterface["te_metric"].(int); v != 0 { + configSet = append(configSet, setPrefixInterface+"te-metric "+strconv.Itoa(v)) + } + if v := ospfInterface["transit_delay"].(int); v != 0 { + configSet = append(configSet, setPrefixInterface+"transit-delay "+strconv.Itoa(v)) } } @@ -403,45 +830,244 @@ func readOspfArea(idArea, version, routingInstance string, if strings.HasPrefix(itemTrim, "interface ") { itemInterfaceList := strings.Split(strings.TrimPrefix(itemTrim, "interface "), " ") interfaceOptions := map[string]interface{}{ - "name": itemInterfaceList[0], - "dead_interval": 0, - "disable": false, - "hello_interval": 0, - "metric": 0, - "passive": false, + "name": itemInterfaceList[0], + "authentication_simple_password": "", + "authentication_md5": make([]map[string]interface{}, 0), + "bandwidth_based_metrics": make([]map[string]interface{}, 0), + "bfd_liveness_detection": make([]map[string]interface{}, 0), + "dead_interval": 0, + "demand_circuit": false, + "disable": false, + "dynamic_neighbors": false, + "flood_reduction": false, + "hello_interval": 0, + "interface_type": "", + "ipsec_sa": "", + "ipv4_adjacency_segment_protected_type": "", + "ipv4_adjacency_segment_protected_value": "", + "ipv4_adjacency_segment_unprotected_type": "", + "ipv4_adjacency_segment_unprotected_value": "", + "link_protection": false, + "metric": 0, + "mtu": 0, + "neighbor": make([]map[string]interface{}, 0), + "no_advertise_adjacency_segment": false, + "no_eligible_backup": false, + "no_eligible_remote_backup": false, + "no_interface_state_traps": false, + "no_neighbor_down_notification": false, + "node_link_protection": false, + "passive": false, + "passive_traffic_engineering_remote_node_id": "", + "passive_traffic_engineering_remote_node_router_id": "", + "poll_interval": 0, + "priority": -1, "retransmit_interval": 0, + "secondary": false, + "strict_bfd": false, + "te_metric": 0, + "transit_delay": 0, } itemTrimInterface := strings.TrimPrefix(itemTrim, "interface "+itemInterfaceList[0]+" ") confRead.interFace = copyAndRemoveItemMapList("name", interfaceOptions, confRead.interFace) switch { + case strings.HasPrefix(itemTrimInterface, "authentication simple-password "): + var err error + interfaceOptions["authentication_simple_password"], err = jdecode.Decode(strings.Trim(strings.TrimPrefix( + itemTrimInterface, "authentication simple-password "), "\"")) + if err != nil { + return confRead, fmt.Errorf("failed to decode authentication simple-password : %w", err) + } + case strings.HasPrefix(itemTrimInterface, "authentication md5 "): + itemTrimInterfaceSplit := strings.Split(strings.TrimPrefix(itemTrimInterface, "authentication md5 "), " ") + keyID, err := strconv.Atoi(itemTrimInterfaceSplit[0]) + if err != nil { + return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrimInterfaceSplit[0], err) + } + authMD5 := map[string]interface{}{ + "key_id": keyID, + "key": "", + "start_time": "", + } + interfaceOptions["authentication_md5"] = copyAndRemoveItemMapList("key_id", authMD5, + interfaceOptions["authentication_md5"].([]map[string]interface{})) + itemTrimAuthMD5 := strings.TrimPrefix(itemTrimInterface, "authentication md5 "+itemTrimInterfaceSplit[0]+" ") + switch { + case strings.HasPrefix(itemTrimAuthMD5, "key "): + var err error + authMD5["key"], err = jdecode.Decode(strings.Trim(strings.TrimPrefix( + itemTrimAuthMD5, "key "), "\"")) + if err != nil { + return confRead, fmt.Errorf("failed to decode authentication md5 key : %w", err) + } + case strings.HasPrefix(itemTrimAuthMD5, "start-time "): + authMD5["start_time"] = strings.Split(strings.Trim(strings.TrimPrefix( + itemTrimAuthMD5, "start-time "), "\""), " ")[0] + } + interfaceOptions["authentication_md5"] = append( + interfaceOptions["authentication_md5"].([]map[string]interface{}), authMD5) + case strings.HasPrefix(itemTrimInterface, "bandwidth-based-metrics bandwidth "): + itemTrimInterfaceSplit := strings.Split(strings.TrimPrefix( + itemTrimInterface, "bandwidth-based-metrics bandwidth "), " ") + if len(itemTrimInterfaceSplit) < 3 { + return confRead, fmt.Errorf("can't read values for bandwidth_based_metrics in %s", itemTrimInterface) + } + metric, err := strconv.Atoi(itemTrimInterfaceSplit[2]) + if err != nil { + return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrimInterfaceSplit[2], err) + } + interfaceOptions["bandwidth_based_metrics"] = append( + interfaceOptions["bandwidth_based_metrics"].([]map[string]interface{}), map[string]interface{}{ + "bandwidth": itemTrimInterfaceSplit[0], + "metric": metric, + }) + case strings.HasPrefix(itemTrimInterface, "bfd-liveness-detection "): + if len(interfaceOptions["bfd_liveness_detection"].([]map[string]interface{})) == 0 { + interfaceOptions["bfd_liveness_detection"] = append( + interfaceOptions["bfd_liveness_detection"].([]map[string]interface{}), map[string]interface{}{ + "authentication_algorithm": "", + "authentication_key_chain": "", + "authentication_loose_check": false, + "detection_time_threshold": 0, + "full_neighbors_only": false, + "holddown_interval": 0, + "minimum_interval": 0, + "minimum_receive_interval": 0, + "multiplier": 0, + "no_adaptation": false, + "transmit_interval_minimum_interval": 0, + "transmit_interval_threshold": 0, + "version": "", + }) + } + if err := readOspfAreaInterfaceBfd(strings.TrimPrefix(itemTrimInterface, "bfd-liveness-detection "), + interfaceOptions["bfd_liveness_detection"].([]map[string]interface{})[0]); err != nil { + return confRead, err + } case strings.HasPrefix(itemTrimInterface, "dead-interval "): interfaceOptions["dead_interval"], err = strconv.Atoi( strings.TrimPrefix(itemTrimInterface, "dead-interval ")) if err != nil { return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrimInterface, err) } + case itemTrimInterface == "demand-circuit": + interfaceOptions["demand_circuit"] = true case itemTrimInterface == disableW: interfaceOptions["disable"] = true + case itemTrimInterface == "dynamic-neighbors": + interfaceOptions["dynamic_neighbors"] = true + case itemTrimInterface == "flood-reduction": + interfaceOptions["flood_reduction"] = true case strings.HasPrefix(itemTrimInterface, "hello-interval "): interfaceOptions["hello_interval"], err = strconv.Atoi( strings.TrimPrefix(itemTrimInterface, "hello-interval ")) if err != nil { return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrimInterface, err) } + case strings.HasPrefix(itemTrimInterface, "interface-type "): + interfaceOptions["interface_type"] = strings.TrimPrefix(itemTrimInterface, "interface-type ") + case strings.HasPrefix(itemTrimInterface, "ipsec-sa "): + interfaceOptions["ipsec_sa"] = strings.Trim(strings.TrimPrefix(itemTrimInterface, "ipsec-sa "), "\"") + case strings.HasPrefix(itemTrimInterface, "ipv4-adjacency-segment protected "): + itemTrimInterfaceSplit := strings.Split(strings.TrimPrefix( + itemTrimInterface, "ipv4-adjacency-segment protected "), " ") + interfaceOptions["ipv4_adjacency_segment_protected_type"] = itemTrimInterfaceSplit[0] + if len(itemTrimInterfaceSplit) > 1 { + interfaceOptions["ipv4_adjacency_segment_protected_value"] = itemTrimInterfaceSplit[1] + } + case strings.HasPrefix(itemTrimInterface, "ipv4-adjacency-segment unprotected "): + itemTrimInterfaceSplit := strings.Split(strings.TrimPrefix( + itemTrimInterface, "ipv4-adjacency-segment unprotected "), " ") + interfaceOptions["ipv4_adjacency_segment_unprotected_type"] = itemTrimInterfaceSplit[0] + if len(itemTrimInterfaceSplit) > 1 { + interfaceOptions["ipv4_adjacency_segment_unprotected_value"] = itemTrimInterfaceSplit[1] + } + case itemTrimInterface == "link-protection": + interfaceOptions["link_protection"] = true case strings.HasPrefix(itemTrimInterface, "metric "): interfaceOptions["metric"], err = strconv.Atoi( strings.TrimPrefix(itemTrimInterface, "metric ")) if err != nil { return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrimInterface, err) } - case itemTrimInterface == passiveW: + case strings.HasPrefix(itemTrimInterface, "mtu "): + interfaceOptions["mtu"], err = strconv.Atoi( + strings.TrimPrefix(itemTrimInterface, "mtu ")) + if err != nil { + return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrimInterface, err) + } + case strings.HasPrefix(itemTrimInterface, "neighbor "): + itemTrimInterfaceSplit := strings.Split(strings.TrimPrefix(itemTrimInterface, "neighbor "), " ") + address := itemTrimInterfaceSplit[0] + if len(itemTrimInterfaceSplit) > 1 && itemTrimInterfaceSplit[1] == "eligible" { + interfaceOptions["neighbor"] = append( + interfaceOptions["neighbor"].([]map[string]interface{}), map[string]interface{}{ + "address": address, + "eligible": true, + }) + } else { + interfaceOptions["neighbor"] = append( + interfaceOptions["neighbor"].([]map[string]interface{}), map[string]interface{}{ + "address": address, + "eligible": false, + }) + } + case itemTrimInterface == "no-advertise-adjacency-segment": + interfaceOptions["no_advertise_adjacency_segment"] = true + case itemTrimInterface == "no-eligible-backup": + interfaceOptions["no_eligible_backup"] = true + case itemTrimInterface == "no-eligible-remote-backup": + interfaceOptions["no_eligible_remote_backup"] = true + case itemTrimInterface == "no-interface-state-traps": + interfaceOptions["no_interface_state_traps"] = true + case itemTrimInterface == "no-neighbor-down-notification": + interfaceOptions["no_neighbor_down_notification"] = true + case itemTrimInterface == "node-link-protection": + interfaceOptions["node_link_protection"] = true + case strings.HasPrefix(itemTrimInterface, "passive"): interfaceOptions["passive"] = true + switch { + case strings.HasPrefix(itemTrimInterface, "passive traffic-engineering remote-node-id "): + interfaceOptions["passive_traffic_engineering_remote_node_id"] = strings.TrimPrefix( + itemTrimInterface, "passive traffic-engineering remote-node-id ") + case strings.HasPrefix(itemTrimInterface, "passive traffic-engineering remote-node-router-id "): + interfaceOptions["passive_traffic_engineering_remote_node_router_id"] = strings.TrimPrefix( + itemTrimInterface, "passive traffic-engineering remote-node-router-id ") + } + case strings.HasPrefix(itemTrimInterface, "poll-interval "): + interfaceOptions["poll_interval"], err = strconv.Atoi( + strings.TrimPrefix(itemTrimInterface, "poll-interval ")) + if err != nil { + return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrimInterface, err) + } + case strings.HasPrefix(itemTrimInterface, "priority "): + interfaceOptions["priority"], err = strconv.Atoi( + strings.TrimPrefix(itemTrimInterface, "priority ")) + if err != nil { + return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrimInterface, err) + } case strings.HasPrefix(itemTrimInterface, "retransmit-interval "): interfaceOptions["retransmit_interval"], err = strconv.Atoi( strings.TrimPrefix(itemTrimInterface, "retransmit-interval ")) if err != nil { return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrimInterface, err) } + case itemTrimInterface == "secondary": + interfaceOptions["secondary"] = true + case itemTrimInterface == "strict-bfd": + interfaceOptions["strict_bfd"] = true + case strings.HasPrefix(itemTrimInterface, "te-metric "): + interfaceOptions["te_metric"], err = strconv.Atoi( + strings.TrimPrefix(itemTrimInterface, "te-metric ")) + if err != nil { + return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrimInterface, err) + } + case strings.HasPrefix(itemTrimInterface, "transit-delay "): + interfaceOptions["transit_delay"], err = strconv.Atoi( + strings.TrimPrefix(itemTrimInterface, "transit-delay ")) + if err != nil { + return confRead, fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrimInterface, err) + } } confRead.interFace = append(confRead.interFace, interfaceOptions) } @@ -451,6 +1077,69 @@ func readOspfArea(idArea, version, routingInstance string, return confRead, nil } +func readOspfAreaInterfaceBfd(itemTrim string, bfd map[string]interface{}) error { + switch { + case strings.HasPrefix(itemTrim, "authentication algorithm "): + bfd["authentication_algorithm"] = strings.TrimPrefix(itemTrim, "authentication algorithm ") + case strings.HasPrefix(itemTrim, "authentication key-chain "): + bfd["authentication_key_chain"] = strings.Trim(strings.TrimPrefix(itemTrim, "authentication key-chain "), "\"") + case itemTrim == authenticationLooseCheck: + bfd["authentication_loose_check"] = true + case strings.HasPrefix(itemTrim, "detection-time threshold "): + var err error + bfd["detection_time_threshold"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "detection-time threshold ")) + if err != nil { + return fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + case itemTrim == "full-neighbors-only": + bfd["full_neighbors_only"] = true + case strings.HasPrefix(itemTrim, "holddown-interval "): + var err error + bfd["holddown_interval"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "holddown-interval ")) + if err != nil { + return fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + case strings.HasPrefix(itemTrim, "minimum-interval "): + var err error + bfd["minimum_interval"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "minimum-interval ")) + if err != nil { + return fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + case strings.HasPrefix(itemTrim, "minimum-receive-interval "): + var err error + bfd["minimum_receive_interval"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "minimum-receive-interval ")) + if err != nil { + return fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + case strings.HasPrefix(itemTrim, "multiplier "): + var err error + bfd["multiplier"], err = strconv.Atoi(strings.TrimPrefix(itemTrim, "multiplier ")) + if err != nil { + return fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + case itemTrim == noAdaptation: + bfd["no_adaptation"] = true + case strings.HasPrefix(itemTrim, "transmit-interval minimum-interval "): + var err error + bfd["transmit_interval_minimum_interval"], err = strconv.Atoi(strings.TrimPrefix( + itemTrim, "transmit-interval minimum-interval ")) + if err != nil { + return fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + case strings.HasPrefix(itemTrim, "transmit-interval threshold "): + var err error + bfd["transmit_interval_threshold"], err = strconv.Atoi(strings.TrimPrefix( + itemTrim, "transmit-interval threshold ")) + if err != nil { + return fmt.Errorf("failed to convert value from '%s' to integer : %w", itemTrim, err) + } + case strings.HasPrefix(itemTrim, "version "): + bfd["version"] = strings.TrimPrefix(itemTrim, "version ") + } + + return nil +} + func delOspfArea(d *schema.ResourceData, m interface{}, jnprSess *NetconfObject) error { sess := m.(*Session) configSet := make([]string, 0, 1) diff --git a/junos/resource_ospf_area_test.go b/junos/resource_ospf_area_test.go index 32784268..c55be6a0 100644 --- a/junos/resource_ospf_area_test.go +++ b/junos/resource_ospf_area_test.go @@ -25,7 +25,7 @@ func TestAccJunosOspfArea_basic(t *testing.T) { Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: testAccJunosOspfAreaConfigCreate(), + Config: testAccJunosOspfAreaConfigCreate(testaccOspfArea), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("junos_ospf_area.testacc_ospfarea", "area_id", "0.0.0.0"), @@ -34,7 +34,7 @@ func TestAccJunosOspfArea_basic(t *testing.T) { resource.TestCheckResourceAttr("junos_ospf_area.testacc_ospfarea", "routing_instance", "default"), resource.TestCheckResourceAttr("junos_ospf_area.testacc_ospfarea", - "interface.#", "1"), + "interface.#", "2"), resource.TestCheckResourceAttr("junos_ospf_area.testacc_ospfarea", "interface.0.name", "all"), resource.TestCheckResourceAttr("junos_ospf_area.testacc_ospfarea", @@ -44,9 +44,9 @@ func TestAccJunosOspfArea_basic(t *testing.T) { resource.TestCheckResourceAttr("junos_ospf_area.testacc_ospfarea", "interface.0.metric", "100"), resource.TestCheckResourceAttr("junos_ospf_area.testacc_ospfarea", - "interface.0.retransmit_interval", "10"), + "interface.0.retransmit_interval", "12"), resource.TestCheckResourceAttr("junos_ospf_area.testacc_ospfarea", - "interface.0.hello_interval", "10"), + "interface.0.hello_interval", "11"), resource.TestCheckResourceAttr("junos_ospf_area.testacc_ospfarea", "interface.0.dead_interval", "10"), ), @@ -55,17 +55,17 @@ func TestAccJunosOspfArea_basic(t *testing.T) { Config: testAccJunosOspfAreaConfigUpdate(testaccOspfArea, testaccOspfArea2), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("junos_ospf_area.testacc_ospfarea", - "interface.#", "1"), + "interface.#", "2"), resource.TestCheckResourceAttr("junos_ospf_area.testacc_ospfarea2", "routing_instance", "testacc_ospfarea"), resource.TestCheckResourceAttr("junos_ospf_area.testacc_ospfarea2", "version", "v3"), resource.TestCheckResourceAttr("junos_ospf_area.testacc_ospfarea2", - "interface.#", "3"), + "interface.#", "2"), resource.TestCheckResourceAttr("junos_ospf_area.testacc_ospfarea2", - "interface.1.name", testaccOspfArea+".0"), + "interface.1.name", testaccOspfArea2+".0"), resource.TestCheckResourceAttr("junos_ospf_area.testacc_ospfarea2", - "interface.1.disable", "true"), + "interface.1.bfd_liveness_detection.#", "1"), ), }, { @@ -78,7 +78,7 @@ func TestAccJunosOspfArea_basic(t *testing.T) { } } -func testAccJunosOspfAreaConfigCreate() string { +func testAccJunosOspfAreaConfigCreate(interFace string) string { return ` resource junos_ospf_area "testacc_ospfarea" { area_id = "0.0.0.0" @@ -87,10 +87,18 @@ resource junos_ospf_area "testacc_ospfarea" { disable = true passive = true metric = 100 - retransmit_interval = 10 - hello_interval = 10 + retransmit_interval = 12 + hello_interval = 11 dead_interval = 10 } + interface { + name = junos_interface_logical.testacc_ospfarea.name + secondary = true + } +} +resource junos_interface_logical "testacc_ospfarea" { + name = "` + interFace + `.0" + description = "testacc_ospfarea" } ` } @@ -100,14 +108,47 @@ func testAccJunosOspfAreaConfigUpdate(interFace, interFace2 string) string { resource junos_ospf_area "testacc_ospfarea" { area_id = "0.0.0.0" interface { - name = "all" - passive = true + name = "all" + passive = true + authentication_simple_password = "testPass" + link_protection = true + no_advertise_adjacency_segment = true + no_interface_state_traps = true + no_neighbor_down_notification = true + poll_interval = 19 + te_metric = 221 + } + interface { + name = junos_interface_logical.testacc_ospfarea.name + disable = true + authentication_md5 { + key_id = 3 + key = "testK3y" + } + authentication_md5 { + key_id = 2 + key = "testK3y2" + start_time = "2022-3-9.12:50:00" + } + strict_bfd = true + bfd_liveness_detection { + minimum_receive_interval = 29 + transmit_interval_minimum_interval = 48 + transmit_interval_threshold = 49 + version = "automatic" + } + neighbor { + address = "192.0.2.6" + } + neighbor { + address = "192.0.2.5" + eligible = "true" + } } } resource junos_interface_logical "testacc_ospfarea" { - name = "` + interFace + `.0" - description = "testacc_ospfarea" - routing_instance = junos_routing_instance.testacc_ospfarea.name + name = "` + interFace + `.0" + description = "testacc_ospfarea" } resource junos_interface_logical "testacc_ospfarea2" { name = "` + interFace2 + `.0" @@ -125,16 +166,45 @@ resource junos_ospf_area "testacc_ospfarea2" { name = "all" passive = true metric = 100 - retransmit_interval = 10 - hello_interval = 10 - dead_interval = 10 - } - interface { - name = junos_interface_logical.testacc_ospfarea.name - disable = true + retransmit_interval = 32 + hello_interval = 31 + dead_interval = 30 + bandwidth_based_metrics { + bandwidth = "100k" + metric = 13 + } + bandwidth_based_metrics { + bandwidth = "1m" + metric = 14 + } + demand_circuit = true + dynamic_neighbors = true + flood_reduction = true + interface_type = "p2mp" + mtu = 900 + no_eligible_backup = true + no_eligible_remote_backup = true + node_link_protection = true + passive_traffic_engineering_remote_node_id = "192.0.2.7" + passive_traffic_engineering_remote_node_router_id = "192.0.2.8" + priority = 21 + transit_delay = 23 } interface { name = junos_interface_logical.testacc_ospfarea2.name + bfd_liveness_detection { + authentication_loose_check = true + detection_time_threshold = 60 + full_neighbors_only = true + holddown_interval = 15 + minimum_interval = 16 + minimum_receive_interval = 17 + multiplier = 2 + no_adaptation = true + transmit_interval_minimum_interval = 18 + transmit_interval_threshold = 19 + version = "automatic" + } } } ` diff --git a/junos/resource_system_services_dhcp_localserver_group.go b/junos/resource_system_services_dhcp_localserver_group.go index a1995c54..f8ad762a 100644 --- a/junos/resource_system_services_dhcp_localserver_group.go +++ b/junos/resource_system_services_dhcp_localserver_group.go @@ -1176,7 +1176,7 @@ func setSystemServicesDhcpLocalServerGroup(d *schema.ResourceData, m interface{} configSet = append(configSet, setPrefixLDMBfd+"multiplier "+strconv.Itoa(v)) } if liveDetectMethBfd["no_adaptation"].(bool) { - configSet = append(configSet, setPrefixLDMBfd+"no-adaptation") + configSet = append(configSet, setPrefixLDMBfd+noAdaptation) } if v := liveDetectMethBfd["session_mode"].(string); v != "" { configSet = append(configSet, setPrefixLDMBfd+"session-mode "+v) @@ -1706,7 +1706,7 @@ func readSystemServicesDhcpLocalServerGroup(name, instance, version string, m in case strings.HasPrefix(itemTrimLiveDetMethBfd, "multiplier "): confRead.livenessDetectionMethodBfd[0]["multiplier"], err = strconv.Atoi(strings.TrimPrefix( itemTrimLiveDetMethBfd, "multiplier ")) - case itemTrimLiveDetMethBfd == "no-adaptation": + case itemTrimLiveDetMethBfd == noAdaptation: confRead.livenessDetectionMethodBfd[0]["no_adaptation"] = true case strings.HasPrefix(itemTrimLiveDetMethBfd, "session-mode "): confRead.livenessDetectionMethodBfd[0]["session_mode"] = strings.TrimPrefix(