initial working version

main
Ben Grewell 4 years ago
parent e8dc386ba0
commit 10cf19535e

@ -2,17 +2,22 @@
<project version="4"> <project version="4">
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="fc2840de-29dc-4fca-8e0e-a283562f60ca" name="Default Changelist" comment=""> <list default="true" id="fc2840de-29dc-4fca-8e0e-a283562f60ca" name="Default Changelist" comment="">
<change afterPath="$PROJECT_DIR$/client.go" afterDir="false" /> <change afterPath="$PROJECT_DIR$/reporter.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/execute.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/protocol.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/server.go" afterDir="false" />
<change afterPath="$PROJECT_DIR$/shared.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/bindata.go" beforeDir="false" afterPath="$PROJECT_DIR$/bindata.go" afterDir="false" /> <change beforePath="$PROJECT_DIR$/bindata.go" beforeDir="false" afterPath="$PROJECT_DIR$/bindata.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/client.go" beforeDir="false" afterPath="$PROJECT_DIR$/client.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/cmd/main.go" beforeDir="false" afterPath="$PROJECT_DIR$/cmd/main.go" afterDir="false" /> <change beforePath="$PROJECT_DIR$/cmd/main.go" beforeDir="false" afterPath="$PROJECT_DIR$/cmd/main.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/execute.go" beforeDir="false" afterPath="$PROJECT_DIR$/execute.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/go.mod" beforeDir="false" afterPath="$PROJECT_DIR$/go.mod" afterDir="false" /> <change beforePath="$PROJECT_DIR$/go.mod" beforeDir="false" afterPath="$PROJECT_DIR$/go.mod" afterDir="false" />
<change beforePath="$PROJECT_DIR$/go.sum" beforeDir="false" afterPath="$PROJECT_DIR$/go.sum" afterDir="false" />
<change beforePath="$PROJECT_DIR$/iperf.go" beforeDir="false" afterPath="$PROJECT_DIR$/iperf.go" afterDir="false" /> <change beforePath="$PROJECT_DIR$/iperf.go" beforeDir="false" afterPath="$PROJECT_DIR$/iperf.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/protocol.go" beforeDir="false" afterPath="$PROJECT_DIR$/protocol.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/report.go" beforeDir="false" afterPath="$PROJECT_DIR$/report.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/sample.json" beforeDir="false" afterPath="$PROJECT_DIR$/sample.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/server.go" beforeDir="false" afterPath="$PROJECT_DIR$/server.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/shared.go" beforeDir="false" afterPath="$PROJECT_DIR$/shared.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/client/client.go" beforeDir="false" afterPath="$PROJECT_DIR$/tests/client/client.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/tests/server/server.go" beforeDir="false" afterPath="$PROJECT_DIR$/tests/server/server.go" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -42,11 +47,12 @@
<component name="PropertiesComponent"> <component name="PropertiesComponent">
<property name="DefaultGoTemplateProperty" value="Go File" /> <property name="DefaultGoTemplateProperty" value="Go File" />
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" /> <property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" /> <property name="WebServerToolWindowFactoryState" value="false" />
<property name="go.import.settings.migrated" value="true" /> <property name="go.import.settings.migrated" value="true" />
<property name="go.sdk.automatically.set" value="true" /> <property name="go.sdk.automatically.set" value="true" />
<property name="go.tried.to.enable.integration.vgo.integrator" value="true" /> <property name="go.tried.to.enable.integration.vgo.integrator" value="true" />
<property name="last_opened_file_path" value="$PROJECT_DIR$/embedded" /> <property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" /> <property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
<property name="nodejs_npm_path_reset_for_default_project" value="true" /> <property name="nodejs_npm_path_reset_for_default_project" value="true" />
</component> </component>
@ -54,8 +60,11 @@
<key name="CopyFile.RECENT_KEYS"> <key name="CopyFile.RECENT_KEYS">
<recent name="C:\Users\BGrewell\repos\go-iperf\embedded" /> <recent name="C:\Users\BGrewell\repos\go-iperf\embedded" />
</key> </key>
<key name="MoveFile.RECENT_KEYS">
<recent name="C:\Users\BGrewell\source\repos\go-iperf\tests\server" />
</key>
</component> </component>
<component name="RunManager"> <component name="RunManager" selected="Go Build.go build github.com/BGrewell/go-iperf/tests/client">
<configuration name="go build github.com/BGrewell/go-iperf/cmd" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true" nameIsGenerated="true"> <configuration name="go build github.com/BGrewell/go-iperf/cmd" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true" nameIsGenerated="true">
<module name="go-iperf" /> <module name="go-iperf" />
<working_directory value="$PROJECT_DIR$" /> <working_directory value="$PROJECT_DIR$" />
@ -65,43 +74,58 @@
<directory value="$PROJECT_DIR$" /> <directory value="$PROJECT_DIR$" />
<method v="2" /> <method v="2" />
</configuration> </configuration>
<configuration name="go build github.com/BGrewell/go-iperf/tests/client" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true" nameIsGenerated="true">
<module name="go-iperf" />
<working_directory value="$PROJECT_DIR$" />
<kind value="PACKAGE" />
<filePath value="$PROJECT_DIR$/tests/client/client.go" />
<package value="github.com/BGrewell/go-iperf/tests/client" />
<directory value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
<configuration name="go build github.com/BGrewell/go-iperf/tests/server" type="GoApplicationRunConfiguration" factoryName="Go Application" temporary="true" nameIsGenerated="true">
<module name="go-iperf" />
<working_directory value="$PROJECT_DIR$" />
<kind value="PACKAGE" />
<filePath value="$PROJECT_DIR$/tests/server/server.go" />
<package value="github.com/BGrewell/go-iperf/tests/server" />
<directory value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
<recent_temporary> <recent_temporary>
<list> <list>
<item itemvalue="Go Build.go build github.com/BGrewell/go-iperf/tests/client" />
<item itemvalue="Go Build.go build github.com/BGrewell/go-iperf/tests/server" />
<item itemvalue="Go Build.go build github.com/BGrewell/go-iperf/cmd" /> <item itemvalue="Go Build.go build github.com/BGrewell/go-iperf/cmd" />
</list> </list>
</recent_temporary> </recent_temporary>
</component> </component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" /> <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="fc2840de-29dc-4fca-8e0e-a283562f60ca" name="Default Changelist" comment="" />
<created>1614708953320</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1614708953320</updated>
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" /> <option name="version" value="3" />
</component> </component>
<component name="VgoProject"> <component name="VgoProject">
<integration-enabled>true</integration-enabled> <integration-enabled>true</integration-enabled>
</component> </component>
<component name="WindowStateProjectService"> <component name="XDebuggerManager">
<state x="652" y="533" key="#Go_Modules" timestamp="1608060904277"> <breakpoint-manager>
<screen x="0" y="0" width="3440" height="1400" /> <breakpoints>
</state> <line-breakpoint enabled="true" type="DlvLineBreakpoint">
<state x="652" y="533" key="#Go_Modules/0.0.3440.1400@0.0.3440.1400" timestamp="1608060904277" /> <url>file://$PROJECT_DIR$/tests/client/client.go</url>
<state width="1676" height="403" key="GridCell.Tab.0.bottom" timestamp="1608066744405"> <line>57</line>
<screen x="0" y="0" width="3440" height="1400" /> <option name="timeStamp" value="5" />
</state> </line-breakpoint>
<state width="1676" height="403" key="GridCell.Tab.0.bottom/0.0.3440.1400@0.0.3440.1400" timestamp="1608066744405" /> </breakpoints>
<state width="1676" height="403" key="GridCell.Tab.0.center" timestamp="1608066744404"> </breakpoint-manager>
<screen x="0" y="0" width="3440" height="1400" />
</state>
<state width="1676" height="403" key="GridCell.Tab.0.center/0.0.3440.1400@0.0.3440.1400" timestamp="1608066744404" />
<state width="1676" height="403" key="GridCell.Tab.0.left" timestamp="1608066744404">
<screen x="0" y="0" width="3440" height="1400" />
</state>
<state width="1676" height="403" key="GridCell.Tab.0.left/0.0.3440.1400@0.0.3440.1400" timestamp="1608066744404" />
<state width="1676" height="403" key="GridCell.Tab.0.right" timestamp="1608066744404">
<screen x="0" y="0" width="3440" height="1400" />
</state>
<state width="1676" height="403" key="GridCell.Tab.0.right/0.0.3440.1400@0.0.3440.1400" timestamp="1608066744404" />
<state x="431" y="297" key="com.intellij.openapi.editor.actions.MultiplePasteAction$ClipboardContentChooser" timestamp="1608062415393">
<screen x="0" y="0" width="3440" height="1400" />
</state>
<state x="431" y="297" key="com.intellij.openapi.editor.actions.MultiplePasteAction$ClipboardContentChooser/0.0.3440.1400@0.0.3440.1400" timestamp="1608062415393" />
</component> </component>
</project> </project>

