Merge pull request #44 from unifi-poller/dn2_uxg_plus

Add UXG data struct, storage for UDM, and other minor improvements.
This commit is contained in:
David Newhall 2021-03-07 22:15:43 -08:00 committed by GitHub
commit ca6239efa9
18 changed files with 686 additions and 426 deletions

View File

@ -1,9 +1,9 @@
language: go language: go
go: go:
- 1.14.x - 1.15.x
before_install: before_install:
# 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 latest
script: script:
- golangci-lint run --enable-all - golangci-lint run --enable-all -D exhaustivestruct,nlreturn
- go test ./... - go test ./...

View File

@ -73,7 +73,7 @@ func (u *Unifi) GetAlarms(sites []*Site) ([]*Alarm, error) {
// GetAlarmsSite retreives the Alarms for a single Site. // GetAlarmsSite retreives the Alarms for a single Site.
func (u *Unifi) GetAlarmsSite(site *Site) ([]*Alarm, error) { func (u *Unifi) GetAlarmsSite(site *Site) ([]*Alarm, error) {
if site == nil || site.Name == "" { if site == nil || site.Name == "" {
return nil, errNoSiteProvided return nil, ErrNoSiteProvided
} }
u.DebugLog("Polling Controller for Alarms, site %s (%s)", site.Name, site.Desc) u.DebugLog("Polling Controller for Alarms, site %s (%s)", site.Name, site.Desc)

View File

@ -44,7 +44,7 @@ func (u *Unifi) GetAnomalies(sites []*Site, timeRange ...time.Time) ([]*Anomaly,
// GetAnomaliesSite retreives the Anomalies for a single Site. // GetAnomaliesSite retreives the Anomalies for a single Site.
func (u *Unifi) GetAnomaliesSite(site *Site, timeRange ...time.Time) ([]*Anomaly, error) { func (u *Unifi) GetAnomaliesSite(site *Site, timeRange ...time.Time) ([]*Anomaly, error) {
if site == nil || site.Name == "" { if site == nil || site.Name == "" {
return nil, errNoSiteProvided return nil, ErrNoSiteProvided
} }
u.DebugLog("Polling Controller for Anomalies, site %s (%s)", site.Name, site.Desc) u.DebugLog("Polling Controller for Anomalies, site %s (%s)", site.Name, site.Desc)
@ -119,7 +119,7 @@ func makeAnomalyParams(scale string, timeRange ...time.Time) (string, error) {
end := timeRange[1].Unix() * int64(time.Microsecond) end := timeRange[1].Unix() * int64(time.Microsecond)
out = append(out, "end="+strconv.FormatInt(end, 10), "start="+strconv.FormatInt(start, 10)) out = append(out, "end="+strconv.FormatInt(end, 10), "start="+strconv.FormatInt(start, 10))
default: default:
return "", errInvalidTimeRange return "", ErrInvalidTimeRange
} }
if len(out) == 0 { if len(out) == 0 {

View File

@ -36,42 +36,27 @@ func (u *Unifi) parseDevices(data []json.RawMessage, siteName string) *Devices {
for _, r := range data { for _, r := range data {
// 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.
assetType := "<type key missing>" o := make(map[string]interface{})
if u.unmarshalDevice("map", r, &o) != nil {
if o := make(map[string]interface{}); u.unmarshalDevice("map", r, &o) != nil { u.ErrorLog("unknown asset type - cannot find asset type in payload - skipping")
continue continue
} else if t, ok := o["type"].(string); ok {
assetType = t
} }
assetType, _ := o["type"].(string)
u.DebugLog("Unmarshalling Device Type: %v, site %s ", assetType, siteName) u.DebugLog("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":
dev := &UAP{SiteName: siteName, SourceName: u.URL} u.unmarshallUAP(siteName, r, devices)
if u.unmarshalDevice(assetType, r, dev) == nil {
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
devices.UAPs = append(devices.UAPs, dev)
}
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.
dev := &USG{SiteName: siteName, SourceName: u.URL} u.unmarshallUSG(siteName, r, devices)
if u.unmarshalDevice(assetType, r, dev) == nil {
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
devices.USGs = append(devices.USGs, dev)
}
case "usw": case "usw":
dev := &USW{SiteName: siteName, SourceName: u.URL} u.unmarshallUSW(siteName, r, devices)
if u.unmarshalDevice(assetType, r, dev) == nil {
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
devices.USWs = append(devices.USWs, dev)
}
case "udm": case "udm":
dev := &UDM{SiteName: siteName, SourceName: u.URL} u.unmarshallUDM(siteName, r, devices)
if u.unmarshalDevice(assetType, r, dev) == nil { case "uxg":
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac)) u.unmarshallUXG(siteName, r, devices)
devices.UDMs = append(devices.UDMs, dev)
}
default: default:
u.ErrorLog("unknown asset type - %v - skipping", assetType) u.ErrorLog("unknown asset type - %v - skipping", assetType)
} }
@ -80,6 +65,46 @@ func (u *Unifi) parseDevices(data []json.RawMessage, siteName string) *Devices {
return devices return devices
} }
func (u *Unifi) unmarshallUAP(siteName string, payload json.RawMessage, devices *Devices) {
dev := &UAP{SiteName: siteName, SourceName: u.URL}
if u.unmarshalDevice("uap", payload, dev) == nil {
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
devices.UAPs = append(devices.UAPs, dev)
}
}
func (u *Unifi) unmarshallUSG(siteName string, payload json.RawMessage, devices *Devices) {
dev := &USG{SiteName: siteName, SourceName: u.URL}
if u.unmarshalDevice("ugw", payload, dev) == nil {
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
devices.USGs = append(devices.USGs, dev)
}
}
func (u *Unifi) unmarshallUSW(siteName string, payload json.RawMessage, devices *Devices) {
dev := &USW{SiteName: siteName, SourceName: u.URL}
if u.unmarshalDevice("usw", payload, dev) == nil {
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
devices.USWs = append(devices.USWs, dev)
}
}
func (u *Unifi) unmarshallUXG(siteName string, payload json.RawMessage, devices *Devices) {
dev := &UXG{SiteName: siteName, SourceName: u.URL}
if u.unmarshalDevice("uxg", payload, dev) == nil {
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
devices.UXGs = append(devices.UXGs, dev)
}
}
func (u *Unifi) unmarshallUDM(siteName string, payload json.RawMessage, devices *Devices) {
dev := &UDM{SiteName: siteName, SourceName: u.URL}
if u.unmarshalDevice("udm", payload, dev) == nil {
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
devices.UDMs = append(devices.UDMs, dev)
}
}
// unmarshalDevice handles logging for the unmarshal operations in parseDevices(). // unmarshalDevice handles logging for the unmarshal operations in parseDevices().
func (u *Unifi) unmarshalDevice(dev string, data json.RawMessage, v interface{}) (err error) { func (u *Unifi) unmarshalDevice(dev string, data json.RawMessage, v interface{}) (err error) {
if err = json.Unmarshal(data, v); err != nil { if err = json.Unmarshal(data, v); err != nil {
@ -92,7 +117,11 @@ func (u *Unifi) unmarshalDevice(dev string, data json.RawMessage, v interface{})
u.DebugLog("==- https://github.com/unifi-poller/unifi/issues/new -==") u.DebugLog("==- https://github.com/unifi-poller/unifi/issues/new -==")
} }
return err if err != nil {
return fmt.Errorf("json unmarshal: %w", err)
}
return nil
} }
// pick returns the first non empty string in a list. // pick returns the first non empty string in a list.

View File

@ -15,12 +15,23 @@ type DPITable struct {
// DPIData is the DPI data in the DPI table. // DPIData is the DPI data in the DPI table.
type DPIData struct { type DPIData struct {
Cat int `json:"cat"` Cat int `json:"cat"`
App int `json:"app"` App int `json:"app"`
RxBytes int64 `json:"rx_bytes"` RxBytes int64 `json:"rx_bytes"`
TxBytes int64 `json:"tx_bytes"` TxBytes int64 `json:"tx_bytes"`
RxPackets int64 `json:"rx_packets"` RxPackets int64 `json:"rx_packets"`
TxPackets int64 `json:"tx_packets"` TxPackets int64 `json:"tx_packets"`
Clients []*DPIClient `json:"clients,omitempty"`
KnownClients FlexInt `json:"known_clients,omitempty"`
}
// DPIClient data is sometimes included in ByApp output.
type DPIClient struct {
Mac string `json:"mac"`
RxBytes FlexInt `json:"rx_bytes"`
TxBytes FlexInt `json:"tx_bytes"`
RxPackets FlexInt `json:"rx_packets"`
TxPackets FlexInt `json:"tx_packets"`
} }
// DPIMap allows binding methods to the DPICat and DPIApps variables. // DPIMap allows binding methods to the DPICat and DPIApps variables.

View File

@ -8,8 +8,8 @@ import (
) )
var ( var (
errNoSiteProvided = fmt.Errorf("site must not be nil or empty") ErrNoSiteProvided = fmt.Errorf("site must not be nil or empty")
errInvalidTimeRange = fmt.Errorf("only 0, 1 or 2 times may be provided to timeRange") ErrInvalidTimeRange = fmt.Errorf("only 0, 1 or 2 times may be provided to timeRange")
) )
const ( const (
@ -35,7 +35,7 @@ func (u *Unifi) GetEvents(sites []*Site, hours time.Duration) ([]*Event, error)
// GetSiteEvents retrieves the last 1 hour's worth of events from a single site. // GetSiteEvents retrieves the last 1 hour's worth of events from a single site.
func (u *Unifi) GetSiteEvents(site *Site, hours time.Duration) ([]*Event, error) { func (u *Unifi) GetSiteEvents(site *Site, hours time.Duration) ([]*Event, error) {
if site == nil || site.Name == "" { if site == nil || site.Name == "" {
return nil, errNoSiteProvided return nil, ErrNoSiteProvided
} }
if hours < time.Hour { if hours < time.Hour {
@ -196,5 +196,9 @@ func (v *IPGeo) UnmarshalJSON(data []byte) error {
v.CountryName = g.CountryName v.CountryName = g.CountryName
v.Organization = g.Organization v.Organization = g.Organization
return err if err != nil {
return fmt.Errorf("json unmarshal: %w", err)
}
return nil
} }

View File

@ -1,10 +1,10 @@
module github.com/unifi-poller/unifi module github.com/unifi-poller/unifi
go 1.14 go 1.15
require ( require (
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.4.0 github.com/stretchr/testify v1.4.0
golang.org/x/net v0.0.0-20200625001655-4c5254603344 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
) )

View File

@ -13,10 +13,16 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4= golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=

View File

@ -76,7 +76,7 @@ func (u *Unifi) GetIDS(sites []*Site, timeRange ...time.Time) ([]*IDS, error) {
// Events between start and end are returned. End defaults to time.Now(). // Events between start and end are returned. End defaults to time.Now().
func (u *Unifi) GetIDSSite(site *Site, timeRange ...time.Time) ([]*IDS, error) { func (u *Unifi) GetIDSSite(site *Site, timeRange ...time.Time) ([]*IDS, error) {
if site == nil || site.Name == "" { if site == nil || site.Name == "" {
return nil, errNoSiteProvided return nil, ErrNoSiteProvided
} }
u.DebugLog("Polling Controller for IDS Events, site %s (%s)", site.Name, site.Desc) u.DebugLog("Polling Controller for IDS Events, site %s (%s)", site.Name, site.Desc)
@ -126,12 +126,15 @@ func makeEventParams(timeRange ...time.Time) (string, error) {
rp.Start = timeRange[0].Unix() * int64(time.Microsecond) rp.Start = timeRange[0].Unix() * int64(time.Microsecond)
rp.End = timeRange[1].Unix() * int64(time.Microsecond) rp.End = timeRange[1].Unix() * int64(time.Microsecond)
default: default:
return "", errInvalidTimeRange return "", ErrInvalidTimeRange
} }
params, err := json.Marshal(&rp) params, err := json.Marshal(&rp)
if err != nil {
return "", fmt.Errorf("json marshal: %w", err)
}
return string(params), err return string(params), nil
} }
type idsList []*IDS type idsList []*IDS

View File

@ -38,7 +38,7 @@ func (u *Unifi) parseNetwork(data json.RawMessage, siteName string) (*Network, e
return network, u.unmarshalDevice(siteName, data, network) return network, u.unmarshalDevice(siteName, data, network)
} }
// Network is metadata about a network managed by a UniFi controller // Network is metadata about a network managed by a UniFi controller.
type Network struct { type Network struct {
DhcpdDNSEnabled FlexBool `json:"dhcpd_dns_enabled"` DhcpdDNSEnabled FlexBool `json:"dhcpd_dns_enabled"`
DhcpdEnabled FlexBool `json:"dhcpd_enabled"` DhcpdEnabled FlexBool `json:"dhcpd_enabled"`

View File

@ -5,9 +5,7 @@ import (
"strings" "strings"
) )
var ( var ErrDPIDataBug = fmt.Errorf("dpi data table contains more than 1 item; please open a bug report")
errDPIDataBug = fmt.Errorf("dpi data table contains more than 1 item; please open a bug report")
)
// 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() ([]*Site, error) {
@ -53,7 +51,7 @@ func (u *Unifi) GetSiteDPI(sites []*Site) ([]*DPITable, error) {
} }
if l := len(response.Data); l > 1 { if l := len(response.Data); l > 1 {
return nil, errDPIDataBug return nil, ErrDPIDataBug
} else if l == 0 { } else if l == 0 {
u.DebugLog("Site DPI data missing! Is DPI enabled in UniFi controller? Site %s (%s) ", site.Name, site.Desc) u.DebugLog("Site DPI data missing! Is DPI enabled in UniFi controller? Site %s (%s) ", site.Name, site.Desc)
continue continue

View File

@ -6,13 +6,10 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/pkg/errors"
) )
var ( var ErrCannotUnmarshalFlexInt = fmt.Errorf("cannot unmarshal to FlexInt")
errCannotUnmarshalFlexInt = fmt.Errorf("cannot unmarshal to FlexInt")
)
// 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.
@ -27,17 +24,17 @@ const (
APISiteDPI string = "/api/s/%s/stat/sitedpi" APISiteDPI string = "/api/s/%s/stat/sitedpi"
// APISiteDPI is site DPI data. // APISiteDPI is site DPI data.
APIClientDPI string = "/api/s/%s/stat/stadpi" APIClientDPI string = "/api/s/%s/stat/stadpi"
// APIClientPath is Unifi Clients API Path // APIClientPath is Unifi Clients API Path.
APIClientPath string = "/api/s/%s/stat/sta" APIClientPath string = "/api/s/%s/stat/sta"
// APINetworkPath is where we get data about Unifi networks. // APINetworkPath is where we get data about Unifi networks.
APINetworkPath string = "/api/s/%s/rest/networkconf" APINetworkPath string = "/api/s/%s/rest/networkconf"
// APIDevicePath is where we get data about Unifi devices. // APIDevicePath is where we get data about Unifi devices.
APIDevicePath string = "/api/s/%s/stat/device" APIDevicePath string = "/api/s/%s/stat/device"
// APILoginPath is Unifi Controller Login API Path // APILoginPath is Unifi Controller Login API Path.
APILoginPath string = "/api/login" APILoginPath string = "/api/login"
// APILoginPathNew is how we log into UDM 5.12.55+ // APILoginPathNew is how we log into UDM 5.12.55+.
APILoginPathNew string = "/api/auth/login" APILoginPathNew string = "/api/auth/login"
// APIEventPathIDS returns Intrusion Detection/Prevention Systems Events // APIEventPathIDS returns Intrusion Detection/Prevention Systems Events.
APIEventPathIDS string = "/api/s/%s/stat/ips/event" APIEventPathIDS string = "/api/s/%s/stat/ips/event"
// APIEventPathAlarms contains the site alarms. // APIEventPathAlarms contains the site alarms.
APIEventPathAlarms string = "/api/s/%s/list/alarm" APIEventPathAlarms string = "/api/s/%s/list/alarm"
@ -79,6 +76,7 @@ type Devices struct {
USGs []*USG USGs []*USG
USWs []*USW USWs []*USW
UDMs []*UDM UDMs []*UDM
UXGs []*UXG
} }
// Config is the data passed into our library. This configures things and allows // Config is the data passed into our library. This configures things and allows
@ -91,6 +89,7 @@ type Config struct {
New bool New bool
ErrorLog Logger ErrorLog Logger
DebugLog Logger DebugLog Logger
Timeout time.Duration // how long to wait for replies, default: forever.
} }
// 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
@ -124,7 +123,7 @@ func (f *FlexInt) UnmarshalJSON(b []byte) error {
var unk interface{} var unk interface{}
if err := json.Unmarshal(b, &unk); err != nil { if err := json.Unmarshal(b, &unk); err != nil {
return err return fmt.Errorf("json unmarshal: %w", err)
} }
switch i := unk.(type) { switch i := unk.(type) {
@ -138,7 +137,7 @@ func (f *FlexInt) UnmarshalJSON(b []byte) error {
f.Txt = "0" f.Txt = "0"
f.Val = 0 f.Val = 0
default: default:
return errors.Wrapf(errCannotUnmarshalFlexInt, "%v", b) return fmt.Errorf("%v: %w", b, ErrCannotUnmarshalFlexInt)
} }
return nil return nil

View File

@ -3,69 +3,59 @@ package unifi
// UDM represents all the data from the Ubiquiti Controller for a Unifi Dream Machine. // UDM represents all the data from the Ubiquiti Controller for a Unifi Dream Machine.
// The UDM shares several structs/type-data with USW and USG. // The UDM shares several structs/type-data with USW and USG.
type UDM struct { type UDM struct {
SourceName string `json:"-"` SourceName string `json:"-"`
SiteID string `json:"site_id"` SiteID string `json:"site_id"`
SiteName string `json:"-"` SiteName string `json:"-"`
Mac string `json:"mac"` Mac string `json:"mac"`
Adopted FlexBool `json:"adopted"` Adopted FlexBool `json:"adopted"`
Serial string `json:"serial"` Serial string `json:"serial"`
IP string `json:"ip"` IP string `json:"ip"`
Uptime FlexInt `json:"uptime"` Uptime FlexInt `json:"uptime"`
Model string `json:"model"` Model string `json:"model"`
Version string `json:"version"` Version string `json:"version"`
Name string `json:"name"` Name string `json:"name"`
Default FlexBool `json:"default"` Default FlexBool `json:"default"`
Locating FlexBool `json:"locating"` Locating FlexBool `json:"locating"`
Type string `json:"type"` Type string `json:"type"`
Unsupported FlexBool `json:"unsupported"` Unsupported FlexBool `json:"unsupported"`
UnsupportedReason FlexInt `json:"unsupported_reason"` UnsupportedReason FlexInt `json:"unsupported_reason"`
DiscoveredVia string `json:"discovered_via"` DiscoveredVia string `json:"discovered_via"`
AdoptIP string `json:"adopt_ip"` AdoptIP string `json:"adopt_ip"`
AdoptURL string `json:"adopt_url"` AdoptURL string `json:"adopt_url"`
State FlexInt `json:"state"` State FlexInt `json:"state"`
AdoptStatus FlexInt `json:"adopt_status"` AdoptStatus FlexInt `json:"adopt_status"`
UpgradeState FlexInt `json:"upgrade_state"` UpgradeState FlexInt `json:"upgrade_state"`
LastSeen FlexInt `json:"last_seen"` LastSeen FlexInt `json:"last_seen"`
AdoptableWhenUpgraded FlexBool `json:"adoptable_when_upgraded"` AdoptableWhenUpgraded FlexBool `json:"adoptable_when_upgraded"`
Cfgversion string `json:"cfgversion"` Cfgversion string `json:"cfgversion"`
ConfigNetwork struct { ConfigNetwork *ConfigNetwork `json:"config_network"`
Type string `json:"type"` VwireTable []interface{} `json:"vwire_table"`
IP string `json:"ip"` Dot1XPortctrlEnabled FlexBool `json:"dot1x_portctrl_enabled"`
} `json:"config_network"` JumboframeEnabled FlexBool `json:"jumboframe_enabled"`
VwireTable []interface{} `json:"vwire_table"` FlowctrlEnabled FlexBool `json:"flowctrl_enabled"`
Dot1XPortctrlEnabled FlexBool `json:"dot1x_portctrl_enabled"` StpVersion string `json:"stp_version"`
JumboframeEnabled FlexBool `json:"jumboframe_enabled"` StpPriority FlexInt `json:"stp_priority"`
FlowctrlEnabled FlexBool `json:"flowctrl_enabled"` PowerSourceCtrlEnabled FlexBool `json:"power_source_ctrl_enabled"`
StpVersion string `json:"stp_version"` LicenseState string `json:"license_state"`
StpPriority FlexInt `json:"stp_priority"` ID string `json:"_id"`
PowerSourceCtrlEnabled FlexBool `json:"power_source_ctrl_enabled"` DeviceID string `json:"device_id"`
LicenseState string `json:"license_state"` AdoptState FlexInt `json:"adopt_state"`
ID string `json:"_id"` AdoptTries FlexInt `json:"adopt_tries"`
DeviceID string `json:"device_id"` AdoptManual FlexBool `json:"adopt_manual"`
AdoptState FlexInt `json:"adopt_state"` InformURL string `json:"inform_url"`
AdoptTries FlexInt `json:"adopt_tries"` InformIP string `json:"inform_ip"`
AdoptManual FlexBool `json:"adopt_manual"` RequiredVersion string `json:"required_version"`
InformURL string `json:"inform_url"` BoardRev FlexInt `json:"board_rev"`
InformIP string `json:"inform_ip"` EthernetTable []*EthernetTable `json:"ethernet_table"`
RequiredVersion string `json:"required_version"` PortTable []Port `json:"port_table"`
BoardRev FlexInt `json:"board_rev"` EthernetOverrides []*EthernetOverrides `json:"ethernet_overrides"`
EthernetTable []struct { UsgCaps FlexInt `json:"usg_caps"`
Mac string `json:"mac"` HasSpeaker FlexBool `json:"has_speaker"`
NumPort FlexInt `json:"num_port"` HasEth1 FlexBool `json:"has_eth1"`
Name string `json:"name"` FwCaps FlexInt `json:"fw_caps"`
} `json:"ethernet_table"` HwCaps FlexInt `json:"hw_caps"`
PortTable []Port `json:"port_table"` WifiCaps FlexInt `json:"wifi_caps"`
EthernetOverrides []struct { SwitchCaps struct {
Ifname string `json:"ifname"`
Networkgroup string `json:"networkgroup"`
} `json:"ethernet_overrides"`
UsgCaps FlexInt `json:"usg_caps"`
HasSpeaker FlexBool `json:"has_speaker"`
HasEth1 FlexBool `json:"has_eth1"`
FwCaps FlexInt `json:"fw_caps"`
HwCaps FlexInt `json:"hw_caps"`
WifiCaps FlexInt `json:"wifi_caps"`
SwitchCaps struct {
MaxMirrorSessions FlexInt `json:"max_mirror_sessions"` MaxMirrorSessions FlexInt `json:"max_mirror_sessions"`
MaxAggregateSessions FlexInt `json:"max_aggregate_sessions"` MaxAggregateSessions FlexInt `json:"max_aggregate_sessions"`
} `json:"switch_caps"` } `json:"switch_caps"`
@ -96,7 +86,7 @@ type UDM struct {
Uplink Uplink `json:"uplink"` Uplink Uplink `json:"uplink"`
ConnectRequestIP string `json:"connect_request_ip"` ConnectRequestIP string `json:"connect_request_ip"`
ConnectRequestPort string `json:"connect_request_port"` ConnectRequestPort string `json:"connect_request_port"`
DownlinkTable []interface{} `json:"downlink_table"` DownlinkTable []*DownlinkTable `json:"downlink_table"`
WlangroupIDNa string `json:"wlangroup_id_na"` WlangroupIDNa string `json:"wlangroup_id_na"`
WlangroupIDNg string `json:"wlangroup_id_ng"` WlangroupIDNg string `json:"wlangroup_id_ng"`
BandsteeringMode string `json:"bandsteering_mode"` BandsteeringMode string `json:"bandsteering_mode"`
@ -109,69 +99,95 @@ type UDM struct {
PortIdx FlexInt `json:"port_idx"` PortIdx FlexInt `json:"port_idx"`
PortconfID string `json:"portconf_id"` PortconfID string `json:"portconf_id"`
} `json:"port_overrides"` } `json:"port_overrides"`
Stat UDMStat `json:"stat"` Stat UDMStat `json:"stat"`
TxBytes FlexInt `json:"tx_bytes"` Storage []*Storage `json:"storage"`
RxBytes FlexInt `json:"rx_bytes"` TxBytes FlexInt `json:"tx_bytes"`
Bytes FlexInt `json:"bytes"` RxBytes FlexInt `json:"rx_bytes"`
BytesD FlexInt `json:"bytes-d"` Bytes FlexInt `json:"bytes"`
TxBytesD FlexInt `json:"tx_bytes-d"` BytesD FlexInt `json:"bytes-d"`
RxBytesD FlexInt `json:"rx_bytes-d"` TxBytesD FlexInt `json:"tx_bytes-d"`
BytesR FlexInt `json:"bytes-r"` RxBytesD FlexInt `json:"rx_bytes-d"`
NumSta FlexInt `json:"num_sta"` // USG BytesR FlexInt `json:"bytes-r"`
WlanNumSta FlexInt `json:"wlan-num_sta"` // UAP NumSta FlexInt `json:"num_sta"` // USG
LanNumSta FlexInt `json:"lan-num_sta"` // USW WlanNumSta FlexInt `json:"wlan-num_sta"` // UAP
UserWlanNumSta FlexInt `json:"user-wlan-num_sta"` // UAP LanNumSta FlexInt `json:"lan-num_sta"` // USW
UserLanNumSta FlexInt `json:"user-lan-num_sta"` // USW UserWlanNumSta FlexInt `json:"user-wlan-num_sta"` // UAP
UserNumSta FlexInt `json:"user-num_sta"` // USG UserLanNumSta FlexInt `json:"user-lan-num_sta"` // USW
GuestWlanNumSta FlexInt `json:"guest-wlan-num_sta"` // UAP UserNumSta FlexInt `json:"user-num_sta"` // USG
GuestLanNumSta FlexInt `json:"guest-lan-num_sta"` // USW GuestWlanNumSta FlexInt `json:"guest-wlan-num_sta"` // UAP
GuestNumSta FlexInt `json:"guest-num_sta"` // USG GuestLanNumSta FlexInt `json:"guest-lan-num_sta"` // USW
NumDesktop FlexInt `json:"num_desktop"` // USG GuestNumSta FlexInt `json:"guest-num_sta"` // USG
NumMobile FlexInt `json:"num_mobile"` // USG NumDesktop FlexInt `json:"num_desktop"` // USG
NumHandheld FlexInt `json:"num_handheld"` // USG NumMobile FlexInt `json:"num_mobile"` // USG
NumHandheld FlexInt `json:"num_handheld"` // USG
}
type EthernetOverrides struct {
Ifname string `json:"ifname"`
Networkgroup string `json:"networkgroup"`
}
type EthernetTable struct {
Mac string `json:"mac"`
NumPort FlexInt `json:"num_port"`
Name string `json:"name"`
} }
// NetworkTable is the list of networks on a gateway. // NetworkTable is the list of networks on a gateway.
// Not all gateways have all features.
type NetworkTable []struct { type NetworkTable []struct {
ID string `json:"_id"` ID string `json:"_id"`
AttrNoDelete FlexBool `json:"attr_no_delete"` AttrNoDelete FlexBool `json:"attr_no_delete"`
AttrHiddenID string `json:"attr_hidden_id"` AttrHiddenID string `json:"attr_hidden_id"`
Name string `json:"name"` Name string `json:"name"`
SiteID string `json:"site_id"` SiteID string `json:"site_id"`
VlanEnabled FlexBool `json:"vlan_enabled"` VlanEnabled FlexBool `json:"vlan_enabled"`
Purpose string `json:"purpose"` Purpose string `json:"purpose"`
IPSubnet string `json:"ip_subnet"` IPSubnet string `json:"ip_subnet"`
Ipv6InterfaceType string `json:"ipv6_interface_type"` Ipv6InterfaceType string `json:"ipv6_interface_type"`
DomainName string `json:"domain_name"` DomainName string `json:"domain_name"`
IsNat FlexBool `json:"is_nat"` IsNat FlexBool `json:"is_nat"`
DhcpdEnabled FlexBool `json:"dhcpd_enabled"` DhcpdEnabled FlexBool `json:"dhcpd_enabled"`
DhcpdStart string `json:"dhcpd_start"` DhcpdStart string `json:"dhcpd_start"`
DhcpdStop string `json:"dhcpd_stop"` DhcpdStop string `json:"dhcpd_stop"`
Dhcpdv6Enabled FlexBool `json:"dhcpdv6_enabled"` Dhcpdv6Enabled FlexBool `json:"dhcpdv6_enabled"`
Ipv6RaEnabled FlexBool `json:"ipv6_ra_enabled"` Ipv6RaEnabled FlexBool `json:"ipv6_ra_enabled"`
LteLanEnabled FlexBool `json:"lte_lan_enabled"` LteLanEnabled FlexBool `json:"lte_lan_enabled"`
Networkgroup string `json:"networkgroup"` AutoScaleEnabled FlexBool `json:"auto_scale_enabled"`
DhcpdLeasetime FlexInt `json:"dhcpd_leasetime"` Networkgroup string `json:"networkgroup"`
DhcpdDNSEnabled FlexBool `json:"dhcpd_dns_enabled"` DhcpdLeasetime FlexInt `json:"dhcpd_leasetime"`
DhcpdGatewayEnabled FlexBool `json:"dhcpd_gateway_enabled"` DhcpdDNSEnabled FlexBool `json:"dhcpd_dns_enabled"`
DhcpdTimeOffsetEnabled FlexBool `json:"dhcpd_time_offset_enabled"` DhcpdGatewayEnabled FlexBool `json:"dhcpd_gateway_enabled"`
Ipv6PdStart string `json:"ipv6_pd_start"` DhcpdTimeOffsetEnabled FlexBool `json:"dhcpd_time_offset_enabled"`
Ipv6PdStop string `json:"ipv6_pd_stop"` Ipv6PdStart string `json:"ipv6_pd_start"`
DhcpdDNS1 string `json:"dhcpd_dns_1"` Ipv6PdStop string `json:"ipv6_pd_stop"`
DhcpdDNS2 string `json:"dhcpd_dns_2"` DhcpdDNS1 string `json:"dhcpd_dns_1"`
DhcpdDNS3 string `json:"dhcpd_dns_3"` DhcpdDNS2 string `json:"dhcpd_dns_2"`
DhcpdDNS4 string `json:"dhcpd_dns_4"` DhcpdDNS3 string `json:"dhcpd_dns_3"`
Enabled FlexBool `json:"enabled"` DhcpdDNS4 string `json:"dhcpd_dns_4"`
DhcpRelayEnabled FlexBool `json:"dhcp_relay_enabled"` Enabled FlexBool `json:"enabled"`
Mac string `json:"mac"` DhcpRelayEnabled FlexBool `json:"dhcp_relay_enabled"`
IsGuest FlexBool `json:"is_guest"` Mac string `json:"mac"`
IP string `json:"ip"` IsGuest FlexBool `json:"is_guest"`
Up FlexBool `json:"up"` IP string `json:"ip"`
NumSta FlexInt `json:"num_sta"` Up FlexBool `json:"up"`
RxBytes FlexInt `json:"rx_bytes"` ActiveDhcpLeaseCount int `json:"active_dhcp_lease_count"`
RxPackets FlexInt `json:"rx_packets"` GatewayInterfaceName string `json:"gateway_interface_name"`
TxBytes FlexInt `json:"tx_bytes"` DPIStatsTable *DPITable `json:"dpistats_table"`
TxPackets FlexInt `json:"tx_packets"` NumSta FlexInt `json:"num_sta"`
RxBytes FlexInt `json:"rx_bytes"`
RxPackets FlexInt `json:"rx_packets"`
TxBytes FlexInt `json:"tx_bytes"`
TxPackets FlexInt `json:"tx_packets"`
}
// Storage is hard drive into for a device with storage.
type Storage struct {
MountPoint string `json:"mount_point"`
Name string `json:"name"`
Size FlexInt `json:"size"`
Type string `json:"type"`
Used FlexInt `json:"used"`
} }
type Temperature struct { type Temperature struct {

View File

@ -7,6 +7,7 @@ package unifi
import ( import (
"bytes" "bytes"
"context"
"crypto/tls" "crypto/tls"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -18,13 +19,13 @@ import (
"strings" "strings"
"time" "time"
"github.com/pkg/errors"
"golang.org/x/net/publicsuffix" "golang.org/x/net/publicsuffix"
) )
var ( var (
errAuthenticationFailed = fmt.Errorf("authentication failed") ErrAuthenticationFailed = fmt.Errorf("authentication failed")
errInvalidStatusCode = fmt.Errorf("invalid status code from server") ErrInvalidStatusCode = fmt.Errorf("invalid status code from server")
ErrNoParams = fmt.Errorf("requedted PUT with no parameters")
) )
// NewUnifi creates a http.Client with authenticated cookies. // NewUnifi creates a http.Client with authenticated cookies.
@ -33,7 +34,7 @@ var (
func NewUnifi(config *Config) (*Unifi, error) { func NewUnifi(config *Config) (*Unifi, error) {
jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List}) jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("creating cookiejar: %w", err)
} }
config.URL = strings.TrimRight(config.URL, "/") config.URL = strings.TrimRight(config.URL, "/")
@ -46,9 +47,11 @@ func NewUnifi(config *Config) (*Unifi, error) {
config.DebugLog = discardLogs config.DebugLog = discardLogs
} }
u := &Unifi{Config: config, u := &Unifi{
Config: config,
Client: &http.Client{ Client: &http.Client{
Jar: jar, Timeout: config.Timeout,
Jar: jar,
Transport: &http.Transport{ Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: !config.VerifySSL}, // nolint: gosec TLSClientConfig: &tls.Config{InsecureSkipVerify: !config.VerifySSL}, // nolint: gosec
}, },
@ -64,7 +67,7 @@ func NewUnifi(config *Config) (*Unifi, error) {
} }
if err := u.GetServerData(); err != nil { if err := u.GetServerData(); err != nil {
return u, errors.Wrap(err, "unable to get server version") return u, fmt.Errorf("unable to get server version: %w", err)
} }
return u, nil return u, nil
@ -82,7 +85,7 @@ func (u *Unifi) Login() error {
resp, err := u.Do(req) resp, err := u.Do(req)
if err != nil { if err != nil {
return err return fmt.Errorf("making request: %w", err)
} }
defer resp.Body.Close() // we need no data here. defer resp.Body.Close() // we need no data here.
@ -91,8 +94,8 @@ func (u *Unifi) Login() error {
req.URL, time.Since(start).Round(time.Millisecond), resp.ContentLength) req.URL, time.Since(start).Round(time.Millisecond), resp.ContentLength)
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return errors.Wrapf(errAuthenticationFailed, "(user: %s): %s (status: %s)", return fmt.Errorf("(user: %s): %s (status: %s): %w",
u.User, req.URL, resp.Status) u.User, req.URL, resp.Status, ErrAuthenticationFailed)
} }
return nil return nil
@ -103,11 +106,21 @@ func (u *Unifi) Login() error {
// check if this is a newer controller or not. If it is, we set new to true. // check if this is a newer controller or not. If it is, we set new to true.
// Setting new to true makes the path() method return different (new) paths. // Setting new to true makes the path() method return different (new) paths.
func (u *Unifi) checkNewStyleAPI() error { func (u *Unifi) checkNewStyleAPI() error {
var (
ctx = context.Background()
cancel func()
)
if u.Config.Timeout != 0 {
ctx, cancel = context.WithTimeout(ctx, u.Config.Timeout)
defer cancel()
}
u.DebugLog("Requesting %s/ to determine API paths", u.URL) u.DebugLog("Requesting %s/ to determine API paths", u.URL)
req, err := http.NewRequest("GET", u.URL+"/", nil) req, err := http.NewRequestWithContext(ctx, "GET", u.URL+"/", nil)
if err != nil { if err != nil {
return err return fmt.Errorf("creating request: %w", err)
} }
// We can't share these cookies with other requests, so make a new client. // We can't share these cookies with other requests, so make a new client.
@ -123,7 +136,7 @@ func (u *Unifi) checkNewStyleAPI() error {
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return err return fmt.Errorf("making request: %w", err)
} }
defer resp.Body.Close() // we need no data here. defer resp.Body.Close() // we need no data here.
@ -185,39 +198,44 @@ func (u *Unifi) PutData(apiPath string, v interface{}, params ...string) error {
// Use this if you're unmarshalling UniFi data into custom types. // 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. :) // And if you're doing that... sumbut a pull request with your new struct. :)
// This is a helper method that is exposed for convenience. // This is a helper method that is exposed for convenience.
func (u *Unifi) UniReq(apiPath string, params string) (req *http.Request, err error) { func (u *Unifi) UniReq(apiPath string, params string) (*http.Request, error) {
var (
req *http.Request
err error
)
switch apiPath = u.path(apiPath); params { switch apiPath = u.path(apiPath); params {
case "": case "":
req, err = http.NewRequest("GET", u.URL+apiPath, nil) req, err = http.NewRequest(http.MethodGet, u.URL+apiPath, nil)
default: default:
req, err = http.NewRequest("POST", u.URL+apiPath, bytes.NewBufferString(params)) req, err = http.NewRequest(http.MethodPost, u.URL+apiPath, bytes.NewBufferString(params))
} }
if err != nil { if err != nil {
return return nil, fmt.Errorf("creating request: %w", err)
} }
u.setHeaders(req, params) u.setHeaders(req, params)
return return req, nil
} }
// UniReqPut is the Put call equivalent to UniReq // UniReqPut is the Put call equivalent to UniReq.
func (u *Unifi) UniReqPut(apiPath string, params string) (req *http.Request, err error) { func (u *Unifi) UniReqPut(apiPath string, params string) (*http.Request, error) {
switch apiPath = u.path(apiPath); params { if params == "" {
case "": return nil, ErrNoParams
err = fmt.Errorf("Put with no parameters. Use UniReq()")
default:
req, err = http.NewRequest("PUT", u.URL+apiPath, bytes.NewBufferString(params))
} }
apiPath = u.path(apiPath)
req, err := http.NewRequest(http.MethodPut, u.URL+apiPath, bytes.NewBufferString(params)) //nolint:noctx
if err != nil { if err != nil {
return return nil, fmt.Errorf("creating request: %w", err)
} }
u.setHeaders(req, params) u.setHeaders(req, params)
return return req, nil
} }
// GetJSON returns the raw JSON from a path. This is useful for debugging. // GetJSON returns the raw JSON from a path. This is useful for debugging.
@ -231,7 +249,7 @@ func (u *Unifi) GetJSON(apiPath string, params ...string) ([]byte, error) {
} }
// PutJSON uses a PUT call and returns the raw JSON in the same way as GetData // PutJSON uses a PUT call and returns the raw JSON in the same way as GetData
// Use this if you want to change data via the REST API // Use this if you want to change data via the REST API.
func (u *Unifi) PutJSON(apiPath string, params ...string) ([]byte, error) { func (u *Unifi) PutJSON(apiPath string, params ...string) ([]byte, error) {
req, err := u.UniReqPut(apiPath, strings.Join(params, " ")) req, err := u.UniReqPut(apiPath, strings.Join(params, " "))
if err != nil { if err != nil {
@ -242,16 +260,26 @@ func (u *Unifi) PutJSON(apiPath string, params ...string) ([]byte, error) {
} }
func (u *Unifi) do(req *http.Request) ([]byte, error) { func (u *Unifi) do(req *http.Request) ([]byte, error) {
resp, err := u.Do(req) var (
cancel func()
ctx = context.Background()
)
if u.Config.Timeout != 0 {
ctx, cancel = context.WithTimeout(ctx, u.Config.Timeout)
defer cancel()
}
resp, err := u.Do(req.WithContext(ctx))
if err != nil { if err != nil {
return []byte{}, err return []byte{}, fmt.Errorf("making request: %w", err)
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return body, err return body, fmt.Errorf("reading response: %w", err)
} }
// Save the returned CSRF header. // Save the returned CSRF header.
@ -260,7 +288,7 @@ func (u *Unifi) do(req *http.Request) ([]byte, error) {
} }
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
err = errors.Wrapf(errInvalidStatusCode, "%s: %s", req.URL, resp.Status) err = fmt.Errorf("%s: %s: %w", req.URL, resp.Status, ErrInvalidStatusCode)
} }
return body, err return body, err

View File

@ -30,9 +30,11 @@ func TestUniReq(t *testing.T) {
a := assert.New(t) a := assert.New(t)
p := "/test/path" p := "/test/path"
u := "http://some.url:8443" u := "http://some.url:8443"
// Test empty parameters. // Test empty parameters.
authReq := &Unifi{Client: &http.Client{}, Config: &Config{URL: u, DebugLog: discardLogs}} authReq := &Unifi{Client: &http.Client{}, Config: &Config{URL: u, DebugLog: discardLogs}}
r, err := authReq.UniReq(p, "") r, err := authReq.UniReq(p, "")
a.Nil(err, "newrequest must not produce an error") a.Nil(err, "newrequest must not produce an error")
a.EqualValues(p, r.URL.Path, a.EqualValues(p, r.URL.Path,
"the provided apiPath was not added to http request") "the provided apiPath was not added to http request")
@ -45,11 +47,13 @@ func TestUniReq(t *testing.T) {
authReq = &Unifi{Client: &http.Client{}, Config: &Config{URL: "http://some.url:8443", DebugLog: discardLogs}} authReq = &Unifi{Client: &http.Client{}, Config: &Config{URL: "http://some.url:8443", DebugLog: discardLogs}}
r, err = authReq.UniReq(p, k) r, err = authReq.UniReq(p, k)
a.Nil(err, "newrequest must not produce an error") a.Nil(err, "newrequest must not produce an error")
a.EqualValues(p, r.URL.Path, a.EqualValues(p, r.URL.Path,
"the provided apiPath was not added to http request") "the provided apiPath was not added to http request")
a.EqualValues(u, r.URL.Scheme+"://"+r.URL.Host, "URL improperly encoded") a.EqualValues(u, r.URL.Scheme+"://"+r.URL.Host, "URL improperly encoded")
a.EqualValues("POST", r.Method, "with parameters the method must be POST") a.EqualValues("POST", r.Method, "with parameters the method must be POST")
a.EqualValues("application/json", r.Header.Get("Accept"), "Accept header must be set to application/json") a.EqualValues("application/json", r.Header.Get("Accept"), "Accept header must be set to application/json")
// Check the parameters. // Check the parameters.
d, err := ioutil.ReadAll(r.Body) d, err := ioutil.ReadAll(r.Body)
a.Nil(err, "problem reading request body, POST parameters may be malformed") a.Nil(err, "problem reading request body, POST parameters may be malformed")
@ -61,28 +65,31 @@ func TestUniReqPut(t *testing.T) {
a := assert.New(t) a := assert.New(t)
p := "/test/path" p := "/test/path"
u := "http://some.url:8443" u := "http://some.url:8443"
// Test empty parameters. // Test empty parameters.
authReq := &Unifi{Client: &http.Client{}, Config: &Config{URL: u, DebugLog: discardLogs}} authReq := &Unifi{Client: &http.Client{}, Config: &Config{URL: u, DebugLog: discardLogs}}
r, err := authReq.UniReqPut(p, "") _, err := authReq.UniReqPut(p, "")
a.NotNil(err, "empty params must produce an error") a.NotNil(err, "empty params must produce an error")
// Test with parameters // Test with parameters
k := "key1=value9&key2=value7" k := "key1=value9&key2=value7"
authReq = &Unifi{Client: &http.Client{}, Config: &Config{URL: "http://some.url:8443", DebugLog: discardLogs}} authReq = &Unifi{Client: &http.Client{}, Config: &Config{URL: "http://some.url:8443", DebugLog: discardLogs}}
r, err = authReq.UniReqPut(p, k) r, err := authReq.UniReqPut(p, k)
a.Nil(err, "newrequest must not produce an error") a.Nil(err, "newrequest must not produce an error")
a.EqualValues(p, r.URL.Path, a.EqualValues(p, r.URL.Path,
"the provided apiPath was not added to http request") "the provided apiPath was not added to http request")
a.EqualValues(u, r.URL.Scheme+"://"+r.URL.Host, "URL improperly encoded") a.EqualValues(u, r.URL.Scheme+"://"+r.URL.Host, "URL improperly encoded")
a.EqualValues("PUT", r.Method, "with parameters the method must be POST") a.EqualValues("PUT", r.Method, "with parameters the method must be POST")
a.EqualValues("application/json", r.Header.Get("Accept"), "Accept header must be set to application/json") a.EqualValues("application/json", r.Header.Get("Accept"), "Accept header must be set to application/json")
// Check the parameters. // Check the parameters.
d, err := ioutil.ReadAll(r.Body) d, err := ioutil.ReadAll(r.Body)
a.Nil(err, "problem reading request body, PUT parameters may be malformed") a.Nil(err, "problem reading request body, PUT parameters may be malformed")
a.EqualValues(k, string(d), "PUT parameters improperly encoded") a.EqualValues(k, string(d), "PUT parameters improperly encoded")
} }
/* NOT DONE: OPEN web server, check parameters posted, more. This test is incomplete. /* NOT DONE: OPEN web server, check parameters posted, more. These tests are incomplete.
a.EqualValues(`{"username": "user1","password": "pass2"}`, string(post_params), a.EqualValues(`{"username": "user1","password": "pass2"}`, string(post_params),
"user/pass json parameters improperly encoded") "user/pass json parameters improperly encoded")
*/ */

View File

@ -7,96 +7,66 @@ import (
// USG represents all the data from the Ubiquiti Controller for a Unifi Security Gateway. // USG represents all the data from the Ubiquiti Controller for a Unifi Security Gateway.
type USG struct { type USG struct {
SourceName string `json:"-"` SourceName string `json:"-"`
ID string `json:"_id"` ID string `json:"_id"`
Adopted FlexBool `json:"adopted"` Adopted FlexBool `json:"adopted"`
Cfgversion string `json:"cfgversion"` Cfgversion string `json:"cfgversion"`
ConfigNetwork struct { ConfigNetwork *ConfigNetwork `json:"config_network"`
Type string `json:"type"` EthernetTable []*EthernetTable `json:"ethernet_table"`
IP string `json:"ip"` FwCaps FlexInt `json:"fw_caps"`
} `json:"config_network"` InformIP string `json:"inform_ip"`
EthernetTable []struct { InformURL string `json:"inform_url"`
Mac string `json:"mac"` IP string `json:"ip"`
NumPort FlexInt `json:"num_port"` LedOverride string `json:"led_override"`
Name string `json:"name"` LicenseState string `json:"license_state"`
} `json:"ethernet_table"` Mac string `json:"mac"`
FwCaps FlexInt `json:"fw_caps"` Model string `json:"model"`
InformIP string `json:"inform_ip"` Name string `json:"name"`
InformURL string `json:"inform_url"` OutdoorModeOverride string `json:"outdoor_mode_override"`
IP string `json:"ip"` Serial string `json:"serial"`
LedOverride string `json:"led_override"` SiteID string `json:"site_id"`
LicenseState string `json:"license_state"` SiteName string `json:"-"`
Mac string `json:"mac"` Type string `json:"type"`
Model string `json:"model"` UsgCaps FlexInt `json:"usg_caps"`
Name string `json:"name"` Version string `json:"version"`
OutdoorModeOverride string `json:"outdoor_mode_override"` RequiredVersion string `json:"required_version"`
Serial string `json:"serial"` EthernetOverrides []*EthernetOverrides `json:"ethernet_overrides"`
SiteID string `json:"site_id"` HwCaps FlexInt `json:"hw_caps"`
SiteName string `json:"-"` BoardRev FlexInt `json:"board_rev"`
Type string `json:"type"` Unsupported FlexBool `json:"unsupported"`
UsgCaps FlexInt `json:"usg_caps"` UnsupportedReason FlexInt `json:"unsupported_reason"`
Version string `json:"version"` DeviceID string `json:"device_id"`
RequiredVersion string `json:"required_version"` State FlexInt `json:"state"`
EthernetOverrides []struct { LastSeen FlexInt `json:"last_seen"`
Ifname string `json:"ifname"` Upgradable FlexBool `json:"upgradable"`
Networkgroup string `json:"networkgroup"` AdoptableWhenUpgraded FlexBool `json:"adoptable_when_upgraded"`
} `json:"ethernet_overrides"` Rollupgrade FlexBool `json:"rollupgrade"`
HwCaps FlexInt `json:"hw_caps"` KnownCfgversion string `json:"known_cfgversion"`
BoardRev FlexInt `json:"board_rev"` Uptime FlexInt `json:"uptime"`
Unsupported FlexBool `json:"unsupported"` Locating FlexBool `json:"locating"`
UnsupportedReason FlexInt `json:"unsupported_reason"` ConnectRequestIP string `json:"connect_request_ip"`
DeviceID string `json:"device_id"` ConnectRequestPort string `json:"connect_request_port"`
State FlexInt `json:"state"` SysStats SysStats `json:"sys_stats"`
LastSeen FlexInt `json:"last_seen"` Temperatures []Temperature `json:"temperatures,omitempty"`
Upgradable FlexBool `json:"upgradable"` SystemStats SystemStats `json:"system-stats"`
AdoptableWhenUpgraded FlexBool `json:"adoptable_when_upgraded"` GuestToken string `json:"guest_token"`
Rollupgrade FlexBool `json:"rollupgrade"` SpeedtestStatus SpeedtestStatus `json:"speedtest-status"`
KnownCfgversion string `json:"known_cfgversion"` SpeedtestStatusSaved FlexBool `json:"speedtest-status-saved"`
Uptime FlexInt `json:"uptime"` Wan1 Wan `json:"wan1"`
Locating FlexBool `json:"locating"` Wan2 Wan `json:"wan2"`
ConnectRequestIP string `json:"connect_request_ip"` PortTable []*Port `json:"port_table"`
ConnectRequestPort string `json:"connect_request_port"` NetworkTable NetworkTable `json:"network_table"`
SysStats SysStats `json:"sys_stats"` Uplink Uplink `json:"uplink"`
SystemStats SystemStats `json:"system-stats"` Stat USGStat `json:"stat"`
GuestToken string `json:"guest_token"` TxBytes FlexInt `json:"tx_bytes"`
SpeedtestStatus SpeedtestStatus `json:"speedtest-status"` RxBytes FlexInt `json:"rx_bytes"`
SpeedtestStatusSaved FlexBool `json:"speedtest-status-saved"` Bytes FlexInt `json:"bytes"`
Wan1 Wan `json:"wan1"` NumSta FlexInt `json:"num_sta"`
Wan2 Wan `json:"wan2"` UserNumSta FlexInt `json:"user-num_sta"`
PortTable []struct { GuestNumSta FlexInt `json:"guest-num_sta"`
Name string `json:"name"` NumDesktop FlexInt `json:"num_desktop"`
Ifname string `json:"ifname"` NumMobile FlexInt `json:"num_mobile"`
IP string `json:"ip"` NumHandheld FlexInt `json:"num_handheld"`
Netmask string `json:"netmask"`
Mac string `json:"mac"`
Up FlexBool `json:"up"`
Speed FlexInt `json:"speed"`
FullDuplex FlexBool `json:"full_duplex"`
RxBytes FlexInt `json:"rx_bytes"`
RxDropped FlexInt `json:"rx_dropped"`
RxErrors FlexInt `json:"rx_errors"`
RxPackets FlexInt `json:"rx_packets"`
TxBytes FlexInt `json:"tx_bytes"`
TxDropped FlexInt `json:"tx_dropped"`
TxErrors FlexInt `json:"tx_errors"`
TxPackets FlexInt `json:"tx_packets"`
RxMulticast FlexInt `json:"rx_multicast"`
Enable FlexBool `json:"enable"`
DNS []string `json:"dns,omitempty"`
Gateway string `json:"gateway,omitempty"`
} `json:"port_table"`
NetworkTable NetworkTable `json:"network_table"`
Uplink Uplink `json:"uplink"`
Stat USGStat `json:"stat"`
TxBytes FlexInt `json:"tx_bytes"`
RxBytes FlexInt `json:"rx_bytes"`
Bytes FlexInt `json:"bytes"`
NumSta FlexInt `json:"num_sta"`
UserNumSta FlexInt `json:"user-num_sta"`
GuestNumSta FlexInt `json:"guest-num_sta"`
NumDesktop FlexInt `json:"num_desktop"`
NumMobile FlexInt `json:"num_mobile"`
NumHandheld FlexInt `json:"num_handheld"`
} }
// Uplink is the Internet connection (or uplink) on a UniFi device. // Uplink is the Internet connection (or uplink) on a UniFi device.
@ -114,12 +84,15 @@ type Uplink struct {
Nameservers []string `json:"nameservers"` Nameservers []string `json:"nameservers"`
Netmask string `json:"netmask"` Netmask string `json:"netmask"`
NumPort FlexInt `json:"num_port"` NumPort FlexInt `json:"num_port"`
Media string `json:"media"`
PortIdx FlexInt `json:"port_idx"`
RxBytes FlexInt `json:"rx_bytes"` RxBytes FlexInt `json:"rx_bytes"`
RxBytesR FlexInt `json:"rx_bytes-r"` RxBytesR FlexInt `json:"rx_bytes-r"`
RxDropped FlexInt `json:"rx_dropped"` RxDropped FlexInt `json:"rx_dropped"`
RxErrors FlexInt `json:"rx_errors"` RxErrors FlexInt `json:"rx_errors"`
RxMulticast FlexInt `json:"rx_multicast"` RxMulticast FlexInt `json:"rx_multicast"`
RxPackets FlexInt `json:"rx_packets"` RxPackets FlexInt `json:"rx_packets"`
RxRate FlexInt `json:"rx_rate"`
Speed FlexInt `json:"speed"` Speed FlexInt `json:"speed"`
SpeedtestLastrun FlexInt `json:"speedtest_lastrun,omitempty"` SpeedtestLastrun FlexInt `json:"speedtest_lastrun,omitempty"`
SpeedtestPing FlexInt `json:"speedtest_ping,omitempty"` SpeedtestPing FlexInt `json:"speedtest_ping,omitempty"`
@ -129,6 +102,7 @@ type Uplink struct {
TxDropped FlexInt `json:"tx_dropped"` TxDropped FlexInt `json:"tx_dropped"`
TxErrors FlexInt `json:"tx_errors"` TxErrors FlexInt `json:"tx_errors"`
TxPackets FlexInt `json:"tx_packets"` TxPackets FlexInt `json:"tx_packets"`
TxRate FlexInt `json:"tx_rate"`
Type string `json:"type"` Type string `json:"type"`
Up FlexBool `json:"up"` Up FlexBool `json:"up"`
Uptime FlexInt `json:"uptime"` Uptime FlexInt `json:"uptime"`
@ -140,7 +114,7 @@ type Uplink struct {
type Wan struct { type Wan struct {
Autoneg FlexBool `json:"autoneg"` Autoneg FlexBool `json:"autoneg"`
BytesR FlexInt `json:"bytes-r"` BytesR FlexInt `json:"bytes-r"`
DNS []string `json:"dns"` DNS []string `json:"dns"` // may be deprecated
Enable FlexBool `json:"enable"` Enable FlexBool `json:"enable"`
FlowctrlRx FlexBool `json:"flowctrl_rx"` FlowctrlRx FlexBool `json:"flowctrl_rx"`
FlowctrlTx FlexBool `json:"flowctrl_tx"` FlowctrlTx FlexBool `json:"flowctrl_tx"`
@ -164,7 +138,9 @@ type Wan struct {
RxErrors FlexInt `json:"rx_errors"` RxErrors FlexInt `json:"rx_errors"`
RxMulticast FlexInt `json:"rx_multicast"` RxMulticast FlexInt `json:"rx_multicast"`
RxPackets FlexInt `json:"rx_packets"` RxPackets FlexInt `json:"rx_packets"`
RxRate FlexInt `json:"rx_rate"`
Speed FlexInt `json:"speed"` Speed FlexInt `json:"speed"`
SpeedCaps FlexInt `json:"speed_caps"`
TxBroadcast FlexInt `json:"tx_broadcast"` TxBroadcast FlexInt `json:"tx_broadcast"`
TxBytes FlexInt `json:"tx_bytes"` TxBytes FlexInt `json:"tx_bytes"`
TxBytesR FlexInt `json:"tx_bytes-r"` TxBytesR FlexInt `json:"tx_bytes-r"`
@ -172,22 +148,41 @@ type Wan struct {
TxErrors FlexInt `json:"tx_errors"` TxErrors FlexInt `json:"tx_errors"`
TxMulticast FlexInt `json:"tx_multicast"` TxMulticast FlexInt `json:"tx_multicast"`
TxPackets FlexInt `json:"tx_packets"` TxPackets FlexInt `json:"tx_packets"`
TxRate FlexInt `json:"tx_rate"`
Type string `json:"type"` Type string `json:"type"`
Up FlexBool `json:"up"` Up FlexBool `json:"up"`
} }
// SpeedtestStatus is the speed test info on a USG or UDM. // SpeedtestStatus is the speed test info on a USG or UDM.
type SpeedtestStatus struct { type SpeedtestStatus struct {
Latency FlexInt `json:"latency"` Latency FlexInt `json:"latency"`
Rundate FlexInt `json:"rundate"` Rundate FlexInt `json:"rundate"`
Runtime FlexInt `json:"runtime"` Runtime FlexInt `json:"runtime"`
ServerDesc string `json:"server_desc,omitempty"` ServerDesc string `json:"server_desc,omitempty"`
StatusDownload FlexInt `json:"status_download"` Server *SpeedtestServer `json:"server"`
StatusPing FlexInt `json:"status_ping"` SourceInterface string `json:"source_interface"`
StatusSummary FlexInt `json:"status_summary"` StatusDownload FlexInt `json:"status_download"`
StatusUpload FlexInt `json:"status_upload"` StatusPing FlexInt `json:"status_ping"`
XputDownload FlexInt `json:"xput_download"` StatusSummary FlexInt `json:"status_summary"`
XputUpload FlexInt `json:"xput_upload"` StatusUpload FlexInt `json:"status_upload"`
XputDownload FlexInt `json:"xput_download"`
XputUpload FlexInt `json:"xput_upload"`
}
type SpeedtestServer struct {
Cc string `json:"cc"`
City string `json:"city"`
Country string `json:"country"`
Lat FlexInt `json:"lat"`
Lon FlexInt `json:"lon"`
Provider string `json:"provider"`
ProviderURL string `json:"provider_url"`
}
// ConfigNetwork comes from gateways.
type ConfigNetwork struct {
Type string `json:"type"`
IP string `json:"ip"`
} }
// SystemStats is system info for a UDM, USG, USW. // SystemStats is system info for a UDM, USG, USW.
@ -233,6 +228,7 @@ type Gw struct {
LanTxBytes FlexInt `json:"lan-tx_bytes"` LanTxBytes FlexInt `json:"lan-tx_bytes"`
LanRxDropped FlexInt `json:"lan-rx_dropped"` LanRxDropped FlexInt `json:"lan-rx_dropped"`
WanRxErrors FlexInt `json:"wan-rx_errors,omitempty"` WanRxErrors FlexInt `json:"wan-rx_errors,omitempty"`
LanRxErrors FlexInt `json:"lan-rx_errors,omitempty"`
} }
// UnmarshalJSON unmarshalls 5.10 or 5.11 formatted Gateway Stat data. // UnmarshalJSON unmarshalls 5.10 or 5.11 formatted Gateway Stat data.

View File

@ -7,84 +7,68 @@ import (
// USW represents all the data from the Ubiquiti Controller for a Unifi Switch. // USW represents all the data from the Ubiquiti Controller for a Unifi Switch.
type USW struct { type USW struct {
SourceName string `json:"-"` SourceName string `json:"-"`
SiteName string `json:"-"` SiteName string `json:"-"`
ID string `json:"_id"` ID string `json:"_id"`
Adopted FlexBool `json:"adopted"` Adopted FlexBool `json:"adopted"`
BoardRev FlexInt `json:"board_rev"` BoardRev FlexInt `json:"board_rev"`
Cfgversion string `json:"cfgversion"` Cfgversion string `json:"cfgversion"`
ConfigNetwork struct { ConfigNetwork *ConfigNetwork `json:"config_network"`
Type string `json:"type"` Dot1XPortctrlEnabled FlexBool `json:"dot1x_portctrl_enabled"`
IP string `json:"ip"` EthernetTable []*EthernetTable `json:"ethernet_table"`
} `json:"config_network"` FlowctrlEnabled FlexBool `json:"flowctrl_enabled"`
Dot1XPortctrlEnabled FlexBool `json:"dot1x_portctrl_enabled"` FwCaps FlexInt `json:"fw_caps"`
EthernetTable []struct { HasFan FlexBool `json:"has_fan"`
Mac string `json:"mac"` HasTemperature FlexBool `json:"has_temperature"`
NumPort FlexInt `json:"num_port,omitempty"` InformIP string `json:"inform_ip"`
Name string `json:"name"` InformURL string `json:"inform_url"`
} `json:"ethernet_table"` IP string `json:"ip"`
FlowctrlEnabled FlexBool `json:"flowctrl_enabled"` JumboframeEnabled FlexBool `json:"jumboframe_enabled"`
FwCaps FlexInt `json:"fw_caps"` LedOverride string `json:"led_override"`
HasFan FlexBool `json:"has_fan"` LicenseState string `json:"license_state"`
HasTemperature FlexBool `json:"has_temperature"` Mac string `json:"mac"`
InformIP string `json:"inform_ip"` Model string `json:"model"`
InformURL string `json:"inform_url"` Name string `json:"name"`
IP string `json:"ip"` OutdoorModeOverride string `json:"outdoor_mode_override"`
JumboframeEnabled FlexBool `json:"jumboframe_enabled"` PortOverrides []struct {
LedOverride string `json:"led_override"`
LicenseState string `json:"license_state"`
Mac string `json:"mac"`
Model string `json:"model"`
Name string `json:"name"`
OutdoorModeOverride string `json:"outdoor_mode_override"`
PortOverrides []struct {
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
PoeMode string `json:"poe_mode,omitempty"` PoeMode string `json:"poe_mode,omitempty"`
PortIdx FlexInt `json:"port_idx"` PortIdx FlexInt `json:"port_idx"`
PortconfID string `json:"portconf_id"` PortconfID string `json:"portconf_id"`
} `json:"port_overrides"` } `json:"port_overrides"`
PortTable []Port `json:"port_table"` PortTable []Port `json:"port_table"`
Serial string `json:"serial"` Serial string `json:"serial"`
SiteID string `json:"site_id"` SiteID string `json:"site_id"`
StpPriority FlexInt `json:"stp_priority"` StpPriority FlexInt `json:"stp_priority"`
StpVersion string `json:"stp_version"` StpVersion string `json:"stp_version"`
Type string `json:"type"` Type string `json:"type"`
Version string `json:"version"` Version string `json:"version"`
RequiredVersion string `json:"required_version"` RequiredVersion string `json:"required_version"`
SwitchCaps struct { SwitchCaps *SwitchCaps `json:"switch_caps"`
FeatureCaps FlexInt `json:"feature_caps"` HwCaps FlexInt `json:"hw_caps"`
MaxMirrorSessions FlexInt `json:"max_mirror_sessions"` Unsupported FlexBool `json:"unsupported"`
MaxAggregateSessions FlexInt `json:"max_aggregate_sessions"` UnsupportedReason FlexInt `json:"unsupported_reason"`
} `json:"switch_caps"` SysErrorCaps FlexInt `json:"sys_error_caps"`
HwCaps FlexInt `json:"hw_caps"` DeviceID string `json:"device_id"`
Unsupported FlexBool `json:"unsupported"` State FlexInt `json:"state"`
UnsupportedReason FlexInt `json:"unsupported_reason"` LastSeen FlexInt `json:"last_seen"`
SysErrorCaps FlexInt `json:"sys_error_caps"` Upgradable FlexBool `json:"upgradable,omitempty"`
DeviceID string `json:"device_id"` AdoptableWhenUpgraded FlexBool `json:"adoptable_when_upgraded,omitempty"`
State FlexInt `json:"state"` Rollupgrade FlexBool `json:"rollupgrade,omitempty"`
LastSeen FlexInt `json:"last_seen"` KnownCfgversion string `json:"known_cfgversion"`
Upgradable FlexBool `json:"upgradable,omitempty"` Uptime FlexInt `json:"uptime"`
AdoptableWhenUpgraded FlexBool `json:"adoptable_when_upgraded,omitempty"` Locating FlexBool `json:"locating"`
Rollupgrade FlexBool `json:"rollupgrade,omitempty"` ConnectRequestIP string `json:"connect_request_ip"`
KnownCfgversion string `json:"known_cfgversion"` ConnectRequestPort string `json:"connect_request_port"`
Uptime FlexInt `json:"uptime"` SysStats SysStats `json:"sys_stats"`
Locating FlexBool `json:"locating"` SystemStats SystemStats `json:"system-stats"`
ConnectRequestIP string `json:"connect_request_ip"` FanLevel FlexInt `json:"fan_level"`
ConnectRequestPort string `json:"connect_request_port"` GeneralTemperature FlexInt `json:"general_temperature"`
SysStats SysStats `json:"sys_stats"` Overheating FlexBool `json:"overheating"`
SystemStats SystemStats `json:"system-stats"` TotalMaxPower FlexInt `json:"total_max_power"`
FanLevel FlexInt `json:"fan_level"` DownlinkTable []*DownlinkTable `json:"downlink_table"`
GeneralTemperature FlexInt `json:"general_temperature"` Uplink Uplink `json:"uplink"`
Overheating FlexBool `json:"overheating"` LastUplink struct {
TotalMaxPower FlexInt `json:"total_max_power"`
DownlinkTable []struct {
PortIdx FlexInt `json:"port_idx"`
Speed FlexInt `json:"speed"`
FullDuplex FlexBool `json:"full_duplex"`
Mac string `json:"mac"`
} `json:"downlink_table"`
Uplink Uplink `json:"uplink"`
LastUplink struct {
UplinkMac string `json:"uplink_mac"` UplinkMac string `json:"uplink_mac"`
} `json:"last_uplink"` } `json:"last_uplink"`
UplinkDepth FlexInt `json:"uplink_depth"` UplinkDepth FlexInt `json:"uplink_depth"`
@ -97,39 +81,59 @@ type USW struct {
GuestNumSta FlexInt `json:"guest-num_sta"` GuestNumSta FlexInt `json:"guest-num_sta"`
} }
// Port is a physical connection on a USW or UDM. type SwitchCaps struct {
FeatureCaps FlexInt `json:"feature_caps"`
MaxMirrorSessions FlexInt `json:"max_mirror_sessions"`
MaxAggregateSessions FlexInt `json:"max_aggregate_sessions"`
}
// MacTable is a newer feature on some switched ports.
type MacTable struct {
Age int64 `json:"age"`
Authorized FlexBool `json:"authorized"`
Hostname string `json:"hostname"`
IP string `json:"ip"`
LastReachable int64 `json:"lastReachable"`
Mac string `json:"mac"`
}
// Port is a physical connection on a USW or Gateway.
// Not every port has the same capabilities.
type Port struct { type Port struct {
AggregatedBy FlexBool `json:"aggregated_by"` AggregatedBy FlexBool `json:"aggregated_by"`
Autoneg FlexBool `json:"autoneg,omitempty"` Autoneg FlexBool `json:"autoneg,omitempty"`
BytesR FlexInt `json:"bytes-r"` BytesR FlexInt `json:"bytes-r"`
DNS []string `json:"dns,omitempty"` DNS []string `json:"dns,omitempty"`
Dot1XMode string `json:"dot1x_mode"` Dot1XMode string `json:"dot1x_mode"`
Dot1XStatus string `json:"dot1x_status"` Dot1XStatus string `json:"dot1x_status"`
Enable FlexBool `json:"enable"` Enable FlexBool `json:"enable"`
FlowctrlRx FlexBool `json:"flowctrl_rx"` FlowctrlRx FlexBool `json:"flowctrl_rx"`
FlowctrlTx FlexBool `json:"flowctrl_tx"` FlowctrlTx FlexBool `json:"flowctrl_tx"`
FullDuplex FlexBool `json:"full_duplex"` FullDuplex FlexBool `json:"full_duplex"`
IP string `json:"ip,omitempty"` IP string `json:"ip,omitempty"`
Ifname string `json:"ifname,omitempty"` Ifname string `json:"ifname,omitempty"`
IsUplink FlexBool `json:"is_uplink"` IsUplink FlexBool `json:"is_uplink"`
Mac string `json:"mac,omitempty"` Mac string `json:"mac,omitempty"`
Jumbo FlexBool `json:"jumbo,omitempty"` MacTable []MacTable `json:"mac_table,omitempty"`
Masked FlexBool `json:"masked"` Jumbo FlexBool `json:"jumbo,omitempty"`
Media string `json:"media"` Masked FlexBool `json:"masked"`
Name string `json:"name"` Media string `json:"media"`
NetworkName string `json:"network_name,omitempty"` Name string `json:"name"`
NumPort int `json:"num_port,omitempty"` NetworkName string `json:"network_name,omitempty"`
OpMode string `json:"op_mode"` Netmask string `json:"netmask,omitempty"`
PoeCaps FlexInt `json:"poe_caps"` NumPort int `json:"num_port,omitempty"`
PoeClass string `json:"poe_class,omitempty"` OpMode string `json:"op_mode"`
PoeCurrent FlexInt `json:"poe_current,omitempty"` PoeCaps FlexInt `json:"poe_caps"`
PoeEnable FlexBool `json:"poe_enable,omitempty"` PoeClass string `json:"poe_class,omitempty"`
PoeGood FlexBool `json:"poe_good,omitempty"` PoeCurrent FlexInt `json:"poe_current,omitempty"`
PoeMode string `json:"poe_mode,omitempty"` PoeEnable FlexBool `json:"poe_enable,omitempty"`
PoePower FlexInt `json:"poe_power,omitempty"` PoeGood FlexBool `json:"poe_good,omitempty"`
PoeVoltage FlexInt `json:"poe_voltage,omitempty"` PoeMode string `json:"poe_mode,omitempty"`
PoePower FlexInt `json:"poe_power,omitempty"`
PoeVoltage FlexInt `json:"poe_voltage,omitempty"`
PortDelta struct { PortDelta struct {
TimeDelta int64 `json:"time_delta"` TimeDelta int64 `json:"time_delta"`
TimeDeltaActivity int64 `json:"time_delta_activity"`
} `json:"port_delta,omitempty"` } `json:"port_delta,omitempty"`
PortIdx FlexInt `json:"port_idx"` PortIdx FlexInt `json:"port_idx"`
PortPoe FlexBool `json:"port_poe"` PortPoe FlexBool `json:"port_poe"`
@ -141,6 +145,7 @@ type Port struct {
RxErrors FlexInt `json:"rx_errors"` RxErrors FlexInt `json:"rx_errors"`
RxMulticast FlexInt `json:"rx_multicast"` RxMulticast FlexInt `json:"rx_multicast"`
RxPackets FlexInt `json:"rx_packets"` RxPackets FlexInt `json:"rx_packets"`
RxRate FlexInt `json:"rx_rate,omitempty"`
Satisfaction FlexInt `json:"satisfaction,omitempty"` Satisfaction FlexInt `json:"satisfaction,omitempty"`
SfpFound FlexBool `json:"sfp_found,omitempty"` SfpFound FlexBool `json:"sfp_found,omitempty"`
Speed FlexInt `json:"speed"` Speed FlexInt `json:"speed"`
@ -154,6 +159,7 @@ type Port struct {
TxErrors FlexInt `json:"tx_errors"` TxErrors FlexInt `json:"tx_errors"`
TxMulticast FlexInt `json:"tx_multicast"` TxMulticast FlexInt `json:"tx_multicast"`
TxPackets FlexInt `json:"tx_packets"` TxPackets FlexInt `json:"tx_packets"`
TxRate FlexInt `json:"tx_rate,omitempty"`
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
Up FlexBool `json:"up"` Up FlexBool `json:"up"`
} }

157
core/unifi/uxg.go Normal file
View File

@ -0,0 +1,157 @@
package unifi
// UXG represents all the data from the Ubiquiti Controller for a UniFi 10Gb Gateway.
// The UDM shares several structs/type-data with USW and USG.
type UXG struct {
SourceName string `json:"-"`
SiteName string `json:"-"`
ID string `json:"_id"`
IP string `json:"ip"`
Mac string `json:"mac"`
Model string `json:"model"`
ModelInLts FlexBool `json:"model_in_lts"`
ModelInEol FlexBool `json:"model_in_eol"`
Type string `json:"type"`
Version string `json:"version"`
Adopted FlexBool `json:"adopted"`
SiteID string `json:"site_id"`
Cfgversion string `json:"cfgversion"`
SyslogKey string `json:"syslog_key"`
ConfigNetwork *ConfigNetwork `json:"config_network"`
SetupID string `json:"setup_id"`
LicenseState string `json:"license_state"`
ConfigNetworkLan *ConfigNetworkLan `json:"config_network_lan"`
InformURL string `json:"inform_url"`
InformIP string `json:"inform_ip"`
RequiredVersion string `json:"required_version"`
KernelVersion string `json:"kernel_version"`
Architecture string `json:"architecture"`
BoardRev FlexInt `json:"board_rev"`
ManufacturerID FlexInt `json:"manufacturer_id"`
Internet FlexBool `json:"internet"`
ModelIncompatible FlexBool `json:"model_incompatible"`
EthernetTable []*EthernetTable `json:"ethernet_table"`
PortTable []*Port `json:"port_table"`
EthernetOverrides []*EthernetOverrides `json:"ethernet_overrides"`
UsgCaps FlexInt `json:"usg_caps"`
HasSpeaker FlexBool `json:"has_speaker"`
HasEth1 FlexBool `json:"has_eth1"`
FwCaps FlexInt `json:"fw_caps"`
HwCaps FlexInt `json:"hw_caps"`
WifiCaps FlexInt `json:"wifi_caps"`
SwitchCaps *SwitchCaps `json:"switch_caps"`
HasFan FlexBool `json:"has_fan"`
HasTemperature FlexBool `json:"has_temperature"`
Temperatures []*Temperature `json:"temperatures"`
Storage []*Storage `json:"storage"`
RulesetInterfaces interface{} `json:"ruleset_interfaces"`
ConnectedAt FlexInt `json:"connected_at"`
ProvisionedAt FlexInt `json:"provisioned_at"`
LedOverride string `json:"led_override"`
LedOverrideColor string `json:"led_override_color"`
LedOverrideColorBrightness FlexInt `json:"led_override_color_brightness"`
OutdoorModeOverride string `json:"outdoor_mode_override"`
LcmBrightnessOverride FlexBool `json:"lcm_brightness_override"`
LcmIdleTimeoutOverride FlexBool `json:"lcm_idle_timeout_override"`
Name string `json:"name"`
Unsupported FlexBool `json:"unsupported"`
UnsupportedReason FlexInt `json:"unsupported_reason"`
Serial string `json:"serial"`
HashID string `json:"hash_id"`
TwoPhaseAdopt FlexBool `json:"two_phase_adopt"`
DeviceID string `json:"device_id"`
State FlexInt `json:"state"`
StartDisconnectedMillis FlexInt `json:"start_disconnected_millis"`
UpgradeState FlexInt `json:"upgrade_state"`
StartConnectedMillis FlexInt `json:"start_connected_millis"`
LastSeen FlexInt `json:"last_seen"`
Uptime FlexInt `json:"uptime"`
UnderscoreUptime FlexInt `json:"_uptime"`
Locating FlexBool `json:"locating"`
SysStats *SysStats `json:"sys_stats"`
SystemStats *SystemStats `json:"system-stats"`
GuestKicks FlexInt `json:"guest_kicks"`
GuestToken string `json:"guest_token"`
UptimeStats map[string]*UptimeStats `json:"uptime_stats"`
Overheating FlexBool `json:"overheating"`
GeoInfo map[string]*GeoInfo `json:"geo_info"`
LedState *LedState `json:"led_state"`
SpeedtestStatus *SpeedtestStatus `json:"speedtest-status"`
SpeedtestStatusSaved FlexBool `json:"speedtest-status-saved"`
Wan1 *Wan `json:"wan1"`
Wan2 *Wan `json:"wan2"`
Uplink *Uplink `json:"uplink"`
DownlinkTable []*DownlinkTable `json:"downlink_table"`
NetworkTable []*NetworkTable `json:"network_table"`
KnownCfgversion string `json:"known_cfgversion"`
ConnectRequestIP string `json:"connect_request_ip"`
ConnectRequestPort string `json:"connect_request_port"`
NextInterval FlexInt `json:"next_interval"`
NextHeartbeatAt FlexInt `json:"next_heartbeat_at"`
ConsideredLostAt FlexInt `json:"considered_lost_at"`
Stat *UXGStat `json:"stat"`
TxBytes FlexInt `json:"tx_bytes"`
RxBytes FlexInt `json:"rx_bytes"`
Bytes FlexInt `json:"bytes"`
NumSta FlexInt `json:"num_sta"`
WlanNumSta FlexInt `json:"wlan-num_sta"`
LanNumSta FlexInt `json:"lan-num_sta"`
UserWlanNumSta FlexInt `json:"user-wlan-num_sta"`
UserLanNumSta FlexInt `json:"user-lan-num_sta"`
UserNumSta FlexInt `json:"user-num_sta"`
GuestWlanNumSta FlexInt `json:"guest-wlan-num_sta"`
GuestLanNumSta FlexInt `json:"guest-lan-num_sta"`
GuestNumSta FlexInt `json:"guest-num_sta"`
NumDesktop FlexInt `json:"num_desktop"`
NumMobile FlexInt `json:"num_mobile"`
NumHandheld FlexInt `json:"num_handheld"`
}
// ConfigNetworkLan is part of a UXG, maybe others.
type ConfigNetworkLan struct {
DhcpEnabled FlexBool `json:"dhcp_enabled"`
Vlan int `json:"vlan"`
}
// DownlinkTable is part of a UXG and UDM output.
type DownlinkTable struct {
PortIdx FlexInt `json:"port_idx"`
Speed FlexInt `json:"speed"`
FullDuplex FlexBool `json:"full_duplex"`
Mac string `json:"mac"`
}
// LedState is incuded with newer devices.
type LedState struct {
Pattern string `json:"pattern"`
Tempo FlexInt `json:"tempo"`
}
// GeoInfo is incuded with certain devices.
type GeoInfo struct {
Accuracy FlexInt `json:"accuracy"`
Address string `json:"address"`
Asn FlexInt `json:"asn"`
City string `json:"city"`
ContinentCode string `json:"continent_code"`
CountryCode string `json:"country_code"`
CountryName string `json:"country_name"`
IspName string `json:"isp_name"`
IspOrganization string `json:"isp_organization"`
Latitude FlexInt `json:"latitude"`
Longitude FlexInt `json:"longitude"`
Timezone string `json:"timezone"`
}
// UptimeStats is incuded with certain devices.
type UptimeStats struct {
Availability FlexInt `json:"availability"`
LatencyAverage FlexInt `json:"latency_average"`
TimePeriod FlexInt `json:"time_period"`
}
// UXGStat holds the "stat" data for a 10Gb gateway.
type UXGStat struct {
*Gw `json:"gw"`
*Sw `json:"sw"`
}