add control wrapper

main
Ben Grewell 4 years ago
parent 1dd7340d4b
commit ab0c867aa1

@ -2,7 +2,20 @@
<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$/api/Dockerfile" afterDir="false" />
<change afterPath="$PROJECT_DIR$/api/build.sh" afterDir="false" />
<change afterPath="$PROJECT_DIR$/api/proto/control.proto" afterDir="false" />
<change afterPath="$PROJECT_DIR$/controller.go" 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$/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$/reporter.go" beforeDir="false" afterPath="$PROJECT_DIR$/reporter.go" afterDir="false" /> <change beforePath="$PROJECT_DIR$/reporter.go" beforeDir="false" afterPath="$PROJECT_DIR$/reporter.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/reporter_linux.go" beforeDir="false" afterPath="$PROJECT_DIR$/reporter_linux.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/reporter_windows.go" beforeDir="false" afterPath="$PROJECT_DIR$/reporter_windows.go" 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" />
</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" />
@ -37,7 +50,7 @@
<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$" /> <property name="last_opened_file_path" value="$PROJECT_DIR$/../ran_onf/onos-ric-master/cmd/apps/onos-ric-mlb/onos-ran-mlb.go" />
<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>

@ -1,6 +1,81 @@
# go-iperf # go-iperf
A Go based wrapper around iperf3 A Go based wrapper around iperf3
## Basic Usage
basic client setup
```go
func main() {
c := iperf.NewClient("192.168.0.10")
c.SetJSON(true)
c.SetIncludeServer(true)
c.SetStreams(4)
c.SetTimeSec(30)
c.SetInterval(1)
err := c.Start()
if err != nil {
fmt.Printf("failed to start client: %v\n", err)
os.Exit(-1)
}
<- c.Done
fmt.Println(c.Report().String())
}
```
basic server setup
```go
func main() {
s := iperf.NewServer()
err := s.Start()
if err != nil {
fmt.Printf("failed to start server: %v\n", err)
os.Exit(-1)
}
for s.Running() {
time.Sleep(100 * time.Millisecond)
}
fmt.Println("server finished")
}
```
client with live results printing
```go
func main() {
c := iperf.NewClient("192.168.0.10")
c.SetJSON(true)
c.SetIncludeServer(true)
c.SetStreams(4)
c.SetTimeSec(30)
c.SetInterval(1)
liveReports := c.SetModeLive()
go func() {
for report := range liveReports {
fmt.Println(report.String())
}
}
err := c.Start()
if err != nil {
fmt.Printf("failed to start client: %v\n", err)
os.Exit(-1)
}
<- c.Done
fmt.Println(c.Report().String())
}
```
building binary data package with iperf binaries
```
go-bindata -pkg iperf -prefix "embedded/" embedded/
``` ```
go-bindata -pkg iperf -prefix "embedded/" embedded/```

@ -0,0 +1,21 @@
# Dockerfile.protogen
FROM golang:latest
LABEL maintainer="Benjamin Grewell <bgrewell@gmail.com>"
ENV PROTOC_VERSION 3.6.1
ENV PROTOC_GEN_GO_VERSION v1.2.0
WORKDIR /go/src/github.com/BGrewell/go-iperf/api/proto
RUN apt update
RUN apt install -y protobuf-compiler python3 python3-pip
RUN go get -u github.com/golang/protobuf/protoc-gen-go
RUN pip3 install grpcio-tools
RUN export PATH=$PATH:$GOPATH/bin
RUN echo $PATH
COPY proto/control.proto control.proto
RUN mkdir go
RUN protoc -I /. --go_out=plugins=grpc:go control.proto

