Commit a14f824f authored by Manuel Kieweg's avatar Manuel Kieweg 🤷
Browse files

Merge branch 'grafana-setup' into 'main'

Grafana setup

See merge request danet/csbi!8
parents d5302dc1 78a4c2e9
repository-base-path: "./models"
orchestrator-shutown-timeout: "1min"
docker-orchestrator-network: "csbi-dev-net"
\ No newline at end of file
docker-orchestrator-network: "testbed"
\ No newline at end of file
......@@ -4,4 +4,7 @@
/00000000-0000-0000-0000-000000000000
testdata/00000000-0000-0000-0000-000000000000/go.sum
testdata/00000000-0000-0000-0000-000000000000/plugin.so
coverage.out
\ No newline at end of file
coverage.out
.DS_Store
*.log
*.txt
\ No newline at end of file
# syntax = docker/dockerfile:1.2
FROM golang:1.16-alpine AS installer
WORKDIR /build
RUN apk add --no-cache git make build-base
RUN apk add --update --no-cache alpine-sdk
COPY go.mod .
COPY go.sum .
RUN go mod download
FROM installer as builder
COPY . .
RUN --mount=type=cache,target=/root/.cache/go-build \
GOOS=linux go build -o executor ./cmd/executor/executor.go
FROM alpine
COPY --from=builder /build/executor .
COPY --from=builder /build/cmd/executor/experiment.yaml .
ENTRYPOINT [ "./executor" ]
CMD [""]
\ No newline at end of file
arista-exp-eos-vxlan-config
ietf-yang-types
openconfig-aaa
openconfig-igmp-types
openconfig-qos
openconfig-bfd
openconfig-hercules-interfaces
arista-cli
openconfig-policy-types
openconfig-inet-types
openconfig-hercules-platform
openconfig-procmon
openconfig-platform-transceiver
openconfig-if-ip
openconfig-ospfv2
openconfig-platform-linecard
openconfig-alarm-types
arista-exp-eos-l2protocolforwarding
openconfig-routing-policy
openconfig-platform-types
openconfig-transport-types
openconfig-relay-agent
openconfig-isis-lsdb-types
openconfig-platform-psu
openconfig-isis-types
openconfig-segment-routing
openconfig-openflow
openconfig-interfaces
openconfig-alarms
openconfig-packet-match-types
ietf-netconf
openconfig-system-logging
openconfig-bgp-policy
openconfig-aft-types
openconfig-network-instance
arista-eos-types
openconfig-ospf-types
openconfig-if-ethernet
openconfig-lldp-types
openconfig-bgp
openconfig-rib-bgp
openconfig-mpls-rsvp
openconfig-aaa-types
openconfig-extensions
openconfig-mpls-sr
arista-exp-eos-varp-intf
openconfig-messages
openconfig-mpls-ldp
arista-exp-eos-multicast
arista-exp-eos-varp-net-inst
arista-exp-eos-igmpsnooping
openconfig-license
arista-exp-eos
openconfig-packet-match
arista-exp-eos-qos-acl-config
arista-gnoi-cert
arista-exp-eos-evpn
openconfig-ospf-policy
openconfig-isis
arista-exp-eos-qos
openconfig-aft
openconfig-system
openconfig-mpls-types
ietf-inet-types
arista-exp-eos-vxlan
openconfig-hercules-qos
openconfig-segment-routing-types
openconfig-if-aggregate
openconfig-qos-types
openconfig-vlan-types
openconfig-yang-types
openconfig-network-instance-types
openconfig-lldp
openconfig-vlan
openconfig-pf-srte
openconfig-rib-bgp-types
ietf-interfaces
openconfig-srte-policy
arista-exp-eos-qos-config
openconfig-system-management
openconfig-bgp-types
openconfig-pim
openconfig-lacp
openconfig-local-routing
openconfig-system-terminal
openconfig-if-poe
openconfig-platform-cpu
openconfig-platform-fan
openconfig-platform-port
openconfig-if-types
ietf-netconf-monitoring
arista-exp-eos-mlag
openconfig-pim-types
openconfig-if-tunnel
openconfig-platform
arista-rpc-netconf
vlan-translation
iana-if-type
openconfig-openflow-types
openconfig-mpls
openconfig-network-instance-l3
openconfig-igmp
openconfig-acl
openconfig-policy-forwarding
openconfig-types
\ No newline at end of file
package main
import (
"bufio"
"encoding/json"
"fmt"
"net"
"net/http"
"os"
"strings"
"time"
"code.fbi.h-da.de/danet/api/go/gosdn/southbound"
"code.fbi.h-da.de/danet/api/go/gosdn/transport"
"code.fbi.h-da.de/danet/gosdn/api"
"code.fbi.h-da.de/danet/gosdn/interfaces/networkdomain"
"github.com/google/uuid"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
"github.com/prometheus/prom2json"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
)
const plugin = southbound.Type_PLUGIN
const containerised = southbound.Type_CONTAINERISED
type experiment struct {
execMode southbound.Type
iterations int
}
type config struct {
gnmiTarget string `yaml:"gnmi-target"`
ceosTarget string `yaml:"ceos-target"`
controller string `yaml:"controller"`
}
type result struct{}
var sbiID uuid.UUID
var pndID uuid.UUID
var pnd networkdomain.NetworkDomain
var targets = []string{
"clab-thesis-gosdn:8080",
"clab-thesis-orchestrator:9338",
}
var experiments = []experiment{
{
execMode: plugin,
iterations: 1,
},
{
execMode: containerised,
iterations: 1,
},
{
execMode: plugin,
iterations: 10,
},
{
execMode: containerised,
iterations: 10,
},
{
execMode: plugin,
iterations: 20,
},
{
execMode: containerised,
iterations: 20,
},
{
execMode: plugin,
iterations: 40,
},
{
execMode: containerised,
iterations: 40,
},
{
execMode: plugin,
iterations: 60,
},
{
execMode: containerised,
iterations: 60,
},
{
execMode: plugin,
iterations: 80,
},
{
execMode: containerised,
iterations: 80,
},
}
func newCollector(targets []string, path string) *metricsCollector {
f, err := os.Create(path)
if err != nil {
log.Fatal(err)
}
return &metricsCollector{
targets: targets,
f: f,
ticker: time.NewTicker(1 * time.Second),
mfChan: make(chan *dto.MetricFamily, 1024),
stopChan: make(chan bool),
}
}
type metricsCollector struct {
targets []string
f *os.File
stopChan chan bool
ticker *time.Ticker
mfChan chan *dto.MetricFamily
results []*prom2json.Family
}
func (mc *metricsCollector) start() {
go func() {
for {
select {
case <-mc.ticker.C:
for _, target := range mc.targets {
if err := mc.collect(target); err != nil {
log.Error(err)
}
}
case <-mc.stopChan:
close(mc.mfChan)
return
}
}
}()
go func() {
for mf := range mc.mfChan {
mc.results = append(mc.results, prom2json.NewFamily(mf))
}
}()
}
func (mc *metricsCollector) stop() {
mc.stopChan <- true
writers := make(map[string]*fileWriter)
for _, result := range mc.results {
writer, ok := writers[result.Name]
if !ok {
path := fmt.Sprintf("/out/prom-%v-%v.csv", time.Now().UTC().Format(time.RFC3339), result.Name)
writer = newFileWriter(path)
writers[result.Name] = writer
}
switch result.Type {
case "GAUGE", "COUNTER":
if err := writeMetric(writer, result); err != nil {
log.Error(err)
}
case "HISTOGRAM":
if err := writeHistogram(writer, result); err != nil {
log.Error(err)
}
default:
}
}
for _, v := range writers {
v.close()
}
jsonText, err := json.Marshal(mc.results)
if err != nil {
log.Error()
}
n, err := mc.f.Write(jsonText)
if err != nil {
log.Error(err)
}
log.WithField("n", n).Info("wrote prom json")
}
func (mc *metricsCollector) collect(target string) error {
resp, err := http.Get("http://" + target + "/metrics")
if err != nil {
return err
}
defer resp.Body.Close()
var parser expfmt.TextParser
mfs, err := parser.TextToMetricFamilies(resp.Body)
if err != nil {
return err
}
for k, v := range mfs {
if strings.Contains(k, "go_memstats") ||
strings.Contains(k, "code_generations_total") ||
strings.Contains(k, "duration_seconds") ||
strings.Contains(k, "errors") ||
strings.Contains(k, "grpc_requests_total") {
mc.mfChan <- v
}
}
return nil
}
func writeMetric(writer *fileWriter, result *prom2json.Family) error {
b := strings.Builder{}
b.WriteString(result.Name)
b.WriteRune(';')
b.WriteString(result.Type)
for _, m := range result.Metrics {
b.WriteRune(';')
metric, ok := m.(prom2json.Metric)
if !ok {
return fmt.Errorf("invalid type assertion")
}
b.WriteString(metric.TimestampMs)
b.WriteRune(';')
for k, v := range metric.Labels {
b.WriteString(k)
b.WriteRune(';')
b.WriteString(v)
b.WriteRune(';')
}
b.WriteString(metric.Value)
}
b.WriteRune('\n')
writer.write(b.String())
return nil
}
func writeHistogram(writer *fileWriter, result *prom2json.Family) error {
b := strings.Builder{}
b.WriteString(result.Name)
b.WriteRune(';')
b.WriteString(result.Type)
for _, m := range result.Metrics {
b.WriteRune(';')
hist, ok := m.(prom2json.Histogram)
if !ok {
return fmt.Errorf("invalid type assertion")
}
b.WriteString(hist.TimestampMs)
b.WriteRune(';')
for k, v := range hist.Labels {
b.WriteString(k)
b.WriteRune(';')
b.WriteString(v)
b.WriteRune(';')
}
for k, v := range hist.Buckets {
b.WriteString(k)
b.WriteRune(';')
b.WriteString(v)
b.WriteRune(';')
}
b.WriteString(hist.Count)
b.WriteRune(';')
b.WriteString(hist.Count)
}
b.WriteRune('\n')
writer.write(b.String())
return nil
}
func newFileWriter(path string) *fileWriter {
f, err := os.Create(path)
if err != nil {
log.Fatal(err)
}
return &fileWriter{
f: f,
w: bufio.NewWriter(f),
}
}
type fileWriter struct {
f *os.File
w *bufio.Writer
}
func (fw *fileWriter) write(out string) {
_, err := fw.w.WriteString(out)
if err != nil {
log.Error(err)
}
}
func (fw *fileWriter) close() {
fw.w.Flush()
fw.f.Close()
}
func main() {
c, err := readConfig()
if err != nil {
log.Fatal(err)
}
log.Info("sleeping 10s")
time.Sleep(10 * time.Second)
if err := api.Init(c.controller); err != nil {
log.Fatal(err)
}
pndID = uuid.MustParse(viper.GetString("CLI_PND"))
sbiID = uuid.MustParse(viper.GetString("CLI_SBI"))
pnd, err = api.NewAdapter(pndID.String(), c.controller)
if err != nil {
log.Fatal(err)
}
addr, err := net.ResolveTCPAddr("tcp", c.ceosTarget)
if err != nil {
log.Error(err)
}
var connected bool
var conn net.Conn
for !connected {
conn, err = net.DialTCP("tcp", nil, addr)
if err != nil {
log.Warn("waiting for cEOS. Retry in 10s...")
time.Sleep(10 * time.Second)
continue
}
connected = true
}
if err := conn.Close(); err != nil {
log.Error()
}
for i, exp := range experiments {
log.WithFields(log.Fields{
"iterations": exp.iterations,
"exec": exp.execMode,
}).Infof("starting experiment %v of %v\n", (i + 1), len(experiments))
err := executeExperiment(exp, c)
if err != nil {
log.Error(err)
}
log.Info("wait 10s for clean up")
time.Sleep(10 * time.Second)
}
}
func readConfig() (*config, error) {
viper.SetConfigFile("./experiment.yaml")
if err := viper.ReadInConfig(); err != nil {
return nil, err
}
fmt.Println("Using config file:", viper.ConfigFileUsed())
return &config{
gnmiTarget: viper.GetString("gnmi-target"),
ceosTarget: viper.GetString("ceos-target"),
controller: viper.GetString("controller"),
}, nil
}
func add(opts *transport.TransportOption, writer *fileWriter) {
var errs int
start := time.Now()
if err := pnd.AddDevice("", opts, sbiID); err != nil {
log.Error(err)
errs++
}
duration := time.Since(start)
writer.write(fmt.Sprintf("%v;add;%v;%v;%v;%v\n", time.Now().UnixNano(), opts.Address, opts.Type.String(), duration, errs))
}
func get(ouid string, opts *transport.TransportOption, writer *fileWriter) {
var errs int
start := time.Now()
_, err := pnd.Request(uuid.MustParse(ouid), "/system/config/hostname")
if err != nil {
log.Error(err)
errs++
}
duration := time.Since(start)
writer.write(fmt.Sprintf("%v;get;%v;%v;%v\n", time.Now().UnixNano(), opts.Type.String(), duration, errs))
}
func delete(ouid string, opts *transport.TransportOption, writer *fileWriter) {
var errs int
start := time.Now()
if err := pnd.RemoveDevice(uuid.MustParse(ouid)); err != nil {
log.Error(err)
errs++
}
duration := time.Since(start)
writer.write(fmt.Sprintf("%v;delete;%v;%v;%v\n", time.Now().UnixNano(), opts.Type.String(), duration, errs))
}
func executeExperiment(params experiment, c *config) error {
expName := fmt.Sprintf("/out/results-%v-%v.csv", params.execMode, params.iterations)
writer := newFileWriter(expName)
coll := newCollector(targets, fmt.Sprintf("/out/metrics-%v-%v.json", params.execMode, params.iterations))
coll.start()
defer coll.stop()
start := time.Now()
opts := &transport.TransportOption{
Address: c.ceosTarget,
Username: "admin",
Password: "admin",
Tls: false,
TransportOption: &transport.TransportOption_GnmiTransportOption{
GnmiTransportOption: &transport.GnmiTransportOption{},
},
Type: params.execMode,
}
for i := 0; i < params.iterations; i++ {
add(opts, writer)
}
resp, err := api.GetIds(c.controller)
if err != nil {
return err
}
ondList := resp[0].Ond
for _, ond := range ondList {
get(ond.Id, opts, writer)
}
for _, ond := range ondList {
delete(ond.Id, opts, writer)
}
duration := time.Since(start)
writer.write(fmt.Sprintf("%v;duration;sequential;%v;%v\n", time.Now().UnixNano(), params.iterations, duration))
writer.close()
log.WithFields(log.Fields{
"duration": duration,
"iterations": params.iterations,
"exec": params.execMode,
}).Info("experiment ended")
return nil
}
ceos-target: clab-thesis-ceos:6030
cli_pnd: f8c649be-60ee-4a53-acf9-cebc8eebf3c2
cli_sbi: f370efe4-fb3c-411b-81b6-7f536027eccb
controller: clab-thesis-gosdn:55055
gnmi-target: clab-thesis-gnmi-target:7030
......@@ -105,7 +105,6 @@ func initConfig() {
fmt.Println("Using config file:", viper.ConfigFileUsed())
}
log.SetReportCaller(true)
switch logLevel {
case "trace":
log.SetLevel(log.TraceLevel)
......@@ -114,7 +113,6 @@ func initConfig() {
default:
log.SetLevel(log.InfoLevel)
log.SetFormatter(&log.JSONFormatter{})
log.SetReportCaller(false)
}
if accessToken != "" {
......
......@@ -4,25 +4,34 @@ services:
image: registry.code.fbi.h-da.de/danet/gosdn:thesis-mk
ports:
- "55055:55055"
- "8080:8080"
- "40000:40000"
security_opt: ["apparmor=unconfined"]
cap_add: [SYS_PTRACE]
environment:
- GOSDN_LOG=trace
command: ["--csbi-orchestrator", "orchestrator:55056"]
networks:
- csbi-dev-net