diff --git a/.changes/issue-672-673.md b/.changes/issue-672-673.md index 3a64e219..6709e8ed 100644 --- a/.changes/issue-672-673.md +++ b/.changes/issue-672-673.md @@ -1,5 +1,6 @@ ENHANCEMENTS: +* **resource/junos_bridge_domain**: add `static_remote_vtep_list` argument inside `vxlan` block argument (Fix [#672](https://github.com/jeremmfr/terraform-provider-junos/issues/672)) * **resource/junos_routing_instance**: add `remote_vtep_list` and `remote_vtep_v6_list` arguments (Fix [#673](https://github.com/jeremmfr/terraform-provider-junos/issues/673)) * **resource/junos_switch_options**: add `remote_vtep_list` and `remote_vtep_v6_list` arguments diff --git a/docs/resources/bridge_domain.md b/docs/resources/bridge_domain.md index 722a32dc..8a983661 100644 --- a/docs/resources/bridge_domain.md +++ b/docs/resources/bridge_domain.md @@ -55,6 +55,8 @@ The following arguments are supported: Declare vxlan options. - **vni** (Required, Number) VXLAN identifier (0..16777214). + - **vni_extend_evpn** (Optional, Boolean) + Extend VNI to EVPN. - **decapsulate_accept_inner_vlan** (Optional, Boolean) Accept VXLAN packets with inner VLAN. - **encapsulate_inner_vlan** (Optional, Boolean) @@ -65,10 +67,10 @@ The following arguments are supported: CIDR for Multicast group registered for VXLAN segment. - **ovsdb_managed** (Optional, Boolean) Bridge-domain is managed remotely via VXLAN OVSDB Controller. + - **static_remote_vtep_list** (Optional, Set of String) + Configure bridge domain specific static remote VXLAN tunnel endpoints. - **unreachable_vtep_aging_timer** (Optional, Number) Unreachable VXLAN tunnel endpoint removal timer (300..1800 seconds). - - **vni_extend_evpn** (Optional, Boolean) - Extend VNI to EVPN. ## Attribute Reference diff --git a/internal/providerfwk/resource_bridge_domain.go b/internal/providerfwk/resource_bridge_domain.go index 2230bdbf..6957d743 100644 --- a/internal/providerfwk/resource_bridge_domain.go +++ b/internal/providerfwk/resource_bridge_domain.go @@ -261,6 +261,17 @@ func (rsc *bridgeDomain) Schema( tfvalidator.BoolTrue(), }, }, + "static_remote_vtep_list": schema.SetAttribute{ + ElementType: types.StringType, + Optional: true, + Description: "Configure bridge domain specific static remote VXLAN tunnel endpoints.", + Validators: []validator.Set{ + setvalidator.SizeAtLeast(1), + setvalidator.ValueStringsAre( + tfvalidator.StringIPAddress().IPv4Only(), + ), + }, + }, "unreachable_vtep_aging_timer": schema.Int64Attribute{ Optional: true, Description: "Unreachable VXLAN tunnel endpoint removal timer.", @@ -289,33 +300,45 @@ type bridgeDomainData struct { IsolatedVLAN types.Int64 `tfsdk:"isolated_vlan"` RoutingInterface types.String `tfsdk:"routing_interface"` ServiceID types.Int64 `tfsdk:"service_id"` - VLANID types.Int64 `tfsdk:"vlan_id"` - VLANIDList []types.String `tfsdk:"vlan_id_list"` - VXLAN *bridgeDomainBlockVXLAN `tfsdk:"vxlan"` + VlanID types.Int64 `tfsdk:"vlan_id"` + VlanIDList []types.String `tfsdk:"vlan_id_list"` + Vxlan *bridgeDomainBlockVxlan `tfsdk:"vxlan"` } type bridgeDomainConfig struct { - ID types.String `tfsdk:"id"` - Name types.String `tfsdk:"name"` - RoutingInstance types.String `tfsdk:"routing_instance"` - CommunityVlans types.Set `tfsdk:"community_vlans"` - Description types.String `tfsdk:"description"` - DomainID types.Int64 `tfsdk:"domain_id"` - DomainTypeBridge types.Bool `tfsdk:"domain_type_bridge"` - Interface types.Set `tfsdk:"interface"` - IsolatedVLAN types.Int64 `tfsdk:"isolated_vlan"` - RoutingInterface types.String `tfsdk:"routing_interface"` - ServiceID types.Int64 `tfsdk:"service_id"` - VLANID types.Int64 `tfsdk:"vlan_id"` - VLANIDList types.Set `tfsdk:"vlan_id_list"` - VXLAN *bridgeDomainBlockVXLAN `tfsdk:"vxlan"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + RoutingInstance types.String `tfsdk:"routing_instance"` + CommunityVlans types.Set `tfsdk:"community_vlans"` + Description types.String `tfsdk:"description"` + DomainID types.Int64 `tfsdk:"domain_id"` + DomainTypeBridge types.Bool `tfsdk:"domain_type_bridge"` + Interface types.Set `tfsdk:"interface"` + IsolatedVLAN types.Int64 `tfsdk:"isolated_vlan"` + RoutingInterface types.String `tfsdk:"routing_interface"` + ServiceID types.Int64 `tfsdk:"service_id"` + VlanID types.Int64 `tfsdk:"vlan_id"` + VlanIDList types.Set `tfsdk:"vlan_id_list"` + Vxlan *bridgeDomainBlockVxlanConfig `tfsdk:"vxlan"` } func (rscConfig *bridgeDomainConfig) isEmpty() bool { return tfdata.CheckBlockIsEmpty(rscConfig, "ID", "Name", "RoutingInstance") } -type bridgeDomainBlockVXLAN struct { +type bridgeDomainBlockVxlan struct { + Vni types.Int64 `tfsdk:"vni"` + VniExtendEvpn types.Bool `tfsdk:"vni_extend_evpn"` + DecapsulateAcceptInnerVlan types.Bool `tfsdk:"decapsulate_accept_inner_vlan"` + EncapsulateInnerVlan types.Bool `tfsdk:"encapsulate_inner_vlan"` + IngressNodeReplication types.Bool `tfsdk:"ingress_node_replication"` + MulticastGroup types.String `tfsdk:"multicast_group"` + OvsdbManaged types.Bool `tfsdk:"ovsdb_managed"` + StaticRemoteVtepList []types.String `tfsdk:"static_remote_vtep_list"` + UnreachableVtepAgingTimer types.Int64 `tfsdk:"unreachable_vtep_aging_timer"` +} + +type bridgeDomainBlockVxlanConfig struct { Vni types.Int64 `tfsdk:"vni"` VniExtendEvpn types.Bool `tfsdk:"vni_extend_evpn"` DecapsulateAcceptInnerVlan types.Bool `tfsdk:"decapsulate_accept_inner_vlan"` @@ -323,6 +346,7 @@ type bridgeDomainBlockVXLAN struct { IngressNodeReplication types.Bool `tfsdk:"ingress_node_replication"` MulticastGroup types.String `tfsdk:"multicast_group"` OvsdbManaged types.Bool `tfsdk:"ovsdb_managed"` + StaticRemoteVtepList types.Set `tfsdk:"static_remote_vtep_list"` UnreachableVtepAgingTimer types.Int64 `tfsdk:"unreachable_vtep_aging_timer"` } @@ -343,16 +367,16 @@ func (rsc *bridgeDomain) ValidateConfig( ) } - if !config.VLANID.IsNull() && !config.VLANID.IsUnknown() && - !config.VLANIDList.IsNull() && !config.VLANIDList.IsUnknown() { + if !config.VlanID.IsNull() && !config.VlanID.IsUnknown() && + !config.VlanIDList.IsNull() && !config.VlanIDList.IsUnknown() { resp.Diagnostics.AddAttributeError( path.Root("vlan_id"), tfdiag.ConflictConfigErrSummary, "vlan_id and vlan_id_list cannot be configured together", ) } - if config.VXLAN != nil { - if config.VXLAN.Vni.IsNull() { + if config.Vxlan != nil { + if config.Vxlan.Vni.IsNull() { resp.Diagnostics.AddAttributeError( path.Root("vxlan").AtName("vni"), tfdiag.MissingConfigErrSummary, @@ -624,39 +648,42 @@ func (rscData *bridgeDomainData) set( configSet = append(configSet, setPrefix+"service-id "+ utils.ConvI64toa(rscData.ServiceID.ValueInt64())) } - if !rscData.VLANID.IsNull() { + if !rscData.VlanID.IsNull() { configSet = append(configSet, setPrefix+"vlan-id "+ - utils.ConvI64toa(rscData.VLANID.ValueInt64())) + utils.ConvI64toa(rscData.VlanID.ValueInt64())) } - for _, v := range rscData.VLANIDList { + for _, v := range rscData.VlanIDList { configSet = append(configSet, setPrefix+"vlan-id-list "+v.ValueString()) } - if rscData.VXLAN != nil { + if rscData.Vxlan != nil { configSet = append(configSet, setPrefix+"vxlan vni "+ - utils.ConvI64toa(rscData.VXLAN.Vni.ValueInt64())) + utils.ConvI64toa(rscData.Vxlan.Vni.ValueInt64())) - if rscData.VXLAN.VniExtendEvpn.ValueBool() { + if rscData.Vxlan.VniExtendEvpn.ValueBool() { configSet = append(configSet, setPrefixProtocolsEvpn+"extended-vni-list "+ - utils.ConvI64toa(rscData.VXLAN.Vni.ValueInt64())) + utils.ConvI64toa(rscData.Vxlan.Vni.ValueInt64())) } - if rscData.VXLAN.DecapsulateAcceptInnerVlan.ValueBool() { + if rscData.Vxlan.DecapsulateAcceptInnerVlan.ValueBool() { configSet = append(configSet, setPrefix+"vxlan decapsulate-accept-inner-vlan") } - if rscData.VXLAN.EncapsulateInnerVlan.ValueBool() { + if rscData.Vxlan.EncapsulateInnerVlan.ValueBool() { configSet = append(configSet, setPrefix+"vxlan encapsulate-inner-vlan") } - if rscData.VXLAN.IngressNodeReplication.ValueBool() { + if rscData.Vxlan.IngressNodeReplication.ValueBool() { configSet = append(configSet, setPrefix+"vxlan ingress-node-replication") } - if v := rscData.VXLAN.MulticastGroup.ValueString(); v != "" { + if v := rscData.Vxlan.MulticastGroup.ValueString(); v != "" { configSet = append(configSet, setPrefix+"vxlan multicast-group "+v) } - if rscData.VXLAN.OvsdbManaged.ValueBool() { + if rscData.Vxlan.OvsdbManaged.ValueBool() { configSet = append(configSet, setPrefix+"vxlan ovsdb-managed") } - if !rscData.VXLAN.UnreachableVtepAgingTimer.IsNull() { + for _, v := range rscData.Vxlan.StaticRemoteVtepList { + configSet = append(configSet, setPrefix+"vxlan static-remote-vtep-list "+v.ValueString()) + } + if !rscData.Vxlan.UnreachableVtepAgingTimer.IsNull() { configSet = append(configSet, setPrefix+"vxlan unreachable-vtep-aging-timer "+ - utils.ConvI64toa(rscData.VXLAN.UnreachableVtepAgingTimer.ValueInt64())) + utils.ConvI64toa(rscData.Vxlan.UnreachableVtepAgingTimer.ValueInt64())) } } @@ -718,19 +745,19 @@ func (rscData *bridgeDomainData) read( return err } case balt.CutPrefixInString(&itemTrim, "vlan-id "): - rscData.VLANID, err = tfdata.ConvAtoi64Value(itemTrim) + rscData.VlanID, err = tfdata.ConvAtoi64Value(itemTrim) if err != nil { return err } case balt.CutPrefixInString(&itemTrim, "vlan-id-list "): - rscData.VLANIDList = append(rscData.VLANIDList, types.StringValue(itemTrim)) + rscData.VlanIDList = append(rscData.VlanIDList, types.StringValue(itemTrim)) case balt.CutPrefixInString(&itemTrim, "vxlan "): - if rscData.VXLAN == nil { - rscData.VXLAN = &bridgeDomainBlockVXLAN{} + if rscData.Vxlan == nil { + rscData.Vxlan = &bridgeDomainBlockVxlan{} } switch { case balt.CutPrefixInString(&itemTrim, "vni "): - rscData.VXLAN.Vni, err = tfdata.ConvAtoi64Value(itemTrim) + rscData.Vxlan.Vni, err = tfdata.ConvAtoi64Value(itemTrim) if err != nil { return err } @@ -747,24 +774,26 @@ func (rscData *bridgeDomainData) read( break } if itemEvpn == junos.SetLS+"extended-vni-list "+itemTrim { - rscData.VXLAN.VniExtendEvpn = types.BoolValue(true) + rscData.Vxlan.VniExtendEvpn = types.BoolValue(true) break } } } case itemTrim == "decapsulate-accept-inner-vlan": - rscData.VXLAN.DecapsulateAcceptInnerVlan = types.BoolValue(true) + rscData.Vxlan.DecapsulateAcceptInnerVlan = types.BoolValue(true) case itemTrim == "encapsulate-inner-vlan": - rscData.VXLAN.EncapsulateInnerVlan = types.BoolValue(true) + rscData.Vxlan.EncapsulateInnerVlan = types.BoolValue(true) case itemTrim == "ingress-node-replication": - rscData.VXLAN.IngressNodeReplication = types.BoolValue(true) + rscData.Vxlan.IngressNodeReplication = types.BoolValue(true) case balt.CutPrefixInString(&itemTrim, "multicast-group "): - rscData.VXLAN.MulticastGroup = types.StringValue(itemTrim) + rscData.Vxlan.MulticastGroup = types.StringValue(itemTrim) case itemTrim == "ovsdb-managed": - rscData.VXLAN.OvsdbManaged = types.BoolValue(true) + rscData.Vxlan.OvsdbManaged = types.BoolValue(true) + case balt.CutPrefixInString(&itemTrim, "static-remote-vtep-list "): + rscData.Vxlan.StaticRemoteVtepList = append(rscData.Vxlan.StaticRemoteVtepList, types.StringValue(itemTrim)) case balt.CutPrefixInString(&itemTrim, "unreachable-vtep-aging-timer "): - rscData.VXLAN.UnreachableVtepAgingTimer, err = tfdata.ConvAtoi64Value(itemTrim) + rscData.Vxlan.UnreachableVtepAgingTimer, err = tfdata.ConvAtoi64Value(itemTrim) if err != nil { return err } @@ -801,10 +830,10 @@ func (rscData *bridgeDomainData) delOpts( for _, v := range rscData.Interface { configSet = append(configSet, delPrefix+"interface "+v.ValueString()) } - if rscData.VXLAN != nil { - if rscData.VXLAN.VniExtendEvpn.ValueBool() { + if rscData.Vxlan != nil { + if rscData.Vxlan.VniExtendEvpn.ValueBool() { configSet = append(configSet, delPrefixProtocolsEvpn+ - "extended-vni-list "+utils.ConvI64toa(rscData.VXLAN.Vni.ValueInt64())) + "extended-vni-list "+utils.ConvI64toa(rscData.Vxlan.Vni.ValueInt64())) } } @@ -822,10 +851,10 @@ func (rscData *bridgeDomainData) del( configSet := []string{ delPrefix + "bridge-domains \"" + rscData.Name.ValueString() + "\"", } - if rscData.VXLAN != nil { - if rscData.VXLAN.VniExtendEvpn.ValueBool() { + if rscData.Vxlan != nil { + if rscData.Vxlan.VniExtendEvpn.ValueBool() { configSet = append(configSet, delPrefix+ - "protocols evpn extended-vni-list "+utils.ConvI64toa(rscData.VXLAN.Vni.ValueInt64())) + "protocols evpn extended-vni-list "+utils.ConvI64toa(rscData.Vxlan.Vni.ValueInt64())) } } diff --git a/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/1/main.tf b/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/1/main.tf index 033694e3..2aafcff9 100644 --- a/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/1/main.tf +++ b/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/1/main.tf @@ -32,6 +32,7 @@ resource "junos_routing_instance" "testacc_bridge_ri" { route_distinguisher = "10:11" vrf_target = "target:1:200" vtep_source_interface = junos_interface_logical.testacc_bridge_ri.name + remote_vtep_list = ["192.0.2.136", "192.0.2.36"] } resource "junos_evpn" "testacc_bridge_ri" { routing_instance = junos_routing_instance.testacc_bridge_ri.name diff --git a/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/4/main.tf b/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/4/main.tf index 702d952d..5b912618 100644 --- a/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/4/main.tf +++ b/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/4/main.tf @@ -27,6 +27,7 @@ resource "junos_routing_instance" "testacc_bridge_ri" { route_distinguisher = "10:11" vrf_target = "target:1:200" vtep_source_interface = junos_interface_logical.testacc_bridge_ri.name + remote_vtep_list = ["192.0.2.136", "192.0.2.36"] } resource "junos_evpn" "testacc_bridge_ri" { routing_instance = junos_routing_instance.testacc_bridge_ri.name @@ -48,6 +49,7 @@ resource "junos_bridge_domain" "testacc_bridge_ri" { service_id = 12 vlan_id = 13 vxlan { - vni = 15 + vni = 15 + static_remote_vtep_list = ["192.0.2.136", "192.0.2.36"] } } diff --git a/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/5/main.tf b/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/5/main.tf index 07cefaba..7a44871f 100644 --- a/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/5/main.tf +++ b/internal/providerfwk/testdata/TestAccResourceBridgeDomain_basic/5/main.tf @@ -27,6 +27,7 @@ resource "junos_routing_instance" "testacc_bridge_ri" { route_distinguisher = "10:11" vrf_target = "target:1:200" vtep_source_interface = junos_interface_logical.testacc_bridge_ri.name + remote_vtep_list = ["192.0.2.136", "192.0.2.36"] } resource "junos_evpn" "testacc_bridge_ri" { routing_instance = junos_routing_instance.testacc_bridge_ri.name @@ -47,6 +48,7 @@ resource "junos_bridge_domain" "testacc_bridge_ri" { service_id = 12 vlan_id = 13 vxlan { - vni = 15 + vni = 15 + static_remote_vtep_list = ["192.0.2.36"] } } diff --git a/internal/providerfwk/upgradestate_bridge_domain.go b/internal/providerfwk/upgradestate_bridge_domain.go index 3a442876..5e6357d4 100644 --- a/internal/providerfwk/upgradestate_bridge_domain.go +++ b/internal/providerfwk/upgradestate_bridge_domain.go @@ -105,9 +105,9 @@ func upgradeBridgeDomainStateV0toV1( IsolatedVLAN types.Int64 `tfsdk:"isolated_vlan"` RoutingInterface types.String `tfsdk:"routing_interface"` ServiceID types.Int64 `tfsdk:"service_id"` - VLANID types.Int64 `tfsdk:"vlan_id"` - VLANIDList []types.String `tfsdk:"vlan_id_list"` - VXLAN []struct { + VlanID types.Int64 `tfsdk:"vlan_id"` + VlanIDList []types.String `tfsdk:"vlan_id_list"` + Vxlan []struct { Vni types.Int64 `tfsdk:"vni"` VniExtendEvpn types.Bool `tfsdk:"vni_extend_evpn"` DecapsulateAcceptInnerVlan types.Bool `tfsdk:"decapsulate_accept_inner_vlan"` @@ -136,18 +136,18 @@ func upgradeBridgeDomainStateV0toV1( dataV1.IsolatedVLAN = dataV0.IsolatedVLAN dataV1.RoutingInterface = dataV0.RoutingInterface dataV1.ServiceID = dataV0.ServiceID - dataV1.VLANID = dataV0.VLANID - dataV1.VLANIDList = dataV0.VLANIDList - if len(dataV0.VXLAN) > 0 { - dataV1.VXLAN = &bridgeDomainBlockVXLAN{ - Vni: dataV0.VXLAN[0].Vni, - VniExtendEvpn: dataV0.VXLAN[0].VniExtendEvpn, - DecapsulateAcceptInnerVlan: dataV0.VXLAN[0].DecapsulateAcceptInnerVlan, - EncapsulateInnerVlan: dataV0.VXLAN[0].EncapsulateInnerVlan, - IngressNodeReplication: dataV0.VXLAN[0].IngressNodeReplication, - OvsdbManaged: dataV0.VXLAN[0].OvsdbManaged, - MulticastGroup: dataV0.VXLAN[0].MulticastGroup, - UnreachableVtepAgingTimer: dataV0.VXLAN[0].UnreachableVtepAgingTimer, + dataV1.VlanID = dataV0.VlanID + dataV1.VlanIDList = dataV0.VlanIDList + if len(dataV0.Vxlan) > 0 { + dataV1.Vxlan = &bridgeDomainBlockVxlan{ + Vni: dataV0.Vxlan[0].Vni, + VniExtendEvpn: dataV0.Vxlan[0].VniExtendEvpn, + DecapsulateAcceptInnerVlan: dataV0.Vxlan[0].DecapsulateAcceptInnerVlan, + EncapsulateInnerVlan: dataV0.Vxlan[0].EncapsulateInnerVlan, + IngressNodeReplication: dataV0.Vxlan[0].IngressNodeReplication, + OvsdbManaged: dataV0.Vxlan[0].OvsdbManaged, + MulticastGroup: dataV0.Vxlan[0].MulticastGroup, + UnreachableVtepAgingTimer: dataV0.Vxlan[0].UnreachableVtepAgingTimer, } }