@ -1,27 +1,42 @@
package iperf package iperf
import "github.com/google/uuid" import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/google/uuid"
"io"
"io/ioutil"
"log"
"os"
"strings"
)
func NewClient() *Client { func NewClient(host string) *Client {
json := true json := true
proto := Protocol(PROTO_TCP) proto := Protocol(PROTO_TCP)
time := 10 time := 10
length := "128KB" length := "128KB"
streams := 1 streams := 1
c := &Client{ c := &Client{
options: &ClientOptions{
JSON: &json, JSON: &json,
Proto: &proto, Proto: &proto,
TimeSec: &time, TimeSec: &time,
Length: &length, Length: &length,
Streams: &streams, Streams: &streams,
Host: &host,
},
} }
c.Id = uuid.New().String() c.Id = uuid.New().String()
c.Done = make(chan bool)
return c return c
} }
type Client struct { type ClientOptions struct {
SharedOptions SharedOptions
Host string Host *string
Proto *Protocol Proto *Protocol
Bandwidth *string Bandwidth *string
TimeSec *int TimeSec *int
@ -40,4 +55,437 @@ type Client struct {
OmitSec *int OmitSec *int
Prefix *string Prefix *string
JSON *bool JSON *bool
LogFile *string
IncludeServer *bool
}
type Client struct {
Id string
Running bool
Done chan bool
options *ClientOptions
exitCode *int
report *TestReport
outputStream io.ReadCloser
errorStream io.ReadCloser
cancel context.CancelFunc
mode TestMode
live bool
reportingChan chan *StreamIntervalReport
reportingFile string
}
func (c *Client) LoadOptionsJSON(jsonStr string) (err error) {
return json.Unmarshal([]byte(jsonStr), c.options)
}
func (c *Client) commandString() (cmd string, err error) {
builder := strings.Builder{}
if c.options.Host == nil || *c.options.Host == "" {
return "", errors.New("unable to execute client. The field 'host' is required")
}
fmt.Fprintf(&builder, "%s -c %s", binaryLocation, c.Host())
if c.options.Proto != nil && *c.options.Proto == PROTO_UDP {
fmt.Fprintf(&builder, " -u")
}
if c.options.Bandwidth != nil {
fmt.Fprintf(&builder, " -b %s", c.Bandwidth())
}
if c.options.TimeSec != nil {
fmt.Fprintf(&builder, " -t %d", c.TimeSec())
}
if c.options.Bytes != nil {
fmt.Fprintf(&builder, " -n %s", c.Bytes())
}
if c.options.BlockCount != nil {
fmt.Fprintf(&builder, " -k %s", c.BlockCount())
}
if c.options.Length != nil {
fmt.Fprintf(&builder, " -l %s", c.Length())
}
if c.options.Streams != nil {
fmt.Fprintf(&builder, " -P %d", c.Streams())
}
if c.options.Reverse != nil && *c.options.Reverse {
builder.WriteString(" -R")
}
if c.options.Window != nil {
fmt.Fprintf(&builder, " -w %s", c.Window())
}
if c.options.MSS != nil {
fmt.Fprintf(&builder, " -M %d", c.MSS())
}
if c.options.NoDelay != nil && *c.options.NoDelay {
builder.WriteString(" -N")
}
if c.options.Version6 != nil && *c.options.Version6 {
builder.WriteString(" -6")
}
if c.options.TOS != nil {
fmt.Fprintf(&builder, " -S %d", c.TOS())
}
if c.options.ZeroCopy != nil && *c.options.ZeroCopy {
builder.WriteString(" -Z")
}
if c.options.OmitSec != nil {
fmt.Fprintf(&builder, " -O %d", c.OmitSec())
}
if c.options.Prefix != nil {
fmt.Fprintf(&builder, " -T %s", c.Prefix())
}
if c.options.LogFile != nil && *c.options.LogFile != "" {
fmt.Fprintf(&builder, " --logfile %s", c.LogFile())
}
if c.options.JSON != nil && *c.options.JSON {
builder.WriteString(" -J")
}
if c.options.IncludeServer != nil && *c.options.IncludeServer {
builder.WriteString(" --get-server-output")
}
return builder.String(), nil
}
func (c *Client) Host() string {
if c.options.Host == nil {
return ""
}
return *c.options.Host
}
func (c *Client) SetHost(host string) {
c.options.Host = &host
}
func (c *Client) Proto() Protocol {
if c.options.Proto == nil {
return PROTO_TCP
}
return *c.options.Proto
}
func (c *Client) SetProto(proto Protocol) {
c.options.Proto = &proto
}
func (c *Client) Bandwidth() string {
if c.options.Bandwidth == nil && c.Proto() == PROTO_TCP {
return "0"
} else if c.options.Bandwidth == nil && c.Proto() == PROTO_UDP {
return "1M"
}
return *c.options.Bandwidth
}
func (c *Client) SetBandwidth(bandwidth string) {
c.options.Bandwidth = &bandwidth
}
func (c *Client) TimeSec() int {
if c.options.TimeSec == nil {
return 10
}
return *c.options.TimeSec
}
func (c *Client) SetTimeSec(timeSec int) {
c.options.TimeSec = &timeSec
}
func (c *Client) Bytes() string {
if c.options.Bytes == nil {
return ""
}
return *c.options.Bytes
}
func (c *Client) SetBytes(bytes string) {
c.options.Bytes = &bytes
}
func (c *Client) BlockCount() string {
if c.options.BlockCount == nil {
return ""
}
return *c.options.BlockCount
}
func (c *Client) SetBlockCount(blockCount string) {
c.options.BlockCount = &blockCount
}
func (c *Client) Length() string {
if c.options.Length == nil {
if c.Proto() == PROTO_UDP {
return "1460"
} else {
return "128K"
}
}
return *c.options.Length
}
func (c *Client) SetLength(length string) {
c.options.Length = &length
}
func (c *Client) Streams() int {
if c.options.Streams == nil {
return 1
}
return *c.options.Streams
}
func (c *Client) SetStreams(streamCount int) {
c.options.Streams = &streamCount
}
func (c *Client) Reverse() bool {
if c.options.Reverse == nil {
return false
}
return *c.options.Reverse
}
func (c *Client) SetReverse(reverse bool) {
c.options.Reverse = &reverse
}
func (c *Client) Window() string {
if c.options.Window == nil {
return ""
}
return *c.options.Window
}
func (c *Client) SetWindow(window string) {
c.options.Window = &window
}
func (c *Client) MSS() int {
if c.options.MSS == nil {
return 1460
}
return *c.options.MSS
}
func (c *Client) SetMSS(mss int) {
c.options.MSS = &mss
}
func (c *Client) NoDelay() bool {
if c.options.NoDelay == nil {
return false
}
return *c.options.NoDelay
}
func (c *Client) SetNoDelay(noDelay bool) {
c.options.NoDelay = &noDelay
}
func (c *Client) Version4() bool {
if c.options.Version6 == nil && c.options.Version4 == nil {
return true
} else if c.options.Version6 != nil && *c.options.Version6 == true {
return false
}
return *c.options.Version4
}
func (c *Client) SetVersion4(set bool) {
c.options.Version4 = &set
}
func (c *Client) Version6() bool {
if c.options.Version6 == nil {
return false
}
return *c.options.Version6
}
func (c *Client) SetVersion6(set bool) {
c.options.Version6 = &set
}
func (c *Client) TOS() int {
if c.options.TOS == nil {
return 0
}
return *c.options.TOS
}
func (c *Client) SetTOS(value int) {
c.options.TOS = &value
}
func (c *Client) ZeroCopy() bool {
if c.options.ZeroCopy == nil {
return false
}
return *c.options.ZeroCopy
}
func (c *Client) SetZeroCopy(set bool) {
c.options.ZeroCopy = &set
}
func (c *Client) OmitSec() int {
if c.options.OmitSec == nil {
return 0
}
return *c.options.OmitSec
}
func (c *Client) SetOmitSec(value int) {
c.options.OmitSec = &value
}
func (c *Client) Prefix() string {
if c.options.Prefix == nil {
return ""
}
return *c.options.Prefix
}
func (c *Client) SetPrefix(prefix string) {
c.options.Prefix = &prefix
}
func (c *Client) LogFile() string {
if c.options.LogFile == nil {
return ""
}
return *c.options.LogFile
}
func (c *Client) SetLogFile(logfile string) {
c.options.LogFile = &logfile
}
func (c *Client) JSON() bool {
if c.options.JSON == nil {
return false
}
return *c.options.JSON
}
func (c *Client) SetJSON(set bool) {
c.options.JSON = &set
}
func (c *Client) IncludeServer() bool {
if c.options.IncludeServer == nil {
return false
}
return *c.options.IncludeServer
}
func (c *Client) SetIncludeServer(set bool) {
c.options.IncludeServer = &set
}
func (c *Client) ExitCode() *int {
return c.exitCode
}
func (c *Client) Report() *TestReport {
return c.report
}
func (c *Client) Mode() TestMode {
return c.mode
}
func (c *Client) SetModeJson() {
c.SetJSON(true)
c.reportingChan = nil
c.reportingFile = ""
}
func (c *Client) SetModeLive() <-chan *StreamIntervalReport {
c.SetJSON(false) // having JSON == true will cause reporting to fail
c.live = true
c.reportingChan = make(chan *StreamIntervalReport, 10000)
f, err := ioutil.TempFile("", "iperf_")
if err != nil {
log.Fatalf("failed to create logfile: %v", err)
}
c.reportingFile = f.Name()
c.SetLogFile(c.reportingFile)
return c.reportingChan
}
func (c *Client) Start() (err error) {
//todo: Need to build the string based on the options above that are set
cmd, err := c.commandString()
if err != nil {
return err
}
fmt.Println(cmd)
var exit chan int
c.outputStream, c.errorStream, exit, c.cancel, err = ExecuteAsyncWithCancel(cmd)
if err != nil {
return err
}
c.Running = true
//go func() {
// ds := DebugScanner{Silent: false}
// ds.Scan(c.outputStream)
//}()
//go func() {
// ds := DebugScanner{Silent: false}
// ds.Scan(c.errorStream)
//}()
go func() {
var reporter *Reporter
if c.live {
reporter = &Reporter{
ReportingChannel: c.reportingChan,
LogFile: c.reportingFile,
}
reporter.Start()
} else {
testOutput, err := ioutil.ReadAll(c.outputStream)
if err != nil {
return
}
c.report, err = Loads(string(testOutput))
}
exitCode := <-exit
c.exitCode = &exitCode
c.Running = false
c.Done <- true
if reporter != nil {
reporter.Stop()
}
}()
return nil
}
func (c *Client) Stop() {
if c.Running && c.cancel != nil {
c.cancel()
os.Remove(c.reportingFile)
c.Done <- true
}
} }

@ -2,13 +2,17 @@ package main
import ( import (
"fmt" "fmt"
"github.com/BGrewell/go-conversions"
"github.com/BGrewell/go-iperf" "github.com/BGrewell/go-iperf"
"time" "time"
) )
func main() { func main() {
s := iperf.NewServer() s := iperf.NewServer()
c := iperf.NewClient() c := iperf.NewClient("127.0.0.1")
includeServer := true
c.IncludeServer = &includeServer
fmt.Println(s.Id) fmt.Println(s.Id)
fmt.Println(c.Id) fmt.Println(c.Id)
@ -16,10 +20,27 @@ func main() {
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }
for s.Running {
err = c.Start()
if err != nil {
fmt.Println(err)
}
for c.Running {
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
} }
fmt.Println("stopping server")
s.Stop()
fmt.Printf("Client exit code: %d\n", *c.ExitCode)
fmt.Printf("Server exit code: %d\n", *s.ExitCode) fmt.Printf("Server exit code: %d\n", *s.ExitCode)
iperf.Cleanup() iperf.Cleanup()
if c.Report.Error != "" {
fmt.Println(c.Report.Error)
} else {
fmt.Printf("Recv Rate: %s\n", conversions.IntBitRateToString(int64(c.Report.End.SumReceived.BitsPerSecond)))
fmt.Printf("Send Rate: %s\n", conversions.IntBitRateToString(int64(c.Report.End.SumSent.BitsPerSecond)))
}
} }

@ -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
} }

