Rename a few pieces.

This commit is contained in:
David Newhall II 2019-01-26 04:15:41 -08:00
parent 7256b15467
commit d2862befc0
10 changed files with 147 additions and 141 deletions

View File

@ -7,95 +7,96 @@ import (
influx "github.com/influxdata/influxdb/client/v2" influx "github.com/influxdata/influxdb/client/v2"
) )
// Points generates a client's datapoints for InfluxDB. // Points generates Unifi Client datapoints for InfluxDB.
func (u UCL) Points() ([]*influx.Point, error) { // These points can be passed directly to influx.
func (c *UCL) Points() ([]*influx.Point, error) {
var points []*influx.Point var points []*influx.Point
// Fix name and hostname fields. Sometimes one or the other is blank. // Fix name and hostname fields. Sometimes one or the other is blank.
if u.Name == "" && u.Hostname != "" { if c.Name == "" && c.Hostname != "" {
u.Name = u.Hostname c.Name = c.Hostname
} else if u.Hostname == "" && u.Name != "" { } else if c.Hostname == "" && c.Name != "" {
u.Hostname = u.Name c.Hostname = c.Name
} else if u.Hostname == "" && u.Name == "" { } else if c.Hostname == "" && c.Name == "" {
u.Hostname = "-no-name-" c.Hostname = "-no-name-"
u.Name = "-no-name-" c.Name = "-no-name-"
} }
tags := map[string]string{ tags := map[string]string{
"id": u.ID, "id": c.ID,
"mac": u.Mac, "mac": c.Mac,
"user_id": u.UserID, "user_id": c.UserID,
"site_id": u.SiteID, "site_id": c.SiteID,
"network_id": u.NetworkID, "network_id": c.NetworkID,
"usergroup_id": u.UserGroupID, "usergroup_id": c.UserGroupID,
"ap_mac": u.ApMac, "ap_mac": c.ApMac,
"gw_mac": u.GwMac, "gw_mac": c.GwMac,
"sw_mac": u.SwMac, "sw_mac": c.SwMac,
"oui": u.Oui, "oui": c.Oui,
"radio_name": u.RadioName, "radio_name": c.RadioName,
"radio": u.Radio, "radio": c.Radio,
"radio_proto": u.RadioProto, "radio_proto": c.RadioProto,
"name": u.Name, "name": c.Name,
"fixed_ip": u.FixedIP, "fixed_ip": c.FixedIP,
"sw_port": strconv.Itoa(u.SwPort), "sw_port": strconv.Itoa(c.SwPort),
"os_class": strconv.Itoa(u.OsClass), "os_class": strconv.Itoa(c.OsClass),
"os_name": strconv.Itoa(u.OsName), "os_name": strconv.Itoa(c.OsName),
"dev_cat": strconv.Itoa(u.DevCat), "dev_cat": strconv.Itoa(c.DevCat),
"dev_id": strconv.Itoa(u.DevID), "dev_id": strconv.Itoa(c.DevID),
"dev_family": strconv.Itoa(u.DevFamily), "dev_family": strconv.Itoa(c.DevFamily),
"authorized": strconv.FormatBool(u.Authorized), "authorized": strconv.FormatBool(c.Authorized),
"is_11r": strconv.FormatBool(u.Is11R), "is_11r": strconv.FormatBool(c.Is11R),
"is_wired": strconv.FormatBool(u.IsWired), "is_wired": strconv.FormatBool(c.IsWired),
"is_guest": strconv.FormatBool(u.IsGuest), "is_guest": strconv.FormatBool(c.IsGuest),
"is_guest_by_uap": strconv.FormatBool(u.IsGuestByUAP), "is_guest_by_uap": strconv.FormatBool(c.IsGuestByUAP),
"is_guest_by_ugw": strconv.FormatBool(u.IsGuestByUGW), "is_guest_by_ugw": strconv.FormatBool(c.IsGuestByUGW),
"is_guest_by_usw": strconv.FormatBool(u.IsGuestByUSW), "is_guest_by_usw": strconv.FormatBool(c.IsGuestByUSW),
"noted": strconv.FormatBool(u.Noted), "noted": strconv.FormatBool(c.Noted),
"powersave_enabled": strconv.FormatBool(u.PowersaveEnabled), "powersave_enabled": strconv.FormatBool(c.PowersaveEnabled),
"qos_policy_applied": strconv.FormatBool(u.QosPolicyApplied), "qos_policy_applied": strconv.FormatBool(c.QosPolicyApplied),
"use_fixedip": strconv.FormatBool(u.UseFixedIP), "use_fixedip": strconv.FormatBool(c.UseFixedIP),
"channel": strconv.Itoa(u.Channel), "channel": strconv.Itoa(c.Channel),
"vlan": strconv.Itoa(u.Vlan), "vlan": strconv.Itoa(c.Vlan),
} }
fields := map[string]interface{}{ fields := map[string]interface{}{
"ip": u.IP, "ip": c.IP,
"essid": u.Essid, "essid": c.Essid,
"bssid": u.Bssid, "bssid": c.Bssid,
"hostname": u.Hostname, "hostname": c.Hostname,
"dpi_stats_last_updated": u.DpiStatsLastUpdated, "dpi_stats_last_updated": c.DpiStatsLastUpdated,
"last_seen_by_uap": u.LastSeenByUAP, "last_seen_by_uap": c.LastSeenByUAP,
"last_seen_by_ugw": u.LastSeenByUGW, "last_seen_by_ugw": c.LastSeenByUGW,
"last_seen_by_usw": u.LastSeenByUSW, "last_seen_by_usw": c.LastSeenByUSW,
"uptime_by_uap": u.UptimeByUAP, "uptime_by_uap": c.UptimeByUAP,
"uptime_by_ugw": u.UptimeByUGW, "uptime_by_ugw": c.UptimeByUGW,
"uptime_by_usw": u.UptimeByUSW, "uptime_by_usw": c.UptimeByUSW,
"assoc_time": u.AssocTime, "assoc_time": c.AssocTime,
"bytes_r": u.BytesR, "bytes_r": c.BytesR,
"ccq": u.Ccq, "ccq": c.Ccq,
"first_seen": u.FirstSeen, "first_seen": c.FirstSeen,
"idle_time": u.IdleTime, "idle_time": c.IdleTime,
"last_seen": u.LastSeen, "last_seen": c.LastSeen,
"latest_assoc_time": u.LatestAssocTime, "latest_assoc_time": c.LatestAssocTime,
"network": u.Network, "network": c.Network,
"noise": u.Noise, "noise": c.Noise,
"note": u.Note, "note": c.Note,
"roam_count": u.RoamCount, "roam_count": c.RoamCount,
"rssi": u.Rssi, "rssi": c.Rssi,
"rx_bytes": u.RxBytes, "rx_bytes": c.RxBytes,
"rx_bytes_r": u.RxBytesR, "rx_bytes_r": c.RxBytesR,
"rx_packets": u.RxPackets, "rx_packets": c.RxPackets,
"rx_rate": u.RxRate, "rx_rate": c.RxRate,
"signal": u.Signal, "signal": c.Signal,
"tx_bytes": u.TxBytes, "tx_bytes": c.TxBytes,
"tx_bytes_r": u.TxBytesR, "tx_bytes_r": c.TxBytesR,
"tx_packets": u.TxPackets, "tx_packets": c.TxPackets,
"tx_power": u.TxPower, "tx_power": c.TxPower,
"tx_rate": u.TxRate, "tx_rate": c.TxRate,
"uptime": u.Uptime, "uptime": c.Uptime,
"wired-rx_bytes": u.WiredRxBytes, "wired-rx_bytes": c.WiredRxBytes,
"wired-rx_bytes-r": u.WiredRxBytesR, "wired-rx_bytes-r": c.WiredRxBytesR,
"wired-rx_packets": u.WiredRxPackets, "wired-rx_packets": c.WiredRxPackets,
"wired-tx_bytes": u.WiredTxBytes, "wired-tx_bytes": c.WiredTxBytes,
"wired-tx_bytes-r": u.WiredTxBytesR, "wired-tx_bytes-r": c.WiredTxBytesR,
"wired-tx_packets": u.WiredTxPackets, "wired-tx_packets": c.WiredTxPackets,
} }
pt, err := influx.NewPoint("clients", tags, fields, time.Now()) pt, err := influx.NewPoint("clients", tags, fields, time.Now())
if err == nil { if err == nil {

View File

@ -7,8 +7,9 @@ import (
influx "github.com/influxdata/influxdb/client/v2" influx "github.com/influxdata/influxdb/client/v2"
) )
// Points generates a device's datapoints for InfluxDB. // Points generates Wireless-Access-Point datapoints for InfluxDB.
func (u UAP) Points() ([]*influx.Point, error) { // These points can be passed directly to influx.
func (u *UAP) Points() ([]*influx.Point, error) {
/* I generally suck at InfluxDB, so if I got the tags/fields wrong, /* I generally suck at InfluxDB, so if I got the tags/fields wrong,
please send me a PR or open an Issue to address my faults. Thanks! please send me a PR or open an Issue to address my faults. Thanks!
*/ */

View File

@ -1,6 +1,6 @@
package unifi package unifi
// UAP is a Unifi Access Point. // UAP represents all the data from the Ubiquit Controller for a Unifi Access Point.
type UAP struct { type UAP struct {
/* This was auto generated and then slowly edited by hand /* This was auto generated and then slowly edited by hand
to get all the data types right and graphable. to get all the data types right and graphable.

View File

@ -11,12 +11,22 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
const (
// ClientPath is Unifi Clients API Path
ClientPath = "/api/s/default/stat/sta"
// DevicePath is where we get data about Unifi devices.
DevicePath = "/api/s/default/stat/device"
// NetworkPath contains network-configuration data. Not really graphable.
NetworkPath = "/api/s/default/rest/networkconf"
// UserGroupPath contains usergroup configurations.
UserGroupPath = "/api/s/default/rest/usergroup"
// LoginPath is Unifi Controller Login API Path
LoginPath = "/api/login"
)
// Logger is a base type to deal with changing log outs. // Logger is a base type to deal with changing log outs.
type Logger func(msg string, fmt ...interface{}) type Logger func(msg string, fmt ...interface{})
// LoginPath is Unifi Controller Login API Path
const LoginPath = "/api/login"
// Devices contains a list of all the unifi devices from a controller. // Devices contains a list of all the unifi devices from a controller.
type Devices struct { type Devices struct {
UAPs []UAP UAPs []UAP
@ -24,8 +34,8 @@ type Devices struct {
USWs []USW USWs []USW
} }
// AuthedReq is what you get in return for providing a password! // Unifi is what you get in return for providing a password!
type AuthedReq struct { type Unifi struct {
*http.Client *http.Client
baseURL string baseURL string
ErrorLog Logger ErrorLog Logger
@ -61,40 +71,40 @@ func (f *FlexInt) UnmarshalJSON(b []byte) error {
// AuthController creates a http.Client with authenticated cookies. // AuthController creates a http.Client with authenticated cookies.
// Used to make additional, authenticated requests to the APIs. // Used to make additional, authenticated requests to the APIs.
func AuthController(user, pass, url string, verifySSL bool) (*AuthedReq, error) { func AuthController(user, pass, url string, verifySSL bool) (*Unifi, error) {
json := `{"username": "` + user + `","password": "` + pass + `"}` json := `{"username": "` + user + `","password": "` + pass + `"}`
jar, err := cookiejar.New(nil) jar, err := cookiejar.New(nil)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "cookiejar.New(nil)") return nil, errors.Wrap(err, "cookiejar.New(nil)")
} }
a := &AuthedReq{Client: &http.Client{ u := &Unifi{Client: &http.Client{
Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: !verifySSL}}, Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: !verifySSL}},
Jar: jar, Jar: jar,
}, baseURL: url} }, baseURL: url}
req, err := a.UniReq(LoginPath, json) req, err := u.UniReq(LoginPath, json)
if err != nil { if err != nil {
return a, errors.Wrap(err, "UniReq(LoginPath, json)") return u, errors.Wrap(err, "UniReq(LoginPath, json)")
} }
resp, err := a.Do(req) resp, err := u.Do(req)
if err != nil { if err != nil {
return a, errors.Wrap(err, "authReq.Do(req)") return u, errors.Wrap(err, "authReq.Do(req)")
} }
defer func() { defer func() {
_ = resp.Body.Close() _ = resp.Body.Close()
}() }()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return a, errors.Errorf("authentication failed (%v): %v (status: %v/%v)", return u, errors.Errorf("authentication failed (%v): %v (status: %v/%v)",
user, url+LoginPath, resp.StatusCode, resp.Status) user, url+LoginPath, resp.StatusCode, resp.Status)
} }
return a, nil return u, nil
} }
// UniReq is a small helper function that adds an Accept header. // UniReq is a small helper function that adds an Accept header.
func (a AuthedReq) UniReq(apiPath string, params string) (req *http.Request, err error) { func (u *Unifi) UniReq(apiPath string, params string) (req *http.Request, err error) {
if params != "" { if params != "" {
req, err = http.NewRequest("POST", a.baseURL+apiPath, bytes.NewBufferString(params)) req, err = http.NewRequest("POST", u.baseURL+apiPath, bytes.NewBufferString(params))
} else { } else {
req, err = http.NewRequest("GET", a.baseURL+apiPath, nil) req, err = http.NewRequest("GET", u.baseURL+apiPath, nil)
} }
if err == nil { if err == nil {
req.Header.Add("Accept", "application/json") req.Header.Add("Accept", "application/json")
@ -102,14 +112,16 @@ func (a AuthedReq) UniReq(apiPath string, params string) (req *http.Request, err
return return
} }
func (a AuthedReq) dLogf(msg string, v ...interface{}) { // dLogf logs a debug message.
if a.DebugLog != nil { func (u *Unifi) dLogf(msg string, v ...interface{}) {
a.DebugLog("[DEBUG] "+msg, v...) if u.DebugLog != nil {
u.DebugLog("[DEBUG] "+msg, v...)
} }
} }
func (a AuthedReq) eLogf(msg string, v ...interface{}) { // dLogf logs an error message.
if a.ErrorLog != nil { func (u *Unifi) eLogf(msg string, v ...interface{}) {
a.ErrorLog("[ERROR] "+msg, v...) if u.ErrorLog != nil {
u.ErrorLog("[ERROR] "+msg, v...)
} }
} }

View File

@ -43,7 +43,7 @@ func TestUniReq(t *testing.T) {
u := "/test/path" u := "/test/path"
url := "http://some.url:8443" url := "http://some.url:8443"
// Test empty parameters. // Test empty parameters.
authReq := &AuthedReq{Client: &http.Client{}, baseURL: url} authReq := &Unifi{Client: &http.Client{}, baseURL: url}
r, err := authReq.UniReq(u, "") r, err := authReq.UniReq(u, "")
a.Nil(err, "newrequest must not produce an error") a.Nil(err, "newrequest must not produce an error")
a.EqualValues(u, r.URL.Path, a.EqualValues(u, r.URL.Path,
@ -54,7 +54,7 @@ func TestUniReq(t *testing.T) {
// Test with parameters // Test with parameters
p := "key1=value9&key2=value7" p := "key1=value9&key2=value7"
authReq = &AuthedReq{Client: &http.Client{}, baseURL: "http://some.url:8443"} authReq = &Unifi{Client: &http.Client{}, baseURL: "http://some.url:8443"}
r, err = authReq.UniReq(u, p) r, err = authReq.UniReq(u, p)
a.Nil(err, "newrequest must not produce an error") a.Nil(err, "newrequest must not produce an error")
a.EqualValues(u, r.URL.Path, a.EqualValues(u, r.URL.Path,

View File

@ -7,27 +7,16 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
const (
// ClientPath is Unifi Clients API Path
ClientPath = "/api/s/default/stat/sta"
// DevicePath is where we get data about Unifi devices.
DevicePath = "/api/s/default/stat/device"
// NetworkPath contains network-configuration data. Not really graphable.
NetworkPath = "/api/s/default/rest/networkconf"
// UserGroupPath contains usergroup configurations.
UserGroupPath = "/api/s/default/rest/usergroup"
)
// GetUnifiClients returns a response full of clients' data from the Unifi Controller. // GetUnifiClients returns a response full of clients' data from the Unifi Controller.
func (c *AuthedReq) GetUnifiClients() ([]UCL, error) { func (u *Unifi) GetUnifiClients() ([]UCL, error) {
var response struct { var response struct {
Clients []UCL `json:"data"` Clients []UCL `json:"data"`
} }
req, err := c.UniReq(ClientPath, "") req, err := u.UniReq(ClientPath, "")
if err != nil { if err != nil {
return nil, errors.Wrap(err, "c.UniReq(ClientPath)") return nil, errors.Wrap(err, "c.UniReq(ClientPath)")
} }
resp, err := c.Do(req) resp, err := u.Do(req)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "c.Do(req)") return nil, errors.Wrap(err, "c.Do(req)")
} }
@ -43,15 +32,15 @@ func (c *AuthedReq) GetUnifiClients() ([]UCL, error) {
} }
// GetUnifiDevices returns a response full of devices' data from the Unifi Controller. // GetUnifiDevices returns a response full of devices' data from the Unifi Controller.
func (c *AuthedReq) GetUnifiDevices() (*Devices, error) { func (u *Unifi) GetUnifiDevices() (*Devices, error) {
var parsed struct { var parsed struct {
Data []json.RawMessage `json:"data"` Data []json.RawMessage `json:"data"`
} }
req, err := c.UniReq(DevicePath, "") req, err := u.UniReq(DevicePath, "")
if err != nil { if err != nil {
return nil, errors.Wrap(err, "c.UniReq(DevicePath)") return nil, errors.Wrap(err, "c.UniReq(DevicePath)")
} }
resp, err := c.Do(req) resp, err := u.Do(req)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "c.Do(req)") return nil, errors.Wrap(err, "c.Do(req)")
} }
@ -63,10 +52,11 @@ func (c *AuthedReq) GetUnifiDevices() (*Devices, error) {
} else if err = json.Unmarshal(body, &parsed); err != nil { } else if err = json.Unmarshal(body, &parsed); err != nil {
return nil, errors.Wrap(err, "json.Unmarshal([]json.RawMessage)") return nil, errors.Wrap(err, "json.Unmarshal([]json.RawMessage)")
} }
return c.parseUnifiDevices(parsed.Data), nil return u.parseUnifiDevices(parsed.Data), nil
} }
func (c *AuthedReq) parseUnifiDevices(data []json.RawMessage) *Devices { // parseUnifiDevices parses the raw JSON from the Unifi Controller into device structures.
func (u *Unifi) parseUnifiDevices(data []json.RawMessage) *Devices {
devices := new(Devices) devices := new(Devices)
// Loop each item in the raw JSON message, detect its type and unmarshal it. // Loop each item in the raw JSON message, detect its type and unmarshal it.
for i, r := range data { for i, r := range data {
@ -76,36 +66,36 @@ func (c *AuthedReq) parseUnifiDevices(data []json.RawMessage) *Devices {
// Unamrshal into a map and check "type" // Unamrshal into a map and check "type"
var obj map[string]interface{} var obj map[string]interface{}
if err := json.Unmarshal(r, &obj); err != nil { if err := json.Unmarshal(r, &obj); err != nil {
c.eLogf("[%d] json.Unmarshal(interfce{}): %v", i, err) u.eLogf("%d: json.Unmarshal(interfce{}): %v", i, err)
continue continue
} }
assetType := "<missing>" assetType := "<missing>"
if t, ok := obj["type"].(string); ok { if t, ok := obj["type"].(string); ok {
assetType = t assetType = t
} }
c.dLogf("Unmarshalling Device Type:", assetType) u.dLogf("Unmarshalling Device Type:", assetType)
// Unmarshal again into the correct type.. // Unmarshal again into the correct type..
switch assetType { switch assetType {
case "uap": case "uap":
if err := json.Unmarshal(r, &uap); err != nil { if err := json.Unmarshal(r, &uap); err != nil {
c.eLogf("[%d] json.Unmarshal([]UAP): %v", i, err) u.eLogf("%d: json.Unmarshal([]UAP): %v", i, err)
continue continue
} }
devices.UAPs = append(devices.UAPs, uap) devices.UAPs = append(devices.UAPs, uap)
case "ugw", "usg": // in case they ever fix the name in the api. case "ugw", "usg": // in case they ever fix the name in the api.
if err := json.Unmarshal(r, &usg); err != nil { if err := json.Unmarshal(r, &usg); err != nil {
c.eLogf("[%d] json.Unmarshal([]USG): %v", i, err) u.eLogf("%d: json.Unmarshal([]USG): %v", i, err)
continue continue
} }
devices.USGs = append(devices.USGs, usg) devices.USGs = append(devices.USGs, usg)
case "usw": case "usw":
if err := json.Unmarshal(r, &usw); err != nil { if err := json.Unmarshal(r, &usw); err != nil {
c.eLogf("[%d] json.Unmarshal([]USW): %v", i, err) u.eLogf("%d: json.Unmarshal([]USW): %v", i, err)
continue continue
} }
devices.USWs = append(devices.USWs, usw) devices.USWs = append(devices.USWs, usw)
default: default:
c.dLogf("unknown asset type -", assetType, "- skipping") u.dLogf("unknown asset type -", assetType, "- skipping")
continue continue
} }
} }

View File

@ -7,8 +7,9 @@ import (
influx "github.com/influxdata/influxdb/client/v2" influx "github.com/influxdata/influxdb/client/v2"
) )
// Points generates a device's datapoints for InfluxDB. // Points generates Unifi Gateway datapoints for InfluxDB.
func (u USG) Points() ([]*influx.Point, error) { // These points can be passed directly to influx.
func (u *USG) Points() ([]*influx.Point, error) {
var points []*influx.Point var points []*influx.Point
tags := map[string]string{ tags := map[string]string{
"id": u.ID, "id": u.ID,

View File

@ -2,7 +2,7 @@ package unifi
import "encoding/json" import "encoding/json"
// USG is a Unifi Security Gateway // USG represents all the data from the Ubiquit Controller for a Unifi Security Gateway.
type USG struct { type USG struct {
ID string `json:"_id"` ID string `json:"_id"`
UUptime float64 `json:"_uptime"` UUptime float64 `json:"_uptime"`

View File

@ -7,8 +7,9 @@ import (
influx "github.com/influxdata/influxdb/client/v2" influx "github.com/influxdata/influxdb/client/v2"
) )
// Points generates a device's datapoints for InfluxDB. // Points generates Unifi Switch datapoints for InfluxDB.
func (u USW) Points() ([]*influx.Point, error) { // These points can be passed directly to influx.
func (u *USW) Points() ([]*influx.Point, error) {
var points []*influx.Point var points []*influx.Point
tags := map[string]string{ tags := map[string]string{
"id": u.ID, "id": u.ID,

View File

@ -1,6 +1,6 @@
package unifi package unifi
// USW is a Unifi Switch // USW represents all the data from the Ubiquit Controller for a Unifi Switch.
type USW struct { type USW struct {
ID string `json:"_id"` ID string `json:"_id"`
UUptime float64 `json:"_uptime"` UUptime float64 `json:"_uptime"`