mirror of
https://github.com/luscis/openlan.git
synced 2025-10-06 00:57:03 +08:00
fea: add statics for output
This commit is contained in:
@@ -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(),
|
||||
|
@@ -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
43
pkg/api/output.go
Executable 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
47
pkg/cache/output.go
vendored
Executable 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),
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
@@ -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
29
pkg/models/ipsec_linux.go
Executable 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
|
||||
}
|
||||
}
|
6
pkg/models/ipsec_others.go
Normal file
6
pkg/models/ipsec_others.go
Normal file
@@ -0,0 +1,6 @@
|
||||
// +build !linux
|
||||
|
||||
package models
|
||||
|
||||
func (l *EspState) Update() {
|
||||
}
|
19
pkg/models/output.go
Executable file
19
pkg/models/output.go
Executable 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
14
pkg/models/output_linux.go
Executable 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
6
pkg/models/output_others.go
Executable file
@@ -0,0 +1,6 @@
|
||||
// +build !linux
|
||||
|
||||
package models
|
||||
|
||||
func (l *Output) Update() {
|
||||
}
|
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
@@ -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>
|
||||
|
@@ -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 {
|
||||
|
@@ -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 {
|
||||
|
@@ -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
13
pkg/schema/output.go
Normal 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"`
|
||||
}
|
@@ -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"`
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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{
|
||||
|
Reference in New Issue
Block a user