diff --git a/connectivity/check/check.go b/connectivity/check/check.go index a2ae564f44..934d90efb7 100644 --- a/connectivity/check/check.go +++ b/connectivity/check/check.go @@ -45,6 +45,7 @@ type Parameters struct { PerfCRR bool PerfHostNet bool PerfSamples int + PerfLatency bool CurlImage string PerformanceImage string JSONMockImage string diff --git a/connectivity/tests/perfpod.go b/connectivity/tests/perfpod.go index a8454496ba..0d1a655581 100644 --- a/connectivity/tests/perfpod.go +++ b/connectivity/tests/perfpod.go @@ -31,6 +31,7 @@ type netPerfPodtoPod struct { } var netPerfRegex = regexp.MustCompile(`\s+\d+\s+\d+\s+(\d+|\S+)\s+(\S+|\d+)\s+(\S+)+\s+(\S+)?`) +var netPerfRegexLatency = regexp.MustCompile(`(\d+(\.\d+)?)\s*$`) func (s *netPerfPodtoPod) Name() string { tn := "perf-pod-to-pod" @@ -44,6 +45,7 @@ func (s *netPerfPodtoPod) Run(ctx context.Context, t *check.Test) { samples := t.Context().Params().PerfSamples duration := t.Context().Params().PerfDuration crr := t.Context().Params().PerfCRR + latency := t.Context().Params().PerfLatency for _, c := range t.Context().PerfClientPods() { c := c for _, server := range t.Context().PerfServerPod() { @@ -57,7 +59,12 @@ func (s *netPerfPodtoPod) Run(ctx context.Context, t *check.Test) { action.CollectFlows = false action.Run(func(a *check.Action) { if crr { - netperf(ctx, server.Pod.Status.PodIP, c.Pod.Name, "TCP_CRR", a, t.Context().PerfResults, 1, 30, scenarioName) + netperf(ctx, server.Pod.Status.PodIP, c.Pod.Name, "TCP_CRR", a, t.Context().PerfResults, samples, duration, scenarioName) + } else if latency { + netperf(ctx, server.Pod.Status.PodIP, c.Pod.Name, "TCP_RR_LATENCY", a, t.Context().PerfResults, samples, duration, scenarioName) + netperf(ctx, server.Pod.Status.PodIP, c.Pod.Name, "TCP_STREAM_LATENCY", a, t.Context().PerfResults, samples, duration, scenarioName) + netperf(ctx, server.Pod.Status.PodIP, c.Pod.Name, "UDP_RR_LATENCY", a, t.Context().PerfResults, samples, duration, scenarioName) + netperf(ctx, server.Pod.Status.PodIP, c.Pod.Name, "UDP_STREAM_LATENCY", a, t.Context().PerfResults, samples, duration, scenarioName) } else { netperf(ctx, server.Pod.Status.PodIP, c.Pod.Name, "TCP_RR", a, t.Context().PerfResults, samples, duration, scenarioName) netperf(ctx, server.Pod.Status.PodIP, c.Pod.Name, "TCP_STREAM", a, t.Context().PerfResults, samples, duration, scenarioName) @@ -69,40 +76,78 @@ func (s *netPerfPodtoPod) Run(ctx context.Context, t *check.Test) { } } +func buildExecCommand(test string, sip string, duration time.Duration, args []string) []string { + exec := []string{"/usr/local/bin/netperf", "-H", sip, "-l", duration.String(), "-t", test, "--", "-R", "1", "-m", fmt.Sprintf("%d", messageSize)} + exec = append(exec, args...) + + return exec +} + func netperf(ctx context.Context, sip string, podname string, test string, a *check.Action, result map[check.PerfTests]check.PerfResult, samples int, duration time.Duration, scenarioName string) { // Define test about to be executed and from which pod k := check.PerfTests{ Pod: podname, Test: test, } - metric := string("OP/s") - if strings.Contains(test, "STREAM") { - metric = "Mb/s" - } - exec := []string{"/usr/local/bin/netperf", "-H", sip, "-l", duration.String(), "-t", test, "--", "-R", "1", "-m", fmt.Sprintf("%d", messageSize)} - // recv socketsize send socketsize msg size|okmsg duration value + var metric string values := []float64{} - // Result data - for i := 0; i < samples; i++ { - a.ExecInPod(ctx, exec) - d := netPerfRegex.FindStringSubmatch(a.CmdOutput()) - if len(d) < 5 { - a.Fatal("Unable to process netperf result") + + if strings.Contains(test, "LATENCY") { + test = strings.ReplaceAll(test, "_LATENCY", "") + k.Test = test + metric = string("μs") + + args := []string{"-o", "mean_latency"} + exec := buildExecCommand(test, sip, duration, args) + + for i := 0; i < samples; i++ { + a.ExecInPod(ctx, exec) + d := netPerfRegexLatency.FindString(a.CmdOutput()) + + if d == "" { + a.Fatal("Unable to process netperf result") + } + + f, err := strconv.ParseFloat(strings.TrimSuffix(d, "\n"), 64) + if err == nil { + values = append(values, f) + } else { + a.Fatal("Unable to parse netperf result") + } } - nv := "" - if len(d[len(d)-1]) > 0 { - nv = d[len(d)-1] - } else { - nv = d[len(d)-2] + + } else { + metric = string("OP/s") + if strings.Contains(test, "STREAM") { + metric = "Mb/s" } - f, err := strconv.ParseFloat(nv, 64) - if err == nil { - values = append(values, f) - } else { - a.Fatal("Unable to parse netperf result") + + exec := buildExecCommand(test, sip, duration, []string{}) + // recv socketsize send socketsize msg size|okmsg duration value + // Result data + for i := 0; i < samples; i++ { + a.ExecInPod(ctx, exec) + d := netPerfRegex.FindStringSubmatch(a.CmdOutput()) + if len(d) < 5 { + a.Fatal("Unable to process netperf result") + } + nv := "" + if len(d[len(d)-1]) > 0 { + nv = d[len(d)-1] + } else { + nv = d[len(d)-2] + } + f, err := strconv.ParseFloat(nv, 64) + if err == nil { + values = append(values, f) + } else { + a.Fatal("Unable to parse netperf result") + } } + } + res := check.PerfResult{ Scenario: scenarioName, Metric: metric, diff --git a/internal/cli/cmd/connectivity.go b/internal/cli/cmd/connectivity.go index bb5ba9b8ca..bb4d15015a 100644 --- a/internal/cli/cmd/connectivity.go +++ b/internal/cli/cmd/connectivity.go @@ -158,6 +158,7 @@ func newCmdConnectivityTest(hooks Hooks) *cobra.Command { cmd.Flags().IntVar(¶ms.PerfSamples, "perf-samples", 1, "Number of Performance samples to capture (how many times to run each test)") cmd.Flags().BoolVar(¶ms.PerfCRR, "perf-crr", false, "Run Netperf CRR Test. --perf-samples and --perf-duration ignored") cmd.Flags().BoolVar(¶ms.PerfHostNet, "host-net", false, "Use host networking during network performance tests") + cmd.Flags().BoolVar(¶ms.PerfLatency, "perf-latency", false, "Run network latency tests") cmd.Flags().StringVar(¶ms.CurlImage, "curl-image", defaults.ConnectivityCheckAlpineCurlImage, "Image path to use for curl") cmd.Flags().StringVar(¶ms.PerformanceImage, "performance-image", defaults.ConnectivityPerformanceImage, "Image path to use for performance")