From 3aa0bab6b27eff4e38d1fe10c9f646483cdfae40 Mon Sep 17 00:00:00 2001 From: davidnewhall2 Date: Fri, 3 Jul 2020 23:59:11 -0700 Subject: [PATCH] add uptimes, rx/tx per client, controller id, eventgroups --- core/webserver/README.md | 45 --------------------- core/webserver/handlers.go | 15 ++++++- core/webserver/plugins.go | 8 ++-- core/webserver/plugins_types.go | 72 ++++++++++++++++++++------------- core/webserver/server.go | 3 +- 5 files changed, 64 insertions(+), 79 deletions(-) diff --git a/core/webserver/README.md b/core/webserver/README.md index be593566..bfb7b904 100644 --- a/core/webserver/README.md +++ b/core/webserver/README.md @@ -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 - } - ] - } -} diff --git a/core/webserver/handlers.go b/core/webserver/handlers.go index 4c118c77..ebc5d1e4 100644 --- a/core/webserver/handlers.go +++ b/core/webserver/handlers.go @@ -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 == "": diff --git a/core/webserver/plugins.go b/core/webserver/plugins.go index e555d111..72fca1cb 100644 --- a/core/webserver/plugins.go +++ b/core/webserver/plugins.go @@ -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)) diff --git a/core/webserver/plugins_types.go b/core/webserver/plugins_types.go index d2f19c32..49236c4b 100644 --- a/core/webserver/plugins_types.go +++ b/core/webserver/plugins_types.go @@ -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. @@ -41,14 +41,18 @@ type Sites []*Site // Site is a network location and its meta data. type Site struct { - ID string `json:"id"` - Name string `json:"name"` - Desc string `json:"desc"` - Source string `json:"source"` + ID string `json:"id"` + 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,15 +91,18 @@ type Devices []*Device // Device holds the data for a network device. type Device struct { - Name string `json:"name"` - SiteID string `json:"site_id"` - Source string `json:"source"` - MAC string `json:"mac"` - IP string `json:"ip"` - Type string `json:"type"` - Model string `json:"model"` - Version string `json:"version"` - Config interface{} `json:"config,omitempty"` + 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"` + Model string `json:"model"` + Version string `json:"version"` + Config interface{} `json:"config,omitempty"` } // Clients is a list of clients with their data. @@ -95,13 +110,16 @@ type Clients []*Client // Client holds the data for a network client. type Client struct { - Name string `json:"name"` - SiteID string `json:"site_id"` - Source string `json:"source"` - MAC string `json:"mac"` - IP string `json:"ip"` - Type string `json:"type"` - DeviceMAC string `json:"device_mac"` - Since time.Time `json:"since"` - Last time.Time `json:"last"` + 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"` + DeviceMAC string `json:"device_mac"` + Since time.Time `json:"since"` + Last time.Time `json:"last"` } diff --git a/core/webserver/server.go b/core/webserver/server.go index 6c91fa85..44d76e59 100644 --- a/core/webserver/server.go +++ b/core/webserver/server.go @@ -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,