@ -0,0 +1,22 @@
#!/usr/bin/env bash
if [ ! -d go ]; then
echo "[!] Creating go output directory"
mkdir go;
fi
echo "[+] Building docker container"
docker image build -t go-iperf-builder:1.0 .
docker container run --detach --name builder go-iperf-builder:1.0
docker cp grpc:/go/src/github.com/BGrewell/go-iperf/api/go/api.pb.go go/.
echo "[+] Updating of go library complete"
echo "[+] Removing docker container"
docker rm builder
echo "[+] Adding new files to source control"
git add go/api.pb.go
git commit -m "regenerated grpc libraries"
git push
echo "[+] Done. Everything has been rebuilt and the repository has been updated and pushed"

@ -0,0 +1,27 @@
syntax = "proto3";
import "google/protobuf/empty.proto";
// [START java_declaration]
option java_multiple_files = true;
option java_package = "com.bengrewell.go-iperf.control";
option java_outer_classname = "Control";
// [END java_declaration]
// [START csharp_declaration]
option csharp_namespace = "BenGrewell.GoIperf.Control";
// [END csharp_declaration]
package api;
service Command {
rpc StartServer(StartServerRequest) returns (StartServerResponse) {}
}
message StartServerRequest {
}
message StartServerResponse {
string id = 1;
int32 listen_port = 2;
}

