diff --git a/feature/gribi/otg_tests/gribi_static_route_on_lemmings_test/README.md b/feature/gribi/otg_tests/gribi_static_route_on_lemmings_test/README.md new file mode 100644 index 00000000000..2dbfbd4334a --- /dev/null +++ b/feature/gribi/otg_tests/gribi_static_route_on_lemmings_test/README.md @@ -0,0 +1,48 @@ +# RT: GRIBI Static Route On Lemmings Test + +## Summary + +Ensure the GRIBI injecting the route entry on lemmings (Control plane), which is brought up in KNE topology + +## Topology + +ATE port-1 <------> port-1 DUT +DUT port-2 <------> port-2 ATE + + +## Baseline + +``` +### Install the following gRIBI AFTs. + +- IPv4Entry {198.50.100.64/32 (DEFAULT)} -> NHG#2 (DEFAULT VRF) -> { + {NH#2, DEFAULT VRF, weight:1}, interface-ref:dut-port-2-interface, + } + +``` + +## Procedure + +The DUT should be reset to the baseline after each of the following tests. + +Test-1, Configure the route entry on lemmings in KNE topology + +``` +1. Configure the DUT and OTG. +2. Configure Nh, NHG and IPv4 entry through gRIBI route injection. +3. Validate the acknowledgement for FIB installation success. + +``` + +## OpenConfig Path and RPC Coverage +```yaml +rpcs: + gnmi: + gNMI.Get: + gNMI.Set: + gNMI.Subscribe: + gribi: + gRIBI.Get: + gRIBI.Modify: + gRIBI.Flush: +``` diff --git a/feature/gribi/otg_tests/gribi_static_route_on_lemmings_test/gribi_static_route_on_lemmings_test.go b/feature/gribi/otg_tests/gribi_static_route_on_lemmings_test/gribi_static_route_on_lemmings_test.go new file mode 100644 index 00000000000..548713278da --- /dev/null +++ b/feature/gribi/otg_tests/gribi_static_route_on_lemmings_test/gribi_static_route_on_lemmings_test.go @@ -0,0 +1,185 @@ +package gribi_static_route_on_lemmings_test + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/open-traffic-generator/snappi/gosnappi" + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/gribi" + "github.com/openconfig/gribigo/chk" + "github.com/openconfig/gribigo/constants" + "github.com/openconfig/gribigo/fluent" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" +) + +const ( + niTransitTeVrf = "DEFAULT" + ipv4OuterDst111 = "198.50.100.64" +) + +var ( + dutPort1 = attrs.Attributes{ + Desc: "DUT Port 1", + IPv4: "192.0.2.1", + IPv4Len: 30, + } + dutPort2 = attrs.Attributes{ + Desc: "DUT Port 2", + IPv4: "192.0.2.5", + IPv4Len: 30, + } + + atePort1 = attrs.Attributes{ + Name: "port1", + MAC: "02:00:01:01:01:01", + Desc: "ATE Port 1", + IPv4: "192.0.2.2", + IPv4Len: 30, + } + atePort2 = attrs.Attributes{ + Name: "port2", + MAC: "02:00:02:01:01:01", + Desc: "ATE Port 2", + IPv4: "192.0.2.6", + IPv4Len: 30, + } +) + +// testArgs holds the objects needed by a test case. +type testArgs struct { + dut *ondatra.DUTDevice + ctx context.Context + client *fluent.GRIBIClient +} + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +func TestGRIBI(t *testing.T) { + dut := ondatra.DUT(t, "dut") + configureDUT(t, dut) + configureOTG(t, dut) + configureGribiRoute(t, dut) + + // TODO: Don't have a support to validate the gRIBI route in AFT for Lemmings, implementation request should be raised. + // gnmi.Get(t, dut, gnmi.OC().NetworkInstance(networkInstance).Afts().Ipv4Entry(ipv4OuterDst111+"/"+mask).State()) + + aft := gnmi.Get(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Afts().State()) + t.Logf("dut1 system: %v", aft) + + afts := gnmi.LookupAll(t, dut, gnmi.OC().NetworkInstanceAny().Afts().State()) + t.Logf("dut1 system: %v", afts) + + fibt := gnmi.LookupAll(t, dut, gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).ProtocolAny().State()) + t.Logf("dut1 system: %v", fibt) +} + +func configureDUT(t *testing.T, dut *ondatra.DUTDevice) { + fptest.ConfigureDefaultNetworkInstance(t, dut) + t.Logf("configureDUT") + p1 := dut.Port(t, "port1") + p2 := dut.Port(t, "port2") + + gnmi.Replace(t, dut, gnmi.OC().Interface(p1.Name()).Config(), dutPort1.NewOCInterface(p1.Name(), dut)) + gnmi.Replace(t, dut, gnmi.OC().Interface(p2.Name()).Config(), dutPort2.NewOCInterface(p2.Name(), dut)) + + if deviations.ExplicitPortSpeed(dut) { + fptest.SetPortSpeed(t, p1) + fptest.SetPortSpeed(t, p2) + } + if deviations.ExplicitInterfaceInDefaultVRF(dut) { + fptest.AssignToNetworkInstance(t, dut, p1.Name(), deviations.DefaultNetworkInstance(dut), 0) + fptest.AssignToNetworkInstance(t, dut, p2.Name(), deviations.DefaultNetworkInstance(dut), 0) + } +} + +func configureOTG(t *testing.T, dut *ondatra.DUTDevice) { + t.Logf("configure OTG") + ate := ondatra.ATE(t, "ate") + ap1 := ate.Port(t, "port1") + ap2 := ate.Port(t, "port2") + + cfg := gosnappi.NewConfig() + cfg.Ports().Add().SetName(ap1.ID()) + intf1 := cfg.Devices().Add().SetName("intf1") + eth1Name := fmt.Sprintf("%s.eth", intf1.Name()) + eth1 := intf1.Ethernets().Add().SetName(eth1Name).SetMac(atePort1.MAC) + eth1.Connection().SetPortName(ap1.ID()) + ip1Name := fmt.Sprintf("%s.ipv4", intf1.Name()) + eth1.Ipv4Addresses().Add(). + SetName(ip1Name). + SetAddress(atePort1.IPv4). + SetPrefix(30). + SetGateway(dutPort1.IPv4) + cfg.Ports().Add().SetName(ap2.ID()) + intf2 := cfg.Devices().Add().SetName("intf2") + eth2Name := fmt.Sprintf("%s.eth", intf2.Name()) + eth2 := intf2.Ethernets().Add().SetName(eth2Name).SetMac(atePort2.MAC) + eth2.Connection().SetPortName(ap2.ID()) + ip2Name := fmt.Sprintf("%s.ipv4", intf2.Name()) + eth2.Ipv4Addresses().Add(). + SetName(ip2Name). + SetAddress(atePort2.IPv4). + SetPrefix(30). + SetGateway(dutPort2.IPv4) + ate.OTG().PushConfig(t, cfg) + ate.OTG().StartProtocols(t) +} + +func configureGribiRoute(t *testing.T, dut *ondatra.DUTDevice) { + t.Log("Configure GRIBI") + t.Helper() + ctx := context.Background() + gribic := dut.RawAPIs().GRIBI(t) + client := fluent.NewClient() + client.Connection().WithStub(gribic).WithPersistence().WithInitialElectionID(12, 0). + WithRedundancyMode(fluent.ElectedPrimaryClient).WithFIBACK() + client.Start(ctx, t) + defer client.Stop(t) + gribi.FlushAll(client) + defer gribi.FlushAll(client) + client.StartSending(ctx, t) + gribi.BecomeLeader(t, client) + + tcArgs := &testArgs{ + ctx: ctx, + client: client, + dut: dut, + } + + tcArgs.client.Modify().AddEntry(t, + fluent.NextHopEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(tcArgs.dut)). + WithIndex(uint64(2)).WithIPAddress(atePort2.IPv4), + fluent.NextHopGroupEntry().WithNetworkInstance(deviations.DefaultNetworkInstance(tcArgs.dut)). + WithID(uint64(2)).AddNextHop(uint64(2), uint64(1)), + + fluent.IPv4Entry().WithNetworkInstance(niTransitTeVrf).WithNextHopGroupNetworkInstance(deviations.DefaultNetworkInstance(dut)). + WithPrefix(ipv4OuterDst111+"/32").WithNextHopGroup(uint64(2))) + + if err := awaitTimeout(tcArgs.ctx, t, tcArgs.client, 90*time.Second); err != nil { + t.Logf("Could not program entries via client, got err, check error codes: %v", err) + } + + chk.HasResult(t, tcArgs.client.Results(t), + fluent.OperationResult(). + WithIPv4Operation(ipv4OuterDst111+"/32"). + WithOperationType(constants.Add). + WithProgrammingResult(fluent.InstalledInFIB). + AsResult(), + chk.IgnoreOperationID(), + ) +} + +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/feature/gribi/otg_tests/gribi_static_route_on_lemmings_test/metadata.textproto b/feature/gribi/otg_tests/gribi_static_route_on_lemmings_test/metadata.textproto new file mode 100644 index 00000000000..ce41cb2f6e3 --- /dev/null +++ b/feature/gribi/otg_tests/gribi_static_route_on_lemmings_test/metadata.textproto @@ -0,0 +1,23 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "12335133-aed1-463d-b9cd-76dbf8ef88cc" +plan_id: "RT" +description: "GRIBI Static Route On Lemmings Test" +testbed: TESTBED_DUT_ATE_2LINKS +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + interface_enabled: true + default_network_instance: "default" + route_policy_under_afi_unsupported: true + static_protocol_name: "STATIC" + aggregate_atomic_update: true + missing_value_for_defaults: true + max_ecmp_paths: true + explicit_interface_in_default_vrf: false + } +} +tags: TAGS_DATACENTER_EDGE \ No newline at end of file