initial working version
parent
e8dc386ba0
commit
10cf19535e
@ -1,41 +1,82 @@
|
|||||||
package iperf
|
package iperf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Execute(cmd string, outPipe io.ReadCloser, errPipe io.ReadCloser, exit chan <- int) (err error) {
|
func ExecuteAsync(cmd string) (outPipe io.ReadCloser, errPipe io.ReadCloser, exitCode chan int, err error) {
|
||||||
|
exitCode = make(chan int)
|
||||||
cmdParts := strings.Fields(cmd)
|
cmdParts := strings.Fields(cmd)
|
||||||
binary, err := exec.LookPath(cmdParts[0])
|
binary, err := exec.LookPath(cmdParts[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
exe := exec.Command(binary, cmdParts[1:]...)
|
exe := exec.Command(binary, cmdParts[1:]...)
|
||||||
outPipe, err = exe.StdoutPipe()
|
outPipe, err = exe.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
errPipe, err = exe.StderrPipe()
|
errPipe, err = exe.StderrPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
err = exe.Start()
|
err = exe.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
if err := exe.Wait(); err != nil {
|
if err := exe.Wait(); err != nil {
|
||||||
if exiterr, ok := err.(*exec.ExitError); ok {
|
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||||
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||||
exit <- status.ExitStatus()
|
exitCode <- status.ExitStatus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
exit <- 0
|
exitCode <- 0
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return nil
|
return outPipe, errPipe, exitCode, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExecuteAsyncWithCancel(cmd string) (stdOut io.ReadCloser, stdErr io.ReadCloser, exitCode chan int, cancelToken context.CancelFunc, err error) {
|
||||||
|
exitCode = make(chan int)
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
cmdParts := strings.Fields(cmd)
|
||||||
|
binary, err := exec.LookPath(cmdParts[0])
|
||||||
|
if err != nil {
|
||||||
|
defer cancel()
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
exe := exec.CommandContext(ctx, binary, cmdParts[1:]...)
|
||||||
|
stdOut, err = exe.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
defer cancel()
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
stdErr, err = exe.StderrPipe()
|
||||||
|
if err != nil {
|
||||||
|
defer cancel()
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
err = exe.Start()
|
||||||
|
if err != nil {
|
||||||
|
defer cancel()
|
||||||
|
return nil, nil, nil, nil, err
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
if err := exe.Wait(); err != nil {
|
||||||
|
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||||
|
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||||
|
exitCode <- status.ExitStatus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
exitCode <- 0
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return stdOut, stdErr, exitCode, cancel, nil
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,25 @@
|
|||||||
|
github.com/BGrewell/go-conversions v0.0.0-20201203155646-5e189e4ca087 h1:/ZR3IHtTSPqwzYQSfZYwGjrmDI5c1u2jEDDtT75Fmqw=
|
||||||
|
github.com/BGrewell/go-conversions v0.0.0-20201203155646-5e189e4ca087/go.mod h1:XtXcz/MP04vhr6c6R/5gZPJZVQpbXlFsKTHr5yy/5sU=
|
||||||
|
github.com/BGrewell/go-execute v0.0.0-20201203155726-b7c037ebde49 h1:HV+WdlqXjmzP59lhgb100vTSIcDuKuLmK11KdnPY0cg=
|
||||||
|
github.com/BGrewell/go-execute v0.0.0-20201203155726-b7c037ebde49/go.mod h1:vQZr3vuuuKuknvi74K22ne7NzOlwKTPKXOFMVr9Qa6A=
|
||||||
|
github.com/BGrewell/tail v1.0.0 h1:sG+Uvv+UApHtj5z+AWWB9i5m2SCH0RLfxYqXujYQo+Q=
|
||||||
|
github.com/BGrewell/tail v1.0.0/go.mod h1:0PFYWAobUZKZLEYIxxmjFgnfvCLA600LkFbGO9KFIRA=
|
||||||
|
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
|
||||||
|
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||||
|
github.com/fatih/gomodifytags v1.13.0 h1:fmhwoecjZ5c34Q2chjRB9cL8Rgag+1TOSMy+grissMc=
|
||||||
|
github.com/fatih/gomodifytags v1.13.0/go.mod h1:TbUyEjH1Zo0GkJd2Q52oVYqYcJ0eGNqG8bsiOb75P9c=
|
||||||
|
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||||
|
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0=
|
||||||
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/tools v0.0.0-20180824175216-6c1c5e93cdc1/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
@ -1 +1,393 @@
|
|||||||
package iperf
|
package iperf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StreamInterval struct {
|
||||||
|
Streams []*StreamIntervalReport `json:"streams"`
|
||||||
|
Sum *StreamIntervalSumReport `json:"sum"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (si *StreamInterval) String() string {
|
||||||
|
b, err := json.Marshal(si)
|
||||||
|
if err != nil {
|
||||||
|
return "error converting to json"
|
||||||
|
}
|
||||||
|
|
||||||
|
var pretty bytes.Buffer
|
||||||
|
err = json.Indent(&pretty, b, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "error converting json to indented format"
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pretty.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamIntervalReport struct {
|
||||||
|
Socket int `json:"socket"`
|
||||||
|
StartInterval float32 `json:"start"`
|
||||||
|
EndInterval float32 `json:"end"`
|
||||||
|
Seconds float32 `json:"seconds"`
|
||||||
|
Bytes int `json:"bytes"`
|
||||||
|
BitsPerSecond float64 `json:"bits_per_second"`
|
||||||
|
Omitted bool `json:"omitted"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sir *StreamIntervalReport) String() string {
|
||||||
|
b, err := json.Marshal(sir)
|
||||||
|
if err != nil {
|
||||||
|
return "error converting to json"
|
||||||
|
}
|
||||||
|
|
||||||
|
var pretty bytes.Buffer
|
||||||
|
err = json.Indent(&pretty, b, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "error converting json to indented format"
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pretty.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamIntervalSumReport struct {
|
||||||
|
StartInterval float32 `json:"start"`
|
||||||
|
EndInterval float32 `json:"end"`
|
||||||
|
Seconds float32 `json:"seconds"`
|
||||||
|
Bytes int `json:"bytes"`
|
||||||
|
BitsPerSecond float64 `json:"bits_per_second"`
|
||||||
|
Omitted bool `json:"omitted"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sisr *StreamIntervalSumReport) String() string {
|
||||||
|
b, err := json.Marshal(sisr)
|
||||||
|
if err != nil {
|
||||||
|
return "error converting to json"
|
||||||
|
}
|
||||||
|
|
||||||
|
var pretty bytes.Buffer
|
||||||
|
err = json.Indent(&pretty, b, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "error converting json to indented format"
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pretty.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamEndReport struct {
|
||||||
|
Sender TcpStreamEndReport `json:"sender"`
|
||||||
|
Receiver TcpStreamEndReport `json:"receiver"`
|
||||||
|
Udp UdpStreamEndReport `json:"udp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ser *StreamEndReport) String() string {
|
||||||
|
b, err := json.Marshal(ser)
|
||||||
|
if err != nil {
|
||||||
|
return "error converting to json"
|
||||||
|
}
|
||||||
|
|
||||||
|
var pretty bytes.Buffer
|
||||||
|
err = json.Indent(&pretty, b, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "error converting json to indented format"
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pretty.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
type UdpStreamEndReport struct {
|
||||||
|
Socket int `json:"socket"`
|
||||||
|
Start float32 `json:"start"`
|
||||||
|
End float32 `json:"end"`
|
||||||
|
Seconds float32 `json:"seconds"`
|
||||||
|
Bytes int `json:"bytes"`
|
||||||
|
BitsPerSecond float64 `json:"bits_per_second"`
|
||||||
|
JitterMs float32 `json:"jitter_ms"`
|
||||||
|
LostPackets int `json:"lost_packets"`
|
||||||
|
Packets int `json:"packets"`
|
||||||
|
LostPercent float32 `json:"lost_percent"`
|
||||||
|
OutOfOrder int `json:"out_of_order"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (user *UdpStreamEndReport) String() string {
|
||||||
|
b, err := json.Marshal(user)
|
||||||
|
if err != nil {
|
||||||
|
return "error converting to json"
|
||||||
|
}
|
||||||
|
|
||||||
|
var pretty bytes.Buffer
|
||||||
|
err = json.Indent(&pretty, b, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "error converting json to indented format"
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pretty.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
type TcpStreamEndReport struct {
|
||||||
|
Socket int `json:"socket"`
|
||||||
|
Start float32 `json:"start"`
|
||||||
|
End float32 `json:"end"`
|
||||||
|
Seconds float32 `json:"seconds"`
|
||||||
|
Bytes int `json:"bytes"`
|
||||||
|
BitsPerSecond float64 `json:"bits_per_second"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tser *TcpStreamEndReport) String() string {
|
||||||
|
b, err := json.Marshal(tser)
|
||||||
|
if err != nil {
|
||||||
|
return "error converting to json"
|
||||||
|
}
|
||||||
|
|
||||||
|
var pretty bytes.Buffer
|
||||||
|
err = json.Indent(&pretty, b, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "error converting json to indented format"
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pretty.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamEndSumReport struct {
|
||||||
|
Start float32 `json:"start"`
|
||||||
|
End float32 `json:"end"`
|
||||||
|
Seconds float32 `json:"seconds"`
|
||||||
|
Bytes int `json:"bytes"`
|
||||||
|
BitsPerSecond float64 `json:"bits_per_second"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sesr *StreamEndSumReport) String() string {
|
||||||
|
b, err := json.Marshal(sesr)
|
||||||
|
if err != nil {
|
||||||
|
return "error converting to json"
|
||||||
|
}
|
||||||
|
|
||||||
|
var pretty bytes.Buffer
|
||||||
|
err = json.Indent(&pretty, b, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "error converting json to indented format"
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pretty.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
type CpuUtilizationReport struct {
|
||||||
|
HostTotal float32 `json:"host_total"`
|
||||||
|
HostUser float32 `json:"host_user"`
|
||||||
|
HostSystem float32 `json:"host_system"`
|
||||||
|
RemoteTotal float32 `json:"remote_total"`
|
||||||
|
RemoteUser float32 `json:"remote_user"`
|
||||||
|
RemoteSystem float32 `json:"remote_system"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cur *CpuUtilizationReport) String() string {
|
||||||
|
b, err := json.Marshal(cur)
|
||||||
|
if err != nil {
|
||||||
|
return "error converting to json"
|
||||||
|
}
|
||||||
|
|
||||||
|
var pretty bytes.Buffer
|
||||||
|
err = json.Indent(&pretty, b, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "error converting json to indented format"
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pretty.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConnectionInfo 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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ci *ConnectionInfo) String() string {
|
||||||
|
b, err := json.Marshal(ci)
|
||||||
|
if err != nil {
|
||||||
|
return "error converting to json"
|
||||||
|
}
|
||||||
|
|
||||||
|
var pretty bytes.Buffer
|
||||||
|
err = json.Indent(&pretty, b, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "error converting json to indented format"
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pretty.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
type TimestampInfo struct {
|
||||||
|
Time string `json:"time"`
|
||||||
|
TimeSecs int `json:"timesecs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsi *TimestampInfo) String() string {
|
||||||
|
b, err := json.Marshal(tsi)
|
||||||
|
if err != nil {
|
||||||
|
return "error converting to json"
|
||||||
|
}
|
||||||
|
|
||||||
|
var pretty bytes.Buffer
|
||||||
|
err = json.Indent(&pretty, b, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "error converting json to indented format"
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pretty.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConnectingToInfo struct {
|
||||||
|
Host string `json:"host"`
|
||||||
|
Port int `json:"port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cti *ConnectingToInfo) String() string {
|
||||||
|
b, err := json.Marshal(cti)
|
||||||
|
if err != nil {
|
||||||
|
return "error converting to json"
|
||||||
|
}
|
||||||
|
|
||||||
|
var pretty bytes.Buffer
|
||||||
|
err = json.Indent(&pretty, b, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "error converting json to indented format"
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pretty.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestStartInfo struct {
|
||||||
|
Protocol string `json:"protocol"`
|
||||||
|
NumStreams int `json:"num_streams"`
|
||||||
|
BlkSize int `json:"blksize"`
|
||||||
|
Omit int `json:"omit"`
|
||||||
|
Duration int `json:"duration"`
|
||||||
|
Bytes int `json:"bytes"`
|
||||||
|
Blocks int `json:"blocks"`
|
||||||
|
Reverse int `json:"reverse"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsi *TestStartInfo) String() string {
|
||||||
|
b, err := json.Marshal(tsi)
|
||||||
|
if err != nil {
|
||||||
|
return "error converting to json"
|
||||||
|
}
|
||||||
|
|
||||||
|
var pretty bytes.Buffer
|
||||||
|
err = json.Indent(&pretty, b, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "error converting json to indented format"
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pretty.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
type StartInfo struct {
|
||||||
|
Connected []*ConnectionInfo `json:"connected"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
SystemInfo string `json:"system_info"`
|
||||||
|
Timestamp TimestampInfo `json:"timestamp"`
|
||||||
|
ConnectingTo ConnectingToInfo `json:"connecting_to"`
|
||||||
|
Cookie string `json:"cookie"`
|
||||||
|
TcpMssDefault int `json:"tcp_mss_default"`
|
||||||
|
TestStart TestStartInfo `json:"test_start"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (si *StartInfo) String() string {
|
||||||
|
b, err := json.Marshal(si)
|
||||||
|
if err != nil {
|
||||||
|
return "error converting to json"
|
||||||
|
}
|
||||||
|
|
||||||
|
var pretty bytes.Buffer
|
||||||
|
err = json.Indent(&pretty, b, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "error converting json to indented format"
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pretty.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
type EndInfo struct {
|
||||||
|
Streams []*StreamEndReport `json:"streams"`
|
||||||
|
SumSent StreamEndSumReport `json:"sum_sent"`
|
||||||
|
SumReceived StreamEndSumReport `json:"sum_received"`
|
||||||
|
CpuReport CpuUtilizationReport `json:"cpu_utilization_percent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ei *EndInfo) String() string {
|
||||||
|
b, err := json.Marshal(ei)
|
||||||
|
if err != nil {
|
||||||
|
return "error converting to json"
|
||||||
|
}
|
||||||
|
|
||||||
|
var pretty bytes.Buffer
|
||||||
|
err = json.Indent(&pretty, b, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "error converting json to indented format"
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pretty.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerReport struct {
|
||||||
|
Start StartInfo `json:"start"`
|
||||||
|
Intervals []*StreamInterval `json:"intervals"`
|
||||||
|
End EndInfo `json:"end"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sr *ServerReport) String() string {
|
||||||
|
b, err := json.Marshal(sr)
|
||||||
|
if err != nil {
|
||||||
|
return "error converting to json"
|
||||||
|
}
|
||||||
|
|
||||||
|
var pretty bytes.Buffer
|
||||||
|
err = json.Indent(&pretty, b, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "error converting json to indented format"
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pretty.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestReport struct {
|
||||||
|
Start StartInfo `json:"start"`
|
||||||
|
Intervals []*StreamInterval `json:"intervals"`
|
||||||
|
End EndInfo `json:"end"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
ServerOutputJson ServerReport `json:"server_output_json"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *TestReport) String() string {
|
||||||
|
b, err := json.Marshal(tr)
|
||||||
|
if err != nil {
|
||||||
|
return "error converting to json"
|
||||||
|
}
|
||||||
|
|
||||||
|
var pretty bytes.Buffer
|
||||||
|
err = json.Indent(&pretty, b, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "error converting json to indented format"
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(pretty.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
func Loads(jsonStr string) (report *TestReport, err error) {
|
||||||
|
r := &TestReport{}
|
||||||
|
err = json.Unmarshal([]byte(jsonStr), r)
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func Load(filename string) (report *TestReport, err error) {
|
||||||
|
contents, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return Loads(string(contents))
|
||||||
|
}
|
||||||
|
@ -1 +1,92 @@
|
|||||||
package iperf
|
package iperf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/BGrewell/tail"
|
||||||
|
"github.com/BGrewell/go-conversions"
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Reporter struct {
|
||||||
|
ReportingChannel chan *StreamIntervalReport
|
||||||
|
LogFile string
|
||||||
|
running bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reporter) Start() {
|
||||||
|
r.running = true
|
||||||
|
go r.runLogProcessor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reporter) Stop() {
|
||||||
|
r.running = false
|
||||||
|
close(r.ReportingChannel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reporter) runLogProcessor() {
|
||||||
|
tailer, err := tail.TailFile(r.LogFile, tail.Config{
|
||||||
|
Follow: true,
|
||||||
|
ReOpen: true,
|
||||||
|
Poll: true,
|
||||||
|
MustExist: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to tail log file: %v", err)
|
||||||
|
}
|
||||||
|
for line := range tailer.Lines {
|
||||||
|
// TODO: For now this only cares about individual streams it ignores the sum lines
|
||||||
|
if len(line.Text) > 5 {
|
||||||
|
id := line.Text[1:4]
|
||||||
|
stream, err := strconv.Atoi(strings.TrimSpace(id))
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields := strings.Fields(line.Text[5:])
|
||||||
|
if len(fields) >= 6 {
|
||||||
|
if fields[0] == "local" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
timeFields := strings.Split(fields[0], "-")
|
||||||
|
start, err := strconv.ParseFloat(timeFields[0], 32)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to convert start time: %s\n", err)
|
||||||
|
}
|
||||||
|
end, err := strconv.ParseFloat(timeFields[1], 32)
|
||||||
|
transferedStr := fmt.Sprintf("%s%s", fields[2], fields[3])
|
||||||
|
transferedBytes, err := conversions.StringBitRateToInt(transferedStr)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to convert units: %s\n", err)
|
||||||
|
}
|
||||||
|
transferedBytes = transferedBytes / 8
|
||||||
|
rateStr := fmt.Sprintf("%s%s", fields[4], fields[5])
|
||||||
|
rate, err := conversions.StringBitRateToInt(rateStr)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to convert units: %s\n", err)
|
||||||
|
}
|
||||||
|
omitted := false
|
||||||
|
if len(fields) >= 7 && fields[6] == "(omitted)" {
|
||||||
|
omitted = true
|
||||||
|
}
|
||||||
|
report := &StreamIntervalReport{
|
||||||
|
Socket: stream,
|
||||||
|
StartInterval: float32(start),
|
||||||
|
EndInterval: float32(end),
|
||||||
|
Seconds: float32(end - start),
|
||||||
|
Bytes: int(transferedBytes),
|
||||||
|
BitsPerSecond: float64(rate),
|
||||||
|
Omitted: omitted,
|
||||||
|
}
|
||||||
|
r.ReportingChannel <- report
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !r.running {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,291 @@
|
|||||||
|
{
|
||||||
|
"start":{
|
||||||
|
"connected":[
|
||||||
|
{
|
||||||
|
"socket":4,
|
||||||
|
"local_host":"127.0.0.1",
|
||||||
|
"local_port":64200,
|
||||||
|
"remote_host":"127.0.0.1",
|
||||||
|
"remote_port":5201
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version":"iperf 3.1.3",
|
||||||
|
"system_info":"CYGWIN_NT-10.0 BGrewell-MOBL3 2.5.1(0.297/5/3) 2016-04-21 22:14 x86_64",
|
||||||
|
"timestamp":{
|
||||||
|
"time":"Tue, 02 Mar 2021 18:28:14 GMT",
|
||||||
|
"timesecs":1614709694
|
||||||
|
},
|
||||||
|
"connecting_to":{
|
||||||
|
"host":"127.0.0.1",
|
||||||
|
"port":5201
|
||||||
|
},
|
||||||
|
"cookie":"BGrewell-MOBL3.1614709694.715824.0bf",
|
||||||
|
"tcp_mss_default":0,
|
||||||
|
"test_start":{
|
||||||
|
"protocol":"TCP",
|
||||||
|
"num_streams":1,
|
||||||
|
"blksize":131072,
|
||||||
|
"omit":0,
|
||||||
|
"duration":10,
|
||||||
|
"bytes":0,
|
||||||
|
"blocks":0,
|
||||||
|
"reverse":0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"intervals":[
|
||||||
|
{
|
||||||
|
"streams":[
|
||||||
|
{
|
||||||
|
"socket":4,
|
||||||
|
"start":0,
|
||||||
|
"end":1.000554,
|
||||||
|
"seconds":1.000554,
|
||||||
|
"bytes":1635647488,
|
||||||
|
"bits_per_second":1.307794e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sum":{
|
||||||
|
"start":0,
|
||||||
|
"end":1.000554,
|
||||||
|
"seconds":1.000554,
|
||||||
|
"bytes":1635647488,
|
||||||
|
"bits_per_second":1.307794e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"streams":[
|
||||||
|
{
|
||||||
|
"socket":4,
|
||||||
|
"start":1.000554,
|
||||||
|
"end":2.000056,
|
||||||
|
"seconds":0.999502,
|
||||||
|
"bytes":1527250944,
|
||||||
|
"bits_per_second":1.222409e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sum":{
|
||||||
|
"start":1.000554,
|
||||||
|
"end":2.000056,
|
||||||
|
"seconds":0.999502,
|
||||||
|
"bytes":1527250944,
|
||||||
|
"bits_per_second":1.222409e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"streams":[
|
||||||
|
{
|
||||||
|
"socket":4,
|
||||||
|
"start":2.000056,
|
||||||
|
"end":3.000353,
|
||||||
|
"seconds":1.000297,
|
||||||
|
"bytes":1712062464,
|
||||||
|
"bits_per_second":1.369244e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sum":{
|
||||||
|
"start":2.000056,
|
||||||
|
"end":3.000353,
|
||||||
|
"seconds":1.000297,
|
||||||
|
"bytes":1712062464,
|
||||||
|
"bits_per_second":1.369244e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"streams":[
|
||||||
|
{
|
||||||
|
"socket":4,
|
||||||
|
"start":3.000353,
|
||||||
|
"end":4.000669,
|
||||||
|
"seconds":1.000316,
|
||||||
|
"bytes":1730412544,
|
||||||
|
"bits_per_second":1.383893e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sum":{
|
||||||
|
"start":3.000353,
|
||||||
|
"end":4.000669,
|
||||||
|
"seconds":1.000316,
|
||||||
|
"bytes":1730412544,
|
||||||
|
"bits_per_second":1.383893e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"streams":[
|
||||||
|
{
|
||||||
|
"socket":4,
|
||||||
|
"start":4.000669,
|
||||||
|
"end":5.000021,
|
||||||
|
"seconds":0.999352,
|
||||||
|
"bytes":1777467392,
|
||||||
|
"bits_per_second":1.422896e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sum":{
|
||||||
|
"start":4.000669,
|
||||||
|
"end":5.000021,
|
||||||
|
"seconds":0.999352,
|
||||||
|
"bytes":1777467392,
|
||||||
|
"bits_per_second":1.422896e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"streams":[
|
||||||
|
{
|
||||||
|
"socket":4,
|
||||||
|
"start":5.000021,
|
||||||
|
"end":6.000122,
|
||||||
|
"seconds":1.000101,
|
||||||
|
"bytes":1673920512,
|
||||||
|
"bits_per_second":1.339001e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sum":{
|
||||||
|
"start":5.000021,
|
||||||
|
"end":6.000122,
|
||||||
|
"seconds":1.000101,
|
||||||
|
"bytes":1673920512,
|
||||||
|
"bits_per_second":1.339001e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"streams":[
|
||||||
|
{
|
||||||
|
"socket":4,
|
||||||
|
"start":6.000122,
|
||||||
|
"end":7.000517,
|
||||||
|
"seconds":1.000395,
|
||||||
|
"bytes":1585446912,
|
||||||
|
"bits_per_second":1.267857e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sum":{
|
||||||
|
"start":6.000122,
|
||||||
|
"end":7.000517,
|
||||||
|
"seconds":1.000395,
|
||||||
|
"bytes":1585446912,
|
||||||
|
"bits_per_second":1.267857e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"streams":[
|
||||||
|
{
|
||||||
|
"socket":4,
|
||||||
|
"start":7.000517,
|
||||||
|
"end":8.000053,
|
||||||
|
"seconds":0.999536,
|
||||||
|
"bytes":1642987520,
|
||||||
|
"bits_per_second":1.315000e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sum":{
|
||||||
|
"start":7.000517,
|
||||||
|
"end":8.000053,
|
||||||
|
"seconds":0.999536,
|
||||||
|
"bytes":1642987520,
|
||||||
|
"bits_per_second":1.315000e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"streams":[
|
||||||
|
{
|
||||||
|
"socket":4,
|
||||||
|
"start":8.000053,
|
||||||
|
"end":9.000105,
|
||||||
|
"seconds":1.000052,
|
||||||
|
"bytes":1547173888,
|
||||||
|
"bits_per_second":1.237675e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sum":{
|
||||||
|
"start":8.000053,
|
||||||
|
"end":9.000105,
|
||||||
|
"seconds":1.000052,
|
||||||
|
"bytes":1547173888,
|
||||||
|
"bits_per_second":1.237675e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"streams":[
|
||||||
|
{
|
||||||
|
"socket":4,
|
||||||
|
"start":9.000105,
|
||||||
|
"end":10.000386,
|
||||||
|
"seconds":1.000281,
|
||||||
|
"bytes":1773142016,
|
||||||
|
"bits_per_second":1.418115e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sum":{
|
||||||
|
"start":9.000105,
|
||||||
|
"end":10.000386,
|
||||||
|
"seconds":1.000281,
|
||||||
|
"bytes":1773142016,
|
||||||
|
"bits_per_second":1.418115e+10,
|
||||||
|
"omitted":false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end":{
|
||||||
|
"streams":[
|
||||||
|
{
|
||||||
|
"sender":{
|
||||||
|
"socket":4,
|
||||||
|
"start":0,
|
||||||
|
"end":10.000386,
|
||||||
|
"seconds":10.000386,
|
||||||
|
"bytes":16605511680,
|
||||||
|
"bits_per_second":1.328390e+10
|
||||||
|
},
|
||||||
|
"receiver":{
|
||||||
|
"socket":4,
|
||||||
|
"start":0,
|
||||||
|
"end":10.000386,
|
||||||
|
"seconds":10.000386,
|
||||||
|
"bytes":16605511680,
|
||||||
|
"bits_per_second":1.328390e+10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sum_sent":{
|
||||||
|
"start":0,
|
||||||
|
"end":10.000386,
|
||||||
|
"seconds":10.000386,
|
||||||
|
"bytes":16605511680,
|
||||||
|
"bits_per_second":1.328390e+10
|
||||||
|
},
|
||||||
|
"sum_received":{
|
||||||
|
"start":0,
|
||||||
|
"end":10.000386,
|
||||||
|
"seconds":10.000386,
|
||||||
|
"bytes":16605511680,
|
||||||
|
"bits_per_second":1.328390e+10
|
||||||
|
},
|
||||||
|
"cpu_utilization_percent":{
|
||||||
|
"host_total":83.983525,
|
||||||
|
"host_user":5.155306,
|
||||||
|
"host_system":78.828219,
|
||||||
|
"remote_total":34.722681,
|
||||||
|
"remote_user":7.224950,
|
||||||
|
"remote_system":27.497731
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,23 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/BGrewell/go-iperf"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
$END$
|
s := iperf.NewServer()
|
||||||
|
err := s.Start()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("failed to start server")
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
for s.Running {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("server has exited")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue