Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions sdn_tests/pins_ondatra/infrastructure/testhelper/gnmi.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package testhelper

import (
"context"
"os"
"testing"

closer "github.com/openconfig/gocloser"
"github.com/openconfig/ondatra"
"github.com/openconfig/ondatra/gnmi"
"github.com/openconfig/ondatra/gnmi/oc/system"
Expand Down Expand Up @@ -88,3 +90,67 @@ func CreateSubscribeRequest(params SubscribeRequestParams) (*gpb.SubscribeReques
},
}, nil
}

// GNMIAble returns whether the gNMI server on the specified device is reachable
// or not.
func GNMIAble(t *testing.T, d *ondatra.DUTDevice) error {
// Since the Ondatra Get() API panics in case of failure, we need to use
// raw gNMI client to test reachability with the gNMI server on the switch.
// The gNMI server reachability is checked by fetching the system boot-time
// path from the switch.
params := SubscribeRequestParams{
Target: testhelperDUTNameGet(d),
Paths: []ygnmi.PathStruct{gnmiSystemBootTimePath()},
Mode: gpb.SubscriptionList_ONCE,
}
subscribeRequest, err := CreateSubscribeRequest(params)
if err != nil {
return errors.Wrapf(err, "failed to create SubscribeRequest")
}

subscribeClient, err := gnmiSubscribeClientGet(t, d, context.Background())
if err != nil {
return errors.Wrapf(err, "unable to get subscribe client")
}
defer closer.CloseAndLog(subscribeClient.CloseSend, "error closing gNMI send stream")

if err := subscribeClient.Send(subscribeRequest); err != nil {
return errors.Wrapf(err, "failed to send gNMI subscribe request")
}

if _, err := subscribeClient.Recv(); err != nil {
return errors.Wrapf(err, "subscribe client Recv() failed")
}

return nil
}

// ConfigGet returns a full config for the given DUT.
func (d GNMIConfigDUT) ConfigGet() ([]byte, error) {
return os.ReadFile("ondatra/data/config.json")
}

