Merge pull request #6 from golift/dn2_cleaanup

Cleanup and re-arrangements.
This commit is contained in:
David Newhall II 2019-04-17 02:15:18 -07:00 committed by GitHub
commit cde65e6078
14 changed files with 317 additions and 289 deletions

View File

@ -8,6 +8,8 @@ data. The data is provided in a large struct you can consume in your application
This library also contains methods to export the Unifi data in InfluxDB format,
and this can be used as an example to base your own metrics collection methods.
If more features are requested, I'll certainly consider them. Do you need to do
more than just collect data? [Let me know](https://github.com/golift/unifi/issues/new)!
Pull requests and feedback are welcomed!
Here's a working example:
@ -21,25 +23,32 @@ func main() {
username := "admin"
password := "superSecret1234"
URL := "https://127.0.0.1:8443/"
uni, err := unifi.GetController(username, password, URL, false)
uni, err := unifi.NewUnifi(username, password, URL, false)
if err != nil {
log.Fatalln("Error:", err)
}
// Log with log.Printf or make your own interface that accepts (msg, fmt)
uni.ErrorLog = log.Printf
uni.DebugLog = log.Printf
clients, err := uni.GetClients()
sites, err := uni.GetSites()
if err != nil {
log.Fatalln("Error:", err)
}
clients, err := uni.GetClients(sites)
if err != nil {
log.Fatalln("Error:", err)
}
devices, err := uni.GetDevices(sites)
if err != nil {
log.Fatalln("Error:", err)
}
log.Println(len(clients.UCLs), "Clients connected:")
for i, client := range clients.UCLs {
log.Println(i+1, client.ID, client.Hostname, client.IP, client.Name, client.LastSeen)
}
devices, err := uni.GetDevices()
if err != nil {
log.Fatalln("Error:", err)
}
log.Println(len(devices.USWs), "Unifi Switches Found")
log.Println(len(devices.USGs), "Unifi Gateways Found")
@ -48,5 +57,4 @@ func main() {
log.Println(i+1, uap.Name, uap.IP)
}
}
```

View File

@ -10,15 +10,15 @@ import (
// Points generates Unifi Client datapoints for InfluxDB.
// These points can be passed directly to influx.
func (c UCL) Points() ([]*influx.Point, error) {
var points []*influx.Point
// Fix name and hostname fields. Sometimes one or the other is blank.
if c.Name == "" && c.Hostname != "" {
c.Name = c.Hostname
} else if c.Hostname == "" && c.Name != "" {
c.Hostname = c.Name
} else if c.Hostname == "" && c.Name == "" {
switch {
case c.Hostname == "" && c.Name == "":
c.Hostname = "-no-name-"
c.Name = "-no-name-"
case c.Hostname == "" && c.Name != "":
c.Hostname = c.Name
case c.Name == "" && c.Hostname != "":
c.Name = c.Hostname
}
tags := map[string]string{
"id": c.ID,
@ -100,7 +100,7 @@ func (c UCL) Points() ([]*influx.Point, error) {
}
pt, err := influx.NewPoint("clients", tags, fields, time.Now())
if err == nil {
points = append(points, pt)
return nil, err
}
return points, err
return []*influx.Point{pt}, nil
}

49
core/unifi/parsers.go Normal file
View File

@ -0,0 +1,49 @@
package unifi
import "encoding/json"
// parseDevices parses the raw JSON from the Unifi Controller into device structures.
func (u *Unifi) parseDevices(data []json.RawMessage) *Devices {
devices := new(Devices)
for _, r := range data {
// Loop each item in the raw JSON message, detect its type and unmarshal it.
assetType := "<type key missing>"
if o := make(map[string]interface{}); u.unmarshalDevice("map", r, &o) != nil {
continue
} else if t, ok := o["type"].(string); ok {
assetType = t
}
u.dLogf("Unmarshalling Device Type: %v", assetType)
// Choose which type to unmarshal into based on the "type" json key.
switch assetType { // Unmarshal again into the correct type..
case "uap":
if uap := (UAP{}); u.unmarshalDevice(assetType, r, &uap) == nil {
devices.UAPs = append(devices.UAPs, uap)
}
case "ugw", "usg": // in case they ever fix the name in the api.
if usg := (USG{}); u.unmarshalDevice(assetType, r, &usg) == nil {
devices.USGs = append(devices.USGs, usg)
}
case "usw":
if usw := (USW{}); u.unmarshalDevice(assetType, r, &usw) == nil {
devices.USWs = append(devices.USWs, usw)
}
default:
u.eLogf("unknown asset type - %v - skipping", assetType)
}
}
return devices
}
// unmarshalDevice handles logging for the unmarshal operations in parseDevices().
func (u *Unifi) unmarshalDevice(dev string, data json.RawMessage, v interface{}) (err error) {
if err = json.Unmarshal(data, v); err != nil {
u.eLogf("json.Unmarshal(%v): %v", dev, err)
u.eLogf("Enable Debug Logging to output the failed payload.")
json, err := data.MarshalJSON()
u.dLogf("Failed Payload: %s (marshal err: %v)", json, err)
u.dLogf("The above payload can prove useful during torubleshooting when you open an Issue:")
u.dLogf("==- https://github.com/golift/unifi/issues/new -==")
}
return err
}

120
core/unifi/types.go Normal file
View File

@ -0,0 +1,120 @@
package unifi
import (
"encoding/json"
"net/http"
"strconv"
"strings"
"github.com/pkg/errors"
)
// This is a list of unifi API paths.
// The %s in each string must be replaced with a Site.Name.
const (
// SiteList is the path to the api site list.
SiteList string = "/api/self/sites"
// ClientPath is Unifi Clients API Path
ClientPath string = "/api/s/%s/stat/sta"
// DevicePath is where we get data about Unifi devices.
DevicePath string = "/api/s/%s/stat/device"
// NetworkPath contains network-configuration data. Not really graphable.
NetworkPath string = "/api/s/%s/rest/networkconf"
// UserGroupPath contains usergroup configurations.
UserGroupPath string = "/api/s/%s/rest/usergroup"
// LoginPath is Unifi Controller Login API Path
LoginPath string = "/api/login"
)
// Logger is a base type to deal with changing log outputs. Create a logger
// that matches this interface to capture debug and error logs.
type Logger func(msg string, fmt ...interface{})
// dLogf logs a debug message.
func (u *Unifi) dLogf(msg string, v ...interface{}) {
if u.DebugLog != nil {
u.DebugLog("[DEBUG] "+msg, v...)
}
}
// dLogf logs an error message.
func (u *Unifi) eLogf(msg string, v ...interface{}) {
if u.ErrorLog != nil {
u.ErrorLog("[ERROR] "+msg, v...)
}
}
// Devices contains a list of all the unifi devices from a controller.
// Contains Access points, security gateways and switches.
type Devices struct {
UAPs []UAP
USGs []USG
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.
type Unifi struct {
*http.Client
baseURL string
ErrorLog Logger
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"`
}
// FlexInt provides a container and unmarshalling for fields that may be
// numbers or strings in the Unifi API.
type FlexInt struct {
Val float64
Txt string
}
// UnmarshalJSON converts a string or number to an integer.
// Generally, do call this directly, it's used in the json interface.
func (f *FlexInt) UnmarshalJSON(b []byte) error {
var unk interface{}
if err := json.Unmarshal(b, &unk); err != nil {
return err
}
switch i := unk.(type) {
case float64:
f.Val = i
f.Txt = strconv.FormatFloat(i, 'f', -1, 64)
return nil
case string:
f.Txt = i
f.Val, _ = strconv.ParseFloat(i, 64)
return nil
default:
return errors.New("Cannot unmarshal to FlexInt")
}
}
// FlexBool provides a container and unmarshalling for fields that may be
// boolean or strings in the Unifi API.
type FlexBool struct {
Val bool
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.
func (f *FlexBool) UnmarshalJSON(b []byte) error {
f.Txt = strings.Trim(string(b), `"`)
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")
return nil
}

36
core/unifi/types_test.go Normal file
View File

@ -0,0 +1,36 @@
package unifi
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
)
func TestFlexInt(t *testing.T) {
t.Parallel()
a := assert.New(t)
type testReply struct {
Five FlexInt `json:"five"`
Seven FlexInt `json:"seven"`
Auto FlexInt `json:"auto"`
Channel FlexInt `json:"channel"`
}
var r testReply
// test unmarshalling the custom type three times with different values.
a.Nil(json.Unmarshal([]byte(`{"five": "5", "seven": 7, "auto": "auto"}`), &r))
// test number in string.
a.EqualValues(5, r.Five.Val)
a.EqualValues("5", r.Five.Txt)
// test number.
a.EqualValues(7, r.Seven.Val)
a.EqualValues("7", r.Seven.Txt)
// test string.
a.EqualValues(0, r.Auto.Val)
a.EqualValues("auto", r.Auto.Txt)
// test (error) struct.
a.NotNil(json.Unmarshal([]byte(`{"channel": {}}`), &r),
"a non-string and non-number must produce an error.")
a.EqualValues(0, r.Channel.Val)
}

