add uptimes, rx/tx per client, controller id, eventgroups

This commit is contained in:
davidnewhall2 2020-07-03 23:59:11 -07:00
parent 55612d3e27
commit 3aa0bab6b2
5 changed files with 64 additions and 79 deletions

View File

@ -13,7 +13,6 @@ The web server may be secured with a simple password. SSL is also optional.
### Controllers
- The web server interface allows you to see the configuration for each controller.
- You may select a controller, and then select a site on the controller.
- Some meta data about each controller is displayed, such as sites, clients and devices.
- Example config: [up.json.example](https://github.com/unifi-poller/unifi-poller/blob/master/examples/up.json.example)
@ -26,47 +25,3 @@ The web server may be secured with a simple password. SSL is also optional.
- You may view output plugin configuration. Currently Prometheus and InfluxDB.
- The example config above shows output plugin data.
### Sites
Each controller has 1 or more sites. Most people only have 1, but some enterprises
run this software and have many more. Each site has devices like switches (`USW`), access
points (`UAP`) and routers (`USG`/`UDM`). We'll have counts for each device type. Each device has a name.
Each device has a count of clients (access points have clients). We'll want to expose
this, but it's not in a useful format yet. It'll look something like what you see below,
but keep the visualization expandable. We may add "model" and "serial number" for each device.
There is a handful of meta data per device. Some users have hundreds of devices.
```
{
"site_name_here": {
"clients": 22,
"UAP": [
{
"name": "ap1-room",
"clients": 6
},
{
"name": "ap2-bran",
"clients": 6
}
],
"USW": [
{
"name": "sw1-cube",
"model": "US-500w-P",
"serial": "xyz637sjs999",
"clients": 7
},
{
"name": "sw2-trap",
"clients": 3
}
],
"USG": [
{
"name": "gw1-role",
"clients": 22
}
]
}
}

View File

@ -3,6 +3,7 @@ package webserver
import (
"net/http"
"path/filepath"
"time"
"github.com/gorilla/mux"
)
@ -35,13 +36,19 @@ func (s *Server) handleStatic(w http.ResponseWriter, r *http.Request) {
// Returns web server and poller configs. /api/v1/config.
func (s *Server) handleConfig(w http.ResponseWriter, r *http.Request) {
data := map[string]interface{}{"poller": s.Collect.Poller()}
data := map[string]interface{}{
"poller": s.Collect.Poller(),
"uptime": int(time.Since(s.start).Round(time.Second).Seconds()),
}
s.handleJSON(w, data)
}
// Returns a list of input and output plugins: /api/v1/plugins.
func (s *Server) handlePlugins(w http.ResponseWriter, r *http.Request) {
data := map[string][]string{"inputs": s.Collect.Inputs(), "outputs": s.Collect.Outputs()}
data := map[string][]string{
"inputs": s.Collect.Inputs(),
"outputs": s.Collect.Outputs(),
}
s.handleJSON(w, data)
}
@ -61,6 +68,8 @@ func (s *Server) handleOutput(w http.ResponseWriter, r *http.Request) {
switch val := vars["value"]; vars["sub"] {
default:
s.handleJSON(w, c.Config)
case "eventgroups":
s.handleJSON(w, c.Events.Groups())
case "events":
switch events, ok := c.Events[val]; {
case val == "":
@ -95,6 +104,8 @@ func (s *Server) handleInput(w http.ResponseWriter, r *http.Request) {
switch val := vars["value"]; vars["sub"] {
default:
s.handleJSON(w, c.Config)
case "eventgroups":
s.handleJSON(w, c.Events.Groups())
case "events":
switch events, ok := c.Events[val]; {
case val == "":

View File

@ -167,11 +167,11 @@ func (w *webPlugins) newInputEvent(plugin, id string, event *Event) {
defer input.Unlock()
if input.Events == nil {
input.Events = make(map[string]*Events)
input.Events = make(Events)
}
if _, ok := input.Events[id]; !ok {
input.Events[id] = &Events{}
input.Events[id] = &EventGroup{}
}
input.Events[id].add(event, int(w.Config.MaxEvents))
@ -187,11 +187,11 @@ func (w *webPlugins) newOutputEvent(plugin, id string, event *Event) {
defer output.Unlock()
if output.Events == nil {
output.Events = make(map[string]*Events)
output.Events = make(Events)
}
if _, ok := output.Events[id]; !ok {
output.Events[id] = &Events{}
output.Events[id] = &EventGroup{}
}
output.Events[id].add(event, int(w.Config.MaxEvents))

View File

@ -11,7 +11,7 @@ import (
type Input struct {
Name string
Sites Sites
Events map[string]*Events
Events Events
Devices Devices
Clients Clients
Config interface{}
@ -25,7 +25,7 @@ type Input struct {
// Setting Config will overwrite previous value.
type Output struct {
Name string
Events map[string]*Events
Events Events
Config interface{}
Counter map[string]int64
sync.RWMutex // Locks this data structure.
@ -45,10 +45,14 @@ type Site struct {
Name string `json:"name"`
Desc string `json:"desc"`
Source string `json:"source"`
Controller string `json:"controller"`
}
// Events allows each plugin to have a map of events. ie. one map per controller.
type Events struct {
// Events is all the events a plugin has. string = Controller.UUID + text.
type Events map[string]*EventGroup
// EventGroup allows each plugin to have a map of events. ie. one map per controller.
type EventGroup struct {
Latest time.Time `json:"latest"`
Events []*Event `json:"events"`
}
@ -60,8 +64,16 @@ type Event struct {
Tags map[string]string `json:"tags,omitempty"`
}
func (e Events) Groups() (groups []string) {
for n := range e {
groups = append(groups, n)
}
return groups
}
// add adds a new event and makes sure the slice is not too big.
func (e *Events) add(event *Event, max int) {
func (e *EventGroup) add(event *Event, max int) {
if !e.Latest.Before(event.Ts) {
return // Ignore older events.
}
@ -79,9 +91,12 @@ type Devices []*Device
// Device holds the data for a network device.
type Device struct {
Clients int `json:"clients"`
Uptime int `json:"uptime"`
Name string `json:"name"`
SiteID string `json:"site_id"`
Source string `json:"source"`
Controller string `json:"controller"`
MAC string `json:"mac"`
IP string `json:"ip"`
Type string `json:"type"`
@ -95,9 +110,12 @@ type Clients []*Client
// Client holds the data for a network client.
type Client struct {
Rx int64 `json:"rx_bytes"`
Tx int64 `json:"tx_bytes"`
Name string `json:"name"`
SiteID string `json:"site_id"`
Source string `json:"source"`
Controller string `json:"controller"`
MAC string `json:"mac"`
IP string `json:"ip"`
Type string `json:"type"`

View File

@ -43,12 +43,13 @@ type Server struct {
server *http.Server
plugins *webPlugins
Collect poller.Collect
start time.Time
}
// init is how this modular code is initialized by the main app.
// This module adds itself as an output module to the poller core.
func init() { // nolint: gochecknoinits
s := &Server{plugins: plugins, Config: &Config{
s := &Server{plugins: plugins, start: time.Now(), Config: &Config{
Port: DefaultPort,
HTMLPath: filepath.Join(poller.DefaultObjPath, "web"),
MaxEvents: DefaultEvents,