@ -2,4 +2,14 @@ module github.com/BGrewell/go-iperf
go 1.15 go 1.15
require github.com/google/uuid v1.1.2 require (
github.com/BGrewell/go-conversions v0.0.0-20201203155646-5e189e4ca087
github.com/BGrewell/go-execute v0.0.0-20201203155726-b7c037ebde49 // indirect
github.com/BGrewell/tail v1.0.0
github.com/fatih/gomodifytags v1.13.0 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/google/uuid v1.1.2
github.com/hpcloud/tail v1.0.0 // indirect
gopkg.in/fsnotify.v1 v1.4.7 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
)

@ -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,40 +1,43 @@
package iperf package iperf
import ( import (
"context"
"fmt" "fmt"
"github.com/google/uuid" "github.com/google/uuid"
"io" "io"
"time"
) )
func NewServer() *Server { func NewServer() *Server {
s := &Server{ s := &Server{}
}
s.Id = uuid.New().String() s.Id = uuid.New().String()
return s return s
} }
type Server struct { type Server struct {
SharedOptions SharedOptions
Id string
OneOff *bool OneOff *bool
ExitCode *int ExitCode *int
Running bool Running bool
outputStream io.ReadCloser outputStream io.ReadCloser
errorStream io.ReadCloser errorStream io.ReadCloser
cancel context.CancelFunc
} }
func (s *Server) Start() (err error) { func (s *Server) Start() (err error) {
exit := make(chan int, 0) var exit chan int
err = Execute(fmt.Sprintf("%s -s", binaryLocation), s.outputStream, s.errorStream, exit) s.outputStream, s.errorStream, exit, s.cancel, err = ExecuteAsyncWithCancel(fmt.Sprintf("%s -s -J", binaryLocation))
if err != nil { if err != nil {
return err return err
} }
s.Running = true s.Running = true
go func() { go func() {
ds := DebugScanner{} ds := DebugScanner{Silent: true}
ds.Scan(s.outputStream) ds.Scan(s.outputStream)
}() }()
go func() { go func() {
ds := DebugScanner{} ds := DebugScanner{Silent: true}
ds.Scan(s.errorStream) ds.Scan(s.errorStream)
}() }()
go func() { go func() {
@ -46,5 +49,8 @@ func (s *Server) Start() (err error) {
} }
func (s *Server) Stop() { func (s *Server) Stop() {
if s.Running && s.cancel != nil {
s.cancel()
time.Sleep(100 * time.Millisecond)
}
} }

@ -6,15 +6,20 @@ import (
"io" "io"
) )
type TestMode string
const (
MODE_JSON TestMode = "json"
MODE_LIVE TestMode = "live"
)
type SharedOptions struct { type SharedOptions struct {
Id string
Port *int Port *int
Format *rune Format *rune
Interval *int Interval *int
} }
type DebugScanner struct { type DebugScanner struct {
Silent bool
} }
func (ds *DebugScanner) Scan(buff io.ReadCloser) { func (ds *DebugScanner) Scan(buff io.ReadCloser) {
@ -26,6 +31,8 @@ func (ds *DebugScanner) Scan(buff io.ReadCloser) {
scanner.Split(bufio.ScanWords) scanner.Split(bufio.ScanWords)
for scanner.Scan() { for scanner.Scan() {
text := scanner.Text() text := scanner.Text()
if !ds.Silent {
fmt.Println(text) fmt.Println(text)
} }
} }
}

@ -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…
Cancel
Save