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
|
- curl -sLo $GOPATH/bin/dep https://github.com/golang/dep/releases/download/v0.5.3/dep-linux-amd64
|
||||||
- chmod +x $GOPATH/bin/dep
|
- chmod +x $GOPATH/bin/dep
|
||||||
# download super-linter: golangci-lint
|
# 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:
|
install:
|
||||||
- dep ensure
|
- dep ensure
|
||||||
script:
|
script:
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println(len(sites), "Unifi Sites Found: ", sites)
|
log.Println(len(sites), "Unifi Sites Found: ", sites)
|
||||||
log.Println(len(clients.UCLs), "Clients connected:")
|
log.Println(len(clients), "Clients connected:")
|
||||||
for i, client := range clients.UCLs {
|
for i, client := range clients {
|
||||||
log.Println(i+1, client.ID, client.Hostname, client.IP, client.Name, client.LastSeen)
|
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.
|
// Points generates Unifi Client datapoints for InfluxDB.
|
||||||
// These points can be passed directly to influx.
|
// 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.
|
// Fix name and hostname fields. Sometimes one or the other is blank.
|
||||||
switch {
|
switch {
|
||||||
case c.Hostname == "" && c.Name == "":
|
case c.Hostname == "" && c.Name == "":
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
package unifi
|
package unifi
|
||||||
|
|
||||||
// UCL defines all the data a connected-network client contains.
|
// Clients contains a list that contains all of the unifi clients from a controller.
|
||||||
type UCL struct {
|
type Clients []Client
|
||||||
|
|
||||||
|
// Client defines all the data a connected-network client contains.
|
||||||
|
type Client struct {
|
||||||
ID string `json:"_id"`
|
ID string `json:"_id"`
|
||||||
IsGuestByUAP FlexBool `json:"_is_guest_by_uap"`
|
IsGuestByUAP FlexBool `json:"_is_guest_by_uap"`
|
||||||
IsGuestByUGW FlexBool `json:"_is_guest_by_ugw"`
|
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 {
|
} else if t, ok := o["type"].(string); ok {
|
||||||
assetType = t
|
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.
|
// Choose which type to unmarshal into based on the "type" json key.
|
||||||
switch assetType { // Unmarshal again into the correct type..
|
switch assetType { // Unmarshal again into the correct type..
|
||||||
case "uap":
|
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.
|
// This is a list of unifi API paths.
|
||||||
// The %s in each string must be replaced with a Site.Name.
|
// The %s in each string must be replaced with a Site.Name.
|
||||||
const (
|
const (
|
||||||
|
// StatusPath shows Controller version.
|
||||||
|
StatusPath string = "/status"
|
||||||
// SiteList is the path to the api site list.
|
// 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 is Unifi Clients API Path
|
||||||
ClientPath string = "/api/s/%s/stat/sta"
|
ClientPath string = "/api/s/%s/stat/sta"
|
||||||
// DevicePath is where we get data about Unifi devices.
|
// DevicePath is where we get data about Unifi devices.
|
||||||
|
|
@ -52,11 +54,6 @@ type Devices struct {
|
||||||
USWs []USW
|
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
|
// 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
|
// a controller that you can make authenticated requests to. Use this to make
|
||||||
// additional requests for devices, clients or other custom data.
|
// additional requests for devices, clients or other custom data.
|
||||||
|
|
@ -67,11 +64,11 @@ type Unifi struct {
|
||||||
DebugLog Logger
|
DebugLog Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// Site represents a site's data. There are more pieces to this, but this is
|
// Server is the /status endpoint from the Unifi controller.
|
||||||
// all we expose.
|
type Server struct {
|
||||||
type Site struct {
|
Up FlexBool `json:"up"`
|
||||||
Name string `json:"name"`
|
ServerVersion string `json:"server_version"`
|
||||||
Desc string `json:"desc"`
|
UUID string `json:"uuid"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlexInt provides a container and unmarshalling for fields that may be
|
// FlexInt provides a container and unmarshalling for fields that may be
|
||||||
|
|
@ -109,12 +106,15 @@ type FlexBool struct {
|
||||||
Txt string
|
Txt string
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSO method converts armed/disarmed, yes/no, active/inactive or 0/1 to true/false.
|
// UnmarshalJSON 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.
|
// 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 {
|
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") ||
|
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, "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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,15 +58,23 @@ func (u *Unifi) getController(user, pass string) error {
|
||||||
return nil
|
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.
|
// GetClients returns a response full of clients' data from the Unifi Controller.
|
||||||
func (u *Unifi) GetClients(sites []Site) (*Clients, error) {
|
func (u *Unifi) GetClients(sites []Site) (Clients, error) {
|
||||||
data := make([]UCL, 0)
|
data := make([]Client, 0)
|
||||||
for _, site := range sites {
|
for _, site := range sites {
|
||||||
var response struct {
|
var response struct {
|
||||||
Data []UCL `json:"data"`
|
Data []Client `json:"data"`
|
||||||
}
|
}
|
||||||
u.dLogf("Polling Site '%s' (%s) Clients", site.Name, site.Desc)
|
u.dLogf("Polling Controller, retreiving Unifi Clients, site %s (%s) ", site.Name, site.Desc)
|
||||||
u.dLogf("Unmarshalling Device Type: ucl")
|
|
||||||
clientPath := fmt.Sprintf(ClientPath, site.Name)
|
clientPath := fmt.Sprintf(ClientPath, site.Name)
|
||||||
if err := u.GetData(clientPath, &response); err != nil {
|
if err := u.GetData(clientPath, &response); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -76,14 +84,13 @@ func (u *Unifi) GetClients(sites []Site) (*Clients, error) {
|
||||||
}
|
}
|
||||||
data = append(data, response.Data...)
|
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.
|
// GetDevices returns a response full of devices' data from the Unifi Controller.
|
||||||
func (u *Unifi) GetDevices(sites []Site) (*Devices, error) {
|
func (u *Unifi) GetDevices(sites []Site) (*Devices, error) {
|
||||||
devices := new(Devices)
|
devices := new(Devices)
|
||||||
for _, site := range sites {
|
for _, site := range sites {
|
||||||
u.dLogf("Polling Site '%s' (%s) Devices", site.Name, site.Desc)
|
|
||||||
var response struct {
|
var response struct {
|
||||||
Data []json.RawMessage `json:"data"`
|
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.
|
// 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 {
|
var response struct {
|
||||||
Data []Site `json:"data"`
|
Data []Site `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
@ -127,7 +134,7 @@ func (u *Unifi) GetSites() ([]Site, error) {
|
||||||
for i := range response.Data {
|
for i := range response.Data {
|
||||||
sites = append(sites, response.Data[i].Name)
|
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
|
return response.Data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue