first commit
commit
9824775015
@ -0,0 +1,19 @@
|
||||
#GOFLAGS=""
|
||||
LDFLAGS=-ldflags "-s -w"
|
||||
BINARY=metrorama
|
||||
|
||||
.DEFAULT_GOAL: $(BINARY)
|
||||
|
||||
$(BINARY): clean
|
||||
go build ${GOFLAGS} ${LDFLAGS} -o ${BINARY}
|
||||
|
||||
install:
|
||||
go install ${GOFLAGS} ${LDFLAGS} -o ${BINARY}
|
||||
|
||||
clean:
|
||||
if [ -f ${BINARY} ]; then rm ${BINARY} ; fi
|
||||
|
||||
run: clean $(BINARY)
|
||||
./$(BINARY)
|
||||
|
||||
.phony: clean install
|
@ -0,0 +1 @@
|
||||
- =apt-get install libstatgrab-dev=
|
@ -0,0 +1,164 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
listen = flag.String("l", "0.0.0.0:8080", "Listen address")
|
||||
rate = flag.Int("r", 500, "Sample rate (ms)")
|
||||
timeout = flag.Int("t", 10, "Total timeout (s)")
|
||||
minRate = 500
|
||||
maxTimeout = 600
|
||||
)
|
||||
|
||||
// TODO: bench test with pointer
|
||||
func getMetrics() (Metric, error) {
|
||||
log.Println("Started getMetrics")
|
||||
var s = Stat{}
|
||||
m := Metric{}
|
||||
cpustats := s.CPUStats()
|
||||
m.Load1m = cpustats.LoadMin1
|
||||
log.Printf("Metric: %v\n", m)
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (t *Test) collectData() {
|
||||
timeo := time.After(t.Timeout)
|
||||
t.Status = "STARTED"
|
||||
for {
|
||||
select {
|
||||
case tick := <-t.ticker.C:
|
||||
t.Status = "ONGOING"
|
||||
|
||||
m, err := getMetrics()
|
||||
if err != nil {
|
||||
log.Printf("Cannot collect metrics: %s\n", err)
|
||||
return
|
||||
}
|
||||
t.Result = append(t.Result, m)
|
||||
log.Printf("%v: %v\n", tick, m)
|
||||
//t.Result += fmt.Sprintf("%v: Collecting data\n", tick)
|
||||
case <-t.Done:
|
||||
log.Printf("DONE\n")
|
||||
t.Status = "COMPLETED"
|
||||
t.Unlock()
|
||||
return
|
||||
case <-timeo:
|
||||
log.Printf("TIMEOUT\n")
|
||||
t.Status = "TIMEOUT"
|
||||
t.Unlock()
|
||||
return
|
||||
}
|
||||
}
|
||||
return // really needed?
|
||||
}
|
||||
|
||||
func (t *Test) startTest(w http.ResponseWriter, req *http.Request) {
|
||||
// Check if another collectData is running (better: check sync.Mutex)
|
||||
if t.Status == "ONGOING" {
|
||||
http.Error(w, "Another test is currently ongoing, stop or try again later", 409) //better error code
|
||||
return
|
||||
}
|
||||
// Check parameters
|
||||
qRate := req.URL.Query().Get("rate")
|
||||
if qRate != "" {
|
||||
qRate, err := strconv.Atoi(qRate)
|
||||
if err != nil {
|
||||
log.Printf("Error while converting rate: %s\n", err.Error())
|
||||
http.Error(w, "Error: error while converting rate", 400)
|
||||
return
|
||||
}
|
||||
if qRate < minRate {
|
||||
log.Printf("Requested rate is too low: %d (min: %d)\n", qRate, minRate)
|
||||
http.Error(w, "Error: requested rate too low", 400)
|
||||
return
|
||||
}
|
||||
t.Rate = qRate
|
||||
}
|
||||
|
||||
qTimeout := req.URL.Query().Get("timeout")
|
||||
if qTimeout != "" {
|
||||
qTimeout, err := strconv.Atoi(qTimeout)
|
||||
if err != nil {
|
||||
log.Printf("Error while converting timeout: %s\n", err.Error())
|
||||
http.Error(w, "Error: error while converting timeout", 400)
|
||||
return
|
||||
}
|
||||
if qTimeout > maxTimeout {
|
||||
log.Printf("Requested timeout is too high: %d (max: %d)\n", qTimeout, maxTimeout)
|
||||
http.Error(w, "Error: requested timeout too high", 400)
|
||||
return
|
||||
}
|
||||
t.Timeout = time.Duration(qTimeout) * time.Second
|
||||
}
|
||||
|
||||
qName := req.URL.Query().Get("name")
|
||||
if qName == "" {
|
||||
t.Name = fmt.Sprintf("test-%d", time.Now().Local().Unix())
|
||||
} else {
|
||||
t.Name = qName
|
||||
}
|
||||
// Check if previous test is present
|
||||
if len(t.Result) == 0 {
|
||||
log.Println("Cleaning previous test data")
|
||||
t.Result = []Metric{}
|
||||
}
|
||||
// Lock to avoid multiple start
|
||||
t.Lock()
|
||||
fmt.Fprintf(w, "Test name: %s, rate: %d (ms), timeout: %d (s)\n", t.Name, t.Rate, t.Timeout/1000000000)
|
||||
endTime := time.Now().Local().Add(t.Timeout)
|
||||
fmt.Fprintf(w, "Test will end @%v\n", endTime.Format(time.RFC1123Z))
|
||||
go t.collectData()
|
||||
}
|
||||
|
||||
func (t *Test) stopTest(w http.ResponseWriter, req *http.Request) {
|
||||
if t.Status != "ONGOING" {
|
||||
e := fmt.Sprintf("Cannot stop test in %s status: try start first", t.Status)
|
||||
http.Error(w, e, 400)
|
||||
return
|
||||
}
|
||||
|
||||
t.ticker.Stop()
|
||||
t.Done <- true
|
||||
fmt.Fprintf(w, "Done\n")
|
||||
fmt.Fprintf(w, "%v\n", t.Result)
|
||||
}
|
||||
|
||||
func (t *Test) testStatus(w http.ResponseWriter, req *http.Request) {
|
||||
fmt.Fprintf(w, "%v\n", t.Result)
|
||||
fmt.Fprintf(w, "Test %s, status: %s\n", t.Name, t.Status)
|
||||
}
|
||||
|
||||
func newTest() *Test {
|
||||
log.Println("Creating new test")
|
||||
// Not checking if rate and timeout are in value range because are set on CLI
|
||||
t := Test{
|
||||
ticker: time.NewTicker(time.Duration(*rate) * time.Millisecond),
|
||||
Done: make(chan bool),
|
||||
Rate: *rate,
|
||||
Timeout: time.Duration(*timeout) * time.Second,
|
||||
Status: "CREATED",
|
||||
}
|
||||
return &t
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
flag.Parse()
|
||||
|
||||
t := newTest()
|
||||
log.Printf("%+v\n", t)
|
||||
|
||||
http.HandleFunc("/start", t.startTest)
|
||||
http.HandleFunc("/status", t.testStatus)
|
||||
http.HandleFunc("/stop", t.stopTest)
|
||||
|
||||
log.Printf("Listening on %s\n", *listen)
|
||||
log.Fatal(http.ListenAndServe(*listen, nil))
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
// #cgo LDFLAGS: -lstatgrab
|
||||
// #include <statgrab.h>
|
||||
import "C"
|
||||
import "time"
|
||||
|
||||
var mainfunc = make(chan func())
|
||||
|
||||
func do(f func()) {
|
||||
done := make(chan bool, 1)
|
||||
mainfunc <- func() {
|
||||
f()
|
||||
done <- true
|
||||
}
|
||||
<-done
|
||||
}
|
||||
|
||||
func (s *Stat) CPUStats() *CPUStats {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
var cpu *CPUStats
|
||||
|
||||
do(func() {
|
||||
cpup := C.sg_get_cpu_percents_of(C.sg_new_diff_cpu_percent, nil)
|
||||
loadStat := C.sg_get_load_stats(nil)
|
||||
cpu = &CPUStats{
|
||||
User: float64(cpup.user),
|
||||
Kernel: float64(cpup.kernel),
|
||||
Idle: float64(cpup.idle),
|
||||
IOWait: float64(cpup.iowait),
|
||||
Swap: float64(cpup.swap),
|
||||
Nice: float64(cpup.nice),
|
||||
LoadMin1: float64(loadStat.min1),
|
||||
LoadMin5: float64(loadStat.min5),
|
||||
LoadMin15: float64(loadStat.min15),
|
||||
Period: time.Duration(int(cpup.time_taken)) * time.Second,
|
||||
TimeTaken: time.Now(),
|
||||
}
|
||||
})
|
||||
return cpu
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Test struct {
|
||||
sync.Mutex
|
||||
ticker *time.Ticker
|
||||
Status string
|
||||
Done chan bool
|
||||
Rate int
|
||||
Name string
|
||||
Result []Metric
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
type Metric struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Load1m float64
|
||||
Load5m float64
|
||||
Load15m float64
|
||||
FreeMem int
|
||||
}
|
||||
|
||||
type CPUStats struct {
|
||||
User float64
|
||||
Kernel float64
|
||||
Idle float64
|
||||
IOWait float64
|
||||
Swap float64
|
||||
Nice float64
|
||||
LoadMin1 float64
|
||||
LoadMin5 float64
|
||||
LoadMin15 float64
|
||||
Period time.Duration
|
||||
TimeTaken time.Time
|
||||
}
|
||||
|
||||
type Stat struct {
|
||||
sync.Mutex
|
||||
exitMessage chan bool
|
||||
}
|
Loading…
Reference in New Issue