From add207696a9a16bb5ba63541ca0b7c4353007ad6 Mon Sep 17 00:00:00 2001 From: Ram Lavi Date: Mon, 20 Nov 2023 13:24:13 +0200 Subject: [PATCH 1/2] trex/client: Unremove stdout lines The trex/client is a wrapper for the trex-console binary on the guest. The stdout of this binary catains many lines - that are currently repressed because they are not used. In the next commits, this trex-console output is going to be parsed, so it makes sense to un-repress these lines to keep the original context of the output. Returning the trex-console output lines to avoid mutating the raw output. Signed-off-by: Ram Lavi --- pkg/internal/checkup/trex/client.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pkg/internal/checkup/trex/client.go b/pkg/internal/checkup/trex/client.go index a56f4df5..49f847c7 100644 --- a/pkg/internal/checkup/trex/client.go +++ b/pkg/internal/checkup/trex/client.go @@ -266,12 +266,7 @@ func (c Client) runTrexConsoleCmdWithJSONResponse(command, requestKey string) (s } func cleanStdout(rawStdout string) string { - stdout := strings.Replace(rawStdout, "Using 'python3' as Python interpeter", "", -1) - stdout = strings.Replace(stdout, "-=TRex Console v3.0=-", "", -1) - stdout = strings.Replace(stdout, "Type 'help' or '?' for supported actions", "", -1) - stdout = strings.Replace(stdout, "trex>Global Statistitcs", "", -1) - stdout = strings.Replace(stdout, "trex>", "", -1) - return removeUnprintableCharacters(stdout) + return removeUnprintableCharacters(rawStdout) } func removeUnprintableCharacters(input string) string { From e57b0db0932565defdf5f5d52b9b2393bd090bfe Mon Sep 17 00:00:00 2001 From: Ram Lavi Date: Mon, 20 Nov 2023 13:31:59 +0200 Subject: [PATCH 2/2] trex/client: Check for command failure The trex/client is a wrapper for the trex-console binary on the guest. The stdout of this binary is repressed by using the -q flag. In order to check for errors in the trex commands: - Removing the -q flag in "Set" functions. - Checking the stdout for [FAILED] mentions, and logging them if they occur. - Add unit test cases to the "Set" functions: StartTraffic, ClearStats. Signed-off-by: Ram Lavi --- pkg/internal/checkup/trex/client.go | 18 +++- pkg/internal/checkup/trex/client_test.go | 113 ++++++++++++++++++++++- 2 files changed, 127 insertions(+), 4 deletions(-) diff --git a/pkg/internal/checkup/trex/client.go b/pkg/internal/checkup/trex/client.go index 49f847c7..a2c403df 100644 --- a/pkg/internal/checkup/trex/client.go +++ b/pkg/internal/checkup/trex/client.go @@ -226,7 +226,7 @@ func (c Client) getStartTrafficCmd(port PortIdx) string { } func (c Client) runTrexConsoleCmd(command string) (string, error) { - shellCommand := fmt.Sprintf("cd %s && echo %q | ./trex-console -q", BinDirectory, command) + shellCommand := fmt.Sprintf("cd %s && echo %q | ./trex-console", BinDirectory, command) resp, err := c.consoleExpecter.SafeExpectBatchWithResponse([]expect.Batcher{ &expect.BSnd{S: shellCommand + "\n"}, &expect.BExp{R: shellPrompt}, @@ -237,7 +237,13 @@ func (c Client) runTrexConsoleCmd(command string) (string, error) { if err != nil { return "", err } - return cleanStdout(resp[0].Output), nil + stdout := cleanStdout(resp[0].Output) + if err = checkStdoutForFailures(stdout); err != nil { + log.Printf("command %q failed. Output:\n%s", shellCommand, stdout) + return "", fmt.Errorf("trex command %q failed. check logs for more information", command) + } + + return stdout, nil } func (c Client) runTrexConsoleCmdWithJSONResponse(command, requestKey string) (string, error) { @@ -269,6 +275,14 @@ func cleanStdout(rawStdout string) string { return removeUnprintableCharacters(rawStdout) } +func checkStdoutForFailures(stdout string) error { + const trexFailureStatus = "[FAILED]" + if strings.Contains(stdout, trexFailureStatus) { + return fmt.Errorf("found failing status %q", trexFailureStatus) + } + return nil +} + func removeUnprintableCharacters(input string) string { ansiEscape := regexp.MustCompile(`\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])|\r`) //nolint: gocritic cleanedInput := ansiEscape.ReplaceAllString(input, "") diff --git a/pkg/internal/checkup/trex/client_test.go b/pkg/internal/checkup/trex/client_test.go index a0cb5d43..3f40fa82 100644 --- a/pkg/internal/checkup/trex/client_test.go +++ b/pkg/internal/checkup/trex/client_test.go @@ -40,6 +40,38 @@ const ( portIdx = trex.SourcePort ) +func TestClearStatsSuccess(t *testing.T) { + expecter := expecterStub{expectTrexConsoleFailure: false} + c := trex.NewClient(expecter, trafficGeneratorPacketsPerSecond, testDuration, verbosePrintsEnabled) + + _, err := c.ClearStats() + assert.NoError(t, err, "ClearStats returned an error") +} + +func TestClearStatsFailure(t *testing.T) { + expecter := expecterStub{expectTrexConsoleFailure: true} + c := trex.NewClient(expecter, trafficGeneratorPacketsPerSecond, testDuration, verbosePrintsEnabled) + + _, err := c.ClearStats() + assert.ErrorContains(t, err, "trex command \"clear\" failed. check logs for more information") +} + +func TestStartTrafficSuccess(t *testing.T) { + expecter := expecterStub{expectTrexConsoleFailure: false} + c := trex.NewClient(expecter, trafficGeneratorPacketsPerSecond, testDuration, verbosePrintsEnabled) + + _, err := c.StartTraffic(trex.SourcePort) + assert.NoError(t, err, "StartTraffic returned an error") +} + +func TestStartTrafficFailure(t *testing.T) { + expecter := expecterStub{expectTrexConsoleFailure: true} + c := trex.NewClient(expecter, trafficGeneratorPacketsPerSecond, testDuration, verbosePrintsEnabled) + + _, err := c.StartTraffic(trex.SourcePort) + assert.ErrorContains(t, err, "trex command \"start -f /opt/tests/testpmd.py -m 1mpps -p 0 -d 1\" failed. check logs for more information") +} + func TestGetPortStatsSuccess(t *testing.T) { expecter := expecterStub{} c := trex.NewClient(expecter, trafficGeneratorPacketsPerSecond, testDuration, verbosePrintsEnabled) @@ -145,6 +177,58 @@ func TestGetGlobalStatsSuccess(t *testing.T) { } const ( + clearCmd = "cd /opt/trex && echo \"clear\" | ./trex-console\n" + clearCmdSuccessfulOutput = "Using 'python3' as Python interpeter\n\n\n" + + "Connecting to RPC server on localhost:4501 [SUCCESS]\n\n\n" + + "Connecting to publisher server on localhost:4500 [SUCCESS]\n\n\n" + + "Acquiring ports [0, 1]: [SUCCESS]\n\n" + + "*** Warning - Port 0 destination is unresolved ***\n" + + "*** Warning - Port 1 destination is unresolved ***\n\n" + + "Server Info:\n\nServer version: v3.03 @ STL\nServer mode: Stateless\nServer CPU: 4 x Intel Xeon Processor (Cascade" + + "lake)\nPorts count: 2 x 10.0Gbps @ Ethernet Virtual Function 700 Series\t\n\n-=TRex Console v3.0=-\n\nType 'help' or '?' for" + + " supported actions\n\ntrex>\n" + + "Clearing stats : [SUCCESS]\n\n" + + "107.02 [ms]\n\ntrex>Shutting down RPC client" + clearCmdFailedOutput = "Using 'python3' as Python interpeter\n\n\n" + + "Connecting to RPC server on localhost:4501 [SUCCESS]\n\n\n" + + "Connecting to publisher server on localhost:4500 [SUCCESS]\n\n\n" + + "Acquiring ports [0, 1]: [SUCCESS]\n\n" + + "*** Warning - Port 0 destination is unresolved ***\n" + + "*** Warning - Port 1 destination is unresolved ***\n\n" + + "Server Info:\n\nServer version: v3.03 @ STL\nServer mode: Stateless\nServer CPU: 4 x Intel Xeon Processor (Cascade" + + "lake)\nPorts count: 2 x 10.0Gbps @ Ethernet Virtual Function 700 Series\t\n\n-=TRex Console v3.0=-\n\nType 'help' or '?' for" + + " supported actions\n\ntrex>\n" + + "Clearing stats : [FAILED]\n\n" + + "Clear : *** some error\n\n" + + "107.02 [ms]\n\ntrex>Shutting down RPC client" + startTrafficCmd = "cd /opt/trex && echo \"start -f /opt/tests/testpmd.py -m 1mpps -p 0 -d 1\" | ./trex-console\n" + startCmdSuccessfulOutput = "Using 'python3' as Python interpeter\n\n\n" + + "Connecting to RPC server on localhost:4501 [SUCCESS]\n\n\n" + + "Connecting to publisher server on localhost:4500 [SUCCESS]\n\n\n" + + "Acquiring ports [0, 1]: [SUCCESS]\n\n" + + "*** Warning - Port 0 destination is unresolved ***\n" + + "*** Warning - Port 1 destination is unresolved ***\n\n" + + "Server Info:\n\nServer version: v3.03 @ STL\nServer mode: Stateless\nServer CPU: 4 x Intel Xeon Processor (Cascade" + + "lake)\nPorts count: 2 x 10.0Gbps @ Ethernet Virtual Function 700 Series\t\n\n-=TRex Console v3.0=-\n\nType 'help' or '?' f" + + "or supported actions\n\ntrex>\n" + + "Removing all streams from port(s) [0._]: [SUCCESS]\n\n\n" + + "Attaching 4 streams to port(s) [0._]: [SUCCESS]\n\n\n" + + "Starting traffic on port(s) [0._]: [SUCCESS]\n\n" + + "72.94 [ms]\n\ntrex>Shutting down RPC client" + startCmdFailedOutput = "Using 'python3' as Python interpeter\n\n\n" + + "Connecting to RPC server on localhost:4501 [SUCCESS]\n\n\n" + + "Connecting to publisher server on localhost:4500 [SUCCESS]\n\n\n" + + "Acquiring ports [0, 1]: [SUCCESS]\n\n" + + "*** Warning - Port 0 destination is unresolved ***\n" + + "*** Warning - Port 1 destination is unresolved ***\n\n" + + "Server Info:\n\nServer version: v3.03 @ STL\nServer mode: Stateless\nServer CPU: 4 x Intel Xeon Processor (Cascade" + + "lake)\nPorts count: 2 x 10.0Gbps @ Ethernet Virtual Function 700 Series\t\n\n-=TRex Console v3.0=-\n\nType 'help' or '?' for s" + + "upported actions\n\ntrex>\n" + + "Removing all streams from port(s) [0._]: [SUCCESS]\n\n\n" + + "Attaching 4 streams to port(s) [0._]: [SUCCESS]\n\n\n" + + "Starting traffic on port(s) [0._]: [FAILED]\n\n\n" + + "start - Port 0 : *** Expected L1 B/W: '13.44 Gbps' exceeds port line rate: '10 Gbps'\n\n" + + "trex>Shutting down RPC client" portStatsCmd = "cd /opt/trex && echo \"verbose on;stats --port 0 -p\" | ./trex-console -q\n" portStatsOutput = "Using 'python3' as Python interpeter\r\n\r\n\r\n-=TRex Console v3.0=-\r\n\r\nType 'help' or '?' for supported act" + "ions\r\n\r\ntrex>\r\n\x1b[1m\x1b[32mverbose set to on\x1b[39m\x1b[22m\r\n\r\n\r\n\r\n[verbose] Sending Request To Server:\r\n\r" + @@ -240,8 +324,9 @@ const ( ) type expecterStub struct { - expectBatchErr error - timeoutErr error + expectBatchErr error + timeoutErr error + expectTrexConsoleFailure bool } func (es expecterStub) SafeExpectBatchWithResponse(expected []expect.Batcher, _ time.Duration) ([]expect.BatchRes, error) { @@ -266,6 +351,30 @@ func (es expecterStub) SafeExpectBatchWithResponse(expected []expect.Batcher, _ Idx: 1, Output: globalStatsOutput, }) + case startTrafficCmd: + var consoleResponse string + if es.expectTrexConsoleFailure { + consoleResponse = startCmdFailedOutput + } else { + consoleResponse = startCmdSuccessfulOutput + } + batchRes = append(batchRes, + expect.BatchRes{ + Idx: 1, + Output: consoleResponse, + }) + case clearCmd: + var consoleResponse string + if es.expectTrexConsoleFailure { + consoleResponse = clearCmdFailedOutput + } else { + consoleResponse = clearCmdSuccessfulOutput + } + batchRes = append(batchRes, + expect.BatchRes{ + Idx: 1, + Output: consoleResponse, + }) default: return nil, fmt.Errorf("command not recognized: %s", expected[0].Arg()) }