Skip to content

Commit

Permalink
Test runner + example for dataplane tests (#507)
Browse files Browse the repository at this point in the history
* Test runner + example for dataplane tests

* feedback
  • Loading branch information
DanG100 authored Dec 13, 2024
1 parent 54ae699 commit f16ef38
Show file tree
Hide file tree
Showing 7 changed files with 454 additions and 11 deletions.
6 changes: 3 additions & 3 deletions integration_tests/dataplane/mplsoudp/mplsoverudp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ func configureDUT(t testing.TB, dut *ondatra.DUTDevice, hop *oc.NetworkInstance_
if err != nil {
t.Fatal(err)
}
saiutil.CreateRIF(t, dut, dut.Port(t, "port1"), dutPort1.MAC)
outRIF := saiutil.CreateRIF(t, dut, dut.Port(t, "port2"), dutPort2.MAC)
saiutil.CreateRIF(t, dut, dut.Port(t, "port1"), dutPort1.MAC, 0)
outRIF := saiutil.CreateRIF(t, dut, dut.Port(t, "port2"), dutPort2.MAC, 0)

nhc := saipb.NewNextHopClient(conn)

Expand Down Expand Up @@ -163,7 +163,7 @@ func configureDUT(t testing.TB, dut *ondatra.DUTDevice, hop *oc.NetworkInstance_
if _, err := fwd.TableEntryAdd(context.Background(), actReq); err != nil {
t.Fatal(err)
}
saiutil.CreateRoute(t, dut, routePrefix, nh.GetOid())
saiutil.CreateRoute(t, dut, routePrefix, nh.GetOid(), 0)
saiutil.CreateNeighbor(t, dut, *hop.IpAddress, neighborMAC, outRIF)
}

Expand Down
19 changes: 19 additions & 0 deletions integration_tests/dataplane/subintf/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
load("@io_bazel_rules_go//go:def.bzl", "go_test")

go_test(
name = "subintf_test",
srcs = ["subintf_test.go"],
data = [
"testbed.pb.txt",
],
deps = [
"//gnmi/oc",
"//integration_tests/saiutil",
"//internal/attrs",
"//internal/binding",
"@com_github_google_gopacket//:gopacket",
"@com_github_google_gopacket//layers",
"@com_github_openconfig_ondatra//:ondatra",
"@org_golang_google_protobuf//proto",
],
)
132 changes: 132 additions & 0 deletions integration_tests/dataplane/subintf/subintf_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// 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
//
// https://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 subintf

import (
"net"
"testing"

"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/openconfig/ondatra"
"google.golang.org/protobuf/proto"

"github.com/openconfig/lemming/gnmi/oc"
"github.com/openconfig/lemming/integration_tests/saiutil"
"github.com/openconfig/lemming/internal/attrs"
"github.com/openconfig/lemming/internal/binding"
)

var pm = &binding.PortMgr{}

func TestMain(m *testing.M) {
ondatra.RunTests(m, binding.Local(".", binding.WithOverridePortManager(pm)))
}

var (
dutPort1 = attrs.Attributes{
Desc: "dutPort1",
MAC: "10:10:10:10:10:10",
}

dutPort2 = attrs.Attributes{
Desc: "dutPort2",
MAC: "10:10:10:10:10:11",
}
)

func TestVLANSubIntfMatch(t *testing.T) {
s := saiutil.NewSuite()
s.BaseConfig = []saiutil.ConfigOp{
saiutil.ConfigVRF("DEFAULT"),
saiutil.ConfigVRF("NON_DEFAULT"),
saiutil.ConfigRIF("port1", dutPort1.MAC, "DEFAULT"),
saiutil.ConfigRIF("port2", dutPort2.MAC, "DEFAULT"),
saiutil.ConfigVLANSubIntf("port1", 1, "10:10:10:10:10:12", 100, "NON_DEFAULT"),
}
s.Case = []*saiutil.Case{{
Config: []saiutil.ConfigOp{
saiutil.ConfigAft("NON_DEFAULT", &oc.NetworkInstance_Afts{
Ipv4Entry: map[string]*oc.NetworkInstance_Afts_Ipv4Entry{
"192.0.1.1/32": {
NextHopGroup: proto.Uint64(1),
},
},
NextHopGroup: map[uint64]*oc.NetworkInstance_Afts_NextHopGroup{
1: {
NextHop: map[uint64]*oc.NetworkInstance_Afts_NextHopGroup_NextHop{
1: {Weight: proto.Uint64(1), Index: proto.Uint64(1)},
},
},
},
NextHop: map[uint64]*oc.NetworkInstance_Afts_NextHop{
1: {
IpAddress: proto.String("192.0.2.2"),
InterfaceRef: &oc.NetworkInstance_Afts_NextHop_InterfaceRef{
Interface: proto.String("port2"),
Subinterface: proto.Uint32(0),
},
},
},
}),
saiutil.ConfigNeighbor(saiutil.InterfaceRef{Intf: "port2"}, "192.0.2.2", "10:10:10:10:10:13"),
},
In: &saiutil.Packet{
Port: "port1",
Layers: []gopacket.SerializableLayer{
&layers.Ethernet{
SrcMAC: saiutil.MustParseMac(t, "10:10:10:10:10:09"),
DstMAC: saiutil.MustParseMac(t, "10:10:10:10:10:10"),
EthernetType: layers.EthernetTypeDot1Q,
},
&layers.Dot1Q{
Type: layers.EthernetTypeIPv4,
VLANIdentifier: 100,
},
&layers.IPv4{
SrcIP: net.ParseIP("192.0.2.1"),
DstIP: net.ParseIP("192.0.1.1"),
TTL: 10,
Version: 4,
Protocol: layers.IPProtocolNoNextHeader,
},
gopacket.Payload{},
},
},
Out: &saiutil.Packet{
Port: "port2",
Layers: []gopacket.SerializableLayer{
&layers.Ethernet{
SrcMAC: saiutil.MustParseMac(t, "10:10:10:10:10:11"),
DstMAC: saiutil.MustParseMac(t, "10:10:10:10:10:13"),
EthernetType: layers.EthernetTypeIPv4,
},
&layers.IPv4{
SrcIP: net.ParseIP("192.0.2.1"),
DstIP: net.ParseIP("192.0.1.1"),
TTL: 9,
Version: 4,
Protocol: layers.IPProtocolNoNextHeader,
Length: 42,
Checksum: 11927,
IHL: 5,
},
gopacket.Payload{},
},
},
}}

s.Run(t, pm)
}
9 changes: 9 additions & 0 deletions integration_tests/dataplane/subintf/testbed.pb.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
duts {
id: "dut"
ports {
id: "port1"
}
ports {
id: "port2"
}
}
11 changes: 10 additions & 1 deletion integration_tests/saiutil/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")

go_library(
name = "saiutil",
srcs = ["saiutil.go"],
srcs = [
"runner.go",
"saiutil.go",
],
importpath = "github.com/openconfig/lemming/integration_tests/saiutil",
visibility = ["//visibility:public"],
deps = [
"//dataplane/proto/sai",
"//gnmi/oc",
"//internal/binding",
"@com_github_google_go_cmp//cmp",
"@com_github_google_go_cmp//cmp/cmpopts",
"@com_github_google_gopacket//:gopacket",
"@com_github_google_gopacket//layers",
"@com_github_openconfig_ondatra//:ondatra",
"@com_github_openconfig_ondatra//binding",
"@org_golang_google_grpc//:grpc",
Expand Down
109 changes: 109 additions & 0 deletions integration_tests/saiutil/runner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// 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
//
// https://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 saiutil

import (
"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/openconfig/ondatra"

"github.com/openconfig/lemming/internal/binding"
)

// ConfigOp is interface for applying a dataplane configuration option.
type ConfigOp interface {
Config(t testing.TB, s *Suite, dut *ondatra.DUTDevice)
UnConfig(t testing.TB, s *Suite, dut *ondatra.DUTDevice)
}

// NewSuite returns a empty test suite.
func NewSuite() *Suite {
return &Suite{
interfaceMap: map[InterfaceRef]uint64{},
oc2SAINextHop: map[uint64]uint64{},
oc2SAINextHopGrp: map[uint64]uint64{},
oc2SAIVRF: map[string]uint64{},
}
}

// Suite contains a baseconfig and sets of tests dataplane packet tests.
type Suite struct {
BaseConfig []ConfigOp
Case []*Case

interfaceMap map[InterfaceRef]uint64
oc2SAINextHop map[uint64]uint64
oc2SAINextHopGrp map[uint64]uint64
oc2SAIVRF map[string]uint64
}

// Packet is a list of layers and a corresponding port.
type Packet struct {
Layers []gopacket.SerializableLayer
Port string
}

// A Case is single test case containing the config and the input and expect output.
type Case struct {
In, Out *Packet
Config []ConfigOp
}

// Run runs all the cases and reports any diffs.
func (cases *Suite) Run(t testing.TB, pm *binding.PortMgr) {
dut := ondatra.DUT(t, "dut")
for _, cfg := range cases.BaseConfig {
cfg.Config(t, cases, dut)
}
for i, cs := range cases.Case {
t.Log("Running case ", i)
for _, cfg := range cs.Config {
cfg.Config(t, cases, dut)
}
buf := gopacket.NewSerializeBuffer()
if err := gopacket.SerializeLayers(buf, gopacket.SerializeOptions{FixLengths: true}, cs.In.Layers...); err != nil {
t.Fatalf("failed to serialize headers: %v", err)
}
p1 := pm.GetPort(dut.Port(t, cs.In.Port))
p1.RXQueue.Write(buf.Bytes())
p2 := pm.GetPort(dut.Port(t, cs.Out.Port))

select {
case packet := (<-p2.TXQueue.Receive()):
p := gopacket.NewPacket(packet.([]byte), layers.LayerTypeEthernet, gopacket.Default)
t.Logf("Got packet:\n%s", p.Dump())

got := []gopacket.SerializableLayer{}
for _, l := range p.Layers() {
got = append(got, l.(gopacket.SerializableLayer))
}
// Skip the payload when comparing layers.
if d := cmp.Diff(got[0:len(got)-1], cs.Out.Layers[0:len(cs.Out.Layers)-1], cmpopts.IgnoreUnexported(layers.IPv6{}, layers.IPv4{}, layers.UDP{}, layers.MPLS{}),
cmpopts.IgnoreFields(layers.IPv4{}, "BaseLayer"), cmpopts.IgnoreFields(layers.UDP{}, "BaseLayer"), cmpopts.IgnoreFields(layers.Ethernet{}, "BaseLayer"), cmpopts.IgnoreFields(layers.IPv6{}, "BaseLayer")); d != "" {
t.Error(d)
}
case <-time.After(10 * time.Millisecond):
}

for i := len(cs.Config) - 1; i >= 0; i-- {
cs.Config[i].UnConfig(t, cases, dut)
}
}
}
Loading

0 comments on commit f16ef38

Please sign in to comment.