View File

@ -13,7 +13,6 @@ func (u UAP) Points() ([]*influx.Point, error) {
/* 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!
*/
var points []*influx.Point
tags := map[string]string{
"id": u.ID,
"mac": u.Mac,
@ -22,7 +21,7 @@ func (u UAP) Points() ([]*influx.Point, error) {
"device_ap": u.Stat.Ap,
"site_id": u.SiteID,
"name": u.Name,
"addopted": strconv.FormatBool(u.Adopted),
"adopted": strconv.FormatBool(u.Adopted),
"bandsteering_mode": u.BandsteeringMode,
"board_rev": strconv.Itoa(u.BoardRev),
"cfgversion": u.Cfgversion,
@ -63,7 +62,7 @@ func (u UAP) Points() ([]*influx.Point, error) {
"rx_bytes-d": u.RxBytesD,
"tx_bytes": u.TxBytes,
"tx_bytes-d": u.TxBytesD,
"uptime": u.Uptime.Number,
"uptime": u.Uptime.Val,
"considered_lost_at": u.ConsideredLostAt,
"next_heartbeat_at": u.NextHeartbeatAt,
"scanning": u.Scanning,
@ -174,7 +173,7 @@ func (u UAP) Points() ([]*influx.Point, error) {
if err != nil {
return nil, err
}
points = append(points, pt)
points := []*influx.Point{pt}
for _, p := range u.RadioTable {
tags := map[string]string{
"device_name": u.Name,
@ -182,7 +181,7 @@ func (u UAP) Points() ([]*influx.Point, error) {
"device_mac": u.Mac,
"name": p.Name,
"wlangroup_id": p.WlangroupID,
"channel": p.Channel.String,
"channel": p.Channel.Txt,
"radio": p.Radio,
}
fields := map[string]interface{}{
@ -197,7 +196,7 @@ func (u UAP) Points() ([]*influx.Point, error) {
"min_txpower": p.MinTxpower,
"nss": p.Nss,
"radio_caps": p.RadioCaps,
"tx_power": p.TxPower.Number,
"tx_power": p.TxPower.Val,
"tx_power_mode": p.TxPowerMode,
}
@ -219,7 +218,7 @@ func (u UAP) Points() ([]*influx.Point, error) {
fields["radio"] = s.Radio
fields["state"] = s.State
fields["radio_tx_packets"] = s.TxPackets
fields["radio_tx_power"] = s.TxPower.Number
fields["radio_tx_power"] = s.TxPower.Val
fields["radio_tx_retries"] = s.TxRetries
fields["user-num_sta"] = s.UserNumSta
break
@ -253,7 +252,7 @@ func (u UAP) Points() ([]*influx.Point, error) {
fields["vap_tx_latency_max"] = s.TxLatencyMax
fields["vap_tx_latency_min"] = s.TxLatencyMin
fields["vap_tx_packets"] = s.TxPackets
fields["vap_tx_power"] = s.TxPower.Number
fields["vap_tx_power"] = s.TxPower.Val
fields["vap_tx_retries"] = s.TxRetries
fields["usage"] = s.Usage
break

View File

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

View File

@ -1,159 +0,0 @@
package unifi
import (
"bytes"
"crypto/tls"
"encoding/json"
"net/http"
"net/http/cookiejar"
"strconv"
"strings"
"github.com/pkg/errors"
)
const (
// SiteList is the path to the api site list.
SiteList string = "/api/self/sites"
// ClientPath is Unifi Clients API Path
ClientPath string = "/api/s/%s/stat/sta"
// DevicePath is where we get data about Unifi devices.
DevicePath string = "/api/s/%s/stat/device"
// NetworkPath contains network-configuration data. Not really graphable.
NetworkPath string = "/api/s/%s/rest/networkconf"
// UserGroupPath contains usergroup configurations.
UserGroupPath string = "/api/s/%s/rest/usergroup"
// LoginPath is Unifi Controller Login API Path
LoginPath string = "/api/login"
)
// Logger is a base type to deal with changing log outputs.
type Logger func(msg string, fmt ...interface{})
// Devices contains a list of all the unifi devices from a controller.
type Devices struct {
UAPs []UAP
USGs []USG
USWs []USW
}
// Clients conptains a list of all the unifi clients from a controller.
type Clients struct {
UCLs []UCL
}
// Unifi is what you get in return for providing a password!
type Unifi struct {
*http.Client
baseURL string
ErrorLog Logger
DebugLog Logger
}
// FlexInt provides a container and unmarshalling for fields that may be
// numbers or strings in the Unifi API
type FlexInt struct {
Number float64
String string
}
// UnmarshalJSON converts a string or number to an integer.
func (f *FlexInt) UnmarshalJSON(b []byte) error {
var unk interface{}
if err := json.Unmarshal(b, &unk); err != nil {
return err
}
switch i := unk.(type) {
case float64:
f.Number = i
f.String = strconv.FormatFloat(i, 'f', -1, 64)
return nil
case string:
f.String = i
f.Number, _ = strconv.ParseFloat(i, 64)
return nil
default:
return errors.New("Cannot unmarshal to FlexInt")
}
}
// FlexBool provides a container and unmarshalling for fields that may be
// boolean or strings in the Unifi API
type FlexBool struct {
Bool bool
String string
}
// 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.
func (f *FlexBool) UnmarshalJSON(b []byte) error {
f.String = strings.Trim(string(b), `"`)
f.Bool = f.String == "1" || strings.EqualFold(f.String, "true") || strings.EqualFold(f.String, "yes") ||
strings.EqualFold(f.String, "t") || strings.EqualFold(f.String, "armed") || strings.EqualFold(f.String, "active") ||
strings.EqualFold(f.String, "enabled") || strings.EqualFold(f.String, "ready") || strings.EqualFold(f.String, "up")
return nil
}
// GetController creates a http.Client with authenticated cookies.
// Used to make additional, authenticated requests to the APIs.
func GetController(user, pass, url string, verifySSL bool) (*Unifi, error) {
json := `{"username": "` + user + `","password": "` + pass + `"}`
jar, err := cookiejar.New(nil)
if err != nil {
return nil, errors.Wrap(err, "cookiejar.New(nil)")
}
u := &Unifi{
Client: &http.Client{
Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: !verifySSL}},
Jar: jar,
},
}
if u.baseURL = url; strings.HasSuffix(url, "/") {
u.baseURL = url[:len(url)-1]
}
req, err := u.UniReq(LoginPath, json)
if err != nil {
return u, errors.Wrap(err, "UniReq(LoginPath, json)")
}
resp, err := u.Do(req)
if err != nil {
return u, errors.Wrap(err, "authReq.Do(req)")
}
defer func() {
_ = resp.Body.Close()
}()
if resp.StatusCode != http.StatusOK {
return u, errors.Errorf("authentication failed (%v): %v (status: %v/%v)",
user, url+LoginPath, resp.StatusCode, resp.Status)
}
return u, nil
}
// UniReq is a small helper function that adds an Accept header.
// Use this if you're unmarshalling Unifi data into custom types.
// And you're doing that... sumbut a pull request with your new struct. :)
func (u *Unifi) UniReq(apiPath string, params string) (req *http.Request, err error) {
if params != "" {
req, err = http.NewRequest("POST", u.baseURL+apiPath, bytes.NewBufferString(params))
} else {
req, err = http.NewRequest("GET", u.baseURL+apiPath, nil)
}
if err == nil {
req.Header.Add("Accept", "application/json")
}
return
}
// dLogf logs a debug message.
func (u *Unifi) dLogf(msg string, v ...interface{}) {
if u.DebugLog != nil {
u.DebugLog("[DEBUG] "+msg, v...)
}
}
// dLogf logs an error message.
func (u *Unifi) eLogf(msg string, v ...interface{}) {
if u.ErrorLog != nil {
u.ErrorLog("[ERROR] "+msg, v...)
}
}

View File

@ -1,14 +1,63 @@
// Package unifi provides a set of types to unload (unmarshal) Unifi Ubiquiti
// controller data. Also provided are methods to easily get data for devices -
// things like access points and switches, and for clients - the things
// connected to those access points and switches. As a bonus, each device and
// client type provided has an attached method to create InfluxDB datapoints.
package unifi
import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"strings"
"github.com/pkg/errors"
)
// NewUnifi creates a http.Client with authenticated cookies.
// Used to make additional, authenticated requests to the APIs.
// Start here.
func NewUnifi(user, pass, url string, verifySSL bool) (*Unifi, error) {
jar, err := cookiejar.New(nil)
if err != nil {
return nil, errors.Wrap(err, "cookiejar.New(nil)")
}
u := &Unifi{baseURL: strings.TrimRight(url, "/"),
Client: &http.Client{
Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: !verifySSL}},
Jar: jar,
},
}
return u, u.getController(user, pass)
}
// getController is a helper method to make testsing a bit easier.
func (u *Unifi) getController(user, pass string) error {
// magic login.
req, err := u.UniReq(LoginPath, `{"username": "`+user+`","password": "`+pass+`"}`)
if err != nil {
return errors.Wrap(err, "UniReq(LoginPath, json)")
}
resp, err := u.Do(req)
if err != nil {
return errors.Wrap(err, "authReq.Do(req)")
}
defer func() {
_, _ = io.Copy(ioutil.Discard, resp.Body) // avoid leaking.
_ = resp.Body.Close()
}()
if resp.StatusCode != http.StatusOK {
return errors.Errorf("authentication failed (user: %s): %s (status: %d/%s)",
user, u.baseURL+LoginPath, resp.StatusCode, resp.Status)
}
return nil
}
// GetClients returns a response full of clients' data from the Unifi Controller.
func (u *Unifi) GetClients(sites []Site) (*Clients, error) {
var data []UCL
@ -44,12 +93,6 @@ func (u *Unifi) GetDevices(sites []Site) (*Devices, error) {
return u.parseDevices(data), nil
}
// Site represents a site's data. There are more pieces to this.
type Site struct {
Name string `json:"name"`
Desc string `json:"desc"`
}
// GetSites returns a list of configured sites on the Unifi controller.
func (u *Unifi) GetSites() ([]Site, error) {
var response struct {
@ -87,55 +130,18 @@ func (u *Unifi) GetData(methodPath string, v interface{}) error {
return nil
}
// parseDevices parses the raw JSON from the Unifi Controller into device structures.
func (u *Unifi) parseDevices(data []json.RawMessage) *Devices {
devices := new(Devices)
for _, r := range data {
// Loop each item in the raw JSON message, detect its type and unmarshal it.
var obj map[string]interface{}
var uap UAP
var usg USG
var usw USW
if u.unmarshalDevice("interface{}", &obj, r) != nil {
continue
}
assetType := "<missing>"
if t, ok := obj["type"].(string); ok {
assetType = t
}
u.dLogf("Unmarshalling Device Type: %v", assetType)
switch assetType { // Unmarshal again into the correct type..
case "uap":
if u.unmarshalDevice(assetType, &uap, r) == nil {
devices.UAPs = append(devices.UAPs, uap)
}
case "ugw", "usg": // in case they ever fix the name in the api.
if u.unmarshalDevice(assetType, &usg, r) == nil {
devices.USGs = append(devices.USGs, usg)
}
case "usw":
if u.unmarshalDevice(assetType, &usw, r) == nil {
devices.USWs = append(devices.USWs, usw)
}
default:
u.eLogf("unknown asset type - %v - skipping", assetType)
continue
}
// UniReq is a small helper function that adds an Accept header.
// Use this if you're unmarshalling Unifi data into custom types.
// And if you're doing that... sumbut a pull request with your new struct. :)
// This is a helper method that is exposed for convenience.
func (u *Unifi) UniReq(apiPath string, params string) (req *http.Request, err error) {
if params != "" {
req, err = http.NewRequest("POST", u.baseURL+apiPath, bytes.NewBufferString(params))
} else {
req, err = http.NewRequest("GET", u.baseURL+apiPath, nil)
}
return devices
}
// unmarshalDevice handles logging for the unmarshal operations in parseDevices().
func (u *Unifi) unmarshalDevice(device string, ptr interface{}, payload json.RawMessage) error {
err := json.Unmarshal(payload, ptr)
if err != nil {
u.eLogf("json.Unmarshal(%v): %v", device, err)
u.eLogf("Enable Debug Logging to output the failed payload.")
json, err := payload.MarshalJSON()
u.dLogf("Failed Payload: %s (marshal err: %v)", json, err)
u.dLogf("The above payload can prove useful during torubleshooting when you open an Issue:")
u.dLogf("==- https://github.com/golift/unifi/issues/new -==")
if err == nil {
req.Header.Add("Accept", "application/json")
}
return err
return
}

View File

@ -1,7 +1,6 @@
package unifi
import (
"encoding/json"
"io/ioutil"
"net/http"
"testing"
@ -9,32 +8,17 @@ import (
"github.com/stretchr/testify/assert"
)
func TestFlexInt(t *testing.T) {
func TestNewUnifi(t *testing.T) {
t.Parallel()
a := assert.New(t)
type testReply struct {
Five FlexInt `json:"five"`
Seven FlexInt `json:"seven"`
Auto FlexInt `json:"auto"`
Channel FlexInt `json:"channel"`
}
var r testReply
// test unmarshalling the custom type three times with different values.
a.Nil(json.Unmarshal([]byte(`{"five": "5", "seven": 7, "auto": "auto"}`), &r))
// test number in string.
a.EqualValues(5, r.Five.Number)
a.EqualValues("5", r.Five.String)
// test number.
a.EqualValues(7, r.Seven.Number)
a.EqualValues("7", r.Seven.String)
// test string.
a.EqualValues(0, r.Auto.Number)
a.EqualValues("auto", r.Auto.String)
// test (error) struct.
a.NotNil(json.Unmarshal([]byte(`{"channel": {}}`), &r),
"a non-string and non-number must produce an error.")
a.EqualValues(0, r.Channel.Number)
url := "http://127.0.0.1:64431"
authReq, err := NewUnifi("user1", "pass2", url, false)
a.NotNil(err)
a.EqualValues(url, authReq.baseURL)
a.Contains(err.Error(), "authReq.Do(req):", "an invalid destination should product a .Do(req) error.")
/* TODO: OPEN web server, check parameters posted, more. This test is incomplete.
a.EqualValues(`{"username": "user1","password": "pass2"}`, string(post_params), "user/pass json parameters improperly encoded")
*/
}
func TestUniReq(t *testing.T) {
@ -67,16 +51,3 @@ func TestUniReq(t *testing.T) {
a.Nil(err, "problem reading request body, POST parameters may be malformed")
a.EqualValues(p, string(d), "POST parameters improperly encoded")
}
func TestAuthController(t *testing.T) {
t.Parallel()
a := assert.New(t)
url := "http://127.0.0.1:64431"
authReq, err := GetController("user1", "pass2", url, false)
a.NotNil(err)
a.EqualValues(url, authReq.baseURL)
a.Contains(err.Error(), "authReq.Do(req):", "an invalid destination should product a .Do(req) error.")
/* TODO: OPEN web server, check parameters posted, more. This test is incomplete.
a.EqualValues(`{"username": "user1","password": "pass2"}`, string(post_params), "user/pass json parameters improperly encoded")
*/
}

View File

@ -10,14 +10,13 @@ import (
// Points generates Unifi Gateway datapoints for InfluxDB.
// These points can be passed directly to influx.
func (u USG) Points() ([]*influx.Point, error) {
var points []*influx.Point
tags := map[string]string{
"id": u.ID,
"mac": u.Mac,
"device_type": u.Stat.O,
"device_oid": u.Stat.Oid,
"site_id": u.SiteID,
"addopted": strconv.FormatBool(u.Adopted),
"adopted": strconv.FormatBool(u.Adopted),
"name": u.Name,
"adopt_ip": u.AdoptIP,
"adopt_url": u.AdoptURL,
@ -92,7 +91,7 @@ func (u USG) Points() ([]*influx.Point, error) {
"wan1_rx_packets": u.Wan1.RxPackets,
"wan1_type": u.Wan1.Type,
"wan1_speed": u.Wan1.Speed,
"wan1_up": u.Wan1.Up.Bool,
"wan1_up": u.Wan1.Up.Val,
"wan1_tx_bytes": u.Wan1.TxBytes,
"wan1_tx_bytes-r": u.Wan1.TxBytesR,
"wan1_tx_dropped": u.Wan1.TxDropped,
@ -130,7 +129,7 @@ func (u USG) Points() ([]*influx.Point, error) {
if err != nil {
return nil, err
}
points = append(points, pt)
points := []*influx.Point{pt}
for _, p := range u.NetworkTable {
tags := map[string]string{
"device_name": u.Name,
@ -170,7 +169,7 @@ func (u USG) Points() ([]*influx.Point, error) {
"rx_packets": p.RxPackets,
"tx_bytes": p.TxBytes,
"tx_packets": p.TxPackets,
"up": p.Up.String,
"up": p.Up.Txt,
"vlan": p.Vlan,
"dhcpd_ntp_1": p.DhcpdNtp1,
"dhcpd_unifi_controller": p.DhcpdUnifiController,
@ -183,5 +182,5 @@ func (u USG) Points() ([]*influx.Point, error) {
}
points = append(points, pt)
}
return points, err
return points, nil
}

View File

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

View File

@ -10,7 +10,6 @@ import (
// Points generates Unifi Switch datapoints for InfluxDB.
// These points can be passed directly to influx.
func (u USW) Points() ([]*influx.Point, error) {
var points []*influx.Point
tags := map[string]string{
"id": u.ID,
"mac": u.Mac,
@ -18,7 +17,7 @@ func (u USW) Points() ([]*influx.Point, error) {
"device_oid": u.Stat.Oid,
"site_id": u.SiteID,
"name": u.Name,
"addopted": strconv.FormatBool(u.Adopted),
"adopted": strconv.FormatBool(u.Adopted),
"adopt_ip": u.AdoptIP,
"adopt_url": u.AdoptURL,
"cfgversion": u.Cfgversion,
@ -112,8 +111,8 @@ func (u USW) Points() ([]*influx.Point, error) {
// Add the port stats too.
}
pt, err := influx.NewPoint("usw", tags, fields, time.Now())
if err == nil {
points = append(points, pt)
if err != nil {
return nil, err
}
return points, err
return []*influx.Point{pt}, nil
}

View File

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