commit
4e061b82f3
|
|
@ -7,7 +7,7 @@ before_install:
|
|||
- curl -sLo $GOPATH/bin/dep https://github.com/golang/dep/releases/download/v0.5.3/dep-linux-amd64
|
||||
- chmod +x $GOPATH/bin/dep
|
||||
# download super-linter: golangci-lint
|
||||
- curl -sL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin latest
|
||||
- curl -sL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.16.0
|
||||
install:
|
||||
- dep ensure
|
||||
script:
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ func main() {
|
|||
}
|
||||
|
||||
log.Println(len(sites), "Unifi Sites Found: ", sites)
|
||||
log.Println(len(clients.UCLs), "Clients connected:")
|
||||
for i, client := range clients.UCLs {
|
||||
log.Println(len(clients), "Clients connected:")
|
||||
for i, client := range clients {
|
||||
log.Println(i+1, client.ID, client.Hostname, client.IP, client.Name, client.LastSeen)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
// Points generates Unifi Client datapoints for InfluxDB.
|
||||
// These points can be passed directly to influx.
|
||||
func (c UCL) Points() ([]*influx.Point, error) {
|
||||
func (c Client) Points() ([]*influx.Point, error) {
|
||||
// Fix name and hostname fields. Sometimes one or the other is blank.
|
||||
switch {
|
||||
case c.Hostname == "" && c.Name == "":
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
package unifi
|
||||
|
||||
// UCL defines all the data a connected-network client contains.
|
||||
type UCL struct {
|
||||
// Clients contains a list that contains all of the unifi clients from a controller.
|
||||
type Clients []Client
|
||||
|
||||
// Client defines all the data a connected-network client contains.
|
||||
type Client struct {
|
||||
ID string `json:"_id"`
|
||||
IsGuestByUAP FlexBool `json:"_is_guest_by_uap"`
|
||||
IsGuestByUGW FlexBool `json:"_is_guest_by_ugw"`
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ func (u *Unifi) parseDevices(data []json.RawMessage, siteName string) *Devices {
|
|||
} else if t, ok := o["type"].(string); ok {
|
||||
assetType = t
|
||||
}
|
||||
u.dLogf("Unmarshalling Device Type: %v", assetType)
|
||||
u.dLogf("Unmarshalling Device Type: %v, site %s ", assetType, siteName)
|
||||
// Choose which type to unmarshal into based on the "type" json key.
|
||||
switch assetType { // Unmarshal again into the correct type..
|
||||
case "uap":
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
package unifi
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
influx "github.com/influxdata/influxdb1-client/v2"
|
||||
)
|
||||
|
||||
// Points generates Unifi Sites' datapoints for InfluxDB.
|
||||
// These points can be passed directly to influx.
|
||||
func (u Site) Points() ([]*influx.Point, error) {
|
||||
points := []*influx.Point{}
|
||||
for _, s := range u.Health {
|
||||
tags := map[string]string{
|
||||
"id": u.ID,
|
||||
"name": u.Name,
|
||||
"desc": u.Desc,
|
||||
"status": s.Status,
|
||||
"subsystem": s.Subsystem,
|
||||
"wan_ip": s.WanIP,
|
||||
"netmask": s.Netmask,
|
||||
"gw_name": s.GwName,
|
||||
"gw_mac": s.GwMac,
|
||||
"gw_version": s.GwVersion,
|
||||
"speedtest_status": s.SpeedtestStatus,
|
||||
"lan_ip": s.LanIP,
|
||||
"remote_user_enabled": s.RemoteUserEnabled.Txt,
|
||||
"site_to_site_enabled": s.SiteToSiteEnabled.Txt,
|
||||
"nameservers": strings.Join(s.Nameservers, ","),
|
||||
"gateways": strings.Join(s.Gateways, ","),
|
||||
"num_new_alarms": u.NumNewAlarms.Txt,
|
||||
"attr_hidden_id": u.AttrHiddenID,
|
||||
"attr_no_delete": u.AttrNoDelete.Txt,
|
||||
}
|
||||
fields := map[string]interface{}{
|
||||
"attr_hidden_id": u.AttrHiddenID,
|
||||
"attr_no_delete": u.AttrNoDelete.Val,
|
||||
"num_user": s.NumUser.Val,
|
||||
"num_guest": s.NumGuest.Val,
|
||||
"num_iot": s.NumIot.Val,
|
||||
"tx_bytes-r": s.TxBytesR.Val,
|
||||
"rx_bytes-r": s.RxBytesR.Val,
|
||||
"status": s.Status,
|
||||
"num_ap": s.NumAp.Val,
|
||||
"num_adopted": s.NumAdopted.Val,
|
||||
"num_disabled": s.NumDisabled.Val,
|
||||
"num_disconnected": s.NumDisconnected.Val,
|
||||
"num_pending": s.NumPending.Val,
|
||||
"num_gw": s.NumGw.Val,
|
||||
"wan_ip": s.WanIP,
|
||||
"num_sta": s.NumSta.Val,
|
||||
"gw_cpu": s.GwSystemStats.CPU.Val,
|
||||
"gw_mem": s.GwSystemStats.Mem.Val,
|
||||
"gw_uptime": s.GwSystemStats.Uptime.Val,
|
||||
"latency": s.Latency.Val,
|
||||
"uptime": s.Uptime.Val,
|
||||
"drops": s.Drops.Val,
|
||||
"xput_up": s.XputUp.Val,
|
||||
"xput_down": s.XputDown.Val,
|
||||
"speedtest_ping": s.SpeedtestPing.Val,
|
||||
"speedtest_lastrun": s.SpeedtestLastrun.Val,
|
||||
"num_sw": s.NumSw.Val,
|
||||
"remote_user_num_active": s.RemoteUserNumActive.Val,
|
||||
"remote_user_num_inactive": s.RemoteUserNumInactive.Val,
|
||||
"remote_user_rx_bytes": s.RemoteUserRxBytes.Val,
|
||||
"remote_user_tx_bytes": s.RemoteUserTxBytes.Val,
|
||||
"remote_user_rx_packets": s.RemoteUserRxPackets.Val,
|
||||
"remote_user_tx_packets": s.RemoteUserTxPackets.Val,
|
||||
"num_new_alarms": u.NumNewAlarms.Val,
|
||||
"nameservers": len(s.Nameservers),
|
||||
"gateways": len(s.Gateways),
|
||||
}
|
||||
pt, err := influx.NewPoint("subsystems", tags, fields, time.Now())
|
||||
if err != nil {
|
||||
return points, err
|
||||
}
|
||||
points = append(points, pt)
|
||||
}
|
||||
return points, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package unifi
|
||||
|
||||
// Sites is a struct to match Devices and Clients.
|
||||
type Sites []Site
|
||||
|
||||
// Site represents a site's data.
|
||||
type Site struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
Desc string `json:"desc"`
|
||||
AttrHiddenID string `json:"attr_hidden_id"`
|
||||
AttrNoDelete FlexBool `json:"attr_no_delete"`
|
||||
Health []struct {
|
||||
Subsystem string `json:"subsystem"`
|
||||
NumUser FlexInt `json:"num_user,omitempty"`
|
||||
NumGuest FlexInt `json:"num_guest,omitempty"`
|
||||
NumIot FlexInt `json:"num_iot,omitempty"`
|
||||
TxBytesR FlexInt `json:"tx_bytes-r,omitempty"`
|
||||
RxBytesR FlexInt `json:"rx_bytes-r,omitempty"`
|
||||
Status string `json:"status"`
|
||||
NumAp FlexInt `json:"num_ap,omitempty"`
|
||||
NumAdopted FlexInt `json:"num_adopted,omitempty"`
|
||||
NumDisabled FlexInt `json:"num_disabled,omitempty"`
|
||||
NumDisconnected FlexInt `json:"num_disconnected,omitempty"`
|
||||
NumPending FlexInt `json:"num_pending,omitempty"`
|
||||
NumGw FlexInt `json:"num_gw,omitempty"`
|
||||
WanIP string `json:"wan_ip,omitempty"`
|
||||
Gateways []string `json:"gateways,omitempty"`
|
||||
Netmask string `json:"netmask,omitempty"`
|
||||
Nameservers []string `json:"nameservers,omitempty"`
|
||||
NumSta FlexInt `json:"num_sta,omitempty"`
|
||||
GwMac string `json:"gw_mac,omitempty"`
|
||||
GwName string `json:"gw_name,omitempty"`
|
||||
GwSystemStats struct {
|
||||
CPU FlexInt `json:"cpu"`
|
||||
Mem FlexInt `json:"mem"`
|
||||
Uptime FlexInt `json:"uptime"`
|
||||
} `json:"gw_system-stats,omitempty"`
|
||||
GwVersion string `json:"gw_version,omitempty"`
|
||||
Latency FlexInt `json:"latency,omitempty"`
|
||||
Uptime FlexInt `json:"uptime,omitempty"`
|
||||
Drops FlexInt `json:"drops,omitempty"`
|
||||
XputUp FlexInt `json:"xput_up,omitempty"`
|
||||
XputDown FlexInt `json:"xput_down,omitempty"`
|
||||
SpeedtestStatus string `json:"speedtest_status,omitempty"`
|
||||
SpeedtestLastrun FlexInt `json:"speedtest_lastrun,omitempty"`
|
||||
SpeedtestPing FlexInt `json:"speedtest_ping,omitempty"`
|
||||
LanIP string `json:"lan_ip,omitempty"`
|
||||
NumSw FlexInt `json:"num_sw,omitempty"`
|
||||
RemoteUserEnabled FlexBool `json:"remote_user_enabled,omitempty"`
|
||||
RemoteUserNumActive FlexInt `json:"remote_user_num_active,omitempty"`
|
||||
RemoteUserNumInactive FlexInt `json:"remote_user_num_inactive,omitempty"`
|
||||
RemoteUserRxBytes FlexInt `json:"remote_user_rx_bytes,omitempty"`
|
||||
RemoteUserTxBytes FlexInt `json:"remote_user_tx_bytes,omitempty"`
|
||||
RemoteUserRxPackets FlexInt `json:"remote_user_rx_packets,omitempty"`
|
||||
RemoteUserTxPackets FlexInt `json:"remote_user_tx_packets,omitempty"`
|
||||
SiteToSiteEnabled FlexBool `json:"site_to_site_enabled,omitempty"`
|
||||
} `json:"health"`
|
||||
NumNewAlarms FlexInt `json:"num_new_alarms"`
|
||||
}
|
||||
|
|
@ -12,8 +12,10 @@ import (
|
|||
// This is a list of unifi API paths.
|
||||
// The %s in each string must be replaced with a Site.Name.
|
||||
const (
|
||||
// StatusPath shows Controller version.
|
||||
StatusPath string = "/status"
|
||||
// SiteList is the path to the api site list.
|
||||
SiteList string = "/api/self/sites"
|
||||
SiteList string = "/api/stat/sites"
|
||||
// ClientPath is Unifi Clients API Path
|
||||
ClientPath string = "/api/s/%s/stat/sta"
|
||||
// DevicePath is where we get data about Unifi devices.
|
||||
|
|
@ -52,11 +54,6 @@ type Devices struct {
|
|||
USWs []USW
|
||||
}
|
||||
|
||||
// Clients contains a list that contains all of the unifi clients from a controller.
|
||||
type Clients struct {
|
||||
UCLs []UCL
|
||||
}
|
||||
|
||||
// Unifi is what you get in return for providing a password! Unifi represents
|
||||
// a controller that you can make authenticated requests to. Use this to make
|
||||
// additional requests for devices, clients or other custom data.
|
||||
|
|
@ -67,11 +64,11 @@ type Unifi struct {
|
|||
DebugLog Logger
|
||||
}
|
||||
|
||||
// Site represents a site's data. There are more pieces to this, but this is
|
||||
// all we expose.
|
||||
type Site struct {
|
||||
Name string `json:"name"`
|
||||
Desc string `json:"desc"`
|
||||
// Server is the /status endpoint from the Unifi controller.
|
||||
type Server struct {
|
||||
Up FlexBool `json:"up"`
|
||||
ServerVersion string `json:"server_version"`
|
||||
UUID string `json:"uuid"`
|
||||
}
|
||||
|
||||
// FlexInt provides a container and unmarshalling for fields that may be
|
||||
|
|
@ -109,12 +106,15 @@ type FlexBool struct {
|
|||
Txt string
|
||||
}
|
||||
|
||||
// UnmarshalJSO method converts armed/disarmed, yes/no, active/inactive or 0/1 to true/false.
|
||||
// Really it converts ready, up, t, armed, yes, active, enabled, 1, true to true. Anything else is false.
|
||||
// UnmarshalJSON method converts armed/disarmed, yes/no, active/inactive or 0/1 to true/false.
|
||||
// Really it converts ready, ok, up, t, armed, yes, active, enabled, 1, true to true. Anything else is false.
|
||||
func (f *FlexBool) UnmarshalJSON(b []byte) error {
|
||||
f.Txt = strings.Trim(string(b), `"`)
|
||||
if f.Txt = strings.Trim(string(b), `"`); f.Txt == "" {
|
||||
f.Txt = "false"
|
||||
}
|
||||
f.Val = f.Txt == "1" || strings.EqualFold(f.Txt, "true") || strings.EqualFold(f.Txt, "yes") ||
|
||||
strings.EqualFold(f.Txt, "t") || strings.EqualFold(f.Txt, "armed") || strings.EqualFold(f.Txt, "active") ||
|
||||
strings.EqualFold(f.Txt, "enabled") || strings.EqualFold(f.Txt, "ready") || strings.EqualFold(f.Txt, "up")
|
||||
strings.EqualFold(f.Txt, "enabled") || strings.EqualFold(f.Txt, "ready") || strings.EqualFold(f.Txt, "up") ||
|
||||
strings.EqualFold(f.Txt, "ok")
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,15 +58,23 @@ func (u *Unifi) getController(user, pass string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetServer returns the controller's version and UUID.
|
||||
func (u *Unifi) GetServer() (Server, error) {
|
||||
var response struct {
|
||||
Data Server `json:"meta"`
|
||||
}
|
||||
err := u.GetData(StatusPath, &response)
|
||||
return response.Data, err
|
||||
}
|
||||
|
||||
// GetClients returns a response full of clients' data from the Unifi Controller.
|
||||
func (u *Unifi) GetClients(sites []Site) (*Clients, error) {
|
||||
data := make([]UCL, 0)
|
||||
func (u *Unifi) GetClients(sites []Site) (Clients, error) {
|
||||
data := make([]Client, 0)
|
||||
for _, site := range sites {
|
||||
var response struct {
|
||||
Data []UCL `json:"data"`
|
||||
Data []Client `json:"data"`
|
||||
}
|
||||
u.dLogf("Polling Site '%s' (%s) Clients", site.Name, site.Desc)
|
||||
u.dLogf("Unmarshalling Device Type: ucl")
|
||||
u.dLogf("Polling Controller, retreiving Unifi Clients, site %s (%s) ", site.Name, site.Desc)
|
||||
clientPath := fmt.Sprintf(ClientPath, site.Name)
|
||||
if err := u.GetData(clientPath, &response); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -76,14 +84,13 @@ func (u *Unifi) GetClients(sites []Site) (*Clients, error) {
|
|||
}
|
||||
data = append(data, response.Data...)
|
||||
}
|
||||
return &Clients{UCLs: data}, nil
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// GetDevices returns a response full of devices' data from the Unifi Controller.
|
||||
func (u *Unifi) GetDevices(sites []Site) (*Devices, error) {
|
||||
devices := new(Devices)
|
||||
for _, site := range sites {
|
||||
u.dLogf("Polling Site '%s' (%s) Devices", site.Name, site.Desc)
|
||||
var response struct {
|
||||
Data []json.RawMessage `json:"data"`
|
||||
}
|
||||
|
|
@ -116,7 +123,7 @@ func (u *Unifi) GetDevices(sites []Site) (*Devices, error) {
|
|||
}
|
||||
|
||||
// GetSites returns a list of configured sites on the Unifi controller.
|
||||
func (u *Unifi) GetSites() ([]Site, error) {
|
||||
func (u *Unifi) GetSites() (Sites, error) {
|
||||
var response struct {
|
||||
Data []Site `json:"data"`
|
||||
}
|
||||
|
|
@ -127,7 +134,7 @@ func (u *Unifi) GetSites() ([]Site, error) {
|
|||
for i := range response.Data {
|
||||
sites = append(sites, response.Data[i].Name)
|
||||
}
|
||||
u.dLogf("Found %d sites: %s", len(sites), strings.Join(sites, ","))
|
||||
u.dLogf("Found %d site(s): %s", len(sites), strings.Join(sites, ","))
|
||||
return response.Data, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue