Skip to content

Commit d3b0093

Browse files
AbdelrahmanElawadymuhamadazmy
authored andcommitted
Add iperf output types and use it instead of go-iperf (#2089)
* Add iperf output types and use it instead of go-iperf * Organize perf tasks * Remove go-iperf tmp directories
1 parent 5745b54 commit d3b0093

File tree

5 files changed

+195
-38
lines changed

5 files changed

+195
-38
lines changed

cmds/modules/noded/main.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"github.com/threefoldtech/zos/pkg/events"
2222
"github.com/threefoldtech/zos/pkg/monitord"
2323
"github.com/threefoldtech/zos/pkg/perf"
24+
"github.com/threefoldtech/zos/pkg/perf/cpubench"
25+
"github.com/threefoldtech/zos/pkg/perf/iperf"
2426
"github.com/threefoldtech/zos/pkg/registrar"
2527
"github.com/threefoldtech/zos/pkg/stubs"
2628
"github.com/threefoldtech/zos/pkg/utils"
@@ -202,10 +204,9 @@ func action(cli *cli.Context) error {
202204
return errors.Wrap(err, "failed to create a new perfMon")
203205
}
204206

205-
iperfTest := perf.NewIperfTest()
206-
perfMon.AddTask(&iperfTest)
207+
perfMon.AddTask(iperf.NewTask())
207208

208-
cpuBenchmarkTask := perf.NewCPUBenchmarkTask()
209+
cpuBenchmarkTask := cpubench.NewCPUBenchmarkTask()
209210
perfMon.AddTask(&cpuBenchmarkTask)
210211

211212
if err = perfMon.Run(ctx); err != nil {

pkg/perf/cpubench_task.go renamed to pkg/perf/cpubench/cpubench_task.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
package perf
1+
package cpubench
22

33
import (
44
"context"
55
"encoding/json"
66
"fmt"
77
"os/exec"
88

9+
"github.com/threefoldtech/zos/pkg/perf"
910
"github.com/threefoldtech/zos/pkg/stubs"
1011
)
1112

@@ -28,7 +29,7 @@ type CPUBenchmarkResult struct {
2829
Workloads int `json:"workloads"`
2930
}
3031

31-
var _ Task = (*CPUBenchmarkTask)(nil)
32+
var _ perf.Task = (*CPUBenchmarkTask)(nil)
3233

3334
// NewCPUBenchmarkTask returns a new CPU benchmark task.
3435
func NewCPUBenchmarkTask() CPUBenchmarkTask {
@@ -59,7 +60,7 @@ func (c *CPUBenchmarkTask) Run(ctx context.Context) (interface{}, error) {
5960
if err != nil {
6061
return nil, fmt.Errorf("failed to parse cpubench output: %w", err)
6162
}
62-
client := GetZbusClient(ctx)
63+
client := perf.GetZbusClient(ctx)
6364
statistics := stubs.NewStatisticsStub(client)
6465

6566
workloads, err := statistics.Workloads(ctx)

pkg/perf/graphql_nodes.go renamed to pkg/perf/iperf/graphql_nodes.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package perf
1+
package iperf
22

33
import (
44
"context"

pkg/perf/iperf_task.go renamed to pkg/perf/iperf/iperf_task.go

Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1-
package perf
1+
package iperf
22

33
import (
44
"context"
5+
"encoding/json"
6+
"fmt"
57
"net"
8+
"os"
69
"os/exec"
10+
"path/filepath"
711

8-
goIperf "github.com/BGrewell/go-iperf"
912
"github.com/pkg/errors"
1013
"github.com/rs/zerolog/log"
1114
"github.com/threefoldtech/zos/pkg/environment"
1215
"github.com/threefoldtech/zos/pkg/network/iperf"
16+
"github.com/threefoldtech/zos/pkg/perf"
1317
)
1418

1519
// IperfTest for iperf tcp/udp tests
@@ -20,18 +24,24 @@ type IperfTest struct {
2024

2125
// IperfResult for iperf test results
2226
type IperfResult struct {
23-
UploadSpeed float64 `json:"upload_speed"` // in bit/sec
24-
DownloadSpeed float64 `json:"download_speed"` // in bit/sec
25-
NodeID uint32 `json:"node_id"`
26-
NodeIpv4 string `json:"node_ip"`
27-
TestType string `json:"test_type"`
28-
Error string `json:"error"`
29-
CpuReport goIperf.CpuUtilizationReport `json:"cpu_report"`
27+
UploadSpeed float64 `json:"upload_speed"` // in bit/sec
28+
DownloadSpeed float64 `json:"download_speed"` // in bit/sec
29+
NodeID uint32 `json:"node_id"`
30+
NodeIpv4 string `json:"node_ip"`
31+
TestType string `json:"test_type"`
32+
Error string `json:"error"`
33+
CpuReport CPUUtilizationPercent `json:"cpu_report"`
3034
}
3135

32-
// NewIperfTest creates a new iperf test
33-
func NewIperfTest() IperfTest {
34-
return IperfTest{taskID: "iperf", schedule: "0 0 */6 * * *"}
36+
// NewTask creates a new iperf test
37+
func NewTask() perf.Task {
38+
// because go-iperf left tmp directories with perf binary in it each time
39+
// the task had run
40+
matches, _ := filepath.Glob("/tmp/goiperf*")
41+
for _, match := range matches {
42+
os.RemoveAll(match)
43+
}
44+
return &IperfTest{taskID: "iperf", schedule: "0 0 */6 * * *"}
3545
}
3646

3747
// ID returns the ID of the tcp task
@@ -104,35 +114,46 @@ func (t *IperfTest) Run(ctx context.Context) (interface{}, error) {
104114
}
105115

106116
func (t *IperfTest) runIperfTest(ctx context.Context, clientIP string, tcp bool) IperfResult {
107-
iperfClient := goIperf.NewClient(clientIP)
108-
iperfClient.SetBandwidth("1M")
109-
iperfClient.SetPort(iperf.IperfPort)
110-
iperfClient.SetInterval(20)
111-
iperfClient.SetJSON(true)
117+
opts := make([]string, 0)
118+
opts = append(opts,
119+
"--client", clientIP,
120+
"--bandwidth", "1M",
121+
"--port", fmt.Sprint(iperf.IperfPort),
122+
"--interval", "20",
123+
"--json",
124+
)
112125

113126
if !tcp {
114-
iperfClient.SetLength("16B")
115-
iperfClient.SetProto(goIperf.PROTO_UDP)
127+
opts = append(opts, "--length", "16B", "--udp")
116128
}
117-
118-
err := iperfClient.Start()
119-
if err != nil {
120-
log.Error().Err(err).Msgf("failed to start iperf client with ip '%s'", clientIP)
129+
output, err := exec.CommandContext(ctx, "iperf", opts...).CombinedOutput()
130+
exitErr := &exec.ExitError{}
131+
if err != nil && !errors.As(err, &exitErr) {
132+
log.Err(err).Msg("failed to run iperf")
133+
return IperfResult{}
121134
}
122135

123-
<-iperfClient.Done
136+
var report iperfCommandOutput
137+
if err := json.Unmarshal(output, &report); err != nil {
138+
log.Err(err).Msg("failed to parse iperf output")
139+
return IperfResult{}
140+
}
124141

142+
proto := "tcp"
143+
if !tcp {
144+
proto = "udp"
145+
}
125146
iperfResult := IperfResult{
126-
UploadSpeed: iperfClient.Report().End.SumSent.BitsPerSecond,
127-
DownloadSpeed: iperfClient.Report().End.SumReceived.BitsPerSecond,
128-
CpuReport: iperfClient.Report().End.CpuReport,
147+
UploadSpeed: report.End.SumSent.BitsPerSecond,
148+
DownloadSpeed: report.End.SumReceived.BitsPerSecond,
149+
CpuReport: report.End.CPUUtilizationPercent,
129150
NodeIpv4: clientIP,
130-
TestType: string(iperfClient.Proto()),
131-
Error: iperfClient.Report().Error,
151+
TestType: proto,
152+
Error: report.Error,
132153
}
133154

134-
if !tcp && len(iperfClient.Report().End.Streams) > 0 {
135-
iperfResult.DownloadSpeed = iperfClient.Report().End.Streams[0].Udp.BitsPerSecond
155+
if !tcp && len(report.End.Streams) > 0 {
156+
iperfResult.DownloadSpeed = report.End.Streams[0].UDP.BitsPerSecond
136157
}
137158

138159
return iperfResult

pkg/perf/iperf/iperf_types.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package iperf
2+
3+
type iperfCommandOutput struct {
4+
Start Start `json:"start"`
5+
Intervals []Interval `json:"intervals"`
6+
End End `json:"end"`
7+
Error string `json:"error"`
8+
}
9+
10+
type End struct {
11+
Streams []EndStream `json:"streams"`
12+
SumSent Sum `json:"sum_sent"`
13+
SumReceived Sum `json:"sum_received"`
14+
CPUUtilizationPercent CPUUtilizationPercent `json:"cpu_utilization_percent"`
15+
SenderTCPCongestion string `json:"sender_tcp_congestion"`
16+
ReceiverTCPCongestion string `json:"receiver_tcp_congestion"`
17+
}
18+
19+
type CPUUtilizationPercent struct {
20+
HostTotal float64 `json:"host_total"`
21+
HostUser float64 `json:"host_user"`
22+
HostSystem float64 `json:"host_system"`
23+
RemoteTotal float64 `json:"remote_total"`
24+
RemoteUser float64 `json:"remote_user"`
25+
RemoteSystem float64 `json:"remote_system"`
26+
}
27+
28+
type EndStream struct {
29+
Sender Sum `json:"sender"`
30+
Receiver Sum `json:"receiver"`
31+
UDP UDPSum `json:"udp"`
32+
}
33+
34+
type UDPSum struct {
35+
Socket int64 `json:"socket"`
36+
Start float64 `json:"start"`
37+
End float64 `json:"end"`
38+
Seconds float64 `json:"seconds"`
39+
Bytes int64 `json:"bytes"`
40+
BitsPerSecond float64 `json:"bits_per_second"`
41+
JitterMS float64 `json:"jitter_ms"`
42+
LostPackets int64 `json:"lost_packets"`
43+
Packets int64 `json:"packets"`
44+
LostPercent float64 `json:"lost_percent"`
45+
OutOfOrder int64 `json:"out_of_order"`
46+
Sender bool `json:"sender"`
47+
}
48+
49+
type Sum struct {
50+
Socket int64 `json:"socket"`
51+
Start float64 `json:"start"`
52+
End float64 `json:"end"`
53+
Seconds float64 `json:"seconds"`
54+
Bytes int64 `json:"bytes"`
55+
BitsPerSecond float64 `json:"bits_per_second"`
56+
Retransmits int64 `json:"retransmits"`
57+
MaxSndCwnd int64 `json:"max_snd_cwnd"`
58+
MaxSndWnd int64 `json:"max_snd_wnd"`
59+
MaxRtt int64 `json:"max_rtt"`
60+
MinRtt int64 `json:"min_rtt"`
61+
MeanRtt int64 `json:"mean_rtt"`
62+
Sender bool `json:"sender"`
63+
}
64+
65+
type Interval struct {
66+
Streams []IntervalStream `json:"streams"`
67+
Sum Sum `json:"sum"`
68+
}
69+
70+
type IntervalStream struct {
71+
Socket int64 `json:"socket"`
72+
Start float64 `json:"start"`
73+
End float64 `json:"end"`
74+
Seconds float64 `json:"seconds"`
75+
Bytes int64 `json:"bytes"`
76+
BitsPerSecond float64 `json:"bits_per_second"`
77+
Retransmits int64 `json:"retransmits"`
78+
SndCwnd int64 `json:"snd_cwnd"`
79+
SndWnd int64 `json:"snd_wnd"`
80+
Rtt int64 `json:"rtt"`
81+
Rttvar int64 `json:"rttvar"`
82+
Pmtu int64 `json:"pmtu"`
83+
Omitted bool `json:"omitted"`
84+
Sender bool `json:"sender"`
85+
}
86+
87+
type Start struct {
88+
Connected []Connected `json:"connected"`
89+
Version string `json:"version"`
90+
SystemInfo string `json:"system_info"`
91+
Timestamp Timestamp `json:"timestamp"`
92+
ConnectingTo ConnectingTo `json:"connecting_to"`
93+
Cookie string `json:"cookie"`
94+
TCPMssDefault int64 `json:"tcp_mss_default"`
95+
TargetBitrate int64 `json:"target_bitrate"`
96+
FqRate int64 `json:"fq_rate"`
97+
SockBufsize int64 `json:"sock_bufsize"`
98+
SndbufActual int64 `json:"sndbuf_actual"`
99+
RcvbufActual int64 `json:"rcvbuf_actual"`
100+
TestStart TestStart `json:"test_start"`
101+
}
102+
103+
type Connected struct {
104+
Socket int64 `json:"socket"`
105+
LocalHost string `json:"local_host"`
106+
LocalPort int64 `json:"local_port"`
107+
RemoteHost string `json:"remote_host"`
108+
RemotePort int64 `json:"remote_port"`
109+
}
110+
111+
type ConnectingTo struct {
112+
Host string `json:"host"`
113+
Port int64 `json:"port"`
114+
}
115+
116+
type TestStart struct {
117+
Protocol string `json:"protocol"`
118+
NumStreams int64 `json:"num_streams"`
119+
Blksize int64 `json:"blksize"`
120+
Omit int64 `json:"omit"`
121+
Duration int64 `json:"duration"`
122+
Bytes int64 `json:"bytes"`
123+
Blocks int64 `json:"blocks"`
124+
Reverse int64 `json:"reverse"`
125+
Tos int64 `json:"tos"`
126+
TargetBitrate int64 `json:"target_bitrate"`
127+
Bidir int64 `json:"bidir"`
128+
Fqrate int64 `json:"fqrate"`
129+
}
130+
131+
type Timestamp struct {
132+
Time string `json:"time"`
133+
Timesecs int64 `json:"timesecs"`
134+
}

0 commit comments

Comments
 (0)