diff --git a/.github/workflows/wiki.yml b/.github/workflows/wiki.yml index 08ec3d63fc6..2c6e0644c11 100644 --- a/.github/workflows/wiki.yml +++ b/.github/workflows/wiki.yml @@ -9,11 +9,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: path: featureprofiles - name: Checkout wiki - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: repository: "openconfig/featureprofiles.wiki" path: featureprofiles.wiki diff --git a/feature/aft/aft_summary/otg_tests/route_summary_counters_test/metadata.textproto b/feature/aft/aft_summary/otg_tests/route_summary_counters_test/metadata.textproto index 1031dcd2e0c..617ae227d00 100644 --- a/feature/aft/aft_summary/otg_tests/route_summary_counters_test/metadata.textproto +++ b/feature/aft/aft_summary/otg_tests/route_summary_counters_test/metadata.textproto @@ -30,6 +30,14 @@ platform_exceptions: { isis_single_topology_required: true } } +platform_exceptions: { + platform: { + vendor: JUNIPER + } + deviations: { + isis_level_enabled: true + } +} platform_exceptions: { platform: { vendor: ARISTA diff --git a/feature/bgp/admin_distance/README.md b/feature/bgp/admin_distance/README.md new file mode 100644 index 00000000000..6ddfb281d76 --- /dev/null +++ b/feature/bgp/admin_distance/README.md @@ -0,0 +1,92 @@ +# RT-1.34: BGP route-distance configuration + +## Summary + +BGP default-route-distance, external-route-distance and internal-route-distance (administrative distance) configuration. + +## Testbed type + +* https://github.com/openconfig/featureprofiles/blob/main/topologies/atedut_4.testbed + +## Procedure + +### Applying configuration + +For each section of configuration below, prepare a gnmi.SetBatch with all the configuration items appended to one SetBatch. Then apply the configuration to the DUT in one gnmi.Set using the `replace` option + +#### Initial Setup: + +* Connect DUT port-1, 2 and 3 to ATE port-1, 2 and 3 +* Configure IPv4/IPv6 addresses on the ports +* Create an IPv4 network i.e. ```ipv4-network-1 = 192.168.10.0/24``` attached to ATE port-1 and port-2 +* Create an IPv6 network i.e. ```ipv6-network-1 = 2024:db8:64:64::/64``` attached to ATE port-1 and port-2 +* Configure IPv4 and IPv6 IS-IS between DUT Port-2 and ATE Port-2 + * /network-instances/network-instance/protocols/protocol/isis/global/config + * Advertise ```ipv4-network-1 = 192.168.10.0/24``` and ```ipv6-network-1 = 2024:db8:64:64::/64``` from ATE to DUT over the IPv4 and IPv6 IS-IS session on port-2 + +### RT-1.34.1 [TODO:https://github.com/openconfig/featureprofiles/issues/3050] +#### Validate traffic with modified eBGP Route-Distance of 5 +* Configure IPv4 and IPv6 eBGP between DUT Port-1 and ATE Port-1 + * /network-instances/network-instance/protocols/protocol/bgp/global/config + * /network-instances/network-instance/protocols/protocol/bgp/global/afi-safis/afi-safi/config/ + * Advertise ```ipv4-network-1 = 192.168.10.0/24``` and ```ipv6-network-1 = 2024:db8:64:64::/64``` from ATE to DUT over the IPv4 and IPv6 eBGP session on port-1 +* Configure Route-Distance of eBGP session on port-1 to 5 + * /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/config/external-route-distance +* Validate using gNMI Subscribe with mode 'ONCE' that the correct Route-Distance value of 5 is reported: + * /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/state/external-route-distance +* Generate traffic from ATE port-3 towards ```ipv4-network-1 = 192.168.10.0/24``` and ```ipv6-network-1 = 2024:db8:64:64::/64``` +* Verify that the traffic is received on port-1 of the ATE + +### RT-1.34.2 [TODO:https://github.com/openconfig/featureprofiles/issues/3050] +#### Validate traffic with modified eBGP Route-Distance of 250 +* Configure Route-Distance of eBGP session on port-1 to 250 + * /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/config/external-route-distance +* Validate using gNMI Subscribe with mode 'ONCE' that the correct Route-Distance value of 250 is reported: + * /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/state/external-route-distance +* Generate traffic from ATE port-3 towards ```ipv4-network-1 = 192.168.10.0/24``` and ```ipv6-network-1 = 2024:db8:64:64::/64``` +* Verify that the traffic is received on port-2 of the ATE + +### RT-1.34.3 [TODO:https://github.com/openconfig/featureprofiles/issues/3050] +#### Validate traffic with modified iBGP Route-Distance of 5 +* Replace IPv4 and IPv6 eBGP with IPv4 and IPv6 iBGP between DUT Port-1 and ATE Port-1 + * /network-instances/network-instance/protocols/protocol/bgp/global/config + * /network-instances/network-instance/protocols/protocol/bgp/global/afi-safis/afi-safi/config/ + * Advertise ```ipv4-network-1 = 192.168.10.0/24``` and ```ipv6-network-1 = 2024:db8:64:64::/64``` from ATE to DUT over the IPv4 and IPv6 iBGP session on port-1 +* Configure Route-Distance of iBGP session on port-1 to 5 + * /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/config/internal-route-distance +* Validate using gNMI Subscribe with mode 'ONCE' that the correct Route-Distance value of 5 is reported: + * /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/state/internal-route-distance +* Generate traffic from ATE port-3 towards ```ipv4-network-1 = 192.168.10.0/24``` and ```ipv6-network-1 = 2024:db8:64:64::/64``` +* Validate that the traffic is received on port-1 of the ATE + +### RT-1.34.4 [TODO:https://github.com/openconfig/featureprofiles/issues/3050] +#### Validate traffic with modified iBGP Route-Distance of 250 +* Configure Route-Distance of iBGP session on port-1 to 250 + * /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/config/internal-route-distance +* Validate using gNMI Subscribe with mode 'ONCE' that the correct Route-Distance value of 250 is reported: + * /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/state/internal-route-distance +* Generate traffic from ATE port-3 towards ```ipv4-network-1 = 192.168.10.0/24``` and ```ipv6-network-1 = 2024:db8:64:64::/64``` +* Validate that the traffic is received on port-2 of the ATE + +## OpenConfig Path and RPC Coverage + +The below yaml defines the OC paths intended to be covered by this test. OC +paths used for test setup are not listed here. + +```yaml +paths: + ## Config paths + ### Route-Distance + /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/config/external-route-distance: + /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/config/internal-route-distance: + + ## State paths + ### Route-Distance + /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/state/internal-route-distance: + /network-instances/network-instance/protocols/protocol/bgp/global/default-route-distance/state/external-route-distance: + +rpcs: + gnmi: + gNMI.Set: + gNMI.Subscribe: +``` diff --git a/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/README.md b/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/README.md index 093bcf0895c..5a9fe642af5 100644 --- a/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/README.md +++ b/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/README.md @@ -355,3 +355,57 @@ ## Required DUT platform * FFF + +## OpenConfig Path and RPC Coverage + +The below yaml defines the OC paths intended to be covered by this test. OC +paths used for test setup are not listed here. + +```yaml +paths: + ## Config paths + /network-instances/network-instance/protocols/protocol/isis/global/config/level-capability: + /network-instances/network-instance/protocols/protocol/isis/levels/level/config/metric-style: + /routing-policy/policy-definitions/policy-definition/config/name: + /routing-policy/policy-definitions/policy-definition/statements/statement/config/name: + /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result: + /routing-policy/policy-definitions/policy-definition/statements/statement/actions/isis-actions/config/set-level: + /routing-policy/defined-sets/prefix-sets/prefix-set/config/name: + /routing-policy/defined-sets/prefix-sets/prefix-set/config/mode: + /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/config/ip-prefix: + /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/config/masklength-range: + /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/config/match-set-options: + /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/config/prefix-set: + /routing-policy/defined-sets/bgp-defined-sets/community-sets/community-set/config/community-set-name: + /routing-policy/defined-sets/bgp-defined-sets/community-sets/community-set/config/community-member: + /routing-policy/policy-definitions/policy-definition/statements/statement/actions/bgp-actions/set-community/reference/config/community-set-ref: + /network-instances/network-instance/table-connections/table-connection/config/address-family: + /network-instances/network-instance/table-connections/table-connection/config/src-protocol: + /network-instances/network-instance/table-connections/table-connection/config/dst-protocol: + /network-instances/network-instance/table-connections/table-connection/config/disable-metric-propagation: + /network-instances/network-instance/table-connections/table-connection/config/import-policy: + + ## State paths + /routing-policy/policy-definitions/policy-definition/state/name: + /routing-policy/policy-definitions/policy-definition/statements/statement/state/name: + /routing-policy/policy-definitions/policy-definition/statements/statement/actions/state/policy-result: + /routing-policy/policy-definitions/policy-definition/statements/statement/actions/isis-actions/state/set-level: + /routing-policy/defined-sets/prefix-sets/prefix-set/state/name: + /routing-policy/defined-sets/prefix-sets/prefix-set/state/mode: + /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/state/ip-prefix: + /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/state/masklength-range: + /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/state/match-set-options: + /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/state/prefix-set: + /network-instances/network-instance/table-connections/table-connection/state/import-policy: + /network-instances/network-instance/table-connections/table-connection/state/address-family: + /network-instances/network-instance/table-connections/table-connection/state/src-protocol: + /network-instances/network-instance/table-connections/table-connection/state/dst-protocol: + /network-instances/network-instance/table-connections/table-connection/state/disable-metric-propagation: + /routing-policy/defined-sets/bgp-defined-sets/community-sets/community-set/state/community-set-name: + /routing-policy/defined-sets/bgp-defined-sets/community-sets/community-set/state/community-member: + +rpcs: + gnmi: + gNMI.Set: + gNMI.Subscribe: +``` diff --git a/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/bgp_isis_redistribution_test.go b/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/bgp_isis_redistribution_test.go index c4430ea1534..00ff8bae5f0 100644 --- a/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/bgp_isis_redistribution_test.go +++ b/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/bgp_isis_redistribution_test.go @@ -25,6 +25,7 @@ import ( "github.com/openconfig/featureprofiles/internal/cfgplugins" "github.com/openconfig/featureprofiles/internal/deviations" "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/helpers" "github.com/openconfig/featureprofiles/internal/isissession" "github.com/openconfig/featureprofiles/internal/otgutils" "github.com/openconfig/ondatra" @@ -36,31 +37,37 @@ import ( ) const ( - bgpName = "BGP" - maskLenExact = "exact" - dummyAS = uint32(64655) - dutAS = uint32(64656) - ateAS = uint32(64657) - v4Route = "203.10.113.0" - v4TrafficStart = "203.10.113.1" - v4DummyRoute = "192.51.100.0" - v4RoutePrefix = uint32(24) - v6Route = "2001:db8:128:128:0:0:0:0" - v6TrafficStart = "2001:db8:128:128:0:0:0:1" - v6DummyRoute = "2001:db8:128:129:0:0:0:0" - v6RoutePrefix = uint32(64) - v4RoutePolicy = "route-policy-v4" - v4Statement = "statement-v4" - v4PrefixSet = "prefix-set-v4" - v4FlowName = "flow-v4" - v4CommunitySet = "community-set-v4" - v6RoutePolicy = "route-policy-v6" - v6Statement = "statement-v6" - v6PrefixSet = "prefix-set-v6" - v6FlowName = "flow-v6" - v6CommunitySet = "community-set-v6" - peerGrpNamev4 = "BGP-PEER-GROUP-V4" - peerGrpNamev6 = "BGP-PEER-GROUP-V6" + bgpName = "BGP" + maskLenExact = "exact" + dummyAS = uint32(64655) + dutAS = uint32(64656) + ateAS = uint32(64657) + v4Route = "203.10.113.0" + v4TrafficStart = "203.10.113.1" + v4DummyRoute = "192.51.100.0" + v4RoutePrefix = uint32(24) + v6Route = "2001:db8:128:128:0:0:0:0" + v6TrafficStart = "2001:db8:128:128:0:0:0:1" + v6DummyRoute = "2001:db8:128:129:0:0:0:0" + v6RoutePrefix = uint32(64) + v4RoutePolicy = "route-policy-v4" + v4Statement = "statement-v4" + v4PrefixSet = "prefix-set-v4" + v4FlowName = "flow-v4" + v4CommunitySet = "community-set-v4" + v6RoutePolicy = "route-policy-v6" + v6Statement = "statement-v6" + v6PrefixSet = "prefix-set-v6" + v6FlowName = "flow-v6" + v6CommunitySet = "community-set-v6" + peerGrpNamev4 = "BGP-PEER-GROUP-V4" + peerGrpNamev6 = "BGP-PEER-GROUP-V6" + allowAllPolicy = "ALLOWAll" + tablePolicyMatchCommunitySetTag = "TablePolicyMatchCommunitySetTag" + matchTagRedistributionPolicy = "MatchTagRedistributionPolicy" + nonMatchingCommunityVal = "64655:200" + matchingCommunityVal = "64657:100" + routeTagVal = 10000 ) var ( @@ -96,13 +103,13 @@ type testCase struct { ipv4 bool } -func TestBGPToBGPRedistribution(t *testing.T) { +func TestBGPToISISRedistribution(t *testing.T) { ts := isissession.MustNew(t).WithISIS() t.Run("ISIS Setup", func(t *testing.T) { ts.PushAndStart(t) ts.MustAdjacency(t) }) - + configureRoutePolicyAllow(t, ts.DUT, allowAllPolicy, oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) setupEBGPAndAdvertise(t, ts) t.Run("BGP Setup", func(t *testing.T) { t.Log("Verify DUT BGP sessions up") @@ -200,7 +207,7 @@ func TestBGPToBGPRedistribution(t *testing.T) { // setupEBGPAndAdvertise setups eBGP on DUT port1 and ATE port1 func setupEBGPAndAdvertise(t *testing.T, ts *isissession.TestSession) { t.Helper() - + dut := ondatra.DUT(t, "dut") // setup eBGP on DUT port2 root := &oc.Root{} dni := root.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(ts.DUT)) @@ -220,6 +227,26 @@ func setupEBGPAndAdvertise(t *testing.T, ts *isissession.TestSession) { pgv4.PeerGroupName = ygot.String(peerGrpNamev4) pgv6 := bgp.GetOrCreatePeerGroup(peerGrpNamev6) pgv6.PeerGroupName = ygot.String(peerGrpNamev6) + if deviations.RoutePolicyUnderAFIUnsupported(dut) { + rpl := pgv4.GetOrCreateApplyPolicy() + rpl.SetExportPolicy([]string{allowAllPolicy}) + rpl.SetImportPolicy([]string{allowAllPolicy}) + rplv6 := pgv6.GetOrCreateApplyPolicy() + rplv6.SetExportPolicy([]string{"ALLOW"}) + rplv6.SetImportPolicy([]string{"ALLOW"}) + } else { + pg1af4 := pgv4.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) + pg1af4.Enabled = ygot.Bool(true) + pg1rpl4 := pg1af4.GetOrCreateApplyPolicy() + pg1rpl4.SetExportPolicy([]string{allowAllPolicy}) + pg1rpl4.SetImportPolicy([]string{allowAllPolicy}) + pg1af6 := pgv6.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) + pg1af6.Enabled = ygot.Bool(true) + pg1rpl6 := pg1af6.GetOrCreateApplyPolicy() + pg1rpl6.SetExportPolicy([]string{allowAllPolicy}) + pg1rpl6.SetImportPolicy([]string{allowAllPolicy}) + } + nV4 := bgp.GetOrCreateNeighbor(isissession.ATETrafficAttrs.IPv4) nV4.SetPeerAs(ateAS) nV4.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) @@ -302,39 +329,49 @@ func matchingPrefixRoutePolicy(t *testing.T, dut *ondatra.DUTDevice) { } func nonMatchingCommunityRoutePolicy(t *testing.T, dut *ondatra.DUTDevice) { - root := &oc.Root{} - rp := root.GetOrCreateRoutingPolicy() - pdef := rp.GetOrCreatePolicyDefinition(v4RoutePolicy) - stmt, err := pdef.AppendNewStatement(v4Statement) - if err != nil { - t.Fatalf("AppendNewStatement(%s) failed: %v", v4Statement, err) - } - stmt.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) - if !deviations.SkipIsisSetLevel(dut) { - stmt.GetOrCreateActions().GetOrCreateIsisActions().SetSetLevel(2) - } - if !deviations.SkipIsisSetMetricStyleType(dut) { - stmt.GetOrCreateActions().GetOrCreateIsisActions().SetSetMetricStyleType(oc.IsisPolicy_MetricStyle_WIDE_METRIC) - } + if deviations.CommunityMatchWithRedistributionUnsupported(dut) { + configureBGPTablePolicyWithSetTag(t, v4PrefixSet, advertisedIPv4.cidr(t), v4CommunitySet, dummyAS, 200, true) + bgpISISRedistributionWithRouteTagPolicy(t, dut, oc.Types_ADDRESS_FAMILY_IPV4) + } else { + root := &oc.Root{} + rp := root.GetOrCreateRoutingPolicy() + pdef := rp.GetOrCreatePolicyDefinition(v4RoutePolicy) + stmt, err := pdef.AppendNewStatement(v4Statement) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", v4Statement, err) + } + stmt.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + if !deviations.SkipIsisSetLevel(dut) { + stmt.GetOrCreateActions().GetOrCreateIsisActions().SetSetLevel(2) + } + if !deviations.SkipIsisSetMetricStyleType(dut) { + stmt.GetOrCreateActions().GetOrCreateIsisActions().SetSetMetricStyleType(oc.IsisPolicy_MetricStyle_WIDE_METRIC) + } - communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(v4CommunitySet) - communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(fmt.Sprintf("%d:%d", dummyAS, 200))}) - communitySet.SetMatchSetOptions(oc.BgpPolicy_MatchSetOptionsType_ANY) + communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(v4CommunitySet) + communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(fmt.Sprintf("%d:%d", dummyAS, 200))}) + communitySet.SetMatchSetOptions(oc.BgpPolicy_MatchSetOptionsType_ANY) - if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { - stmt.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(v4CommunitySet) - } else { - stmt.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet().SetCommunitySet(v4CommunitySet) + if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { + stmt.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(v4CommunitySet) + } else { + stmt.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet().SetCommunitySet(v4CommunitySet) + } + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) } - gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) } func matchingCommunityRoutePolicy(t *testing.T, dut *ondatra.DUTDevice) { - root := &oc.Root{} - rp := root.GetOrCreateRoutingPolicy() - communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(v4CommunitySet) - communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(fmt.Sprintf("%d:%d", ateAS, 100))}) - gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().BgpDefinedSets().CommunitySet(v4CommunitySet).Config(), communitySet) + if deviations.CommunityMatchWithRedistributionUnsupported(dut) { + configureBGPTablePolicyWithSetTag(t, v4PrefixSet, advertisedIPv4.cidr(t), v4CommunitySet, ateAS, 100, true) + bgpISISRedistributionWithRouteTagPolicy(t, dut, oc.Types_ADDRESS_FAMILY_IPV4) + } else { + root := &oc.Root{} + rp := root.GetOrCreateRoutingPolicy() + communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(v4CommunitySet) + communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(fmt.Sprintf("%d:%d", ateAS, 100))}) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().BgpDefinedSets().CommunitySet(v4CommunitySet).Config(), communitySet) + } } func verifyNonMatchingPrefixTelemetry(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { @@ -422,9 +459,16 @@ func verifyNonMatchingCommunityTelemetry(t *testing.T, dut *ondatra.DUTDevice, a if commSet == nil { t.Errorf("Community set is nil, want non-nil") } - cm, _ := strconv.ParseInt(fmt.Sprintf("%04x%04x", dummyAS, 200), 16, 0) - if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionUint32(cm))) { - t.Errorf("Community set member: %v, want: %d", commSetMember, cm) + if deviations.BgpCommunityMemberIsAString(dut) { + cm := nonMatchingCommunityVal + if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionString(cm))) { + t.Errorf("Community set member: %v, want: %s", commSetMember, cm) + } + } else { + cm, _ := strconv.ParseInt(fmt.Sprintf("%04x%04x", dummyAS, 200), 16, 0) + if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionUint32(cm))) { + t.Errorf("Community set member: %v, want: %d", commSetMember, cm) + } } _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().ExtendedIpv4Reachability().Prefix(advertisedIPv4.address).State(), 30*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_ExtendedIpv4Reachability_Prefix]) bool { @@ -441,9 +485,16 @@ func verifyMatchingCommunityTelemetry(t *testing.T, dut *ondatra.DUTDevice, ate if commSet == nil { t.Errorf("Community set is nil, want non-nil") } - cm, _ := strconv.ParseInt(fmt.Sprintf("%04x%04x", ateAS, 100), 16, 0) - if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionUint32(cm))) { - t.Errorf("Community set member: %v, want: %v", commSetMember, cm) + if deviations.BgpCommunityMemberIsAString(dut) { + cm := matchingCommunityVal + if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionString(cm))) { + t.Errorf("Community set member: %v, want: %v", commSetMember, cm) + } + } else { + cm, _ := strconv.ParseInt(fmt.Sprintf("%04x%04x", ateAS, 100), 16, 0) + if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionUint32(cm))) { + t.Errorf("Community set member: %v, want: %v", commSetMember, cm) + } } _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().ExtendedIpv4Reachability().Prefix(advertisedIPv4.address).State(), 30*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_ExtendedIpv4Reachability_Prefix]) bool { @@ -494,39 +545,49 @@ func matchingPrefixRoutePolicyV6(t *testing.T, dut *ondatra.DUTDevice) { } func nonMatchingCommunityRoutePolicyV6(t *testing.T, dut *ondatra.DUTDevice) { - root := &oc.Root{} - rp := root.GetOrCreateRoutingPolicy() - pdef := rp.GetOrCreatePolicyDefinition(v6RoutePolicy) - stmt, err := pdef.AppendNewStatement(v6Statement) - if err != nil { - t.Fatalf("AppendNewStatement(%s) failed: %v", v6Statement, err) - } - stmt.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) - if !deviations.SkipIsisSetLevel(dut) { - stmt.GetOrCreateActions().GetOrCreateIsisActions().SetSetLevel(2) - } - if !deviations.SkipIsisSetMetricStyleType(dut) { - stmt.GetOrCreateActions().GetOrCreateIsisActions().SetSetMetricStyleType(oc.IsisPolicy_MetricStyle_WIDE_METRIC) - } + if deviations.CommunityMatchWithRedistributionUnsupported(dut) { + configureBGPTablePolicyWithSetTag(t, v6PrefixSet, advertisedIPv6.cidr(t), v6CommunitySet, dummyAS, 200, false) + bgpISISRedistributionWithRouteTagPolicy(t, dut, oc.Types_ADDRESS_FAMILY_IPV6) + } else { + root := &oc.Root{} + rp := root.GetOrCreateRoutingPolicy() + pdef := rp.GetOrCreatePolicyDefinition(v6RoutePolicy) + stmt, err := pdef.AppendNewStatement(v6Statement) + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", v6Statement, err) + } + stmt.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + if !deviations.SkipIsisSetLevel(dut) { + stmt.GetOrCreateActions().GetOrCreateIsisActions().SetSetLevel(2) + } + if !deviations.SkipIsisSetMetricStyleType(dut) { + stmt.GetOrCreateActions().GetOrCreateIsisActions().SetSetMetricStyleType(oc.IsisPolicy_MetricStyle_WIDE_METRIC) + } - communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(v6CommunitySet) - communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(fmt.Sprintf("%d:%d", dummyAS, 200))}) - communitySet.SetMatchSetOptions(oc.BgpPolicy_MatchSetOptionsType_ANY) + communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(v6CommunitySet) + communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(fmt.Sprintf("%d:%d", dummyAS, 200))}) + communitySet.SetMatchSetOptions(oc.BgpPolicy_MatchSetOptionsType_ANY) - if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { - stmt.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(v6CommunitySet) - } else { - stmt.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet().SetCommunitySet(v6CommunitySet) + if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { + stmt.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(v6CommunitySet) + } else { + stmt.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet().SetCommunitySet(v6CommunitySet) + } + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) } - gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) } func matchingCommunityRoutePolicyV6(t *testing.T, dut *ondatra.DUTDevice) { - root := &oc.Root{} - rp := root.GetOrCreateRoutingPolicy() - communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(v6CommunitySet) - communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(fmt.Sprintf("%d:%d", ateAS, 100))}) - gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().BgpDefinedSets().CommunitySet(v6CommunitySet).Config(), communitySet) + if deviations.CommunityMatchWithRedistributionUnsupported(dut) { + configureBGPTablePolicyWithSetTag(t, v6PrefixSet, advertisedIPv6.cidr(t), v6CommunitySet, ateAS, 100, false) + bgpISISRedistributionWithRouteTagPolicy(t, dut, oc.Types_ADDRESS_FAMILY_IPV6) + } else { + root := &oc.Root{} + rp := root.GetOrCreateRoutingPolicy() + communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(v6CommunitySet) + communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(fmt.Sprintf("%d:%d", ateAS, 100))}) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().BgpDefinedSets().CommunitySet(v6CommunitySet).Config(), communitySet) + } } func verifyNonMatchingPrefixTelemetryV6(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice) { @@ -581,7 +642,7 @@ func verifyNonMatchingPrefixTelemetryV6(t *testing.T, dut *ondatra.DUTDevice, at t.Errorf("Import policy: %v, want: %s", importPolicy, []string{v6RoutePolicy}) } - _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().Ipv6Reachability().Prefix(advertisedIPv6.address).State(), 30*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_Ipv6Reachability_Prefix]) bool { + _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().Ipv6Reachability().Prefix(advertisedIPv6.address).State(), 60*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_Ipv6Reachability_Prefix]) bool { prefix, present := v.Val() return present && prefix.GetPrefix() == advertisedIPv6.address }).Await(t) @@ -600,7 +661,7 @@ func verifyMatchingPrefixTelemetryV6(t *testing.T, dut *ondatra.DUTDevice, ate * t.Errorf("Prefix is nil, want: %s", advertisedIPv6.cidr(t)) } - _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().Ipv6Reachability().Prefix(advertisedIPv6.address).State(), 30*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_Ipv6Reachability_Prefix]) bool { + _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().Ipv6Reachability().Prefix(advertisedIPv6.address).State(), 60*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_Ipv6Reachability_Prefix]) bool { prefix, present := v.Val() return present && prefix.GetPrefix() == advertisedIPv6.address }).Await(t) @@ -614,12 +675,19 @@ func verifyNonMatchingCommunityTelemetryV6(t *testing.T, dut *ondatra.DUTDevice, if commSet == nil { t.Errorf("Community set is nil, want non-nil") } - cm, _ := strconv.ParseInt(fmt.Sprintf("%04x%04x", dummyAS, 200), 16, 0) - if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionUint32(cm))) { - t.Errorf("Community set member: %v, want: %d", commSetMember, cm) + if deviations.BgpCommunityMemberIsAString(dut) { + cm := nonMatchingCommunityVal + if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionString(cm))) { + t.Errorf("Community set member: %v, want: %v", commSetMember, cm) + } + } else { + cm, _ := strconv.ParseInt(fmt.Sprintf("%04x%04x", dummyAS, 200), 16, 0) + if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionUint32(cm))) { + t.Errorf("Community set member: %v, want: %d", commSetMember, cm) + } } - _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().Ipv6Reachability().Prefix(advertisedIPv6.address).State(), 30*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_Ipv6Reachability_Prefix]) bool { + _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().Ipv6Reachability().Prefix(advertisedIPv6.address).State(), 60*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_Ipv6Reachability_Prefix]) bool { prefix, present := v.Val() return present && prefix.GetPrefix() == advertisedIPv6.address }).Await(t) @@ -633,12 +701,19 @@ func verifyMatchingCommunityTelemetryV6(t *testing.T, dut *ondatra.DUTDevice, at if commSet == nil { t.Errorf("Community set is nil, want non-nil") } - cm, _ := strconv.ParseInt(fmt.Sprintf("%04x%04x", ateAS, 100), 16, 0) - if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionUint32(cm))) { - t.Errorf("Community set member: %v, want: %v", commSetMember, cm) + if deviations.BgpCommunityMemberIsAString(dut) { + cm := matchingCommunityVal + if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionString(cm))) { + t.Errorf("Community set member: %v, want: %v", commSetMember, cm) + } + } else { + cm, _ := strconv.ParseInt(fmt.Sprintf("%04x%04x", ateAS, 100), 16, 0) + if commSetMember := commSet.GetCommunityMember(); len(commSetMember) == 0 || !containsValue(commSetMember, oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union(oc.UnionUint32(cm))) { + t.Errorf("Community set member: %v, want: %v", commSetMember, cm) + } } - _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().Ipv6Reachability().Prefix(advertisedIPv6.address).State(), 30*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_Ipv6Reachability_Prefix]) bool { + _, ok := gnmi.WatchAll(t, ate.OTG(), gnmi.OTG().IsisRouter("devIsis").LinkStateDatabase().LspsAny().Tlvs().Ipv6Reachability().Prefix(advertisedIPv6.address).State(), 60*time.Second, func(v *ygnmi.Value[*otgtelemetry.IsisRouter_LinkStateDatabase_Lsps_Tlvs_Ipv6Reachability_Prefix]) bool { prefix, present := v.Val() return present && prefix.GetPrefix() == advertisedIPv6.address }).Await(t) @@ -654,7 +729,9 @@ func bgpISISRedistribution(t *testing.T, dut *ondatra.DUTDevice) { if !deviations.SkipSettingDisableMetricPropagation(dut) { tableConn.SetDisableMetricPropagation(false) } - tableConn.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + if !deviations.DefaultRoutePolicyUnsupported(dut) { + tableConn.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + } tableConn.SetImportPolicy([]string{v4RoutePolicy}) gnmi.Update(t, dut, gnmi.OC().NetworkInstance(dni).TableConnection(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, oc.Types_ADDRESS_FAMILY_IPV4).Config(), tableConn) } @@ -666,10 +743,86 @@ func bgpISISRedistributionV6(t *testing.T, dut *ondatra.DUTDevice) { if !deviations.SkipSettingDisableMetricPropagation(dut) { tableConn.SetDisableMetricPropagation(false) } - tableConn.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + if !deviations.DefaultRoutePolicyUnsupported(dut) { + tableConn.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + } tableConn.SetImportPolicy([]string{v6RoutePolicy}) gnmi.Update(t, dut, gnmi.OC().NetworkInstance(dni).TableConnection(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, oc.Types_ADDRESS_FAMILY_IPV6).Config(), tableConn) } +func bgpISISRedistributionWithRouteTagPolicy(t *testing.T, dut *ondatra.DUTDevice, afi oc.E_Types_ADDRESS_FAMILY) { + dni := deviations.DefaultNetworkInstance(dut) + root := &oc.Root{} + tableConn := root.GetOrCreateNetworkInstance(dni).GetOrCreateTableConnection(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, afi) + if !deviations.SkipSettingDisableMetricPropagation(dut) { + tableConn.SetDisableMetricPropagation(false) + } + tableConn.SetImportPolicy([]string{matchTagRedistributionPolicy}) + gnmi.Update(t, dut, gnmi.OC().NetworkInstance(dni).TableConnection(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, afi).Config(), tableConn) +} + +func configureBGPTablePolicyWithSetTag(t *testing.T, prefixSetName, prefixSetAddress, communitySetName string, commAS, commValue uint32, v4Nbr bool) { + dut := ondatra.DUT(t, "dut") + root := &oc.Root{} + rp := root.GetOrCreateRoutingPolicy() + //BGP Table-policy to match community & prefix and set the route-Tag + pdef1 := rp.GetOrCreatePolicyDefinition(tablePolicyMatchCommunitySetTag) + stmt1, err := pdef1.AppendNewStatement("SetTag") + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", "routePolicyStatement", err) + } + //Create prefix-set + prefixSet := rp.GetOrCreateDefinedSets().GetOrCreatePrefixSet(prefixSetName) + prefixSet.GetOrCreatePrefix(prefixSetAddress, maskLenExact) + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().DefinedSets().PrefixSet(prefixSetName).Config(), prefixSet) + //Create community-set + communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(communitySetName) + communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(fmt.Sprintf("%d:%d", commAS, commValue))}) + communitySet.SetMatchSetOptions(oc.BgpPolicy_MatchSetOptionsType_ANY) + + stmt1.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetPrefixSet(prefixSetName) + stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(communitySetName) + stmt1.GetOrCreateActions().GetOrCreateSetTag().SetMode(oc.SetTag_Mode_INLINE) + stmt1.GetOrCreateActions().GetOrCreateSetTag().GetOrCreateInline().SetTag([]oc.RoutingPolicy_PolicyDefinition_Statement_Actions_SetTag_Inline_Tag_Union{oc.UnionUint32(routeTagVal)}) + stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + + //Create tag-set with above route tag value + tagSet := rp.GetOrCreateDefinedSets().GetOrCreateTagSet("RouteTagForRedistribution") + tagSet.SetName("RouteTagForRedistribution") + tagSet.SetTagValue([]oc.RoutingPolicy_DefinedSets_TagSet_TagValue_Union{oc.UnionUint32(routeTagVal)}) + + //Route-policy to match tag and accept + pdef2 := rp.GetOrCreatePolicyDefinition("MatchTagRedistributionPolicy") + stmt2, err := pdef2.AppendNewStatement("matchTag") + stmt2.GetOrCreateConditions().GetOrCreateMatchPrefixSet().SetPrefixSet(prefixSetName) + stmt2.GetOrCreateConditions().GetOrCreateMatchTagSet().SetTagSet("RouteTagForRedistribution") + stmt2.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", "routePolicyStatement", err) + } + + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + var bgpTablePolicyCLI string + if v4Nbr { + bgpTablePolicyCLI = fmt.Sprintf("router bgp %v instance BGP address-family ipv4 unicast \n table-policy %v", dutAS, tablePolicyMatchCommunitySetTag) + helpers.GnmiCLIConfig(t, dut, bgpTablePolicyCLI) + } else { + bgpTablePolicyCLI = fmt.Sprintf("router bgp %v instance BGP address-family ipv6 unicast \n table-policy %v", dutAS, tablePolicyMatchCommunitySetTag) + helpers.GnmiCLIConfig(t, dut, bgpTablePolicyCLI) + } +} + +func configureRoutePolicyAllow(t *testing.T, dut *ondatra.DUTDevice, name string, pr oc.E_RoutingPolicy_PolicyResultType) { + d := &oc.Root{} + rp := d.GetOrCreateRoutingPolicy() + pd := rp.GetOrCreatePolicyDefinition(name) + st, err := pd.AppendNewStatement("id-1") + if err != nil { + t.Fatal(err) + } + st.GetOrCreateActions().PolicyResult = pr + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) +} func createFlow(t *testing.T, ts *isissession.TestSession) { ts.ATETop.Flows().Clear() @@ -693,6 +846,7 @@ func createFlow(t *testing.T, ts *isissession.TestSession) { ts.ATE.OTG().PushConfig(t, ts.ATETop) ts.ATE.OTG().StartProtocols(t) otgutils.WaitForARP(t, ts.ATE.OTG(), ts.ATETop, "IPv4") + cfgplugins.VerifyDUTBGPEstablished(t, ts.DUT) } func createFlowV6(t *testing.T, ts *isissession.TestSession) { @@ -717,6 +871,7 @@ func createFlowV6(t *testing.T, ts *isissession.TestSession) { ts.ATE.OTG().PushConfig(t, ts.ATETop) ts.ATE.OTG().StartProtocols(t) otgutils.WaitForARP(t, ts.ATE.OTG(), ts.ATETop, "IPv6") + cfgplugins.VerifyDUTBGPEstablished(t, ts.DUT) } func checkTraffic(t *testing.T, ts *isissession.TestSession, flowName string) { diff --git a/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/metadata.textproto b/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/metadata.textproto index a87078e24c0..f4552e8cdf0 100644 --- a/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/metadata.textproto +++ b/feature/bgp/bgp_isis_redistribution/otg_tests/bgp_isis_redistribution_test/metadata.textproto @@ -32,3 +32,16 @@ platform_exceptions: { isis_level_enabled: true } } +platform_exceptions: { + platform: { + vendor: CISCO + } + deviations: { + skip_isis_set_level: true + skip_isis_set_metric_style_type: true + bgp_conditions_match_community_set_unsupported: true + bgp_community_member_is_a_string: true + community_match_with_redistribution_unsupported: true + default_route_policy_unsupported: true + } +} diff --git a/feature/bgp/policybase/otg_tests/aspath_and_community_test/README.md b/feature/bgp/policybase/otg_tests/aspath_and_community_test/README.md index 683d2dec7fe..8f434b0bcb4 100644 --- a/feature/bgp/policybase/otg_tests/aspath_and_community_test/README.md +++ b/feature/bgp/policybase/otg_tests/aspath_and_community_test/README.md @@ -126,3 +126,53 @@ import-policy * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/state/prefixes/received-pre-policy * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/state/prefixes/received * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/state/prefixes/installed + +### OpenConfig Path and RPC Coverage +The below yaml defines the OC paths intended to be covered by this test. OC paths used for test setup are not listed here. + +```yaml +paths: + ## Config paths + ### Policy definition + /routing-policy/policy-definitions/policy-definition/config/name: + /routing-policy/policy-definitions/policy-definition/statements/statement/config/name: + ### Policy for community-set match + /routing-policy/defined-sets/bgp-defined-sets/community-sets/community-set/config/community-set-name: + /routing-policy/defined-sets/bgp-defined-sets/community-sets/community-set/config/community-member: + /routing-policy/defined-sets/bgp-defined-sets/community-sets/community-set/config/match-set-options: + /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/bgp-conditions/match-community-set/config/community-set: + /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/bgp-conditions/match-community-set/config/match-set-options: + /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/import-policy: + + ## State paths + ### Policy definition state + + /routing-policy/policy-definitions/policy-definition/state/name: + /routing-policy/policy-definitions/policy-definition/statements/statement/state/name: + + ### Policy for community-set match state + + /routing-policy/defined-sets/bgp-defined-sets/community-sets/community-set/state/community-set-name: + /routing-policy/defined-sets/bgp-defined-sets/community-sets/community-set/state/community-member: + /routing-policy/defined-sets/bgp-defined-sets/community-sets/community-set/state/match-set-options: + /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/bgp-conditions/state/community-set: + + ### Paths to verify policy state + + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/export-policy: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/import-policy: + + ### Paths to verify prefixes sent and received + + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/state/prefixes/sent: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/state/prefixes/received-pre-policy: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/state/prefixes/received: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/state/prefixes/installed: +rpcs: + gnmi: + gNMI.Set: + gNMI.Get: + gNMI.Subscribe: +``` + diff --git a/feature/bgp/policybase/otg_tests/aspath_and_community_test/aspath_and_community_test.go b/feature/bgp/policybase/otg_tests/aspath_and_community_test/aspath_and_community_test.go index 4394d2c4248..de109463b72 100644 --- a/feature/bgp/policybase/otg_tests/aspath_and_community_test/aspath_and_community_test.go +++ b/feature/bgp/policybase/otg_tests/aspath_and_community_test/aspath_and_community_test.go @@ -15,6 +15,8 @@ package aspath_and_community_test import ( + "fmt" + "strconv" "testing" "time" @@ -22,6 +24,7 @@ import ( "github.com/openconfig/featureprofiles/internal/cfgplugins" "github.com/openconfig/featureprofiles/internal/deviations" "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/helpers" "github.com/openconfig/featureprofiles/internal/otgutils" "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" @@ -34,6 +37,8 @@ const ( trafficPps = 100 totalPackets = 1200 bgpName = "BGP" + ImpPolicy = "routing-policy-ASPATH-COMMUNITY" + RPLPermitAll = "PERMIT-ALL" ) var prefixesV4 = [][]string{ @@ -46,37 +51,68 @@ var prefixesV4 = [][]string{ var prefixesV6 = [][]string{ {"2048:db1:64:64::0", "2048:db1:64:64::4"}, - {"2048:db1:64:64::8", "2048:db1:64:64::12"}, - {"2048:db1:64:64::16", "2048:db1:64:64::20"}, - {"2048:db1:64:64::24", "2048:db1:64:64::28"}, - {"2048:db1:64:64::32", "2048:db1:64:64::40"}, + {"2048:db1:64:64::8", "2048:db1:64:64::c"}, + {"2048:db1:64:64::10", "2048:db1:64:64::14"}, + {"2048:db1:64:64::18", "2048:db1:64:64::1c"}, + {"2048:db1:64:64::20", "2048:db1:64:64::24"}, } func TestMain(m *testing.M) { fptest.RunTests(m) } -func configureImportBGPPolicy(t *testing.T, dut *ondatra.DUTDevice, ipv4 string, ipv6 string) { - communitySetName := "path_and_community" - communityMatch := "10[0-9]:1" - commMatchSetOptions := oc.BgpPolicy_MatchSetOptionsType_ANY - aspathSetName := "any_my_3_comms" - aspathMatch := []string{"10[0-9]:1"} - aspMatchSetOptions := oc.RoutingPolicy_MatchSetOptionsType_ANY - +func configureImportBGPPolicy(t *testing.T, dut *ondatra.DUTDevice, ipv4 string, ipv6 string, aspathMatch []string, communityMatch string, aspathSetName string, communitySetName string, commMatchSetOptions oc.E_BgpPolicy_MatchSetOptionsType, aspMatchSetOptions oc.E_RoutingPolicy_MatchSetOptionsType) { root := &oc.Root{} rp := root.GetOrCreateRoutingPolicy() - pdef1 := rp.GetOrCreatePolicyDefinition("routePolicy") - stmt1, err := pdef1.AppendNewStatement("match_community") + pdef1 := rp.GetOrCreatePolicyDefinition(ImpPolicy) + stmt1, err := pdef1.AppendNewStatement("match_as_and_community") + if err != nil { + t.Fatalf("AppendNewStatement(%s) failed: %v", "any_my_aspath", err) + } + if !deviations.SkipSettingStatementForPolicy(dut) { + stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + } + + aspathSet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateAsPathSet(aspathSetName) + aspathSet.SetAsPathSetMember(aspathMatch) + stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchAsPathSet().SetAsPathSet(aspathSetName) + stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchAsPathSet().SetMatchSetOptions(aspMatchSetOptions) + pdAllow := rp.GetOrCreatePolicyDefinition(RPLPermitAll) + st, err := pdAllow.AppendNewStatement("id-1") if err != nil { - t.Fatalf("AppendNewStatement(%s) failed: %v", "match_community", err) + t.Fatalf("AppendNewStatement(%s) failed: %v", "routePolicyStatement", err) } + st.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) - stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + // stmt2, err := pdef1.AppendNewStatement("match_comms") + // if err != nil { + // t.Fatalf("AppendNewStatement(%s) failed: %v", "routePolicyStatement", err) + // } + if !deviations.SkipSettingStatementForPolicy(dut) { + stmt1.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) + } - communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(communitySetName) - communitySet.SetCommunityMember([]oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{oc.UnionString(communityMatch)}) - communitySet.SetMatchSetOptions(commMatchSetOptions) + if !(deviations.CommunityMemberRegexUnsupported(dut) && communitySetName == "any_my_3_comms") { + communitySet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(communitySetName) + cs := []oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{} + if communityMatch != "" { + cs = append(cs, oc.UnionString(communityMatch)) + } + communitySet.SetCommunityMember(cs) + communitySet.SetMatchSetOptions(commMatchSetOptions) + } + + var communitySetCLIConfig string + if deviations.CommunityMemberRegexUnsupported(dut) && communitySetName == "any_my_3_comms" { + switch dut.Vendor() { + case ondatra.CISCO: + communitySetCLIConfig = fmt.Sprintf("community-set %v\n ios-regex '(10[0-9]:1)'\n end-set", communitySetName) + default: + t.Fatalf("Unsupported vendor %s for deviation 'CommunityMemberRegexUnsupported'", dut.Vendor()) + } + helpers.GnmiCLIConfig(t, dut, communitySetCLIConfig) + } if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(communitySetName) @@ -84,29 +120,27 @@ func configureImportBGPPolicy(t *testing.T, dut *ondatra.DUTDevice, ipv4 string, stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet().SetCommunitySet(communitySetName) } - stmt2, err := pdef1.AppendNewStatement("match_as") - if err != nil { - t.Fatalf("AppendNewStatement(%s) failed: %v", "match_as", err) + if deviations.CommunityMemberRegexUnsupported(dut) && communitySetName == "any_my_3_comms" { + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + } else { + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) } - aspathSet := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateAsPathSet(aspathSetName) - aspathSet.SetAsPathSetMember(aspathMatch) - stmt2.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchAsPathSet().SetAsPathSet(aspathSetName) - stmt2.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchAsPathSet().SetMatchSetOptions(aspMatchSetOptions) - - gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) - dni := deviations.DefaultNetworkInstance(dut) pathV6 := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(ipv6).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).ApplyPolicy() policyV6 := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(ipv6).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).GetOrCreateApplyPolicy() - policyV6.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) - policyV6.SetImportPolicy([]string{"routePolicy"}) + if !deviations.DefaultRoutePolicyUnsupported(dut) { + policyV6.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + } + policyV6.SetImportPolicy([]string{ImpPolicy}) gnmi.Replace(t, dut, pathV6.Config(), policyV6) pathV4 := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(ipv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy() policyV4 := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(ipv4).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateApplyPolicy() - policyV4.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) - policyV4.SetImportPolicy([]string{"routePolicy"}) + if !deviations.DefaultRoutePolicyUnsupported(dut) { + policyV4.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + } + policyV4.SetImportPolicy([]string{ImpPolicy}) gnmi.Replace(t, dut, pathV4.Config(), policyV4) } @@ -142,18 +176,14 @@ func configureOTG(t *testing.T, bs *cfgplugins.BGPSession, prefixesV4 [][]string ipv4 := devices[1].Ethernets().Items()[0].Ipv4Addresses().Items()[0] bgp4Peer := devices[1].Bgp().Ipv4Interfaces().Items()[0].Peers().Items()[0] - bgp4PeerRoute := bgp4Peer.V4Routes().Add() - bgp4PeerRoute.SetName(bs.ATEPorts[1].Name + ".BGP4.peer.dut") - bgp4PeerRoute.SetNextHopIpv4Address(ipv4.Address()) - ipv6 := devices[1].Ethernets().Items()[0].Ipv6Addresses().Items()[0] bgp6Peer := devices[1].Bgp().Ipv6Interfaces().Items()[0].Peers().Items()[0] - bgp6PeerRoute := bgp6Peer.V6Routes().Add() - bgp6PeerRoute.SetName(bs.ATEPorts[1].Name + ".BGP6.peer.dut") - bgp6PeerRoute.SetNextHopIpv6Address(ipv6.Address()) - for index, prefixes := range prefixesV4 { + bgp4PeerRoute := bgp4Peer.V4Routes().Add() + bgp4PeerRoute.SetName(bs.ATEPorts[1].Name + ".BGP4.peer.dut." + strconv.Itoa(index)) + bgp4PeerRoute.SetNextHopIpv4Address(ipv4.Address()) + route4Address1 := bgp4PeerRoute.Addresses().Add().SetAddress(prefixes[0]) route4Address1.SetPrefix(prefixV4Len) route4Address2 := bgp4PeerRoute.Addresses().Add().SetAddress(prefixes[1]) @@ -162,6 +192,10 @@ func configureOTG(t *testing.T, bs *cfgplugins.BGPSession, prefixesV4 [][]string asp4 := bgp4PeerRoute.AsPath().Segments().Add() asp4.SetAsNumbers(aspathMembers[index]) + bgp6PeerRoute := bgp6Peer.V6Routes().Add() + bgp6PeerRoute.SetName(bs.ATEPorts[1].Name + ".BGP6.peer.dut." + strconv.Itoa(index)) + bgp6PeerRoute.SetNextHopIpv6Address(ipv6.Address()) + route6Address1 := bgp6PeerRoute.Addresses().Add().SetAddress(prefixesV6[index][0]) route6Address1.SetPrefix(prefixV6Len) route6Address2 := bgp6PeerRoute.Addresses().Add().SetAddress(prefixesV6[index][1]) @@ -186,24 +220,19 @@ func configureOTG(t *testing.T, bs *cfgplugins.BGPSession, prefixesV4 [][]string } } -func configureFlow(bs *cfgplugins.BGPSession, prefixPair []string, prefixType string) { - bs.ATETop.Flows().Clear() +func configureFlow(t *testing.T, bs *cfgplugins.BGPSession, prefixPair []string, prefixType string, index int) { - var rxNames []string - for i := 1; i < len(bs.ATEPorts); i++ { - rxNames = append(rxNames, bs.ATEPorts[i].Name+".BGP4.peer.dut") - } - flow := bs.ATETop.Flows().Add().SetName("flow") + flow := bs.ATETop.Flows().Add().SetName("flow" + prefixType + strconv.Itoa(index)) flow.Metrics().SetEnable(true) if prefixType == "ipv4" { flow.TxRx().Device(). - SetTxNames([]string{bs.ATEPorts[1].Name + ".IPv4"}). - SetRxNames(rxNames) + SetTxNames([]string{bs.ATEPorts[0].Name + ".IPv4"}). + SetRxNames([]string{bs.ATEPorts[1].Name + ".BGP4.peer.dut." + strconv.Itoa(index)}) } else { flow.TxRx().Device(). - SetTxNames([]string{bs.ATEPorts[1].Name + ".IPv6"}). - SetRxNames(rxNames) + SetTxNames([]string{bs.ATEPorts[0].Name + ".IPv6"}). + SetRxNames([]string{bs.ATEPorts[1].Name + ".BGP6.peer.dut." + strconv.Itoa(index)}) } flow.Duration().FixedPackets().SetPackets(totalPackets) @@ -215,18 +244,19 @@ func configureFlow(bs *cfgplugins.BGPSession, prefixPair []string, prefixType st if prefixType == "ipv4" { v4 := flow.Packet().Add().Ipv4() - v4.Src().SetValue(bs.ATEPorts[1].IPv4) + v4.Src().SetValue(bs.ATEPorts[0].IPv4) v4.Dst().SetValues(prefixPair) } else { v6 := flow.Packet().Add().Ipv6() - v6.Src().SetValue(bs.ATEPorts[1].IPv6) + v6.Src().SetValue(bs.ATEPorts[0].IPv6) v6.Dst().SetValues(prefixPair) } } -func verifyTraffic(t *testing.T, ate *ondatra.ATEDevice, ports int, testResults bool) { - framesTx := gnmi.Get[uint64](t, ate.OTG(), gnmi.OTG().Port(ate.Port(t, "port1").ID()).Counters().OutFrames().State()) - framesRx := gnmi.Get[uint64](t, ate.OTG(), gnmi.OTG().Port(ate.Port(t, "port2").ID()).Counters().InFrames().State()) +func verifyTraffic(t *testing.T, ate *ondatra.ATEDevice, prefixType string, testResults bool, index int) { + recvMetric := gnmi.Get(t, ate.OTG(), gnmi.OTG().Flow("flow"+prefixType+strconv.Itoa(index)).State()) + framesTx := recvMetric.GetCounters().GetOutPkts() + framesRx := recvMetric.GetCounters().GetInPkts() if framesTx == 0 { t.Error("No traffic was generated and frames transmitted were 0") @@ -253,26 +283,36 @@ func TestCommunitySet(t *testing.T) { ipv4 := bs.ATETop.Devices().Items()[1].Ethernets().Items()[0].Ipv4Addresses().Items()[0].Address() ipv6 := bs.ATETop.Devices().Items()[1].Ethernets().Items()[0].Ipv6Addresses().Items()[0].Address() - - configureImportBGPPolicy(t, bs.DUT, ipv4, ipv6) + aspathMatch := []string{"(10[0-9]|200)"} + communityMatch := "(10[0-9]:1)" + communitySetName := "any_my_3_comms" + aspathSetName := "any_my_aspath" + commMatchSetOptions := oc.BgpPolicy_MatchSetOptionsType_ANY + aspMatchSetOptions := oc.RoutingPolicy_MatchSetOptionsType_ANY + configureImportBGPPolicy(t, bs.DUT, ipv4, ipv6, aspathMatch, communityMatch, aspathSetName, communitySetName, commMatchSetOptions, aspMatchSetOptions) sleepTime := time.Duration(totalPackets/trafficPps) + 5 for index, prefixPairV4 := range prefixesV4 { - + bs.ATETop.Flows().Clear() t.Logf("Running traffic test for IPv4 prefixes: [%s, %s]. Expected Result: [%t]", prefixPairV4[0], prefixPairV4[1], testResults[index]) - configureFlow(bs, prefixPairV4, "ipv4") + configureFlow(t, bs, prefixPairV4, "ipv4", index) + bs.PushAndStartATE(t) + time.Sleep(sleepTime * time.Second) bs.ATE.OTG().StartTraffic(t) time.Sleep(sleepTime * time.Second) bs.ATE.OTG().StopTraffic(t) otgutils.LogFlowMetrics(t, bs.ATE.OTG(), bs.ATETop) - verifyTraffic(t, bs.ATE, int(cfgplugins.PortCount2), testResults[index]) + verifyTraffic(t, bs.ATE, "ipv4", testResults[index], index) + bs.ATETop.Flows().Clear() t.Logf("Running traffic test for IPv6 prefixes: [%s, %s]. Expected Result: [%t]", prefixesV6[index][0], prefixesV6[index][1], testResults[index]) - configureFlow(bs, prefixesV6[index], "ipv6") + configureFlow(t, bs, prefixesV6[index], "ipv6", index) + bs.PushAndStartATE(t) + time.Sleep(sleepTime * time.Second) bs.ATE.OTG().StartTraffic(t) time.Sleep(sleepTime * time.Second) bs.ATE.OTG().StopTraffic(t) otgutils.LogFlowMetrics(t, bs.ATE.OTG(), bs.ATETop) - verifyTraffic(t, bs.ATE, int(cfgplugins.PortCount2), testResults[index]) + verifyTraffic(t, bs.ATE, "ipv6", testResults[index], index) } } diff --git a/feature/bgp/policybase/otg_tests/aspath_and_community_test/metadata.textproto b/feature/bgp/policybase/otg_tests/aspath_and_community_test/metadata.textproto index 9aa803a9f83..a3023b9be66 100644 --- a/feature/bgp/policybase/otg_tests/aspath_and_community_test/metadata.textproto +++ b/feature/bgp/policybase/otg_tests/aspath_and_community_test/metadata.textproto @@ -1,6 +1,7 @@ # proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto # proto-message: Metadata + uuid: "61f4c5b2-e188-4cc8-9b8b-c744660db2f0" plan_id: "RT-7.4" description: "BGP Policy AS Path Set and Community Set" @@ -21,3 +22,17 @@ platform_exceptions: { bgp_conditions_match_community_set_unsupported: true } } + +platform_exceptions: { + platform: { + vendor: CISCO + } + deviations: { + bgp_conditions_match_community_set_unsupported: true + skip_setting_statement_for_policy: true + default_import_export_policy: true + community_member_regex_unsupported: true + default_route_policy_unsupported: true + } +} + diff --git a/feature/bgp/policybase/otg_tests/default_policies_test/README.md b/feature/bgp/policybase/otg_tests/default_policies_test/README.md index 336c32be788..eb0d2ead415 100644 --- a/feature/bgp/policybase/otg_tests/default_policies_test/README.md +++ b/feature/bgp/policybase/otg_tests/default_policies_test/README.md @@ -96,44 +96,34 @@ B <-- IBGP+IS-IS --> C[Port2:OTG]; * DUT:Port2 wouldn't export routes to IPv4-prefix1, IPv4-prefix2, IPv4-prefix3, IPv6-prefix1, IPv6-prefix2 and IPv6-prefix3 since they are missing from the DUT's forwarding table. * IS-IS and static routes shouldn't be advertised to the EBGP and IBGP peers. -### Config Parameter Coverage - * Defined Sets - * /routing-policy/defined-sets/prefix-sets/prefix-set/ - * /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/config/ip-prefix - * /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/config/masklength-range/exact - - * Policy-Definition - * /routing-policy/policy-definitions/policy-definition/config/name - * /routing-policy/policy-definitions/policy-definition/statements/statement/config/name - * /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/config/prefix-set - * /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/config/match-set-options - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result/ACCEPT_ROUTE - * /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result/REJECT_ROUTE - - * Path to Neighbor or Peer-Group level - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/ - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/peer-group/ - - * Apply Policy at Neighbor or Peer-Group level - * afi-safis/afi-safi/apply-policy/config/import-policy - * afi-safis/afi-safi/apply-policy/config/export-policy - * afi-safis/afi-safi/apply-policy/config/default-import-policy/ACCEPT-ALL - * afi-safis/afi-safi/apply-policy/config/default-export-policy/ACCEPT-ALL - * afi-safis/afi-safi/apply-policy/config/default-import-policy/REJECT-ALL - * afi-safis/afi-safi/apply-policy/config/default-export-policy/REJECT-ALL - - -### Telemetry Parameter Coverage - - * Path to Neighbor or Peer-Group level: - * /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor - * /network-instances/network-instance/protocols/protocol/bgp/peer-groups/peer-group - - * Paths under Neighbor and Peer-Group level: - * afi-safis/afi-safi/apply-policy/state/export-policy - * afi-safis/afi-safi/apply-policy/state/import-policy - * afi-safis/afi-safi/state/prefixes/installed - * afi-safis/afi-safi/state/prefixes/received - * afi-safis/afi-safi/state/prefixes/received-pre-policy - * afi-safis/afi-safi/state/prefixes/sent +## OpenConfig Path and RPC Coverage +```yaml +paths: + # Defined Sets + /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/config/ip-prefix: + /routing-policy/defined-sets/prefix-sets/prefix-set/prefixes/prefix/config/masklength-range: + # Policy-Definition + /routing-policy/policy-definitions/policy-definition/config/name: + /routing-policy/policy-definitions/policy-definition/statements/statement/config/name: + /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/config/prefix-set: + /routing-policy/policy-definitions/policy-definition/statements/statement/conditions/match-prefix-set/config/match-set-options: + /routing-policy/policy-definitions/policy-definition/statements/statement/actions/config/policy-result: + # Apply Policy at Neighbor or Peer-Group level + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/import-policy: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/export-policy: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-import-policy: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/config/default-export-policy: + # Paths under Neighbor and Peer-Group level + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/export-policy: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/apply-policy/state/import-policy: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/state/prefixes/installed: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/state/prefixes/received: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/state/prefixes/received-pre-policy: + /network-instances/network-instance/protocols/protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/state/prefixes/sent: +rpcs: + gnmi: + gNMI.Get: + gNMI.Set: + gNMI.Subscribe: +``` diff --git a/feature/bgp/policybase/otg_tests/default_policies_test/bgp_default_policies_test.go b/feature/bgp/policybase/otg_tests/default_policies_test/bgp_default_policies_test.go index 3e20da18c68..cd29632a05a 100644 --- a/feature/bgp/policybase/otg_tests/default_policies_test/bgp_default_policies_test.go +++ b/feature/bgp/policybase/otg_tests/default_policies_test/bgp_default_policies_test.go @@ -15,8 +15,6 @@ package bgp_default_policies_test import ( - "context" - "encoding/json" "fmt" "testing" "time" @@ -26,7 +24,6 @@ import ( "github.com/openconfig/featureprofiles/internal/attrs" "github.com/openconfig/featureprofiles/internal/deviations" "github.com/openconfig/featureprofiles/internal/fptest" - gpb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" "github.com/openconfig/ondatra/gnmi/oc" @@ -42,54 +39,54 @@ func TestMain(m *testing.M) { } const ( - peerGrpName1 = "BGP-PEER-GROUP1" - peerGrpName2 = "BGP-PEER-GROUP2" - peerGrpName3 = "BGP-PEER-GROUP3" - peerGrpName4 = "BGP-PEER-GROUP4" - dutAS = 65501 - ateAS = 65502 - plenIPv4 = 30 - plenIPv6 = 126 - dutAreaAddress = "49.0001" - dutSysID = "1920.0000.2001" - otgSysID2 = "640000000001" - isisInstance = "DEFAULT" - otgIsisPort2LoopV4 = "203.0.113.10" - otgIsisPort2LoopV6 = "2001:db8::203:0:113:10" - v4Prefixes = true - rejectAll = "REJECT-ALL" - rejectRoute = oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE - acceptRoute = oc.RoutingPolicy_DefaultPolicyType_ACCEPT_ROUTE - ebgpImportIPv4 = "EBGP-IMPORT-IPV4" - ebgpImportIPv6 = "EBGP-IMPORT-IPV6" - ebgpExportIPv4 = "EBGP-EXPORT-IPV4" - ebgpExportIPv6 = "EBGP-EXPORT-IPV6" - ibgpImportIPv4 = "IBGP-IMPORT-IPV4" - ibgpImportIPv6 = "IBGP-IMPORT-IPV6" - ibgpExportIPv4 = "IBGP-EXPORT-IPV4" - ibgpExportIPv6 = "IBGP-EXPORT-IPV6" - maskLengthRange32 = "32..32" - maskLengthRange128 = "128..128" - maskLen32 = "32" - maskLen128 = "128" - ipv4Prefix1 = "198.51.100.1" - ipv4Prefix2 = "198.51.100.2" - ipv4Prefix3 = "198.51.100.3" - ipv4Prefix4 = "198.51.100.4" - ipv4Prefix5 = "198.51.100.5" - ipv4Prefix6 = "198.51.100.6" - ipv4Prefix7 = "198.51.100.7" - ipv4Prefix8 = "198.51.100.8" - ipv6Prefix1 = "2001:DB8:2::1" - ipv6Prefix2 = "2001:DB8:2::2" - ipv6Prefix3 = "2001:DB8:2::3" - ipv6Prefix4 = "2001:DB8:2::4" - ipv6Prefix5 = "2001:DB8:2::5" - ipv6Prefix6 = "2001:DB8:2::6" - ipv6Prefix7 = "2001:DB8:2::7" - ipv6Prefix8 = "2001:DB8:2::8" - maskLenExact = "exact" - replacePolicy = true + peerGrpName1 = "BGP-PEER-GROUP1" + peerGrpName2 = "BGP-PEER-GROUP2" + peerGrpName3 = "BGP-PEER-GROUP3" + peerGrpName4 = "BGP-PEER-GROUP4" + dutAS = 65501 + ateAS = 65502 + plenIPv4 = 30 + plenIPv6 = 126 + dutAreaAddress = "49.0001" + dutSysID = "1920.0000.2001" + otgSysID2 = "640000000001" + isisInstance = "DEFAULT" + otgIsisPort2LoopV4 = "203.0.113.10" + otgIsisPort2LoopV6 = "2001:db8::203:0:113:10" + v4Prefixes = true + rejectAll = "REJECT-ALL" + rejectRoute = oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE + acceptRoute = oc.RoutingPolicy_DefaultPolicyType_ACCEPT_ROUTE + ebgpImportIPv4 = "EBGP-IMPORT-IPV4" + ebgpImportIPv6 = "EBGP-IMPORT-IPV6" + ebgpExportIPv4 = "EBGP-EXPORT-IPV4" + ebgpExportIPv6 = "EBGP-EXPORT-IPV6" + ibgpImportIPv4 = "IBGP-IMPORT-IPV4" + ibgpImportIPv6 = "IBGP-IMPORT-IPV6" + ibgpExportIPv4 = "IBGP-EXPORT-IPV4" + ibgpExportIPv6 = "IBGP-EXPORT-IPV6" + maskLengthRange32 = "32..32" + maskLengthRange128 = "128..128" + maskLen32 = "32" + maskLen128 = "128" + ipv4Prefix1 = "198.51.100.1" + ipv4Prefix2 = "198.51.100.2" + ipv4Prefix3 = "198.51.100.3" + ipv4Prefix4 = "198.51.100.4" + ipv4Prefix5 = "198.51.100.5" + ipv4Prefix6 = "198.51.100.6" + ipv4Prefix7 = "198.51.100.7" + ipv4Prefix8 = "198.51.100.8" + ipv6Prefix1 = "2001:DB8:2::1" + ipv6Prefix2 = "2001:DB8:2::2" + ipv6Prefix3 = "2001:DB8:2::3" + ipv6Prefix4 = "2001:DB8:2::4" + ipv6Prefix5 = "2001:DB8:2::5" + ipv6Prefix6 = "2001:DB8:2::6" + ipv6Prefix7 = "2001:DB8:2::7" + ipv6Prefix8 = "2001:DB8:2::8" + maskLenExact = "exact" + defaultStatementOnly = true ) var ( @@ -195,6 +192,13 @@ func configurePrefixMatchPolicy(t *testing.T, dut *ondatra.DUTDevice, prefixSet, pset := rp.GetOrCreateDefinedSets().GetOrCreatePrefixSet(prefixSet) for _, pref := range ipPrefixSet { pset.GetOrCreatePrefix(pref+"/"+maskLen, prefixSubnetRange) + mode := oc.PrefixSet_Mode_IPV4 + if maskLen == maskLen128 { + mode = oc.PrefixSet_Mode_IPV6 + } + if !deviations.SkipPrefixSetMode(dut) { + pset.SetMode(mode) + } } pdef := rp.GetOrCreatePolicyDefinition(prefixSet) @@ -422,9 +426,9 @@ func configureOTG(t *testing.T, otg *otg.OTG) { t.Logf("Pushing config to OTG and starting protocols...") otg.PushConfig(t, config) - time.Sleep(30 * time.Second) + time.Sleep(40 * time.Second) otg.StartProtocols(t) - time.Sleep(30 * time.Second) + time.Sleep(40 * time.Second) } func verifyBGPCapabilities(t *testing.T, dut *ondatra.DUTDevice) { @@ -523,11 +527,15 @@ func configureISIS(t *testing.T, dut *ondatra.DUTDevice, intfName []string, dutA isisIntfLevelAfi := isisIntfLevel.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV4, oc.IsisTypes_SAFI_TYPE_UNICAST) isisIntfLevelAfi.Metric = ygot.Uint32(200) isisIntfLevelAfi.Enabled = ygot.Bool(true) + isisIntfLevelAfi6 := isisIntfLevel.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV6, oc.IsisTypes_SAFI_TYPE_UNICAST) + isisIntfLevelAfi6.Metric = ygot.Uint32(200) + isisIntfLevelAfi6.Enabled = ygot.Bool(true) if deviations.ISISInterfaceAfiUnsupported(dut) { isisIntf.Af = nil } if deviations.MissingIsisInterfaceAfiSafiEnable(dut) { isisIntfLevelAfi.Enabled = nil + isisIntfLevelAfi6.Enabled = nil } } gnmi.Replace(t, dut, dutConfIsisPath.Config(), prot) @@ -583,57 +591,77 @@ func deleteBGPPolicy(t *testing.T, dut *ondatra.DUTDevice, nbrList []*bgpNbrList } } -func configureRoutingPolicyDefaultAction(t *testing.T, dut *ondatra.DUTDevice, action string, replace bool) { +func configurePrefixMatchAndDefaultStatement(t *testing.T, dut *ondatra.DUTDevice, prefixSet, prefixSubnetRange, maskLen string, ipPrefixSet []string, action string, defaultStatementOnly bool) *oc.RoutingPolicy { + d := &oc.Root{} + rp := d.GetOrCreateRoutingPolicy() + if !defaultStatementOnly { + pset := rp.GetOrCreateDefinedSets().GetOrCreatePrefixSet(prefixSet) + for _, pref := range ipPrefixSet { + pset.GetOrCreatePrefix(pref+"/"+maskLen, prefixSubnetRange) + mode := oc.PrefixSet_Mode_IPV4 + if maskLen == maskLen128 { + mode = oc.PrefixSet_Mode_IPV6 + } + if !deviations.SkipPrefixSetMode(dut) { + pset.SetMode(mode) + } + } + } - var routingPolicyDefaultAction = []any{ - map[string]any{ - "default-action": map[string]any{ - "policy-result": action, - }, - }, + pdef := rp.GetOrCreatePolicyDefinition(prefixSet) + if !defaultStatementOnly { + stmt1, err := pdef.AppendNewStatement("10") + if err != nil { + t.Fatal(err) + } + stmt1.GetOrCreateConditions().GetOrCreateMatchPrefixSet().PrefixSet = ygot.String(prefixSet) + stmt1.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE } - defaultAction, err := json.Marshal(routingPolicyDefaultAction) + stmt2, err := pdef.AppendNewStatement("50") if err != nil { - t.Fatalf("Error with json Marshal: %v", err) - } - - var updates []*gpb.Update - for _, policy := range []string{ebgpExportIPv4, ebgpExportIPv6, ibgpExportIPv4, ibgpExportIPv6, ebgpImportIPv4, ebgpImportIPv6, ibgpImportIPv4, ibgpImportIPv6} { - update := gpb.Update{ - Path: &gpb.Path{ - Elem: []*gpb.PathElem{ - {Name: "routing-policy"}, - {Name: "policy", Key: map[string]string{"name": policy}}, - }, - }, - Val: &gpb.TypedValue{ - Value: &gpb.TypedValue_JsonIetfVal{ - JsonIetfVal: defaultAction, - }, - }, - } - updates = append(updates, &update) - } - gpbSetRequest := &gpb.SetRequest{ - Prefix: &gpb.Path{ - Origin: "native", - }, - Update: updates, - } - if replace { - gpbSetRequest = &gpb.SetRequest{ - Prefix: &gpb.Path{ - Origin: "native", - }, - Replace: updates, - } + t.Fatal(err) } - - gnmiClient := dut.RawAPIs().GNMI(t) - if _, err := gnmiClient.Set(context.Background(), gpbSetRequest); err != nil { - t.Fatalf("Unexpected error updating SRL routing-policy default-action: %v", err) + stmt2.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE + if action == "reject" { + stmt2.GetOrCreateActions().PolicyResult = oc.RoutingPolicy_PolicyResultType_REJECT_ROUTE } - time.Sleep(5 * time.Second) + + return rp +} + +func configureRoutingPolicyDefaultAction(t *testing.T, dut *ondatra.DUTDevice, action string, defaultStatementOnly bool) { + t.Helper() + batchConfig := &gnmi.SetBatch{} + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() + + t.Logf("Delete prefix set policies") + deleteBGPPolicy(t, dut, []*bgpNbrList{ebgpNbrV4, ebgpNbrV6, ibgpNbrV4, ibgpNbrV6}) + gnmi.BatchDelete(batchConfig, gnmi.OC().RoutingPolicy().Config()) + batchConfig.Set(t, dut) + time.Sleep(20 * time.Second) + + gnmi.BatchUpdate(batchConfig, gnmi.OC().RoutingPolicy().Config(), configurePrefixMatchAndDefaultStatement(t, dut, ebgpImportIPv4, maskLenExact, maskLen32, []string{ipv4Prefix1, ipv4Prefix2}, action, defaultStatementOnly)) + gnmi.BatchUpdate(batchConfig, gnmi.OC().RoutingPolicy().Config(), configurePrefixMatchAndDefaultStatement(t, dut, ebgpImportIPv6, maskLenExact, maskLen128, []string{ipv6Prefix1, ipv6Prefix2}, action, defaultStatementOnly)) + gnmi.BatchUpdate(batchConfig, gnmi.OC().RoutingPolicy().Config(), configurePrefixMatchAndDefaultStatement(t, dut, ebgpExportIPv4, maskLenExact, maskLen32, []string{ipv4Prefix4}, action, defaultStatementOnly)) + gnmi.BatchUpdate(batchConfig, gnmi.OC().RoutingPolicy().Config(), configurePrefixMatchAndDefaultStatement(t, dut, ebgpExportIPv6, maskLenExact, maskLen128, []string{ipv6Prefix4}, action, defaultStatementOnly)) + gnmi.BatchUpdate(batchConfig, gnmi.OC().RoutingPolicy().Config(), configurePrefixMatchAndDefaultStatement(t, dut, ibgpImportIPv4, maskLenExact, maskLen32, []string{ipv4Prefix4, ipv4Prefix5}, action, defaultStatementOnly)) + gnmi.BatchUpdate(batchConfig, gnmi.OC().RoutingPolicy().Config(), configurePrefixMatchAndDefaultStatement(t, dut, ibgpImportIPv6, maskLenExact, maskLen128, []string{ipv6Prefix4, ipv6Prefix5}, action, defaultStatementOnly)) + gnmi.BatchUpdate(batchConfig, gnmi.OC().RoutingPolicy().Config(), configurePrefixMatchAndDefaultStatement(t, dut, ibgpExportIPv4, maskLenExact, maskLen32, []string{ipv4Prefix1}, action, defaultStatementOnly)) + gnmi.BatchUpdate(batchConfig, gnmi.OC().RoutingPolicy().Config(), configurePrefixMatchAndDefaultStatement(t, dut, ibgpExportIPv6, maskLenExact, maskLen128, []string{ipv6Prefix1}, action, defaultStatementOnly)) + + // Apply the above policies to the respective peering at the respective AFI-SAFI levels + gnmi.BatchReplace(batchConfig, bgpPath.Neighbor(ebgpNbrV4.nbrAddr).AfiSafi(ebgpNbrV4.afiSafi).ApplyPolicy().ImportPolicy().Config(), []string{ebgpImportIPv4}) + gnmi.BatchReplace(batchConfig, bgpPath.Neighbor(ebgpNbrV4.nbrAddr).AfiSafi(ebgpNbrV4.afiSafi).ApplyPolicy().ExportPolicy().Config(), []string{ebgpExportIPv4}) + gnmi.BatchReplace(batchConfig, bgpPath.Neighbor(ebgpNbrV6.nbrAddr).AfiSafi(ebgpNbrV6.afiSafi).ApplyPolicy().ImportPolicy().Config(), []string{ebgpImportIPv6}) + gnmi.BatchReplace(batchConfig, bgpPath.Neighbor(ebgpNbrV6.nbrAddr).AfiSafi(ebgpNbrV6.afiSafi).ApplyPolicy().ExportPolicy().Config(), []string{ebgpExportIPv6}) + gnmi.BatchReplace(batchConfig, bgpPath.Neighbor(ibgpNbrV4.nbrAddr).AfiSafi(ibgpNbrV4.afiSafi).ApplyPolicy().ImportPolicy().Config(), []string{ibgpImportIPv4}) + gnmi.BatchReplace(batchConfig, bgpPath.Neighbor(ibgpNbrV4.nbrAddr).AfiSafi(ibgpNbrV4.afiSafi).ApplyPolicy().ExportPolicy().Config(), []string{ibgpExportIPv4}) + gnmi.BatchReplace(batchConfig, bgpPath.Neighbor(ibgpNbrV6.nbrAddr).AfiSafi(ibgpNbrV6.afiSafi).ApplyPolicy().ImportPolicy().Config(), []string{ibgpImportIPv6}) + gnmi.BatchReplace(batchConfig, bgpPath.Neighbor(ibgpNbrV6.nbrAddr).AfiSafi(ibgpNbrV6.afiSafi).ApplyPolicy().ExportPolicy().Config(), []string{ibgpExportIPv6}) + + batchConfig.Set(t, dut) + + time.Sleep(20 * time.Second) } func testDefaultPolicyRejectRouteAction(t *testing.T, dut *ondatra.DUTDevice) { @@ -641,7 +669,7 @@ func testDefaultPolicyRejectRouteAction(t *testing.T, dut *ondatra.DUTDevice) { t.Run("Create and apply default-policy REJECT-ALL with action as REJECT_ROUTE", func(t *testing.T) { if deviations.BgpDefaultPolicyUnsupported(dut) { - configureRoutingPolicyDefaultAction(t, dut, "reject", !replacePolicy) + configureRoutingPolicyDefaultAction(t, dut, "reject", !defaultStatementOnly) } else { configureBGPDefaultPolicy(t, dut, rejectRoute) } @@ -665,7 +693,7 @@ func testDefaultPolicyAcceptRouteAction(t *testing.T, dut *ondatra.DUTDevice) { t.Helper() t.Run("Create and apply default-policy ACCEPT-ALL with action as ACCEPT_ROUTE", func(t *testing.T) { if deviations.BgpDefaultPolicyUnsupported(dut) { - configureRoutingPolicyDefaultAction(t, dut, "accept", !replacePolicy) + configureRoutingPolicyDefaultAction(t, dut, "accept", !defaultStatementOnly) } else { configureBGPDefaultPolicy(t, dut, acceptRoute) } @@ -690,7 +718,7 @@ func testDefaultPolicyAcceptRouteActionOnly(t *testing.T, dut *ondatra.DUTDevice t.Run("Create and apply default-policy ACCEPT-ALL with action as ACCEPT_ROUTE", func(t *testing.T) { if deviations.BgpDefaultPolicyUnsupported(dut) { - configureRoutingPolicyDefaultAction(t, dut, "accept", replacePolicy) + configureRoutingPolicyDefaultAction(t, dut, "accept", defaultStatementOnly) } else { configureBGPDefaultPolicy(t, dut, acceptRoute) t.Run("Delete prefix set policies", func(t *testing.T) { @@ -730,7 +758,7 @@ func testDefaultPolicyRejectRouteActionOnly(t *testing.T, dut *ondatra.DUTDevice t.Run("Create and apply default-policy REJECT-ALL with action as REJECT_ROUTE", func(t *testing.T) { if deviations.BgpDefaultPolicyUnsupported(dut) { - configureRoutingPolicyDefaultAction(t, dut, "reject", replacePolicy) + configureRoutingPolicyDefaultAction(t, dut, "reject", defaultStatementOnly) } else { configureBGPDefaultPolicy(t, dut, rejectRoute) } @@ -829,14 +857,14 @@ func verifyPostPolicyPrefixTelemetry(t *testing.T, dut *ondatra.DUTDevice, nbr * } if !deviations.MissingPrePolicyReceivedRoutes(dut) { - if gotRxPrePol, ok := gnmi.Watch(t, dut, afiSafiPath.Prefixes().ReceivedPrePolicy().State(), 10*time.Second, func(val *ygnmi.Value[uint32]) bool { + if gotRxPrePol, ok := gnmi.Watch(t, dut, afiSafiPath.Prefixes().ReceivedPrePolicy().State(), 20*time.Second, func(val *ygnmi.Value[uint32]) bool { gotRxPrePol, ok := val.Val() return ok && gotRxPrePol == nbr.wantRxPrePolicy }).Await(t); !ok { t.Errorf("Received pre policy prefixes mismatch: got %v, want %v", gotRxPrePol, nbr.wantRxPrePolicy) } } - if gotRx, ok := gnmi.Watch(t, dut, afiSafiPath.Prefixes().Received().State(), 10*time.Second, func(val *ygnmi.Value[uint32]) bool { + if gotRx, ok := gnmi.Watch(t, dut, afiSafiPath.Prefixes().Received().State(), 20*time.Second, func(val *ygnmi.Value[uint32]) bool { gotRx, ok := val.Val() return ok && gotRx == nbr.wantRx }).Await(t); !ok { diff --git a/feature/bgp/policybase/otg_tests/default_policies_test/metadata.textproto b/feature/bgp/policybase/otg_tests/default_policies_test/metadata.textproto index 44cac34eaf7..1d499a1c02e 100644 --- a/feature/bgp/policybase/otg_tests/default_policies_test/metadata.textproto +++ b/feature/bgp/policybase/otg_tests/default_policies_test/metadata.textproto @@ -26,6 +26,7 @@ platform_exceptions: { isis_interface_afi_unsupported: true isis_instance_enabled_required: true default_import_export_policy: true + bgp_default_policy_unsupported: true } } platform_exceptions: { @@ -40,5 +41,16 @@ platform_exceptions: { skip_non_bgp_route_export_check: true missing_isis_interface_afi_safi_enable: true bgp_default_policy_unsupported: true + skip_prefix_set_mode: true + } +} +platform_exceptions: { + platform: { + vendor: CISCO + } + deviations: { + missing_isis_interface_afi_safi_enable: true + bgp_default_policy_unsupported: true + prepolicy_received_routes: true } } diff --git a/feature/bgp/policybase/otg_tests/import_export_multi_test/import_export_multi_test.go b/feature/bgp/policybase/otg_tests/import_export_multi_test/import_export_multi_test.go index fcc29de6e15..4464225b2ee 100644 --- a/feature/bgp/policybase/otg_tests/import_export_multi_test/import_export_multi_test.go +++ b/feature/bgp/policybase/otg_tests/import_export_multi_test/import_export_multi_test.go @@ -16,6 +16,7 @@ package import_export_multi_test import ( + "fmt" "strconv" "testing" "time" @@ -24,6 +25,7 @@ import ( "github.com/openconfig/featureprofiles/internal/cfgplugins" "github.com/openconfig/featureprofiles/internal/deviations" "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/helpers" "github.com/openconfig/featureprofiles/internal/otgutils" "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" @@ -101,12 +103,41 @@ var communityMembers = [][][]int{ }, } +type bgpNbrList struct { + nbrAddr string + afiSafi oc.E_BgpTypes_AFI_SAFI_TYPE +} + // TestMain triggers the test run func TestMain(m *testing.M) { fptest.RunTests(m) } +func deleteBGPPolicy(t *testing.T, dut *ondatra.DUTDevice, nbrList []*bgpNbrList) { + t.Helper() + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() + for _, nbr := range nbrList { + nbrAfiSafiPath := bgpPath.Neighbor(nbr.nbrAddr).AfiSafi(nbr.afiSafi) + b := &gnmi.SetBatch{} + gnmi.BatchDelete(b, nbrAfiSafiPath.ApplyPolicy().ImportPolicy().Config()) + gnmi.BatchDelete(b, nbrAfiSafiPath.ApplyPolicy().ExportPolicy().Config()) + b.Set(t, dut) + } +} + func configureImportExportAcceptAllBGPPolicy(t *testing.T, dut *ondatra.DUTDevice, ipv4 string, ipv6 string) { + // Delete PERMIT-ALL policy applied to neighbor + deleteBGPPolicy(t, dut, []*bgpNbrList{ + { + nbrAddr: ipv4, + afiSafi: oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST, + }, + { + nbrAddr: ipv6, + afiSafi: oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST, + }, + }) + root := &oc.Root{} rp := root.GetOrCreateRoutingPolicy() pdef1 := rp.GetOrCreatePolicyDefinition("routePolicy") @@ -159,16 +190,29 @@ func configureImportExportMultifacetMatchActionsBGPPolicy(t *testing.T, dut *ond } // Configure regex_community:["^30:.*$"] to match_community_regex statement - communitySetRegex := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(regexCommunitySet) + if !(deviations.CommunityMemberRegexUnsupported(dut)) { + communitySetRegex := rp.GetOrCreateDefinedSets().GetOrCreateBgpDefinedSets().GetOrCreateCommunitySet(regexCommunitySet) - pd2cs1 := []oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{} - for _, commMatchPd2Cs1 := range regexCommunities { - if commMatchPd2Cs1 != "" { - pd2cs1 = append(pd2cs1, oc.UnionString(commMatchPd2Cs1)) + pd2cs1 := []oc.RoutingPolicy_DefinedSets_BgpDefinedSets_CommunitySet_CommunityMember_Union{} + for _, commMatchPd2Cs1 := range regexCommunities { + if commMatchPd2Cs1 != "" { + pd2cs1 = append(pd2cs1, oc.UnionString(commMatchPd2Cs1)) + } } + communitySetRegex.SetCommunityMember(pd2cs1) + communitySetRegex.SetMatchSetOptions(matchAny) + } + + var communitySetCLIConfig string + if deviations.CommunityMemberRegexUnsupported(dut) { + switch dut.Vendor() { + case ondatra.CISCO: + communitySetCLIConfig = fmt.Sprintf("community-set %v\n ios-regex '(%v)'\n end-set", regexCommunitySet, regexCommunities[0]) + default: + t.Fatalf("Unsupported vendor %s for deviation 'CommunityMemberRegexUnsupported'", dut.Vendor()) + } + helpers.GnmiCLIConfig(t, dut, communitySetCLIConfig) } - communitySetRegex.SetCommunityMember(pd2cs1) - communitySetRegex.SetMatchSetOptions(matchAny) if deviations.BGPConditionsMatchCommunitySetUnsupported(dut) { pd2stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().SetCommunitySet(regexCommunitySet) @@ -176,7 +220,9 @@ func configureImportExportMultifacetMatchActionsBGPPolicy(t *testing.T, dut *ond pd2stmt1.GetOrCreateConditions().GetOrCreateBgpConditions().GetOrCreateMatchCommunitySet().SetCommunitySet(regexCommunitySet) } - pd2stmt1.GetOrCreateActions().SetPolicyResult(nextstatementResult) + if !deviations.SkipSettingStatementForPolicy(dut) { + pd2stmt1.GetOrCreateActions().SetPolicyResult(nextstatementResult) + } // Configure the parent policy multi_policy. @@ -276,7 +322,9 @@ func configureImportExportMultifacetMatchActionsBGPPolicy(t *testing.T, dut *ond stmt3.GetOrCreateActions().GetOrCreateBgpActions().GetOrCreateSetCommunity().SetOptions(bgpSetCommunityOptionType) } - stmt3.GetOrCreateActions().SetPolicyResult(nextstatementResult) + if !deviations.SkipSettingStatementForPolicy(dut) { + stmt3.GetOrCreateActions().SetPolicyResult(nextstatementResult) + } // Configure multi_policy:STATEMENT4: match_comm_and_prefix_add_2_community_sets statement @@ -333,7 +381,9 @@ func configureImportExportMultifacetMatchActionsBGPPolicy(t *testing.T, dut *ond // set-local-pref = 5 stmt4.GetOrCreateActions().GetOrCreateBgpActions().SetSetLocalPref(localPref) - stmt4.GetOrCreateActions().SetPolicyResult(nextstatementResult) + if !deviations.SkipSettingStatementForPolicy(dut) { + stmt4.GetOrCreateActions().SetPolicyResult(nextstatementResult) + } // Configure multi_policy:STATEMENT5: match_aspath_set_med statement stmt5, err := pdef1.AppendNewStatement(matchAspathSetMedStatement) @@ -347,7 +397,11 @@ func configureImportExportMultifacetMatchActionsBGPPolicy(t *testing.T, dut *ond stmt5.GetOrCreateActions().SetPolicyResult(oc.RoutingPolicy_PolicyResultType_ACCEPT_ROUTE) - gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + if deviations.CommunityMemberRegexUnsupported(dut) { + gnmi.Update(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + } else { + gnmi.Replace(t, dut, gnmi.OC().RoutingPolicy().Config(), rp) + } // Configure the parent BGP import and export policy. dni := deviations.DefaultNetworkInstance(dut) @@ -355,16 +409,20 @@ func configureImportExportMultifacetMatchActionsBGPPolicy(t *testing.T, dut *ond policyV6 := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(ipv6).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).GetOrCreateApplyPolicy() policyV6.SetImportPolicy([]string{parentPolicy}) policyV6.SetExportPolicy([]string{parentPolicy}) - policyV6.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) - policyV6.SetDefaultExportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + if !deviations.DefaultRoutePolicyUnsupported(dut) { + policyV6.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + policyV6.SetDefaultExportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + } gnmi.Replace(t, dut, pathV6.Config(), policyV6) pathV4 := gnmi.OC().NetworkInstance(dni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).Bgp().Neighbor(ipv4).AfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).ApplyPolicy() policyV4 := root.GetOrCreateNetworkInstance(dni).GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, bgpName).GetOrCreateBgp().GetOrCreateNeighbor(ipv4).GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).GetOrCreateApplyPolicy() policyV4.SetImportPolicy([]string{parentPolicy}) policyV4.SetExportPolicy([]string{parentPolicy}) - policyV4.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) - policyV4.SetDefaultExportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + if !deviations.DefaultRoutePolicyUnsupported(dut) { + policyV4.SetDefaultImportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + policyV4.SetDefaultExportPolicy(oc.RoutingPolicy_DefaultPolicyType_REJECT_ROUTE) + } gnmi.Replace(t, dut, pathV4.Config(), policyV4) } diff --git a/feature/bgp/policybase/otg_tests/import_export_multi_test/metadata.textproto b/feature/bgp/policybase/otg_tests/import_export_multi_test/metadata.textproto index a1e6e2490a2..ce12ec20824 100644 --- a/feature/bgp/policybase/otg_tests/import_export_multi_test/metadata.textproto +++ b/feature/bgp/policybase/otg_tests/import_export_multi_test/metadata.textproto @@ -23,3 +23,15 @@ platform_exceptions: { bgp_community_member_is_a_string: true } } +platform_exceptions: { + platform: { + vendor: CISCO + } + deviations: { + bgp_conditions_match_community_set_unsupported: true + bgp_community_set_refs_unsupported: true + community_member_regex_unsupported: true + skip_setting_statement_for_policy: true + default_route_policy_unsupported: true + } +} diff --git a/feature/experimental/interface/interface_loopback_aggregate/otg_tests/interface_loopback_aggregate/README.md b/feature/experimental/interface/interface_loopback_aggregate/otg_tests/interface_loopback_aggregate/README.md index c3996d72bcf..aea9ddcf069 100644 --- a/feature/experimental/interface/interface_loopback_aggregate/otg_tests/interface_loopback_aggregate/README.md +++ b/feature/experimental/interface/interface_loopback_aggregate/otg_tests/interface_loopback_aggregate/README.md @@ -16,23 +16,24 @@ Ensure Interface mode can be set to loopback mode and can be added as part of st * Validate that port-1 operational status is “UP”. * Validate on DUT that LAG interface status is “UP”. -## Config Parameter Coverage - -* /interfaces/interface/config/loopback-mode -* /interfaces/interface/ethernet/config/port-speed -* /interfaces/interface/ethernet/config/duplex-mode -* /interfaces/interface/ethernet/config/aggregate-id -* /interfaces/interface/aggregation/config/lag-type -* /interfaces/interface/aggregation/config/min-links - -## Telemetry Parameter Coverage - -* /interfaces/interface/state/loopback-mode - -## Protocol/RPC Parameter Coverage - -None - -## Minimum DUT Platform Requirement - -vRX +## OpenConfig Path and RPC Coverage + +The below YAML defines the OC paths intended to be covered by this test. OC paths used for test setup are not listed here. + +```yaml +openconfig_paths: + ## Config paths + /interfaces/interface/config/loopback-mode: + /interfaces/interface/ethernet/config/port-speed: + /interfaces/interface/ethernet/config/duplex-mode: + /interfaces/interface/ethernet/config/aggregate-id: + /interfaces/interface/aggregation/config/lag-type: + /interfaces/interface/aggregation/config/min-links: + + ## Telemetry paths + /interfaces/interface/state/loopback-mode: + +rpcs: + gnmi: + gNMI.Set: + gNMI.Subscribe: diff --git a/feature/experimental/interface/interface_loopback_aggregate/otg_tests/interface_loopback_aggregate/interface_loopback_aggregate_test.go b/feature/experimental/interface/interface_loopback_aggregate/otg_tests/interface_loopback_aggregate/interface_loopback_aggregate_test.go index 7b5eb91c7ea..8e31b1550d0 100644 --- a/feature/experimental/interface/interface_loopback_aggregate/otg_tests/interface_loopback_aggregate/interface_loopback_aggregate_test.go +++ b/feature/experimental/interface/interface_loopback_aggregate/otg_tests/interface_loopback_aggregate/interface_loopback_aggregate_test.go @@ -15,7 +15,6 @@ package interface_loopback_aggregate_test import ( - "context" "fmt" "testing" "time" @@ -24,7 +23,6 @@ import ( "github.com/openconfig/featureprofiles/internal/attrs" "github.com/openconfig/featureprofiles/internal/deviations" "github.com/openconfig/featureprofiles/internal/fptest" - gpb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" "github.com/openconfig/ondatra/gnmi/oc" @@ -232,40 +230,9 @@ func TestInterfaceLoopbackMode(t *testing.T) { t.Run("Configure interface loopback mode FACILITY on DUT AE interface", func(t *testing.T) { if deviations.InterfaceLoopbackModeRawGnmi(dut) { - gpbSetRequest := &gpb.SetRequest{ - Update: []*gpb.Update{{ - Path: &gpb.Path{ - Origin: "openconfig", - Elem: []*gpb.PathElem{ - { - Name: "interfaces", - }, - { - Name: "interface", - Key: map[string]string{ - "name": dut.Port(t, "port1").Name(), - }, - }, - { - Name: "config", - }, - { - Name: "loopback-mode", - }, - }, - }, - Val: &gpb.TypedValue{ - Value: &gpb.TypedValue_JsonIetfVal{ - JsonIetfVal: []byte("true"), - }, - }, - }}, - } - gnmiClient := dut.RawAPIs().GNMI(t) - _, err := gnmiClient.Set(context.Background(), gpbSetRequest) - if err != nil { - t.Errorf("Failed to update interface loopback mode") - } + + gnmi.Update(t, dut, gnmi.OC().Interface(dutPort1.Name()).LoopbackMode().Config(), oc.Interfaces_LoopbackModeType_TERMINAL) + } else { if deviations.MemberLinkLoopbackUnsupported(dut) { gnmi.Update(t, dut, gnmi.OC().Interface(aggID).LoopbackMode().Config(), oc.Interfaces_LoopbackModeType_FACILITY) diff --git a/feature/gribi/otg_tests/encap_frr/encap_frr_test.go b/feature/gribi/otg_tests/encap_frr/encap_frr_test.go index 04a9d884de3..eb8f2c8599e 100644 --- a/feature/gribi/otg_tests/encap_frr/encap_frr_test.go +++ b/feature/gribi/otg_tests/encap_frr/encap_frr_test.go @@ -34,12 +34,11 @@ import ( "github.com/open-traffic-generator/snappi/gosnappi" "github.com/openconfig/featureprofiles/internal/attrs" "github.com/openconfig/featureprofiles/internal/deviations" + baseScenario "github.com/openconfig/featureprofiles/internal/encap_frr" "github.com/openconfig/featureprofiles/internal/fptest" "github.com/openconfig/featureprofiles/internal/gribi" "github.com/openconfig/featureprofiles/internal/vrfpolicy" spb "github.com/openconfig/gnoi/system" - "github.com/openconfig/gribigo/chk" - "github.com/openconfig/gribigo/constants" "github.com/openconfig/gribigo/fluent" "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" @@ -68,48 +67,24 @@ func TestMain(m *testing.M) { // DUT port-8 <------> port-8 ATE const ( - plenIPv4 = 30 - plenIPv6 = 126 - numPorts = 8 - dscpEncapA1 = 10 - dscpEncapA2 = 18 - dscpEncapB1 = 20 - dscpEncapB2 = 28 - dscpEncapNoMatch = 30 - ipv4OuterSrc111Addr = "198.51.100.111" - ipv4OuterSrc222Addr = "198.51.100.222" - ipv4OuterSrcAddr = "198.100.200.123" - ipv4InnerDst = "138.0.11.8" - ipv4OuterDst333 = "192.58.200.7" - noMatchEncapDest = "20.0.0.1" - prot4 = 4 - prot41 = 41 - polName = "pol1" - gribiIPv4entry = "192.51.100.0" - maskLen24 = "24" - maskLen32 = "32" - niDecapTeVrf = "DECAP_TE_VRF" - niEncapTeVrfA = "ENCAP_TE_VRF_A" - niEncapTeVrfB = "ENCAP_TE_VRF_B" - niTeVrf111 = "TE_VRF_111" - niTeVrf222 = "TE_VRF_222" - niDefault = "DEFAULT" - tolerancePct = 2 - tolerance = 0.2 - encapFlow = "encapFlow" - correspondingHopLimit = 64 - magicIP = "192.168.1.1" - magicMac = "02:00:00:00:00:01" - gribiIPv4EntryDefVRF1 = "192.0.2.101" - gribiIPv4EntryDefVRF2 = "192.0.2.102" - gribiIPv4EntryDefVRF3 = "192.0.2.103" - gribiIPv4EntryDefVRF4 = "192.0.2.104" - gribiIPv4EntryDefVRF5 = "192.0.2.105" - gribiIPv4EntryVRF1111 = "203.0.113.1" - gribiIPv4EntryVRF1112 = "203.0.113.2" - gribiIPv4EntryVRF2221 = "203.0.113.100" - gribiIPv4EntryVRF2222 = "203.0.113.101" - gribiIPv4EntryEncapVRF = "138.0.11.0" + plenIPv4 = 30 + plenIPv6 = 126 + dscpEncapA1 = 10 + ipv4OuterSrcAddr = "198.100.200.123" + ipv4InnerDst = "138.0.11.8" + ipv4OuterDst333 = "192.58.200.7" + noMatchEncapDest = "20.0.0.1" + niDecapTeVrf = "DECAP_TE_VRF" + niEncapTeVrfA = "ENCAP_TE_VRF_A" + niRepairVrf = "REPAIR_VRF" + tolerancePct = 2 + tolerance = 0.2 + encapFlow = "encapFlow" + correspondingHopLimit = 64 + magicIP = "192.168.1.1" + magicMAC = "02:00:00:00:00:01" + gribiIPv4EntryVRF1111 = "203.0.113.1" + gribiIPv4EntryVRF1112 = "203.0.113.2" dutAreaAddress = "49.0001" dutSysID = "1920.0000.2001" @@ -202,75 +177,6 @@ var ( IPv4Len: 32, IPv6Len: 128, } - dutPort2DummyIP = attrs.Attributes{ - Desc: "dutPort2", - IPv4Sec: "192.0.2.33", - IPv4LenSec: plenIPv4, - } - - otgPort2DummyIP = attrs.Attributes{ - Desc: "otgPort2", - IPv4: "192.0.2.34", - IPv4Len: plenIPv4, - } - - dutPort3DummyIP = attrs.Attributes{ - Desc: "dutPort3", - IPv4Sec: "192.0.2.37", - IPv4LenSec: plenIPv4, - } - - otgPort3DummyIP = attrs.Attributes{ - Desc: "otgPort3", - IPv4: "192.0.2.38", - IPv4Len: plenIPv4, - } - - dutPort4DummyIP = attrs.Attributes{ - Desc: "dutPort4", - IPv4Sec: "192.0.2.41", - IPv4LenSec: plenIPv4, - } - - otgPort4DummyIP = attrs.Attributes{ - Desc: "otgPort4", - IPv4: "192.0.2.42", - IPv4Len: plenIPv4, - } - - dutPort5DummyIP = attrs.Attributes{ - Desc: "dutPort5", - IPv4Sec: "192.0.2.45", - IPv4LenSec: plenIPv4, - } - - otgPort5DummyIP = attrs.Attributes{ - Desc: "otgPort5", - IPv4: "192.0.2.46", - IPv4Len: plenIPv4, - } - dutPort6DummyIP = attrs.Attributes{ - Desc: "dutPort5", - IPv4Sec: "192.0.2.49", - IPv4LenSec: plenIPv4, - } - - otgPort6DummyIP = attrs.Attributes{ - Desc: "otgPort5", - IPv4: "192.0.2.50", - IPv4Len: plenIPv4, - } - dutPort7DummyIP = attrs.Attributes{ - Desc: "dutPort5", - IPv4Sec: "192.0.2.53", - IPv4LenSec: plenIPv4, - } - - otgPort7DummyIP = attrs.Attributes{ - Desc: "otgPort5", - IPv4: "192.0.2.54", - IPv4Len: plenIPv4, - } loopbackIntfName string atePortNamelist []string ) @@ -284,7 +190,6 @@ func awaitTimeout(ctx context.Context, t testing.TB, c *fluent.GRIBIClient, time } type testArgs struct { - ctx context.Context client *fluent.GRIBIClient dut *ondatra.DUTDevice ate *ondatra.ATEDevice @@ -370,29 +275,6 @@ func dutInterface(p *ondatra.Port, dut *ondatra.DUTDevice) *oc.Interface { return i } -// staticARPWithSecondaryIP configures secondary IPs and static ARP. -func staticARPWithSpecificIP(t *testing.T, dut *ondatra.DUTDevice) { - t.Helper() - p2 := dut.Port(t, "port2") - p3 := dut.Port(t, "port3") - p4 := dut.Port(t, "port4") - p5 := dut.Port(t, "port5") - p6 := dut.Port(t, "port6") - p7 := dut.Port(t, "port7") - gnmi.Update(t, dut, gnmi.OC().Interface(p2.Name()).Config(), dutPort2DummyIP.NewOCInterface(p2.Name(), dut)) - gnmi.Update(t, dut, gnmi.OC().Interface(p3.Name()).Config(), dutPort3DummyIP.NewOCInterface(p3.Name(), dut)) - gnmi.Update(t, dut, gnmi.OC().Interface(p4.Name()).Config(), dutPort4DummyIP.NewOCInterface(p4.Name(), dut)) - gnmi.Update(t, dut, gnmi.OC().Interface(p5.Name()).Config(), dutPort5DummyIP.NewOCInterface(p5.Name(), dut)) - gnmi.Update(t, dut, gnmi.OC().Interface(p6.Name()).Config(), dutPort6DummyIP.NewOCInterface(p6.Name(), dut)) - gnmi.Update(t, dut, gnmi.OC().Interface(p7.Name()).Config(), dutPort7DummyIP.NewOCInterface(p7.Name(), dut)) - gnmi.Update(t, dut, gnmi.OC().Interface(p2.Name()).Config(), configStaticArp(p2.Name(), otgPort2DummyIP.IPv4, magicMac)) - gnmi.Update(t, dut, gnmi.OC().Interface(p3.Name()).Config(), configStaticArp(p3.Name(), otgPort3DummyIP.IPv4, magicMac)) - gnmi.Update(t, dut, gnmi.OC().Interface(p4.Name()).Config(), configStaticArp(p4.Name(), otgPort4DummyIP.IPv4, magicMac)) - gnmi.Update(t, dut, gnmi.OC().Interface(p5.Name()).Config(), configStaticArp(p5.Name(), otgPort5DummyIP.IPv4, magicMac)) - gnmi.Update(t, dut, gnmi.OC().Interface(p6.Name()).Config(), configStaticArp(p6.Name(), otgPort6DummyIP.IPv4, magicMac)) - gnmi.Update(t, dut, gnmi.OC().Interface(p7.Name()).Config(), configStaticArp(p7.Name(), otgPort7DummyIP.IPv4, magicMac)) -} - // configureDUT configures all the interfaces on the DUT. func configureDUT(t *testing.T, dut *ondatra.DUTDevice, dutPortList []*ondatra.Port) { dc := gnmi.OC() @@ -436,318 +318,51 @@ func configureDUT(t *testing.T, dut *ondatra.DUTDevice, dutPortList []*ondatra.P t.Logf("Got DUT IPv6 loopback address: %v", dutlo0Attrs.IPv6) } if deviations.GRIBIMACOverrideWithStaticARP(dut) { - staticARPWithSpecificIP(t, dut) - } -} - -// configStaticArp configures static arp entries -func configStaticArp(p string, ipv4addr string, macAddr string) *oc.Interface { - i := &oc.Interface{Name: ygot.String(p)} - i.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd - s := i.GetOrCreateSubinterface(0) - s4 := s.GetOrCreateIpv4() - n4 := s4.GetOrCreateNeighbor(ipv4addr) - n4.LinkLayerAddress = ygot.String(macAddr) - return i -} - -func staticARPWithMagicUniversalIP(t *testing.T, dut *ondatra.DUTDevice) { - t.Helper() - sb := &gnmi.SetBatch{} - p2 := dut.Port(t, "port2") - p3 := dut.Port(t, "port3") - p4 := dut.Port(t, "port4") - p5 := dut.Port(t, "port5") - p6 := dut.Port(t, "port6") - p7 := dut.Port(t, "port7") - portList := []*ondatra.Port{p2, p3, p4, p5, p6, p7} - for idx, p := range portList { - s := &oc.NetworkInstance_Protocol_Static{ - Prefix: ygot.String(magicIP + "/32"), - NextHop: map[string]*oc.NetworkInstance_Protocol_Static_NextHop{ - strconv.Itoa(idx): { - Index: ygot.String(strconv.Itoa(idx)), - InterfaceRef: &oc.NetworkInstance_Protocol_Static_NextHop_InterfaceRef{ - Interface: ygot.String(p.Name()), - }, - }, - }, - } - sp := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, deviations.StaticProtocolName(dut)) - gnmi.BatchUpdate(sb, sp.Static(magicIP+"/32").Config(), s) - gnmi.BatchUpdate(sb, gnmi.OC().Interface(p.Name()).Config(), configStaticArp(p.Name(), magicIP, magicMac)) + baseScenario.StaticARPWithSpecificIP(t, dut) } - sb.Set(t, dut) -} - -func programAftWithDummyIP(t *testing.T, dut *ondatra.DUTDevice, args *testArgs) { - args.client.Modify().AddEntry(t, - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(11).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port2").Name()). - WithIPAddress(otgPort2DummyIP.IPv4), - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(12).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port3").Name()). - WithIPAddress(otgPort3DummyIP.IPv4), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(11).AddNextHop(11, 1).AddNextHop(12, 3), - fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryDefVRF1+"/"+maskLen32).WithNextHopGroup(11), - - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(13).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port4").Name()). - WithIPAddress(otgPort4DummyIP.IPv4), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(12).AddNextHop(13, 2), - fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryDefVRF2+"/"+maskLen32).WithNextHopGroup(12), - - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(14).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port5").Name()). - WithIPAddress(otgPort5DummyIP.IPv4), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(13).AddNextHop(14, 1), - fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryDefVRF3+"/"+maskLen32).WithNextHopGroup(13), - - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(15).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port6").Name()). - WithIPAddress(otgPort6DummyIP.IPv4), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(14).AddNextHop(15, 1), - fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryDefVRF4+"/"+maskLen32).WithNextHopGroup(14), - - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(16).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port7").Name()). - WithIPAddress(otgPort7DummyIP.IPv4), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(15).AddNextHop(16, 1), - fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryDefVRF5+"/"+maskLen32).WithNextHopGroup(15), - ) } -func configureGribiRoute(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, args *testArgs) { +// configureGribiRoute configures Gribi route as per the requirements +func configureGribiRoute(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, client *fluent.GRIBIClient) { t.Helper() - // Programming AFT entries for prefixes in DEFAULT VRF - if deviations.GRIBIMACOverrideStaticARPStaticRoute(dut) { - args.client.Modify().AddEntry(t, - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(11).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port2").Name()).WithIPAddress(magicIP), - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(12).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port3").Name()).WithIPAddress(magicIP), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(11).AddNextHop(11, 1).AddNextHop(12, 3), - - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(13).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port4").Name()).WithIPAddress(magicIP), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(12).AddNextHop(13, 2), - - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(14).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port5").Name()).WithIPAddress(magicIP), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(13).AddNextHop(14, 1), - - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(15).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port6").Name()).WithIPAddress(magicIP), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(14).AddNextHop(15, 1), - - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(16).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port7").Name()).WithIPAddress(magicIP), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(15).AddNextHop(16, 1), - ) - } else if deviations.GRIBIMACOverrideWithStaticARP(dut) { - programAftWithDummyIP(t, dut, args) - } else { - args.client.Modify().AddEntry(t, - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(11).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port2").Name()), - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(12).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port3").Name()), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(11).AddNextHop(11, 1).AddNextHop(12, 3), - - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(13).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port4").Name()), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(12).AddNextHop(13, 2), - - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(14).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port5").Name()), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(13).AddNextHop(14, 1), - - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(15).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port6").Name()), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(14).AddNextHop(15, 1), - - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(16).WithMacAddress(magicMac).WithInterfaceRef(dut.Port(t, "port7").Name()), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(15).AddNextHop(16, 1), - ) - } - if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { - t.Logf("Could not program entries via client, got err, check error codes: %v", err) - } - - args.client.Modify().AddEntry(t, - fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryDefVRF1+"/"+maskLen32).WithNextHopGroup(11), - fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryDefVRF2+"/"+maskLen32).WithNextHopGroup(12), - fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryDefVRF3+"/"+maskLen32).WithNextHopGroup(13), - fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryDefVRF4+"/"+maskLen32).WithNextHopGroup(14), - fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryDefVRF5+"/"+maskLen32).WithNextHopGroup(15), - ) - if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { - t.Logf("Could not program entries via client, got err, check error codes: %v", err) - } - - defaultVRFIPList := []string{gribiIPv4EntryDefVRF1, gribiIPv4EntryDefVRF2, gribiIPv4EntryDefVRF3, gribiIPv4EntryDefVRF4, gribiIPv4EntryDefVRF5} - for ip := range defaultVRFIPList { - chk.HasResult(t, args.client.Results(t), - fluent.OperationResult(). - WithIPv4Operation(defaultVRFIPList[ip]+"/32"). - WithOperationType(constants.Add). - WithProgrammingResult(fluent.InstalledInFIB). - AsResult(), - chk.IgnoreOperationID(), - ) - } - - // Programming AFT entries for backup NHG - args.client.Modify().AddEntry(t, - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(2000).WithDecapsulateHeader(fluent.IPinIP).WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(2000).AddNextHop(2000, 1), - ) - if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { - t.Logf("Could not program entries via client, got err, check error codes: %v", err) - } - - // Programming AFT entries for prefixes in TE_VRF_222 - args.client.Modify().AddEntry(t, - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(3).WithIPAddress(gribiIPv4EntryDefVRF3), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(2).AddNextHop(3, 1).WithBackupNHG(2000), - fluent.IPv4Entry().WithNetworkInstance(niTeVrf222).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryVRF2221+"/"+maskLen32).WithNextHopGroup(2), - - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(5).WithIPAddress(gribiIPv4EntryDefVRF5), - fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(4).AddNextHop(5, 1).WithBackupNHG(2000), - fluent.IPv4Entry().WithNetworkInstance(niTeVrf222).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryVRF2222+"/"+maskLen32).WithNextHopGroup(4), - ) - if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { - t.Logf("Could not program entries via client, got err, check error codes: %v", err) - } - - teVRF222IPList := []string{gribiIPv4EntryVRF2221, gribiIPv4EntryVRF2222} - for ip := range teVRF222IPList { - chk.HasResult(t, args.client.Results(t), - fluent.OperationResult(). - WithIPv4Operation(teVRF222IPList[ip]+"/32"). - WithOperationType(constants.Add). - WithProgrammingResult(fluent.InstalledInFIB). - AsResult(), - chk.IgnoreOperationID(), - ) - } + baseScenario.ConfigureBaseGribiRoutes(ctx, t, dut, client) - // Programming AFT entries for backup NHG - args.client.Modify().AddEntry(t, - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(1000).WithDecapsulateHeader(fluent.IPinIP).WithEncapsulateHeader(fluent.IPinIP). - WithIPinIP(ipv4OuterSrc222Addr, gribiIPv4EntryVRF2221). - WithNextHopNetworkInstance(niTeVrf222), + // amend base AFT entries to use decap encap backup NHG + client.Modify().ReplaceEntry(t, fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(1000).AddNextHop(1000, 1), - - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(1001).WithDecapsulateHeader(fluent.IPinIP).WithEncapsulateHeader(fluent.IPinIP). - WithIPinIP(ipv4OuterSrc222Addr, gribiIPv4EntryVRF2222). - WithNextHopNetworkInstance(niTeVrf222), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(1001).AddNextHop(1001, 1), - ) - if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { - t.Logf("Could not program entries via client, got err, check error codes: %v", err) - } - - // Programming AFT entries for prefixes in TE_VRF_111 - args.client.Modify().AddEntry(t, - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(1).WithIPAddress(gribiIPv4EntryDefVRF1), - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(2).WithIPAddress(gribiIPv4EntryDefVRF2), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(1).AddNextHop(1, 1).AddNextHop(2, 3).WithBackupNHG(1000), - fluent.IPv4Entry().WithNetworkInstance(niTeVrf111).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryVRF1111+"/"+maskLen32).WithNextHopGroup(1), - - fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(4).WithIPAddress(gribiIPv4EntryDefVRF4), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(3).AddNextHop(4, 1).WithBackupNHG(1001), - fluent.IPv4Entry().WithNetworkInstance(niTeVrf111).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryVRF1112+"/"+maskLen32).WithNextHopGroup(3), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(101).AddNextHop(101, 1).AddNextHop(102, 3).WithBackupNHG(18), ) - if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { t.Logf("Could not program entries via client, got err, check error codes: %v", err) } - teVRF111IPList := []string{gribiIPv4EntryVRF1111, gribiIPv4EntryVRF1112} - for ip := range teVRF111IPList { - chk.HasResult(t, args.client.Results(t), - fluent.OperationResult(). - WithIPv4Operation(teVRF111IPList[ip]+"/32"). - WithOperationType(constants.Add). - WithProgrammingResult(fluent.InstalledInFIB). - AsResult(), - chk.IgnoreOperationID(), - ) - } - - // Programming AFT entries for prefixes in ENCAP_TE_VRF_A - args.client.Modify().AddEntry(t, + client.Modify().DeleteEntry(t, fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(101).WithEncapsulateHeader(fluent.IPinIP). - WithIPinIP(ipv4OuterSrc111Addr, gribiIPv4EntryVRF1111). - WithNextHopNetworkInstance(niTeVrf111), + WithIndex(3000).WithNextHopNetworkInstance(niRepairVrf), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(3000).AddNextHop(3000, 1), + fluent.IPv4Entry().WithNetworkInstance(niRepairVrf).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryVRF1111+"/32").WithNextHopGroup(1000), + fluent.IPv4Entry().WithNetworkInstance(niRepairVrf).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryVRF1112+"/32").WithNextHopGroup(1001), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithIndex(102).WithEncapsulateHeader(fluent.IPinIP). - WithIPinIP(ipv4OuterSrc111Addr, gribiIPv4EntryVRF1112). - WithNextHopNetworkInstance(niTeVrf111), + WithIndex(2001).WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithID(101).AddNextHop(101, 1).AddNextHop(102, 3), - fluent.IPv4Entry().WithNetworkInstance(niEncapTeVrfA).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). - WithPrefix(gribiIPv4EntryEncapVRF+"/"+maskLen24).WithNextHopGroup(101), + WithID(2001).AddNextHop(2001, 1), ) - if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { t.Logf("Could not program entries via client, got err, check error codes: %v", err) } - - chk.HasResult(t, args.client.Results(t), - fluent.OperationResult(). - WithIPv4Operation(gribiIPv4EntryEncapVRF+"/24"). - WithOperationType(constants.Add). - WithProgrammingResult(fluent.InstalledInFIB). - AsResult(), - chk.IgnoreOperationID(), - ) } func configureISIS(t *testing.T, dut *ondatra.DUTDevice, intfName, dutAreaAddress, dutSysID string) { @@ -931,7 +546,7 @@ func configureOTG(t testing.TB, otg *otg.OTG, atePorts []*ondatra.Port) gosnappi atePortNamelist[5], atePortNamelist[6], atePortNamelist[7]}). SetFormat(gosnappi.CaptureFormat.PCAP) - // Disable FEC for 100G-FR ports because Novus does not support it. + // Disable FEC for 100G-FR ports because Novus does not support it. if len(pmd100GFRPorts) > 0 { l1Settings := config.Layer1().Add().SetName("L1").SetPortNames(pmd100GFRPorts) l1Settings.SetAutoNegotiate(true).SetIeeeMediaDefaults(false).SetSpeed("speed_100_gbps") @@ -1390,7 +1005,7 @@ func TestEncapFrr(t *testing.T) { vrfpolicy.ConfigureVRFSelectionPolicy(t, dut, vrfpolicy.VRFPolicyC) if deviations.GRIBIMACOverrideStaticARPStaticRoute(dut) { - staticARPWithMagicUniversalIP(t, dut) + baseScenario.StaticARPWithMagicUniversalIP(t, dut) } t.Log("Install BGP route resolved by ISIS.") @@ -1431,7 +1046,6 @@ func TestEncapFrr(t *testing.T) { eID := gribi.BecomeLeader(t, client) args := &testArgs{ - ctx: ctx, client: client, dut: dut, ate: ate, @@ -1441,81 +1055,9 @@ func TestEncapFrr(t *testing.T) { otg: otg, } - cases := []struct { - desc string - downPortList []string - capturePortList []string - encapHeaderOuterIPList []string - encapHeaderInnerIPList []string - trafficDestIP string - loadBalancePercent []float64 - encapUnviable string - }{{ - desc: "Test-1: primary encap unviable but backup encap viable for single tunnel", - downPortList: []string{"port2", "port3", "port4"}, - capturePortList: []string{atePortNamelist[4], atePortNamelist[5]}, - encapHeaderOuterIPList: []string{gribiIPv4EntryVRF2221, gribiIPv4EntryVRF1112}, - encapHeaderInnerIPList: []string{ipv4InnerDst, ipv4InnerDst}, - trafficDestIP: ipv4InnerDst, - loadBalancePercent: []float64{0, 0, 0, 0.25, 0.75, 0, 0}, - encapUnviable: "primarySingle", - }, { - desc: "Test-2: primary and backup encap unviable for single tunnel", - downPortList: []string{"port2", "port3", "port4", "port5"}, - capturePortList: []string{atePortNamelist[5], atePortNamelist[7]}, - encapHeaderOuterIPList: []string{gribiIPv4EntryVRF1112}, - encapHeaderInnerIPList: []string{ipv4InnerDst}, - trafficDestIP: ipv4InnerDst, - loadBalancePercent: []float64{0, 0, 0, 0, 0.75, 0, 0.25}, - encapUnviable: "primaryBackupSingle", - }, { - desc: "Test-3: primary encap unviable with backup to routing for single tunnel", - downPortList: []string{"port2", "port3", "port4"}, - capturePortList: []string{atePortNamelist[5], atePortNamelist[7]}, - encapHeaderOuterIPList: []string{gribiIPv4EntryVRF1112}, - encapHeaderInnerIPList: []string{ipv4InnerDst}, - trafficDestIP: ipv4InnerDst, - loadBalancePercent: []float64{0, 0, 0, 0, 0.75, 0, 0.25}, - encapUnviable: "primaryBackupRoutingSingle", - }, { - desc: "Test-4: primary encap unviable but backup encap viable for all tunnels", - downPortList: []string{"port2", "port3", "port4", "port6"}, - capturePortList: []string{atePortNamelist[4], atePortNamelist[6]}, - encapHeaderOuterIPList: []string{gribiIPv4EntryVRF2221, gribiIPv4EntryVRF2222}, - encapHeaderInnerIPList: []string{ipv4InnerDst, ipv4InnerDst}, - trafficDestIP: ipv4InnerDst, - loadBalancePercent: []float64{0, 0, 0, 0.25, 0, 0.75, 0}, - encapUnviable: "primaryAll", - }, { - desc: "Test-5: primary and backup encap unviable for all tunnels", - downPortList: []string{"port2", "port3", "port4", "port5", "port6", "port7"}, - capturePortList: []string{atePortNamelist[7]}, - encapHeaderOuterIPList: []string{}, - encapHeaderInnerIPList: []string{ipv4InnerDst}, - trafficDestIP: ipv4InnerDst, - loadBalancePercent: []float64{0, 0, 0, 0, 0, 0, 1}, - encapUnviable: "primaryBackupAll", - }, { - desc: "Test-6: primary encap unviable with backup to routing for all tunnels", - downPortList: []string{"port2", "port3", "port4", "port6"}, - capturePortList: []string{atePortNamelist[7]}, - encapHeaderOuterIPList: []string{}, - encapHeaderInnerIPList: []string{ipv4InnerDst}, - trafficDestIP: ipv4InnerDst, - loadBalancePercent: []float64{0, 0, 0, 0, 0, 0, 1}, - encapUnviable: "primaryBackupRoutingAll", - }, { - desc: "Test-7: no match in encap VRF", - downPortList: []string{}, - capturePortList: []string{atePortNamelist[7]}, - encapHeaderOuterIPList: []string{}, - encapHeaderInnerIPList: []string{noMatchEncapDest}, - trafficDestIP: noMatchEncapDest, - loadBalancePercent: []float64{0, 0, 0, 0, 0, 0, 1}, - encapUnviable: "encapNoMatch", - }} - for _, tc := range cases { - t.Run(tc.desc, func(t *testing.T) { + testCases := baseScenario.TestCases(atePortNamelist, ipv4InnerDst) + for _, tc := range testCases { + t.Run(tc.Desc, func(t *testing.T) { t.Log("Verify whether the ports are in up state") portList := []string{"port2", "port3", "port4", "port5", "port6", "port7", "port8"} verifyPortStatus(t, args, portList, true) @@ -1525,7 +1067,7 @@ func TestEncapFrr(t *testing.T) { t.Fatal(err) } baseCapturePortList := []string{atePortNamelist[1], atePortNamelist[5]} - configureGribiRoute(ctx, t, dut, args) + configureGribiRoute(ctx, t, dut, client) createFlow(t, otgConfig, otg, ipv4InnerDst) captureState := startCapture(t, args, baseCapturePortList) sendTraffic(t, args, baseCapturePortList, captureState) @@ -1533,7 +1075,7 @@ func TestEncapFrr(t *testing.T) { baseLoadBalancePercent := []float64{0.0156, 0.0468, 0.1875, 0, 0.75, 0, 0} verifyTraffic(t, args, baseCapturePortList, baseLoadBalancePercent, !wantLoss, checkEncap, baseHeaderDstIP) - if tc.encapUnviable == "primaryBackupRoutingSingle" { + if tc.TestID == "primaryBackupRoutingSingle" { args.client.Modify().AddEntry(t, fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithIndex(1100).WithDecapsulateHeader(fluent.IPinIP). @@ -1541,11 +1083,11 @@ func TestEncapFrr(t *testing.T) { fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(1000).AddNextHop(1100, 1), ) - if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { + if err := awaitTimeout(ctx, t, args.client, time.Minute); err != nil { t.Logf("Could not program entries via client, got err, check error codes: %v", err) } } - if tc.encapUnviable == "primaryBackupRoutingAll" { + if tc.TestID == "primaryBackupRoutingAll" { args.client.Modify().AddEntry(t, fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithIndex(1100).WithDecapsulateHeader(fluent.IPinIP). @@ -1558,11 +1100,11 @@ func TestEncapFrr(t *testing.T) { fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithID(1001).AddNextHop(1101, 1), ) - if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { + if err := awaitTimeout(ctx, t, args.client, time.Minute); err != nil { t.Logf("Could not program entries via client, got err, check error codes: %v", err) } } - if tc.encapUnviable == "encapNoMatch" { + if tc.TestID == "encapNoMatch" { args.client.Modify().AddEntry(t, fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). WithIndex(1003).WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), @@ -1571,23 +1113,23 @@ func TestEncapFrr(t *testing.T) { fluent.IPv4Entry().WithNetworkInstance(niEncapTeVrfA). WithPrefix("0.0.0.0/0").WithNextHopGroup(1003).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)), ) - if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { + if err := awaitTimeout(ctx, t, args.client, time.Minute); err != nil { t.Logf("Could not program entries via client, got err, check error codes: %v", err) } createFlow(t, otgConfig, otg, noMatchEncapDest) } - captureState = startCapture(t, args, tc.capturePortList) - if len(tc.downPortList) > 0 { - t.Logf("Bring down ports %s", tc.downPortList) - portState(t, args, tc.downPortList, false) - defer portState(t, args, tc.downPortList, true) + captureState = startCapture(t, args, tc.CapturePortList) + if len(tc.DownPortList) > 0 { + t.Logf("Bring down ports %s", tc.DownPortList) + portState(t, args, tc.DownPortList, false) + defer portState(t, args, tc.DownPortList, true) t.Log("Verify the port status after bringing down the ports") - verifyPortStatus(t, args, tc.downPortList, false) + verifyPortStatus(t, args, tc.DownPortList, false) } - sendTraffic(t, args, tc.capturePortList, captureState) - headerDstIP := map[string][]string{"outerIP": tc.encapHeaderOuterIPList, "innerIP": tc.encapHeaderInnerIPList} - verifyTraffic(t, args, tc.capturePortList, tc.loadBalancePercent, !wantLoss, checkEncap, headerDstIP) + sendTraffic(t, args, tc.CapturePortList, captureState) + headerDstIP := map[string][]string{"outerIP": tc.EncapHeaderOuterIPList, "innerIP": tc.EncapHeaderInnerIPList} + verifyTraffic(t, args, tc.CapturePortList, tc.LoadBalancePercent, !wantLoss, checkEncap, headerDstIP) }) } } diff --git a/feature/gribi/otg_tests/encap_frr_with_repair_vrf/README.md b/feature/gribi/otg_tests/encap_frr_with_reencap_vrf_test/README.md similarity index 99% rename from feature/gribi/otg_tests/encap_frr_with_repair_vrf/README.md rename to feature/gribi/otg_tests/encap_frr_with_reencap_vrf_test/README.md index 3298b85d563..06412bb00e6 100644 --- a/feature/gribi/otg_tests/encap_frr_with_repair_vrf/README.md +++ b/feature/gribi/otg_tests/encap_frr_with_reencap_vrf_test/README.md @@ -540,7 +540,7 @@ NH#1001 -> { 3. Validate that all traffic is no longer encapsulated, and is all egressing out of DUT port-8 per the BGP-ISIS routes in the default VRF. -#### Test-8, no match in TE_VRF_111 +#### Test-9, no match in TE_VRF_111 Tests that if the primary encaps point to tunnels that do not exist, then the traffic should be routed to the `DEFAULT` VRF for further lookup. @@ -606,11 +606,18 @@ NH#102 -> { * network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/action/post-network-instance * network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/action/decap-fallback-network-instance -## Protocol/RPC Parameter Coverage - -* gRIBI: - * Modify - * ModifyRequest +## OpenConfig Path and RPC Coverage +```yaml +rpcs: + gnmi: + gNMI.Get: + gNMI.Set: + gNMI.Subscribe: + gribi: + gRIBI.Get: + gRIBI.Modify: + gRIBI.Flush: +``` ## Required DUT platform diff --git a/feature/gribi/otg_tests/encap_frr_with_reencap_vrf_test/encap_frr_with_reencap_vrf_test.go b/feature/gribi/otg_tests/encap_frr_with_reencap_vrf_test/encap_frr_with_reencap_vrf_test.go new file mode 100644 index 00000000000..0b46cbd04a7 --- /dev/null +++ b/feature/gribi/otg_tests/encap_frr_with_reencap_vrf_test/encap_frr_with_reencap_vrf_test.go @@ -0,0 +1,1049 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package encap_frr_with_repair_vrf_test + +import ( + "bytes" + "context" + "encoding/binary" + "fmt" + "net" + "os" + "sort" + "strconv" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/gopacket" + "github.com/google/gopacket/layers" + "github.com/google/gopacket/pcap" + "github.com/open-traffic-generator/snappi/gosnappi" + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/deviations" + baseScenario "github.com/openconfig/featureprofiles/internal/encap_frr" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/gribi" + "github.com/openconfig/featureprofiles/internal/otgutils" + "github.com/openconfig/featureprofiles/internal/vrfpolicy" + "github.com/openconfig/gribigo/fluent" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/ondatra/netutil" + "github.com/openconfig/ondatra/otg" + "github.com/openconfig/ygnmi/ygnmi" + "github.com/openconfig/ygot/ygot" +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +// Settings for configuring the baseline testbed with the test +// topology. +// +// ATE port-1 <------> port-1 DUT +// DUT port-2 <------> port-2 ATE +// DUT port-3 <------> port-3 ATE +// DUT port-4 <------> port-4 ATE +// DUT port-5 <------> port-5 ATE +// DUT port-6 <------> port-6 ATE +// DUT port-7 <------> port-7 ATE +// DUT port-8 <------> port-8 ATE + +const ( + plenIPv4 = 30 + plenIPv6 = 126 + numPorts = 8 + dscpEncapA1 = 10 + dscpEncapA2 = 18 + dscpEncapB1 = 20 + dscpEncapB2 = 28 + dscpEncapNoMatch = 30 + ipv4OuterSrc111Addr = "198.51.100.111" + ipv4OuterSrc222Addr = "198.51.100.222" + ipv4OuterSrcAddr = "198.100.200.123" + ipv4InnerDst = "138.0.11.8" + ipv4OuterDst333 = "192.58.200.7" + noMatchEncapDest = "20.0.0.1" + prot4 = 4 + prot41 = 41 + polName = "pol1" + gribiIPv4entry = "192.51.100.0" + maskLen24 = "24" + maskLen32 = "32" + niDecapTeVrf = "DECAP_TE_VRF" + niEncapTeVrfA = "ENCAP_TE_VRF_A" + niEncapTeVrfB = "ENCAP_TE_VRF_B" + niTeVrf111 = "TE_VRF_111" + niTeVrf222 = "TE_VRF_222" + niRepairVrf = "REPAIR_VRF" + tolerancePct = 2 + tolerance = 0.2 + encapFlow = "encapFlow" + correspondingHopLimit = 64 + magicIP = "192.168.1.1" + magicMac = "02:00:00:00:00:01" + gribiIPv4EntryDefVRF1 = "192.0.2.101" + gribiIPv4EntryDefVRF2 = "192.0.2.102" + gribiIPv4EntryDefVRF3 = "192.0.2.103" + gribiIPv4EntryDefVRF4 = "192.0.2.104" + gribiIPv4EntryDefVRF5 = "192.0.2.105" + gribiIPv4EntryVRF1111 = "203.0.113.1" + gribiIPv4EntryVRF1112 = "203.0.113.2" + gribiIPv4EntryVRF2221 = "203.0.113.100" + gribiIPv4EntryVRF2222 = "203.0.113.101" + gribiIPv4EntryEncapVRF = "138.0.11.0" + + dutAreaAddress = "49.0001" + dutSysID = "1920.0000.2001" + otgSysID1 = "640000000001" + isisInstance = "DEFAULT" + + otgIsisPort8LoopV4 = "203.0.113.10" + otgIsisPort8LoopV6 = "2001:db8::203:0:113:10" + + dutAS = 65501 + peerGrpName1 = "BGP-PEER-GROUP1" + + ateSrcPort = "ate:port1" + ateSrcPortMac = "02:00:01:01:01:01" + ateSrcNetName = "srcnet" + ateSrcNet = "198.51.100.0" + ateSrcNetCIDR = "198.51.100.0/24" + ateSrcNetFirstIP = "198.51.100.1" + ateSrcNetCount = 250 + ipOverIPProtocol = 4 + + checkEncap = true + wantLoss = true +) + +var ( + portsIPv4 = map[string]string{ + "dut:port1": "192.0.2.1", + "ate:port1": "192.0.2.2", + + "dut:port2": "192.0.2.5", + "ate:port2": "192.0.2.6", + + "dut:port3": "192.0.2.9", + "ate:port3": "192.0.2.10", + + "dut:port4": "192.0.2.13", + "ate:port4": "192.0.2.14", + + "dut:port5": "192.0.2.17", + "ate:port5": "192.0.2.18", + + "dut:port6": "192.0.2.21", + "ate:port6": "192.0.2.22", + + "dut:port7": "192.0.2.25", + "ate:port7": "192.0.2.26", + + "dut:port8": "192.0.2.29", + "ate:port8": "192.0.2.30", + } + portsIPv6 = map[string]string{ + "dut:port1": "2001:db8::192:0:2:1", + "ate:port1": "2001:db8::192:0:2:2", + + "dut:port2": "2001:db8::192:0:2:5", + "ate:port2": "2001:db8::192:0:2:6", + + "dut:port3": "2001:db8::192:0:2:9", + "ate:port3": "2001:db8::192:0:2:a", + + "dut:port4": "2001:db8::192:0:2:d", + "ate:port4": "2001:db8::192:0:2:e", + + "dut:port5": "2001:db8::192:0:2:11", + "ate:port5": "2001:db8::192:0:2:12", + + "dut:port6": "2001:db8::192:0:2:15", + "ate:port6": "2001:db8::192:0:2:16", + + "dut:port7": "2001:db8::192:0:2:19", + "ate:port7": "2001:db8::192:0:2:1a", + + "dut:port8": "2001:db8::192:0:2:1d", + "ate:port8": "2001:db8::192:0:2:1e", + } + otgPortDevices []gosnappi.Device + dutlo0Attrs = attrs.Attributes{ + Desc: "Loopback ip", + IPv4: "203.0.113.11", + IPv6: "2001:db8::203:0:113:1", + IPv4Len: 32, + IPv6Len: 128, + } + loopbackIntfName string + atePortNamelist []string +) + +// awaitTimeout calls a fluent client Await, adding a timeout to the context. +func awaitTimeout(ctx context.Context, t testing.TB, c *fluent.GRIBIClient, timeout time.Duration) error { + t.Helper() + subctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + return c.Await(subctx, t) +} + +type testArgs struct { + ctx context.Context + client *fluent.GRIBIClient + dut *ondatra.DUTDevice + ate *ondatra.ATEDevice + otgConfig gosnappi.Config + top gosnappi.Config + electionID gribi.Uint128 + otg *otg.OTG +} + +// incrementMAC increments the MAC by i. Returns error if the mac cannot be parsed or overflows the mac address space +func incrementMAC(mac string, i int) (string, error) { + macAddr, err := net.ParseMAC(mac) + if err != nil { + return "", err + } + convMac := binary.BigEndian.Uint64(append([]byte{0, 0}, macAddr...)) + convMac = convMac + uint64(i) + buf := new(bytes.Buffer) + err = binary.Write(buf, binary.BigEndian, convMac) + if err != nil { + return "", err + } + newMac := net.HardwareAddr(buf.Bytes()[2:8]) + return newMac.String(), nil +} + +func sortPorts(ports []*ondatra.Port) []*ondatra.Port { + sort.Slice(ports, func(i, j int) bool { + idi, idj := ports[i].ID(), ports[j].ID() + li, lj := len(idi), len(idj) + if li == lj { + return idi < idj + } + return li < lj // "port2" < "port10" + }) + return ports +} + +// dutInterface builds a DUT interface ygot struct for a given port +// according to portsIPv4. Returns nil if the port has no IP address +// mapping. +func dutInterface(p *ondatra.Port, dut *ondatra.DUTDevice) *oc.Interface { + id := fmt.Sprintf("%s:%s", p.Device().ID(), p.ID()) + i := &oc.Interface{ + Name: ygot.String(p.Name()), + Description: ygot.String(p.String()), + Type: oc.IETFInterfaces_InterfaceType_ethernetCsmacd, + } + if deviations.InterfaceEnabled(dut) { + i.Enabled = ygot.Bool(true) + } + + if p.PMD() == ondatra.PMD100GBASEFR { + e := i.GetOrCreateEthernet() + e.AutoNegotiate = ygot.Bool(false) + e.DuplexMode = oc.Ethernet_DuplexMode_FULL + e.PortSpeed = oc.IfEthernet_ETHERNET_SPEED_SPEED_100GB + } + + ipv4, ok := portsIPv4[id] + if !ok { + return nil + } + ipv6, ok := portsIPv6[id] + if !ok { + return nil + } + s := i.GetOrCreateSubinterface(0) + s4 := s.GetOrCreateIpv4() + if deviations.InterfaceEnabled(dut) && !deviations.IPv4MissingEnabled(dut) { + s4.Enabled = ygot.Bool(true) + } + + a := s4.GetOrCreateAddress(ipv4) + a.PrefixLength = ygot.Uint8(plenIPv4) + s6 := s.GetOrCreateIpv6() + if deviations.InterfaceEnabled(dut) { + s6.Enabled = ygot.Bool(true) + } + a6 := s6.GetOrCreateAddress(ipv6) + a6.PrefixLength = ygot.Uint8(plenIPv6) + + return i +} + +// configureDUT configures all the interfaces on the DUT. +func configureDUT(t *testing.T, dut *ondatra.DUTDevice, dutPortList []*ondatra.Port) { + dc := gnmi.OC() + for _, dp := range dutPortList { + + if i := dutInterface(dp, dut); i != nil { + gnmi.Replace(t, dut, dc.Interface(dp.Name()).Config(), i) + } else { + t.Fatalf("No address found for port %v", dp) + } + } + if deviations.ExplicitInterfaceInDefaultVRF(dut) { + for _, dp := range dut.Ports() { + fptest.AssignToNetworkInstance(t, dut, dp.Name(), deviations.DefaultNetworkInstance(dut), 0) + } + } + if deviations.ExplicitPortSpeed(dut) { + for _, dp := range dut.Ports() { + fptest.SetPortSpeed(t, dp) + } + } + + loopbackIntfName = netutil.LoopbackInterface(t, dut, 0) + lo0 := gnmi.OC().Interface(loopbackIntfName).Subinterface(0) + ipv4Addrs := gnmi.LookupAll(t, dut, lo0.Ipv4().AddressAny().State()) + ipv6Addrs := gnmi.LookupAll(t, dut, lo0.Ipv6().AddressAny().State()) + if len(ipv4Addrs) == 0 && len(ipv6Addrs) == 0 { + loop1 := dutlo0Attrs.NewOCInterface(loopbackIntfName, dut) + loop1.Type = oc.IETFInterfaces_InterfaceType_softwareLoopback + gnmi.Update(t, dut, dc.Interface(loopbackIntfName).Config(), loop1) + } else { + v4, ok := ipv4Addrs[0].Val() + if ok { + dutlo0Attrs.IPv4 = v4.GetIp() + } + v6, ok := ipv6Addrs[0].Val() + if ok { + dutlo0Attrs.IPv6 = v6.GetIp() + } + t.Logf("Got DUT IPv4 loopback address: %v", dutlo0Attrs.IPv4) + t.Logf("Got DUT IPv6 loopback address: %v", dutlo0Attrs.IPv6) + } +} + +// configStaticArp configures static arp entries +func configStaticArp(p string, ipv4addr string, macAddr string) *oc.Interface { + i := &oc.Interface{Name: ygot.String(p)} + i.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + s := i.GetOrCreateSubinterface(0) + s4 := s.GetOrCreateIpv4() + n4 := s4.GetOrCreateNeighbor(ipv4addr) + n4.LinkLayerAddress = ygot.String(macAddr) + return i +} + +func staticARPWithMagicUniversalIP(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + sb := &gnmi.SetBatch{} + p2 := dut.Port(t, "port2") + p3 := dut.Port(t, "port3") + p4 := dut.Port(t, "port4") + p5 := dut.Port(t, "port5") + p6 := dut.Port(t, "port6") + p7 := dut.Port(t, "port7") + portList := []*ondatra.Port{p2, p3, p4, p5, p6, p7} + for idx, p := range portList { + s := &oc.NetworkInstance_Protocol_Static{ + Prefix: ygot.String(magicIP + "/32"), + NextHop: map[string]*oc.NetworkInstance_Protocol_Static_NextHop{ + strconv.Itoa(idx): { + Index: ygot.String(strconv.Itoa(idx)), + InterfaceRef: &oc.NetworkInstance_Protocol_Static_NextHop_InterfaceRef{ + Interface: ygot.String(p.Name()), + }, + }, + }, + } + sp := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, deviations.StaticProtocolName(dut)) + gnmi.BatchUpdate(sb, sp.Static(magicIP+"/32").Config(), s) + gnmi.BatchUpdate(sb, gnmi.OC().Interface(p.Name()).Config(), configStaticArp(p.Name(), magicIP, magicMac)) + } + sb.Set(t, dut) +} + +func configureISIS(t *testing.T, dut *ondatra.DUTDevice, intfName, dutAreaAddress, dutSysID string) { + t.Helper() + d := &oc.Root{} + dutConfIsisPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance) + netInstance := d.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + prot := netInstance.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance) + prot.Enabled = ygot.Bool(true) + isis := prot.GetOrCreateIsis() + globalISIS := isis.GetOrCreateGlobal() + globalISIS.LevelCapability = oc.Isis_LevelType_LEVEL_2 + globalISIS.Net = []string{fmt.Sprintf("%v.%v.00", dutAreaAddress, dutSysID)} + globalISIS.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV4, oc.IsisTypes_SAFI_TYPE_UNICAST).Enabled = ygot.Bool(true) + if deviations.ISISInstanceEnabledRequired(dut) { + globalISIS.Instance = ygot.String(isisInstance) + } + isisLevel2 := isis.GetOrCreateLevel(2) + isisLevel2.MetricStyle = oc.Isis_MetricStyle_WIDE_METRIC + if deviations.ISISLevelEnabled(dut) { + isisLevel2.Enabled = ygot.Bool(true) + } + + isisIntf := isis.GetOrCreateInterface(intfName) + isisIntf.Enabled = ygot.Bool(true) + isisIntf.CircuitType = oc.Isis_CircuitType_POINT_TO_POINT + isisIntfLevel := isisIntf.GetOrCreateLevel(2) + isisIntfLevel.Enabled = ygot.Bool(true) + isisIntfLevelAfi := isisIntfLevel.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV4, oc.IsisTypes_SAFI_TYPE_UNICAST) + isisIntfLevelAfi.Metric = ygot.Uint32(200) + if deviations.ISISInterfaceAfiUnsupported(dut) { + isisIntf.Af = nil + } + if deviations.MissingIsisInterfaceAfiSafiEnable(dut) { + isisIntfLevelAfi.Enabled = nil + } + + gnmi.Replace(t, dut, dutConfIsisPath.Config(), prot) +} + +func bgpCreateNbr(localAs uint32, dut *ondatra.DUTDevice) *oc.NetworkInstance_Protocol { + dutOcRoot := &oc.Root{} + ni1 := dutOcRoot.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + niProto := ni1.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") + bgp := niProto.GetOrCreateBgp() + + global := bgp.GetOrCreateGlobal() + global.RouterId = ygot.String(dutlo0Attrs.IPv4) + global.As = ygot.Uint32(localAs) + global.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).Enabled = ygot.Bool(true) + global.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).Enabled = ygot.Bool(true) + pg1 := bgp.GetOrCreatePeerGroup(peerGrpName1) + pg1.PeerAs = ygot.Uint32(localAs) + + bgpNbr := bgp.GetOrCreateNeighbor(otgIsisPort8LoopV4) + bgpNbr.PeerGroup = ygot.String(peerGrpName1) + bgpNbr.PeerAs = ygot.Uint32(localAs) + bgpNbr.Enabled = ygot.Bool(true) + bgpNbrT := bgpNbr.GetOrCreateTransport() + bgpNbrT.LocalAddress = ygot.String(dutlo0Attrs.IPv4) + af4 := bgpNbr.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) + af4.Enabled = ygot.Bool(true) + af6 := bgpNbr.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) + af6.Enabled = ygot.Bool(true) + + return niProto +} + +func verifyISISTelemetry(t *testing.T, dut *ondatra.DUTDevice, dutIntf string) { + t.Helper() + statePath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance).Isis() + + if deviations.ExplicitInterfaceInDefaultVRF(dut) { + dutIntf = dutIntf + ".0" + } + nbrPath := statePath.Interface(dutIntf) + query := nbrPath.LevelAny().AdjacencyAny().AdjacencyState().State() + _, ok := gnmi.WatchAll(t, dut, query, time.Minute, func(val *ygnmi.Value[oc.E_Isis_IsisInterfaceAdjState]) bool { + state, present := val.Val() + return present && state == oc.Isis_IsisInterfaceAdjState_UP + }).Await(t) + if !ok { + t.Logf("IS-IS state on %v has no adjacencies", dutIntf) + t.Fatal("No IS-IS adjacencies reported.") + } +} + +func verifyBgpTelemetry(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + t.Logf("Verifying BGP state.") + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() + + nbrPath := bgpPath.Neighbor(otgIsisPort8LoopV4) + // Get BGP adjacency state. + t.Logf("Waiting for BGP neighbor to establish...") + var status *ygnmi.Value[oc.E_Bgp_Neighbor_SessionState] + status, ok := gnmi.Watch(t, dut, nbrPath.SessionState().State(), time.Minute, func(val *ygnmi.Value[oc.E_Bgp_Neighbor_SessionState]) bool { + state, ok := val.Val() + return ok && state == oc.Bgp_Neighbor_SessionState_ESTABLISHED + }).Await(t) + if !ok { + fptest.LogQuery(t, "BGP reported state", nbrPath.State(), gnmi.Get(t, dut, nbrPath.State())) + t.Fatal("No BGP neighbor formed") + } + state, _ := status.Val() + t.Logf("BGP adjacency for %s: %v", otgIsisPort8LoopV4, state) + if want := oc.Bgp_Neighbor_SessionState_ESTABLISHED; state != want { + t.Errorf("BGP peer %s status got %d, want %d", otgIsisPort8LoopV4, state, want) + } +} + +// configureOTG configures the topology of the ATE. +func configureOTG(t testing.TB, otg *otg.OTG, atePorts []*ondatra.Port) gosnappi.Config { + t.Helper() + config := gosnappi.NewConfig() + pmd100GFRPorts := []string{} + for i, ap := range atePorts { + if ap.PMD() == ondatra.PMD100GBASEFR { + pmd100GFRPorts = append(pmd100GFRPorts, ap.ID()) + } + // DUT and ATE ports are connected by the same names. + dutid := fmt.Sprintf("dut:%s", ap.ID()) + ateid := fmt.Sprintf("ate:%s", ap.ID()) + + port := config.Ports().Add().SetName(ap.ID()) + atePortNamelist = append(atePortNamelist, port.Name()) + portName := fmt.Sprintf("atePort%s", strconv.Itoa(i)) + dev := config.Devices().Add().SetName(portName) + macAddress, _ := incrementMAC(ateSrcPortMac, i) + eth := dev.Ethernets().Add().SetName(portName + ".Eth").SetMac(macAddress) + eth.Connection().SetPortName(port.Name()) + eth.Ipv4Addresses().Add().SetName(portName + ".IPv4"). + SetAddress(portsIPv4[ateid]).SetGateway(portsIPv4[dutid]). + SetPrefix(plenIPv4) + eth.Ipv6Addresses().Add().SetName(portName + ".IPv6"). + SetAddress(portsIPv6[ateid]).SetGateway(portsIPv6[dutid]). + SetPrefix(plenIPv6) + + otgPortDevices = append(otgPortDevices, dev) + if i == 7 { + iDut8LoopV4 := dev.Ipv4Loopbacks().Add().SetName("Port8LoopV4").SetEthName(eth.Name()) + iDut8LoopV4.SetAddress(otgIsisPort8LoopV4) + iDut8LoopV6 := dev.Ipv6Loopbacks().Add().SetName("Port8LoopV6").SetEthName(eth.Name()) + iDut8LoopV6.SetAddress(otgIsisPort8LoopV6) + isisDut := dev.Isis().SetName("ISIS1").SetSystemId(otgSysID1) + isisDut.Basic().SetIpv4TeRouterId(portsIPv4[ateid]).SetHostname(isisDut.Name()).SetLearnedLspFilter(true) + isisDut.Interfaces().Add().SetEthName(dev.Ethernets().Items()[0].Name()). + SetName("devIsisInt1"). + SetLevelType(gosnappi.IsisInterfaceLevelType.LEVEL_2). + SetNetworkType(gosnappi.IsisInterfaceNetworkType.POINT_TO_POINT) + + // Advertise OTG Port8 loopback address via ISIS. + isisPort2V4 := dev.Isis().V4Routes().Add().SetName("ISISPort8V4").SetLinkMetric(10) + isisPort2V4.Addresses().Add().SetAddress(otgIsisPort8LoopV4).SetPrefix(32) + isisPort2V6 := dev.Isis().V6Routes().Add().SetName("ISISPort8V6").SetLinkMetric(10) + isisPort2V6.Addresses().Add().SetAddress(otgIsisPort8LoopV6).SetPrefix(uint32(128)) + iDutBgp := dev.Bgp().SetRouterId(otgIsisPort8LoopV4) + iDutBgp4Peer := iDutBgp.Ipv4Interfaces().Add().SetIpv4Name(iDut8LoopV4.Name()).Peers().Add().SetName(ap.ID() + ".BGP4.peer") + iDutBgp4Peer.SetPeerAddress(dutlo0Attrs.IPv4).SetAsNumber(dutAS).SetAsType(gosnappi.BgpV4PeerAsType.IBGP) + iDutBgp4Peer.Capability().SetIpv4Unicast(true).SetIpv6Unicast(true) + iDutBgp4Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(true) + + bgpNeti1Bgp4PeerRoutes := iDutBgp4Peer.V4Routes().Add().SetName(port.Name() + ".BGP4.Route") + bgpNeti1Bgp4PeerRoutes.SetNextHopIpv4Address(otgIsisPort8LoopV4). + SetNextHopAddressType(gosnappi.BgpV4RouteRangeNextHopAddressType.IPV4). + SetNextHopMode(gosnappi.BgpV4RouteRangeNextHopMode.MANUAL). + Advanced().SetLocalPreference(100).SetIncludeLocalPreference(true) + bgpNeti1Bgp4PeerRoutes.Addresses().Add().SetAddress(ipv4InnerDst).SetPrefix(32). + SetCount(1).SetStep(1) + bgpNeti1Bgp4PeerRoutes.Addresses().Add().SetAddress(noMatchEncapDest).SetPrefix(32). + SetCount(1).SetStep(1) + } + + } + config.Captures().Add().SetName("packetCapture"). + SetPortNames([]string{atePortNamelist[1], atePortNamelist[2], atePortNamelist[3], atePortNamelist[4], + atePortNamelist[5], atePortNamelist[6], atePortNamelist[7]}). + SetFormat(gosnappi.CaptureFormat.PCAP) + + // Disable FEC for 100G-FR ports because Novus does not support it. + if len(pmd100GFRPorts) > 0 { + l1Settings := config.Layer1().Add().SetName("L1").SetPortNames(pmd100GFRPorts) + l1Settings.SetAutoNegotiate(true).SetIeeeMediaDefaults(false).SetSpeed("speed_100_gbps") + autoNegotiate := l1Settings.AutoNegotiation() + autoNegotiate.SetRsFec(false) + } + + otg.PushConfig(t, config) + time.Sleep(30 * time.Second) + otg.StartProtocols(t) + time.Sleep(30 * time.Second) + pb, _ := config.Marshal().ToProto() + t.Log(pb.GetCaptures()) + return config +} + +func createFlow(t *testing.T, config gosnappi.Config, otg *otg.OTG, trafficDestIP string) { + t.Helper() + + config.Flows().Clear() + + flow1 := gosnappi.NewFlow().SetName(encapFlow) + flow1.Metrics().SetEnable(true) + flow1.TxRx().Device(). + SetTxNames([]string{otgPortDevices[0].Name() + ".IPv4"}). + SetRxNames([]string{otgPortDevices[1].Name() + ".IPv4", otgPortDevices[2].Name() + ".IPv4", otgPortDevices[3].Name() + ".IPv4", + otgPortDevices[4].Name() + ".IPv4", otgPortDevices[5].Name() + ".IPv4", otgPortDevices[6].Name() + ".IPv4", + otgPortDevices[7].Name() + ".IPv4", + }) + flow1.Size().SetFixed(512) + flow1.Rate().SetPps(100) + flow1.Duration().Continuous() + + ethHeader1 := flow1.Packet().Add().Ethernet() + ethHeader1.Src().SetValue(ateSrcPortMac) + + IPHeader := flow1.Packet().Add().Ipv4() + IPHeader.Src().Increment().SetCount(1000).SetStep("0.0.0.1").SetStart(ipv4OuterSrcAddr) + IPHeader.Dst().SetValue(trafficDestIP) + IPHeader.Priority().Dscp().Phb().SetValue(dscpEncapA1) + + UDPHeader := flow1.Packet().Add().Udp() + UDPHeader.DstPort().Increment().SetStart(1).SetCount(50000).SetStep(1) + UDPHeader.SrcPort().Increment().SetStart(1).SetCount(50000).SetStep(1) + + config.Flows().Append(flow1) + + t.Logf("Pushing traffic flows to OTG and starting protocols...") + otg.PushConfig(t, config) + time.Sleep(30 * time.Second) + otg.StartProtocols(t) + time.Sleep(30 * time.Second) +} + +func startCapture(t *testing.T, args *testArgs, capturePortList []string) gosnappi.ControlState { + t.Helper() + args.otgConfig.Captures().Clear() + args.otgConfig.Captures().Add().SetName("packetCapture"). + SetPortNames(capturePortList). + SetFormat(gosnappi.CaptureFormat.PCAP) + args.otg.PushConfig(t, args.otgConfig) + time.Sleep(30 * time.Second) + args.otg.StartProtocols(t) + time.Sleep(30 * time.Second) + cs := gosnappi.NewControlState() + cs.Port().Capture().SetState(gosnappi.StatePortCaptureState.START) + args.otg.SetControlState(t, cs) + return cs +} + +func sendTraffic(t *testing.T, args *testArgs, capturePortList []string, cs gosnappi.ControlState) { + t.Helper() + otgutils.WaitForARP(t, args.otg, args.top, "IPv4") + t.Logf("Starting traffic") + args.otg.StartTraffic(t) + time.Sleep(15 * time.Second) + t.Logf("Stop traffic") + args.otg.StopTraffic(t) + cs.Port().Capture().SetState(gosnappi.StatePortCaptureState.STOP) + args.otg.SetControlState(t, cs) +} + +func verifyTraffic(t *testing.T, args *testArgs, capturePortList []string, loadBalancePercent []float64, wantLoss, checkEncap bool, headerDstIP map[string][]string) { + t.Helper() + t.Logf("Verifying flow metrics for the flow: encapFlow\n") + recvMetric := gnmi.Get(t, args.otg, gnmi.OTG().Flow(encapFlow).State()) + txPackets := recvMetric.GetCounters().GetOutPkts() + rxPackets := recvMetric.GetCounters().GetInPkts() + lostPackets := txPackets - rxPackets + var lossPct uint64 + if txPackets != 0 { + lossPct = lostPackets * 100 / txPackets + } else { + t.Errorf("Traffic stats are not correct %v", recvMetric) + } + if wantLoss { + if lossPct < 100-tolerancePct { + t.Errorf("Traffic is expected to fail %s\n got %v, want 100%% failure", encapFlow, lossPct) + } else { + t.Logf("Traffic Loss Test Passed!") + } + } else { + if lossPct > tolerancePct { + t.Errorf("Traffic Loss Pct for Flow: %s\n got %v, want 0", encapFlow, lossPct) + } else { + t.Logf("Traffic Test Passed!") + } + } + t.Log("Verify packet load balancing as per the programmed weight") + validateTrafficDistribution(t, args.ate, loadBalancePercent) + var pcapFileList []string + for _, capturePort := range capturePortList { + bytes := args.otg.GetCapture(t, gosnappi.NewCaptureRequest().SetPortName(capturePort)) + pcapFileName, err := os.CreateTemp("", "pcap") + if err != nil { + t.Errorf("ERROR: Could not create temporary pcap file: %v\n", err) + } + if _, err := pcapFileName.Write(bytes); err != nil { + t.Errorf("ERROR: Could not write bytes to pcap file: %v\n", err) + } + pcapFileName.Close() + pcapFileList = append(pcapFileList, pcapFileName.Name()) + } + validatePackets(t, pcapFileList, checkEncap, headerDstIP) + args.otgConfig.Captures().Clear() + args.otg.PushConfig(t, args.otgConfig) + time.Sleep(30 * time.Second) +} + +func validatePackets(t *testing.T, filename []string, checkEncap bool, headerDstIP map[string][]string) { + t.Helper() + for index, file := range filename { + fileStat, err := os.Stat(file) + if err != nil { + t.Errorf("Filestat for pcap file failed %s", err) + } + fileSize := fileStat.Size() + if fileSize > 0 { + handle, err := pcap.OpenOffline(file) + if err != nil { + t.Errorf("Unable to open the pcap file, error: %s", err) + } else { + packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) + if checkEncap { + validateTrafficEncap(t, packetSource, headerDstIP, index) + } + } + defer handle.Close() + } + } +} + +func validateTrafficEncap(t *testing.T, packetSource *gopacket.PacketSource, headerDstIP map[string][]string, index int) { + t.Helper() + for packet := range packetSource.Packets() { + ipLayer := packet.Layer(layers.LayerTypeIPv4) + if ipLayer == nil { + continue + } + ipPacket, _ := ipLayer.(*layers.IPv4) + encapHeaderListLength := len(headerDstIP["outerIP"]) + if index <= encapHeaderListLength-1 { + innerPacket := gopacket.NewPacket(ipPacket.Payload, ipPacket.NextLayerType(), gopacket.Default) + ipInnerLayer := innerPacket.Layer(layers.LayerTypeIPv4) + if ipInnerLayer != nil { + destIP := ipPacket.DstIP.String() + t.Logf("Outer dest ip in received packet %s", destIP) + if ipPacket.DstIP.String() != headerDstIP["outerIP"][index] { + t.Errorf("Packets are not encapsulated") + } + ipInnerPacket, _ := ipInnerLayer.(*layers.IPv4) + if ipInnerPacket.DstIP.String() != headerDstIP["innerIP"][index] { + t.Errorf("Packets are not encapsulated") + } + t.Logf("Traffic for encap routes passed.") + break + } else { + t.Errorf("Packet is not encapsulated") + } + } else if index >= encapHeaderListLength || encapHeaderListLength == 0 { + if ipPacket.DstIP.String() == otgIsisPort8LoopV4 { + continue + } else if ipPacket.DstIP.String() != headerDstIP["innerIP"][0] { + destIP := ipPacket.DstIP.String() + t.Logf("Dest ip in received packet %s", destIP) + t.Errorf("Packets are encapsulated which is not expected") + } else { + t.Logf("Traffic for non-encap routes passed.") + break + } + } + } +} + +func verifyPortStatus(t *testing.T, args *testArgs, portList []string, portStatus bool) { + wantStatus := oc.Interface_OperStatus_UP + if !portStatus { + wantStatus = oc.Interface_OperStatus_DOWN + } + for _, port := range portList { + p := args.dut.Port(t, port) + t.Log("Check for port status") + gnmi.Await(t, args.dut, gnmi.OC().Interface(p.Name()).OperStatus().State(), 1*time.Minute, wantStatus) + operStatus := gnmi.Get(t, args.dut, gnmi.OC().Interface(p.Name()).OperStatus().State()) + if operStatus != wantStatus { + t.Errorf("Get(DUT %v oper status): got %v, want %v", port, operStatus, wantStatus) + } + } +} + +// setDUTInterfaceState sets the admin state on the dut interface +func setDUTInterfaceWithState(t testing.TB, dut *ondatra.DUTDevice, p *ondatra.Port, state bool) { + dc := gnmi.OC() + i := &oc.Interface{} + i.Enabled = ygot.Bool(state) + i.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + i.Name = ygot.String(p.Name()) + gnmi.Update(t, dut, dc.Interface(p.Name()).Config(), i) +} + +func portState(t *testing.T, args *testArgs, portList []string, portEnabled bool) { + t.Logf("Change port enable state to %t", portEnabled) + if deviations.ATEPortLinkStateOperationsUnsupported(args.ate) { + for _, port := range portList { + p := args.dut.Port(t, port) + if portEnabled { + setDUTInterfaceWithState(t, args.dut, p, true) + } else { + setDUTInterfaceWithState(t, args.dut, p, false) + } + } + } else { + var portNames []string + for _, p := range portList { + portNames = append(portNames, args.ate.Port(t, p).ID()) + } + portStateAction := gosnappi.NewControlState() + if portEnabled { + portStateAction.Port().Link().SetPortNames(portNames).SetState(gosnappi.StatePortLinkState.UP) + } else { + portStateAction.Port().Link().SetPortNames(portNames).SetState(gosnappi.StatePortLinkState.DOWN) + } + args.ate.OTG().SetControlState(t, portStateAction) + } +} + +func normalize(xs []uint64) (ys []float64, sum uint64) { + for _, x := range xs { + sum += x + } + ys = make([]float64, len(xs)) + for i, x := range xs { + ys[i] = float64(x) / float64(sum) + } + return ys, sum +} + +func validateTrafficDistribution(t *testing.T, ate *ondatra.ATEDevice, wantWeights []float64) { + inFramesAllPorts := gnmi.GetAll(t, ate.OTG(), gnmi.OTG().PortAny().Counters().InFrames().State()) + // Skip source port, Port1. + gotWeights, _ := normalize(inFramesAllPorts[1:]) + + t.Log("got ratio:", gotWeights) + t.Log("want ratio:", wantWeights) + if diff := cmp.Diff(wantWeights, gotWeights, cmpopts.EquateApprox(0, tolerance)); diff != "" { + t.Errorf("Packet distribution ratios -want,+got:\n%s", diff) + } +} + +// TestEncapFrr is to test Test FRR behaviors with encapsulation scenarios +func TestEncapFrr(t *testing.T) { + ctx := context.Background() + dut := ondatra.DUT(t, "dut") + + gribic := dut.RawAPIs().GRIBI(t) + ate := ondatra.ATE(t, "ate") + top := gosnappi.NewConfig() + dutPorts := sortPorts(dut.Ports())[0:8] + atePorts := sortPorts(ate.Ports())[0:8] + + t.Log("Configure Default Network Instance") + fptest.ConfigureDefaultNetworkInstance(t, dut) + + if deviations.BackupNHGRequiresVrfWithDecap(dut) { + d := &oc.Root{} + ni := d.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + pf := ni.GetOrCreatePolicyForwarding() + fp1 := pf.GetOrCreatePolicy("match-ipip") + fp1.SetType(oc.Policy_Type_VRF_SELECTION_POLICY) + fp1.GetOrCreateRule(1).GetOrCreateIpv4().Protocol = oc.UnionUint8(ipOverIPProtocol) + fp1.GetOrCreateRule(1).GetOrCreateAction().NetworkInstance = ygot.String(deviations.DefaultNetworkInstance(dut)) + p1 := dut.Port(t, "port1") + intf := pf.GetOrCreateInterface(p1.Name()) + intf.ApplyVrfSelectionPolicy = ygot.String("match-ipip") + gnmi.Replace(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).PolicyForwarding().Config(), pf) + } + + configureDUT(t, dut, dutPorts) + + t.Log("Apply vrf selection policy to DUT port-1") + vrfpolicy.ConfigureVRFSelectionPolicy(t, dut, vrfpolicy.VRFPolicyC) + + if deviations.GRIBIMACOverrideStaticARPStaticRoute(dut) { + staticARPWithMagicUniversalIP(t, dut) + } + + t.Log("Install BGP route resolved by ISIS.") + t.Log("Configure ISIS on DUT") + configureISIS(t, dut, dut.Port(t, "port8").Name(), dutAreaAddress, dutSysID) + + dutConfPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") + gnmi.Delete(t, dut, dutConfPath.Config()) + dutConf := bgpCreateNbr(dutAS, dut) + gnmi.Replace(t, dut, dutConfPath.Config(), dutConf) + fptest.LogQuery(t, "DUT BGP Config", dutConfPath.Config(), gnmi.Get(t, dut, dutConfPath.Config())) + + otg := ate.OTG() + otgConfig := configureOTG(t, otg, atePorts) + + verifyISISTelemetry(t, dut, dutPorts[7].Name()) + verifyBgpTelemetry(t, dut) + + // Connect gRIBI client to DUT referred to as gRIBI - using PRESERVE persistence and + // SINGLE_PRIMARY mode, with FIB ACK requested. Specify gRIBI as the leader. + client := fluent.NewClient() + client.Connection().WithStub(gribic).WithPersistence().WithInitialElectionID(1, 0). + WithFIBACK().WithRedundancyMode(fluent.ElectedPrimaryClient) + client.Start(ctx, t) + defer client.Stop(t) + + defer func() { + // Flush all entries after test. + if err := gribi.FlushAll(client); err != nil { + t.Error(err) + } + }() + + client.StartSending(ctx, t) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Fatalf("Await got error during session negotiation for clientA: %v", err) + } + eID := gribi.BecomeLeader(t, client) + + args := &testArgs{ + client: client, + dut: dut, + ate: ate, + otgConfig: otgConfig, + top: top, + electionID: eID, + otg: otg, + } + + testCases := baseScenario.TestCases(atePortNamelist, ipv4InnerDst) + testCases = append(testCases, + &baseScenario.TestCase{ + Desc: "Test-8: no match in TE_VRF_222", + DownPortList: []string{"port2", "port3", "port4", "port6"}, + CapturePortList: []string{atePortNamelist[7]}, + EncapHeaderOuterIPList: []string{}, + EncapHeaderInnerIPList: []string{ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0, 0, 0, 1}, + TestID: "teVrf222NoMatch", + }, + &baseScenario.TestCase{ + Desc: "Test-9: no match in TE_VRF_111", + DownPortList: []string{"port2", "port3", "port4", "port6"}, + CapturePortList: []string{atePortNamelist[7]}, + EncapHeaderOuterIPList: []string{}, + EncapHeaderInnerIPList: []string{ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0, 0, 0, 1}, + TestID: "teVrf111NoMatch", + }, + ) + for _, tc := range testCases { + t.Run(tc.Desc, func(t *testing.T) { + t.Log("Verify whether the ports are in up state") + portList := []string{"port2", "port3", "port4", "port5", "port6", "port7", "port8"} + verifyPortStatus(t, args, portList, true) + + t.Log("Flush existing gRIBI routes before test.") + if err := gribi.FlushAll(client); err != nil { + t.Fatal(err) + } + baseCapturePortList := []string{atePortNamelist[1], atePortNamelist[5]} + baseScenario.ConfigureBaseGribiRoutes(ctx, t, dut, args.client) + createFlow(t, otgConfig, otg, ipv4InnerDst) + captureState := startCapture(t, args, baseCapturePortList) + sendTraffic(t, args, baseCapturePortList, captureState) + baseHeaderDstIP := map[string][]string{"outerIP": {gribiIPv4EntryVRF1111, gribiIPv4EntryVRF1112}, "innerIP": {ipv4InnerDst, ipv4InnerDst}} + baseLoadBalancePercent := []float64{0.0156, 0.0468, 0.1875, 0, 0.75, 0, 0} + verifyTraffic(t, args, baseCapturePortList, baseLoadBalancePercent, !wantLoss, checkEncap, baseHeaderDstIP) + + if tc.TestID == "primaryBackupRoutingSingle" { + args.client.Modify().ReplaceEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1000).WithDecapsulateHeader(fluent.IPinIP). + WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1000).AddNextHop(1000, 1), + ) + if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + } + if tc.TestID == "primaryBackupRoutingAll" { + args.client.Modify().ReplaceEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1000).WithDecapsulateHeader(fluent.IPinIP). + WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1000).AddNextHop(1000, 1), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1001).WithDecapsulateHeader(fluent.IPinIP). + WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1001).AddNextHop(1001, 1), + ) + if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + } + if tc.TestID == "encapNoMatch" { + args.client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1003).WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1003).AddNextHop(1003, 1), + fluent.IPv4Entry().WithNetworkInstance(niEncapTeVrfA). + WithPrefix("0.0.0.0/0").WithNextHopGroup(1003).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)), + ) + if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + createFlow(t, otgConfig, otg, noMatchEncapDest) + } + if tc.TestID == "teVrf222NoMatch" { + args.client.Modify().ReplaceEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1000).WithDecapsulateHeader(fluent.IPinIP).WithEncapsulateHeader(fluent.IPinIP). + WithIPinIP(ipv4OuterSrc222Addr, gribiIPv4EntryVRF2221).WithNextHopNetworkInstance(niTeVrf222), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1000).AddNextHop(1000, 1), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1001).WithDecapsulateHeader(fluent.IPinIP).WithEncapsulateHeader(fluent.IPinIP). + WithIPinIP(ipv4OuterSrc222Addr, gribiIPv4EntryVRF2222).WithNextHopNetworkInstance(niTeVrf222), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1001).AddNextHop(1001, 1), + ) + if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + } + if tc.TestID == "teVrf111NoMatch" { + args.client.Modify().ReplaceEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(101).WithEncapsulateHeader(fluent.IPinIP).WithIPinIP(ipv4OuterSrc111Addr, "203.100.113.1"). + WithNextHopNetworkInstance(niTeVrf111), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(102).WithEncapsulateHeader(fluent.IPinIP).WithIPinIP(ipv4OuterSrc111Addr, "203.100.113.2"). + WithNextHopNetworkInstance(niTeVrf111), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(101).AddNextHop(101, 1).AddNextHop(102, 3).WithBackupNHG(2001), + ) + if err := awaitTimeout(args.ctx, t, args.client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + } + + captureState = startCapture(t, args, tc.CapturePortList) + if len(tc.DownPortList) > 0 { + t.Logf("Bring down ports %s", tc.DownPortList) + portState(t, args, tc.DownPortList, false) + defer portState(t, args, tc.DownPortList, true) + t.Log("Verify the port status after bringing down the ports") + verifyPortStatus(t, args, tc.DownPortList, false) + } + sendTraffic(t, args, tc.CapturePortList, captureState) + headerDstIP := map[string][]string{"outerIP": tc.EncapHeaderOuterIPList, "innerIP": tc.EncapHeaderInnerIPList} + + if deviations.EncapTunnelShutBackupNhgZeroTraffic(dut) { + if tc.TestID == "primarySingle" || tc.TestID == "primaryBackupSingle" || tc.TestID == "primaryBackupRoutingSingle" { + tc.LoadBalancePercent = []float64{0, 0, 0, 0, 1, 0, 0} + } else if tc.TestID == "primaryAll" { + tc.LoadBalancePercent = []float64{0, 0, 0, 0, 0, 0, 1} + } + } + verifyTraffic(t, args, tc.CapturePortList, tc.LoadBalancePercent, !wantLoss, checkEncap, headerDstIP) + }) + } +} diff --git a/feature/gribi/otg_tests/encap_frr_with_reencap_vrf_test/metadata.textproto b/feature/gribi/otg_tests/encap_frr_with_reencap_vrf_test/metadata.textproto new file mode 100644 index 00000000000..1fffbb034f2 --- /dev/null +++ b/feature/gribi/otg_tests/encap_frr_with_reencap_vrf_test/metadata.textproto @@ -0,0 +1,57 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "f8eee005-f5dc-4c58-a7f2-444571bcd49f" +plan_id: "TE-16.3" +description: "encapsulation FRR scenarios" +testbed: TESTBED_DUT_ATE_8LINKS + +platform_exceptions: { + platform: { + vendor: CISCO + } + deviations: { + ipv4_missing_enabled: true + gribi_mac_override_with_static_arp: true + interface_ref_interface_id_format: true + pf_require_match_default_rule: true + pf_require_sequential_order_pbr_rules: true + } +} +platform_exceptions: { + platform: { + vendor: JUNIPER + } + deviations: { + isis_level_enabled: true + } +} +platform_exceptions: { + platform: { + vendor: NOKIA + } + deviations: { + explicit_port_speed: true + explicit_interface_in_default_vrf: true + interface_enabled: true + } +} +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + interface_enabled: true + default_network_instance: "default" + isis_instance_enabled_required: true + static_protocol_name: "STATIC" + gribi_mac_override_static_arp_static_route: true + ttl_copy_unsupported: true + omit_l2_mtu: true + backup_nhg_requires_vrf_with_decap: true + missing_isis_interface_afi_safi_enable: true + isis_interface_afi_unsupported: true + encap_tunnel_shut_backup_nhg_zero_traffic: true + } +} + diff --git a/feature/gribi/otg_tests/static_lsp_test/static_lsp_test.go b/feature/gribi/otg_tests/static_lsp_test/static_lsp_test.go index f1deedd9d71..712f8ebed32 100644 --- a/feature/gribi/otg_tests/static_lsp_test/static_lsp_test.go +++ b/feature/gribi/otg_tests/static_lsp_test/static_lsp_test.go @@ -1,4 +1,4 @@ -package mplsstaticlabel +package static_lsp_test import ( "fmt" diff --git a/feature/platform/tests/telemetry_inventory_test/README.md b/feature/platform/tests/telemetry_inventory_test/README.md index bbdadf87d62..7cbcdde3ae2 100644 --- a/feature/platform/tests/telemetry_inventory_test/README.md +++ b/feature/platform/tests/telemetry_inventory_test/README.md @@ -19,30 +19,65 @@ card, power supply, disk, flash, NPU, transceiver, fabric card), validate: * TODO: /components/component/linecard/config -## Telemetry Parameter coverage - -* /components/component[name=]/state/temperature/instant -* /components/component/storage -* TODO: /components/component/software-module -* TODO: /components/component/software-module/state/module-type -* /components/component/state/description -* /components/component/state/firmware-version -* /components/component/state/hardware-version -* /components/component/state/id -* /components/component/state/mfg-date -* /components/component/state/mfg-name -* /components/component/state/name -* /components/component/state/oper-status -* /components/component/state/parent -* /components/component/state/part-no -* /components/component/state/serial-no -* /components/component/state/software-version -* /components/component/state/type -* /components/component/state/temperature/alarm-status -* /components/component/state/temperature/instant -* /components/component/state/temperature/max -* /components/component/state/temperature/max-time -* /components/component/integrated-circuit/backplane-facing-capacity/state/available-pct -* /components/component/integrated-circuit/backplane-facing-capacity/state/consumed-capacity -* /components/component/integrated-circuit/backplane-facing-capacity/state/total -* /components/component/integrated-circuit/backplane-facing-capacity/state/total-operational-capacity +## OpenConfig Path and RPC Coverage + +TODO: + /components/component/storage + /components/component/software-module + /components/component/software-module/state/module-type + /components/component/state/mfg-date + /components/component/state/software-version + +```yaml +paths: + /components/component/state/description: + platform_type: ["CHASSIS", "CONTROLLER_CARD", "FABRIC", "FAN", "FRU", "LINECARD", "POWER_SUPPLY"] + /components/component/state/firmware-version: + platform_type: ["TRANSCEIVER"] + /components/component/state/hardware-version: + platform_type: ["CHASSIS", "CONTROLLER_CARD", "FABRIC", "LINECARD", "POWER_SUPPLY", "TRANSCEIVER"] + /components/component/state/id: + platform_type: ["CONTROLLER_CARD", "FABRIC", "FAN", "INTEGRATED_CIRCUIT", "LINECARD", "POWER_SUPPLY", "SENSOR"] + /components/component/state/install-component: + platform_type: ["FABRIC", "FAN", "FAN_TRAY", "FRU", "CONTROLLER_CARD", "LINECARD", "POWER_SUPPLY", "TRANSCEIVER"] + /components/component/state/install-position: + platform_type: ["FABRIC", "FAN", "FAN_TRAY", "FRU", "CONTROLLER_CARD", "LINECARD", "POWER_SUPPLY", "TRANSCEIVER"] + /components/component/state/location: + platform_type: ["FABRIC", "FAN", "FAN_TRAY", "FRU", "CONTROLLER_CARD", "LINECARD", "POWER_SUPPLY", "TRANSCEIVER"] + /components/component/state/mfg-name: + platform_type: ["CHASSIS", "CONTROLLER_CARD", "FABRIC", "LINECARD", "POWER_SUPPLY", "TRANSCEIVER"] + /components/component/state/model-name: + platform_type: ["CHASSIS"] + /components/component/state/name: + platform_type: ["CHASSIS", "CONTROLLER_CARD", "CPU", "FABRIC", "FAN", "INTEGRATED_CIRCUIT", "LINECARD", "POWER_SUPPLY", "SENSOR", "STORAGE", "TRANSCEIVER"] + /components/component/state/oper-status: + platform_type: ["CHASSIS", "CONTROLLER_CARD", "CPU", "FABRIC", "FAN", "INTEGRATED_CIRCUIT", "LINECARD", "POWER_SUPPLY", "STORAGE", "TRANSCEIVER"] + /components/component/state/parent: + platform_type: ["CONTROLLER_CARD", "FABRIC", "FRU", "LINECARD", "POWER_SUPPLY"] + /components/component/state/part-no: + platform_type: ["CHASSIS", "CONTROLLER_CARD", "CPU", "FABRIC", "FAN", "FRU", "LINECARD", "POWER_SUPPLY", "STORAGE", "TRANSCEIVER"] + /components/component/state/serial-no: + platform_type: ["CHASSIS", "CONTROLLER_CARD", "CPU", "FABRIC", "FAN", "FRU", "LINECARD", "POWER_SUPPLY", "STORAGE", "TRANSCEIVER"] + /components/component/state/type: + platform_type: ["CHASSIS", "CONTROLLER_CARD", "CPU", "FABRIC", "FAN", "INTEGRATED_CIRCUIT", "LINECARD", "POWER_SUPPLY", "SENSOR", "STORAGE", "TRANSCEIVER"] + /components/component/state/temperature/alarm-status: + platform_type: ["SENSOR"] + /components/component/state/temperature/instant: + platform_type: ["SENSOR"] + /components/component/state/temperature/max: + platform_type: ["SENSOR"] + /components/component/state/temperature/max-time: + platform_type: ["SENSOR"] + /components/component/integrated-circuit/backplane-facing-capacity/state/available-pct: + platform_type: ["INTEGRATED_CIRCUIT"] + /components/component/integrated-circuit/backplane-facing-capacity/state/consumed-capacity: + platform_type: ["INTEGRATED_CIRCUIT"] + /components/component/integrated-circuit/backplane-facing-capacity/state/total: + platform_type: ["INTEGRATED_CIRCUIT"] + /components/component/integrated-circuit/backplane-facing-capacity/state/total-operational-capacity: + platform_type: ["INTEGRATED_CIRCUIT"] + +rpcs: + gnmi: + gNMI.Get: +``` diff --git a/feature/platform/tests/telemetry_inventory_test/metadata.textproto b/feature/platform/tests/telemetry_inventory_test/metadata.textproto index b8c5d5b234a..7e505d97adc 100644 --- a/feature/platform/tests/telemetry_inventory_test/metadata.textproto +++ b/feature/platform/tests/telemetry_inventory_test/metadata.textproto @@ -5,6 +5,24 @@ uuid: "44fed7d9-4c79-4952-8b83-fbd5f7138ae9" plan_id: "gNMI-1.4" description: "Telemetry: Inventory" testbed: TESTBED_DUT_ATE_2LINKS +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + install_position_and_install_component_unsupported: true + model_name_unsupported: true + } +} +platform_exceptions: { + platform: { + vendor: CISCO + } + deviations: { + install_position_and_install_component_unsupported: true + model_name_unsupported: true + } +} platform_exceptions: { platform: { vendor: JUNIPER @@ -12,6 +30,8 @@ platform_exceptions: { deviations: { switch_chip_id_unsupported: true backplane_facing_capacity_unsupported: true + install_position_and_install_component_unsupported: true + model_name_unsupported: true storage_component_unsupported: true } } @@ -21,6 +41,7 @@ platform_exceptions: { } deviations: { backplane_facing_capacity_unsupported: true + install_position_and_install_component_unsupported: true + model_name_unsupported: true } } - diff --git a/feature/platform/tests/telemetry_inventory_test/telemetry_inventory_test.go b/feature/platform/tests/telemetry_inventory_test/telemetry_inventory_test.go index 0e6ef43e82f..9637b62ac8b 100644 --- a/feature/platform/tests/telemetry_inventory_test/telemetry_inventory_test.go +++ b/feature/platform/tests/telemetry_inventory_test/telemetry_inventory_test.go @@ -43,25 +43,60 @@ var componentType = map[string]oc.E_PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT{ "Storage": oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_STORAGE, } +// validInstallComponentTypes indicates for each component type, which types of +// install-component it can have (i.e., what types of components can it be installed into). +var validInstallComponentTypes = map[oc.Component_Type_Union]map[oc.Component_Type_Union]bool{ + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_CONTROLLER_CARD: { + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_CHASSIS: true, + }, + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_FABRIC: { + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_CHASSIS: true, + }, + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_FAN: { + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_CHASSIS: true, + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_FAN_TRAY: true, + }, + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_FAN_TRAY: { + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_CHASSIS: true, + }, + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_LINECARD: { + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_CHASSIS: true, + }, + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_POWER_SUPPLY: { + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_CHASSIS: true, + // Sometimes the parent is the power tray, which has type FRU. + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_FRU: true, + }, + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_TRANSCEIVER: { + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_CHASSIS: true, + oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_LINECARD: true, + }, +} + // use this map to cache related components used in subtests to run the test faster. var componentsByType map[string][]*oc.Component // Define a superset of the checklist for each component type properties struct { - descriptionValidation bool - idValidation bool - nameValidation bool - partNoValidation bool - serialNoValidation bool - mfgNameValidation bool - mfgDateValidation bool - swVerValidation bool - hwVerValidation bool - fwVerValidation bool - rrValidation bool - operStatus oc.E_PlatformTypes_COMPONENT_OPER_STATUS - parentValidation bool - pType oc.Component_Type_Union + descriptionValidation bool + idValidation bool + installPositionAndComponentValidation bool + nameValidation bool + partNoValidation bool + serialNoValidation bool + mfgNameValidation bool + mfgDateValidation bool + // If modelNameValidation is being used, the /components/component/state/model-name + // of the chassis component must be equal to the ondatra hardware_model name + // of its device. + modelNameValidation bool + swVerValidation bool + hwVerValidation bool + fwVerValidation bool + rrValidation bool + operStatus oc.E_PlatformTypes_COMPONENT_OPER_STATUS + parentValidation bool + pType oc.Component_Type_Union } func TestMain(m *testing.M) { @@ -93,6 +128,7 @@ func TestMain(m *testing.M) { // - Validate telemetry /components/component/storage exists. // - TempSensor // - Validate telemetry /components/component/state/temperature/instant exists. +// - Validate telemetry /components/component/state/model-name for Chassis. // // Topology: // @@ -133,6 +169,7 @@ func TestHardwareCards(t *testing.T) { serialNoValidation: true, mfgNameValidation: true, mfgDateValidation: false, + modelNameValidation: true, hwVerValidation: true, fwVerValidation: false, rrValidation: false, @@ -143,106 +180,112 @@ func TestHardwareCards(t *testing.T) { }, { desc: "Fabric", cardFields: properties{ - descriptionValidation: true, - idValidation: true, - nameValidation: true, - partNoValidation: true, - serialNoValidation: true, - mfgNameValidation: true, - mfgDateValidation: false, - hwVerValidation: true, - fwVerValidation: false, - rrValidation: false, - operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE, - parentValidation: true, - pType: componentType["Fabric"], + descriptionValidation: true, + idValidation: true, + installPositionAndComponentValidation: true, + nameValidation: true, + partNoValidation: true, + serialNoValidation: true, + mfgNameValidation: true, + mfgDateValidation: false, + hwVerValidation: true, + fwVerValidation: false, + rrValidation: false, + operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE, + parentValidation: true, + pType: componentType["Fabric"], }, }, { desc: "Fan", cardFields: properties{ - descriptionValidation: true, - idValidation: false, - nameValidation: true, - partNoValidation: true, - serialNoValidation: true, - mfgNameValidation: false, - mfgDateValidation: false, - hwVerValidation: false, - fwVerValidation: false, - rrValidation: false, - operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE, - parentValidation: false, - pType: componentType["Fan"], + descriptionValidation: true, + idValidation: false, + installPositionAndComponentValidation: true, + nameValidation: true, + partNoValidation: true, + serialNoValidation: true, + mfgNameValidation: false, + mfgDateValidation: false, + hwVerValidation: false, + fwVerValidation: false, + rrValidation: false, + operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE, + parentValidation: false, + pType: componentType["Fan"], }, }, { desc: "Linecard", cardFields: properties{ - descriptionValidation: true, - idValidation: true, - nameValidation: true, - partNoValidation: true, - serialNoValidation: true, - mfgNameValidation: true, - mfgDateValidation: false, - hwVerValidation: true, - fwVerValidation: false, - rrValidation: false, - operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE, - parentValidation: true, - pType: oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_LINECARD, + descriptionValidation: true, + idValidation: true, + installPositionAndComponentValidation: true, + nameValidation: true, + partNoValidation: true, + serialNoValidation: true, + mfgNameValidation: true, + mfgDateValidation: false, + hwVerValidation: true, + fwVerValidation: false, + rrValidation: false, + operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE, + parentValidation: true, + pType: oc.PlatformTypes_OPENCONFIG_HARDWARE_COMPONENT_LINECARD, }, }, { desc: "PowerSupply", cardFields: properties{ - descriptionValidation: true, - idValidation: true, - nameValidation: true, - partNoValidation: true, - serialNoValidation: true, - mfgNameValidation: true, - mfgDateValidation: false, - hwVerValidation: true, - fwVerValidation: false, - rrValidation: false, - operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE, - parentValidation: true, - pType: componentType["PowerSupply"], + descriptionValidation: true, + idValidation: true, + installPositionAndComponentValidation: true, + nameValidation: true, + partNoValidation: true, + serialNoValidation: true, + mfgNameValidation: true, + mfgDateValidation: false, + hwVerValidation: true, + fwVerValidation: false, + rrValidation: false, + operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE, + parentValidation: true, + pType: componentType["PowerSupply"], }, }, { desc: "Supervisor", cardFields: properties{ - descriptionValidation: true, - idValidation: true, - nameValidation: true, - partNoValidation: true, - serialNoValidation: true, - mfgNameValidation: true, - mfgDateValidation: false, - swVerValidation: false, - hwVerValidation: true, - fwVerValidation: false, - rrValidation: true, - operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE, - parentValidation: true, - pType: componentType["Supervisor"], + descriptionValidation: true, + idValidation: true, + installPositionAndComponentValidation: true, + nameValidation: true, + partNoValidation: true, + serialNoValidation: true, + mfgNameValidation: true, + mfgDateValidation: false, + swVerValidation: false, + hwVerValidation: true, + fwVerValidation: false, + rrValidation: true, + operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_ACTIVE, + parentValidation: true, + pType: componentType["Supervisor"], }, }, { desc: "Transceiver", cardFields: properties{ - descriptionValidation: false, - idValidation: false, - nameValidation: true, - partNoValidation: true, - serialNoValidation: true, - mfgNameValidation: true, - mfgDateValidation: false, - swVerValidation: false, - hwVerValidation: true, - fwVerValidation: true, - rrValidation: false, - operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_UNSET, - parentValidation: false, - pType: componentType["Transceiver"], + descriptionValidation: false, + idValidation: false, + installPositionAndComponentValidation: true, + nameValidation: true, + partNoValidation: true, + serialNoValidation: true, + mfgNameValidation: true, + mfgDateValidation: false, + swVerValidation: false, + hwVerValidation: true, + fwVerValidation: true, + rrValidation: false, + operStatus: oc.PlatformTypes_COMPONENT_OPER_STATUS_UNSET, + parentValidation: false, + pType: componentType["Transceiver"], }, }, { desc: "Cpu", @@ -582,6 +625,14 @@ func ValidateComponentState(t *testing.T, dut *ondatra.DUTDevice, cards []*oc.Co } } + if p.installPositionAndComponentValidation && !deviations.InstallPositionAndInstallComponentUnsupported(dut) { + // If the component has a location and is removable, then it needs to have install-component + // and install-position. + if card.GetLocation() != "" && card.GetRemovable() { + testInstallComponentAndInstallPosition(t, card, validCards) + } + } + if p.nameValidation { name := card.GetName() t.Logf("Component %s Name: %s", cName, name) @@ -759,6 +810,14 @@ func ValidateComponentState(t *testing.T, dut *ondatra.DUTDevice, cards []*oc.Co } } + if p.modelNameValidation { + if deviations.ModelNameUnsupported(dut) { + t.Logf("Telemetry path /components/component/state/model-name is not supported due to deviation ModelNameUnsupported. Skipping model name validation.") + } else if card.GetModelName() != dut.Model() { + t.Errorf("Component %s ModelName: got %s, want %s (dut's hardware model)", cName, card.GetModelName(), dut.Model()) + } + } + if p.pType != nil { ptype := card.GetType() t.Logf("Component %s Type: %v", cName, ptype) @@ -770,6 +829,38 @@ func ValidateComponentState(t *testing.T, dut *ondatra.DUTDevice, cards []*oc.Co } } +func testInstallComponentAndInstallPosition(t *testing.T, c *oc.Component, components []*oc.Component) { + icName := c.GetInstallComponent() + ip := c.GetInstallPosition() + hasInstallComponentAndPosition(t, c, icName, ip) + validateInstallComponent(t, icName, components, c) +} + +func validateInstallComponent(t *testing.T, icName string, components []*oc.Component, c *oc.Component) { + compMap := compNameMap(t, ondatra.DUT(t, "dut")) + ic, ok := compMap[icName] + if !ok { + t.Errorf("Component %s's install-component %s is not in component tree", icName, c.GetName()) + return + } + validTypes := validInstallComponentTypes[c.GetType()] + icType := ic.GetType() + if !validTypes[icType] { + t.Errorf("Component %s's install-component %s is not a supported parent type (%s)", c.GetName(), icName, icType) + } +} + +func hasInstallComponentAndPosition(t *testing.T, c *oc.Component, icName string, ip string) { + if icName == "" { + t.Errorf("Component %s is missing install-component", c.GetName()) + return + } + if ip == "" { + t.Errorf("Component %s is missing install-position", c.GetName()) + return + } +} + func TestStorage(t *testing.T) { // TODO: Add Storage test case here once supported. t.Skipf("Telemetry path /components/component/storage is not supported.") @@ -804,14 +895,19 @@ func TestHeatsinkTempSensor(t *testing.T) { t.Skipf("/components/component[name=]/state/temperature/instant is not supported.") } -func TestInterfaceComponentHierarchy(t *testing.T) { - dut := ondatra.DUT(t, "dut") - - // Map of component Name to corresponding Component OC object. +// Creates a map of component Name to corresponding Component OC object. +func compNameMap(t *testing.T, dut *ondatra.DUTDevice) map[string]*oc.Component { compMap := make(map[string]*oc.Component) for _, c := range gnmi.GetAll(t, dut, gnmi.OC().ComponentAny().State()) { compMap[c.GetName()] = c } + return compMap +} + +func TestInterfaceComponentHierarchy(t *testing.T) { + dut := ondatra.DUT(t, "dut") + + compMap := compNameMap(t, dut) // Map of populated Transceivers to a random integer. transceivers := make(map[string]int) diff --git a/feature/platform/transceiver/tests/zr_fec_uncorrectable_frames_test/zr_fec_uncorrectable_frames_test.go b/feature/platform/transceiver/tests/zr_fec_uncorrectable_frames_test/zr_fec_uncorrectable_frames_test.go index 590fa814e86..9cb628c6189 100644 --- a/feature/platform/transceiver/tests/zr_fec_uncorrectable_frames_test/zr_fec_uncorrectable_frames_test.go +++ b/feature/platform/transceiver/tests/zr_fec_uncorrectable_frames_test/zr_fec_uncorrectable_frames_test.go @@ -32,6 +32,8 @@ import ( const ( sampleInterval = 10 * time.Second intUpdateTime = 2 * time.Minute + otnIndexBase = uint32(4000) + ethIndexBase = uint32(40000) ) func TestMain(m *testing.M) { @@ -47,8 +49,8 @@ func validateFecUncorrectableBlocks(t *testing.T, stream *samplestream.SampleStr if !ok { t.Fatalf("Error capturing streaming Fec value") } - if reflect.TypeOf(fec).Kind() != reflect.Int64 { - t.Fatalf("fec value is not type int64") + if reflect.TypeOf(fec).Kind() != reflect.Uint64 { + t.Fatalf("fec value is not type uint64") } if fec != 0 { t.Fatalf("Got FecUncorrectableBlocks got %d, want 0", fec) @@ -57,17 +59,36 @@ func validateFecUncorrectableBlocks(t *testing.T, stream *samplestream.SampleStr func TestZrUncorrectableFrames(t *testing.T) { dut := ondatra.DUT(t, "dut") - cfgplugins.InterfaceConfig(t, dut, dut.Port(t, "port1")) - cfgplugins.InterfaceConfig(t, dut, dut.Port(t, "port2")) - for _, port := range []string{"port1", "port2"} { + var ( + trs = make(map[string]string) + ochs = make(map[string]string) + otnIndexes = make(map[string]uint32) + ethIndexes = make(map[string]uint32) + ) + + ports := []string{"port1", "port2"} + + for i, port := range ports { + dp := dut.Port(t, port) + cfgplugins.InterfaceConfig(t, dut, dp) + trs[dp.Name()] = gnmi.Get(t, dut, gnmi.OC().Interface(dp.Name()).Transceiver().State()) + ochs[dp.Name()] = gnmi.Get(t, dut, gnmi.OC().Component(trs[dp.Name()]).Transceiver().Channel(0).AssociatedOpticalChannel().State()) + otnIndexes[dp.Name()] = otnIndexBase + uint32(i) + ethIndexes[dp.Name()] = ethIndexBase + uint32(i) + cfgplugins.ConfigOTNChannel(t, dut, ochs[dp.Name()], otnIndexes[dp.Name()], ethIndexes[dp.Name()]) + cfgplugins.ConfigETHChannel(t, dut, dp.Name(), trs[dp.Name()], otnIndexes[dp.Name()], ethIndexes[dp.Name()]) + } + + for _, port := range ports { t.Run(fmt.Sprintf("Port:%s", port), func(t *testing.T) { - dp := dut.Port(t, "port1") + dp := dut.Port(t, port) + gnmi.Await(t, dut, gnmi.OC().Interface(dp.Name()).OperStatus().State(), intUpdateTime, oc.Interface_OperStatus_UP) - streamFec := samplestream.New(t, dut, gnmi.OC().TerminalDevice().Channel(0).Otn().FecUncorrectableBlocks().State(), sampleInterval) - defer streamFec.Close() - validateFecUncorrectableBlocks(t, streamFec) + streamFecOtn := samplestream.New(t, dut, gnmi.OC().TerminalDevice().Channel(otnIndexes[dp.Name()]).Otn().FecUncorrectableBlocks().State(), sampleInterval) + defer streamFecOtn.Close() + validateFecUncorrectableBlocks(t, streamFecOtn) // Toggle interface enabled d := &oc.Root{} @@ -86,7 +107,7 @@ func TestZrUncorrectableFrames(t *testing.T) { // Wait for the cooling off period gnmi.Await(t, dut, gnmi.OC().Interface(dp.Name()).OperStatus().State(), intUpdateTime, oc.Interface_OperStatus_UP) - validateFecUncorrectableBlocks(t, streamFec) + validateFecUncorrectableBlocks(t, streamFecOtn) }) } } diff --git a/feature/platform/transceiver/tests/zr_pm_test/README.md b/feature/platform/transceiver/tests/zr_pm_test/README.md index fc3f96c7e05..943e6806179 100644 --- a/feature/platform/transceiver/tests/zr_pm_test/README.md +++ b/feature/platform/transceiver/tests/zr_pm_test/README.md @@ -69,22 +69,32 @@ module CMIS VDM(Versatile Diagnostics Monitor): * Verify the ZR optics pre FEC PM is updated to the value in the normal range again. -## Config Parameter coverage - -* /components/component/transceiver/config/enabled - -## Telemetry Parameter coverage - - * /terminal-device/logical-channels/channel/otn/state/esnr/instant - * /terminal-device/logical-channels/channel/otn/state/esnr/avg - * /terminal-device/logical-channels/channel/otn/state/esnr/min - * /terminal-device/logical-channels/channel/otn/state/esnr/max - * /terminal-device/logical-channels/channel/otn/state/q-value/instant - * /terminal-device/logical-channels/channel/otn/state/q-value/avg - * /terminal-device/logical-channels/channel/otn/state/q-value/min - * /terminal-device/logical-channels/channel/otn/state/q-value/max - * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/instant - * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/avg - * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/min - * /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/max -FFF - Fixed form factor \ No newline at end of file +## OpenConfig Path and RPC Coverage + +```yaml +paths: + # Config Parameter coverage + /interfaces/interface/config/enabled: + /components/component/transceiver/config/enabled: + platform_type: ["OPTICAL_CHANNEL"] + # Telemetry Parameter coverage + /terminal-device/logical-channels/channel/otn/state/fec-uncorrectable-blocks: + /terminal-device/logical-channels/channel/otn/state/esnr/instant: + /terminal-device/logical-channels/channel/otn/state/esnr/avg: + /terminal-device/logical-channels/channel/otn/state/esnr/min: + /terminal-device/logical-channels/channel/otn/state/esnr/max: + /terminal-device/logical-channels/channel/otn/state/q-value/instant: + /terminal-device/logical-channels/channel/otn/state/q-value/avg: + /terminal-device/logical-channels/channel/otn/state/q-value/min: + /terminal-device/logical-channels/channel/otn/state/q-value/max: + /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/instant: + /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/avg: + /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/min: + /terminal-device/logical-channels/channel/otn/state/pre-fec-ber/max: + +rpcs: + gnmi: + gNMI.Get: + gNMI.Set: + gNMI.Subscribe: +``` \ No newline at end of file diff --git a/feature/platform/transceiver/tests/zr_pm_test/zr_pm_test.go b/feature/platform/transceiver/tests/zr_pm_test/zr_pm_test.go index f4d86199a35..710cfff6c75 100644 --- a/feature/platform/transceiver/tests/zr_pm_test/zr_pm_test.go +++ b/feature/platform/transceiver/tests/zr_pm_test/zr_pm_test.go @@ -4,18 +4,17 @@ import ( "testing" "time" - "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/cfgplugins" "github.com/openconfig/featureprofiles/internal/fptest" "github.com/openconfig/featureprofiles/internal/samplestream" "github.com/openconfig/ondatra" "github.com/openconfig/ondatra/gnmi" "github.com/openconfig/ondatra/gnmi/oc" "github.com/openconfig/ygnmi/ygnmi" - "github.com/openconfig/ygot/ygot" ) const ( - dp16QAM = uint16(16) + dp16QAM = uint16(1) samplingInterval = 10 * time.Second minAllowedQValue = 7.0 maxAllowedQValue = 14.0 @@ -34,20 +33,7 @@ const ( var ( frequencies = []uint64{191400000, 196100000} - targetOpticalPowers = []float64{-6, -10} - - dutPorts = []attrs.Attributes{ - { - Desc: "dutPort1", - IPv4: "192.0.2.1", - IPv4Len: 30, - }, - { - Desc: "dutPort2", - IPv4: "192.0.2.5", - IPv4Len: 30, - }, - } + targetOpticalPowers = []float64{-9, -13} ) func TestMain(m *testing.M) { @@ -72,9 +58,6 @@ func TestPM(t *testing.T) { t.Fatalf("%s PMD is %v, not 400ZR", p.Name(), p.PMD()) } - // Configure interfaces. - gnmi.Replace(t, dut, gnmi.OC().Interface(p.Name()).Config(), dutPorts[i].NewOCInterface(p.Name(), dut)) - // Get transceiver and optical channel. trs[p.Name()] = gnmi.Get(t, dut, gnmi.OC().Interface(p.Name()).Transceiver().State()) ochs[p.Name()] = gnmi.Get(t, dut, gnmi.OC().Component(trs[p.Name()]).Transceiver().Channel(0).AssociatedOpticalChannel().State()) @@ -88,111 +71,138 @@ func TestPM(t *testing.T) { for _, targetOpticalPower := range targetOpticalPowers { // Configure OCH component and OTN and ETH logical channels. for _, p := range dut.Ports() { - configOpticalChannel(t, dut, ochs[p.Name()], frequency, targetOpticalPower) - configOTNChannel(t, dut, ochs[p.Name()], otnIndexes[p.Name()]) - configETHChannel(t, dut, otnIndexes[p.Name()], ethIndexes[p.Name()]) + cfgplugins.ConfigOpticalChannel(t, dut, ochs[p.Name()], frequency, targetOpticalPower, dp16QAM) + cfgplugins.ConfigOTNChannel(t, dut, ochs[p.Name()], otnIndexes[p.Name()], ethIndexes[p.Name()]) + cfgplugins.ConfigETHChannel(t, dut, p.Name(), trs[p.Name()], otnIndexes[p.Name()], ethIndexes[p.Name()]) } - // Create OTN channel sample steam for each port. + // Create sample steams for each port. otnStreams := make(map[string]*samplestream.SampleStream[*oc.TerminalDevice_Channel]) + interfaceStreams := make(map[string]*samplestream.SampleStream[*oc.Interface]) for portName, otnIndex := range otnIndexes { otnStreams[portName] = samplestream.New(t, dut, gnmi.OC().TerminalDevice().Channel(otnIndex).State(), samplingInterval) + interfaceStreams[portName] = samplestream.New(t, dut, gnmi.OC().Interface(portName).State(), samplingInterval) defer otnStreams[portName].Close() + defer interfaceStreams[portName].Close() } - // Enable transceivers. - for _, tr := range trs { - gnmi.Replace(t, dut, gnmi.OC().Component(tr).Transceiver().Enabled().Config(), true) + // Enable interface. + for _, p := range dut.Ports() { + cfgplugins.ToggleInterface(t, dut, p.Name(), true) } // Wait for streaming telemetry to report the channels as up. - for _, otnIndex := range otnIndexes { - gnmi.Await(t, dut, gnmi.OC().TerminalDevice().Channel(otnIndex).LinkState().State(), timeout, oc.Channel_LinkState_UP) + for _, p := range dut.Ports() { + gnmi.Await(t, dut, gnmi.OC().Interface(p.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) } time.Sleep(samplingInterval) // Wait an extra sample interval to ensure the device has time to process the change. - // Disable transceivers. - for _, tr := range trs { - gnmi.Replace(t, dut, gnmi.OC().Component(tr).Transceiver().Enabled().Config(), false) + validateAllSamples(t, dut, true, interfaceStreams, otnStreams) + + // Disable interface. + for _, p := range dut.Ports() { + cfgplugins.ToggleInterface(t, dut, p.Name(), false) } // Wait for streaming telemetry to report the channels as down. - for _, otnIndex := range otnIndexes { - gnmi.Await(t, dut, gnmi.OC().TerminalDevice().Channel(otnIndex).LinkState().State(), timeout, oc.Channel_LinkState_DOWN) + for _, p := range dut.Ports() { + gnmi.Await(t, dut, gnmi.OC().Interface(p.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_DOWN) } time.Sleep(samplingInterval) // Wait an extra sample interval to ensure the device has time to process the change. + validateAllSamples(t, dut, false, interfaceStreams, otnStreams) + // Re-enable transceivers. - for _, tr := range trs { - gnmi.Replace(t, dut, gnmi.OC().Component(tr).Transceiver().Enabled().Config(), true) + for _, p := range dut.Ports() { + cfgplugins.ToggleInterface(t, dut, p.Name(), true) } // Wait for streaming telemetry to report the channels as up. - for _, otnIndex := range otnIndexes { - gnmi.Await(t, dut, gnmi.OC().TerminalDevice().Channel(otnIndex).LinkState().State(), timeout, oc.Channel_LinkState_UP) + for _, p := range dut.Ports() { + gnmi.Await(t, dut, gnmi.OC().Interface(p.Name()).OperStatus().State(), timeout, oc.Interface_OperStatus_UP) } time.Sleep(samplingInterval) // Wait an extra sample interval to ensure the device has time to process the change. - // Now validate OTN streams didn't return any invalid values. - for portName, stream := range otnStreams { - var linkStates []oc.E_Channel_LinkState - for _, val := range stream.All() { - linkStates = append(linkStates, validateStream(t, val, portName)) + validateAllSamples(t, dut, true, interfaceStreams, otnStreams) + } + } +} + +// validateAllSamples validates all the sample streams. +func validateAllSamples(t *testing.T, dut *ondatra.DUTDevice, isEnabled bool, interfaceStreams map[string]*samplestream.SampleStream[*oc.Interface], otnStreams map[string]*samplestream.SampleStream[*oc.TerminalDevice_Channel]) { + for _, p := range dut.Ports() { + for valIndex := range interfaceStreams[p.Name()].All() { + if valIndex >= len(otnStreams[p.Name()].All()) { + break + } + operStatus := validateSampleStream(t, interfaceStreams[p.Name()].All()[valIndex], otnStreams[p.Name()].All()[valIndex], p.Name()) + switch operStatus { + case oc.Interface_OperStatus_UP: + if !isEnabled { + t.Errorf("Invalid %v operStatus value: want DOWN, got %v", p.Name(), operStatus) + } + case oc.Interface_OperStatus_DOWN: + if isEnabled { + t.Errorf("Invalid %v operStatus value: want UP, got %v", p.Name(), operStatus) } - validateLinkStateTransitions(t, linkStates, portName) } } } } -// validateStream validates the stream data. -func validateStream(t *testing.T, data *ygnmi.Value[*oc.TerminalDevice_Channel], portName string) oc.E_Channel_LinkState { - if data == nil { +// validateSampleStream validates the stream data. +func validateSampleStream(t *testing.T, interfaceData *ygnmi.Value[*oc.Interface], terminalDeviceData *ygnmi.Value[*oc.TerminalDevice_Channel], portName string) oc.E_Interface_OperStatus { + if interfaceData == nil { t.Errorf("Data not received for port %v.", portName) - return oc.Channel_LinkState_UNSET + return oc.Interface_OperStatus_UNSET } - v, ok := data.Val() + interfaceValue, ok := interfaceData.Val() if !ok { t.Errorf("Channel data is empty for port %v.", portName) - return oc.Channel_LinkState_UNSET + return oc.Interface_OperStatus_UNSET } - linkState := v.GetLinkState() - if linkState == oc.Channel_LinkState_UNSET { + operStatus := interfaceValue.GetOperStatus() + if operStatus == oc.Interface_OperStatus_UNSET { t.Errorf("Link state data is empty for port %v", portName) - return oc.Channel_LinkState_UNSET + return oc.Interface_OperStatus_UNSET } - otn := v.GetOtn() + terminalDeviceValue, ok := terminalDeviceData.Val() + if !ok { + t.Errorf("Terminal Device data is empty for port %v.", portName) + return oc.Interface_OperStatus_UNSET + } + otn := terminalDeviceValue.GetOtn() if otn == nil { t.Errorf("OTN data is empty for port %v", portName) - return linkState + return operStatus } if b := otn.GetPreFecBer(); b == nil { t.Errorf("PreFECBER data is empty for port %v", portName) } else { - validatePMValue(t, portName, "PreFECBER", b.GetMin(), b.GetMax(), b.GetAvg(), b.GetInstant(), minAllowedPreFECBER, maxAllowedPreFECBER, inactivePreFECBER, linkState) + validatePMValue(t, portName, "PreFECBER", b.GetInstant(), b.GetMin(), b.GetMax(), b.GetAvg(), minAllowedPreFECBER, maxAllowedPreFECBER, inactivePreFECBER, operStatus) } if e := otn.GetEsnr(); e == nil { t.Errorf("ESNR data is empty for port %v", portName) } else { - validatePMValue(t, portName, "esnr", e.GetMin(), e.GetMax(), e.GetAvg(), e.GetInstant(), minAllowedESNR, maxAllowedESNR, inactiveESNR, linkState) + validatePMValue(t, portName, "esnr", e.GetInstant(), e.GetMin(), e.GetMax(), e.GetAvg(), minAllowedESNR, maxAllowedESNR, inactiveESNR, operStatus) } if q := otn.GetQValue(); q == nil { t.Errorf("QValue data is empty for port %v", portName) } else { - validatePMValue(t, portName, "QValue", q.GetMin(), q.GetMax(), q.GetAvg(), q.GetInstant(), minAllowedQValue, maxAllowedQValue, inactiveQValue, linkState) + validatePMValue(t, portName, "QValue", q.GetInstant(), q.GetMin(), q.GetMax(), q.GetAvg(), minAllowedQValue, maxAllowedQValue, inactiveQValue, operStatus) } - return linkState + return operStatus } // validatePMValue validates the pm value. -func validatePMValue(t *testing.T, portName, pm string, instant, min, max, avg, minAllowed, maxAllowed, inactiveValue float64, linkState oc.E_Channel_LinkState) { +func validatePMValue(t *testing.T, portName, pm string, instant, min, max, avg, minAllowed, maxAllowed, inactiveValue float64, linkState oc.E_Interface_OperStatus) { switch linkState { - case oc.Channel_LinkState_UP: - if instant < min || instant > max || avg < min || avg > max || min < minAllowed || max > maxAllowed { + case oc.Interface_OperStatus_UP: + if instant < minAllowed || instant > maxAllowed { t.Errorf("Invalid %v sample when %v is UP --> min : %v, max : %v, avg : %v, instant : %v", pm, portName, min, max, avg, instant) return } - case oc.Channel_LinkState_DOWN: + case oc.Interface_OperStatus_DOWN: if instant != inactiveValue || avg != inactiveValue || min != inactiveValue || max != inactiveValue { t.Errorf("Invalid %v sample when %v is DOWN --> min : %v, max : %v, avg : %v, instant : %v", pm, portName, min, max, avg, instant) return @@ -200,83 +210,3 @@ func validatePMValue(t *testing.T, portName, pm string, instant, min, max, avg, } t.Logf("Valid %v sample when %v is %v --> min : %v, max : %v, avg : %v, instant : %v", pm, portName, linkState, min, max, avg, instant) } - -// validateLinkStateTransitions validates the link state transitions. -func validateLinkStateTransitions(t *testing.T, linkStates []oc.E_Channel_LinkState, portName string) { - if len(linkStates) < 3 { - t.Errorf("Invalid %v link state transitions: want at least 3 samples, got %v", portName, len(linkStates)) - return - } - if linkStates[0] != oc.Channel_LinkState_DOWN { - t.Errorf("Invalid %v link state transitions: want DOWN for initial link state, got %v ", portName, linkStates[0]) - return - } - var transitionIndexes []int - for i := range linkStates { - if i == 0 { - continue - } - if linkStates[i-1] != linkStates[i] { - transitionIndexes = append(transitionIndexes, i) - } - } - if len(transitionIndexes) != 2 { - t.Errorf("Invalid %v link state transitions: want 2 transitions, got %v ", portName, len(transitionIndexes)) - return - } - if linkStates[transitionIndexes[0]] != oc.Channel_LinkState_UP { - t.Errorf("Invalid %v link state transitions: want DOWN-->UP, got %v-->%v", portName, linkStates[transitionIndexes[0]-1], linkStates[transitionIndexes[0]]) - return - } - if linkStates[transitionIndexes[1]] != oc.Channel_LinkState_DOWN { - t.Errorf("Invalid %v link state transitions: want UP-->DOWN, got %v-->%v", portName, linkStates[transitionIndexes[1]-1], linkStates[transitionIndexes[1]]) - } -} - -// configOpticalChannel configures the optical channel. -func configOpticalChannel(t *testing.T, dut *ondatra.DUTDevice, och string, frequency uint64, targetOpticalPower float64) { - gnmi.Replace(t, dut, gnmi.OC().Component(och).OpticalChannel().Config(), &oc.Component_OpticalChannel{ - Frequency: ygot.Uint64(frequency), - TargetOutputPower: ygot.Float64(targetOpticalPower), - }) -} - -// configOTNChannel configures the OTN channel. -func configOTNChannel(t *testing.T, dut *ondatra.DUTDevice, och string, otnIndex uint32) { - gnmi.Replace(t, dut, gnmi.OC().TerminalDevice().Channel(otnIndex).Config(), &oc.TerminalDevice_Channel{ - LogicalChannelType: oc.TransportTypes_LOGICAL_ELEMENT_PROTOCOL_TYPE_PROT_OTN, - AdminState: oc.TerminalDevice_AdminStateType_ENABLED, - Description: ygot.String("OTN Logical Channel"), - Index: ygot.Uint32(otnIndex), - Assignment: map[uint32]*oc.TerminalDevice_Channel_Assignment{ - 1: { - Index: ygot.Uint32(1), - OpticalChannel: ygot.String(och), - Description: ygot.String("OTN to Optical"), - Allocation: ygot.Float64(400), - AssignmentType: oc.Assignment_AssignmentType_OPTICAL_CHANNEL, - }, - }, - }) -} - -// configETHChannel configures the ETH channel. -func configETHChannel(t *testing.T, dut *ondatra.DUTDevice, otnIndex, ethIndex uint32) { - gnmi.Replace(t, dut, gnmi.OC().TerminalDevice().Channel(ethIndex).Config(), &oc.TerminalDevice_Channel{ - LogicalChannelType: oc.TransportTypes_LOGICAL_ELEMENT_PROTOCOL_TYPE_PROT_ETHERNET, - AdminState: oc.TerminalDevice_AdminStateType_ENABLED, - Description: ygot.String("ETH Logical Channel"), - Index: ygot.Uint32(ethIndex), - RateClass: oc.TransportTypes_TRIBUTARY_RATE_CLASS_TYPE_TRIB_RATE_400G, - TribProtocol: oc.TransportTypes_TRIBUTARY_PROTOCOL_TYPE_PROT_400GE, - Assignment: map[uint32]*oc.TerminalDevice_Channel_Assignment{ - 1: { - Index: ygot.Uint32(1), - LogicalChannel: ygot.Uint32(otnIndex), - Description: ygot.String("ETH to OTN"), - Allocation: ygot.Float64(400), - AssignmentType: oc.Assignment_AssignmentType_LOGICAL_CHANNEL, - }, - }, - }) -} diff --git a/feature/platform/transceiver/tests/zr_supply_voltage_test/zr_supply_voltage_test.go b/feature/platform/transceiver/tests/zr_supply_voltage_test/zr_supply_voltage_test.go index ce9383143a7..c05b29aae9f 100644 --- a/feature/platform/transceiver/tests/zr_supply_voltage_test/zr_supply_voltage_test.go +++ b/feature/platform/transceiver/tests/zr_supply_voltage_test/zr_supply_voltage_test.go @@ -88,6 +88,12 @@ func TestZrSupplyVoltage(t *testing.T) { volInstNew := verifyVoltageValue(t, streamInst, "Instant") t.Logf("Port %s instant voltage after port down: %v", dp.Name(), volInstNew) + + // Enable interface again. + i.Enabled = ygot.Bool(true) + gnmi.Replace(t, dut, gnmi.OC().Interface(dp.Name()).Config(), i) + // Wait for the cooling off period + gnmi.Await(t, dut, gnmi.OC().Interface(dp.Name()).OperStatus().State(), intUpdateTime, oc.Interface_OperStatus_UP) }) } } diff --git a/feature/policy_forwarding/encapsulation/otg_tests/encap_gre_ipv4/README.md b/feature/policy_forwarding/encapsulation/otg_tests/encap_gre_ipv4/README.md new file mode 100644 index 00000000000..fc7ba59b02a --- /dev/null +++ b/feature/policy_forwarding/encapsulation/otg_tests/encap_gre_ipv4/README.md @@ -0,0 +1,145 @@ +# PF-1.2: Policy-based traffic GRE Encapsulation to IPv4 GRE tunnel + +## Summary + +The test verifies policy forwarding(PF) encapsulation action to IPv4 GRE tunnel when matching on source/destination. + +## Testbed type + +* [`featureprofiles/topologies/atedut_4.testbed`](https://github.com/openconfig/featureprofiles/blob/main/topologies/atedut_4.testbed) + +## Procedure + +### Test environment setup + +* DUT has an ingress port and 2 egress ports. + + ``` + | | --eBGP-- | ATE Port 2 | + [ ATE Port 1 ] ---- | DUT | | | + | | --eBGP-- | ATE Port 3 | + ``` + +* Traffic is generated from ATE Port 1. +* ATE Port 2 is used as the destination port for encapsulated + traffic. +* ATE Port 3 is used as the fallback destination for + pass-through traffic. + +#### Configuration + +1. All DUT Ports are configured as a singleton IP interfaces. Configure MTU of 9216 (L2) on ATE Port1, MTU 2000 on ATE Port 2, 3 + +2. IPv4 and IPv6 static routes to test destination networks IPV4-DST/IPV6-DST are configured on DUT towards ATE Port 3. + +3. Another set of IPv4 static routes to 32x IPv4 GRE encap destinations towards ATE Port 2. + +4. 2 IPv4 and 2 IPv6 source prefixes will be used to generate tests traffic +(SRC1-SRC2). Apply policy-forwarding with 4 rules to DUT Port 1: + - Match IPV4-SRC1 and accept/foward. + - Match IPV6-SRC1 and accept/foward. + - Match IPV4-SRC2 and encapsulate to 32 IPv4 GRE destinations. + - Match IPV6-SRC2 and encapsulate to 32 IPv4 GRE destinations. + +5. Set GRE encap source to device's loopback interface. +6. Either `identifying-prefix` or `targets/target/config/destination` can be used to configure GRE destinations based on vendor implementation. +7. Configure QoS classifier for incoming traffic on ATE Port1 for IPv4 and IPv6 traffic. + QoS classifier should remark egress packet to the matching ingress DSCP value (eg. match DSCP 32, set egress DSCP 32). + Match and remark all values for 3 leftmost DSCP bits [0, 8, 16, 24, 32, 40, 48, 56]. + + +### PF-1.1.1: Verify PF GRE encapsulate action for IPv4 traffic +Generate traffic on ATE Port 1 from IPV4-SRC2 from a random combination of 1000 source addresses to IPV4-DST at linerate. +Use 512 bytes frame size. + +Verify: + +* All traffic received on ATE Port 2 GRE-encapsulated. +* No packet loss when forwarding. +* Traffic equally load-balanced across 32 GRE destinations. +* Verify PF packet counters matching traffic generated. + +### PF-1.1.2: Verify PF GRE encapsulate action for IPv6 traffic +Generate traffic on ATE Port 1 from IPV6-SRC2 from a random combination of 1000 source addresses to IPV6-DST. +Use 512 bytes frame size. + +Verify: + +* All traffic received on ATE Port 2 GRE-encapsulated. +* No packet loss when forwarding. +* Traffic equally load-balanced across 32 GRE destinations. +* Verify PF packet counters matching traffic generated. + +### PF-1.1.3: Verify PF IPV4 forward action +Generate traffic on ATE Port 1 from sources IPV4-SRC1 to IPV4-DST. + +Verify: + +* All traffic received on ATE Port 3. +* No packet loss when forwarding. + +### PF-1.1.4: Verify PF IPV6 forward action +Generate traffic on ATE Port 1 from sources IPV6-SRC1 to IPV6-DST. + +Verify: + +* All traffic received on ATE Port 3. +* No packet loss when forwarding. + +### PF-1.1.5: Verify PF GRE DSCP copy to outer header for IPv4 traffic +Generate traffic on ATE Port 1 from IPV4-SRC1 source for every DSCP value in [0, 8, 16, 24, 32, 40, 48, 56] + +Verify: + +* All traffic received on ATE Port 2 GRE-encapsulated. +* Outer GRE IPv4 header has same marking as ingress non-encapsulated IPv4 packet. + +### PF-1.1.6: Verify PF GRE DSCP copy to outer header for IPv6 traffic +Generate traffic on ATE Port 1 from IPV6-SRC1 for every IPv6 TC 8-bit value [0, 32, 64, 96, 128, 160, 192, 224] + +Verify: + +* All traffic received on ATE Port 2 GRE-encapsulated. +* Outer GRE IPv4 header has DSCP match to ingress IPv6 TC packet. + +### PF-1.1.7: Verify MTU handling during GRE encap +* Generate traffic on ATE Port 1 from IPV4-SRC1 with frame size of 4000 with DF-bit set. +* Generate traffic on ATE Port 1 from IPV6-SRC1 with frame size of 4000 with DF-bit set. + +Verify: + +* DUT generates "Fragmentation Needed" message back to ATE source. + +## OpenConfig Path and RPC Coverage + +```yaml +paths: + # match condition + /network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv4/config/source-address: + /network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv6/config/source-address: + # encap action + /network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/action/encapsulate-gre/targets/target/config/id: + /network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/action/encapsulate-gre/targets/target/config/source: + # either destination or identifying-prefix can be specified based on specific vendor implementation. + /network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/action/encapsulate-gre/targets/target/config/destination: + /network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/action/encapsulate-gre/config/identifying-prefix: + # application to the interface + /network-instances/network-instance/policy-forwarding/interfaces/interface/config/apply-forwarding-policy: + + # telemetry + /network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/state/matched-pkts: + /network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/state/matched-octets: + +rpcs: + gnmi: + gNMI.Set: + union_replace: true + replace: true + gNMI.Subscribe: + on_change: true +``` + +## Required DUT platform + +* MFF +* FFF \ No newline at end of file diff --git a/feature/policy_forwarding/otg_tests/match_dscp_indirect_next_hop/README.md b/feature/policy_forwarding/otg_tests/match_dscp_indirect_next_hop/README.md new file mode 100644 index 00000000000..a1ebcd45420 --- /dev/null +++ b/feature/policy_forwarding/otg_tests/match_dscp_indirect_next_hop/README.md @@ -0,0 +1,97 @@ +# PF-1.1: IPv4/IPv6 policy-forwarding to indirect NH matching DSCP/TC. + +## Summary + +The test verifies policy-forwarding(PF) when matching specific DSCP values in IPv4/IPv6 header and redirecting traffic to an indirect BGP next-hop. + +2 right-most bits are used for this test with all the possibles combinations of 3 left-most DSCP bits: `...011`. + +## Testbed type + +* [`featureprofiles/topologies/atedut_4.testbed`](https://github.com/openconfig/featureprofiles/blob/main/topologies/atedut_4.testbed) + +## Procedure + +### Test environment setup + +* DUT has an ingress port (Port 1) and 2 egress ports combined. + + ``` + | | --eBGP-- | ATE Port 2 | + [ ATE Port 1 ] ---- | DUT | | | + | | -------- | ATE Port 3 | + ``` + +* Traffic is generated from ATE Port 1. +* ATE Port 2 is used as the destination port for PF. eBGP peerings + on this port announces BG4IE NH. +* ATE Port 3 is used as the fallback destination when PF NH routes + are withdrawn. + +#### Configuration + +1. All DUT Ports are configured as a singleton IP interfaces. + +2. Static routes (ST1) to test IPv4 and IPv6 destination networks (IPV4-DST1/IPV6-DST1) are configured on DUT towards ATE Port 3. + +3. eBGP session is configured on DUT port 2. Indirect /32 (IPV-NH-V4) and /128 (IPV-NH-V6) prefixes are announced via eBGP from ATE Port 2. + +4. PF is configured on DUT port 1 to match the traffic marked with rightmost 2 bits set in DSCP to 11. PF action is to redirect to BGP-announced next-hops (IPV-NH-V4/IPV-NH-V6): + * List of DSCP values (6-bit) to be matched [3, 11, 19, 27, 35, 43, 51, 59] + * Matching rules for IPv6 should map the above 6-bit DSCP values to the leftmost 6-bits of IPv6 traffic-class. + * PF should permit the rest of the traffic. + +### PF-1.1.1: Verify PF next-hop action +Generate traffic on ATE Port 1 to test IPv4 and IPv6 destination networks (IPV4-DST1/IPV6-DST1) with DSCP/TC rightmost 2 bits set to `11`. Generate flows for every DSCP value in the set [3, 11, 19, 27, 35, 43, 51, 59]. +IPv6 flows should use TC 8-bit values [12, 44, 76, 108, 172, 163, 204, 236] + +Verify: + +* All traffic received on ATE Port 2. +* No packet loss when forwarding. +* Verify PF packet counters matching traffic generated. + +### PF-1.1.2: Verify PF no-match action +Generate traffic on ATE Port 1 to test IPv4 and IPv6 destination networks (IPV4-DST1/IPV6-DST1) with DSCP/TC rightmost 2 bits set to `00`. Generate flows for every DSCP/TC values in the set [0, 8, 16, 24, 32, 40, 48, 56]. IPv6 flows should use TC 8-bit values [0, 32, 64, 96, 128, 160, 192, 224] + +Verify: + +* All traffic received on ATE Port 3. +* No packet loss when forwarding. + +### PF-1.1.3: Verify PF without NH present +Withdraw next-hop prefixes (IPV-NH-V4/IPV-NH-V6) from BGP announcement. Generate traffic on ATE Port 1 to test IPv4 and IPv6 destination (IPV4-DST1/IPV6-DST1) networks with DSCP/TC rightmost 2 bits set to `11`. Generate flows for every IPv4 DSCP value in the set [3, 11, 19, 27, 35, 43, 51, 59] and IPv6 TC [0, 32, 64, 96, 128, 160, 192, 224]. + +Verify: + +* All traffic received on ATE Port 3. +* Traffic follows fallbacks static routes (ST1). +* No packet loss when forwarding. + +## OpenConfig Path and RPC Coverage + +```yaml +paths: + # PF configuration + /network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv4/config/dscp-set: + /network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/ipv6/config/dscp-set: + /network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/action/config/next-hop: + # application to the interface + /network-instances/network-instance/policy-forwarding/interfaces/interface/config/apply-forwarding-policy: + + # telemetry + /network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/state/matched-pkts: + /network-instances/network-instance/policy-forwarding/policies/policy/rules/rule/state/matched-octets: + +rpcs: + gnmi: + gNMI.Set: + union_replace: true + replace: true + gNMI.Subscribe: + on_change: true +``` + +## Required DUT platform + * MFF + * FFF \ No newline at end of file diff --git a/feature/system/control_plane_traffic/otg_tests/ingress_acl/README.md b/feature/system/control_plane_traffic/otg_tests/ingress_acl/README.md new file mode 100644 index 00000000000..ade9ff395aa --- /dev/null +++ b/feature/system/control_plane_traffic/otg_tests/ingress_acl/README.md @@ -0,0 +1,90 @@ +# SYS-2.1: Ingress control-plane ACL. + +## Summary + +The test verifies securing device control-plane access with an ingress access-control-list (ACL). + +## Testbed type + +* [`featureprofiles/topologies/atedut_4.testbed`](https://github.com/openconfig/featureprofiles/blob/main/topologies/atedut_4.testbed) + +## Procedure + +### Test environment setup + +* DUT has an single ingress port with IPv4/IPv6 enabled. + + ATE <> DUT + +### Configuration + +1. Configure test address on the device loopback (secondary) + +2. Configure IPv4/IPv6 ACL/filters with following terms: + - Allow gRPC from any (lab management access) + - Allow SSH from MGMT-SRC + - Allow ICMP from MGMT-SRC + - Explicit deny + +3. Apply filter to control-plane ingress. + +## Test cases + +### SYS-2.1.1: Verify ingress control-plane ACL permit +Generate ICMP traffic to device loopback from MGMT-SRC +Generate SSH SYN packets to device loopback from MGMT-SRC + +Verify: + +* ACL counters for corresponding ACL entries are incrementing. +* Device responds to ICMP permitted +* Device sends TCP-ACK for SSH session + +### SYS-2.1.2: Verify control-plane ACL deny +Generate ICMP traffic to device loopback from UNKNOWN-SRC +Generate SSH SYN packets to device loopback from UNKNOWN-SRC + +Verify: + +* Explicit deny ACL counter is incrementing. +* Device does not respond to ICMP +* Device sends TCP-ACK for SSH session + +## OpenConfig Path and RPC Coverage + +```yaml +paths: + # acl definition + /acl/acl-sets/acl-set/config/name: + /acl/acl-sets/acl-set/config/type: + /acl/acl-sets/acl-set/config/description: + /acl/acl-sets/acl-set/acl-entries/acl-entry/config/sequence-id: + /acl/acl-sets/acl-set/acl-entries/acl-entry/config/description: + /acl/acl-sets/acl-set/acl-entries/acl-entry/ipv4/config/source-address: + /acl/acl-sets/acl-set/acl-entries/acl-entry/ipv4/config/protocol: + /acl/acl-sets/acl-set/acl-entries/acl-entry/ipv6/config/source-address: + /acl/acl-sets/acl-set/acl-entries/acl-entry/ipv6/config/protocol: + /acl/acl-sets/acl-set/acl-entries/acl-entry/transport/config/destination-port: + # acl application + /system/control-plane-traffic/ingress/acl/acl-set/config/set-name: + /system/control-plane-traffic/ingress/acl/acl-set/config/type: + + # telemetry + /system/control-plane-traffic/ingress/acl/acl-set/state/set-name: + /system/control-plane-traffic/ingress/acl/acl-set/acl-entries/acl-entry/state/sequence-id: + /system/control-plane-traffic/ingress/acl/acl-set/acl-entries/acl-entry/state/matched-packets: + +rpcs: + gnmi: + gNMI.Set: + union_replace: true + replace: true + gNMI.Subscribe: + on_change: true +``` + +## Required DUT platform + +* MFF +* FFF +* VRX \ No newline at end of file diff --git a/feature/system/gnmi/set/tests/gnmi_set_test/README.md b/feature/system/gnmi/set/tests/gnmi_set_test/README.md index 98aa53351ce..f87f4045c14 100644 --- a/feature/system/gnmi/set/tests/gnmi_set_test/README.md +++ b/feature/system/gnmi/set/tests/gnmi_set_test/README.md @@ -196,3 +196,13 @@ This test checks that the static protocol name is usable. ## RPC Coverage * gNMI.Set + +## OpenConfig Path and RPC Coverage + +```yaml +rpcs: + gnmi: + gNMI.Get: + gNMI.Subscribe: + +``` diff --git a/feature/system/gnmi/set/tests/gnmi_set_test/metadata.textproto b/feature/system/gnmi/set/tests/gnmi_set_test/metadata.textproto index 9fe05d0e4b2..13cf7f56ecd 100644 --- a/feature/system/gnmi/set/tests/gnmi_set_test/metadata.textproto +++ b/feature/system/gnmi/set/tests/gnmi_set_test/metadata.textproto @@ -22,6 +22,7 @@ platform_exceptions: { } deviations: { skip_static_nexthop_check: true + interface_ref_interface_id_format: true } } platform_exceptions: { diff --git a/feature/system/ntp/tests/system_ntp_test/metadata.textproto b/feature/system/ntp/tests/system_ntp_test/metadata.textproto index dddf1072d80..cf10ef334d1 100644 --- a/feature/system/ntp/tests/system_ntp_test/metadata.textproto +++ b/feature/system/ntp/tests/system_ntp_test/metadata.textproto @@ -5,14 +5,6 @@ uuid: "9e5ec4a5-0adb-48aa-b6c2-c0d604d15934" plan_id: "OC-26.1" description: "Network Time Protocol (NTP)" testbed: TESTBED_DUT -platform_exceptions: { - platform: { - vendor: CISCO - } - deviations: { - ntp_non_default_vrf_unsupported: true - } -} platform_exceptions: { platform: { vendor: JUNIPER diff --git a/feature/system/ntp/tests/system_ntp_test/system_ntp_test.go b/feature/system/ntp/tests/system_ntp_test/system_ntp_test.go index 4acc14ae9a9..b72e3344d12 100644 --- a/feature/system/ntp/tests/system_ntp_test/system_ntp_test.go +++ b/feature/system/ntp/tests/system_ntp_test/system_ntp_test.go @@ -99,7 +99,7 @@ func TestNtpServerConfigurability(t *testing.T) { if ntpServer == nil { t.Errorf("Missing NTP server from NTP state: %s", address) } - if got, want := testCase.vrf, ntpServer.GetNetworkInstance(); want != "" && got != want { + if got, want := ntpServer.GetNetworkInstance(), testCase.vrf; want != "" && got != want { t.Errorf("Incorrect NTP Server network instance for address %s: got %s, want %s", address, got, want) } } diff --git a/go.mod b/go.mod index f353236dae9..40f4d310dde 100644 --- a/go.mod +++ b/go.mod @@ -20,14 +20,14 @@ require ( github.com/openconfig/gnmi v0.11.0 github.com/openconfig/gnoi v0.4.0 github.com/openconfig/gnoigo v0.0.0-20240320202954-ebd033e3542c - github.com/openconfig/gnsi v1.4.0 + github.com/openconfig/gnsi v1.4.5 github.com/openconfig/gocloser v0.0.0-20220310182203-c6c950ed3b0b github.com/openconfig/goyang v1.4.5 github.com/openconfig/gribi v1.0.0 github.com/openconfig/gribigo v0.0.0-20231213034307-d0abeba7f432 github.com/openconfig/kne v0.1.18 github.com/openconfig/models-ci v1.0.2-0.20231113233730-f0986391428e - github.com/openconfig/ondatra v0.5.8 + github.com/openconfig/ondatra v0.6.0 github.com/openconfig/replayer v0.0.0-20240110192655-4e9cf83d8d30 github.com/openconfig/testt v0.0.0-20220311054427-efbb1a32ec07 github.com/openconfig/ygnmi v0.11.1 @@ -120,6 +120,7 @@ require ( github.com/networkop/meshnet-cni v0.3.1-0.20230525201116-d7c306c635cf // indirect github.com/open-traffic-generator/keng-operator v0.3.28 // indirect github.com/openconfig/attestz v0.2.0 // indirect + github.com/openconfig/gnpsi v0.3.2 // indirect github.com/openconfig/grpctunnel v0.0.0-20220819142823-6f5422b8ca70 // indirect github.com/openconfig/lemming/operator v0.2.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect diff --git a/go.sum b/go.sum index 02850480f71..4bbda0ef3d7 100644 --- a/go.sum +++ b/go.sum @@ -1649,8 +1649,10 @@ github.com/openconfig/gnoi v0.4.0 h1:jbYXRMNmmvA8ZFv2FBLrYoxA1MFSui4tEui+8LAWyVc github.com/openconfig/gnoi v0.4.0/go.mod h1:0d3/bgooXM/je6jI+fb7+9Iky8R8k6zSFG19xRYTRso= github.com/openconfig/gnoigo v0.0.0-20240320202954-ebd033e3542c h1:egPgBUBDn0XEtbz0CvE+Bh/I/3iTzwzMq5/rmtPJdQs= github.com/openconfig/gnoigo v0.0.0-20240320202954-ebd033e3542c/go.mod h1:Se/HklUcFVcCGB66khgYouiesLRPoa4UL1ovvmE/68k= -github.com/openconfig/gnsi v1.4.0 h1:E/OnAZh7jr3xpTdyawYXloVUg0cQbU3WQLtDGib/7L4= -github.com/openconfig/gnsi v1.4.0/go.mod h1:jzPF4rVWPHhIG0F3t910Hh2VqqTbSfv18shbgE4AXhw= +github.com/openconfig/gnpsi v0.3.2 h1:+bl1bXMOTrWOcGydWB+8wGgvxlgvL8Y6joAiWFU5sog= +github.com/openconfig/gnpsi v0.3.2/go.mod h1:+Qj2PwadJ/jvGkH6H/A3XO9ZRKQRVtl3A30ubwz0M18= +github.com/openconfig/gnsi v1.4.5 h1:ZkUVN+HDg0GFe3wIYocWjPqjOHZP3vVEB+NIctaYV6c= +github.com/openconfig/gnsi v1.4.5/go.mod h1:i4guw9Vn3Mn/aUKB/2z84Moe2P811H3SV3g0Cu0X4h8= github.com/openconfig/gocloser v0.0.0-20220310182203-c6c950ed3b0b h1:NSYuxdlOWLldNpid1dThR6Dci96juXioUguMho6aliI= github.com/openconfig/gocloser v0.0.0-20220310182203-c6c950ed3b0b/go.mod h1:uhC/ybmPapgeyAL2b9ZrUQ+DZE+DB+J+/7377PX+lek= github.com/openconfig/goyang v0.0.0-20200115183954-d0a48929f0ea/go.mod h1:dhXaV0JgHJzdrHi2l+w0fZrwArtXL7jEFoiqLEdmkvU= @@ -1673,8 +1675,8 @@ github.com/openconfig/lemming/operator v0.2.0 h1:dovZnR6lQkOHXcODli1NDOr/GVYrBY0 github.com/openconfig/lemming/operator v0.2.0/go.mod h1:LKgEXSR5VK2CAeh2uKijKAXFj42uQuwakrCHVPF0iII= github.com/openconfig/models-ci v1.0.2-0.20231113233730-f0986391428e h1:6N4jXpZa/SXYcNpJFjjZvenxO/xnTwuUCgCEinhNLfU= github.com/openconfig/models-ci v1.0.2-0.20231113233730-f0986391428e/go.mod h1:w38G/kObu95PbtwMYVp6SKhkHCegJFwL8B58Ns84g4s= -github.com/openconfig/ondatra v0.5.8 h1:hTkewcIRtOC6FOpcGThFZPS38EgCbfqh+uwlppMotiA= -github.com/openconfig/ondatra v0.5.8/go.mod h1:6vT2uWz1neH/hDa4ZX8H8pT6sYZHvIftrUugQyG9RrA= +github.com/openconfig/ondatra v0.6.0 h1:wKedHwumAjJTFrbtEdWu71S4S6fO2IEXWgI6mQPtHho= +github.com/openconfig/ondatra v0.6.0/go.mod h1:8w9H5yGPctJrdeORW/WCK1sos+goQCqIx8ABTLNPR68= github.com/openconfig/replayer v0.0.0-20240110192655-4e9cf83d8d30 h1:KcHS08m7nFHq/D03ZfZKKNCSaS1jsuvdF3lCyDjPWJc= github.com/openconfig/replayer v0.0.0-20240110192655-4e9cf83d8d30/go.mod h1:VQ8FdPVaHwxKtamhcrwkPsvTeeoEgFYNK1xE8nHD0S8= github.com/openconfig/testt v0.0.0-20220311054427-efbb1a32ec07 h1:X631iD/B0ximGFb5P9LY5wHju4SiedxUhc5UZEo7VSw= diff --git a/internal/cfgplugins/interface.go b/internal/cfgplugins/interface.go index 7567518c89e..cee56b81fb3 100644 --- a/internal/cfgplugins/interface.go +++ b/internal/cfgplugins/interface.go @@ -89,3 +89,72 @@ func ValidateInterfaceConfig(t *testing.T, dut *ondatra.DUTDevice, dp *ondatra.P t.Fatalf("Frequency is not within expected tolerance, got: %v want: %v tolerance: %v", frequency, targetFrequencyMHz, targetFrequencyToleranceMHz) } } + +// ToggleInterface toggles the interface. +func ToggleInterface(t *testing.T, dut *ondatra.DUTDevice, intf string, isEnabled bool) { + d := &oc.Root{} + i := d.GetOrCreateInterface(intf) + i.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + i.Enabled = ygot.Bool(isEnabled) + gnmi.Replace(t, dut, gnmi.OC().Interface(intf).Config(), i) +} + +// ConfigOpticalChannel configures the optical channel. +func ConfigOpticalChannel(t *testing.T, dut *ondatra.DUTDevice, och string, frequency uint64, targetOpticalPower float64, operationalMode uint16) { + gnmi.Replace(t, dut, gnmi.OC().Component(och).OpticalChannel().Config(), &oc.Component_OpticalChannel{ + OperationalMode: ygot.Uint16(operationalMode), + Frequency: ygot.Uint64(frequency), + TargetOutputPower: ygot.Float64(targetOpticalPower), + }) +} + +// ConfigOTNChannel configures the OTN channel. +func ConfigOTNChannel(t *testing.T, dut *ondatra.DUTDevice, och string, otnIndex, ethIndex uint32) { + t.Helper() + gnmi.Replace(t, dut, gnmi.OC().TerminalDevice().Channel(otnIndex).Config(), &oc.TerminalDevice_Channel{ + Description: ygot.String("OTN Logical Channel"), + Index: ygot.Uint32(otnIndex), + LogicalChannelType: oc.TransportTypes_LOGICAL_ELEMENT_PROTOCOL_TYPE_PROT_OTN, + TribProtocol: oc.TransportTypes_TRIBUTARY_PROTOCOL_TYPE_PROT_400GE, + Assignment: map[uint32]*oc.TerminalDevice_Channel_Assignment{ + 0: { + Index: ygot.Uint32(0), + OpticalChannel: ygot.String(och), + Description: ygot.String("OTN to Optical Channel"), + Allocation: ygot.Float64(400), + AssignmentType: oc.Assignment_AssignmentType_OPTICAL_CHANNEL, + }, + 1: { + Index: ygot.Uint32(1), + LogicalChannel: ygot.Uint32(ethIndex), + Description: ygot.String("OTN to ETH"), + Allocation: ygot.Float64(400), + AssignmentType: oc.Assignment_AssignmentType_LOGICAL_CHANNEL, + }, + }, + }) +} + +// ConfigETHChannel configures the ETH channel. +func ConfigETHChannel(t *testing.T, dut *ondatra.DUTDevice, interfaceName, transceiverName string, otnIndex, ethIndex uint32) { + t.Helper() + gnmi.Replace(t, dut, gnmi.OC().TerminalDevice().Channel(ethIndex).Config(), &oc.TerminalDevice_Channel{ + Description: ygot.String("ETH Logical Channel"), + Index: ygot.Uint32(ethIndex), + LogicalChannelType: oc.TransportTypes_LOGICAL_ELEMENT_PROTOCOL_TYPE_PROT_ETHERNET, + TribProtocol: oc.TransportTypes_TRIBUTARY_PROTOCOL_TYPE_PROT_400GE, + Ingress: &oc.TerminalDevice_Channel_Ingress{ + Interface: ygot.String(interfaceName), + Transceiver: ygot.String(transceiverName), + }, + Assignment: map[uint32]*oc.TerminalDevice_Channel_Assignment{ + 0: { + Index: ygot.Uint32(0), + LogicalChannel: ygot.Uint32(otnIndex), + Description: ygot.String("ETH to OTN"), + Allocation: ygot.Float64(400), + AssignmentType: oc.Assignment_AssignmentType_LOGICAL_CHANNEL, + }, + }, + }) +} diff --git a/internal/deviations/deviations.go b/internal/deviations/deviations.go index 0197075438b..7cca103c0cd 100644 --- a/internal/deviations/deviations.go +++ b/internal/deviations/deviations.go @@ -1024,12 +1024,36 @@ func DefaultRoutePolicyUnsupported(dut *ondatra.DUTDevice) bool { return lookupDUTDeviations(dut).GetDefaultRoutePolicyUnsupported() } -// Devices does not support bgp max multipaths. +// CommunityMatchWithRedistributionUnsupported is set to true for devices that do not support matching community at the redistribution attach point. +func CommunityMatchWithRedistributionUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetCommunityMatchWithRedistributionUnsupported() +} + +// BgpMaxMultipathPathsUnsupported returns true if the device does not support +// bgp max multipaths. func BgpMaxMultipathPathsUnsupported(dut *ondatra.DUTDevice) bool { return lookupDUTDeviations(dut).GetBgpMaxMultipathPathsUnsupported() } -// Devices does not support multipath under neighbor or afisafi +// MultipathUnsupportedNeighborOrAfisafi returns true if the device does not +// support multipath under neighbor or afisafi. func MultipathUnsupportedNeighborOrAfisafi(dut *ondatra.DUTDevice) bool { return lookupDUTDeviations(dut).GetMultipathUnsupportedNeighborOrAfisafi() } + +// ModelNameUnsupported returns true if /components/components/state/model-name +// is not supported for any component type. +func ModelNameUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetModelNameUnsupported() +} + +// InstallPositionAndInstallComponentUnsupported returns true if install +// position and install component are not supported. +func InstallPositionAndInstallComponentUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetInstallPositionAndInstallComponentUnsupported() +} + +// EncapTunnelShutBackupNhgZeroTraffic returns true when encap tunnel is shut then zero traffic flows to backup NHG +func EncapTunnelShutBackupNhgZeroTraffic(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetEncapTunnelShutBackupNhgZeroTraffic() +} diff --git a/internal/encap_frr/base.go b/internal/encap_frr/base.go new file mode 100644 index 00000000000..30a0203e072 --- /dev/null +++ b/internal/encap_frr/base.go @@ -0,0 +1,578 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package base contains utility functions for encap frr using repair VRF. +package base + +import ( + "context" + "strconv" + "testing" + "time" + + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/gribigo/chk" + "github.com/openconfig/gribigo/constants" + "github.com/openconfig/gribigo/fluent" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/ygot/ygot" +) + +const ( + plenIPv4 = 30 + magicIP = "192.168.1.1" + magicMAC = "02:00:00:00:00:01" + maskLen24 = "24" + maskLen32 = "32" + niEncapTeVrfA = "ENCAP_TE_VRF_A" + niTEVRF111 = "TE_VRF_111" + niTEVRF222 = "TE_VRF_222" + niRepairVrf = "REPAIR_VRF" + ipv4OuterSrc111Addr = "198.51.100.111" + ipv4OuterSrc222Addr = "198.51.100.222" + gribiIPv4EntryDefVRF1 = "192.0.2.101" + gribiIPv4EntryDefVRF2 = "192.0.2.102" + gribiIPv4EntryDefVRF3 = "192.0.2.103" + gribiIPv4EntryDefVRF4 = "192.0.2.104" + gribiIPv4EntryDefVRF5 = "192.0.2.105" + gribiIPv4EntryVRF1111 = "203.0.113.1" + gribiIPv4EntryVRF1112 = "203.0.113.2" + gribiIPv4EntryVRF2221 = "203.0.113.100" + gribiIPv4EntryVRF2222 = "203.0.113.101" + gribiIPv4EntryEncapVRF = "138.0.11.0" + noMatchEncapDest = "20.0.0.1" +) + +var ( + dutPort2DummyIP = attrs.Attributes{ + Desc: "dutPort2", + IPv4Sec: "192.0.2.33", + IPv4LenSec: plenIPv4, + } + + otgPort2DummyIP = attrs.Attributes{ + Desc: "otgPort2", + IPv4: "192.0.2.34", + IPv4Len: plenIPv4, + } + + dutPort3DummyIP = attrs.Attributes{ + Desc: "dutPort3", + IPv4Sec: "192.0.2.37", + IPv4LenSec: plenIPv4, + } + + otgPort3DummyIP = attrs.Attributes{ + Desc: "otgPort3", + IPv4: "192.0.2.38", + IPv4Len: plenIPv4, + } + + dutPort4DummyIP = attrs.Attributes{ + Desc: "dutPort4", + IPv4Sec: "192.0.2.41", + IPv4LenSec: plenIPv4, + } + + otgPort4DummyIP = attrs.Attributes{ + Desc: "otgPort4", + IPv4: "192.0.2.42", + IPv4Len: plenIPv4, + } + + dutPort5DummyIP = attrs.Attributes{ + Desc: "dutPort5", + IPv4Sec: "192.0.2.45", + IPv4LenSec: plenIPv4, + } + + otgPort5DummyIP = attrs.Attributes{ + Desc: "otgPort5", + IPv4: "192.0.2.46", + IPv4Len: plenIPv4, + } + dutPort6DummyIP = attrs.Attributes{ + Desc: "dutPort5", + IPv4Sec: "192.0.2.49", + IPv4LenSec: plenIPv4, + } + + otgPort6DummyIP = attrs.Attributes{ + Desc: "otgPort5", + IPv4: "192.0.2.50", + IPv4Len: plenIPv4, + } + dutPort7DummyIP = attrs.Attributes{ + Desc: "dutPort5", + IPv4Sec: "192.0.2.53", + IPv4LenSec: plenIPv4, + } + + otgPort7DummyIP = attrs.Attributes{ + Desc: "otgPort5", + IPv4: "192.0.2.54", + IPv4Len: plenIPv4, + } +) + +func programAftWithDummyIP(t *testing.T, dut *ondatra.DUTDevice, client *fluent.GRIBIClient) { + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(11).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port2").Name()). + WithIPAddress(otgPort2DummyIP.IPv4), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(12).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port3").Name()). + WithIPAddress(otgPort3DummyIP.IPv4), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(11).AddNextHop(11, 1).AddNextHop(12, 3), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF1+"/"+maskLen32).WithNextHopGroup(11), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(13).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port4").Name()). + WithIPAddress(otgPort4DummyIP.IPv4), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(12).AddNextHop(13, 2), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF2+"/"+maskLen32).WithNextHopGroup(12), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(14).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port5").Name()). + WithIPAddress(otgPort5DummyIP.IPv4), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(13).AddNextHop(14, 1), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF3+"/"+maskLen32).WithNextHopGroup(13), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(15).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port6").Name()). + WithIPAddress(otgPort6DummyIP.IPv4), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(14).AddNextHop(15, 1), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF4+"/"+maskLen32).WithNextHopGroup(14), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(16).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port7").Name()). + WithIPAddress(otgPort7DummyIP.IPv4), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(15).AddNextHop(16, 1), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF5+"/"+maskLen32).WithNextHopGroup(15), + ) +} + +// configStaticArp configures static arp entries +func configStaticArp(p string, ipv4addr string, macAddr string) *oc.Interface { + i := &oc.Interface{Name: ygot.String(p)} + i.Type = oc.IETFInterfaces_InterfaceType_ethernetCsmacd + s := i.GetOrCreateSubinterface(0) + s4 := s.GetOrCreateIpv4() + n4 := s4.GetOrCreateNeighbor(ipv4addr) + n4.LinkLayerAddress = ygot.String(macAddr) + return i +} + +// StaticARPWithSpecificIP configures secondary IPs and static ARP. +func StaticARPWithSpecificIP(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + p2 := dut.Port(t, "port2") + p3 := dut.Port(t, "port3") + p4 := dut.Port(t, "port4") + p5 := dut.Port(t, "port5") + p6 := dut.Port(t, "port6") + p7 := dut.Port(t, "port7") + gnmi.Update(t, dut, gnmi.OC().Interface(p2.Name()).Config(), dutPort2DummyIP.NewOCInterface(p2.Name(), dut)) + gnmi.Update(t, dut, gnmi.OC().Interface(p3.Name()).Config(), dutPort3DummyIP.NewOCInterface(p3.Name(), dut)) + gnmi.Update(t, dut, gnmi.OC().Interface(p4.Name()).Config(), dutPort4DummyIP.NewOCInterface(p4.Name(), dut)) + gnmi.Update(t, dut, gnmi.OC().Interface(p5.Name()).Config(), dutPort5DummyIP.NewOCInterface(p5.Name(), dut)) + gnmi.Update(t, dut, gnmi.OC().Interface(p6.Name()).Config(), dutPort6DummyIP.NewOCInterface(p6.Name(), dut)) + gnmi.Update(t, dut, gnmi.OC().Interface(p7.Name()).Config(), dutPort7DummyIP.NewOCInterface(p7.Name(), dut)) + gnmi.Update(t, dut, gnmi.OC().Interface(p2.Name()).Config(), configStaticArp(p2.Name(), otgPort2DummyIP.IPv4, magicMAC)) + gnmi.Update(t, dut, gnmi.OC().Interface(p3.Name()).Config(), configStaticArp(p3.Name(), otgPort3DummyIP.IPv4, magicMAC)) + gnmi.Update(t, dut, gnmi.OC().Interface(p4.Name()).Config(), configStaticArp(p4.Name(), otgPort4DummyIP.IPv4, magicMAC)) + gnmi.Update(t, dut, gnmi.OC().Interface(p5.Name()).Config(), configStaticArp(p5.Name(), otgPort5DummyIP.IPv4, magicMAC)) + gnmi.Update(t, dut, gnmi.OC().Interface(p6.Name()).Config(), configStaticArp(p6.Name(), otgPort6DummyIP.IPv4, magicMAC)) + gnmi.Update(t, dut, gnmi.OC().Interface(p7.Name()).Config(), configStaticArp(p7.Name(), otgPort7DummyIP.IPv4, magicMAC)) +} + +// StaticARPWithMagicUniversalIP programs the static ARP with magic universal IP +func StaticARPWithMagicUniversalIP(t *testing.T, dut *ondatra.DUTDevice) { + t.Helper() + sb := &gnmi.SetBatch{} + p2 := dut.Port(t, "port2") + p3 := dut.Port(t, "port3") + p4 := dut.Port(t, "port4") + p5 := dut.Port(t, "port5") + p6 := dut.Port(t, "port6") + p7 := dut.Port(t, "port7") + portList := []*ondatra.Port{p2, p3, p4, p5, p6, p7} + for idx, p := range portList { + s := &oc.NetworkInstance_Protocol_Static{ + Prefix: ygot.String(magicIP + "/32"), + NextHop: map[string]*oc.NetworkInstance_Protocol_Static_NextHop{ + strconv.Itoa(idx): { + Index: ygot.String(strconv.Itoa(idx)), + InterfaceRef: &oc.NetworkInstance_Protocol_Static_NextHop_InterfaceRef{ + Interface: ygot.String(p.Name()), + }, + }, + }, + } + sp := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, deviations.StaticProtocolName(dut)) + gnmi.BatchUpdate(sb, sp.Static(magicIP+"/32").Config(), s) + gnmi.BatchUpdate(sb, gnmi.OC().Interface(p.Name()).Config(), configStaticArp(p.Name(), magicIP, magicMAC)) + } + sb.Set(t, dut) +} + +// ConfigureBaseGribiRoutes programs the base gribi routes for encap FRR using repair VRF +func ConfigureBaseGribiRoutes(ctx context.Context, t *testing.T, dut *ondatra.DUTDevice, client *fluent.GRIBIClient) { + t.Helper() + + // Programming AFT entries for prefixes in DEFAULT VRF + if deviations.GRIBIMACOverrideStaticARPStaticRoute(dut) { + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(11).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port2").Name()).WithIPAddress(magicIP), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(12).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port3").Name()).WithIPAddress(magicIP), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(11).AddNextHop(11, 1).AddNextHop(12, 3), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(13).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port4").Name()).WithIPAddress(magicIP), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(12).AddNextHop(13, 2), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(14).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port5").Name()).WithIPAddress(magicIP), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(13).AddNextHop(14, 1), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(15).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port6").Name()).WithIPAddress(magicIP), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(14).AddNextHop(15, 1), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(16).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port7").Name()).WithIPAddress(magicIP), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(15).AddNextHop(16, 1), + ) + } else if deviations.GRIBIMACOverrideWithStaticARP(dut) { + programAftWithDummyIP(t, dut, client) + } else { + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(11).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port2").Name()), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(12).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port3").Name()), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(11).AddNextHop(11, 1).AddNextHop(12, 3), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(13).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port4").Name()), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(12).AddNextHop(13, 2), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(14).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port5").Name()), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(13).AddNextHop(14, 1), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(15).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port6").Name()), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(14).AddNextHop(15, 1), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(16).WithMacAddress(magicMAC).WithInterfaceRef(dut.Port(t, "port7").Name()), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(15).AddNextHop(16, 1), + ) + } + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + client.Modify().AddEntry(t, + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF1+"/"+maskLen32).WithNextHopGroup(11), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF2+"/"+maskLen32).WithNextHopGroup(12), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF3+"/"+maskLen32).WithNextHopGroup(13), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF4+"/"+maskLen32).WithNextHopGroup(14), + fluent.IPv4Entry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryDefVRF5+"/"+maskLen32).WithNextHopGroup(15), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + defaultVRFIPList := []string{gribiIPv4EntryDefVRF1, gribiIPv4EntryDefVRF2, gribiIPv4EntryDefVRF3, gribiIPv4EntryDefVRF4, gribiIPv4EntryDefVRF5} + for ip := range defaultVRFIPList { + chk.HasResult(t, client.Results(t), + fluent.OperationResult(). + WithIPv4Operation(defaultVRFIPList[ip]+"/32"). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) + } + + // Programming AFT entries for backup NHG + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(2000).WithDecapsulateHeader(fluent.IPinIP).WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(2000).AddNextHop(2000, 1), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + // Programming AFT entries for prefixes in TE_VRF_222 + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(3).WithIPAddress(gribiIPv4EntryDefVRF3), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(2).AddNextHop(3, 1).WithBackupNHG(2000), + fluent.IPv4Entry().WithNetworkInstance(niTEVRF222).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryVRF2221+"/"+maskLen32).WithNextHopGroup(2), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(5).WithIPAddress(gribiIPv4EntryDefVRF5), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(4).AddNextHop(5, 1).WithBackupNHG(2000), + fluent.IPv4Entry().WithNetworkInstance(niTEVRF222).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryVRF2222+"/"+maskLen32).WithNextHopGroup(4), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + teVRF222IPList := []string{gribiIPv4EntryVRF2221, gribiIPv4EntryVRF2222} + for ip := range teVRF222IPList { + chk.HasResult(t, client.Results(t), + fluent.OperationResult(). + WithIPv4Operation(teVRF222IPList[ip]+"/32"). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) + } + + // Programming AFT entries for backup NHG + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1000).WithDecapsulateHeader(fluent.IPinIP).WithEncapsulateHeader(fluent.IPinIP). + WithIPinIP(ipv4OuterSrc222Addr, gribiIPv4EntryVRF2221). + WithNextHopNetworkInstance(niTEVRF222), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1000).AddNextHop(1000, 1).WithBackupNHG(2000), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1001).WithDecapsulateHeader(fluent.IPinIP).WithEncapsulateHeader(fluent.IPinIP). + WithIPinIP(ipv4OuterSrc222Addr, gribiIPv4EntryVRF2222). + WithNextHopNetworkInstance(niTEVRF222), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1001).AddNextHop(1001, 1).WithBackupNHG(2000), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(3000).WithNextHopNetworkInstance(niRepairVrf), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(3000).AddNextHop(3000, 1), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + // Programming AFT entries for prefixes in TE_VRF_111 + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(1).WithIPAddress(gribiIPv4EntryDefVRF1), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(2).WithIPAddress(gribiIPv4EntryDefVRF2), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(1).AddNextHop(1, 1).AddNextHop(2, 3).WithBackupNHG(3000), + fluent.IPv4Entry().WithNetworkInstance(niTEVRF111).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryVRF1111+"/"+maskLen32).WithNextHopGroup(1), + + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(4).WithIPAddress(gribiIPv4EntryDefVRF4), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(3).AddNextHop(4, 1).WithBackupNHG(3000), + fluent.IPv4Entry().WithNetworkInstance(niTEVRF111).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryVRF1112+"/"+maskLen32).WithNextHopGroup(3), + + fluent.IPv4Entry().WithNetworkInstance(niRepairVrf).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryVRF1111+"/"+maskLen32).WithNextHopGroup(1000), + fluent.IPv4Entry().WithNetworkInstance(niRepairVrf).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryVRF1112+"/"+maskLen32).WithNextHopGroup(1001), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + teVRF111IPList := []string{gribiIPv4EntryVRF1111, gribiIPv4EntryVRF1112} + for ip := range teVRF111IPList { + chk.HasResult(t, client.Results(t), + fluent.OperationResult(). + WithIPv4Operation(teVRF111IPList[ip]+"/32"). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) + } + + // Programming AFT entries for backup NHG + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(2001).WithNextHopNetworkInstance(deviations.DefaultNetworkInstance(dut)), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(2001).AddNextHop(2001, 1), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + // Programming AFT entries for prefixes in ENCAP_TE_VRF_A + client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(101).WithEncapsulateHeader(fluent.IPinIP). + WithIPinIP(ipv4OuterSrc111Addr, gribiIPv4EntryVRF1111). + WithNextHopNetworkInstance(niTEVRF111), + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithIndex(102).WithEncapsulateHeader(fluent.IPinIP). + WithIPinIP(ipv4OuterSrc111Addr, gribiIPv4EntryVRF1112). + WithNextHopNetworkInstance(niTEVRF111), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithID(101).AddNextHop(101, 1).AddNextHop(102, 3).WithBackupNHG(2001), + fluent.IPv4Entry().WithNetworkInstance(niEncapTeVrfA).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(gribiIPv4EntryEncapVRF+"/"+maskLen24).WithNextHopGroup(101), + ) + if err := awaitTimeout(ctx, t, client, time.Minute); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + chk.HasResult(t, client.Results(t), + fluent.OperationResult(). + WithIPv4Operation(gribiIPv4EntryEncapVRF+"/24"). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) +} + +// TestCase is a struct to hold the parameters for FRR test cases. +type TestCase struct { + Desc string + DownPortList []string + CapturePortList []string + EncapHeaderOuterIPList []string + EncapHeaderInnerIPList []string + TrafficDestIP string + LoadBalancePercent []float64 + TestID string +} + +// TestCases returns a list of base test cases for FRR tests. +func TestCases(atePortNamelist []string, ipv4InnerDst string) []*TestCase { + cases := []*TestCase{ + { + Desc: "Test-1: primary encap unviable but backup encap viable for single tunnel", + DownPortList: []string{"port2", "port3", "port4"}, + CapturePortList: []string{atePortNamelist[4], atePortNamelist[5]}, + EncapHeaderOuterIPList: []string{gribiIPv4EntryVRF2221, gribiIPv4EntryVRF1112}, + EncapHeaderInnerIPList: []string{ipv4InnerDst, ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0.25, 0.75, 0, 0}, + TestID: "primarySingle", + }, { + Desc: "Test-2: primary and backup encap unviable for single tunnel", + DownPortList: []string{"port2", "port3", "port4", "port5"}, + CapturePortList: []string{atePortNamelist[5], atePortNamelist[7]}, + EncapHeaderOuterIPList: []string{gribiIPv4EntryVRF1112}, + EncapHeaderInnerIPList: []string{ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0, 0.75, 0, 0.25}, + TestID: "primaryBackupSingle", + }, { + Desc: "Test-3: primary encap unviable with backup to routing for single tunnel", + DownPortList: []string{"port2", "port3", "port4"}, + CapturePortList: []string{atePortNamelist[5], atePortNamelist[7]}, + EncapHeaderOuterIPList: []string{gribiIPv4EntryVRF1112}, + EncapHeaderInnerIPList: []string{ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0, 0.75, 0, 0.25}, + TestID: "primaryBackupRoutingSingle", + }, { + Desc: "Test-4: primary encap unviable but backup encap viable for all tunnels", + DownPortList: []string{"port2", "port3", "port4", "port6"}, + CapturePortList: []string{atePortNamelist[4], atePortNamelist[6]}, + EncapHeaderOuterIPList: []string{gribiIPv4EntryVRF2221, gribiIPv4EntryVRF2222}, + EncapHeaderInnerIPList: []string{ipv4InnerDst, ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0.25, 0, 0.75, 0}, + TestID: "primaryAll", + }, { + Desc: "Test-5: primary and backup encap unviable for all tunnels", + DownPortList: []string{"port2", "port3", "port4", "port5", "port6", "port7"}, + CapturePortList: []string{atePortNamelist[7]}, + EncapHeaderOuterIPList: []string{}, + EncapHeaderInnerIPList: []string{ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0, 0, 0, 1}, + TestID: "primaryBackupAll", + }, { + Desc: "Test-6: primary encap unviable with backup to routing for all tunnels", + DownPortList: []string{"port2", "port3", "port4", "port6"}, + CapturePortList: []string{atePortNamelist[7]}, + EncapHeaderOuterIPList: []string{}, + EncapHeaderInnerIPList: []string{ipv4InnerDst}, + TrafficDestIP: ipv4InnerDst, + LoadBalancePercent: []float64{0, 0, 0, 0, 0, 0, 1}, + TestID: "primaryBackupRoutingAll", + }, { + Desc: "Test-7: no match in encap VRF", + DownPortList: []string{}, + CapturePortList: []string{atePortNamelist[7]}, + EncapHeaderOuterIPList: []string{}, + EncapHeaderInnerIPList: []string{noMatchEncapDest}, + TrafficDestIP: noMatchEncapDest, + LoadBalancePercent: []float64{0, 0, 0, 0, 0, 0, 1}, + TestID: "encapNoMatch", + }, + } + + return cases +} + +// awaitTimeout calls a fluent client Await, adding a timeout to the context. +func awaitTimeout(ctx context.Context, t testing.TB, c *fluent.GRIBIClient, timeout time.Duration) error { + t.Helper() + subctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + return c.Await(subctx, t) +} diff --git a/proto/metadata.proto b/proto/metadata.proto index 520990591fc..051148b8d1f 100644 --- a/proto/metadata.proto +++ b/proto/metadata.proto @@ -563,10 +563,24 @@ message Metadata { // Devices does not multipath config at neighbor or afisafi level // Juniper: b/341130490 bool multipath_unsupported_neighbor_or_afisafi = 193; - + // Devices that do not support /components/component/state/model-name for + // any component types. + // Note that for model name to be supported, the + // /components/component/state/model-name of the chassis component must be + // equal to the canonical hardware model name of its device. + bool model_name_unsupported = 194; + // community_match_with_redistribution_unsupported is set to true for devices that do not support matching community at the redistribution attach point. + bool community_match_with_redistribution_unsupported = 195; + // Devices that do not support components/component/state/install-component + // and components/component/state/install-position. + bool install_position_and_install_component_unsupported = 196; + // Encap tunnel is shut then zero traffic will flow to backup NHG + bool encap_tunnel_shut_backup_nhg_zero_traffic = 197; + // Reserved field numbers and identifiers. reserved 84, 9, 28, 20, 90, 97, 55, 89, 19; } + message PlatformExceptions { Platform platform = 1; Deviations deviations = 2; diff --git a/proto/metadata_go_proto/metadata.pb.go b/proto/metadata_go_proto/metadata.pb.go index e08b8e922eb..023ba1e9780 100644 --- a/proto/metadata_go_proto/metadata.pb.go +++ b/proto/metadata_go_proto/metadata.pb.go @@ -14,7 +14,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.34.1 +// protoc-gen-go v1.33.0 // protoc v3.21.12 // source: metadata.proto @@ -845,6 +845,19 @@ type Metadata_Deviations struct { // Devices does not multipath config at neighbor or afisafi level // Juniper: b/341130490 MultipathUnsupportedNeighborOrAfisafi bool `protobuf:"varint,193,opt,name=multipath_unsupported_neighbor_or_afisafi,json=multipathUnsupportedNeighborOrAfisafi,proto3" json:"multipath_unsupported_neighbor_or_afisafi,omitempty"` + // Devices that do not support /components/component/state/model-name for + // any component types. + // Note that for model name to be supported, the + // /components/component/state/model-name of the chassis component must be + // equal to the canonical hardware model name of its device. + ModelNameUnsupported bool `protobuf:"varint,194,opt,name=model_name_unsupported,json=modelNameUnsupported,proto3" json:"model_name_unsupported,omitempty"` + // community_match_with_redistribution_unsupported is set to true for devices that do not support matching community at the redistribution attach point. + CommunityMatchWithRedistributionUnsupported bool `protobuf:"varint,195,opt,name=community_match_with_redistribution_unsupported,json=communityMatchWithRedistributionUnsupported,proto3" json:"community_match_with_redistribution_unsupported,omitempty"` + // Devices that do not support components/component/state/install-component + // and components/component/state/install-position. + InstallPositionAndInstallComponentUnsupported bool `protobuf:"varint,196,opt,name=install_position_and_install_component_unsupported,json=installPositionAndInstallComponentUnsupported,proto3" json:"install_position_and_install_component_unsupported,omitempty"` + // Encap tunnel is shut then zero traffic will flow to backup NHG + EncapTunnelShutBackupNhgZeroTraffic bool `protobuf:"varint,197,opt,name=encap_tunnel_shut_backup_nhg_zero_traffic,json=encapTunnelShutBackupNhgZeroTraffic,proto3" json:"encap_tunnel_shut_backup_nhg_zero_traffic,omitempty"` } func (x *Metadata_Deviations) Reset() { @@ -2118,6 +2131,34 @@ func (x *Metadata_Deviations) GetMultipathUnsupportedNeighborOrAfisafi() bool { return false } +func (x *Metadata_Deviations) GetModelNameUnsupported() bool { + if x != nil { + return x.ModelNameUnsupported + } + return false +} + +func (x *Metadata_Deviations) GetCommunityMatchWithRedistributionUnsupported() bool { + if x != nil { + return x.CommunityMatchWithRedistributionUnsupported + } + return false +} + +func (x *Metadata_Deviations) GetInstallPositionAndInstallComponentUnsupported() bool { + if x != nil { + return x.InstallPositionAndInstallComponentUnsupported + } + return false +} + +func (x *Metadata_Deviations) GetEncapTunnelShutBackupNhgZeroTraffic() bool { + if x != nil { + return x.EncapTunnelShutBackupNhgZeroTraffic + } + return false +} + type Metadata_PlatformExceptions struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2181,7 +2222,7 @@ var file_metadata_proto_rawDesc = []byte{ 0x74, 0x69, 0x6e, 0x67, 0x1a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x72, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x62, 0x65, - 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc1, 0x6d, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa4, 0x70, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x61, 0x6e, 0x49, @@ -2215,7 +2256,7 @@ var file_metadata_proto_rawDesc = []byte{ 0x67, 0x65, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x67, 0x65, 0x78, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x52, 0x0e, 0x68, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x5f, - 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x1a, 0x94, 0x65, 0x0a, 0x0a, 0x44, 0x65, 0x76, 0x69, 0x61, 0x74, + 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x1a, 0xf7, 0x67, 0x0a, 0x0a, 0x44, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x70, 0x76, 0x34, 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x69, 0x70, 0x76, 0x34, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x45, @@ -3021,44 +3062,66 @@ var file_metadata_proto_rawDesc = []byte{ 0x5f, 0x61, 0x66, 0x69, 0x73, 0x61, 0x66, 0x69, 0x18, 0xc1, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x25, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x61, 0x74, 0x68, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x4e, 0x65, 0x69, 0x67, 0x68, 0x62, 0x6f, 0x72, 0x4f, 0x72, 0x41, - 0x66, 0x69, 0x73, 0x61, 0x66, 0x69, 0x4a, 0x04, 0x08, 0x54, 0x10, 0x55, 0x4a, 0x04, 0x08, 0x09, - 0x10, 0x0a, 0x4a, 0x04, 0x08, 0x1c, 0x10, 0x1d, 0x4a, 0x04, 0x08, 0x14, 0x10, 0x15, 0x4a, 0x04, - 0x08, 0x5a, 0x10, 0x5b, 0x4a, 0x04, 0x08, 0x61, 0x10, 0x62, 0x4a, 0x04, 0x08, 0x37, 0x10, 0x38, - 0x4a, 0x04, 0x08, 0x59, 0x10, 0x5a, 0x4a, 0x04, 0x08, 0x13, 0x10, 0x14, 0x1a, 0xa0, 0x01, 0x0a, - 0x12, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x41, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x08, 0x70, 0x6c, - 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x47, 0x0a, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65, - 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, - 0xfa, 0x01, 0x0a, 0x07, 0x54, 0x65, 0x73, 0x74, 0x62, 0x65, 0x64, 0x12, 0x17, 0x0a, 0x13, 0x54, - 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, - 0x44, 0x55, 0x54, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, - 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x34, 0x4c, 0x49, 0x4e, 0x4b, 0x53, 0x10, - 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, - 0x5f, 0x41, 0x54, 0x45, 0x5f, 0x32, 0x4c, 0x49, 0x4e, 0x4b, 0x53, 0x10, 0x03, 0x12, 0x1a, 0x0a, - 0x16, 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x41, 0x54, 0x45, - 0x5f, 0x34, 0x4c, 0x49, 0x4e, 0x4b, 0x53, 0x10, 0x04, 0x12, 0x1e, 0x0a, 0x1a, 0x54, 0x45, 0x53, - 0x54, 0x42, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x41, 0x54, 0x45, 0x5f, 0x39, 0x4c, 0x49, - 0x4e, 0x4b, 0x53, 0x5f, 0x4c, 0x41, 0x47, 0x10, 0x05, 0x12, 0x1e, 0x0a, 0x1a, 0x54, 0x45, 0x53, - 0x54, 0x42, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x41, 0x54, 0x45, - 0x5f, 0x32, 0x4c, 0x49, 0x4e, 0x4b, 0x53, 0x10, 0x06, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x45, 0x53, - 0x54, 0x42, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x41, 0x54, 0x45, 0x5f, 0x38, 0x4c, 0x49, - 0x4e, 0x4b, 0x53, 0x10, 0x07, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, - 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x34, 0x30, 0x30, 0x5a, 0x52, 0x10, 0x08, 0x22, 0x6d, 0x0a, 0x04, - 0x54, 0x61, 0x67, 0x73, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x41, 0x47, 0x53, 0x5f, 0x55, 0x4e, 0x53, - 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x41, - 0x47, 0x53, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, - 0x12, 0x18, 0x0a, 0x14, 0x54, 0x41, 0x47, 0x53, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x43, 0x45, 0x4e, - 0x54, 0x45, 0x52, 0x5f, 0x45, 0x44, 0x47, 0x45, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x41, - 0x47, 0x53, 0x5f, 0x45, 0x44, 0x47, 0x45, 0x10, 0x03, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x41, 0x47, - 0x53, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x49, 0x54, 0x10, 0x04, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x66, 0x69, 0x73, 0x61, 0x66, 0x69, 0x12, 0x35, 0x0a, 0x16, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x18, 0xc2, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x4e, 0x61, + 0x6d, 0x65, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x65, 0x0a, + 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x6d, 0x61, 0x74, 0x63, 0x68, + 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x72, 0x65, 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x18, 0xc3, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x2b, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, + 0x74, 0x79, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x57, 0x69, 0x74, 0x68, 0x52, 0x65, 0x64, 0x69, 0x73, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x12, 0x6a, 0x0a, 0x32, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x5f, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x73, + 0x74, 0x61, 0x6c, 0x6c, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x5f, 0x75, + 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0xc4, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x2d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x41, 0x6e, 0x64, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x43, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x12, 0x57, 0x0a, 0x29, 0x65, 0x6e, 0x63, 0x61, 0x70, 0x5f, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, + 0x5f, 0x73, 0x68, 0x75, 0x74, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x6e, 0x68, 0x67, + 0x5f, 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x18, 0xc5, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x23, 0x65, 0x6e, 0x63, 0x61, 0x70, 0x54, 0x75, 0x6e, 0x6e, 0x65, + 0x6c, 0x53, 0x68, 0x75, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x4e, 0x68, 0x67, 0x5a, 0x65, + 0x72, 0x6f, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x4a, 0x04, 0x08, 0x54, 0x10, 0x55, 0x4a, + 0x04, 0x08, 0x09, 0x10, 0x0a, 0x4a, 0x04, 0x08, 0x1c, 0x10, 0x1d, 0x4a, 0x04, 0x08, 0x14, 0x10, + 0x15, 0x4a, 0x04, 0x08, 0x5a, 0x10, 0x5b, 0x4a, 0x04, 0x08, 0x61, 0x10, 0x62, 0x4a, 0x04, 0x08, + 0x37, 0x10, 0x38, 0x4a, 0x04, 0x08, 0x59, 0x10, 0x5a, 0x4a, 0x04, 0x08, 0x13, 0x10, 0x14, 0x1a, + 0xa0, 0x01, 0x0a, 0x12, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x45, 0x78, 0x63, 0x65, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x41, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, + 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x52, + 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x47, 0x0a, 0x0a, 0x64, 0x65, 0x76, + 0x69, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, + 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, + 0x6e, 0x67, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x44, 0x65, 0x76, 0x69, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0a, 0x64, 0x65, 0x76, 0x69, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x22, 0xfa, 0x01, 0x0a, 0x07, 0x54, 0x65, 0x73, 0x74, 0x62, 0x65, 0x64, 0x12, 0x17, + 0x0a, 0x13, 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, + 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x45, 0x53, 0x54, 0x42, + 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x45, 0x53, 0x54, + 0x42, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x34, 0x4c, 0x49, 0x4e, + 0x4b, 0x53, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, + 0x44, 0x55, 0x54, 0x5f, 0x41, 0x54, 0x45, 0x5f, 0x32, 0x4c, 0x49, 0x4e, 0x4b, 0x53, 0x10, 0x03, + 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, + 0x41, 0x54, 0x45, 0x5f, 0x34, 0x4c, 0x49, 0x4e, 0x4b, 0x53, 0x10, 0x04, 0x12, 0x1e, 0x0a, 0x1a, + 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x41, 0x54, 0x45, 0x5f, + 0x39, 0x4c, 0x49, 0x4e, 0x4b, 0x53, 0x5f, 0x4c, 0x41, 0x47, 0x10, 0x05, 0x12, 0x1e, 0x0a, 0x1a, + 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x44, 0x55, 0x54, 0x5f, + 0x41, 0x54, 0x45, 0x5f, 0x32, 0x4c, 0x49, 0x4e, 0x4b, 0x53, 0x10, 0x06, 0x12, 0x1a, 0x0a, 0x16, + 0x54, 0x45, 0x53, 0x54, 0x42, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x41, 0x54, 0x45, 0x5f, + 0x38, 0x4c, 0x49, 0x4e, 0x4b, 0x53, 0x10, 0x07, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x45, 0x53, 0x54, + 0x42, 0x45, 0x44, 0x5f, 0x44, 0x55, 0x54, 0x5f, 0x34, 0x30, 0x30, 0x5a, 0x52, 0x10, 0x08, 0x22, + 0x6d, 0x0a, 0x04, 0x54, 0x61, 0x67, 0x73, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x41, 0x47, 0x53, 0x5f, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, + 0x10, 0x54, 0x41, 0x47, 0x53, 0x5f, 0x41, 0x47, 0x47, 0x52, 0x45, 0x47, 0x41, 0x54, 0x49, 0x4f, + 0x4e, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x54, 0x41, 0x47, 0x53, 0x5f, 0x44, 0x41, 0x54, 0x41, + 0x43, 0x45, 0x4e, 0x54, 0x45, 0x52, 0x5f, 0x45, 0x44, 0x47, 0x45, 0x10, 0x02, 0x12, 0x0d, 0x0a, + 0x09, 0x54, 0x41, 0x47, 0x53, 0x5f, 0x45, 0x44, 0x47, 0x45, 0x10, 0x03, 0x12, 0x10, 0x0a, 0x0c, + 0x54, 0x41, 0x47, 0x53, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, 0x49, 0x54, 0x10, 0x04, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/testregistry.textproto b/testregistry.textproto index 92451754997..651fcca1dc8 100644 --- a/testregistry.textproto +++ b/testregistry.textproto @@ -530,6 +530,12 @@ test: { id: "RT-1.33" readme: "https://github.com/openconfig/featureprofiles/feature/bgp/policybase/otg_tests/prefix_set_test/README.md" } +test: { + id: "RT-1.34" + description: "Administrative Distance test for iBGP and eBGP" + readme: "https://github.com/openconfig/featureprofiles/feature/bgp/admin_distance/README.md" + exec: " " +} test: { id: "RT-1.3" description: "BGP Route Propagation"