// ConfigPush pushes the given config onto the DUT. If nil is passed in for config,
// this function will use ConfigGet() to get a full config for the DUT.
func ConfigPush(t *testing.T, dut *ondatra.DUTDevice, config *[]byte) error {
if dut == nil {
return errors.New("nil DUT passed into ConfigPush()")
}
if config == nil {
getConfig, err := GNMIConfigDUT{dut}.ConfigGet()
if err != nil {
return err
}
config = &getConfig
}
setRequest := &gpb.SetRequest{
Prefix: &gpb.Path{Origin: "openconfig", Target: testhelperDUTNameGet(dut)},
Replace: []*gpb.Update{{
Path: &gpb.Path{},
Val: &gpb.TypedValue{Value: &gpb.TypedValue_JsonIetfVal{JsonIetfVal: *config}},
}},
}
t.Logf("Pushing config on %v: %v", testhelperDUTNameGet(dut), setRequest)
_, err := gnmiSet(t, dut, setRequest)
return err
}
70 changes: 70 additions & 0 deletions sdn_tests/pins_ondatra/infrastructure/testhelper/gnoi.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import (
"testing"
"time"

log "github.com/golang/glog"
"github.com/openconfig/ondatra"
"github.com/openconfig/ondatra/gnmi"
"github.com/pkg/errors"

healthzpb "github.com/openconfig/gnoi/healthz"
syspb "github.com/openconfig/gnoi/system"
Expand Down Expand Up @@ -83,6 +85,74 @@ func (p *RebootParams) measureLatency() bool {
return p.waitTime > 0 && p.lmTitle != ""
}

// Reboot sends a RebootRequest message to the switch. It waits for a specified
// amount of time for the switch reboot to be successful. A switch reboot is
// considered to be successful if the gNOI server is up and the boot time is
// after the reboot request time.
func Reboot(t *testing.T, d *ondatra.DUTDevice, params *RebootParams) error {
if params.waitTime < params.checkInterval {
return errors.Errorf("wait time:%v cannot be less than check interval:%v", params.waitTime, params.checkInterval)
}

var req *syspb.RebootRequest
switch v := params.request.(type) {
case syspb.RebootMethod:
// User only specified the reboot type. Construct reboot request.
req = &syspb.RebootRequest{
Method: v,
Message: "Reboot",
}
case *syspb.RebootRequest:
// Use the specified reboot request.
req = v
default:
return errors.New("invalid reboot request (valid parameters are RebootRequest protobuf and RebootMethod)")
}

log.Infof("Rebooting %v switch", testhelperDUTNameGet(d))
timeBeforeReboot := time.Now().UnixNano()
systemClient := gnoiSystemClientGet(t, d)

if _, err := systemClient.Reboot(context.Background(), req); err != nil {
return errors.Wrapf(err, "reboot RPC failed")
}

if params.waitTime == 0 {
// User did not request a wait time which implies that the API did not verify whether
// the switch has rebooted or not. Therefore, do not return an error in this case.
return nil
}

log.Infof("Polling gNOI server reachability in %v intervals for max duration of %v", params.checkInterval, params.waitTime)
for timeout := time.Now().Add(params.waitTime); time.Now().Before(timeout); {
// The switch backend might not have processed the request or might take
// sometime to execute the request. So wait for check interval time and
// later verify that the switch rebooted within the specified wait time.
time.Sleep(params.checkInterval)
doneTime := time.Now()
timeElapsed := (doneTime.UnixNano() - timeBeforeReboot) / int64(time.Second)

if err := GNOIAble(t, d); err != nil {
log.Infof("gNOI server not up after %v seconds", timeElapsed)
continue
}
log.Infof("gNOI server up after %v seconds", timeElapsed)

// An extra check to ensure that the system has rebooted.
if bootTime := gnmiSystemBootTimeGet(t, d); bootTime < uint64(timeBeforeReboot) {
log.Infof("Switch has not rebooted after %v seconds", timeElapsed)
continue
}

log.Infof("Switch rebooted after %v seconds", timeElapsed)
return nil
}

err := errors.Errorf("failed to reboot %v", testhelperDUTNameGet(d))

return err
}

// GNOIAble returns whether the gNOI server on the specified device is reachable
// or not.
func GNOIAble(t *testing.T, d *ondatra.DUTDevice) error {
Expand Down
87 changes: 87 additions & 0 deletions sdn_tests/pins_ondatra/infrastructure/testhelper/lacp.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package testhelper

import (
"testing"
"time"

log "github.com/golang/glog"
"github.com/openconfig/ondatra"
"github.com/openconfig/ondatra/gnmi/oc"
"github.com/pkg/errors"
)

// PeerPorts holds the name of 2 Ethernet interfaces. These interfaces will be on separate machines,
Expand All @@ -11,6 +17,59 @@ type PeerPorts struct {
Peer string
}

// PeerPortsBySpeed iterates through all the available Ethernet ports on a host device, and
// determines if they have a matching port on the peer device. All host ports with a valid peer will
// be grouped together based on their speed.
func PeerPortsBySpeed(t *testing.T, host *ondatra.DUTDevice, peer *ondatra.DUTDevice) map[oc.E_IfEthernet_ETHERNET_SPEED][]PeerPorts {
peerPortsBySpeed := make(map[oc.E_IfEthernet_ETHERNET_SPEED][]PeerPorts)

for _, hostPort := range testhelperDUTPortsGet(host) {
peerPort := testhelperDUTPortGet(t, peer, testhelperOndatraPortIDGet(hostPort))

// Verify the host port is UP. Otherwise, LACPDU packets will never be transmitted.
if got, want := testhelperIntfOperStatusGet(t, host, testhelperOndatraPortNameGet(hostPort)), oc.Interface_OperStatus_UP; got != want {
log.Warningf("Port %v:%v oper state will not work for LACP testing: want=%v got=%v", testhelperDUTNameGet(host), testhelperOndatraPortNameGet(hostPort), want, got)
continue
}

// Verify we are not already part of an existing PortChannel since one port cannot belong to
// multiple PortChannels.
if got, want := testhelperConfigIntfAggregateIDGet(t, host, testhelperOndatraPortNameGet(hostPort)), ""; got != want {
log.Warningf("Port %v:%v cannot be used since it is already assigned to a PortChannel: want=%v got=%v", testhelperDUTNameGet(host), testhelperOndatraPortNameGet(hostPort), want, got)
continue
}

// Check that the host port has a valid peer port.
if peerPort == nil {
log.Warningf("Port %v:%v does not have a peer on %v.", testhelperDUTNameGet(host), testhelperOndatraPortNameGet(hostPort), testhelperDUTNameGet(peer))
continue
}

log.Infof("Found peer ports: %v:%v and %v:%v", testhelperDUTNameGet(host), testhelperOndatraPortNameGet(hostPort), testhelperDUTNameGet(peer), testhelperOndatraPortNameGet(peerPort))
portSpeed := testhelperConfigPortSpeedGet(t, host, testhelperOndatraPortNameGet(hostPort))
peerPortsBySpeed[portSpeed] = append(peerPortsBySpeed[portSpeed], PeerPorts{testhelperOndatraPortNameGet(hostPort), testhelperOndatraPortNameGet(peerPort)})
}

return peerPortsBySpeed
}

// PeerPortGroupWithNumMembers returns a list of PeerPorts of size `numMembers`.
func PeerPortGroupWithNumMembers(t *testing.T, host *ondatra.DUTDevice, peer *ondatra.DUTDevice, numMembers int) ([]PeerPorts, error) {
// GPINs requires that all members of a LACP LAG have the same speed. So we first group all the
// ports based on their configured speed.
peerPortsBySpeed := PeerPortsBySpeed(t, host, peer)

// Then we search through the port speed gropus for one that has enough members to match the users
// requested amount.
for _, ports := range peerPortsBySpeed {
if len(ports) >= numMembers {
// Only return enough members to match the users request.
return ports[0:numMembers], nil
}
}
return nil, errors.Errorf("cannot make group of %v member ports with the same speed from %v.", numMembers, peerPortsBySpeed)
}

// GeneratePortChannelInterface will return a minimal PortChannel interface that tests can extend as needed.
func GeneratePortChannelInterface(portChannelName string) oc.Interface {
enabled := true
Expand Down Expand Up @@ -40,3 +99,31 @@ func GenerateLACPInterface(pcName string) oc.Lacp_Interface {
LacpMode: oc.Lacp_LacpActivityType_ACTIVE,
}
}

// RemovePortChannelFromDevice will cleanup all configs relating to a PortChannel on a given switch.
// It will also verify that the state has been updated before returning. If the state fails to
// converge then an error will be returned.
func RemovePortChannelFromDevice(t *testing.T, timeout time.Duration, dut *ondatra.DUTDevice, portChannelName string) error {
// We only need to delete the PortChannel interface and gNMI should take care of all the other
// configs relating to the PortChannel.
testhelperIntfDelete(t, dut, portChannelName)

stopTime := time.Now().Add(timeout)
for time.Now().Before(stopTime) {
if !testhelperIntfLookup(t, dut, portChannelName).IsPresent() {
return nil
}
time.Sleep(time.Second)
}

return errors.Errorf("interface still exists after %v", timeout)
}

// AssignPortsToAggregateID will assign the list of ports to the given aggregate ID on a device. The
// aggregate ID should correspond to an existing PortChannel interface or this call will fail.
func AssignPortsToAggregateID(t *testing.T, dut *ondatra.DUTDevice, portChannelName string, portNames ...string) {
for _, portName := range portNames {
log.Infof("Assigning %v:%v to %v", testhelperDUTNameGet(dut), portName, portChannelName)
testhelperIntfAggregateIDReplace(t, dut, portName, portChannelName)
}
}
2 changes: 1 addition & 1 deletion sdn_tests/pins_ondatra/infrastructure/testhelper/p4rt.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func (p *P4RTClient) P4Info() (*p4infopb.P4Info, error) {

// Read P4Info from file.
p4Info = &p4infopb.P4Info{}
data, err := os.ReadFile("infrastructure/data/p4rtconfig.prototext")
data, err := os.ReadFile("ondatra/data/p4rtconfig.prototext")
if err != nil {
return nil, err
}
Expand Down
33 changes: 33 additions & 0 deletions sdn_tests/pins_ondatra/tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,22 @@ ondatra_test(
],
)

go_library(
name = "gnmi_stress_helper",
testonly = 1,
srcs = ["gnmi_helper.go"],
importpath = "github.com/sonic-net/sonic-mgmt/sdn_tests/pins_ondatra/tests/gnmi_stress_helper",
deps = [
"//infrastructure/testhelper",
"@com_github_openconfig_gnmi//proto/gnmi:gnmi_go_proto",
"@com_github_openconfig_gnmi//value",
"@com_github_openconfig_ondatra//:go_default_library",
"@com_github_openconfig_ygot//ygot",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_protobuf//encoding/prototext",
],
)

# Gnoi File tests
ondatra_test(
name = "gnoi_file_test",
Expand Down Expand Up @@ -308,3 +324,20 @@ ondatra_test(
"@com_github_pkg_errors//:errors",
],
)

# gNMI Features: Stress Test
ondatra_test(
name = "z_gnmi_stress_test",
srcs = ["gnmi_stress_test.go"],
run_timeout = "120m",
deps = [
":gnmi_stress_helper",
"//infrastructure/binding:pinsbind",
"//infrastructure/testhelper",
"@com_github_openconfig_gnmi//proto/gnmi:gnmi_go_proto",
"@com_github_openconfig_ondatra//:go_default_library",
"@com_github_openconfig_ondatra//gnmi",
"@com_github_openconfig_ygot//ygot",
"@org_golang_google_grpc//:go_default_library",
],
)
7 changes: 6 additions & 1 deletion sdn_tests/pins_ondatra/tests/gnmi_get_modes_test.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
package gnmi_get_modes_test

import (
"fmt"
"context"
"encoding/json"
"strings"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/openconfig/gnmi/value"
"github.com/openconfig/ondatra"
"github.com/openconfig/ygot/ygot"
"github.com/openconfig/ondatra/gnmi"
"github.com/sonic-net/sonic-mgmt/sdn_tests/pins_ondatra/infrastructure/binding/pinsbind"
"github.com/sonic-net/sonic-mgmt/sdn_tests/pins_ondatra/infrastructure/testhelper/testhelper"
"google.golang.org/grpc"
"google.golang.org/protobuf/testing/protocmp"
"google.golang.org/protobuf/encoding/prototext"

gpb "github.com/openconfig/gnmi/proto/gnmi"
)
Expand Down Expand Up @@ -761,7 +766,7 @@ func createAndValidateLeafRequest(t *testing.T, dut *ondatra.DUTDevice, paths []
// Test for gNMI GET consistency for specified data type with ALL type at leaf level.
func (c getDataTypeTest) consistencyCheckLeafLevel(t *testing.T) {
t.Helper()
defer esthelper.NewTearDownOptions(t).WithID(c.uuid).Teardown(t)
defer testhelper.NewTearDownOptions(t).WithID(c.uuid).Teardown(t)
dut := ondatra.DUT(t, "DUT")

sPath, err := ygot.StringToStructuredPath(c.reqPath)
Expand Down
6 changes: 0 additions & 6 deletions sdn_tests/pins_ondatra/tests/gnmi_set_get_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,13 @@ import (
"testing"
"time"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/openconfig/ondatra"
"github.com/openconfig/ondatra/gnmi"
"github.com/openconfig/ondatra/gnmi/oc"
"github.com/openconfig/testt"
"github.com/openconfig/ygnmi/ygnmi"
"github.com/openconfig/ygot/ygot"
"github.com/sonic-net/sonic-mgmt/sdn_tests/pins_ondatra/infrastructure/binding/pinsbind"
"github.com/sonic-net/sonic-mgmt/sdn_tests/pins_ondatra/infrastructure/testhelper/testhelper"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/protobuf/encoding/prototext"

gpb "github.com/openconfig/gnmi/proto/gnmi"
)
Expand Down
7 changes: 4 additions & 3 deletions sdn_tests/pins_ondatra/tests/lacp_timeout_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/openconfig/ondatra/gnmi"
"github.com/openconfig/ondatra/gnmi/oc"
"github.com/sonic-net/sonic-mgmt/sdn_tests/pins_ondatra/infrastructure/binding/pinsbind"
"github.com/sonic-net/sonic-mgmt/sdn_tests/pins_ondatra/infrastructure//testhelper/testhelper"
"github.com/sonic-net/sonic-mgmt/sdn_tests/pins_ondatra/infrastructure/testhelper/testhelper"
"github.com/pkg/errors"
)

Expand Down Expand Up @@ -131,9 +131,10 @@ func verifyLACPTimeout(t *testing.T, hostActivity oc.E_Lacp_LacpActivityType, ho

// Choose a random port to test, and get the LACPDU count.
peerportslen := len(peerPorts)
max := big.NewInt(peerportslen)
max := big.NewInt(int64(peerportslen))
randomIndex, _ := rand.Int(rand.Reader, max)
port := randomIndex
port_64 := randomIndex.Int64()
port := int(port_64)
hostBefore := gnmi.Get(t, host, gnmi.OC().Lacp().Interface(portChannel).Member(peerPorts[port].Host).Counters().State())
peerBefore := gnmi.Get(t, peer, gnmi.OC().Lacp().Interface(portChannel).Member(peerPorts[port].Peer).Counters().State())

Expand Down
Loading