add uptimes, rx/tx per client, controller id, eventgroups
This commit is contained in:
		
							parent
							
								
									55612d3e27
								
							
						
					
					
						commit
						3aa0bab6b2
					
				| 
						 | 
					@ -13,7 +13,6 @@ The web server may be secured with a simple password. SSL is also optional.
 | 
				
			||||||
### Controllers
 | 
					### Controllers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-   The web server interface allows you to see the configuration for each controller.
 | 
					-   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.
 | 
					-   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)
 | 
					-   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.
 | 
					-   You may view output plugin configuration. Currently Prometheus and InfluxDB.
 | 
				
			||||||
-   The example config above shows output plugin data.
 | 
					-   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
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@ package webserver
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gorilla/mux"
 | 
						"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.
 | 
					// Returns web server and poller configs. /api/v1/config.
 | 
				
			||||||
func (s *Server) handleConfig(w http.ResponseWriter, r *http.Request) {
 | 
					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)
 | 
						s.handleJSON(w, data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Returns a list of input and output plugins: /api/v1/plugins.
 | 
					// Returns a list of input and output plugins: /api/v1/plugins.
 | 
				
			||||||
func (s *Server) handlePlugins(w http.ResponseWriter, r *http.Request) {
 | 
					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)
 | 
						s.handleJSON(w, data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -61,6 +68,8 @@ func (s *Server) handleOutput(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	switch val := vars["value"]; vars["sub"] {
 | 
						switch val := vars["value"]; vars["sub"] {
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		s.handleJSON(w, c.Config)
 | 
							s.handleJSON(w, c.Config)
 | 
				
			||||||
 | 
						case "eventgroups":
 | 
				
			||||||
 | 
							s.handleJSON(w, c.Events.Groups())
 | 
				
			||||||
	case "events":
 | 
						case "events":
 | 
				
			||||||
		switch events, ok := c.Events[val]; {
 | 
							switch events, ok := c.Events[val]; {
 | 
				
			||||||
		case val == "":
 | 
							case val == "":
 | 
				
			||||||
| 
						 | 
					@ -95,6 +104,8 @@ func (s *Server) handleInput(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
	switch val := vars["value"]; vars["sub"] {
 | 
						switch val := vars["value"]; vars["sub"] {
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		s.handleJSON(w, c.Config)
 | 
							s.handleJSON(w, c.Config)
 | 
				
			||||||
 | 
						case "eventgroups":
 | 
				
			||||||
 | 
							s.handleJSON(w, c.Events.Groups())
 | 
				
			||||||
	case "events":
 | 
						case "events":
 | 
				
			||||||
		switch events, ok := c.Events[val]; {
 | 
							switch events, ok := c.Events[val]; {
 | 
				
			||||||
		case val == "":
 | 
							case val == "":
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -167,11 +167,11 @@ func (w *webPlugins) newInputEvent(plugin, id string, event *Event) {
 | 
				
			||||||
	defer input.Unlock()
 | 
						defer input.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if input.Events == nil {
 | 
						if input.Events == nil {
 | 
				
			||||||
		input.Events = make(map[string]*Events)
 | 
							input.Events = make(Events)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, ok := input.Events[id]; !ok {
 | 
						if _, ok := input.Events[id]; !ok {
 | 
				
			||||||
		input.Events[id] = &Events{}
 | 
							input.Events[id] = &EventGroup{}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	input.Events[id].add(event, int(w.Config.MaxEvents))
 | 
						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()
 | 
						defer output.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if output.Events == nil {
 | 
						if output.Events == nil {
 | 
				
			||||||
		output.Events = make(map[string]*Events)
 | 
							output.Events = make(Events)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, ok := output.Events[id]; !ok {
 | 
						if _, ok := output.Events[id]; !ok {
 | 
				
			||||||
		output.Events[id] = &Events{}
 | 
							output.Events[id] = &EventGroup{}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	output.Events[id].add(event, int(w.Config.MaxEvents))
 | 
						output.Events[id].add(event, int(w.Config.MaxEvents))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,7 @@ import (
 | 
				
			||||||
type Input struct {
 | 
					type Input struct {
 | 
				
			||||||
	Name         string
 | 
						Name         string
 | 
				
			||||||
	Sites        Sites
 | 
						Sites        Sites
 | 
				
			||||||
	Events       map[string]*Events
 | 
						Events       Events
 | 
				
			||||||
	Devices      Devices
 | 
						Devices      Devices
 | 
				
			||||||
	Clients      Clients
 | 
						Clients      Clients
 | 
				
			||||||
	Config       interface{}
 | 
						Config       interface{}
 | 
				
			||||||
| 
						 | 
					@ -25,7 +25,7 @@ type Input struct {
 | 
				
			||||||
// Setting Config will overwrite previous value.
 | 
					// Setting Config will overwrite previous value.
 | 
				
			||||||
type Output struct {
 | 
					type Output struct {
 | 
				
			||||||
	Name         string
 | 
						Name         string
 | 
				
			||||||
	Events       map[string]*Events
 | 
						Events       Events
 | 
				
			||||||
	Config       interface{}
 | 
						Config       interface{}
 | 
				
			||||||
	Counter      map[string]int64
 | 
						Counter      map[string]int64
 | 
				
			||||||
	sync.RWMutex // Locks this data structure.
 | 
						sync.RWMutex // Locks this data structure.
 | 
				
			||||||
| 
						 | 
					@ -45,10 +45,14 @@ type Site struct {
 | 
				
			||||||
	Name       string `json:"name"`
 | 
						Name       string `json:"name"`
 | 
				
			||||||
	Desc       string `json:"desc"`
 | 
						Desc       string `json:"desc"`
 | 
				
			||||||
	Source     string `json:"source"`
 | 
						Source     string `json:"source"`
 | 
				
			||||||
 | 
						Controller string `json:"controller"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Events allows each plugin to have a map of events. ie. one map per controller.
 | 
					// Events is all the events a plugin has. string = Controller.UUID + text.
 | 
				
			||||||
type Events struct {
 | 
					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"`
 | 
						Latest time.Time `json:"latest"`
 | 
				
			||||||
	Events []*Event  `json:"events"`
 | 
						Events []*Event  `json:"events"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -60,8 +64,16 @@ type Event struct {
 | 
				
			||||||
	Tags map[string]string `json:"tags,omitempty"`
 | 
						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.
 | 
					// 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) {
 | 
						if !e.Latest.Before(event.Ts) {
 | 
				
			||||||
		return // Ignore older events.
 | 
							return // Ignore older events.
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -79,9 +91,12 @@ type Devices []*Device
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Device holds the data for a network device.
 | 
					// Device holds the data for a network device.
 | 
				
			||||||
type Device struct {
 | 
					type Device struct {
 | 
				
			||||||
 | 
						Clients    int         `json:"clients"`
 | 
				
			||||||
 | 
						Uptime     int         `json:"uptime"`
 | 
				
			||||||
	Name       string      `json:"name"`
 | 
						Name       string      `json:"name"`
 | 
				
			||||||
	SiteID     string      `json:"site_id"`
 | 
						SiteID     string      `json:"site_id"`
 | 
				
			||||||
	Source     string      `json:"source"`
 | 
						Source     string      `json:"source"`
 | 
				
			||||||
 | 
						Controller string      `json:"controller"`
 | 
				
			||||||
	MAC        string      `json:"mac"`
 | 
						MAC        string      `json:"mac"`
 | 
				
			||||||
	IP         string      `json:"ip"`
 | 
						IP         string      `json:"ip"`
 | 
				
			||||||
	Type       string      `json:"type"`
 | 
						Type       string      `json:"type"`
 | 
				
			||||||
| 
						 | 
					@ -95,9 +110,12 @@ type Clients []*Client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Client holds the data for a network client.
 | 
					// Client holds the data for a network client.
 | 
				
			||||||
type Client struct {
 | 
					type Client struct {
 | 
				
			||||||
 | 
						Rx         int64     `json:"rx_bytes"`
 | 
				
			||||||
 | 
						Tx         int64     `json:"tx_bytes"`
 | 
				
			||||||
	Name       string    `json:"name"`
 | 
						Name       string    `json:"name"`
 | 
				
			||||||
	SiteID     string    `json:"site_id"`
 | 
						SiteID     string    `json:"site_id"`
 | 
				
			||||||
	Source     string    `json:"source"`
 | 
						Source     string    `json:"source"`
 | 
				
			||||||
 | 
						Controller string    `json:"controller"`
 | 
				
			||||||
	MAC        string    `json:"mac"`
 | 
						MAC        string    `json:"mac"`
 | 
				
			||||||
	IP         string    `json:"ip"`
 | 
						IP         string    `json:"ip"`
 | 
				
			||||||
	Type       string    `json:"type"`
 | 
						Type       string    `json:"type"`
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,12 +43,13 @@ type Server struct {
 | 
				
			||||||
	server  *http.Server
 | 
						server  *http.Server
 | 
				
			||||||
	plugins *webPlugins
 | 
						plugins *webPlugins
 | 
				
			||||||
	Collect poller.Collect
 | 
						Collect poller.Collect
 | 
				
			||||||
 | 
						start   time.Time
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// init is how this modular code is initialized by the main app.
 | 
					// init is how this modular code is initialized by the main app.
 | 
				
			||||||
// This module adds itself as an output module to the poller core.
 | 
					// This module adds itself as an output module to the poller core.
 | 
				
			||||||
func init() { // nolint: gochecknoinits
 | 
					func init() { // nolint: gochecknoinits
 | 
				
			||||||
	s := &Server{plugins: plugins, Config: &Config{
 | 
						s := &Server{plugins: plugins, start: time.Now(), Config: &Config{
 | 
				
			||||||
		Port:      DefaultPort,
 | 
							Port:      DefaultPort,
 | 
				
			||||||
		HTMLPath:  filepath.Join(poller.DefaultObjPath, "web"),
 | 
							HTMLPath:  filepath.Join(poller.DefaultObjPath, "web"),
 | 
				
			||||||
		MaxEvents: DefaultEvents,
 | 
							MaxEvents: DefaultEvents,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue