hs-test: added a redis-benchmark test 17/41417/12
authorAdrian Villin <[email protected]>
Thu, 15 Aug 2024 10:53:53 +0000 (12:53 +0200)
committerFlorin Coras <[email protected]>
Tue, 27 Aug 2024 19:05:36 +0000 (19:05 +0000)
- basically a copy of LdpIperf test
- small update of LdpIperf test
- new LDP suite

Type: test

Change-Id: I3f8653288c6fc6dfd6a061315e983c000974d3ff
Signed-off-by: Adrian Villin <[email protected]>
extras/hs-test/docker/Dockerfile.vpp
extras/hs-test/infra/hst_suite.go
extras/hs-test/infra/suite_ldp.go [new file with mode: 0644]
extras/hs-test/infra/utils.go
extras/hs-test/ldp_test.go
extras/hs-test/linux_iperf_test.go
extras/hs-test/topo-containers/2peerVethLdp.yaml [new file with mode: 0644]

index f87ee30..82a1a1a 100644 (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
index 5c124d3..ed8da3f 100644 (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.
diff --git a/extras/hs-test/infra/suite_ldp.go b/extras/hs-test/infra/suite_ldp.go
new file mode 100644 (file)
index 0000000..15b45f7
--- /dev/null
@@ -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))
+               }
+       }
+})
index 9dd2dbd..25d8519 100644 (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
+               }
+       }
+}
index e9e8bba..03636b1 100644 (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{}{}
+}
index f49d9bc..9342e86 100644 (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)
diff --git a/extras/hs-test/topo-containers/2peerVethLdp.yaml b/extras/hs-test/topo-containers/2peerVethLdp.yaml
new file mode 100644 (file)
index 0000000..bd6e63a
--- /dev/null
@@ -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