fea: add statics for output

This commit is contained in:
Daniel Ding
2024-01-08 20:56:21 +08:00
parent 53dcac37f9
commit c542a184cc
20 changed files with 268 additions and 63 deletions

View File

@@ -237,9 +237,9 @@ func (w *Worker) FlushStatus() {
}
sts := client.Statistics()
status := &schema.Point{
RxBytes: sts[libol.CsRecvOkay],
TxBytes: sts[libol.CsSendOkay],
ErrPkt: sts[libol.CsSendError],
RxBytes: uint64(sts[libol.CsRecvOkay]),
TxBytes: uint64(sts[libol.CsSendOkay]),
ErrPkt: uint64(sts[libol.CsSendError]),
Uptime: client.UpTime(),
State: client.Status().String(),
Device: device.Name(),

View File

@@ -32,8 +32,8 @@ func (h Link) List(w http.ResponseWriter, r *http.Request) {
func (h Link) Get(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
libol.Info("Link.Get %s", vars["id"])
libol.Debug("Link.Get %s", vars["id"])
link := cache.Link.Get(vars["id"])
if link != nil {
ResponseJson(w, models.NewLinkSchema(link))

43
pkg/api/output.go Executable file
View File

@@ -0,0 +1,43 @@
package api
import (
"net/http"
"github.com/gorilla/mux"
"github.com/luscis/openlan/pkg/cache"
"github.com/luscis/openlan/pkg/libol"
"github.com/luscis/openlan/pkg/models"
"github.com/luscis/openlan/pkg/schema"
)
type Output struct {
Switcher Switcher
}
func (h Output) Router(router *mux.Router) {
router.HandleFunc("/api/output", h.List).Methods("GET")
router.HandleFunc("/api/output/{id}", h.Get).Methods("GET")
}
func (h Output) List(w http.ResponseWriter, r *http.Request) {
outputs := make([]schema.Output, 0, 1024)
for l := range cache.Output.List() {
if l == nil {
break
}
outputs = append(outputs, models.NewOutputSchema(l))
}
ResponseJson(w, outputs)
}
func (h Output) Get(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
libol.Debug("Output.Get %s", vars["id"])
output := cache.Output.Get(vars["id"])
if output != nil {
ResponseJson(w, models.NewOutputSchema(output))
} else {
http.Error(w, vars["id"], http.StatusNotFound)
}
}

47
pkg/cache/output.go vendored Executable file
View File

@@ -0,0 +1,47 @@
package cache
import (
"github.com/luscis/openlan/pkg/libol"
"github.com/luscis/openlan/pkg/models"
)
type output struct {
outputs *libol.SafeStrMap
}
func (p *output) Init(size int) {
p.outputs = libol.NewSafeStrMap(size)
}
func (p *output) Add(uuid string, output *models.Output) {
_ = p.outputs.Set(uuid, output)
}
func (p *output) Get(key string) *models.Output {
ret := p.outputs.Get(key)
if ret != nil {
return ret.(*models.Output)
}
return nil
}
func (p *output) Del(key string) {
p.outputs.Del(key)
}
func (p *output) List() <-chan *models.Output {
c := make(chan *models.Output, 128)
go func() {
p.outputs.Iter(func(k string, v interface{}) {
m := v.(*models.Output)
m.Update()
c <- m
})
c <- nil //Finish channel by nil.
}()
return c
}
var Output = output{
outputs: libol.NewSafeStrMap(1024),
}

View File

@@ -214,8 +214,8 @@ func PrettyTime(t int64) string {
return fmt.Sprintf("%s%dd%dh", s, days, hours%24)
}
func PrettyBytes(b int64) string {
split := func(_v int64, _m int64) (i int64, d int) {
func PrettyBytes(b uint64) string {
split := func(_v uint64, _m uint64) (i uint64, d int) {
_d := float64(_v%_m) / float64(_m)
return _v / _m, int(_d * 100) //move two decimal to integer
}

View File

@@ -1,13 +1,11 @@
// +build linux
package models
import (
"fmt"
"github.com/luscis/openlan/pkg/libol"
"time"
"github.com/luscis/openlan/pkg/schema"
nl "github.com/vishvananda/netlink"
"time"
)
type Esp struct {
@@ -39,26 +37,6 @@ type EspState struct {
Out *nl.XfrmState
}
func (l *EspState) Update() {
used := int64(0)
if xss, err := nl.XfrmStateGet(l.In); xss != nil {
l.TxBytes = int64(xss.Statistics.Bytes)
l.TxPackages = int64(xss.Statistics.Packets)
used = int64(xss.Statistics.UseTime)
} else {
libol.Debug("EspState.Update %s", err)
}
if xss, err := nl.XfrmStateGet(l.Out); xss != nil {
l.RxBytes = int64(xss.Statistics.Bytes)
l.RxPackages = int64(xss.Statistics.Packets)
} else {
libol.Debug("EspState.Update %s", err)
}
if used > 0 {
l.AliveTime = time.Now().Unix() - used
}
}
func (l *EspState) ID() string {
return fmt.Sprintf("spi:%d %s-%s", l.Spi, l.Local, l.Remote)
}
@@ -94,9 +72,6 @@ type EspPolicy struct {
Out *nl.XfrmPolicy
}
func (l *EspPolicy) Update() {
}
func (l *EspPolicy) ID() string {
return fmt.Sprintf("spi:%d %s-%s", l.Spi, l.Source, l.Dest)
}
@@ -105,6 +80,9 @@ func (l *EspPolicy) String() string {
return fmt.Sprintf("{Spi: %d Source: %s Dest: %s}", l.Spi, l.Source, l.Dest)
}
func (l *EspPolicy) Update() {
}
func NewEspPolicySchema(e *EspPolicy) schema.EspPolicy {
e.Update()
se := schema.EspPolicy{

29
pkg/models/ipsec_linux.go Executable file
View File

@@ -0,0 +1,29 @@
package models
import (
"github.com/luscis/openlan/pkg/libol"
nl "github.com/vishvananda/netlink"
"time"
)
func (l *EspState) Update() {
used := int64(0)
if xss, err := nl.XfrmStateGet(l.In); xss != nil {
l.TxBytes = xss.Statistics.Bytes
l.TxPackages = xss.Statistics.Packets
used = int64(xss.Statistics.UseTime)
} else {
libol.Debug("EspState.Update %s", err)
}
if xss, err := nl.XfrmStateGet(l.Out); xss != nil {
l.RxBytes = xss.Statistics.Bytes
l.RxPackages = xss.Statistics.Packets
} else {
libol.Debug("EspState.Update %s", err)
}
if used > 0 {
l.AliveTime = time.Now().Unix() - used
}
}

View File

@@ -0,0 +1,6 @@
// +build !linux
package models
func (l *EspState) Update() {
}

19
pkg/models/output.go Executable file
View File

@@ -0,0 +1,19 @@
package models
import "time"
type Output struct {
Network string
Protocol string
Connection string
Vlan int
Device string
RxBytes uint64
TxBytes uint64
ErrPkt uint64
NewTime int64
}
func (o *Output) UpTime() int64 {
return time.Now().Unix() - o.NewTime
}

14
pkg/models/output_linux.go Executable file
View File

@@ -0,0 +1,14 @@
package models
import (
nl "github.com/vishvananda/netlink"
)
func (l *Output) Update() {
if link, err := nl.LinkByName(l.Device); err == nil {
sts := link.Attrs().Statistics
l.RxBytes = sts.RxBytes
l.TxBytes = sts.TxBytes
}
return
}

6
pkg/models/output_others.go Executable file
View File

@@ -0,0 +1,6 @@
// +build !linux
package models
func (l *Output) Update() {
}

View File

@@ -16,9 +16,9 @@ func NewPointSchema(p *Point) schema.Point {
Protocol: p.Protocol,
Remote: client.String(),
Device: dev.Name(),
RxBytes: sts[libol.CsRecvOkay],
TxBytes: sts[libol.CsSendOkay],
ErrPkt: sts[libol.CsSendError],
RxBytes: uint64(sts[libol.CsRecvOkay]),
TxBytes: uint64(sts[libol.CsSendOkay]),
ErrPkt: uint64(sts[libol.CsSendError]),
State: client.Status().String(),
Network: p.Network,
AliveTime: client.AliveTime(),
@@ -116,3 +116,15 @@ func NewNetworkSchema(n *Network) schema.Network {
}
return sn
}
func NewOutputSchema(o *Output) schema.Output {
return schema.Output{
Network: o.Network,
Protocol: o.Protocol,
Connection: o.Connection,
Device: o.Device,
RxBytes: o.RxBytes,
TxBytes: o.RxBytes,
AliveTime: o.UpTime(),
}
}

View File

@@ -150,12 +150,28 @@
</tr>
</thead>
<tbody>
{{ range .Outputs }}
<tr>
<td>output</td>
<td>{{ prettyTime .AliveTime }}</td>
<td>{{ .Device }}</td>
<td>--</td>
<td>{{ .Protocol }}</td>
<td>{{ .Connection }}</a></td>
<td>{{ prettyBytes .RxBytes }}/{{ prettyBytes .TxBytes }}</td>
<td>
{{ if .RxBytes }}<span class="success">success</span>
{{ else }}<span class="unknown">unknown</span>
{{ end}}
</td>
</tr>
{{ end }}
{{ range .States }}
<tr>
<td>{{ .Name }}</td>
<td>{{ prettyTime .AliveTime }}</td>
<td>spi:{{ .Spi }}</td>
<td>{{ .Local }}</td>
<td>--</td>
<td>esp</td>
<td><a href="https://{{ .Remote }}:10000">{{ .Remote }}</a></td>
<td>{{ prettyBytes .RxBytes }}/{{ prettyBytes .TxBytes }}</td>

View File

@@ -10,6 +10,7 @@ type Index struct {
Network []Network `json:"network"`
Clients []VPNClient `json:"clients"`
States []EspState `json:"states"`
Outputs []Output `json:"output"`
}
type Ctrl struct {

View File

@@ -20,10 +20,10 @@ type EspState struct {
Crypt string `json:"crypt"`
Encap string `json:"encap" `
RemotePort int `json:"remotePort"`
TxBytes int64 `json:"txBytes"`
TxPackages int64 `json:"txPackages"`
RxBytes int64 `json:"rxBytes"`
RxPackages int64 `json:"rxPackages"`
TxBytes uint64 `json:"txBytes"`
TxPackages uint64 `json:"txPackages"`
RxBytes uint64 `json:"rxBytes"`
RxPackages uint64 `json:"rxPackages"`
}
type EspPolicy struct {

View File

@@ -9,9 +9,9 @@ type Link struct {
Protocol string `json:"protocol"`
Server string `json:"server"`
Device string `json:"device"`
RxBytes int64 `json:"rxBytes"`
TxBytes int64 `json:"txBytes"`
ErrPkt int64 `json:"errors"`
RxBytes uint64 `json:"rxBytes"`
TxBytes uint64 `json:"txBytes"`
ErrPkt uint64 `json:"errors"`
State string `json:"state"`
AliveTime int64 `json:"aliveTime"`
}

13
pkg/schema/output.go Normal file
View File

@@ -0,0 +1,13 @@
package schema
type Output struct {
Network string `json:"network"`
Protocol string `json:"protocol"`
Connection string `json:"connection"`
Vlan int `json:"vlan"`
Device string `json:"device"`
RxBytes uint64 `json:"rxBytes"`
TxBytes uint64 `json:"txBytes"`
ErrPkt uint64 `json:"errors"`
AliveTime int64 `json:"aliveTime"`
}

View File

@@ -10,9 +10,9 @@ type Point struct {
Remote string `json:"remote"`
Switch string `json:"switch,omitempty"`
Device string `json:"device"`
RxBytes int64 `json:"rxBytes"`
TxBytes int64 `json:"txBytes"`
ErrPkt int64 `json:"errors"`
RxBytes uint64 `json:"rxBytes"`
TxBytes uint64 `json:"txBytes"`
ErrPkt uint64 `json:"errors"`
State string `json:"state"`
AliveTime int64 `json:"aliveTime"`
System string `json:"system"`

View File

@@ -305,6 +305,19 @@ func (h *Http) getIndex(body *schema.Index) *schema.Index {
jj := body.States[j]
return ii.Spi > jj.Spi
})
// display esp state
for s := range cache.Output.List() {
if s == nil {
break
}
body.Outputs = append(body.Outputs, models.NewOutputSchema(s))
}
sort.SliceStable(body.Outputs, func(i, j int) bool {
ii := body.Outputs[i]
jj := body.Outputs[j]
return ii.Device > jj.Device
})
return body
}
@@ -327,14 +340,7 @@ func (h *Http) ParseFiles(w http.ResponseWriter, name string, data interface{})
}
func (h *Http) IndexHtml(w http.ResponseWriter, r *http.Request) {
body := schema.Index{
Points: make([]schema.Point, 0, 128),
Links: make([]schema.Link, 0, 128),
Neighbors: make([]schema.Neighbor, 0, 128),
OnLines: make([]schema.OnLine, 0, 128),
Clients: make([]schema.VPNClient, 0, 128),
States: make([]schema.EspState, 0, 128),
}
body := schema.Index{}
h.getIndex(&body)
file := h.getFile("/index.html")
if err := h.ParseFiles(w, file, &body); err != nil {
@@ -343,14 +349,7 @@ func (h *Http) IndexHtml(w http.ResponseWriter, r *http.Request) {
}
func (h *Http) GetIndex(w http.ResponseWriter, r *http.Request) {
body := schema.Index{
Points: make([]schema.Point, 0, 128),
Links: make([]schema.Link, 0, 128),
Neighbors: make([]schema.Neighbor, 0, 128),
OnLines: make([]schema.OnLine, 0, 128),
Network: make([]schema.Network, 0, 128),
Clients: make([]schema.VPNClient, 0, 128),
}
body := schema.Index{}
h.getIndex(&body)
api.ResponseJson(w, body)
}

View File

@@ -155,7 +155,17 @@ func (w *WorkerImpl) AddPhysical(bridge string, vlan int, output string) {
func (w *WorkerImpl) AddOutput(bridge string, port *LinuxPort) {
name := port.name
values := strings.SplitN(name, ":", 6)
out := &models.Output{
Network: w.cfg.Name,
NewTime: time.Now().Unix(),
}
if values[0] == "gre" {
if len(values) < 3 {
w.out.Error("WorkerImpl.LinkAdd %s wrong", name)
return
}
if port.link == "" {
port.link = co.GenName("gre")
}
@@ -175,11 +185,14 @@ func (w *WorkerImpl) AddOutput(bridge string, port *LinuxPort) {
w.out.Error("WorkerImpl.LinkAdd %s %s", name, err)
return
}
out.Protocol = "gre"
out.Connection = fmt.Sprintf("%s:%s", values[1], values[2])
} else if values[0] == "vxlan" {
if len(values) < 3 {
w.out.Error("WorkerImpl.LinkAdd %s wrong", name)
return
}
if port.link == "" {
port.link = co.GenName("vxn")
}
@@ -202,9 +215,15 @@ func (w *WorkerImpl) AddOutput(bridge string, port *LinuxPort) {
w.out.Error("WorkerImpl.LinkAdd %s %s", name, err)
return
}
out.Protocol = "vxlan"
out.Connection = fmt.Sprintf("%s:%s", values[1], values[2])
} else {
port.link = name
}
out.Device = port.link
cache.Output.Add(port.link, out)
w.out.Info("WorkerImpl.AddOutput %s %s", port.link, port.name)
w.AddPhysical(bridge, port.vlan, port.link)
}
@@ -355,7 +374,10 @@ func (w *WorkerImpl) DelPhysical(bridge string, vlan int, output string) {
func (w *WorkerImpl) DelOutput(bridge string, port *LinuxPort) {
w.out.Info("WorkerImpl.DelOutput %s %s", port.link, port.name)
cache.Output.Del(port.link)
w.DelPhysical(bridge, port.vlan, port.link)
values := strings.SplitN(port.name, ":", 6)
if values[0] == "gre" {
link := &netlink.Gretap{