hs-test: added a redis-benchmark test

- basically a copy of LdpIperf test
- small update of LdpIperf test
- new LDP suite

Type: test

Change-Id: I3f8653288c6fc6dfd6a061315e983c000974d3ff
Signed-off-by: Adrian Villin <avillin@cisco.com>
This commit is contained in:
Adrian Villin
2024-08-15 12:53:53 +02:00
committed by Florin Coras
parent a647a83496
commit d01a63abd4
7 changed files with 405 additions and 136 deletions

View File

@ -5,7 +5,7 @@ FROM ubuntu:${UBUNTU_VERSION}
RUN apt-get update \
&& apt-get install -y openssl libapr1 libnuma1 libsubunit0 \
iproute2 libnl-3-dev libnl-route-3-dev python3 iputils-ping \
vim gdb libunwind-dev \
vim gdb libunwind-dev redis redis-tools iperf3 \
&& rm -rf /var/lib/apt/lists/*
ENV DIR=vpp-data/lib/vpp_plugins

View File

@ -2,7 +2,6 @@ package hst
import (
"bufio"
"errors"
"flag"
"fmt"
"github.com/edwarnicke/exechelper"
@ -607,84 +606,6 @@ func (s *HstSuite) GetPortFromPpid() string {
return port[len(port)-3:] + s.ProcessIndex
}
func (s *HstSuite) StartServerApp(running chan error, done chan struct{}, env []string) {
cmd := exec.Command("iperf3", "-4", "-s", "-p", s.GetPortFromPpid())
if env != nil {
cmd.Env = env
}
s.Log(cmd)
err := cmd.Start()
if err != nil {
msg := fmt.Errorf("failed to start iperf server: %v", err)
running <- msg
return
}
running <- nil
<-done
cmd.Process.Kill()
}
func (s *HstSuite) StartClientApp(ipAddress string, env []string, clnCh chan error, clnRes chan string) {
defer func() {
clnCh <- nil
}()
nTries := 0
for {
cmd := exec.Command("iperf3", "-c", ipAddress, "-u", "-l", "1460", "-b", "10g", "-p", s.GetPortFromPpid())
if env != nil {
cmd.Env = env
}
s.Log(cmd)
o, err := cmd.CombinedOutput()
if err != nil {
if nTries > 5 {
clnCh <- fmt.Errorf("failed to start client app '%s'.\n%s", err, o)
return
}
time.Sleep(1 * time.Second)
nTries++
continue
} else {
clnRes <- fmt.Sprintf("Client output: %s", o)
}
break
}
}
func (s *HstSuite) StartHttpServer(running chan struct{}, done chan struct{}, addressPort, netNs string) {
cmd := newCommand([]string{"./http_server", addressPort, s.Ppid, s.ProcessIndex}, netNs)
err := cmd.Start()
s.Log(cmd)
if err != nil {
s.Log("Failed to start http server: " + fmt.Sprint(err))
return
}
running <- struct{}{}
<-done
cmd.Process.Kill()
}
func (s *HstSuite) StartWget(finished chan error, server_ip, port, query, netNs string) {
defer func() {
finished <- errors.New("wget error")
}()
cmd := newCommand([]string{"wget", "--timeout=10", "--no-proxy", "--tries=5", "-O", "/dev/null", server_ip + ":" + port + "/" + query},
netNs)
s.Log(cmd)
o, err := cmd.CombinedOutput()
if err != nil {
finished <- fmt.Errorf("wget error: '%v\n\n%s'", err, o)
return
} else if !strings.Contains(string(o), "200 OK") {
finished <- fmt.Errorf("wget error: response not 200 OK")
return
}
finished <- nil
}
/*
RunBenchmark creates Gomega's experiment with the passed-in name and samples the passed-in callback repeatedly (samplesNum times),
passing in suite context, experiment and your data.

View File

@ -0,0 +1,203 @@
package hst
import (
"fmt"
"reflect"
"runtime"
"strings"
"time"
. "github.com/onsi/ginkgo/v2"
)
// These correspond to names used in yaml config
const (
ServerLdpInterfaceName = "srv"
ClientLdpInterfaceName = "cln"
)
var ldpTests = map[string][]func(s *LdpSuite){}
var ldpSoloTests = map[string][]func(s *LdpSuite){}
type LdpSuite struct {
HstSuite
}
func RegisterLdpTests(tests ...func(s *LdpSuite)) {
ldpTests[getTestFilename()] = tests
}
func RegisterSoloLdpTests(tests ...func(s *LdpSuite)) {
ldpSoloTests[getTestFilename()] = tests
}
func (s *LdpSuite) SetupSuite() {
time.Sleep(1 * time.Second)
s.HstSuite.SetupSuite()
s.ConfigureNetworkTopology("2peerVeth")
s.LoadContainerTopology("2peerVethLdp")
}
func (s *LdpSuite) SetupTest() {
s.HstSuite.SetupTest()
// Setup test conditions
var sessionConfig Stanza
sessionConfig.
NewStanza("session").
Append("enable").
Append("use-app-socket-api")
if strings.Contains(CurrentSpecReport().LeafNodeText, "InterruptMode") {
sessionConfig.Append("use-private-rx-mqs").Close()
s.Log("**********************INTERRUPT MODE**********************")
} else {
sessionConfig.Close()
}
// ... For server
serverContainer := s.GetContainerByName("server-vpp")
serverVpp, err := serverContainer.newVppInstance(serverContainer.AllocatedCpus, sessionConfig)
s.AssertNotNil(serverVpp, fmt.Sprint(err))
s.SetupServerVpp()
// ... For client
clientContainer := s.GetContainerByName("client-vpp")
clientVpp, err := clientContainer.newVppInstance(clientContainer.AllocatedCpus, sessionConfig)
s.AssertNotNil(clientVpp, fmt.Sprint(err))
s.setupClientVpp()
serverContainer.AddEnvVar("VCL_CONFIG", serverContainer.GetContainerWorkDir()+"/vcl_srv.conf")
clientContainer.AddEnvVar("VCL_CONFIG", clientContainer.GetContainerWorkDir()+"/vcl_cln.conf")
for _, container := range s.StartedContainers {
container.AddEnvVar("LD_PRELOAD", "/usr/lib/libvcl_ldpreload.so")
container.AddEnvVar("LDP_DEBUG", "0")
container.AddEnvVar("VCL_DEBUG", "0")
}
}
func (s *LdpSuite) TearDownTest() {
for _, container := range s.StartedContainers {
delete(container.EnvVars, "LD_PRELOAD")
delete(container.EnvVars, "VCL_CONFIG")
}
s.HstSuite.TearDownTest()
}
func (s *LdpSuite) SetupServerVpp() {
var srvVclConf Stanza
serverContainer := s.GetContainerByName("server-vpp")
serverVclFileName := serverContainer.GetHostWorkDir() + "/vcl_srv.conf"
serverVpp := serverContainer.VppInstance
s.AssertNil(serverVpp.Start())
serverVeth := s.GetInterfaceByName(ServerInterfaceName)
idx, err := serverVpp.createAfPacket(serverVeth)
s.AssertNil(err, fmt.Sprint(err))
s.AssertNotEqual(0, idx)
serverAppSocketApi := fmt.Sprintf("app-socket-api %s/var/run/app_ns_sockets/default",
serverContainer.GetContainerWorkDir())
err = srvVclConf.
NewStanza("vcl").
Append("rx-fifo-size 4000000").
Append("tx-fifo-size 4000000").
Append("app-scope-local").
Append("app-scope-global").
Append("use-mq-eventfd").
Append(serverAppSocketApi).Close().
SaveToFile(serverVclFileName)
s.AssertNil(err, fmt.Sprint(err))
}
func (s *LdpSuite) setupClientVpp() {
var clnVclConf Stanza
clientContainer := s.GetContainerByName("client-vpp")
clientVclFileName := clientContainer.GetHostWorkDir() + "/vcl_cln.conf"
clientVpp := clientContainer.VppInstance
s.AssertNil(clientVpp.Start())
clientVeth := s.GetInterfaceByName(ClientInterfaceName)
idx, err := clientVpp.createAfPacket(clientVeth)
s.AssertNil(err, fmt.Sprint(err))
s.AssertNotEqual(0, idx)
clientAppSocketApi := fmt.Sprintf("app-socket-api %s/var/run/app_ns_sockets/default",
clientContainer.GetContainerWorkDir())
err = clnVclConf.
NewStanza("vcl").
Append("rx-fifo-size 4000000").
Append("tx-fifo-size 4000000").
Append("app-scope-local").
Append("app-scope-global").
Append("use-mq-eventfd").
Append(clientAppSocketApi).Close().
SaveToFile(clientVclFileName)
s.AssertNil(err, fmt.Sprint(err))
}
var _ = Describe("LdpSuite", Ordered, ContinueOnFailure, func() {
var s LdpSuite
BeforeAll(func() {
s.SetupSuite()
})
BeforeEach(func() {
s.SetupTest()
})
AfterAll(func() {
s.TearDownSuite()
})
AfterEach(func() {
s.TearDownTest()
})
// https://onsi.github.io/ginkgo/#dynamically-generating-specs
for filename, tests := range ldpTests {
for _, test := range tests {
test := test
pc := reflect.ValueOf(test).Pointer()
funcValue := runtime.FuncForPC(pc)
testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
It(testName, func(ctx SpecContext) {
s.Log(testName + ": BEGIN")
test(&s)
}, SpecTimeout(SuiteTimeout))
}
}
})
var _ = Describe("LdpSuiteSolo", Ordered, ContinueOnFailure, Serial, func() {
var s LdpSuite
BeforeAll(func() {
s.SetupSuite()
})
BeforeEach(func() {
s.SetupTest()
})
AfterAll(func() {
s.TearDownSuite()
})
AfterEach(func() {
s.TearDownTest()
})
// https://onsi.github.io/ginkgo/#dynamically-generating-specs
for filename, tests := range ldpSoloTests {
for _, test := range tests {
test := test
pc := reflect.ValueOf(test).Pointer()
funcValue := runtime.FuncForPC(pc)
testName := filename + "/" + strings.Split(funcValue.Name(), ".")[2]
It(testName, Label("SOLO"), func(ctx SpecContext) {
s.Log(testName + ": BEGIN")
test(&s)
}, SpecTimeout(SuiteTimeout))
}
}
})

View File

@ -1,6 +1,7 @@
package hst
import (
"errors"
"fmt"
"io"
"net"
@ -183,3 +184,133 @@ func (s *HstSuite) CollectEnvoyLogs(containerName string) {
s.Log(fmt.Sprint(err))
}
}
func (s *HstSuite) StartIperfServerApp(running chan error, done chan struct{}, env []string) {
cmd := exec.Command("iperf3", "-4", "-s", "-p", s.GetPortFromPpid())
if env != nil {
cmd.Env = env
}
s.Log(cmd)
err := cmd.Start()
if err != nil {
msg := fmt.Errorf("failed to start iperf server: %v", err)
running <- msg
return
}
running <- nil
<-done
cmd.Process.Kill()
}
func (s *HstSuite) StartIperfClientApp(ipAddress string, env []string, clnCh chan error, clnRes chan string) {
defer func() {
clnCh <- nil
}()
nTries := 0
for {
cmd := exec.Command("iperf3", "-c", ipAddress, "-u", "-l", "1460", "-b", "10g", "-p", s.GetPortFromPpid())
if env != nil {
cmd.Env = env
}
s.Log(cmd)
o, err := cmd.CombinedOutput()
if err != nil {
if nTries > 5 {
clnRes <- ""
clnCh <- fmt.Errorf("failed to start client app '%s'.\n%s", err, o)
return
}
time.Sleep(1 * time.Second)
nTries++
continue
} else {
clnRes <- fmt.Sprintf("Client output: %s", o)
}
break
}
}
func (s *HstSuite) StartHttpServer(running chan struct{}, done chan struct{}, addressPort, netNs string) {
cmd := newCommand([]string{"./http_server", addressPort, s.Ppid, s.ProcessIndex}, netNs)
err := cmd.Start()
s.Log(cmd)
if err != nil {
s.Log("Failed to start http server: " + fmt.Sprint(err))
return
}
running <- struct{}{}
<-done
cmd.Process.Kill()
}
func (s *HstSuite) StartWget(finished chan error, server_ip, port, query, netNs string) {
defer func() {
finished <- errors.New("wget error")
}()
cmd := newCommand([]string{"wget", "--timeout=10", "--no-proxy", "--tries=5", "-O", "/dev/null", server_ip + ":" + port + "/" + query},
netNs)
s.Log(cmd)
o, err := cmd.CombinedOutput()
if err != nil {
finished <- fmt.Errorf("wget error: '%v\n\n%s'", err, o)
return
} else if !strings.Contains(string(o), "200 OK") {
finished <- fmt.Errorf("wget error: response not 200 OK")
return
}
finished <- nil
}
// Start a server app. 'processName' is used to check whether the app started correctly.
func (s *HstSuite) StartServerApp(c *Container, processName string, cmd string,
running chan error, done chan struct{}) {
s.Log("starting server")
c.ExecServer(cmd)
cmd2 := exec.Command("docker", "exec", c.Name, "pidof", processName)
err := cmd2.Run()
if err != nil {
msg := fmt.Errorf("failed to start server app: %v", err)
running <- msg
<-done
return
}
running <- nil
<-done
}
func (s *HstSuite) StartClientApp(c *Container, cmd string,
clnCh chan error, clnRes chan string) {
defer func() {
close(clnCh)
close(clnRes)
}()
s.Log("starting client app, please wait")
nTries := 0
for {
// exec.Cmd can only be used once, which is why it's in the loop
cmd2 := exec.Command("/bin/sh", "-c", "docker exec "+c.getEnvVarsAsCliOption()+" "+
c.Name+" "+cmd)
s.Log(cmd2)
o, err := cmd2.CombinedOutput()
if err != nil {
s.Log(err)
if nTries > 5 {
clnRes <- ""
clnCh <- fmt.Errorf("failed to start client app '%s'", err)
s.AssertNil(err, fmt.Sprint(err))
break
}
time.Sleep(1 * time.Second)
nTries++
} else {
clnRes <- fmt.Sprintf("Client output: %s", o)
break
}
}
}

View File

@ -2,86 +2,42 @@ package main
import (
"fmt"
"os"
. "fd.io/hs-test/infra"
. "github.com/onsi/ginkgo/v2"
)
func init() {
RegisterVethTests(LDPreloadIperfVppTest, LDPreloadIperfVppInterruptModeTest)
RegisterLdpTests(LDPreloadIperfVppTest, LDPreloadIperfVppInterruptModeTest, RedisBenchmarkTest)
}
func LDPreloadIperfVppInterruptModeTest(s *VethsSuite) {
func LDPreloadIperfVppInterruptModeTest(s *LdpSuite) {
LDPreloadIperfVppTest(s)
}
func LDPreloadIperfVppTest(s *VethsSuite) {
var clnVclConf, srvVclConf Stanza
var ldpreload string
serverContainer := s.GetContainerByName("server-vpp")
serverVclFileName := serverContainer.GetHostWorkDir() + "/vcl_srv.conf"
func LDPreloadIperfVppTest(s *LdpSuite) {
clientContainer := s.GetContainerByName("client-vpp")
clientVclFileName := clientContainer.GetHostWorkDir() + "/vcl_cln.conf"
if *IsDebugBuild {
ldpreload = "LD_PRELOAD=../../build-root/build-vpp_debug-native/vpp/lib/x86_64-linux-gnu/libvcl_ldpreload.so"
} else {
ldpreload = "LD_PRELOAD=../../build-root/build-vpp-native/vpp/lib/x86_64-linux-gnu/libvcl_ldpreload.so"
}
serverContainer := s.GetContainerByName("server-vpp")
stopServerCh := make(chan struct{}, 1)
srvCh := make(chan error, 1)
clnCh := make(chan error)
clnRes := make(chan string, 1)
s.Log("starting VPPs")
clientAppSocketApi := fmt.Sprintf("app-socket-api %s/var/run/app_ns_sockets/default",
clientContainer.GetHostWorkDir())
err := clnVclConf.
NewStanza("vcl").
Append("rx-fifo-size 4000000").
Append("tx-fifo-size 4000000").
Append("app-scope-local").
Append("app-scope-global").
Append("use-mq-eventfd").
Append(clientAppSocketApi).Close().
SaveToFile(clientVclFileName)
s.AssertNil(err, fmt.Sprint(err))
serverAppSocketApi := fmt.Sprintf("app-socket-api %s/var/run/app_ns_sockets/default",
serverContainer.GetHostWorkDir())
err = srvVclConf.
NewStanza("vcl").
Append("rx-fifo-size 4000000").
Append("tx-fifo-size 4000000").
Append("app-scope-local").
Append("app-scope-global").
Append("use-mq-eventfd").
Append(serverAppSocketApi).Close().
SaveToFile(serverVclFileName)
s.AssertNil(err, fmt.Sprint(err))
s.Log("attaching server to vpp")
srvEnv := append(os.Environ(), ldpreload, "VCL_CONFIG="+serverVclFileName)
go func() {
defer GinkgoRecover()
s.StartServerApp(srvCh, stopServerCh, srvEnv)
cmd := "iperf3 -4 -s -p " + s.GetPortFromPpid()
s.StartServerApp(serverContainer, "iperf3", cmd, srvCh, stopServerCh)
}()
err = <-srvCh
err := <-srvCh
s.AssertNil(err, fmt.Sprint(err))
s.Log("attaching client to vpp")
var clnRes = make(chan string, 1)
clnEnv := append(os.Environ(), ldpreload, "VCL_CONFIG="+clientVclFileName)
serverVethAddress := s.GetInterfaceByName(ServerInterfaceName).Ip4AddressString()
go func() {
defer GinkgoRecover()
s.StartClientApp(serverVethAddress, clnEnv, clnCh, clnRes)
cmd := "iperf3 -c " + serverVethAddress + " -u -l 1460 -b 10g -p " + s.GetPortFromPpid()
s.StartClientApp(clientContainer, cmd, clnCh, clnRes)
}()
s.Log(<-clnRes)
@ -92,3 +48,43 @@ func LDPreloadIperfVppTest(s *VethsSuite) {
// stop server
stopServerCh <- struct{}{}
}
func RedisBenchmarkTest(s *LdpSuite) {
s.SkipIfMultiWorker()
serverContainer := s.GetContainerByName("server-vpp")
clientContainer := s.GetContainerByName("client-vpp")
serverVethAddress := s.GetInterfaceByName(ServerInterfaceName).Ip4AddressString()
runningSrv := make(chan error)
doneSrv := make(chan struct{})
clnCh := make(chan error)
clnRes := make(chan string, 1)
go func() {
defer GinkgoRecover()
cmd := "redis-server --daemonize yes --protected-mode no --bind " + serverVethAddress
s.StartServerApp(serverContainer, "redis-server", cmd, runningSrv, doneSrv)
}()
err := <-runningSrv
s.AssertNil(err)
go func() {
defer GinkgoRecover()
var cmd string
if *NConfiguredCpus == 1 {
cmd = "redis-benchmark --threads 1 -h " + serverVethAddress
} else {
cmd = "redis-benchmark --threads " + fmt.Sprint(*NConfiguredCpus) + "-h " + serverVethAddress
}
s.StartClientApp(clientContainer, cmd, clnCh, clnRes)
}()
s.Log(<-clnRes)
// wait for client's result
err = <-clnCh
s.AssertNil(err, fmt.Sprint(err))
// stop server
doneSrv <- struct{}{}
}

View File

@ -21,7 +21,7 @@ func LinuxIperfTest(s *TapSuite) {
go func() {
defer GinkgoRecover()
s.StartServerApp(srvCh, stopServerCh, nil)
s.StartIperfServerApp(srvCh, stopServerCh, nil)
}()
err := <-srvCh
s.AssertNil(err, fmt.Sprint(err))
@ -30,7 +30,7 @@ func LinuxIperfTest(s *TapSuite) {
ipAddress := s.GetInterfaceByName(TapInterfaceName).Ip4AddressString()
go func() {
defer GinkgoRecover()
s.StartClientApp(ipAddress, nil, clnCh, clnRes)
s.StartIperfClientApp(ipAddress, nil, clnCh, clnRes)
}()
s.Log("client running")
s.Log(<-clnRes)

View File

@ -0,0 +1,18 @@
---
volumes:
- volume: &server-vol
host-dir: "$HST_VOLUME_DIR/server-share"
container-dir: "/tmp/server-share"
is-default-work-dir: true
- volume: &client-vol
host-dir: "$HST_VOLUME_DIR/client-share"
container-dir: "/tmp/client-share"
is-default-work-dir: true
containers:
- name: "server-vpp"
volumes:
- <<: *server-vol
- name: "client-vpp"
volumes:
- <<: *client-vol