hs-test: improved iperf testing 03/41903/5
authorAdrian Villin <[email protected]>
Fri, 6 Dec 2024 15:00:25 +0000 (16:00 +0100)
committerFlorin Coras <[email protected]>
Tue, 10 Dec 2024 05:03:54 +0000 (05:03 +0000)
- set iperf to json output
- added iperf json parsing function
- we can now check if iperf transferred more than X megabytes
  (other asserts can be added)

Type: test

Change-Id: I560104a153456b46f22a1affee4301018063b99d
Signed-off-by: Adrian Villin <[email protected]>
extras/hs-test/infra/hst_suite.go
extras/hs-test/infra/utils.go
extras/hs-test/iperf_linux_test.go
extras/hs-test/ldp_test.go

index 0513e86..9a3a3c9 100644 (file)
@@ -364,6 +364,15 @@ func (s *HstSuite) AssertChannelClosed(timeout time.Duration, channel chan error
        EventuallyWithOffset(2, channel).WithTimeout(timeout).Should(BeClosed())
 }
 
+// Pass the parsed result struct and the minimum amount of data transferred in MB
+func (s *HstSuite) AssertIperfMinTransfer(result IPerfResult, minTransferred int) {
+       if result.Start.Details.Protocol == "TCP" {
+               s.AssertGreaterThan(result.End.TcpReceived.MBytes, minTransferred)
+       } else {
+               s.AssertGreaterThan(result.End.Udp.MBytes, minTransferred)
+       }
+}
+
 func (s *HstSuite) CreateLogger() {
        suiteName := s.GetCurrentSuiteName()
        var err error
index 16a6a8c..a7c2f13 100644 (file)
@@ -2,6 +2,7 @@ package hst
 
 import (
        "crypto/tls"
+       "encoding/json"
        "errors"
        "fmt"
        "io"
@@ -36,6 +37,43 @@ type JsonResult struct {
        StdOutput string
 }
 
+type IPerfResult struct {
+       Start struct {
+               Timestamp struct {
+                       Time string `json:"time"`
+               } `json:"timestamp"`
+               Connected []struct {
+                       Socket     int    `json:"socket"`
+                       LocalHost  string `json:"local_host"`
+                       LocalPort  int    `json:"local_port"`
+                       RemoteHost string `json:"remote_host"`
+                       RemotePort int    `json:"remote_port"`
+               } `json:"connected"`
+               Version string `json:"version"`
+               Details struct {
+                       Protocol string `json:"protocol"`
+               } `json:"test_start"`
+       } `json:"start"`
+       End struct {
+               TcpSent *struct {
+                       MbitsPerSecond float64 `json:"bits_per_second"`
+                       MBytes         float64 `json:"bytes"`
+               } `json:"sum_sent,omitempty"`
+               TcpReceived *struct {
+                       MbitsPerSecond float64 `json:"bits_per_second"`
+                       MBytes         float64 `json:"bytes"`
+               } `json:"sum_received,omitempty"`
+               Udp *struct {
+                       MbitsPerSecond float64 `json:"bits_per_second"`
+                       JitterMs       float64 `json:"jitter_ms,omitempty"`
+                       LostPackets    int     `json:"lost_packets,omitempty"`
+                       Packets        int     `json:"packets,omitempty"`
+                       LostPercent    float64 `json:"lost_percent,omitempty"`
+                       MBytes         float64 `json:"bytes"`
+               } `json:"sum,omitempty"`
+       } `json:"end"`
+}
+
 func AssertFileSize(f1, f2 string) error {
        fi1, err := os.Stat(f1)
        if err != nil {
@@ -286,7 +324,7 @@ func (s *HstSuite) StartServerApp(c *Container, processName string, cmd string,
 }
 
 func (s *HstSuite) StartClientApp(c *Container, cmd string,
-       clnCh chan error, clnRes chan string) {
+       clnCh chan error, clnRes chan []byte) {
        defer func() {
                close(clnCh)
                close(clnRes)
@@ -301,10 +339,71 @@ func (s *HstSuite) StartClientApp(c *Container, cmd string,
        if err != nil {
                s.Log(err)
                s.Log(string(o))
-               clnRes <- ""
+               clnRes <- nil
                clnCh <- fmt.Errorf("failed to start client app '%s'", err)
                s.AssertNil(err, fmt.Sprint(err))
        } else {
-               clnRes <- fmt.Sprintf("Client output: %s", o)
+               clnRes <- o
+       }
+}
+
+func (s *HstSuite) ParseJsonIperfOutput(jsonResult []byte) IPerfResult {
+       var result IPerfResult
+       // remove iperf warning line if present
+       if strings.Contains(string(jsonResult), "warning") {
+               index := strings.Index(string(jsonResult), "\n")
+               jsonResult = jsonResult[index+1:]
+       }
+
+       err := json.Unmarshal(jsonResult, &result)
+       s.AssertNil(err)
+
+       if result.Start.Details.Protocol == "TCP" {
+               result.End.TcpSent.MbitsPerSecond = result.End.TcpSent.MbitsPerSecond / 1000000
+               result.End.TcpSent.MBytes = result.End.TcpSent.MBytes / 1000000
+               result.End.TcpReceived.MbitsPerSecond = result.End.TcpReceived.MbitsPerSecond / 1000000
+               result.End.TcpReceived.MBytes = result.End.TcpReceived.MBytes / 1000000
+       } else {
+               result.End.Udp.MBytes = result.End.Udp.MBytes / 1000000
+               result.End.Udp.MbitsPerSecond = result.End.Udp.MbitsPerSecond / 1000000
+       }
+
+       return result
+}
+
+func (s *HstSuite) LogJsonIperfOutput(result IPerfResult) {
+       s.Log("\n*******************************************\n"+
+               "%s\n"+
+               "[%s] %s:%d connected to %s:%d\n"+
+               "Started:  %s\n",
+               result.Start.Version,
+               result.Start.Details.Protocol,
+               result.Start.Connected[0].LocalHost, result.Start.Connected[0].LocalPort,
+               result.Start.Connected[0].RemoteHost, result.Start.Connected[0].RemotePort,
+               result.Start.Timestamp.Time)
+
+       if result.Start.Details.Protocol == "TCP" {
+               s.Log("Transfer (sent):     %.2f MBytes\n"+
+                       "Bitrate  (sent):     %.2f Mbits/sec\n"+
+                       "Transfer (received): %.2f MBytes\n"+
+                       "Bitrate  (received): %.2f Mbits/sec",
+                       result.End.TcpSent.MBytes,
+                       result.End.TcpSent.MbitsPerSecond,
+                       result.End.TcpReceived.MBytes,
+                       result.End.TcpReceived.MbitsPerSecond)
+       } else {
+               s.Log("Transfer:     %.2f MBytes\n"+
+                       "Bitrate:      %.2f Mbits/sec\n"+
+                       "Jitter:       %.3f ms\n"+
+                       "Packets:      %d\n"+
+                       "Packets lost: %d\n"+
+                       "Percent lost: %.2f%%",
+                       result.End.Udp.MBytes,
+                       result.End.Udp.MbitsPerSecond,
+                       result.End.Udp.JitterMs,
+                       result.End.Udp.Packets,
+                       result.End.Udp.LostPackets,
+                       result.End.Udp.LostPercent)
        }
+       s.Log("*******************************************\n")
 }
index ff1467c..0a59f6a 100644 (file)
@@ -21,7 +21,7 @@ func IperfUdpLinuxTest(s *IperfSuite) {
        clnCh := make(chan error)
        stopServerCh := make(chan struct{})
        srvCh := make(chan error, 1)
-       clnRes := make(chan string, 1)
+       clnRes := make(chan []byte, 1)
 
        defer func() {
                stopServerCh <- struct{}{}
@@ -39,9 +39,12 @@ func IperfUdpLinuxTest(s *IperfSuite) {
        go func() {
                defer GinkgoRecover()
                cmd := "iperf3 -c " + serverIpAddress + " -B " + clientIpAddress +
-                       " -u -l 1460 -b 10g -p " + s.GetPortFromPpid()
+                       " -u -l 1460 -b 10g -J -p " + s.GetPortFromPpid()
                s.StartClientApp(clientContainer, cmd, clnCh, clnRes)
        }()
        s.AssertChannelClosed(time.Minute*3, clnCh)
-       s.Log(<-clnRes)
+       output := <-clnRes
+       result := s.ParseJsonIperfOutput(output)
+       s.LogJsonIperfOutput(result)
+       s.AssertIperfMinTransfer(result, 800)
 }
index 32473d5..76bc366 100644 (file)
@@ -48,7 +48,7 @@ func ldPreloadIperfVpp(s *LdpSuite, useUdp bool) {
        stopServerCh := make(chan struct{}, 1)
        srvCh := make(chan error, 1)
        clnCh := make(chan error)
-       clnRes := make(chan string, 1)
+       clnRes := make(chan []byte, 1)
 
        defer func() {
                stopServerCh <- struct{}{}
@@ -65,12 +65,15 @@ func ldPreloadIperfVpp(s *LdpSuite, useUdp bool) {
 
        go func() {
                defer GinkgoRecover()
-               cmd := "iperf3 -c " + serverVethAddress + " -l 1460 -b 10g -p " + s.GetPortFromPpid() + protocol
+               cmd := "iperf3 -c " + serverVethAddress + " -l 1460 -b 10g -J -p " + s.GetPortFromPpid() + protocol
                s.StartClientApp(clientContainer, cmd, clnCh, clnRes)
        }()
 
        s.AssertChannelClosed(time.Minute*3, clnCh)
-       s.Log(<-clnRes)
+       output := <-clnRes
+       result := s.ParseJsonIperfOutput(output)
+       s.LogJsonIperfOutput(result)
+       s.AssertIperfMinTransfer(result, 400)
 }
 
 func RedisBenchmarkTest(s *LdpSuite) {
@@ -83,7 +86,7 @@ func RedisBenchmarkTest(s *LdpSuite) {
        runningSrv := make(chan error)
        doneSrv := make(chan struct{})
        clnCh := make(chan error)
-       clnRes := make(chan string, 1)
+       clnRes := make(chan []byte, 1)
 
        defer func() {
                doneSrv <- struct{}{}
@@ -112,5 +115,5 @@ func RedisBenchmarkTest(s *LdpSuite) {
 
        // 4.5 minutes
        s.AssertChannelClosed(time.Second*270, clnCh)
-       s.Log(<-clnRes)
+       s.Log(string(<-clnRes))
 }