add control wrapper
This commit is contained in:
15
.idea/workspace.xml
generated
15
.idea/workspace.xml
generated
@@ -2,7 +2,20 @@
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<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_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>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@@ -37,7 +50,7 @@
|
||||
<property name="go.import.settings.migrated" value="true" />
|
||||
<property name="go.sdk.automatically.set" 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_npm_path_reset_for_default_project" value="true" />
|
||||
</component>
|
||||
|
||||
77
README.md
77
README.md
@@ -1,6 +1,81 @@
|
||||
# go-iperf
|
||||
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/```
|
||||
|
||||
21
api/Dockerfile
Normal file
21
api/Dockerfile
Normal file
@@ -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
|
||||
22
api/build.sh
Normal file
22
api/build.sh
Normal file
@@ -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"
|
||||
27
api/proto/control.proto
Normal file
27
api/proto/control.proto
Normal file
@@ -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 {
|
||||
Id string `json:"id" yaml:"id" xml:"id"`
|
||||
Running bool `json:"running" yaml:"running" xml:"running"`
|
||||
Done chan bool `json:"-" yaml:"-" xml:"-"`
|
||||
Id string `json:"id" yaml:"id" xml:"id"`
|
||||
Running bool `json:"running" yaml:"running" xml:"running"`
|
||||
Done chan bool `json:"-" yaml:"-" xml:"-"`
|
||||
Options *ClientOptions `json:"options" yaml:"options" xml:"options"`
|
||||
exitCode *int
|
||||
report *TestReport
|
||||
@@ -487,7 +487,6 @@ func (c *Client) SetModeLive() <-chan *StreamIntervalReport {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
26
controller.go
Normal file
26
controller.go
Normal file
@@ -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
|
||||
LogFile string
|
||||
running bool
|
||||
tailer *tail.Tail
|
||||
tailer *tail.Tail
|
||||
}
|
||||
|
||||
func (r *Reporter) Start() {
|
||||
|
||||
@@ -59,70 +59,70 @@ func (r *Reporter) runLogProcessor() {
|
||||
|
||||
for {
|
||||
select {
|
||||
case line := <- r.tailer.Lines:
|
||||
if line == nil {
|
||||
case line := <-r.tailer.Lines:
|
||||
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
|
||||
}
|
||||
if len(line.Text) > 5 {
|
||||
id := line.Text[1:4]
|
||||
stream, err := strconv.Atoi(strings.TrimSpace(id))
|
||||
if err != nil {
|
||||
fields := strings.Fields(line.Text[5:])
|
||||
if len(fields) >= 9 {
|
||||
if fields[0] == "local" {
|
||||
continue
|
||||
}
|
||||
fields := strings.Fields(line.Text[5:])
|
||||
if len(fields) >= 9 {
|
||||
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)
|
||||
}
|
||||
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
|
||||
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
|
||||
}
|
||||
case <- time.After(100 * time.Millisecond):
|
||||
if !r.running {
|
||||
return
|
||||
}
|
||||
}
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
if !r.running {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ func (r *Reporter) runLogProcessor() {
|
||||
|
||||
for {
|
||||
select {
|
||||
case line := <- r.tailer.Lines:
|
||||
case line := <-r.tailer.Lines:
|
||||
if line == nil {
|
||||
continue
|
||||
}
|
||||
@@ -72,7 +72,7 @@ func (r *Reporter) runLogProcessor() {
|
||||
r.ReportingChannel <- report
|
||||
}
|
||||
}
|
||||
case <- time.After(100 * time.Millisecond):
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
if !r.running {
|
||||
return
|
||||
}
|
||||
|
||||
142
server.go
142
server.go
@@ -2,41 +2,159 @@ package iperf
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultPort = 5201
|
||||
defaultInterval = 1
|
||||
defaultJSON = true
|
||||
)
|
||||
|
||||
func NewServer() *Server {
|
||||
defaultPort := 5201
|
||||
defaultInterval := 1
|
||||
s := &Server{
|
||||
Port: &defaultPort,
|
||||
Interval: &defaultInterval,
|
||||
Options: &ServerOptions{
|
||||
Port: &defaultPort,
|
||||
Interval: &defaultInterval,
|
||||
JSON: &defaultJSON,
|
||||
},
|
||||
}
|
||||
s.Id = uuid.New().String()
|
||||
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 {
|
||||
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"`
|
||||
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"`
|
||||
errorStream io.ReadCloser `json:"error_stream" yaml:"error_stream" xml:"error_stream"`
|
||||
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) {
|
||||
cmd, err := s.commandString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user