@ -62,9 +62,9 @@ type ClientOptions struct {
} }
type Client struct { type Client struct {
Id string `json:"id" yaml:"id" xml:"id"` Id string `json:"id" yaml:"id" xml:"id"`
Running bool `json:"running" yaml:"running" xml:"running"` Running bool `json:"running" yaml:"running" xml:"running"`
Done chan bool `json:"-" yaml:"-" xml:"-"` Done chan bool `json:"-" yaml:"-" xml:"-"`
Options *ClientOptions `json:"options" yaml:"options" xml:"options"` Options *ClientOptions `json:"options" yaml:"options" xml:"options"`
exitCode *int exitCode *int
report *TestReport report *TestReport
@ -487,7 +487,6 @@ func (c *Client) SetModeLive() <-chan *StreamIntervalReport {
} }
func (c *Client) Start() (err error) { func (c *Client) Start() (err error) {
//todo: Need to build the string based on the Options above that are set
cmd, err := c.commandString() cmd, err := c.commandString()
if err != nil { if err != nil {
return err return err

@ -0,0 +1,26 @@
package iperf
type Controller struct {
clients map[string]*Client
servers map[string]*Server
}
func (c *Controller) StartListener() (err error) {
}
func (c *Controller) StopListener() (err error) {
}
func (c *Controller) StopServer(id string) (err error) {
}
func (c *Controller) NewServer() (server *Server, err error) {
}
func (c *Controller) NewClient(serverAddr string) (client *Client, err error) {
}

@ -9,7 +9,7 @@ type Reporter struct {
ReportingChannel chan *StreamIntervalReport ReportingChannel chan *StreamIntervalReport
LogFile string LogFile string
running bool running bool
tailer *tail.Tail tailer *tail.Tail
} }
func (r *Reporter) Start() { func (r *Reporter) Start() {

@ -59,70 +59,70 @@ func (r *Reporter) runLogProcessor() {
for { for {
select { select {
case line := <- r.tailer.Lines: case line := <-r.tailer.Lines:
if line == nil { if line == nil {
continue
}
if len(line.Text) > 5 {
id := line.Text[1:4]
stream, err := strconv.Atoi(strings.TrimSpace(id))
if err != nil {
continue continue
} }
if len(line.Text) > 5 { fields := strings.Fields(line.Text[5:])
id := line.Text[1:4] if len(fields) >= 9 {
stream, err := strconv.Atoi(strings.TrimSpace(id)) if fields[0] == "local" {
if err != nil {
continue continue
} }
fields := strings.Fields(line.Text[5:]) timeFields := strings.Split(fields[0], "-")
if len(fields) >= 9 { start, err := strconv.ParseFloat(timeFields[0], 32)
if fields[0] == "local" { if err != nil {
continue log.Printf("failed to convert start time: %s\n", err)
}
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)
}
retrans, err := strconv.Atoi(fields[6])
if err != nil {
log.Printf("failed to convert units: %s\n", err)
}
cwndStr := fmt.Sprintf("%s%s", fields[7], fields[8])
cwnd, err := conversions.StringBitRateToInt(cwndStr)
if err != nil {
log.Printf("failed to convert units: %s\n", err)
}
cwnd = cwnd / 8
omitted := false
if len(fields) >= 10 && fields[9] == "(omitted)" {
omitted = true
}
report := &StreamIntervalReport{
Socket: stream,
StartInterval: float32(start),
EndInterval: float32(end),
Seconds: float32(end - start),
Bytes: int(transferedBytes),
BitsPerSecond: float64(rate),
Retransmissions: retrans,
CongestionWindow: int(cwnd),
Omitted: omitted,
}
r.ReportingChannel <- report
} }
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)
}
retrans, err := strconv.Atoi(fields[6])
if err != nil {
log.Printf("failed to convert units: %s\n", err)
}
cwndStr := fmt.Sprintf("%s%s", fields[7], fields[8])
cwnd, err := conversions.StringBitRateToInt(cwndStr)
if err != nil {
log.Printf("failed to convert units: %s\n", err)
}
cwnd = cwnd / 8
omitted := false
if len(fields) >= 10 && fields[9] == "(omitted)" {
omitted = true
}
report := &StreamIntervalReport{
Socket: stream,
StartInterval: float32(start),
EndInterval: float32(end),
Seconds: float32(end - start),
Bytes: int(transferedBytes),
BitsPerSecond: float64(rate),
Retransmissions: retrans,
CongestionWindow: int(cwnd),
Omitted: omitted,
}
r.ReportingChannel <- report
} }
case <- time.After(100 * time.Millisecond): }
if !r.running { case <-time.After(100 * time.Millisecond):
return if !r.running {
} return
}
} }
} }
} }

@ -24,7 +24,7 @@ func (r *Reporter) runLogProcessor() {
for { for {
select { select {
case line := <- r.tailer.Lines: case line := <-r.tailer.Lines:
if line == nil { if line == nil {
continue continue
} }
@ -72,7 +72,7 @@ func (r *Reporter) runLogProcessor() {
r.ReportingChannel <- report r.ReportingChannel <- report
} }
} }
case <- time.After(100 * time.Millisecond): case <-time.After(100 * time.Millisecond):
if !r.running { if !r.running {
return return
} }

@ -2,41 +2,159 @@ package iperf
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"github.com/google/uuid" "github.com/google/uuid"
"io" "io"
"strings"
"time" "time"
) )
var (
defaultPort = 5201
defaultInterval = 1
defaultJSON = true
)
func NewServer() *Server { func NewServer() *Server {
defaultPort := 5201
defaultInterval := 1
s := &Server{ s := &Server{
Port: &defaultPort, Options: &ServerOptions{
Interval: &defaultInterval, Port: &defaultPort,
Interval: &defaultInterval,
JSON: &defaultJSON,
},
} }
s.Id = uuid.New().String() s.Id = uuid.New().String()
return s return s
} }
type ServerOptions struct {
OneOff *bool `json:"one_off" yaml:"one_off" xml:"one_off"`
Port *int `json:"port" yaml:"port" xml:"port"`
Format *rune `json:"format" yaml:"format" xml:"format"`
Interval *int `json:"interval" yaml:"interval" xml:"interval"`
JSON *bool `json:"json" yaml:"json" xml:"json"`
LogFile *string `json:"log_file" yaml:"log_file" xml:"log_file"`
}
type Server struct { type Server struct {
Id string `json:"id" yaml:"id" xml:"id"` Id string `json:"id" yaml:"id" xml:"id"`
OneOff *bool `json:"one_off" yaml:"one_off" xml:"one_off"`
ExitCode *int `json:"exit_code" yaml:"exit_code" xml:"exit_code"`
Port *int `json:"port" yaml:"port" xml:"port"`
Format *rune `json:"format" yaml:"format" xml:"format"`
Interval *int `json:"interval" yaml:"interval" xml:"interval"`
JSON *bool `json:"json" yaml:"json" xml:"json"`
LogFile *string `json:"log_file" yaml:"log_file" xml:"log_file"`
Running bool `json:"running" yaml:"running" xml:"running"` Running bool `json:"running" yaml:"running" xml:"running"`
Options *ServerOptions `json:"-" yaml:"-" xml:"-"`
ExitCode *int `json:"exit_code" yaml:"exit_code" xml:"exit_code"`
outputStream io.ReadCloser `json:"output_stream" yaml:"output_stream" xml:"output_stream"` outputStream io.ReadCloser `json:"output_stream" yaml:"output_stream" xml:"output_stream"`
errorStream io.ReadCloser `json:"error_stream" yaml:"error_stream" xml:"error_stream"` errorStream io.ReadCloser `json:"error_stream" yaml:"error_stream" xml:"error_stream"`
cancel context.CancelFunc `json:"cancel" yaml:"cancel" xml:"cancel"` cancel context.CancelFunc `json:"cancel" yaml:"cancel" xml:"cancel"`
} }
func (s *Server) LoadOptionsJSON(jsonStr string) (err error) {
return json.Unmarshal([]byte(jsonStr), s.Options)
}
func (s *Server) LoadOptions(options *ServerOptions) {
s.Options = options
}
func (s *Server) commandString() (cmd string, err error) {
builder := strings.Builder{}
fmt.Fprintf(&builder, "%s -s", binaryLocation)
if s.Options.OneOff != nil && s.OneOff() == true {
builder.WriteString(" --one-off")
}
if s.Options.Port != nil {
fmt.Fprintf(&builder, " --port %d", s.Port())
}
if s.Options.Format != nil {
fmt.Fprintf(&builder, " --format %c", s.Format())
}
if s.Options.Interval != nil {
fmt.Fprintf(&builder, " --interval %d", s.Interval())
}
if s.Options.JSON != nil && s.JSON() == true {
builder.WriteString(" --json")
}
if s.Options.LogFile != nil && s.LogFile() != "" {
fmt.Fprintf(&builder, " --logfile %s --forceflush", s.LogFile())
}
return builder.String(), nil
}
func (s *Server) OneOff() bool {
if s.Options.OneOff == nil {
return false
}
return *s.Options.OneOff
}
func (s *Server) SetOneOff(oneOff bool) {
s.Options.OneOff = &oneOff
}
func (s *Server) Port() int {
if s.Options.Port == nil {
return defaultPort
}
return *s.Options.Port
}
func (s *Server) SetPort(port int) {
s.Options.Port = &port
}
func (s *Server) Format() rune {
if s.Options.Format == nil {
return ' '
}
return *s.Options.Format
}
func (s *Server) SetFormat(format rune) {
s.Options.Format = &format
}
func (s *Server) Interval() int {
if s.Options.Interval == nil {
return defaultInterval
}
return *s.Options.Interval
}
func (s *Server) JSON() bool {
if s.Options.JSON == nil {
return false
}
return *s.Options.JSON
}
func (s *Server) SetJSON(json bool) {
s.Options.JSON = &json
}
func (s *Server) LogFile() string {
if s.Options.LogFile == nil {
return ""
}
return *s.Options.LogFile
}
func (s *Server) SetLogFile(filename string) {
s.Options.LogFile = &filename
}
func (s *Server) Start() (err error) { func (s *Server) Start() (err error) {
cmd, err := s.commandString()
if err != nil {
return err
}
var exit chan int var exit chan int
s.outputStream, s.errorStream, exit, s.cancel, err = ExecuteAsyncWithCancel(fmt.Sprintf("%s -s -J", binaryLocation)) s.outputStream, s.errorStream, exit, s.cancel, err = ExecuteAsyncWithCancel(cmd)
if err != nil { if err != nil {
return err return err
} }

Loading…
Cancel
Save