Merge branch 'master' of ../unifi into merge-them-all
This commit is contained in:
commit
8deb07970c
|
|
@ -0,0 +1,2 @@
|
|||
/vendor
|
||||
*.swp
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
language: go
|
||||
go:
|
||||
- 1.16.x
|
||||
before_install:
|
||||
# download super-linter: golangci-lint
|
||||
- curl -sL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.38.0
|
||||
script:
|
||||
- golangci-lint run --enable-all -D exhaustivestruct,nlreturn,wrapcheck,maligned,interfacer
|
||||
- go test ./...
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
MIT LICENSE.
|
||||
Copyright (c) 2018-2020 David Newhall II
|
||||
Copyright (c) 2016 Garrett Bjerkhoel
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
# Go Library: `unifi`
|
||||
|
||||
It connects to a Unifi Controller, given a url, username and password. Returns
|
||||
an authenticated http Client you may use to query the device for data. Also
|
||||
contains some built-in methods for de-serializing common client and device
|
||||
data. The data is provided in a large struct you can consume in your application.
|
||||
|
||||
This library is designed to PULL data FROM the controller. It has no methods that
|
||||
update settings or change things on the controller.
|
||||
[Someone expressed interest](https://github.com/unpoller/unifi/issues/31) in
|
||||
adding methods to update data, and I'm okay with that. I'll even help add them.
|
||||
[Tell me what you want to do](https://github.com/unpoller/unifi/issues/new), and we'll make it happen.
|
||||
|
||||
Pull requests, feature requests, code reviews and feedback are welcomed!
|
||||
|
||||
Here's a working example:
|
||||
```golang
|
||||
package main
|
||||
|
||||
import "log"
|
||||
import "github.com/unpoller/unifi"
|
||||
|
||||
func main() {
|
||||
c := *unifi.Config{
|
||||
User: "admin",
|
||||
Pass: "superSecret1234",
|
||||
URL: "https://127.0.0.1:8443/",
|
||||
// Log with log.Printf or make your own interface that accepts (msg, fmt)
|
||||
ErrorLog: log.Printf,
|
||||
DebugLog: log.Printf,
|
||||
}
|
||||
uni, err := unifi.NewUnifi(c)
|
||||
if err != nil {
|
||||
log.Fatalln("Error:", err)
|
||||
}
|
||||
|
||||
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(sites), "Unifi Sites Found: ", sites)
|
||||
log.Println(len(clients), "Clients connected:")
|
||||
for i, client := range clients {
|
||||
log.Println(i+1, client.ID, client.Hostname, client.IP, client.Name, client.LastSeen)
|
||||
}
|
||||
|
||||
log.Println(len(devices.USWs), "Unifi Switches Found")
|
||||
log.Println(len(devices.USGs), "Unifi Gateways Found")
|
||||
|
||||
log.Println(len(devices.UAPs), "Unifi Wireless APs Found:")
|
||||
for i, uap := range devices.UAPs {
|
||||
log.Println(i+1, uap.Name, uap.IP)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
package unifi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Alarm struct {
|
||||
Archived FlexBool `json:"archived"`
|
||||
DestPort int `json:"dest_port"`
|
||||
SrcPort int `json:"src_port"`
|
||||
FlowID int64 `json:"flow_id"`
|
||||
InnerAlertGID int64 `json:"inner_alert_gid"`
|
||||
InnerAlertRev int64 `json:"inner_alert_rev"`
|
||||
InnerAlertSeverity int64 `json:"inner_alert_severity"`
|
||||
InnerAlertSignatureID int64 `json:"inner_alert_signature_id"`
|
||||
Time int64 `json:"time"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Datetime time.Time `json:"datetime"`
|
||||
HandledTime time.Time `json:"handled_time,omitempty"`
|
||||
AppProto string `json:"app_proto,omitempty"`
|
||||
Catname string `json:"catname"`
|
||||
DestIP string `json:"dest_ip"`
|
||||
DstMAC string `json:"dst_mac"`
|
||||
DstIPASN string `json:"dstipASN,omitempty"`
|
||||
DstIPCountry string `json:"dstipCountry,omitempty"`
|
||||
EventType string `json:"event_type"`
|
||||
HandledAdminID string `json:"handled_admin_id,omitempty"`
|
||||
Host string `json:"host"`
|
||||
ID string `json:"_id"`
|
||||
InIface string `json:"in_iface"`
|
||||
InnerAlertAction string `json:"inner_alert_action"`
|
||||
InnerAlertCategory string `json:"inner_alert_category"`
|
||||
InnerAlertSignature string `json:"inner_alert_signature"`
|
||||
Key string `json:"key"`
|
||||
Msg string `json:"msg"`
|
||||
Proto string `json:"proto"`
|
||||
SiteID string `json:"site_id"`
|
||||
SiteName string `json:"-"`
|
||||
SourceName string `json:"-"`
|
||||
SrcIP string `json:"src_ip"`
|
||||
SrcIPASN string `json:"srcipASN,omitempty"`
|
||||
SrcIPCountry string `json:"srcipCountry,omitempty"`
|
||||
SrcMAC string `json:"src_mac"`
|
||||
Subsystem string `json:"subsystem"`
|
||||
UniqueAlertID string `json:"unique_alertid"`
|
||||
USGIP string `json:"usgip"`
|
||||
USGIPASN string `json:"usgipASN"`
|
||||
USGIPCountry string `json:"usgipCountry"`
|
||||
TxID FlexInt `json:"tx_id,omitempty"`
|
||||
DestIPGeo IPGeo `json:"dstipGeo"`
|
||||
SourceIPGeo IPGeo `json:"usgipGeo"`
|
||||
USGIPGeo IPGeo `json:"srcipGeo,omitempty"`
|
||||
}
|
||||
|
||||
// GetAlarms returns Alarms for a list of Sites.
|
||||
func (u *Unifi) GetAlarms(sites []*Site) ([]*Alarm, error) {
|
||||
data := []*Alarm{}
|
||||
|
||||
for _, site := range sites {
|
||||
response, err := u.GetAlarmsSite(site)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
data = append(data, response...)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// GetAlarmsSite retreives the Alarms for a single Site.
|
||||
func (u *Unifi) GetAlarmsSite(site *Site) ([]*Alarm, error) {
|
||||
if site == nil || site.Name == "" {
|
||||
return nil, ErrNoSiteProvided
|
||||
}
|
||||
|
||||
u.DebugLog("Polling Controller for Alarms, site %s", site.SiteName)
|
||||
|
||||
var (
|
||||
path = fmt.Sprintf(APIEventPathAlarms, site.Name)
|
||||
alarms struct {
|
||||
Data alarms `json:"data"`
|
||||
}
|
||||
)
|
||||
|
||||
if err := u.GetData(path, &alarms, ""); err != nil {
|
||||
return alarms.Data, err
|
||||
}
|
||||
|
||||
for i := range alarms.Data {
|
||||
// Add special SourceName value.
|
||||
alarms.Data[i].SourceName = u.URL
|
||||
// Add the special "Site Name" to each event. This becomes a Grafana filter somewhere.
|
||||
alarms.Data[i].SiteName = site.SiteName
|
||||
}
|
||||
|
||||
sort.Sort(alarms.Data)
|
||||
|
||||
return alarms.Data, nil
|
||||
}
|
||||
|
||||
// alarms satisfies the sort.Sort Interface.
|
||||
type alarms []*Alarm
|
||||
|
||||
// Len satisfies sort.Interface.
|
||||
func (a alarms) Len() int {
|
||||
return len(a)
|
||||
}
|
||||
|
||||
// Swap satisfies sort.Interface.
|
||||
func (a alarms) Swap(i, j int) {
|
||||
a[i], a[j] = a[j], a[i]
|
||||
}
|
||||
|
||||
// Less satisfies sort.Interface. Sort our list by Datetime.
|
||||
func (a alarms) Less(i, j int) bool {
|
||||
return a[i].Datetime.Before(a[j].Datetime)
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
package unifi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// anomaly is the type UniFi returns, but not the type this library returns.
|
||||
type anomaly struct {
|
||||
Anomaly string `json:"anomaly"`
|
||||
MAC string `json:"mac"`
|
||||
Timestamps []int64 `json:"timestamps"`
|
||||
}
|
||||
|
||||
// Anomaly is the reformatted data type that this library returns.
|
||||
type Anomaly struct {
|
||||
Datetime time.Time
|
||||
SourceName string
|
||||
SiteName string
|
||||
Anomaly string
|
||||
DeviceMAC string
|
||||
// DeviceName string // we do not have this....
|
||||
}
|
||||
|
||||
// GetAnomalies returns Anomalies for a list of Sites.
|
||||
func (u *Unifi) GetAnomalies(sites []*Site, timeRange ...time.Time) ([]*Anomaly, error) {
|
||||
data := []*Anomaly{}
|
||||
|
||||
for _, site := range sites {
|
||||
response, err := u.GetAnomaliesSite(site, timeRange...)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
data = append(data, response...)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// GetAnomaliesSite retreives the Anomalies for a single Site.
|
||||
func (u *Unifi) GetAnomaliesSite(site *Site, timeRange ...time.Time) ([]*Anomaly, error) {
|
||||
if site == nil || site.Name == "" {
|
||||
return nil, ErrNoSiteProvided
|
||||
}
|
||||
|
||||
u.DebugLog("Polling Controller for Anomalies, site %s", site.SiteName)
|
||||
|
||||
var (
|
||||
path = fmt.Sprintf(APIAnomaliesPath, site.Name)
|
||||
anomalies = anomalies{}
|
||||
data struct {
|
||||
Data []*anomaly `json:"data"`
|
||||
}
|
||||
)
|
||||
|
||||
if params, err := makeAnomalyParams("hourly", timeRange...); err != nil {
|
||||
return anomalies, err
|
||||
} else if err := u.GetData(path+params, &data, ""); err != nil {
|
||||
return anomalies, err
|
||||
}
|
||||
|
||||
for _, d := range data.Data {
|
||||
for _, ts := range d.Timestamps {
|
||||
anomalies = append(anomalies, &Anomaly{
|
||||
Datetime: time.Unix(ts/int64(time.Microsecond), 0),
|
||||
SourceName: u.URL,
|
||||
SiteName: site.SiteName,
|
||||
Anomaly: d.Anomaly,
|
||||
DeviceMAC: d.MAC,
|
||||
// DeviceName: d.Anomaly,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(anomalies)
|
||||
|
||||
return anomalies, nil
|
||||
}
|
||||
|
||||
// anomalies satisfies the sort.Sort interface.
|
||||
type anomalies []*Anomaly
|
||||
|
||||
// Len satisfies sort.Interface.
|
||||
func (a anomalies) Len() int {
|
||||
return len(a)
|
||||
}
|
||||
|
||||
// Swap satisfies sort.Interface.
|
||||
func (a anomalies) Swap(i, j int) {
|
||||
a[i], a[j] = a[j], a[i]
|
||||
}
|
||||
|
||||
// Less satisfies sort.Interface. Sort our list by Datetime.
|
||||
func (a anomalies) Less(i, j int) bool {
|
||||
return a[i].Datetime.Before(a[j].Datetime)
|
||||
}
|
||||
|
||||
func makeAnomalyParams(scale string, timeRange ...time.Time) (string, error) {
|
||||
out := []string{}
|
||||
|
||||
if scale != "" {
|
||||
out = append(out, "scale="+scale)
|
||||
}
|
||||
|
||||
switch len(timeRange) {
|
||||
case 0:
|
||||
end := time.Now().Unix() * int64(time.Microsecond)
|
||||
out = append(out, "end="+strconv.FormatInt(end, 10))
|
||||
case 1:
|
||||
start := timeRange[0].Unix() * int64(time.Microsecond)
|
||||
end := time.Now().Unix() * int64(time.Microsecond)
|
||||
out = append(out, "end="+strconv.FormatInt(end, 10), "start="+strconv.FormatInt(start, 10))
|
||||
case 2: // nolint: gomnd
|
||||
start := timeRange[0].Unix() * int64(time.Microsecond)
|
||||
end := timeRange[1].Unix() * int64(time.Microsecond)
|
||||
out = append(out, "end="+strconv.FormatInt(end, 10), "start="+strconv.FormatInt(start, 10))
|
||||
default:
|
||||
return "", ErrInvalidTimeRange
|
||||
}
|
||||
|
||||
if len(out) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return "?" + strings.Join(out, "&"), nil
|
||||
}
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
package unifi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GetClients returns a response full of clients' data from the UniFi Controller.
|
||||
func (u *Unifi) GetClients(sites []*Site) ([]*Client, error) {
|
||||
data := make([]*Client, 0)
|
||||
|
||||
for _, site := range sites {
|
||||
var response struct {
|
||||
Data []*Client `json:"data"`
|
||||
}
|
||||
|
||||
u.DebugLog("Polling Controller, retreiving UniFi Clients, site %s ", site.SiteName)
|
||||
|
||||
clientPath := fmt.Sprintf(APIClientPath, site.Name)
|
||||
if err := u.GetData(clientPath, &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, d := range response.Data {
|
||||
// Add special SourceName value.
|
||||
response.Data[i].SourceName = u.URL
|
||||
// Add the special "Site Name" to each client. This becomes a Grafana filter somewhere.
|
||||
response.Data[i].SiteName = site.SiteName
|
||||
// Fix name and hostname fields. Sometimes one or the other is blank.
|
||||
response.Data[i].Hostname = strings.TrimSpace(pick(d.Hostname, d.Name, d.Mac))
|
||||
response.Data[i].Name = strings.TrimSpace(pick(d.Name, d.Hostname))
|
||||
}
|
||||
|
||||
data = append(data, response.Data...)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// GetClientsDPI garners dpi data for clients.
|
||||
func (u *Unifi) GetClientsDPI(sites []*Site) ([]*DPITable, error) {
|
||||
var data []*DPITable
|
||||
|
||||
for _, site := range sites {
|
||||
u.DebugLog("Polling Controller, retreiving Client DPI data, site %s", site.SiteName)
|
||||
|
||||
var response struct {
|
||||
Data []*DPITable `json:"data"`
|
||||
}
|
||||
|
||||
clientDPIpath := fmt.Sprintf(APIClientDPI, site.Name)
|
||||
if err := u.GetData(clientDPIpath, &response, `{"type":"by_app"}`); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, d := range response.Data {
|
||||
d.SourceName = site.SourceName
|
||||
d.SiteName = site.SiteName
|
||||
data = append(data, d)
|
||||
}
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Client defines all the data a connected-network client contains.
|
||||
type Client struct {
|
||||
SourceName string `json:"-"`
|
||||
Anomalies int64 `json:"anomalies,omitempty"`
|
||||
ApMac string `json:"ap_mac"`
|
||||
ApName string `json:"-"`
|
||||
AssocTime int64 `json:"assoc_time"`
|
||||
Blocked bool `json:"blocked,omitempty"`
|
||||
Bssid string `json:"bssid"`
|
||||
BytesR int64 `json:"bytes-r"`
|
||||
Ccq int64 `json:"ccq"`
|
||||
Channel FlexInt `json:"channel"`
|
||||
DevCat FlexInt `json:"dev_cat"`
|
||||
DevFamily FlexInt `json:"dev_family"`
|
||||
DevID FlexInt `json:"dev_id"`
|
||||
DevVendor FlexInt `json:"dev_vendor,omitempty"`
|
||||
DhcpendTime FlexInt `json:"dhcpend_time,omitempty"`
|
||||
Satisfaction FlexInt `json:"satisfaction,omitempty"`
|
||||
Essid string `json:"essid"`
|
||||
FirstSeen int64 `json:"first_seen"`
|
||||
FixedIP string `json:"fixed_ip"`
|
||||
GwMac string `json:"gw_mac"`
|
||||
GwName string `json:"-"`
|
||||
Hostname string `json:"hostname"`
|
||||
ID string `json:"_id"`
|
||||
IP string `json:"ip"`
|
||||
IdleTime int64 `json:"idle_time"`
|
||||
Is11R FlexBool `json:"is_11r"`
|
||||
IsGuest FlexBool `json:"is_guest"`
|
||||
IsGuestByUAP FlexBool `json:"_is_guest_by_uap"`
|
||||
IsGuestByUGW FlexBool `json:"_is_guest_by_ugw"`
|
||||
IsGuestByUSW FlexBool `json:"_is_guest_by_usw"`
|
||||
IsWired FlexBool `json:"is_wired"`
|
||||
LastSeen int64 `json:"last_seen"`
|
||||
LastSeenByUAP int64 `json:"_last_seen_by_uap"`
|
||||
LastSeenByUGW int64 `json:"_last_seen_by_ugw"`
|
||||
LastSeenByUSW int64 `json:"_last_seen_by_usw"`
|
||||
LatestAssocTime int64 `json:"latest_assoc_time"`
|
||||
Mac string `json:"mac"`
|
||||
Name string `json:"name"`
|
||||
Network string `json:"network"`
|
||||
NetworkID string `json:"network_id"`
|
||||
Noise int64 `json:"noise"`
|
||||
Note string `json:"note"`
|
||||
Noted FlexBool `json:"noted"`
|
||||
OsClass FlexInt `json:"os_class"`
|
||||
OsName FlexInt `json:"os_name"`
|
||||
Oui string `json:"oui"`
|
||||
PowersaveEnabled FlexBool `json:"powersave_enabled"`
|
||||
QosPolicyApplied FlexBool `json:"qos_policy_applied"`
|
||||
Radio string `json:"radio"`
|
||||
RadioName string `json:"radio_name"`
|
||||
RadioProto string `json:"radio_proto"`
|
||||
RadioDescription string `json:"-"`
|
||||
RoamCount int64 `json:"roam_count"`
|
||||
Rssi int64 `json:"rssi"`
|
||||
RxBytes int64 `json:"rx_bytes"`
|
||||
RxBytesR int64 `json:"rx_bytes-r"`
|
||||
RxPackets int64 `json:"rx_packets"`
|
||||
RxRate int64 `json:"rx_rate"`
|
||||
Signal int64 `json:"signal"`
|
||||
SiteID string `json:"site_id"`
|
||||
SiteName string `json:"-"`
|
||||
SwDepth int `json:"sw_depth"`
|
||||
SwMac string `json:"sw_mac"`
|
||||
SwName string `json:"-"`
|
||||
SwPort FlexInt `json:"sw_port"`
|
||||
TxBytes int64 `json:"tx_bytes"`
|
||||
TxBytesR int64 `json:"tx_bytes-r"`
|
||||
TxPackets int64 `json:"tx_packets"`
|
||||
TxRetries int64 `json:"tx_retries"`
|
||||
TxPower int64 `json:"tx_power"`
|
||||
TxRate int64 `json:"tx_rate"`
|
||||
Uptime int64 `json:"uptime"`
|
||||
UptimeByUAP int64 `json:"_uptime_by_uap"`
|
||||
UptimeByUGW int64 `json:"_uptime_by_ugw"`
|
||||
UptimeByUSW int64 `json:"_uptime_by_usw"`
|
||||
UseFixedIP FlexBool `json:"use_fixedip"`
|
||||
UserGroupID string `json:"usergroup_id"`
|
||||
UserID string `json:"user_id"`
|
||||
Vlan FlexInt `json:"vlan"`
|
||||
WifiTxAttempts int64 `json:"wifi_tx_attempts"`
|
||||
WiredRxBytes int64 `json:"wired-rx_bytes"`
|
||||
WiredRxBytesR int64 `json:"wired-rx_bytes-r"`
|
||||
WiredRxPackets int64 `json:"wired-rx_packets"`
|
||||
WiredTxBytes int64 `json:"wired-tx_bytes"`
|
||||
WiredTxBytesR int64 `json:"wired-tx_bytes-r"`
|
||||
WiredTxPackets int64 `json:"wired-tx_packets"`
|
||||
}
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
package unifi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GetDevices returns a response full of devices' data from the UniFi Controller.
|
||||
func (u *Unifi) GetDevices(sites []*Site) (*Devices, error) {
|
||||
devices := new(Devices)
|
||||
|
||||
for _, site := range sites {
|
||||
var response struct {
|
||||
Data []json.RawMessage `json:"data"`
|
||||
}
|
||||
|
||||
devicePath := fmt.Sprintf(APIDevicePath, site.Name)
|
||||
if err := u.GetData(devicePath, &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
loopDevices := u.parseDevices(response.Data, site)
|
||||
devices.UAPs = append(devices.UAPs, loopDevices.UAPs...)
|
||||
devices.USGs = append(devices.USGs, loopDevices.USGs...)
|
||||
devices.USWs = append(devices.USWs, loopDevices.USWs...)
|
||||
devices.UDMs = append(devices.UDMs, loopDevices.UDMs...)
|
||||
devices.UXGs = append(devices.UXGs, loopDevices.UXGs...)
|
||||
}
|
||||
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
// GetUSWs returns all switches, an error, or nil if there are no switches.
|
||||
func (u *Unifi) GetUSWs(site *Site) ([]*USW, error) {
|
||||
var response struct {
|
||||
Data []json.RawMessage `json:"data"`
|
||||
}
|
||||
|
||||
err := u.GetData(fmt.Sprintf(APIDevicePath, site.Name), &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return u.parseDevices(response.Data, site).USWs, nil
|
||||
}
|
||||
|
||||
// GetUAPs returns all access points, an error, or nil if there are no APs.
|
||||
func (u *Unifi) GetUAPs(site *Site) ([]*UAP, error) {
|
||||
var response struct {
|
||||
Data []json.RawMessage `json:"data"`
|
||||
}
|
||||
|
||||
err := u.GetData(fmt.Sprintf(APIDevicePath, site.Name), &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return u.parseDevices(response.Data, site).UAPs, nil
|
||||
}
|
||||
|
||||
// GetUDMs returns all dream machines, an error, or nil if there are no UDMs.
|
||||
func (u *Unifi) GetUDMs(site *Site) ([]*UDM, error) {
|
||||
var response struct {
|
||||
Data []json.RawMessage `json:"data"`
|
||||
}
|
||||
|
||||
err := u.GetData(fmt.Sprintf(APIDevicePath, site.Name), &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return u.parseDevices(response.Data, site).UDMs, nil
|
||||
}
|
||||
|
||||
// GetUXGs returns all 10Gb gateways, an error, or nil if there are no UXGs.
|
||||
func (u *Unifi) GetUXGs(site *Site) ([]*UXG, error) {
|
||||
var response struct {
|
||||
Data []json.RawMessage `json:"data"`
|
||||
}
|
||||
|
||||
err := u.GetData(fmt.Sprintf(APIDevicePath, site.Name), &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return u.parseDevices(response.Data, site).UXGs, nil
|
||||
}
|
||||
|
||||
// GetUSGs returns all 1Gb gateways, an error, or nil if there are no USGs.
|
||||
func (u *Unifi) GetUSGs(site *Site) ([]*USG, error) {
|
||||
var response struct {
|
||||
Data []json.RawMessage `json:"data"`
|
||||
}
|
||||
|
||||
err := u.GetData(fmt.Sprintf(APIDevicePath, site.Name), &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return u.parseDevices(response.Data, site).USGs, nil
|
||||
}
|
||||
|
||||
// parseDevices parses the raw JSON from the Unifi Controller into device structures.
|
||||
func (u *Unifi) parseDevices(data []json.RawMessage, site *Site) *Devices {
|
||||
devices := new(Devices)
|
||||
|
||||
for _, r := range data {
|
||||
// Loop each item in the raw JSON message, detect its type and unmarshal it.
|
||||
o := make(map[string]interface{})
|
||||
if u.unmarshalDevice("map", r, &o) != nil {
|
||||
u.ErrorLog("unknown asset type - cannot find asset type in payload - skipping")
|
||||
continue
|
||||
}
|
||||
|
||||
assetType, _ := o["type"].(string)
|
||||
u.DebugLog("Unmarshalling Device Type: %v, site %s ", assetType, site.SiteName)
|
||||
// Choose which type to unmarshal into based on the "type" json key.
|
||||
|
||||
switch assetType { // Unmarshal again into the correct type..
|
||||
case "uap":
|
||||
u.unmarshallUAP(site, r, devices)
|
||||
case "ugw", "usg": // in case they ever fix the name in the api.
|
||||
u.unmarshallUSG(site, r, devices)
|
||||
case "usw":
|
||||
u.unmarshallUSW(site, r, devices)
|
||||
case "udm":
|
||||
u.unmarshallUDM(site, r, devices)
|
||||
case "uxg":
|
||||
u.unmarshallUXG(site, r, devices)
|
||||
default:
|
||||
u.ErrorLog("unknown asset type - %v - skipping", assetType)
|
||||
}
|
||||
}
|
||||
|
||||
return devices
|
||||
}
|
||||
|
||||
func (u *Unifi) unmarshallUAP(site *Site, payload json.RawMessage, devices *Devices) {
|
||||
dev := &UAP{SiteName: site.SiteName, SourceName: u.URL}
|
||||
if u.unmarshalDevice("uap", payload, dev) == nil {
|
||||
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
|
||||
dev.site = site
|
||||
devices.UAPs = append(devices.UAPs, dev)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Unifi) unmarshallUSG(site *Site, payload json.RawMessage, devices *Devices) {
|
||||
dev := &USG{SiteName: site.SiteName, SourceName: u.URL}
|
||||
if u.unmarshalDevice("ugw", payload, dev) == nil {
|
||||
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
|
||||
dev.site = site
|
||||
devices.USGs = append(devices.USGs, dev)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Unifi) unmarshallUSW(site *Site, payload json.RawMessage, devices *Devices) {
|
||||
dev := &USW{SiteName: site.SiteName, SourceName: u.URL}
|
||||
if u.unmarshalDevice("usw", payload, dev) == nil {
|
||||
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
|
||||
dev.site = site
|
||||
devices.USWs = append(devices.USWs, dev)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Unifi) unmarshallUXG(site *Site, payload json.RawMessage, devices *Devices) {
|
||||
dev := &UXG{SiteName: site.SiteName, SourceName: u.URL}
|
||||
if u.unmarshalDevice("uxg", payload, dev) == nil {
|
||||
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
|
||||
dev.site = site
|
||||
devices.UXGs = append(devices.UXGs, dev)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Unifi) unmarshallUDM(site *Site, payload json.RawMessage, devices *Devices) {
|
||||
dev := &UDM{SiteName: site.SiteName, SourceName: u.URL}
|
||||
if u.unmarshalDevice("udm", payload, dev) == nil {
|
||||
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
|
||||
dev.site = site
|
||||
devices.UDMs = append(devices.UDMs, dev)
|
||||
}
|
||||
}
|
||||
|
||||
// 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.ErrorLog("json.Unmarshal(%v): %v", dev, err)
|
||||
u.ErrorLog("Enable Debug Logging to output the failed payload.")
|
||||
|
||||
json, err := data.MarshalJSON()
|
||||
u.DebugLog("Failed Payload: %s (marshal err: %v)", json, err)
|
||||
u.DebugLog("The above payload can prove useful during torubleshooting when you open an Issue:")
|
||||
u.DebugLog("==- https://github.com/unifi-poller/unifi/issues/new -==")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("json unmarshal: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// pick returns the first non empty string in a list.
|
||||
// used in a few places around this library.
|
||||
func pick(strings ...string) string {
|
||||
for _, s := range strings {
|
||||
if s != "" {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
|
@ -0,0 +1,299 @@
|
|||
package unifi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Known commands that can be sent to device manager. All of these are implemented.
|
||||
//nolint:lll // https://ubntwiki.com/products/software/unifi-controller/api#callable
|
||||
const (
|
||||
DevMgrPowerCycle = "power-cycle" // mac = switch mac (required), port_idx = PoE port to cycle (required)
|
||||
DevMgrAdopt = "adopt" // mac = device mac (required)
|
||||
DevMgrRestart = "restart" // mac = device mac (required)
|
||||
DevMgrForceProvision = "force-provision" // mac = device mac (required)
|
||||
DevMgrSpeedTest = "speedtest" // Start a speed test
|
||||
DevMgrSpeedTestStatus = "speedtest-status" // Get current state of the speed test
|
||||
DevMgrSetLocate = "set-locate" // mac = device mac (required): blink unit to locate
|
||||
DevMgrUnsetLocate = "unset-locate" // mac = device mac (required): led to normal state
|
||||
DevMgrUpgrade = "upgrade" // mac = device mac (required): upgrade firmware
|
||||
DevMgrUpgradeExternal = "upgrade-external" // mac = device mac (required), url = firmware URL (required)
|
||||
DevMgrMigrate = "migrate" // mac = device mac (required), inform_url = New Inform URL for device (required)
|
||||
DevMgrCancelMigrate = "cancel-migrate" // mac = device mac (required)
|
||||
DevMgrSpectrumScan = "spectrum-scan" // mac = AP mac (required): trigger RF scan
|
||||
)
|
||||
|
||||
// devMgrCmd is the type marshalled and sent to APIDevMgrPath.
|
||||
type devMgrCmd struct {
|
||||
Cmd string `json:"cmd"` // Required.
|
||||
Mac string `json:"mac"` // Device MAC (required for most, but not all).
|
||||
URL string `json:"url,omitempty"` // External Upgrade only.
|
||||
Inform string `json:"inform_url,omitempty"` // Migration only.
|
||||
Port int `json:"port_idx,omitempty"` // Power Cycle only.
|
||||
}
|
||||
|
||||
// devMgrCommandReply is for commands with a return value.
|
||||
func (s *Site) devMgrCommandReply(cmd *devMgrCmd) ([]byte, error) {
|
||||
data, err := json.Marshal(cmd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("json marshal: %w", err)
|
||||
}
|
||||
|
||||
b, err := s.controller.GetJSON(fmt.Sprintf(APIDevMgrPath, s.Name), string(data))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("controller: %w", err)
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// devMgrCommandSimple is for commands with no return value.
|
||||
func (s *Site) devMgrCommandSimple(cmd *devMgrCmd) error {
|
||||
_, err := s.devMgrCommandReply(cmd)
|
||||
return err
|
||||
}
|
||||
|
||||
// PowerCycle shuts off the PoE and turns it back on for a specific port.
|
||||
// Get a USW from the device list to call this.
|
||||
func (u *USW) PowerCycle(portIndex int) error {
|
||||
return u.site.devMgrCommandSimple(&devMgrCmd{
|
||||
Cmd: DevMgrPowerCycle,
|
||||
Mac: u.Mac,
|
||||
Port: portIndex,
|
||||
})
|
||||
}
|
||||
|
||||
// ScanRF begins a spectrum scan on an access point.
|
||||
func (u *UAP) ScanRF() error {
|
||||
return u.site.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrSpectrumScan, Mac: u.Mac})
|
||||
}
|
||||
|
||||
// Restart a device by MAC address on your site.
|
||||
func (s *Site) Restart(mac string) error {
|
||||
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrRestart, Mac: mac})
|
||||
}
|
||||
|
||||
// Restart an access point.
|
||||
func (u *UAP) Restart() error {
|
||||
return u.site.Restart(u.Mac)
|
||||
}
|
||||
|
||||
// Restart a switch.
|
||||
func (u *USW) Restart() error {
|
||||
return u.site.Restart(u.Mac)
|
||||
}
|
||||
|
||||
// Restart a security gateway.
|
||||
func (u *USG) Restart() error {
|
||||
return u.site.Restart(u.Mac)
|
||||
}
|
||||
|
||||
// Restart a dream machine.
|
||||
func (u *UDM) Restart() error {
|
||||
return u.site.Restart(u.Mac)
|
||||
}
|
||||
|
||||
// Restart a 10Gb security gateway.
|
||||
func (u *UXG) Restart() error {
|
||||
return u.site.Restart(u.Mac)
|
||||
}
|
||||
|
||||
// Locate a device by MAC address on your site. This makes it blink.
|
||||
func (s *Site) Locate(mac string) error {
|
||||
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrSetLocate, Mac: mac})
|
||||
}
|
||||
|
||||
// Locate an access point.
|
||||
func (u *UAP) Locate() error {
|
||||
return u.site.Locate(u.Mac)
|
||||
}
|
||||
|
||||
// Locate a switch.
|
||||
func (u *USW) Locate() error {
|
||||
return u.site.Locate(u.Mac)
|
||||
}
|
||||
|
||||
// Locate a security gateway.
|
||||
func (u *USG) Locate() error {
|
||||
return u.site.Locate(u.Mac)
|
||||
}
|
||||
|
||||
// Locate a dream machine.
|
||||
func (u *UDM) Locate() error {
|
||||
return u.site.Locate(u.Mac)
|
||||
}
|
||||
|
||||
// Locate a 10Gb security gateway.
|
||||
func (u *UXG) Locate() error {
|
||||
return u.site.Locate(u.Mac)
|
||||
}
|
||||
|
||||
// Unlocate a device by MAC address on your site. This makes it stop blinking.
|
||||
func (s *Site) Unlocate(mac string) error {
|
||||
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrUnsetLocate, Mac: mac})
|
||||
}
|
||||
|
||||
// Unlocate an access point (stop blinking).
|
||||
func (u *UAP) Unlocate() error {
|
||||
return u.site.Unlocate(u.Mac)
|
||||
}
|
||||
|
||||
// Unlocate a switch (stop blinking).
|
||||
func (u *USW) Unlocate() error {
|
||||
return u.site.Unlocate(u.Mac)
|
||||
}
|
||||
|
||||
// Unlocate a security gateway (stop blinking).
|
||||
func (u *USG) Unlocate() error {
|
||||
return u.site.Unlocate(u.Mac)
|
||||
}
|
||||
|
||||
// Unlocate a dream machine (stop blinking).
|
||||
func (u *UDM) Unlocate() error {
|
||||
return u.site.Unlocate(u.Mac)
|
||||
}
|
||||
|
||||
// Unlocate a 10Gb security gateway (stop blinking).
|
||||
func (u *UXG) Unlocate() error {
|
||||
return u.site.Unlocate(u.Mac)
|
||||
}
|
||||
|
||||
// Provision force provisions a device by MAC address on your site.
|
||||
func (s *Site) Provision(mac string) error {
|
||||
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrForceProvision, Mac: mac})
|
||||
}
|
||||
|
||||
// Provision an access point forcefully.
|
||||
func (u *UAP) Provision() error {
|
||||
return u.site.Provision(u.Mac)
|
||||
}
|
||||
|
||||
// Provision a switch forcefully.
|
||||
func (u *USW) Provision() error {
|
||||
return u.site.Provision(u.Mac)
|
||||
}
|
||||
|
||||
// Provision a security gateway forcefully.
|
||||
func (u *USG) Provision() error {
|
||||
return u.site.Provision(u.Mac)
|
||||
}
|
||||
|
||||
// Provision a dream machine forcefully.
|
||||
func (u *UDM) Provision() error {
|
||||
return u.site.Provision(u.Mac)
|
||||
}
|
||||
|
||||
// Provision a 10Gb security gateway forcefully.
|
||||
func (u *UXG) Provision() error {
|
||||
return u.site.Provision(u.Mac)
|
||||
}
|
||||
|
||||
// Upgrade starts a firmware upgrade on a device by MAC address on your site.
|
||||
// URL is optional. If URL is not "" an external upgrade is performed.
|
||||
func (s *Site) Upgrade(mac string, url string) error {
|
||||
if url == "" {
|
||||
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrUpgrade, Mac: mac})
|
||||
}
|
||||
|
||||
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrUpgradeExternal, Mac: mac, URL: url})
|
||||
}
|
||||
|
||||
// Upgrade firmware on an access point.
|
||||
// URL is optional. If URL is not "" an external upgrade is performed.
|
||||
func (u *UAP) Upgrade(url string) error {
|
||||
return u.site.Upgrade(u.Mac, url)
|
||||
}
|
||||
|
||||
// Upgrade firmware on a switch.
|
||||
// URL is optional. If URL is not "" an external upgrade is performed.
|
||||
func (u *USW) Upgrade(url string) error {
|
||||
return u.site.Upgrade(u.Mac, url)
|
||||
}
|
||||
|
||||
// Upgrade firmware on a security gateway.
|
||||
// URL is optional. If URL is not "" an external upgrade is performed.
|
||||
func (u *USG) Upgrade(url string) error {
|
||||
return u.site.Upgrade(u.Mac, url)
|
||||
}
|
||||
|
||||
// Upgrade firmware on a dream machine.
|
||||
// URL is optional. If URL is not "" an external upgrade is performed.
|
||||
func (u *UDM) Upgrade(url string) error {
|
||||
return u.site.Upgrade(u.Mac, url)
|
||||
}
|
||||
|
||||
// Upgrade formware on a 10Gb security gateway.
|
||||
// URL is optional. If URL is not "" an external upgrade is performed.
|
||||
func (u *UXG) Upgrade(url string) error {
|
||||
return u.site.Upgrade(u.Mac, url)
|
||||
}
|
||||
|
||||
// Migrate sends a device to another controller's URL.
|
||||
// Probably does not work on devices with built-in controllers like UDM & UXG.
|
||||
func (s *Site) Migrate(mac string, url string) error {
|
||||
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrMigrate, Mac: mac, Inform: url})
|
||||
}
|
||||
|
||||
// Migrate sends an access point to another controller's URL.
|
||||
func (u *UAP) Migrate(url string) error {
|
||||
return u.site.Migrate(u.Mac, url)
|
||||
}
|
||||
|
||||
// Migrate sends a switch to another controller's URL.
|
||||
func (u *USW) Migrate(url string) error {
|
||||
return u.site.Migrate(u.Mac, url)
|
||||
}
|
||||
|
||||
// Migrate sends a security gateway to another controller's URL.
|
||||
func (u *USG) Migrate(url string) error {
|
||||
return u.site.Migrate(u.Mac, url)
|
||||
}
|
||||
|
||||
// Migrate sends a 10Gb gateway to another controller's URL.
|
||||
func (u *UXG) Migrate(url string) error {
|
||||
return u.site.Migrate(u.Mac, url)
|
||||
}
|
||||
|
||||
// CancelMigrate stops a migration in progress.
|
||||
// Probably does not work on devices with built-in controllers like UDM & UXG.
|
||||
func (s *Site) CancelMigrate(mac string) error {
|
||||
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrCancelMigrate, Mac: mac})
|
||||
}
|
||||
|
||||
// CancelMigrate stops an access point migration in progress.
|
||||
func (u *UAP) CancelMigrate() error {
|
||||
return u.site.CancelMigrate(u.Mac)
|
||||
}
|
||||
|
||||
// CancelMigrate stops a switch migration in progress.
|
||||
func (u *USW) CancelMigrate() error {
|
||||
return u.site.CancelMigrate(u.Mac)
|
||||
}
|
||||
|
||||
// CancelMigrate stops a security gateway migration in progress.
|
||||
func (u *USG) CancelMigrate() error {
|
||||
return u.site.CancelMigrate(u.Mac)
|
||||
}
|
||||
|
||||
// CancelMigrate stops 10Gb gateway a migration in progress.
|
||||
func (u *UXG) CancelMigrate() error {
|
||||
return u.site.CancelMigrate(u.Mac)
|
||||
}
|
||||
|
||||
// Adopt a device by MAC address to your site.
|
||||
func (s *Site) Adopt(mac string) error {
|
||||
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrAdopt, Mac: mac})
|
||||
}
|
||||
|
||||
// SpeedTest begins a speed test on a site.
|
||||
func (s *Site) SpeedTest() error {
|
||||
return s.devMgrCommandSimple(&devMgrCmd{Cmd: DevMgrSpeedTest})
|
||||
}
|
||||
|
||||
// SpeedTestStatus returns the raw response for the status of a speed test.
|
||||
// XXX: marshal the response into a data structure. This method will change!
|
||||
func (s *Site) SpeedTestStatus() ([]byte, error) {
|
||||
body, err := s.devMgrCommandReply(&devMgrCmd{Cmd: DevMgrSpeedTestStatus})
|
||||
// marshal into struct here.
|
||||
return body, err
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,204 @@
|
|||
package unifi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoSiteProvided = fmt.Errorf("site must not be nil or empty")
|
||||
ErrInvalidTimeRange = fmt.Errorf("only 0, 1 or 2 times may be provided to timeRange")
|
||||
)
|
||||
|
||||
const (
|
||||
eventLimit = 50000
|
||||
)
|
||||
|
||||
// GetEvents returns a response full of UniFi Events for the last 1 hour from multiple sites.
|
||||
func (u *Unifi) GetEvents(sites []*Site, hours time.Duration) ([]*Event, error) {
|
||||
data := make([]*Event, 0)
|
||||
|
||||
for _, site := range sites {
|
||||
response, err := u.GetSiteEvents(site, hours)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
data = append(data, response...)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if site == nil || site.Name == "" {
|
||||
return nil, ErrNoSiteProvided
|
||||
}
|
||||
|
||||
if hours < time.Hour {
|
||||
hours = time.Hour
|
||||
}
|
||||
|
||||
u.DebugLog("Polling Controller, retreiving UniFi Events, site %s", site.SiteName)
|
||||
|
||||
var (
|
||||
path = fmt.Sprintf(APIEventPath, site.Name)
|
||||
params = fmt.Sprintf(`{"_limit":%d,"within":%d,"_sort":"-time"}}`,
|
||||
eventLimit, int(hours.Round(time.Hour).Hours()))
|
||||
event struct {
|
||||
Data events `json:"data"`
|
||||
}
|
||||
)
|
||||
|
||||
if err := u.GetData(path, &event, params); err != nil {
|
||||
return event.Data, err
|
||||
}
|
||||
|
||||
for i := range event.Data {
|
||||
// Add special SourceName value.
|
||||
event.Data[i].SourceName = u.URL
|
||||
// Add the special "Site Name" to each event. This becomes a Grafana filter somewhere.
|
||||
event.Data[i].SiteName = site.SiteName
|
||||
}
|
||||
|
||||
sort.Sort(event.Data)
|
||||
|
||||
return event.Data, nil
|
||||
}
|
||||
|
||||
// Event describes a UniFi Event.
|
||||
// API Path: /api/s/default/stat/event.
|
||||
type Event struct {
|
||||
IsAdmin FlexBool `json:"is_admin"`
|
||||
DestPort int `json:"dest_port"`
|
||||
SrcPort int `json:"src_port"`
|
||||
Bytes FlexInt `json:"bytes"`
|
||||
Duration FlexInt `json:"duration"`
|
||||
FlowID FlexInt `json:"flow_id"`
|
||||
InnerAlertGID FlexInt `json:"inner_alert_gid"`
|
||||
InnerAlertRev FlexInt `json:"inner_alert_rev"`
|
||||
InnerAlertSeverity FlexInt `json:"inner_alert_severity"`
|
||||
InnerAlertSignatureID FlexInt `json:"inner_alert_signature_id"`
|
||||
Channel FlexInt `json:"channel"`
|
||||
ChannelFrom FlexInt `json:"channel_from"`
|
||||
ChannelTo FlexInt `json:"channel_to"`
|
||||
Time int64 `json:"time"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Datetime time.Time `json:"datetime"`
|
||||
Admin string `json:"admin"`
|
||||
Ap string `json:"ap"`
|
||||
ApFrom string `json:"ap_from"`
|
||||
ApName string `json:"ap_name"`
|
||||
ApTo string `json:"ap_to"`
|
||||
AppProto string `json:"app_proto"`
|
||||
Catname string `json:"catname"`
|
||||
DestIP string `json:"dest_ip"`
|
||||
DstMAC string `json:"dst_mac"`
|
||||
EventType string `json:"event_type"`
|
||||
Guest string `json:"guest"`
|
||||
Gw string `json:"gw"`
|
||||
GwName string `json:"gw_name"`
|
||||
Host string `json:"host"`
|
||||
Hostname string `json:"hostname"`
|
||||
ID string `json:"_id"`
|
||||
IP string `json:"ip"`
|
||||
InIface string `json:"in_iface"`
|
||||
InnerAlertAction string `json:"inner_alert_action"`
|
||||
InnerAlertCategory string `json:"inner_alert_category"`
|
||||
InnerAlertSignature string `json:"inner_alert_signature"`
|
||||
Key string `json:"key"`
|
||||
Msg string `json:"msg"`
|
||||
Network string `json:"network"`
|
||||
Proto string `json:"proto"`
|
||||
Radio string `json:"radio"`
|
||||
RadioFrom string `json:"radio_from"`
|
||||
RadioTo string `json:"radio_to"`
|
||||
SiteID string `json:"site_id"`
|
||||
SiteName string `json:"-"`
|
||||
SourceName string `json:"-"`
|
||||
SrcIP string `json:"src_ip"`
|
||||
SrcMAC string `json:"src_mac"`
|
||||
SrcIPASN string `json:"srcipASN"`
|
||||
SrcIPCountry string `json:"srcipCountry"`
|
||||
SSID string `json:"ssid"`
|
||||
Subsystem string `json:"subsystem"`
|
||||
Sw string `json:"sw"`
|
||||
SwName string `json:"sw_name"`
|
||||
UniqueAlertID string `json:"unique_alertid"`
|
||||
User string `json:"user"`
|
||||
USGIP string `json:"usgip"`
|
||||
USGIPASN string `json:"usgipASN"`
|
||||
USGIPCountry string `json:"usgipCountry"`
|
||||
DestIPGeo IPGeo `json:"dstipGeo"`
|
||||
SourceIPGeo IPGeo `json:"srcipGeo"`
|
||||
USGIPGeo IPGeo `json:"usgipGeo"`
|
||||
}
|
||||
|
||||
// IPGeo is part of the UniFi Event data. Each event may have up to three of these.
|
||||
// One for source, one for dest and one for the USG location.
|
||||
type IPGeo struct {
|
||||
Asn int64 `json:"asn"`
|
||||
Latitude float64 `json:"latitude"`
|
||||
Longitude float64 `json:"longitude"`
|
||||
City string `json:"city"`
|
||||
ContinentCode string `json:"continent_code"`
|
||||
CountryCode string `json:"country_code"`
|
||||
CountryName string `json:"country_name"`
|
||||
Organization string `json:"organization"`
|
||||
}
|
||||
|
||||
// Events satisfied the sort.Interface.
|
||||
type events []*Event
|
||||
|
||||
// Len satisfies sort.Interface.
|
||||
func (e events) Len() int {
|
||||
return len(e)
|
||||
}
|
||||
|
||||
// Swap satisfies sort.Interface.
|
||||
func (e events) Swap(i, j int) {
|
||||
e[i], e[j] = e[j], e[i]
|
||||
}
|
||||
|
||||
// Less satisfies sort.Interface. Sort our list by date/time.
|
||||
func (e events) Less(i, j int) bool {
|
||||
return e[i].Datetime.Before(e[j].Datetime)
|
||||
}
|
||||
|
||||
// UnmarshalJSON is required because sometimes the unifi api returns
|
||||
// an empty array instead of a struct filled with data.
|
||||
func (v *IPGeo) UnmarshalJSON(data []byte) error {
|
||||
if string(data) == "[]" {
|
||||
return nil // it's empty
|
||||
}
|
||||
|
||||
g := struct {
|
||||
Asn int64 `json:"asn"`
|
||||
Latitude float64 `json:"latitude"`
|
||||
Longitude float64 `json:"longitude"`
|
||||
City string `json:"city"`
|
||||
ContinentCode string `json:"continent_code"`
|
||||
CountryCode string `json:"country_code"`
|
||||
CountryName string `json:"country_name"`
|
||||
Organization string `json:"organization"`
|
||||
}{}
|
||||
|
||||
err := json.Unmarshal(data, &g)
|
||||
v.Asn = g.Asn
|
||||
v.Latitude = g.Latitude
|
||||
v.Longitude = g.Longitude
|
||||
v.City = g.City
|
||||
v.ContinentCode = g.ContinentCode
|
||||
v.CountryCode = g.CountryCode
|
||||
v.CountryName = g.CountryName
|
||||
v.Organization = g.Organization
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("json unmarshal: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package unifi_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/unpoller/unifi"
|
||||
)
|
||||
|
||||
func TestIPGeoUnmarshalJSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
a := assert.New(t)
|
||||
i := &unifi.IPGeo{}
|
||||
|
||||
a.Nil(i.UnmarshalJSON([]byte(`[]`)))
|
||||
a.EqualValues(0, i.Asn)
|
||||
a.Nil(i.UnmarshalJSON([]byte(`{"asn": 123}`)))
|
||||
a.EqualValues(123, i.Asn)
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#!/bin/bash
|
||||
# Usage:
|
||||
# ./convert.sh <file> [prefix]
|
||||
# <file> should contain a go struct, like uap_type.go
|
||||
# It converts the go struct to an influx thing, like you see in uap_influx.go.
|
||||
# [prefix] is optional. I used it to do all the stat_ uap metrics.
|
||||
# Very crude, just helps skip a lot of copy/paste.
|
||||
#
|
||||
path=$1
|
||||
pre=$2
|
||||
|
||||
# Reads in the file one line at a time.
|
||||
while IFS='' read -r line; do
|
||||
# Split each piece of the file out.
|
||||
name=$(echo "${line}" | awk '{print $1}')
|
||||
type=$(echo "${line}" | awk '{print $2}')
|
||||
json=$(echo "${line}" | awk '{print $3}')
|
||||
json=$(echo "${json}" | cut -d\" -f2)
|
||||
|
||||
# Don't print junk lines. (it still prints some junk lines)
|
||||
if [ "$json" != "" ]; then
|
||||
# Add a .Val suffix if this is a FlexInt or FlexBool.
|
||||
[[ "$type" = Flex* ]] && suf=.Val
|
||||
echo "\"${pre}${json}\": u.Stat.${name}${suf},"
|
||||
fi
|
||||
done < ${path}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
{
|
||||
"_id": "5ee9e572453d4e00f3c04a7c",
|
||||
"user": "d8:4c:90:9f:82:5a",
|
||||
"ssid": "Extra Fast",
|
||||
"ap": "b4:fb:e4:d2:74:39",
|
||||
"radio": "na",
|
||||
"channel": "36",
|
||||
"key": "EVT_WU_Connected",
|
||||
"subsystem": "wlan",
|
||||
"site_id": "574e86994566ffb914a2683c",
|
||||
"time": 1592386923851,
|
||||
"datetime": "2020-06-17T09:42:03Z",
|
||||
"msg": "User[d8:4c:90:9f:82:5a] has connected to AP[b4:fb:e4:d2:74:39] with SSID \"Extra Fast\" on \"channel 36(na)\""
|
||||
},
|
||||
{
|
||||
"_id": "5ee9e56b453d4e00f3c04a7a",
|
||||
"user": "d8:4c:90:9f:82:5a",
|
||||
"ssid": "Extra Fast",
|
||||
"hostname": "dns-ipp",
|
||||
"ap": "74:83:c2:d4:11:3d",
|
||||
"duration": 1084,
|
||||
"bytes": 846171,
|
||||
"key": "EVT_WU_Disconnected",
|
||||
"subsystem": "wlan",
|
||||
"site_id": "574e86994566ffb914a2683c",
|
||||
"time": 1592386923000,
|
||||
"datetime": "2020-06-17T09:42:03Z",
|
||||
"msg": "User[d8:4c:90:9f:82:5a] disconnected from \"Extra Fast\" (18m 4s connected, 826.34K bytes, last AP[74:83:c2:d4:11:3d])"
|
||||
},
|
||||
{
|
||||
"_id": "5ee9f7ca453d4e00f3c04b57",
|
||||
"timestamp": 1592391625,
|
||||
"flow_id": 1510453960799559,
|
||||
"in_iface": "eth0",
|
||||
"event_type": "alert",
|
||||
"src_ip": "192.168.1.199",
|
||||
"src_mac": "00:50:b6:96:76:6e",
|
||||
"src_port": 50447,
|
||||
"dest_ip": "54.36.xxx.xxx",
|
||||
"dst_mac": "74:83:c2:1a:35:39",
|
||||
"dest_port": 80,
|
||||
"proto": "TCP",
|
||||
"tx_id": 0,
|
||||
"app_proto": "http",
|
||||
"host": "usg-sensor",
|
||||
"usgip": "67.181.75.120",
|
||||
"unique_alertid": "1603112333-2020-06-17T04:00:25.225809-0700",
|
||||
"srcipGeo": [],
|
||||
"dstipGeo": {
|
||||
"continent_code": "EU",
|
||||
"country_code": "FR",
|
||||
"country_name": "France",
|
||||
"latitude": 48.8582,
|
||||
"longitude": 2.3387,
|
||||
"asn": 16276,
|
||||
"organization": "OVH SAS"
|
||||
},
|
||||
"dstipCountry": "FR",
|
||||
"dstipASN": "16276 OVH SAS",
|
||||
"usgipGeo": {
|
||||
"continent_code": "NA",
|
||||
"country_code": "US",
|
||||
"country_name": "United States",
|
||||
"city": "Lodi",
|
||||
"latitude": 38.1228,
|
||||
"longitude": -121.2543,
|
||||
"asn": 7922,
|
||||
"organization": "COMCAST-7922"
|
||||
},
|
||||
"usgipCountry": "US",
|
||||
"usgipASN": "7922 COMCAST-7922",
|
||||
"catname": "emerging-malware",
|
||||
"inner_alert_action": "allowed",
|
||||
"inner_alert_gid": 1,
|
||||
"inner_alert_signature_id": 2003337,
|
||||
"inner_alert_rev": 21,
|
||||
"inner_alert_signature": "ET MALWARE Suspicious User Agent (Autoupdate)",
|
||||
"inner_alert_category": "A Network Trojan was Detected",
|
||||
"inner_alert_severity": 1,
|
||||
"key": "EVT_IPS_IpsAlert",
|
||||
"subsystem": "www",
|
||||
"site_id": "574e86994566ffb914a2683c",
|
||||
"time": 1592391625000,
|
||||
"datetime": "2020-06-17T11:00:25Z",
|
||||
"msg": "IPS Alert 1: A Network Trojan was Detected. Signature ET MALWARE Suspicious User Agent (Autoupdate). From: 192.168.1.199:50447, to: 54.36.xxx.xxx:80, protocol: TCP"
|
||||
},
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
"_id": "5d2416c78f0385ccf1c6df44",
|
||||
"archived": false,
|
||||
"timestamp": 1562646211,
|
||||
"flow_id": 1591464006222389,
|
||||
"in_iface": "eth1",
|
||||
"event_type": "alert",
|
||||
"src_ip": "196.196.244.84",
|
||||
"src_mac": "f0:9f:c2:c4:bb:f1",
|
||||
"src_port": 51413,
|
||||
"dest_ip": "192.168.3.2",
|
||||
"dst_mac": "40:a8:f0:68:c3:58",
|
||||
"dest_port": 36881,
|
||||
"proto": "UDP",
|
||||
"app_proto": "failed",
|
||||
"host": "f0:22:22:22:22:22",
|
||||
"usgip": "11.22.33.44",
|
||||
"unique_alertid": "1341902566-2019-07-08T21:23:31.229941-0700",
|
||||
"srcipCountry": "SE",
|
||||
"dstipCountry": false,
|
||||
"usgipCountry": "US",
|
||||
"srcipGeo": {
|
||||
"continent_code": "EU",
|
||||
"country_code": "SE",
|
||||
"country_code3": "SWE",
|
||||
"country_name": "Sweden",
|
||||
"region": "26",
|
||||
"city": "Stockholm",
|
||||
"postal_code": "168 65",
|
||||
"latitude": 59.349998474121094,
|
||||
"longitude": 17.91670036315918,
|
||||
"dma_code": 0,
|
||||
"area_code": 0
|
||||
},
|
||||
"dstipGeo": false,
|
||||
"usgipGeo": {
|
||||
"continent_code": "NA",
|
||||
"country_code": "US",
|
||||
"country_code3": "USA",
|
||||
"country_name": "United States",
|
||||
"region": "CA",
|
||||
"city": "Other",
|
||||
"postal_code": "99999",
|
||||
"latitude": 99.139400482177734,
|
||||
"longitude": -99.39669799804688,
|
||||
"dma_code": 862,
|
||||
"area_code": 999
|
||||
},
|
||||
"srcipASN": "AS42607 Internet Carrier Limited",
|
||||
"dstipASN": "",
|
||||
"usgipASN": "AS7922 Comcast Cable Communications, LLC",
|
||||
"catname": "spamhaus",
|
||||
"inner_alert_action": "allowed",
|
||||
"inner_alert_gid": 1,
|
||||
"inner_alert_signature_id": 2400022,
|
||||
"inner_alert_rev": 2673,
|
||||
"inner_alert_signature": "ET DROP Spamhaus DROP Listed Traffic Inbound group 23",
|
||||
"inner_alert_category": "Misc Attack",
|
||||
"inner_alert_severity": 2,
|
||||
"key": "EVT_IPS_IpsAlert",
|
||||
"subsystem": "www",
|
||||
"site_id": "574e86994566ffb914a2683c",
|
||||
"time": 1562646211000,
|
||||
"datetime": "2019-07-09T04:23:31Z",
|
||||
"msg": "IPS Alert 2: Misc Attack. Signature ET DROP Spamhaus DROP Listed Traffic Inbound group 23. From: 196.196.244.84:51413, to: 192.168.3.2:36881, protocol: UDP"
|
||||
},
|
||||
|
|
@ -0,0 +1,738 @@
|
|||
{
|
||||
"_id": "574e8bde4566ffb914a26853",
|
||||
"adopted": true,
|
||||
"antenna_table": [
|
||||
{
|
||||
"default": true,
|
||||
"id": 4,
|
||||
"name": "Combined",
|
||||
"wifi0_gain": 3,
|
||||
"wifi1_gain": 3
|
||||
}
|
||||
],
|
||||
"bandsteering_mode": "prefer_5g",
|
||||
"board_rev": 18,
|
||||
"cfgversion": "08cd8d6b71ebe82f",
|
||||
"config_network": {
|
||||
"type": "dhcp",
|
||||
"ip": "10.1.10.67"
|
||||
},
|
||||
"countrycode_table": [
|
||||
840,
|
||||
124,
|
||||
630
|
||||
],
|
||||
"ethernet_table": [
|
||||
{
|
||||
"mac": "80:22:a8:22:ae:22",
|
||||
"num_port": 2,
|
||||
"name": "eth0"
|
||||
}
|
||||
],
|
||||
"fw_caps": 4128063,
|
||||
"has_eth1": false,
|
||||
"has_speaker": false,
|
||||
"inform_ip": "192.168.3.1",
|
||||
"inform_url": "http://unifi:8080/inform",
|
||||
"ip": "192.168.1.8",
|
||||
"led_override": "default",
|
||||
"mac": "80:22:22:22:22:22",
|
||||
"mesh_sta_vap_enabled": true,
|
||||
"model": "U7PG2",
|
||||
"name": "wap-lower",
|
||||
"outdoor_mode_override": "default",
|
||||
"port_table": [
|
||||
{
|
||||
"port_idx": 1,
|
||||
"op_mode": "switch",
|
||||
"portconf_id": "574e869d4566ffb914a26845",
|
||||
"attr_no_edit": true,
|
||||
"media": "GE",
|
||||
"name": "Main",
|
||||
"poe_caps": 0,
|
||||
"port_poe": false,
|
||||
"tx_bytes-r": 0,
|
||||
"rx_bytes-r": 0,
|
||||
"bytes-r": 0,
|
||||
"port_delta": {
|
||||
"time_delta": 21
|
||||
},
|
||||
"enable": true,
|
||||
"masked": false,
|
||||
"aggregated_by": false
|
||||
},
|
||||
{
|
||||
"port_idx": 2,
|
||||
"op_mode": "switch",
|
||||
"portconf_id": "574e869d4566ffb914a26845",
|
||||
"media": "GE",
|
||||
"name": "Secondary",
|
||||
"poe_caps": 0,
|
||||
"port_poe": false,
|
||||
"tx_bytes-r": 0,
|
||||
"rx_bytes-r": 0,
|
||||
"bytes-r": 0,
|
||||
"port_delta": {
|
||||
"time_delta": 21
|
||||
},
|
||||
"enable": true,
|
||||
"masked": false,
|
||||
"aggregated_by": false
|
||||
}
|
||||
],
|
||||
"radio_table": [
|
||||
{
|
||||
"radio": "ng",
|
||||
"name": "wifi0",
|
||||
"channel": "auto",
|
||||
"ht": "40",
|
||||
"tx_power_mode": "auto",
|
||||
"tx_power": "0",
|
||||
"max_txpower": 22,
|
||||
"min_txpower": 6,
|
||||
"nss": 3,
|
||||
"min_rssi_enabled": false,
|
||||
"builtin_antenna": true,
|
||||
"builtin_ant_gain": 3,
|
||||
"current_antenna_gain": 0,
|
||||
"radio_caps": 16420,
|
||||
"wlangroup_id": "574e869d4566ffb914a26843"
|
||||
},
|
||||
{
|
||||
"radio": "na",
|
||||
"name": "wifi1",
|
||||
"channel": "auto",
|
||||
"ht": "80",
|
||||
"tx_power_mode": "high",
|
||||
"tx_power": "0",
|
||||
"max_txpower": 22,
|
||||
"min_txpower": 6,
|
||||
"nss": 3,
|
||||
"min_rssi_enabled": false,
|
||||
"is_11ac": true,
|
||||
"has_dfs": true,
|
||||
"has_fccdfs": true,
|
||||
"builtin_antenna": true,
|
||||
"builtin_ant_gain": 3,
|
||||
"current_antenna_gain": 0,
|
||||
"radio_caps": 50479140,
|
||||
"wlangroup_id": "574e869d4566ffb914a26843"
|
||||
}
|
||||
],
|
||||
"scan_radio_table": [],
|
||||
"serial": "xxxyyyzzz",
|
||||
"site_id": "574e86994566ffb914a2683c",
|
||||
"type": "uap",
|
||||
"version": "4.0.42.10433",
|
||||
"vwire_table": [],
|
||||
"wifi_caps": 49141,
|
||||
"wlangroup_id_na": "574e869d4566ffb914a26843",
|
||||
"wlangroup_id_ng": "574e869d4566ffb914a26843",
|
||||
"required_version": "3.4.1",
|
||||
"hw_caps": 0,
|
||||
"unsupported": false,
|
||||
"unsupported_reason": 0,
|
||||
"sys_error_caps": 0,
|
||||
"has_fan": false,
|
||||
"has_temperature": false,
|
||||
"device_id": "574e8bde4566ffb914a26853",
|
||||
"state": 1,
|
||||
"last_seen": 1562225381,
|
||||
"upgradable": false,
|
||||
"adoptable_when_upgraded": false,
|
||||
"rollupgrade": false,
|
||||
"known_cfgversion": "08cd8d6b71ebe82f",
|
||||
"uptime": 3105845,
|
||||
"_uptime": 3105845,
|
||||
"locating": false,
|
||||
"connect_request_ip": "192.168.1.8",
|
||||
"connect_request_port": "43913",
|
||||
"sys_stats": {
|
||||
"loadavg_1": "0.04",
|
||||
"loadavg_15": "0.14",
|
||||
"loadavg_5": "0.10",
|
||||
"mem_buffer": 0,
|
||||
"mem_total": 128622592,
|
||||
"mem_used": 67178496
|
||||
},
|
||||
"system-stats": {
|
||||
"cpu": "24.7",
|
||||
"mem": "52.2",
|
||||
"uptime": "3105845"
|
||||
},
|
||||
"ssh_session_table": [],
|
||||
"scanning": false,
|
||||
"spectrum_scanning": false,
|
||||
"guest_token": "4D630D2A1AF84771FCBB8EEB4C47E030",
|
||||
"meshv3_peer_mac": "",
|
||||
"satisfaction": 95,
|
||||
"isolated": false,
|
||||
"radio_table_stats": [
|
||||
{
|
||||
"name": "wifi0",
|
||||
"channel": 6,
|
||||
"radio": "ng",
|
||||
"ast_txto": null,
|
||||
"ast_cst": null,
|
||||
"ast_be_xmit": 398,
|
||||
"cu_total": 20,
|
||||
"cu_self_rx": 14,
|
||||
"cu_self_tx": 4,
|
||||
"gain": 3,
|
||||
"state": "RUN",
|
||||
"extchannel": 1,
|
||||
"tx_power": 22,
|
||||
"tx_packets": 183,
|
||||
"tx_retries": 108,
|
||||
"num_sta": 2,
|
||||
"guest-num_sta": 0,
|
||||
"user-num_sta": 2
|
||||
},
|
||||
{
|
||||
"name": "wifi1",
|
||||
"channel": 36,
|
||||
"radio": "na",
|
||||
"ast_txto": null,
|
||||
"ast_cst": null,
|
||||
"ast_be_xmit": 398,
|
||||
"cu_total": 12,
|
||||
"cu_self_rx": 10,
|
||||
"cu_self_tx": 2,
|
||||
"gain": 3,
|
||||
"state": "RUN",
|
||||
"extchannel": 1,
|
||||
"tx_power": 22,
|
||||
"tx_packets": 22466,
|
||||
"tx_retries": 858,
|
||||
"num_sta": 4,
|
||||
"guest-num_sta": 0,
|
||||
"user-num_sta": 4
|
||||
}
|
||||
],
|
||||
"uplink": {
|
||||
"full_duplex": true,
|
||||
"ip": "0.0.0.0",
|
||||
"mac": "80:22:22:22:22:22",
|
||||
"max_vlan": 96,
|
||||
"name": "eth0",
|
||||
"netmask": "0.0.0.0",
|
||||
"num_port": 2,
|
||||
"rx_bytes": 3752803309,
|
||||
"rx_dropped": 102338,
|
||||
"rx_errors": 0,
|
||||
"rx_multicast": 0,
|
||||
"rx_packets": 245302304,
|
||||
"speed": 1000,
|
||||
"tx_bytes": 1604707458,
|
||||
"tx_dropped": 341,
|
||||
"tx_errors": 0,
|
||||
"tx_packets": 194278357,
|
||||
"up": true,
|
||||
"max_speed": 1000,
|
||||
"type": "wire",
|
||||
"tx_bytes-r": 9693222,
|
||||
"rx_bytes-r": 92418,
|
||||
"uplink_mac": "f0:22:22:22:22:22",
|
||||
"uplink_remote_port": 15
|
||||
},
|
||||
"vap_table": [
|
||||
{
|
||||
"avg_client_signal": 0,
|
||||
"bssid": "82:22:22:22:22:22",
|
||||
"ccq": 0,
|
||||
"channel": 36,
|
||||
"essid": "Extra Free",
|
||||
"extchannel": 1,
|
||||
"id": "574e96834566ffb914a26875",
|
||||
"mac_filter_rejections": 0,
|
||||
"name": "ath3",
|
||||
"num_satisfaction_sta": 0,
|
||||
"num_sta": 0,
|
||||
"radio": "na",
|
||||
"radio_name": "wifi1",
|
||||
"rx_bytes": 61253,
|
||||
"rx_crypts": 0,
|
||||
"rx_dropped": 0,
|
||||
"rx_errors": 0,
|
||||
"rx_frags": 0,
|
||||
"rx_nwids": 47658,
|
||||
"rx_packets": 576,
|
||||
"rx_tcp_stats": {
|
||||
"goodbytes": 0,
|
||||
"lat_avg": 0,
|
||||
"lat_max": 0,
|
||||
"lat_min": 4294967295,
|
||||
"stalls": 0
|
||||
},
|
||||
"satisfaction": -1,
|
||||
"satisfaction_now": -1,
|
||||
"state": "RUN",
|
||||
"tx_bytes": 922841,
|
||||
"tx_combined_retries": 0,
|
||||
"tx_data_mpdu_bytes": 0,
|
||||
"tx_dropped": 18128,
|
||||
"tx_errors": 7,
|
||||
"tx_packets": 736,
|
||||
"tx_power": 22,
|
||||
"tx_retries": 0,
|
||||
"tx_rts_retries": 0,
|
||||
"tx_success": 0,
|
||||
"tx_tcp_stats": {
|
||||
"goodbytes": 0,
|
||||
"lat_avg": 0,
|
||||
"lat_max": 0,
|
||||
"lat_min": 4294967295,
|
||||
"stalls": 0
|
||||
},
|
||||
"tx_total": 0,
|
||||
"up": true,
|
||||
"usage": "guest",
|
||||
"wifi_tx_attempts": 0,
|
||||
"wifi_tx_dropped": 0,
|
||||
"wifi_tx_latency_mov": {
|
||||
"avg": 0,
|
||||
"max": 0,
|
||||
"min": 4294967295,
|
||||
"total": 0,
|
||||
"total_count": 0
|
||||
},
|
||||
"t": "vap",
|
||||
"wlanconf_id": "574e96834566ffb914a26875",
|
||||
"is_guest": true,
|
||||
"is_wep": false,
|
||||
"ap_mac": "80:22:a8:22:22:22",
|
||||
"map_id": null,
|
||||
"site_id": "574e86994566ffb914a2683c"
|
||||
},
|
||||
{
|
||||
"avg_client_signal": -65,
|
||||
"bssid": "80:22:a8:22:22:22",
|
||||
"ccq": 333,
|
||||
"channel": 36,
|
||||
"essid": "Extra Fast",
|
||||
"extchannel": 1,
|
||||
"id": "574e96614566ffb914a26874",
|
||||
"mac_filter_rejections": 0,
|
||||
"name": "ath2",
|
||||
"num_satisfaction_sta": 4,
|
||||
"num_sta": 4,
|
||||
"radio": "na",
|
||||
"radio_name": "wifi1",
|
||||
"rx_bytes": 3358763522,
|
||||
"rx_crypts": 161639,
|
||||
"rx_dropped": 161639,
|
||||
"rx_errors": 161639,
|
||||
"rx_frags": 0,
|
||||
"rx_nwids": 37605,
|
||||
"rx_packets": 99128603,
|
||||
"rx_tcp_stats": {
|
||||
"goodbytes": 16243150352,
|
||||
"lat_avg": 7,
|
||||
"lat_max": 100,
|
||||
"lat_min": 10,
|
||||
"stalls": 0
|
||||
},
|
||||
"satisfaction": 96,
|
||||
"satisfaction_now": 97,
|
||||
"state": "RUN",
|
||||
"tx_bytes": 834859686,
|
||||
"tx_combined_retries": 9067488,
|
||||
"tx_data_mpdu_bytes": 48117828355,
|
||||
"tx_dropped": 109,
|
||||
"tx_errors": 4076905,
|
||||
"tx_packets": 111980588,
|
||||
"tx_power": 22,
|
||||
"tx_retries": 9067488,
|
||||
"tx_rts_retries": 0,
|
||||
"tx_success": 62855481,
|
||||
"tx_tcp_stats": {
|
||||
"goodbytes": 2154118473,
|
||||
"lat_avg": 18,
|
||||
"lat_max": 120,
|
||||
"lat_min": 10,
|
||||
"stalls": 0
|
||||
},
|
||||
"tx_total": 62855552,
|
||||
"up": true,
|
||||
"usage": "user",
|
||||
"wifi_tx_attempts": 71923040,
|
||||
"wifi_tx_dropped": 71,
|
||||
"wifi_tx_latency_mov": {
|
||||
"avg": 565,
|
||||
"max": 31757,
|
||||
"min": 98,
|
||||
"total": 10183098,
|
||||
"total_count": 18019
|
||||
},
|
||||
"t": "vap",
|
||||
"wlanconf_id": "574e96614566ffb914a26874",
|
||||
"is_guest": false,
|
||||
"is_wep": false,
|
||||
"ap_mac": "80:22:22:22:22:22",
|
||||
"map_id": null,
|
||||
"site_id": "574e86994566ffb914a2683c"
|
||||
},
|
||||
{
|
||||
"avg_client_signal": 0,
|
||||
"bssid": "82:22:a8:22:22:22",
|
||||
"ccq": 0,
|
||||
"channel": 6,
|
||||
"essid": "Extra Free",
|
||||
"extchannel": 1,
|
||||
"id": "574e96834566ffb914a26875",
|
||||
"mac_filter_rejections": 0,
|
||||
"name": "ath1",
|
||||
"num_satisfaction_sta": 0,
|
||||
"num_sta": 0,
|
||||
"radio": "ng",
|
||||
"radio_name": "wifi0",
|
||||
"reasons_bar_chart": {
|
||||
"phy_rate": 0,
|
||||
"signal": 0,
|
||||
"sleepy_client": 0,
|
||||
"sta_arp_timeout": 0,
|
||||
"sta_dns_timeout": 0,
|
||||
"sta_ip_timeout": 0,
|
||||
"stream_eff": 0,
|
||||
"tcp_latency": 0,
|
||||
"tcp_packet_loss": 0,
|
||||
"wifi_latency": 0,
|
||||
"wifi_retries": 0
|
||||
},
|
||||
"reasons_bar_chart_now": {
|
||||
"phy_rate": 0,
|
||||
"signal": 0,
|
||||
"sleepy_client": 0,
|
||||
"sta_arp_timeout": 0,
|
||||
"sta_dns_timeout": 0,
|
||||
"sta_ip_timeout": 0,
|
||||
"stream_eff": 0,
|
||||
"tcp_latency": 0,
|
||||
"tcp_packet_loss": 0,
|
||||
"wifi_latency": 0,
|
||||
"wifi_retries": 0
|
||||
},
|
||||
"rx_bytes": 0,
|
||||
"rx_crypts": 0,
|
||||
"rx_dropped": 0,
|
||||
"rx_errors": 0,
|
||||
"rx_frags": 0,
|
||||
"rx_nwids": 120385,
|
||||
"rx_packets": 0,
|
||||
"rx_tcp_stats": {
|
||||
"goodbytes": 0,
|
||||
"lat_avg": 0,
|
||||
"lat_max": 0,
|
||||
"lat_min": 4294967295,
|
||||
"stalls": 0
|
||||
},
|
||||
"satisfaction": -1,
|
||||
"satisfaction_now": -1,
|
||||
"state": "RUN",
|
||||
"tx_bytes": 0,
|
||||
"tx_combined_retries": 0,
|
||||
"tx_data_mpdu_bytes": 0,
|
||||
"tx_dropped": 20262,
|
||||
"tx_errors": 0,
|
||||
"tx_packets": 0,
|
||||
"tx_power": 22,
|
||||
"tx_retries": 0,
|
||||
"tx_rts_retries": 0,
|
||||
"tx_success": 0,
|
||||
"tx_tcp_stats": {
|
||||
"goodbytes": 0,
|
||||
"lat_avg": 0,
|
||||
"lat_max": 0,
|
||||
"lat_min": 4294967295,
|
||||
"stalls": 0
|
||||
},
|
||||
"tx_total": 0,
|
||||
"up": true,
|
||||
"usage": "guest",
|
||||
"wifi_tx_attempts": 0,
|
||||
"wifi_tx_dropped": 0,
|
||||
"wifi_tx_latency_mov": {
|
||||
"avg": 0,
|
||||
"max": 0,
|
||||
"min": 4294967295,
|
||||
"total": 0,
|
||||
"total_count": 0
|
||||
},
|
||||
"t": "vap",
|
||||
"wlanconf_id": "574e96834566ffb914a26875",
|
||||
"is_guest": true,
|
||||
"is_wep": false,
|
||||
"ap_mac": "80:22:a8:22:22:22",
|
||||
"map_id": null,
|
||||
"site_id": "574e86994566ffb914a2683c"
|
||||
},
|
||||
{
|
||||
"avg_client_signal": -68,
|
||||
"bssid": "80:22:22:22:22:22",
|
||||
"ccq": 966,
|
||||
"channel": 6,
|
||||
"essid": "Extra Fast",
|
||||
"extchannel": 1,
|
||||
"id": "574e96614566ffb914a26874",
|
||||
"mac_filter_rejections": 0,
|
||||
"name": "ath0",
|
||||
"num_satisfaction_sta": 2,
|
||||
"num_sta": 2,
|
||||
"radio": "ng",
|
||||
"radio_name": "wifi0",
|
||||
"reasons_bar_chart": {
|
||||
"phy_rate": 0,
|
||||
"signal": 0,
|
||||
"sleepy_client": 0,
|
||||
"sta_arp_timeout": 0,
|
||||
"sta_dns_timeout": 0,
|
||||
"sta_ip_timeout": 0,
|
||||
"stream_eff": 0,
|
||||
"tcp_latency": 0,
|
||||
"tcp_packet_loss": 0,
|
||||
"wifi_latency": 0,
|
||||
"wifi_retries": 1
|
||||
},
|
||||
"reasons_bar_chart_now": {
|
||||
"phy_rate": 0,
|
||||
"signal": 0,
|
||||
"sleepy_client": 0,
|
||||
"sta_arp_timeout": 0,
|
||||
"sta_dns_timeout": 0,
|
||||
"sta_ip_timeout": 0,
|
||||
"stream_eff": 0,
|
||||
"tcp_latency": 0,
|
||||
"tcp_packet_loss": 0,
|
||||
"wifi_latency": 0,
|
||||
"wifi_retries": 1
|
||||
},
|
||||
"rx_bytes": 1017366419,
|
||||
"rx_crypts": 11013,
|
||||
"rx_dropped": 12764,
|
||||
"rx_errors": 12764,
|
||||
"rx_frags": 0,
|
||||
"rx_nwids": 177145,
|
||||
"rx_packets": 22542668,
|
||||
"rx_tcp_stats": {
|
||||
"goodbytes": 114220296,
|
||||
"lat_avg": 5,
|
||||
"lat_max": 10,
|
||||
"lat_min": 10,
|
||||
"stalls": 0
|
||||
},
|
||||
"satisfaction": 93,
|
||||
"satisfaction_now": 94,
|
||||
"state": "RUN",
|
||||
"tx_bytes": 965488630,
|
||||
"tx_combined_retries": 8202639,
|
||||
"tx_data_mpdu_bytes": 1145631754,
|
||||
"tx_dropped": 43,
|
||||
"tx_errors": 0,
|
||||
"tx_packets": 22623798,
|
||||
"tx_power": 22,
|
||||
"tx_retries": 7194267,
|
||||
"tx_rts_retries": 1008372,
|
||||
"tx_success": 9545999,
|
||||
"tx_tcp_stats": {
|
||||
"goodbytes": 182912739,
|
||||
"lat_avg": 5,
|
||||
"lat_max": 10,
|
||||
"lat_min": 10,
|
||||
"stalls": 0
|
||||
},
|
||||
"tx_total": 9547096,
|
||||
"up": true,
|
||||
"usage": "user",
|
||||
"wifi_tx_attempts": 16740276,
|
||||
"wifi_tx_dropped": 1095,
|
||||
"wifi_tx_latency_mov": {
|
||||
"avg": 673,
|
||||
"max": 13612,
|
||||
"min": 0,
|
||||
"total": 263176,
|
||||
"total_count": 391
|
||||
},
|
||||
"t": "vap",
|
||||
"wlanconf_id": "574e96614566ffb914a26874",
|
||||
"is_guest": false,
|
||||
"is_wep": false,
|
||||
"ap_mac": "80:22:22:22:22:22",
|
||||
"map_id": null,
|
||||
"site_id": "574e86994566ffb914a2683c"
|
||||
}
|
||||
],
|
||||
"downlink_table": [],
|
||||
"vwire_vap_table": [],
|
||||
"bytes-d": 204913232,
|
||||
"tx_bytes-d": 1921569,
|
||||
"rx_bytes-d": 202991663,
|
||||
"bytes-r": 9757772,
|
||||
"last_uplink": {
|
||||
"uplink_mac": "f0:22:22:22:22:22",
|
||||
"uplink_remote_port": 15
|
||||
},
|
||||
"stat": {
|
||||
"site_id": "574e86994566ffb914a2683c",
|
||||
"o": "ap",
|
||||
"oid": "80:22:a8:22:22:22",
|
||||
"ap": "80:22:a8:22:22:22",
|
||||
"time": 1562207100000,
|
||||
"datetime": "2019-07-04T02:25:00Z",
|
||||
"guest-wifi0-rx_packets": 0,
|
||||
"guest-wifi1-rx_packets": 0,
|
||||
"user-wifi1-rx_packets": 31373230,
|
||||
"user-wifi0-rx_packets": 169790,
|
||||
"user-rx_packets": 31543020,
|
||||
"guest-rx_packets": 0,
|
||||
"wifi0-rx_packets": 169790,
|
||||
"wifi1-rx_packets": 31373230,
|
||||
"rx_packets": 31543020,
|
||||
"guest-wifi0-rx_bytes": 0,
|
||||
"guest-wifi1-rx_bytes": 0,
|
||||
"user-wifi1-rx_bytes": 42049645434,
|
||||
"user-wifi0-rx_bytes": 16755639,
|
||||
"user-rx_bytes": 42066401073,
|
||||
"guest-rx_bytes": 0,
|
||||
"wifi0-rx_bytes": 16755639,
|
||||
"wifi1-rx_bytes": 42049645434,
|
||||
"rx_bytes": 42066401073,
|
||||
"guest-wifi0-rx_errors": 0,
|
||||
"guest-wifi1-rx_errors": 0,
|
||||
"user-wifi1-rx_errors": 150651,
|
||||
"user-wifi0-rx_errors": 0,
|
||||
"user-rx_errors": 150651,
|
||||
"guest-rx_errors": 0,
|
||||
"wifi0-rx_errors": 0,
|
||||
"wifi1-rx_errors": 150651,
|
||||
"rx_errors": 150651,
|
||||
"guest-wifi0-rx_dropped": 0,
|
||||
"guest-wifi1-rx_dropped": 0,
|
||||
"user-wifi1-rx_dropped": 150651,
|
||||
"user-wifi0-rx_dropped": 0,
|
||||
"user-rx_dropped": 150651,
|
||||
"guest-rx_dropped": 0,
|
||||
"wifi0-rx_dropped": 0,
|
||||
"wifi1-rx_dropped": 150651,
|
||||
"rx_dropped": 150651,
|
||||
"guest-wifi0-rx_crypts": 0,
|
||||
"guest-wifi1-rx_crypts": 0,
|
||||
"user-wifi1-rx_crypts": 150651,
|
||||
"user-wifi0-rx_crypts": 0,
|
||||
"user-rx_crypts": 150651,
|
||||
"guest-rx_crypts": 0,
|
||||
"wifi0-rx_crypts": 0,
|
||||
"wifi1-rx_crypts": 150651,
|
||||
"rx_crypts": 150651,
|
||||
"guest-wifi0-rx_frags": 0,
|
||||
"guest-wifi1-rx_frags": 0,
|
||||
"user-wifi1-rx_frags": 0,
|
||||
"user-wifi0-rx_frags": 0,
|
||||
"user-rx_frags": 0,
|
||||
"guest-rx_frags": 0,
|
||||
"wifi0-rx_frags": 0,
|
||||
"wifi1-rx_frags": 0,
|
||||
"rx_frags": 0,
|
||||
"guest-wifi0-tx_packets": 0,
|
||||
"guest-wifi1-tx_packets": 0,
|
||||
"user-wifi1-tx_packets": 7125589,
|
||||
"user-wifi0-tx_packets": 210389,
|
||||
"user-tx_packets": 7335978,
|
||||
"guest-tx_packets": 0,
|
||||
"wifi0-tx_packets": 210389,
|
||||
"wifi1-tx_packets": 7125589,
|
||||
"tx_packets": 7335978,
|
||||
"guest-wifi0-tx_bytes": 0,
|
||||
"guest-wifi1-tx_bytes": 0,
|
||||
"user-wifi1-tx_bytes": 3011293823,
|
||||
"user-wifi0-tx_bytes": 25966558,
|
||||
"user-tx_bytes": 3037260381,
|
||||
"guest-tx_bytes": 0,
|
||||
"wifi0-tx_bytes": 25966558,
|
||||
"wifi1-tx_bytes": 3011293823,
|
||||
"tx_bytes": 3037260381,
|
||||
"guest-wifi0-tx_errors": 0,
|
||||
"guest-wifi1-tx_errors": 0,
|
||||
"user-wifi1-tx_errors": 102193,
|
||||
"user-wifi0-tx_errors": 0,
|
||||
"user-tx_errors": 102193,
|
||||
"guest-tx_errors": 0,
|
||||
"wifi0-tx_errors": 0,
|
||||
"wifi1-tx_errors": 102193,
|
||||
"tx_errors": 102193,
|
||||
"guest-wifi0-tx_dropped": 296,
|
||||
"guest-wifi1-tx_dropped": 296,
|
||||
"user-wifi1-tx_dropped": 0,
|
||||
"user-wifi0-tx_dropped": 0,
|
||||
"user-tx_dropped": 0,
|
||||
"guest-tx_dropped": 592,
|
||||
"wifi0-tx_dropped": 296,
|
||||
"wifi1-tx_dropped": 296,
|
||||
"tx_dropped": 592,
|
||||
"guest-wifi0-tx_retries": 0,
|
||||
"guest-wifi1-tx_retries": 0,
|
||||
"user-wifi1-tx_retries": 519734,
|
||||
"user-wifi0-tx_retries": 90225,
|
||||
"user-tx_retries": 609959,
|
||||
"guest-tx_retries": 0,
|
||||
"wifi0-tx_retries": 90225,
|
||||
"wifi1-tx_retries": 519734,
|
||||
"tx_retries": 609959,
|
||||
"guest-wifi0-mac_filter_rejections": 0,
|
||||
"guest-wifi1-mac_filter_rejections": 0,
|
||||
"user-wifi1-mac_filter_rejections": 0,
|
||||
"user-wifi0-mac_filter_rejections": 0,
|
||||
"user-mac_filter_rejections": 0,
|
||||
"guest-mac_filter_rejections": 0,
|
||||
"wifi0-mac_filter_rejections": 0,
|
||||
"wifi1-mac_filter_rejections": 0,
|
||||
"mac_filter_rejections": 0,
|
||||
"guest-wifi0-wifi_tx_attempts": 0,
|
||||
"guest-wifi1-wifi_tx_attempts": 0,
|
||||
"user-wifi1-wifi_tx_attempts": 4419026,
|
||||
"user-wifi0-wifi_tx_attempts": 255999,
|
||||
"user-wifi_tx_attempts": 4675025,
|
||||
"guest-wifi_tx_attempts": 0,
|
||||
"wifi0-wifi_tx_attempts": 255999,
|
||||
"wifi1-wifi_tx_attempts": 4419026,
|
||||
"wifi_tx_attempts": 4675025,
|
||||
"guest-wifi0-wifi_tx_dropped": 0,
|
||||
"guest-wifi1-wifi_tx_dropped": 0,
|
||||
"user-wifi1-wifi_tx_dropped": 25,
|
||||
"user-wifi0-wifi_tx_dropped": 2,
|
||||
"user-wifi_tx_dropped": 27,
|
||||
"guest-wifi_tx_dropped": 0,
|
||||
"wifi0-wifi_tx_dropped": 2,
|
||||
"wifi1-wifi_tx_dropped": 25,
|
||||
"wifi_tx_dropped": 27,
|
||||
"bytes": 45103661454,
|
||||
"duration": 17988000,
|
||||
"user-wifi1-ath2-574e96614566ffb914a26874-rx_packets": 31373230,
|
||||
"user-wifi1-ath2-574e96614566ffb914a26874-rx_bytes": 42049645434,
|
||||
"user-wifi1-ath2-574e96614566ffb914a26874-tx_packets": 7125589,
|
||||
"user-wifi1-ath2-574e96614566ffb914a26874-tx_bytes": 3011293823,
|
||||
"user-wifi1-ath2-574e96614566ffb914a26874-tx_errors": 102193,
|
||||
"user-wifi1-ath2-574e96614566ffb914a26874-tx_retries": 519734,
|
||||
"user-wifi1-ath2-574e96614566ffb914a26874-wifi_tx_attempts": 4419026,
|
||||
"user-wifi0-ath0-574e96614566ffb914a26874-rx_packets": 169790,
|
||||
"user-wifi0-ath0-574e96614566ffb914a26874-rx_bytes": 16755639,
|
||||
"user-wifi0-ath0-574e96614566ffb914a26874-tx_packets": 210389,
|
||||
"user-wifi0-ath0-574e96614566ffb914a26874-tx_bytes": 25966558,
|
||||
"user-wifi0-ath0-574e96614566ffb914a26874-tx_retries": 90225,
|
||||
"user-wifi0-ath0-574e96614566ffb914a26874-wifi_tx_attempts": 255999,
|
||||
"guest-wifi1-ath3-574e96834566ffb914a26875-tx_dropped": 296,
|
||||
"guest-wifi0-ath1-574e96834566ffb914a26875-tx_dropped": 296,
|
||||
"user-wifi1-ath2-574e96614566ffb914a26874-rx_errors": 150651,
|
||||
"user-wifi1-ath2-574e96614566ffb914a26874-rx_dropped": 150651,
|
||||
"user-wifi1-ath2-574e96614566ffb914a26874-rx_crypts": 150651,
|
||||
"user-wifi0-ath0-574e96614566ffb914a26874-wifi_tx_dropped": 2,
|
||||
"user-wifi1-ath2-574e96614566ffb914a26874-wifi_tx_dropped": 25
|
||||
},
|
||||
"tx_bytes": 3037260381,
|
||||
"rx_bytes": 42066401073,
|
||||
"bytes": 45103661454,
|
||||
"vwireEnabled": false,
|
||||
"uplink_table": [],
|
||||
"num_sta": 6,
|
||||
"user-num_sta": 6,
|
||||
"guest-num_sta": 0
|
||||
}
|
||||
|
|
@ -0,0 +1,371 @@
|
|||
{
|
||||
"_id": "59a35da745663e6cc82600f6",
|
||||
"adopted": true,
|
||||
"cfgversion": "bf9f0335063fe6ea",
|
||||
"config_network": {
|
||||
"type": "dhcp",
|
||||
"ip": "192.168.2.0"
|
||||
},
|
||||
"ethernet_table": [
|
||||
{
|
||||
"mac": "22:22:00:22:22:00",
|
||||
"num_port": 1,
|
||||
"name": "eth0"
|
||||
},
|
||||
{
|
||||
"mac": "22:22:00:22:22:00",
|
||||
"num_port": 1,
|
||||
"name": "eth1"
|
||||
},
|
||||
{
|
||||
"mac": "22:22:00:22:22:00",
|
||||
"num_port": 1,
|
||||
"name": "eth2"
|
||||
}
|
||||
],
|
||||
"fw_caps": 184323,
|
||||
"inform_ip": "192.168.3.1",
|
||||
"inform_url": "http://security:8080/inform",
|
||||
"ip": "3.1.33.7",
|
||||
"led_override": "default",
|
||||
"license_state": "registered",
|
||||
"mac": "22:22:00:22:22:00",
|
||||
"model": "UGW3",
|
||||
"name": "gateway",
|
||||
"outdoor_mode_override": "default",
|
||||
"serial": "xxxyyyzzz",
|
||||
"site_id": "574e86994566ffb914a2683c",
|
||||
"type": "ugw",
|
||||
"usg_caps": 786431,
|
||||
"version": "4.4.41.5193700",
|
||||
"required_version": "4.0.0",
|
||||
"ethernet_overrides": [
|
||||
{
|
||||
"ifname": "eth1",
|
||||
"networkgroup": "LAN"
|
||||
},
|
||||
{
|
||||
"ifname": "eth0",
|
||||
"networkgroup": "WAN"
|
||||
}
|
||||
],
|
||||
"hw_caps": 0,
|
||||
"board_rev": 16,
|
||||
"unsupported": false,
|
||||
"unsupported_reason": 0,
|
||||
"device_id": "59a35da745663e6cc82600f6",
|
||||
"state": 1,
|
||||
"last_seen": 1562311857,
|
||||
"upgradable": false,
|
||||
"adoptable_when_upgraded": false,
|
||||
"rollupgrade": false,
|
||||
"known_cfgversion": "bf9f0335063fe6ea",
|
||||
"uptime": 3191626,
|
||||
"_uptime": 3191626,
|
||||
"locating": false,
|
||||
"connect_request_ip": "192.168.2.1",
|
||||
"connect_request_port": "35615",
|
||||
"sys_stats": {
|
||||
"loadavg_1": "0.01",
|
||||
"loadavg_15": "0.12",
|
||||
"loadavg_5": "0.06",
|
||||
"mem_buffer": 62406656,
|
||||
"mem_total": 507412480,
|
||||
"mem_used": 397500416
|
||||
},
|
||||
"system-stats": {
|
||||
"cpu": "14",
|
||||
"mem": "30",
|
||||
"uptime": "3191066"
|
||||
},
|
||||
"guest_token": "83342830AE9C0641DC39DD2759C122A1",
|
||||
"speedtest-status": {
|
||||
"latency": 14,
|
||||
"rundate": 1562310531,
|
||||
"runtime": 172,
|
||||
"status_download": 2,
|
||||
"status_ping": 2,
|
||||
"status_summary": 2,
|
||||
"status_upload": 2,
|
||||
"xput_download": 157.36776733398438,
|
||||
"xput_upload": 37.90521240234375
|
||||
},
|
||||
"speedtest-status-saved": true,
|
||||
"wan1": {
|
||||
"tx_bytes-r": 2852355,
|
||||
"rx_bytes-r": 1224743,
|
||||
"bytes-r": 4077098,
|
||||
"max_speed": 1000,
|
||||
"type": "wire",
|
||||
"name": "wan",
|
||||
"ifname": "eth0",
|
||||
"ip": "3.1.33.7",
|
||||
"netmask": "255.255.254.0",
|
||||
"mac": "22:22:00:22:22:00",
|
||||
"up": true,
|
||||
"speed": 1000,
|
||||
"full_duplex": true,
|
||||
"rx_bytes": 2648236513108,
|
||||
"rx_dropped": 34030,
|
||||
"rx_errors": 0,
|
||||
"rx_packets": 3068347172,
|
||||
"tx_bytes": 3009601283006,
|
||||
"tx_dropped": 0,
|
||||
"tx_errors": 0,
|
||||
"tx_packets": 2859713220,
|
||||
"rx_multicast": 939111,
|
||||
"enable": true,
|
||||
"dns": [
|
||||
"1.1.1.1",
|
||||
"8.8.8.8"
|
||||
],
|
||||
"gateway": "3.1.33.8"
|
||||
},
|
||||
"port_table": [
|
||||
{
|
||||
"name": "wan",
|
||||
"ifname": "eth0",
|
||||
"ip": "3.1.33.7",
|
||||
"netmask": "255.255.254.0",
|
||||
"mac": "22:22:00:22:22:00",
|
||||
"up": true,
|
||||
"speed": 1000,
|
||||
"full_duplex": true,
|
||||
"rx_bytes": 2648236513108,
|
||||
"rx_dropped": 34030,
|
||||
"rx_errors": 0,
|
||||
"rx_packets": 3068347172,
|
||||
"tx_bytes": 3009601283006,
|
||||
"tx_dropped": 0,
|
||||
"tx_errors": 0,
|
||||
"tx_packets": 2859713220,
|
||||
"rx_multicast": 939111,
|
||||
"enable": true,
|
||||
"dns": [
|
||||
"216.146.35.35",
|
||||
"216.146.36.36"
|
||||
],
|
||||
"gateway": "3.1.33.8"
|
||||
},
|
||||
{
|
||||
"name": "lan",
|
||||
"ifname": "eth1",
|
||||
"ip": "192.168.2.1",
|
||||
"netmask": "255.255.252.0",
|
||||
"mac": "22:22:00:22:22:00",
|
||||
"up": true,
|
||||
"speed": 1000,
|
||||
"full_duplex": true,
|
||||
"rx_bytes": 2911311797255,
|
||||
"rx_dropped": 3438,
|
||||
"rx_errors": 0,
|
||||
"rx_packets": 2659342049,
|
||||
"tx_bytes": 2140222188895,
|
||||
"tx_dropped": 0,
|
||||
"tx_errors": 0,
|
||||
"tx_packets": 2734245088,
|
||||
"rx_multicast": 11929365,
|
||||
"enable": true
|
||||
},
|
||||
{
|
||||
"name": "lan2",
|
||||
"ifname": "eth2",
|
||||
"ip": "0.0.0.0",
|
||||
"netmask": "0.0.0.0",
|
||||
"mac": "22:22:00:22:22:00",
|
||||
"up": false,
|
||||
"speed": 0,
|
||||
"full_duplex": false,
|
||||
"rx_bytes": 0,
|
||||
"rx_dropped": 0,
|
||||
"rx_errors": 0,
|
||||
"rx_packets": 0,
|
||||
"tx_bytes": 0,
|
||||
"tx_dropped": 0,
|
||||
"tx_errors": 0,
|
||||
"tx_packets": 0,
|
||||
"rx_multicast": 0,
|
||||
"enable": false
|
||||
}
|
||||
],
|
||||
"network_table": [
|
||||
{
|
||||
"_id": "574e8de34566ffb914a26862",
|
||||
"is_nat": true,
|
||||
"dhcpd_dns_enabled": false,
|
||||
"purpose": "guest",
|
||||
"dhcpd_leasetime": "86400",
|
||||
"igmp_snooping": true,
|
||||
"dhcpguard_enabled": false,
|
||||
"dhcpd_start": "192.168.5.1",
|
||||
"enabled": true,
|
||||
"dhcpd_stop": "192.168.5.254",
|
||||
"dhcpd_wins_enabled": false,
|
||||
"domain_name": "guest.lan",
|
||||
"dhcpd_enabled": true,
|
||||
"ip_subnet": "192.168.5.0/23",
|
||||
"vlan": "5",
|
||||
"networkgroup": "LAN",
|
||||
"name": "Public Wireless",
|
||||
"site_id": "574e86994566ffb914a2683c",
|
||||
"dhcpd_ip_1": "",
|
||||
"vlan_enabled": true,
|
||||
"dhcpd_gateway_enabled": false,
|
||||
"dhcpd_time_offset_enabled": false,
|
||||
"ipv6_interface_type": "none",
|
||||
"dhcp_relay_enabled": false,
|
||||
"mac": "22:22:00:22:22:00",
|
||||
"is_guest": true,
|
||||
"ip": "192.168.5.0",
|
||||
"up": "true",
|
||||
"num_sta": 1,
|
||||
"rx_bytes": 578602537,
|
||||
"rx_packets": 471151,
|
||||
"tx_bytes": 182318948,
|
||||
"tx_packets": 239651
|
||||
},
|
||||
{
|
||||
"_id": "59a362f645663e6cc8260133",
|
||||
"is_nat": true,
|
||||
"dhcpd_dns_enabled": false,
|
||||
"purpose": "corporate",
|
||||
"dhcpd_leasetime": 86400,
|
||||
"dhcpd_start": "192.168.68.2",
|
||||
"dhcpd_stop": "192.168.68.250",
|
||||
"enabled": true,
|
||||
"domain_name": "secure.lan",
|
||||
"dhcpd_enabled": true,
|
||||
"vlan": "69",
|
||||
"ip_subnet": "192.168.69.1/23",
|
||||
"networkgroup": "LAN",
|
||||
"name": "Security Network",
|
||||
"site_id": "574e86994566ffb914a2683c",
|
||||
"vlan_enabled": true,
|
||||
"dhcpd_ntp_1": "192.168.69.1",
|
||||
"dhcpd_gateway_enabled": false,
|
||||
"dhcpd_time_offset_enabled": false,
|
||||
"dhcp_relay_enabled": false,
|
||||
"dhcpd_ntp_enabled": true,
|
||||
"ipv6_interface_type": "none",
|
||||
"dhcpd_unifi_controller": "192.168.3.1",
|
||||
"igmp_snooping": true,
|
||||
"mac": "22:22:00:22:22:00",
|
||||
"is_guest": false,
|
||||
"ip": "192.168.69.1",
|
||||
"up": "true",
|
||||
"num_sta": 11,
|
||||
"rx_bytes": 5221725,
|
||||
"rx_packets": 70663,
|
||||
"tx_bytes": 0,
|
||||
"tx_packets": 0
|
||||
},
|
||||
{
|
||||
"_id": "574e869d4566ffb914a26841",
|
||||
"purpose": "corporate",
|
||||
"dhcpd_leasetime": "86400",
|
||||
"igmp_snooping": false,
|
||||
"dhcpd_ntp_1": "192.168.2.1",
|
||||
"dhcpguard_enabled": false,
|
||||
"dhcpd_gateway_enabled": false,
|
||||
"dhcpd_time_offset_enabled": false,
|
||||
"dhcpd_start": "192.168.1.1",
|
||||
"dhcpd_unifi_controller": "192.168.3.1",
|
||||
"dhcpd_stop": "192.168.1.254",
|
||||
"enabled": true,
|
||||
"domain_name": "home.lan",
|
||||
"dhcpd_enabled": true,
|
||||
"ip_subnet": "192.168.2.1/22",
|
||||
"networkgroup": "LAN",
|
||||
"dhcpd_ip_1": "",
|
||||
"vlan_enabled": false,
|
||||
"is_nat": true,
|
||||
"dhcpd_dns_enabled": false,
|
||||
"dhcp_relay_enabled": false,
|
||||
"dhcpd_wins_enabled": false,
|
||||
"upnp_lan_enabled": true,
|
||||
"dhcpd_ntp_enabled": true,
|
||||
"name": "Home Network",
|
||||
"site_id": "574e86994566ffb914a2683c",
|
||||
"attr_no_delete": true,
|
||||
"attr_hidden_id": "LAN",
|
||||
"ipv6_interface_type": "none",
|
||||
"mac": "22:22:00:22:22:00",
|
||||
"is_guest": false,
|
||||
"ip": "192.168.2.1",
|
||||
"up": "true",
|
||||
"num_sta": 30,
|
||||
"rx_bytes": 2099754971983,
|
||||
"rx_packets": 2689749160,
|
||||
"tx_bytes": 2877873632166,
|
||||
"tx_packets": 2579198457
|
||||
}
|
||||
],
|
||||
"uplink": {
|
||||
"drops": 40,
|
||||
"enable": true,
|
||||
"full_duplex": true,
|
||||
"gateways": [
|
||||
"3.1.33.8"
|
||||
],
|
||||
"ip": "3.1.33.7",
|
||||
"latency": 103,
|
||||
"mac": "22:22:00:22:22:00",
|
||||
"name": "eth0",
|
||||
"nameservers": [
|
||||
"1.1.1.1",
|
||||
"8.8.8.8"
|
||||
],
|
||||
"netmask": "255.255.254.0",
|
||||
"num_port": 1,
|
||||
"rx_bytes": 2648236513108,
|
||||
"rx_dropped": 34030,
|
||||
"rx_errors": 0,
|
||||
"rx_multicast": 939111,
|
||||
"rx_packets": 3068347172,
|
||||
"speed": 1000,
|
||||
"speedtest_lastrun": 1562310531,
|
||||
"speedtest_ping": 14,
|
||||
"speedtest_status": "Success",
|
||||
"tx_bytes": 3009601283006,
|
||||
"tx_dropped": 0,
|
||||
"tx_errors": 0,
|
||||
"tx_packets": 2859713220,
|
||||
"up": true,
|
||||
"uptime": 559088,
|
||||
"xput_down": 157.368,
|
||||
"xput_up": 37.905,
|
||||
"tx_bytes-r": 2852355,
|
||||
"rx_bytes-r": 1224743,
|
||||
"bytes-r": 4077098,
|
||||
"max_speed": 1000,
|
||||
"type": "wire"
|
||||
},
|
||||
"stat": {
|
||||
"site_id": "574e86994566ffb914a2683c",
|
||||
"o": "gw",
|
||||
"oid": "22:22:00:22:22:00",
|
||||
"gw": "22:22:00:22:22:00",
|
||||
"time": 1562207100000,
|
||||
"datetime": "2019-07-04T02:25:00Z",
|
||||
"duration": 104466000,
|
||||
"wan-rx_packets": 151387924,
|
||||
"wan-rx_bytes": 111251311739,
|
||||
"wan-tx_packets": 182985900,
|
||||
"wan-tx_bytes": 230372237709,
|
||||
"lan-rx_packets": 173953163,
|
||||
"lan-rx_bytes": 226862410885,
|
||||
"lan-tx_packets": 137029474,
|
||||
"lan-tx_bytes": 89478206254,
|
||||
"wan-rx_dropped": 561,
|
||||
"lan-rx_dropped": 29
|
||||
},
|
||||
"tx_bytes": 2648236513108,
|
||||
"rx_bytes": 3009601283006,
|
||||
"bytes": 5657837796114,
|
||||
"num_sta": 41,
|
||||
"user-num_sta": 41,
|
||||
"guest-num_sta": 0,
|
||||
"num_desktop": 7,
|
||||
"num_mobile": 2,
|
||||
"num_handheld": 8
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,9 @@
|
|||
module github.com/unpoller/unifi
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/stretchr/testify v1.4.0
|
||||
golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d
|
||||
)
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d h1:BgJvlyh+UqCUaPlscHJ+PN8GcpfrFdr7NHjd1JL0+Gs=
|
||||
golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.6/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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
package unifi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
// IDS holds an Intrusion Prevention System Event.
|
||||
type IDS struct {
|
||||
Archived FlexBool `json:"archived"`
|
||||
DestPort int `json:"dest_port,omitempty"`
|
||||
SrcPort int `json:"src_port,omitempty"`
|
||||
FlowID int64 `json:"flow_id"`
|
||||
InnerAlertRev int64 `json:"inner_alert_rev"`
|
||||
InnerAlertSeverity int64 `json:"inner_alert_severity"`
|
||||
InnerAlertGID int64 `json:"inner_alert_gid"`
|
||||
InnerAlertSignatureID int64 `json:"inner_alert_signature_id"`
|
||||
Time int64 `json:"time"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Datetime time.Time `json:"datetime"`
|
||||
AppProto string `json:"app_proto,omitempty"`
|
||||
Catname string `json:"catname"`
|
||||
DestIP string `json:"dest_ip"`
|
||||
DstMAC string `json:"dst_mac"`
|
||||
DstIPASN string `json:"dstipASN"`
|
||||
DstIPCountry string `json:"dstipCountry"`
|
||||
EventType string `json:"event_type"`
|
||||
Host string `json:"host"`
|
||||
ID string `json:"_id"`
|
||||
InIface string `json:"in_iface"`
|
||||
InnerAlertAction string `json:"inner_alert_action"`
|
||||
InnerAlertCategory string `json:"inner_alert_category"`
|
||||
InnerAlertSignature string `json:"inner_alert_signature"`
|
||||
Key string `json:"key"`
|
||||
Msg string `json:"msg"`
|
||||
Proto string `json:"proto"`
|
||||
SiteID string `json:"site_id"`
|
||||
SiteName string `json:"-"`
|
||||
SourceName string `json:"-"`
|
||||
SrcIP string `json:"src_ip"`
|
||||
SrcIPASN string `json:"srcipASN"`
|
||||
SrcIPCountry string `json:"srcipCountry"`
|
||||
SrcMAC string `json:"src_mac"`
|
||||
Subsystem string `json:"subsystem"`
|
||||
UniqueAlertID string `json:"unique_alertid"`
|
||||
USGIP string `json:"usgip"`
|
||||
USGIPASN string `json:"usgipASN"`
|
||||
USGIPCountry string `json:"usgipCountry"`
|
||||
DestIPGeo IPGeo `json:"dstipGeo"`
|
||||
SourceIPGeo IPGeo `json:"srcipGeo"`
|
||||
USGIPGeo IPGeo `json:"usgipGeo"`
|
||||
}
|
||||
|
||||
// GetIDS returns Intrusion Detection Systems events for a list of Sites.
|
||||
// timeRange may have a length of 0, 1 or 2. The first time is Start, the second is End.
|
||||
// Events between start and end are returned. End defaults to time.Now().
|
||||
func (u *Unifi) GetIDS(sites []*Site, timeRange ...time.Time) ([]*IDS, error) {
|
||||
data := []*IDS{}
|
||||
|
||||
for _, site := range sites {
|
||||
response, err := u.GetIDSSite(site, timeRange...)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
data = append(data, response...)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// GetIDSSite retreives the Intrusion Detection System Data for a single Site.
|
||||
// timeRange may have a length of 0, 1 or 2. The first time is Start, the second is End.
|
||||
// Events between start and end are returned. End defaults to time.Now().
|
||||
func (u *Unifi) GetIDSSite(site *Site, timeRange ...time.Time) ([]*IDS, error) {
|
||||
if site == nil || site.Name == "" {
|
||||
return nil, ErrNoSiteProvided
|
||||
}
|
||||
|
||||
u.DebugLog("Polling Controller for IDS Events, site %s", site.SiteName)
|
||||
|
||||
var (
|
||||
path = fmt.Sprintf(APIEventPathIDS, site.Name)
|
||||
ids struct {
|
||||
Data idsList `json:"data"`
|
||||
}
|
||||
)
|
||||
|
||||
if params, err := makeEventParams(timeRange...); err != nil {
|
||||
return ids.Data, err
|
||||
} else if err = u.GetData(path, &ids, params); err != nil {
|
||||
return ids.Data, err
|
||||
}
|
||||
|
||||
for i := range ids.Data {
|
||||
// Add special SourceName value.
|
||||
ids.Data[i].SourceName = u.URL
|
||||
// Add the special "Site Name" to each event. This becomes a Grafana filter somewhere.
|
||||
ids.Data[i].SiteName = site.SiteName
|
||||
}
|
||||
|
||||
sort.Sort(ids.Data)
|
||||
|
||||
return ids.Data, nil
|
||||
}
|
||||
|
||||
func makeEventParams(timeRange ...time.Time) (string, error) {
|
||||
type eventReq struct {
|
||||
Start int64 `json:"start,omitempty"`
|
||||
End int64 `json:"end,omitempty"`
|
||||
Limit int `json:"_limit,omitempty"`
|
||||
Sort string `json:"_sort"`
|
||||
}
|
||||
|
||||
rp := eventReq{Limit: eventLimit, Sort: "-time"}
|
||||
|
||||
switch len(timeRange) {
|
||||
case 0:
|
||||
rp.End = time.Now().Unix() * int64(time.Microsecond)
|
||||
case 1:
|
||||
rp.Start = timeRange[0].Unix() * int64(time.Microsecond)
|
||||
rp.End = time.Now().Unix() * int64(time.Microsecond)
|
||||
case 2: // nolint: gomnd
|
||||
rp.Start = timeRange[0].Unix() * int64(time.Microsecond)
|
||||
rp.End = timeRange[1].Unix() * int64(time.Microsecond)
|
||||
default:
|
||||
return "", ErrInvalidTimeRange
|
||||
}
|
||||
|
||||
params, err := json.Marshal(&rp)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("json marshal: %w", err)
|
||||
}
|
||||
|
||||
return string(params), nil
|
||||
}
|
||||
|
||||
type idsList []*IDS
|
||||
|
||||
// Len satisfies sort.Interface.
|
||||
func (e idsList) Len() int {
|
||||
return len(e)
|
||||
}
|
||||
|
||||
// Swap satisfies sort.Interface.
|
||||
func (e idsList) Swap(i, j int) {
|
||||
e[i], e[j] = e[j], e[i]
|
||||
}
|
||||
|
||||
// Less satisfies sort.Interface. Sort our list by Datetime.
|
||||
func (e idsList) Less(i, j int) bool {
|
||||
return e[i].Datetime.Before(e[j].Datetime)
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package unifi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// GetNetworks returns a response full of network data from the UniFi Controller.
|
||||
func (u *Unifi) GetNetworks(sites []*Site) ([]Network, error) {
|
||||
networks := make([]Network, 0)
|
||||
|
||||
for _, site := range sites {
|
||||
var response struct {
|
||||
Data []json.RawMessage `json:"data"`
|
||||
}
|
||||
|
||||
networkPath := fmt.Sprintf(APINetworkPath, site.Name)
|
||||
if err := u.GetData(networkPath, &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, data := range response.Data {
|
||||
network, err := u.parseNetwork(data, site.SiteName)
|
||||
if err != nil {
|
||||
return networks, err
|
||||
}
|
||||
|
||||
networks = append(networks, *network)
|
||||
}
|
||||
}
|
||||
|
||||
return networks, nil
|
||||
}
|
||||
|
||||
// parseNetwork parses the raw JSON from the Unifi Controller into network structures.
|
||||
func (u *Unifi) parseNetwork(data json.RawMessage, siteName string) (*Network, error) {
|
||||
network := new(Network)
|
||||
return network, u.unmarshalDevice(siteName, data, network)
|
||||
}
|
||||
|
||||
// Network is metadata about a network managed by a UniFi controller.
|
||||
type Network struct {
|
||||
DhcpdDNSEnabled FlexBool `json:"dhcpd_dns_enabled"`
|
||||
DhcpdEnabled FlexBool `json:"dhcpd_enabled"`
|
||||
DhcpdGatewayEnabled FlexBool `json:"dhcpd_gateway_enabled"`
|
||||
DhcpdIP1 string `json:"dhcpd_ip_1"`
|
||||
DhcpdLeasetime FlexInt `json:"dhcpd_leasetime"`
|
||||
DhcpRelayEnabled FlexBool `json:"dhcp_relay_enabled"`
|
||||
DhcpdTimeOffsetEnabled FlexBool `json:"dhcpd_time_offset_enabled"`
|
||||
DhcpGuardEnabled FlexBool `json:"dhcpguard_enabled"`
|
||||
DomainName string `json:"domain_name"`
|
||||
Enabled FlexBool `json:"enabled"`
|
||||
ID string `json:"_id"`
|
||||
IPSubnet string `json:"ip_subnet"`
|
||||
IsNat FlexBool `json:"is_nat"`
|
||||
Name string `json:"name"`
|
||||
Networkgroup string `json:"networkgroup"`
|
||||
Purpose string `json:"purpose"`
|
||||
SiteID string `json:"site_id"`
|
||||
Vlan FlexInt `json:"vlan"`
|
||||
VlanEnabled FlexBool `json:"vlan_enabled"`
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
package unifi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var 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.
|
||||
func (u *Unifi) GetSites() ([]*Site, error) {
|
||||
var response struct {
|
||||
Data []*Site `json:"data"`
|
||||
}
|
||||
|
||||
if err := u.GetData(APISiteList, &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sites := []string{} // used for debug log only
|
||||
|
||||
for i, d := range response.Data {
|
||||
// Add the unifi struct to the site.
|
||||
response.Data[i].controller = u
|
||||
// Add special SourceName value.
|
||||
response.Data[i].SourceName = u.URL
|
||||
// If the human name is missing (description), set it to the cryptic name.
|
||||
response.Data[i].Desc = strings.TrimSpace(pick(d.Desc, d.Name))
|
||||
// Add the custom site name to each site. used as a Grafana filter somewhere.
|
||||
response.Data[i].SiteName = d.Desc + " (" + d.Name + ")"
|
||||
sites = append(sites, d.Name) // used for debug log only
|
||||
}
|
||||
|
||||
u.DebugLog("Found %d site(s): %s", len(sites), strings.Join(sites, ","))
|
||||
|
||||
return response.Data, nil
|
||||
}
|
||||
|
||||
// GetSiteDPI garners dpi data for sites.
|
||||
func (u *Unifi) GetSiteDPI(sites []*Site) ([]*DPITable, error) {
|
||||
data := []*DPITable{}
|
||||
|
||||
for _, site := range sites {
|
||||
u.DebugLog("Polling Controller, retreiving Site DPI data, site %s", site.SiteName)
|
||||
|
||||
var response struct {
|
||||
Data []*DPITable `json:"data"`
|
||||
}
|
||||
|
||||
siteDPIpath := fmt.Sprintf(APISiteDPI, site.Name)
|
||||
if err := u.GetData(siteDPIpath, &response, `{"type":"by_app"}`); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if l := len(response.Data); l > 1 {
|
||||
return nil, ErrDPIDataBug
|
||||
} else if l == 0 {
|
||||
u.DebugLog("Site DPI data missing! Is DPI enabled in UniFi controller? Site %s", site.SiteName)
|
||||
continue
|
||||
}
|
||||
|
||||
response.Data[0].SourceName = site.SourceName
|
||||
response.Data[0].SiteName = site.SiteName
|
||||
data = append(data, response.Data[0])
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Site represents a site's data.
|
||||
type Site struct {
|
||||
controller *Unifi
|
||||
SourceName string `json:"-"`
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
Desc string `json:"desc"`
|
||||
SiteName string `json:"-"`
|
||||
AttrHiddenID string `json:"attr_hidden_id"`
|
||||
AttrNoDelete FlexBool `json:"attr_no_delete"`
|
||||
Health []struct {
|
||||
Subsystem string `json:"subsystem"`
|
||||
NumUser FlexInt `json:"num_user,omitempty"`
|
||||
NumGuest FlexInt `json:"num_guest,omitempty"`
|
||||
NumIot FlexInt `json:"num_iot,omitempty"`
|
||||
TxBytesR FlexInt `json:"tx_bytes-r,omitempty"`
|
||||
RxBytesR FlexInt `json:"rx_bytes-r,omitempty"`
|
||||
Status string `json:"status"`
|
||||
NumAp FlexInt `json:"num_ap,omitempty"`
|
||||
NumAdopted FlexInt `json:"num_adopted,omitempty"`
|
||||
NumDisabled FlexInt `json:"num_disabled,omitempty"`
|
||||
NumDisconnected FlexInt `json:"num_disconnected,omitempty"`
|
||||
NumPending FlexInt `json:"num_pending,omitempty"`
|
||||
NumGw FlexInt `json:"num_gw,omitempty"`
|
||||
WanIP string `json:"wan_ip,omitempty"`
|
||||
Gateways []string `json:"gateways,omitempty"`
|
||||
Netmask string `json:"netmask,omitempty"`
|
||||
Nameservers []string `json:"nameservers,omitempty"`
|
||||
NumSta FlexInt `json:"num_sta,omitempty"`
|
||||
GwMac string `json:"gw_mac,omitempty"`
|
||||
GwName string `json:"gw_name,omitempty"`
|
||||
GwSystemStats struct {
|
||||
CPU FlexInt `json:"cpu"`
|
||||
Mem FlexInt `json:"mem"`
|
||||
Uptime FlexInt `json:"uptime"`
|
||||
} `json:"gw_system-stats,omitempty"`
|
||||
GwVersion string `json:"gw_version,omitempty"`
|
||||
Latency FlexInt `json:"latency,omitempty"`
|
||||
Uptime FlexInt `json:"uptime,omitempty"`
|
||||
Drops FlexInt `json:"drops,omitempty"`
|
||||
XputUp FlexInt `json:"xput_up,omitempty"`
|
||||
XputDown FlexInt `json:"xput_down,omitempty"`
|
||||
SpeedtestStatus string `json:"speedtest_status,omitempty"`
|
||||
SpeedtestLastrun FlexInt `json:"speedtest_lastrun,omitempty"`
|
||||
SpeedtestPing FlexInt `json:"speedtest_ping,omitempty"`
|
||||
LanIP string `json:"lan_ip,omitempty"`
|
||||
NumSw FlexInt `json:"num_sw,omitempty"`
|
||||
RemoteUserEnabled FlexBool `json:"remote_user_enabled,omitempty"`
|
||||
RemoteUserNumActive FlexInt `json:"remote_user_num_active,omitempty"`
|
||||
RemoteUserNumInactive FlexInt `json:"remote_user_num_inactive,omitempty"`
|
||||
RemoteUserRxBytes FlexInt `json:"remote_user_rx_bytes,omitempty"`
|
||||
RemoteUserTxBytes FlexInt `json:"remote_user_tx_bytes,omitempty"`
|
||||
RemoteUserRxPackets FlexInt `json:"remote_user_rx_packets,omitempty"`
|
||||
RemoteUserTxPackets FlexInt `json:"remote_user_tx_packets,omitempty"`
|
||||
SiteToSiteEnabled FlexBool `json:"site_to_site_enabled,omitempty"`
|
||||
} `json:"health"`
|
||||
NumNewAlarms FlexInt `json:"num_new_alarms"`
|
||||
}
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
package unifi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ErrCannotUnmarshalFlexInt = fmt.Errorf("cannot unmarshal to FlexInt")
|
||||
|
||||
// This is a list of unifi API paths.
|
||||
// The %s in each string must be replaced with a Site.Name.
|
||||
const (
|
||||
// APIRogueAP shows your neighbors' wifis.
|
||||
APIRogueAP string = "/api/s/%s/stat/rogueap"
|
||||
// APIStatusPath shows Controller version.
|
||||
APIStatusPath string = "/status"
|
||||
// APIEventPath contains UniFi Event data.
|
||||
APIEventPath string = "/api/s/%s/stat/event"
|
||||
// APISiteList is the path to the api site list.
|
||||
APISiteList string = "/api/stat/sites"
|
||||
// APISiteDPI is site DPI data.
|
||||
APISiteDPI string = "/api/s/%s/stat/sitedpi"
|
||||
// APISiteDPI is site DPI data.
|
||||
APIClientDPI string = "/api/s/%s/stat/stadpi"
|
||||
// APIClientPath is Unifi Clients API Path.
|
||||
APIClientPath string = "/api/s/%s/stat/sta"
|
||||
// APIAllUserPath is Unifi Insight all previous Clients API Path.
|
||||
APIAllUserPath string = "/api/s/%s/stat/alluser"
|
||||
// APINetworkPath is where we get data about Unifi networks.
|
||||
APINetworkPath string = "/api/s/%s/rest/networkconf"
|
||||
// APIDevicePath is where we get data about Unifi devices.
|
||||
APIDevicePath string = "/api/s/%s/stat/device"
|
||||
// APILoginPath is Unifi Controller Login API Path.
|
||||
APILoginPath string = "/api/login"
|
||||
// APILoginPathNew is how we log into UDM 5.12.55+.
|
||||
APILoginPathNew string = "/api/auth/login"
|
||||
// APILogoutPath is how we logout from UDM.
|
||||
APILogoutPath string = "/api/logout"
|
||||
// APIEventPathIDS returns Intrusion Detection/Prevention Systems Events.
|
||||
APIEventPathIDS string = "/api/s/%s/stat/ips/event"
|
||||
// APIEventPathAlarms contains the site alarms.
|
||||
APIEventPathAlarms string = "/api/s/%s/list/alarm"
|
||||
// APIPrefixNew is the prefix added to the new API paths; except login. duh.
|
||||
APIPrefixNew string = "/proxy/network"
|
||||
// APIAnomaliesPath returns site anomalies.
|
||||
APIAnomaliesPath string = "/api/s/%s/stat/anomalies"
|
||||
APICommandPath string = "/api/s/%s/cmd"
|
||||
APIDevMgrPath string = APICommandPath + "/devmgr"
|
||||
)
|
||||
|
||||
// path returns the correct api path based on the new variable.
|
||||
// new is based on the unifi-controller output. is it new or old output?
|
||||
func (u *Unifi) path(path string) string {
|
||||
if u.new {
|
||||
if path == APILoginPath {
|
||||
return APILoginPathNew
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(path, APIPrefixNew) && path != APILoginPathNew {
|
||||
return APIPrefixNew + path
|
||||
}
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
// 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{})
|
||||
|
||||
// discardLogs is the default debug logger.
|
||||
func discardLogs(msg string, v ...interface{}) {
|
||||
// do nothing.
|
||||
}
|
||||
|
||||
// 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
|
||||
UDMs []*UDM
|
||||
UXGs []*UXG
|
||||
}
|
||||
|
||||
// Config is the data passed into our library. This configures things and allows
|
||||
// us to connect to a controller and write log messages. Optional SSLCert is used
|
||||
// for ssl cert pinning; provide the content of a PEM to validate the server's cert.
|
||||
type Config struct {
|
||||
User string
|
||||
Pass string
|
||||
URL string
|
||||
SSLCert [][]byte
|
||||
ErrorLog Logger
|
||||
DebugLog Logger
|
||||
Timeout time.Duration // how long to wait for replies, default: forever.
|
||||
VerifySSL bool
|
||||
}
|
||||
|
||||
// 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. Do not set
|
||||
// the loggers to nil. Set them to DiscardLogs if you want no logs.
|
||||
type Unifi struct {
|
||||
*http.Client
|
||||
*Config
|
||||
*server
|
||||
csrf string
|
||||
fingerprints fingerprints
|
||||
new bool
|
||||
}
|
||||
|
||||
type fingerprints []string
|
||||
|
||||
// Contains returns true if the fingerprint is in the list.
|
||||
func (f fingerprints) Contains(s string) bool {
|
||||
for i := range f {
|
||||
if s == f[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// server is the /status endpoint from the Unifi controller.
|
||||
type server struct {
|
||||
Up FlexBool `json:"up"`
|
||||
ServerVersion string `json:"server_version"`
|
||||
UUID string `json:"uuid"`
|
||||
}
|
||||
|
||||
// FlexInt provides a container and unmarshalling for fields that may be
|
||||
// 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 fmt.Errorf("json unmarshal: %w", err)
|
||||
}
|
||||
|
||||
switch i := unk.(type) {
|
||||
case float64:
|
||||
f.Val = i
|
||||
f.Txt = strconv.FormatFloat(i, 'f', -1, 64)
|
||||
case string:
|
||||
f.Txt = i
|
||||
f.Val, _ = strconv.ParseFloat(i, 64)
|
||||
case nil:
|
||||
f.Txt = "0"
|
||||
f.Val = 0
|
||||
default:
|
||||
return fmt.Errorf("%v: %w", b, ErrCannotUnmarshalFlexInt)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FlexInt) String() string {
|
||||
return f.Txt
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// UnmarshalJSON method converts armed/disarmed, yes/no, active/inactive or 0/1 to true/false.
|
||||
// Really it converts ready, ok, up, t, armed, yes, active, enabled, 1, true to true. Anything else is false.
|
||||
func (f *FlexBool) UnmarshalJSON(b []byte) error {
|
||||
f.Txt = strings.Trim(string(b), `"`)
|
||||
f.Val = f.Txt == "1" || strings.EqualFold(f.Txt, "true") || strings.EqualFold(f.Txt, "yes") ||
|
||||
strings.EqualFold(f.Txt, "t") || strings.EqualFold(f.Txt, "armed") || strings.EqualFold(f.Txt, "active") ||
|
||||
strings.EqualFold(f.Txt, "enabled") || strings.EqualFold(f.Txt, "ready") || strings.EqualFold(f.Txt, "up") ||
|
||||
strings.EqualFold(f.Txt, "ok")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FlexBool) String() string {
|
||||
return f.Txt
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package unifi_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/unpoller/unifi"
|
||||
)
|
||||
|
||||
func TestFlexInt(t *testing.T) {
|
||||
t.Parallel()
|
||||
a := assert.New(t)
|
||||
five, seven := 5, 7
|
||||
|
||||
var r struct {
|
||||
Five unifi.FlexInt `json:"five"`
|
||||
Seven unifi.FlexInt `json:"seven"`
|
||||
Auto unifi.FlexInt `json:"auto"`
|
||||
Channel unifi.FlexInt `json:"channel"`
|
||||
Nil unifi.FlexInt `json:"nil"`
|
||||
}
|
||||
|
||||
// test unmarshalling the custom type three times with different values.
|
||||
a.Nil(json.Unmarshal([]byte(`{"five": "5", "seven": 7, "auto": "auto", "nil": null}`), &r))
|
||||
// test number in string.
|
||||
a.EqualValues(five, r.Five.Val)
|
||||
a.EqualValues("5", r.Five.Txt)
|
||||
// test number.
|
||||
a.EqualValues(seven, 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)
|
||||
// test null.
|
||||
a.EqualValues(0, r.Nil.Val)
|
||||
a.EqualValues("0", r.Nil.Txt)
|
||||
}
|
||||
|
|
@ -0,0 +1,644 @@
|
|||
package unifi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// UAP represents all the data from the Ubiquiti Controller for a Unifi Access Point.
|
||||
// This was auto generated then edited by hand to get all the data types right.
|
||||
type UAP struct {
|
||||
site *Site
|
||||
SourceName string `json:"-"`
|
||||
ID string `json:"_id"`
|
||||
Adopted FlexBool `json:"adopted"`
|
||||
AntennaTable []struct {
|
||||
Default FlexBool `json:"default"`
|
||||
ID FlexInt `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Wifi0Gain FlexInt `json:"wifi0_gain"`
|
||||
Wifi1Gain FlexInt `json:"wifi1_gain"`
|
||||
} `json:"antenna_table"`
|
||||
BandsteeringMode string `json:"bandsteering_mode,omitempty"`
|
||||
BoardRev int `json:"board_rev"`
|
||||
Cfgversion string `json:"cfgversion"`
|
||||
ConfigNetwork struct {
|
||||
Type string `json:"type"`
|
||||
IP string `json:"ip"`
|
||||
} `json:"config_network"`
|
||||
CountrycodeTable []int `json:"countrycode_table"`
|
||||
EthernetTable []struct {
|
||||
Mac string `json:"mac"`
|
||||
NumPort FlexInt `json:"num_port"`
|
||||
Name string `json:"name"`
|
||||
} `json:"ethernet_table"`
|
||||
FwCaps int `json:"fw_caps"`
|
||||
HasEth1 FlexBool `json:"has_eth1"`
|
||||
HasSpeaker FlexBool `json:"has_speaker"`
|
||||
InformIP string `json:"inform_ip"`
|
||||
InformURL string `json:"inform_url"`
|
||||
IP string `json:"ip"`
|
||||
LedOverride string `json:"led_override"`
|
||||
Mac string `json:"mac"`
|
||||
MeshStaVapEnabled FlexBool `json:"mesh_sta_vap_enabled"`
|
||||
Model string `json:"model"`
|
||||
Name string `json:"name"`
|
||||
OutdoorModeOverride string `json:"outdoor_mode_override"`
|
||||
PortTable []Port `json:"port_table"`
|
||||
RadioTable RadioTable `json:"radio_table"`
|
||||
ScanRadioTable []interface{} `json:"scan_radio_table"`
|
||||
Serial string `json:"serial"`
|
||||
SiteID string `json:"site_id"`
|
||||
SiteName string `json:"-"`
|
||||
Type string `json:"type"`
|
||||
Version string `json:"version"`
|
||||
VwireTable []interface{} `json:"vwire_table"`
|
||||
WifiCaps int `json:"wifi_caps"`
|
||||
WlangroupIDNa string `json:"wlangroup_id_na"`
|
||||
WlangroupIDNg string `json:"wlangroup_id_ng"`
|
||||
RequiredVersion string `json:"required_version"`
|
||||
HwCaps int `json:"hw_caps"`
|
||||
Unsupported FlexBool `json:"unsupported"`
|
||||
UnsupportedReason FlexInt `json:"unsupported_reason"`
|
||||
SysErrorCaps int `json:"sys_error_caps"`
|
||||
HasFan FlexBool `json:"has_fan"`
|
||||
HasTemperature FlexBool `json:"has_temperature"`
|
||||
DeviceID string `json:"device_id"`
|
||||
State FlexInt `json:"state"`
|
||||
LastSeen FlexInt `json:"last_seen"`
|
||||
Upgradable FlexBool `json:"upgradable"`
|
||||
AdoptableWhenUpgraded FlexBool `json:"adoptable_when_upgraded"`
|
||||
Rollupgrade FlexBool `json:"rollupgrade"`
|
||||
KnownCfgversion string `json:"known_cfgversion"`
|
||||
Uptime FlexInt `json:"uptime"`
|
||||
UUptime FlexInt `json:"_uptime"`
|
||||
Locating FlexBool `json:"locating"`
|
||||
ConnectRequestIP string `json:"connect_request_ip"`
|
||||
ConnectRequestPort string `json:"connect_request_port"`
|
||||
SysStats SysStats `json:"sys_stats"`
|
||||
SystemStats SystemStats `json:"system-stats"`
|
||||
SSHSessionTable []interface{} `json:"ssh_session_table"`
|
||||
Scanning FlexBool `json:"scanning"`
|
||||
SpectrumScanning FlexBool `json:"spectrum_scanning"`
|
||||
GuestToken string `json:"guest_token"`
|
||||
Meshv3PeerMac string `json:"meshv3_peer_mac"`
|
||||
Satisfaction FlexInt `json:"satisfaction"`
|
||||
Isolated FlexBool `json:"isolated"`
|
||||
RadioTableStats RadioTableStats `json:"radio_table_stats"`
|
||||
Uplink struct {
|
||||
FullDuplex FlexBool `json:"full_duplex"`
|
||||
IP string `json:"ip"`
|
||||
Mac string `json:"mac"`
|
||||
MaxVlan int `json:"max_vlan"`
|
||||
Name string `json:"name"`
|
||||
Netmask string `json:"netmask"`
|
||||
NumPort int `json:"num_port"`
|
||||
RxBytes FlexInt `json:"rx_bytes"`
|
||||
RxDropped FlexInt `json:"rx_dropped"`
|
||||
RxErrors FlexInt `json:"rx_errors"`
|
||||
RxMulticast FlexInt `json:"rx_multicast"`
|
||||
RxPackets FlexInt `json:"rx_packets"`
|
||||
Speed FlexInt `json:"speed"`
|
||||
TxBytes FlexInt `json:"tx_bytes"`
|
||||
TxDropped FlexInt `json:"tx_dropped"`
|
||||
TxErrors FlexInt `json:"tx_errors"`
|
||||
TxPackets FlexInt `json:"tx_packets"`
|
||||
Up FlexBool `json:"up"`
|
||||
MaxSpeed FlexInt `json:"max_speed"`
|
||||
Type string `json:"type"`
|
||||
TxBytesR FlexInt `json:"tx_bytes-r"`
|
||||
RxBytesR FlexInt `json:"rx_bytes-r"`
|
||||
UplinkMac string `json:"uplink_mac"`
|
||||
UplinkRemotePort int `json:"uplink_remote_port"`
|
||||
} `json:"uplink"`
|
||||
VapTable VapTable `json:"vap_table"`
|
||||
DownlinkTable []struct {
|
||||
PortIdx int `json:"port_idx"`
|
||||
Speed int `json:"speed"`
|
||||
FullDuplex bool `json:"full_duplex"`
|
||||
Mac string `json:"mac"`
|
||||
} `json:"downlink_table,omitempty"`
|
||||
VwireVapTable []interface{} `json:"vwire_vap_table"`
|
||||
BytesD FlexInt `json:"bytes-d"`
|
||||
TxBytesD FlexInt `json:"tx_bytes-d"`
|
||||
RxBytesD FlexInt `json:"rx_bytes-d"`
|
||||
BytesR FlexInt `json:"bytes-r"`
|
||||
LastUplink struct {
|
||||
UplinkMac string `json:"uplink_mac"`
|
||||
UplinkRemotePort int `json:"uplink_remote_port"`
|
||||
} `json:"last_uplink"`
|
||||
Stat UAPStat `json:"stat"`
|
||||
TxBytes FlexInt `json:"tx_bytes"`
|
||||
RxBytes FlexInt `json:"rx_bytes"`
|
||||
Bytes FlexInt `json:"bytes"`
|
||||
VwireEnabled FlexBool `json:"vwireEnabled"`
|
||||
UplinkTable []interface{} `json:"uplink_table"`
|
||||
NumSta FlexInt `json:"num_sta"`
|
||||
UserNumSta FlexInt `json:"user-num_sta"`
|
||||
GuestNumSta FlexInt `json:"guest-num_sta"`
|
||||
TwoPhaseAdopt FlexBool `json:"two_phase_adopt,omitempty"`
|
||||
}
|
||||
|
||||
// UAPStat holds the "stat" data for an access point.
|
||||
// This is split out because of a JSON data format change from 5.10 to 5.11.
|
||||
type UAPStat struct {
|
||||
*Ap
|
||||
}
|
||||
|
||||
// Ap is a subtype of UAPStat to make unmarshalling of different controller versions possible.
|
||||
type Ap struct {
|
||||
SiteID string `json:"site_id"`
|
||||
O string `json:"o"`
|
||||
Oid string `json:"oid"`
|
||||
Ap string `json:"ap"`
|
||||
Time FlexInt `json:"time"`
|
||||
Datetime time.Time `json:"datetime"`
|
||||
Bytes FlexInt `json:"bytes"`
|
||||
Duration FlexInt `json:"duration"`
|
||||
WifiTxDropped FlexInt `json:"wifi_tx_dropped"`
|
||||
RxErrors FlexInt `json:"rx_errors"`
|
||||
RxDropped FlexInt `json:"rx_dropped"`
|
||||
RxFrags FlexInt `json:"rx_frags"`
|
||||
RxCrypts FlexInt `json:"rx_crypts"`
|
||||
TxPackets FlexInt `json:"tx_packets"`
|
||||
TxBytes FlexInt `json:"tx_bytes"`
|
||||
TxErrors FlexInt `json:"tx_errors"`
|
||||
TxDropped FlexInt `json:"tx_dropped"`
|
||||
TxRetries FlexInt `json:"tx_retries"`
|
||||
RxPackets FlexInt `json:"rx_packets"`
|
||||
RxBytes FlexInt `json:"rx_bytes"`
|
||||
UserRxDropped FlexInt `json:"user-rx_dropped"`
|
||||
GuestRxDropped FlexInt `json:"guest-rx_dropped"`
|
||||
UserRxErrors FlexInt `json:"user-rx_errors"`
|
||||
GuestRxErrors FlexInt `json:"guest-rx_errors"`
|
||||
UserRxPackets FlexInt `json:"user-rx_packets"`
|
||||
GuestRxPackets FlexInt `json:"guest-rx_packets"`
|
||||
UserRxBytes FlexInt `json:"user-rx_bytes"`
|
||||
GuestRxBytes FlexInt `json:"guest-rx_bytes"`
|
||||
UserRxCrypts FlexInt `json:"user-rx_crypts"`
|
||||
GuestRxCrypts FlexInt `json:"guest-rx_crypts"`
|
||||
UserRxFrags FlexInt `json:"user-rx_frags"`
|
||||
GuestRxFrags FlexInt `json:"guest-rx_frags"`
|
||||
UserTxPackets FlexInt `json:"user-tx_packets"`
|
||||
GuestTxPackets FlexInt `json:"guest-tx_packets"`
|
||||
UserTxBytes FlexInt `json:"user-tx_bytes"`
|
||||
GuestTxBytes FlexInt `json:"guest-tx_bytes"`
|
||||
UserTxErrors FlexInt `json:"user-tx_errors"`
|
||||
GuestTxErrors FlexInt `json:"guest-tx_errors"`
|
||||
UserTxDropped FlexInt `json:"user-tx_dropped"`
|
||||
GuestTxDropped FlexInt `json:"guest-tx_dropped"`
|
||||
UserTxRetries FlexInt `json:"user-tx_retries"`
|
||||
GuestTxRetries FlexInt `json:"guest-tx_retries"`
|
||||
MacFilterRejections FlexInt `json:"mac_filter_rejections"`
|
||||
UserMacFilterRejections FlexInt `json:"user-mac_filter_rejections"`
|
||||
GuestMacFilterRejections FlexInt `json:"guest-mac_filter_rejections"`
|
||||
WifiTxAttempts FlexInt `json:"wifi_tx_attempts"`
|
||||
UserWifiTxDropped FlexInt `json:"user-wifi_tx_dropped"`
|
||||
GuestWifiTxDropped FlexInt `json:"guest-wifi_tx_dropped"`
|
||||
UserWifiTxAttempts FlexInt `json:"user-wifi_tx_attempts"`
|
||||
GuestWifiTxAttempts FlexInt `json:"guest-wifi_tx_attempts"`
|
||||
|
||||
// UAP-AC-PRO names, others may differ.
|
||||
/* These are all in VAP TABLE */
|
||||
/*
|
||||
GuestWifi0RxPackets FlexInt `json:"guest-wifi0-rx_packets"`
|
||||
GuestWifi1RxPackets FlexInt `json:"guest-wifi1-rx_packets"`
|
||||
UserWifi1RxPackets FlexInt `json:"user-wifi1-rx_packets"`
|
||||
UserWifi0RxPackets FlexInt `json:"user-wifi0-rx_packets"`
|
||||
Wifi0RxPackets FlexInt `json:"wifi0-rx_packets"`
|
||||
Wifi1RxPackets FlexInt `json:"wifi1-rx_packets"`
|
||||
GuestWifi0RxBytes FlexInt `json:"guest-wifi0-rx_bytes"`
|
||||
GuestWifi1RxBytes FlexInt `json:"guest-wifi1-rx_bytes"`
|
||||
UserWifi1RxBytes FlexInt `json:"user-wifi1-rx_bytes"`
|
||||
UserWifi0RxBytes FlexInt `json:"user-wifi0-rx_bytes"`
|
||||
Wifi0RxBytes FlexInt `json:"wifi0-rx_bytes"`
|
||||
Wifi1RxBytes FlexInt `json:"wifi1-rx_bytes"`
|
||||
GuestWifi0RxErrors FlexInt `json:"guest-wifi0-rx_errors"`
|
||||
GuestWifi1RxErrors FlexInt `json:"guest-wifi1-rx_errors"`
|
||||
UserWifi1RxErrors FlexInt `json:"user-wifi1-rx_errors"`
|
||||
UserWifi0RxErrors FlexInt `json:"user-wifi0-rx_errors"`
|
||||
Wifi0RxErrors FlexInt `json:"wifi0-rx_errors"`
|
||||
Wifi1RxErrors FlexInt `json:"wifi1-rx_errors"`
|
||||
GuestWifi0RxDropped FlexInt `json:"guest-wifi0-rx_dropped"`
|
||||
GuestWifi1RxDropped FlexInt `json:"guest-wifi1-rx_dropped"`
|
||||
UserWifi1RxDropped FlexInt `json:"user-wifi1-rx_dropped"`
|
||||
UserWifi0RxDropped FlexInt `json:"user-wifi0-rx_dropped"`
|
||||
Wifi0RxDropped FlexInt `json:"wifi0-rx_dropped"`
|
||||
Wifi1RxDropped FlexInt `json:"wifi1-rx_dropped"`
|
||||
GuestWifi0RxCrypts FlexInt `json:"guest-wifi0-rx_crypts"`
|
||||
GuestWifi1RxCrypts FlexInt `json:"guest-wifi1-rx_crypts"`
|
||||
UserWifi1RxCrypts FlexInt `json:"user-wifi1-rx_crypts"`
|
||||
UserWifi0RxCrypts FlexInt `json:"user-wifi0-rx_crypts"`
|
||||
Wifi0RxCrypts FlexInt `json:"wifi0-rx_crypts"`
|
||||
Wifi1RxCrypts FlexInt `json:"wifi1-rx_crypts"`
|
||||
GuestWifi0RxFrags FlexInt `json:"guest-wifi0-rx_frags"`
|
||||
GuestWifi1RxFrags FlexInt `json:"guest-wifi1-rx_frags"`
|
||||
UserWifi1RxFrags FlexInt `json:"user-wifi1-rx_frags"`
|
||||
UserWifi0RxFrags FlexInt `json:"user-wifi0-rx_frags"`
|
||||
Wifi0RxFrags FlexInt `json:"wifi0-rx_frags"`
|
||||
Wifi1RxFrags FlexInt `json:"wifi1-rx_frags"`
|
||||
GuestWifi0TxPackets FlexInt `json:"guest-wifi0-tx_packets"`
|
||||
GuestWifi1TxPackets FlexInt `json:"guest-wifi1-tx_packets"`
|
||||
UserWifi1TxPackets FlexInt `json:"user-wifi1-tx_packets"`
|
||||
UserWifi0TxPackets FlexInt `json:"user-wifi0-tx_packets"`
|
||||
Wifi0TxPackets FlexInt `json:"wifi0-tx_packets"`
|
||||
Wifi1TxPackets FlexInt `json:"wifi1-tx_packets"`
|
||||
GuestWifi0TxBytes FlexInt `json:"guest-wifi0-tx_bytes"`
|
||||
GuestWifi1TxBytes FlexInt `json:"guest-wifi1-tx_bytes"`
|
||||
UserWifi1TxBytes FlexInt `json:"user-wifi1-tx_bytes"`
|
||||
UserWifi0TxBytes FlexInt `json:"user-wifi0-tx_bytes"`
|
||||
Wifi0TxBytes FlexInt `json:"wifi0-tx_bytes"`
|
||||
Wifi1TxBytes FlexInt `json:"wifi1-tx_bytes"`
|
||||
GuestWifi0TxErrors FlexInt `json:"guest-wifi0-tx_errors"`
|
||||
GuestWifi1TxErrors FlexInt `json:"guest-wifi1-tx_errors"`
|
||||
UserWifi1TxErrors FlexInt `json:"user-wifi1-tx_errors"`
|
||||
UserWifi0TxErrors FlexInt `json:"user-wifi0-tx_errors"`
|
||||
Wifi0TxErrors FlexInt `json:"wifi0-tx_errors"`
|
||||
Wifi1TxErrors FlexInt `json:"wifi1-tx_errors"`
|
||||
GuestWifi0TxDropped FlexInt `json:"guest-wifi0-tx_dropped"`
|
||||
GuestWifi1TxDropped FlexInt `json:"guest-wifi1-tx_dropped"`
|
||||
UserWifi1TxDropped FlexInt `json:"user-wifi1-tx_dropped"`
|
||||
UserWifi0TxDropped FlexInt `json:"user-wifi0-tx_dropped"`
|
||||
Wifi0TxDropped FlexInt `json:"wifi0-tx_dropped"`
|
||||
Wifi1TxDropped FlexInt `json:"wifi1-tx_dropped"`
|
||||
GuestWifi0TxRetries FlexInt `json:"guest-wifi0-tx_retries"`
|
||||
GuestWifi1TxRetries FlexInt `json:"guest-wifi1-tx_retries"`
|
||||
UserWifi1TxRetries FlexInt `json:"user-wifi1-tx_retries"`
|
||||
UserWifi0TxRetries FlexInt `json:"user-wifi0-tx_retries"`
|
||||
Wifi0TxRetries FlexInt `json:"wifi0-tx_retries"`
|
||||
Wifi1TxRetries FlexInt `json:"wifi1-tx_retries"`
|
||||
GuestWifi0MacFilterRejections FlexInt `json:"guest-wifi0-mac_filter_rejections"`
|
||||
GuestWifi1MacFilterRejections FlexInt `json:"guest-wifi1-mac_filter_rejections"`
|
||||
UserWifi1MacFilterRejections FlexInt `json:"user-wifi1-mac_filter_rejections"`
|
||||
UserWifi0MacFilterRejections FlexInt `json:"user-wifi0-mac_filter_rejections"`
|
||||
Wifi0MacFilterRejections FlexInt `json:"wifi0-mac_filter_rejections"`
|
||||
Wifi1MacFilterRejections FlexInt `json:"wifi1-mac_filter_rejections"`
|
||||
GuestWifi0WifiTxAttempts FlexInt `json:"guest-wifi0-wifi_tx_attempts"`
|
||||
GuestWifi1WifiTxAttempts FlexInt `json:"guest-wifi1-wifi_tx_attempts"`
|
||||
UserWifi1WifiTxAttempts FlexInt `json:"user-wifi1-wifi_tx_attempts"`
|
||||
UserWifi0WifiTxAttempts FlexInt `json:"user-wifi0-wifi_tx_attempts"`
|
||||
Wifi0WifiTxAttempts FlexInt `json:"wifi0-wifi_tx_attempts"`
|
||||
Wifi1WifiTxAttempts FlexInt `json:"wifi1-wifi_tx_attempts"`
|
||||
GuestWifi0WifiTxDropped FlexInt `json:"guest-wifi0-wifi_tx_dropped"`
|
||||
GuestWifi1WifiTxDropped FlexInt `json:"guest-wifi1-wifi_tx_dropped"`
|
||||
UserWifi1WifiTxDropped FlexInt `json:"user-wifi1-wifi_tx_dropped"`
|
||||
UserWifi0WifiTxDropped FlexInt `json:"user-wifi0-wifi_tx_dropped"`
|
||||
Wifi0WifiTxDropped FlexInt `json:"wifi0-wifi_tx_dropped"`
|
||||
Wifi1WifiTxDropped FlexInt `json:"wifi1-wifi_tx_dropped"`
|
||||
// UDM Names
|
||||
GuestRa0RxPackets FlexInt `json:"guest-ra0-rx_packets"`
|
||||
UserRa0RxPackets FlexInt `json:"user-ra0-rx_packets"`
|
||||
Ra0RxPackets FlexInt `json:"ra0-rx_packets"`
|
||||
GuestRa0RxBytes FlexInt `json:"guest-ra0-rx_bytes"`
|
||||
UserRa0RxBytes FlexInt `json:"user-ra0-rx_bytes"`
|
||||
Ra0RxBytes FlexInt `json:"ra0-rx_bytes"`
|
||||
GuestRa0RxErrors FlexInt `json:"guest-ra0-rx_errors"`
|
||||
UserRa0RxErrors FlexInt `json:"user-ra0-rx_errors"`
|
||||
Ra0RxErrors FlexInt `json:"ra0-rx_errors"`
|
||||
GuestRa0RxDropped FlexInt `json:"guest-ra0-rx_dropped"`
|
||||
UserRa0RxDropped FlexInt `json:"user-ra0-rx_dropped"`
|
||||
Ra0RxDropped FlexInt `json:"ra0-rx_dropped"`
|
||||
GuestRa0RxCrypts FlexInt `json:"guest-ra0-rx_crypts"`
|
||||
UserRa0RxCrypts FlexInt `json:"user-ra0-rx_crypts"`
|
||||
Ra0RxCrypts FlexInt `json:"ra0-rx_crypts"`
|
||||
GuestRa0RxFrags FlexInt `json:"guest-ra0-rx_frags"`
|
||||
UserRa0RxFrags FlexInt `json:"user-ra0-rx_frags"`
|
||||
Ra0RxFrags FlexInt `json:"ra0-rx_frags"`
|
||||
GuestRa0TxPackets FlexInt `json:"guest-ra0-tx_packets"`
|
||||
UserRa0TxPackets FlexInt `json:"user-ra0-tx_packets"`
|
||||
Ra0TxPackets FlexInt `json:"ra0-tx_packets"`
|
||||
GuestRa0TxBytes FlexInt `json:"guest-ra0-tx_bytes"`
|
||||
UserRa0TxBytes FlexInt `json:"user-ra0-tx_bytes"`
|
||||
Ra0TxBytes FlexInt `json:"ra0-tx_bytes"`
|
||||
GuestRa0TxErrors FlexInt `json:"guest-ra0-tx_errors"`
|
||||
UserRa0TxErrors FlexInt `json:"user-ra0-tx_errors"`
|
||||
Ra0TxErrors FlexInt `json:"ra0-tx_errors"`
|
||||
GuestRa0TxDropped FlexInt `json:"guest-ra0-tx_dropped"`
|
||||
UserRa0TxDropped FlexInt `json:"user-ra0-tx_dropped"`
|
||||
Ra0TxDropped FlexInt `json:"ra0-tx_dropped"`
|
||||
GuestRa0TxRetries FlexInt `json:"guest-ra0-tx_retries"`
|
||||
UserRa0TxRetries FlexInt `json:"user-ra0-tx_retries"`
|
||||
Ra0TxRetries FlexInt `json:"ra0-tx_retries"`
|
||||
GuestRa0MacFilterRejections FlexInt `json:"guest-ra0-mac_filter_rejections"`
|
||||
UserRa0MacFilterRejections FlexInt `json:"user-ra0-mac_filter_rejections"`
|
||||
Ra0MacFilterRejections FlexInt `json:"ra0-mac_filter_rejections"`
|
||||
GuestRa0WifiTxAttempts FlexInt `json:"guest-ra0-wifi_tx_attempts"`
|
||||
UserRa0WifiTxAttempts FlexInt `json:"user-ra0-wifi_tx_attempts"`
|
||||
Ra0WifiTxAttempts FlexInt `json:"ra0-wifi_tx_attempts"`
|
||||
GuestRa0WifiTxDropped FlexInt `json:"guest-ra0-wifi_tx_dropped"`
|
||||
UserRa0WifiTxDropped FlexInt `json:"user-ra0-wifi_tx_dropped"`
|
||||
Ra0WifiTxDropped FlexInt `json:"ra0-wifi_tx_dropped"`
|
||||
GuestRai0RxPackets FlexInt `json:"guest-rai0-rx_packets"`
|
||||
UserRai0RxPackets FlexInt `json:"user-rai0-rx_packets"`
|
||||
Rai0RxPackets FlexInt `json:"rai0-rx_packets"`
|
||||
GuestRai0RxBytes FlexInt `json:"guest-rai0-rx_bytes"`
|
||||
UserRai0RxBytes FlexInt `json:"user-rai0-rx_bytes"`
|
||||
Rai0RxBytes FlexInt `json:"rai0-rx_bytes"`
|
||||
GuestRai0RxErrors FlexInt `json:"guest-rai0-rx_errors"`
|
||||
UserRai0RxErrors FlexInt `json:"user-rai0-rx_errors"`
|
||||
Rai0RxErrors FlexInt `json:"rai0-rx_errors"`
|
||||
GuestRai0RxDropped FlexInt `json:"guest-rai0-rx_dropped"`
|
||||
UserRai0RxDropped FlexInt `json:"user-rai0-rx_dropped"`
|
||||
Rai0RxDropped FlexInt `json:"rai0-rx_dropped"`
|
||||
GuestRai0RxCrypts FlexInt `json:"guest-rai0-rx_crypts"`
|
||||
UserRai0RxCrypts FlexInt `json:"user-rai0-rx_crypts"`
|
||||
Rai0RxCrypts FlexInt `json:"rai0-rx_crypts"`
|
||||
GuestRai0RxFrags FlexInt `json:"guest-rai0-rx_frags"`
|
||||
UserRai0RxFrags FlexInt `json:"user-rai0-rx_frags"`
|
||||
Rai0RxFrags FlexInt `json:"rai0-rx_frags"`
|
||||
GuestRai0TxPackets FlexInt `json:"guest-rai0-tx_packets"`
|
||||
UserRai0TxPackets FlexInt `json:"user-rai0-tx_packets"`
|
||||
Rai0TxPackets FlexInt `json:"rai0-tx_packets"`
|
||||
GuestRai0TxBytes FlexInt `json:"guest-rai0-tx_bytes"`
|
||||
UserRai0TxBytes FlexInt `json:"user-rai0-tx_bytes"`
|
||||
Rai0TxBytes FlexInt `json:"rai0-tx_bytes"`
|
||||
GuestRai0TxErrors FlexInt `json:"guest-rai0-tx_errors"`
|
||||
UserRai0TxErrors FlexInt `json:"user-rai0-tx_errors"`
|
||||
Rai0TxErrors FlexInt `json:"rai0-tx_errors"`
|
||||
GuestRai0TxDropped FlexInt `json:"guest-rai0-tx_dropped"`
|
||||
UserRai0TxDropped FlexInt `json:"user-rai0-tx_dropped"`
|
||||
Rai0TxDropped FlexInt `json:"rai0-tx_dropped"`
|
||||
GuestRai0TxRetries FlexInt `json:"guest-rai0-tx_retries"`
|
||||
UserRai0TxRetries FlexInt `json:"user-rai0-tx_retries"`
|
||||
Rai0TxRetries FlexInt `json:"rai0-tx_retries"`
|
||||
GuestRai0MacFilterRejections FlexInt `json:"guest-rai0-mac_filter_rejections"`
|
||||
UserRai0MacFilterRejections FlexInt `json:"user-rai0-mac_filter_rejections"`
|
||||
Rai0MacFilterRejections FlexInt `json:"rai0-mac_filter_rejections"`
|
||||
GuestRai0WifiTxAttempts FlexInt `json:"guest-rai0-wifi_tx_attempts"`
|
||||
UserRai0WifiTxAttempts FlexInt `json:"user-rai0-wifi_tx_attempts"`
|
||||
Rai0WifiTxAttempts FlexInt `json:"rai0-wifi_tx_attempts"`
|
||||
GuestRai0WifiTxDropped FlexInt `json:"guest-rai0-wifi_tx_dropped"`
|
||||
UserRai0WifiTxDropped FlexInt `json:"user-rai0-wifi_tx_dropped"`
|
||||
Rai0WifiTxDropped FlexInt `json:"rai0-wifi_tx_dropped"`
|
||||
*/
|
||||
}
|
||||
|
||||
// RadioTable is part of the data for UAPs and UDMs.
|
||||
type RadioTable []struct {
|
||||
AntennaGain FlexInt `json:"antenna_gain"`
|
||||
BuiltinAntGain FlexInt `json:"builtin_ant_gain"`
|
||||
BuiltinAntenna FlexBool `json:"builtin_antenna"`
|
||||
Channel FlexInt `json:"channel"`
|
||||
CurrentAntennaGain FlexInt `json:"current_antenna_gain"`
|
||||
HasDfs FlexBool `json:"has_dfs"`
|
||||
HasFccdfs FlexBool `json:"has_fccdfs"`
|
||||
HasHt160 FlexBool `json:"has_ht160"`
|
||||
Ht FlexInt `json:"ht"`
|
||||
Is11Ac FlexBool `json:"is_11ac"`
|
||||
MaxTxpower FlexInt `json:"max_txpower"`
|
||||
MinRssi FlexInt `json:"min_rssi,omitempty"`
|
||||
MinRssiEnabled FlexBool `json:"min_rssi_enabled"`
|
||||
MinTxpower FlexInt `json:"min_txpower"`
|
||||
Name string `json:"name"`
|
||||
Nss FlexInt `json:"nss"`
|
||||
Radio string `json:"radio"`
|
||||
RadioCaps FlexInt `json:"radio_caps"`
|
||||
SensLevelEnabled FlexBool `json:"sens_level_enabled"`
|
||||
TxPower FlexInt `json:"tx_power"`
|
||||
TxPowerMode string `json:"tx_power_mode"`
|
||||
VwireEnabled FlexBool `json:"vwire_enabled"`
|
||||
WlangroupID string `json:"wlangroup_id"`
|
||||
}
|
||||
|
||||
// RadioTableStats is part of the data shared between UAP and UDM.
|
||||
type RadioTableStats []struct {
|
||||
Name string `json:"name"`
|
||||
Channel FlexInt `json:"channel"`
|
||||
Radio string `json:"radio"`
|
||||
AstTxto interface{} `json:"ast_txto"`
|
||||
AstCst interface{} `json:"ast_cst"`
|
||||
AstBeXmit FlexInt `json:"ast_be_xmit"`
|
||||
CuTotal FlexInt `json:"cu_total"`
|
||||
CuSelfRx FlexInt `json:"cu_self_rx"`
|
||||
CuSelfTx FlexInt `json:"cu_self_tx"`
|
||||
Gain FlexInt `json:"gain"`
|
||||
Satisfaction FlexInt `json:"satisfaction"`
|
||||
State string `json:"state"`
|
||||
Extchannel FlexInt `json:"extchannel"`
|
||||
TxPower FlexInt `json:"tx_power"`
|
||||
TxPackets FlexInt `json:"tx_packets"`
|
||||
TxRetries FlexInt `json:"tx_retries"`
|
||||
NumSta FlexInt `json:"num_sta"`
|
||||
GuestNumSta FlexInt `json:"guest-num_sta"`
|
||||
UserNumSta FlexInt `json:"user-num_sta"`
|
||||
}
|
||||
|
||||
// VapTable holds much of the UAP wireless data. Shared by UDM.
|
||||
type VapTable []struct {
|
||||
AnomaliesBarChart struct {
|
||||
HighDNSLatency FlexInt `json:"high_dns_latency"`
|
||||
HighTCPLatency FlexInt `json:"high_tcp_latency"`
|
||||
HighTCPPacketLoss FlexInt `json:"high_tcp_packet_loss"`
|
||||
HighWifiLatency FlexInt `json:"high_wifi_latency"`
|
||||
HighWifiRetries FlexInt `json:"high_wifi_retries"`
|
||||
LowPhyRate FlexInt `json:"low_phy_rate"`
|
||||
PoorStreamEff FlexInt `json:"poor_stream_eff"`
|
||||
SleepyClient FlexInt `json:"sleepy_client"`
|
||||
StaArpTimeout FlexInt `json:"sta_arp_timeout"`
|
||||
StaDNSTimeout FlexInt `json:"sta_dns_timeout"`
|
||||
StaIPTimeout FlexInt `json:"sta_ip_timeout"`
|
||||
WeakSignal FlexInt `json:"weak_signal"`
|
||||
} `json:"anomalies_bar_chart"`
|
||||
AnomaliesBarChartNow struct {
|
||||
HighDNSLatency FlexInt `json:"high_dns_latency"`
|
||||
HighTCPLatency FlexInt `json:"high_tcp_latency"`
|
||||
HighTCPPacketLoss FlexInt `json:"high_tcp_packet_loss"`
|
||||
HighWifiLatency FlexInt `json:"high_wifi_latency"`
|
||||
HighWifiRetries FlexInt `json:"high_wifi_retries"`
|
||||
LowPhyRate FlexInt `json:"low_phy_rate"`
|
||||
PoorStreamEff FlexInt `json:"poor_stream_eff"`
|
||||
SleepyClient FlexInt `json:"sleepy_client"`
|
||||
StaArpTimeout FlexInt `json:"sta_arp_timeout"`
|
||||
StaDNSTimeout FlexInt `json:"sta_dns_timeout"`
|
||||
StaIPTimeout FlexInt `json:"sta_ip_timeout"`
|
||||
WeakSignal FlexInt `json:"weak_signal"`
|
||||
} `json:"anomalies_bar_chart_now"`
|
||||
ReasonsBarChart struct {
|
||||
PhyRate FlexInt `json:"phy_rate"`
|
||||
Signal FlexInt `json:"signal"`
|
||||
SleepyClient FlexInt `json:"sleepy_client"`
|
||||
StaArpTimeout FlexInt `json:"sta_arp_timeout"`
|
||||
StaDNSLatency FlexInt `json:"sta_dns_latency"`
|
||||
StaDNSTimeout FlexInt `json:"sta_dns_timeout"`
|
||||
StaIPTimeout FlexInt `json:"sta_ip_timeout"`
|
||||
StreamEff FlexInt `json:"stream_eff"`
|
||||
TCPLatency FlexInt `json:"tcp_latency"`
|
||||
TCPPacketLoss FlexInt `json:"tcp_packet_loss"`
|
||||
WifiLatency FlexInt `json:"wifi_latency"`
|
||||
WifiRetries FlexInt `json:"wifi_retries"`
|
||||
} `json:"reasons_bar_chart"`
|
||||
ReasonsBarChartNow struct {
|
||||
PhyRate FlexInt `json:"phy_rate"`
|
||||
Signal FlexInt `json:"signal"`
|
||||
SleepyClient FlexInt `json:"sleepy_client"`
|
||||
StaArpTimeout FlexInt `json:"sta_arp_timeout"`
|
||||
StaDNSLatency FlexInt `json:"sta_dns_latency"`
|
||||
StaDNSTimeout FlexInt `json:"sta_dns_timeout"`
|
||||
StaIPTimeout FlexInt `json:"sta_ip_timeout"`
|
||||
StreamEff FlexInt `json:"stream_eff"`
|
||||
TCPLatency FlexInt `json:"tcp_latency"`
|
||||
TCPPacketLoss FlexInt `json:"tcp_packet_loss"`
|
||||
WifiLatency FlexInt `json:"wifi_latency"`
|
||||
WifiRetries FlexInt `json:"wifi_retries"`
|
||||
} `json:"reasons_bar_chart_now"`
|
||||
RxTCPStats struct {
|
||||
Goodbytes FlexInt `json:"goodbytes"`
|
||||
LatAvg FlexInt `json:"lat_avg"`
|
||||
LatMax FlexInt `json:"lat_max"`
|
||||
LatMin FlexInt `json:"lat_min"`
|
||||
Stalls FlexInt `json:"stalls"`
|
||||
} `json:"rx_tcp_stats"`
|
||||
TxTCPStats struct {
|
||||
Goodbytes FlexInt `json:"goodbytes"`
|
||||
LatAvg FlexInt `json:"lat_avg"`
|
||||
LatMax FlexInt `json:"lat_max"`
|
||||
LatMin FlexInt `json:"lat_min"`
|
||||
Stalls FlexInt `json:"stalls"`
|
||||
} `json:"tx_tcp_stats"`
|
||||
WifiTxLatencyMov struct {
|
||||
Avg FlexInt `json:"avg"`
|
||||
Max FlexInt `json:"max"`
|
||||
Min FlexInt `json:"min"`
|
||||
Total FlexInt `json:"total"`
|
||||
TotalCount FlexInt `json:"total_count"`
|
||||
} `json:"wifi_tx_latency_mov"`
|
||||
ApMac string `json:"ap_mac"`
|
||||
AvgClientSignal FlexInt `json:"avg_client_signal"`
|
||||
Bssid string `json:"bssid"`
|
||||
Ccq int `json:"ccq"`
|
||||
Channel FlexInt `json:"channel"`
|
||||
DNSAvgLatency FlexInt `json:"dns_avg_latency"`
|
||||
Essid string `json:"essid"`
|
||||
Extchannel int `json:"extchannel"`
|
||||
ID string `json:"id"`
|
||||
IsGuest FlexBool `json:"is_guest"`
|
||||
IsWep FlexBool `json:"is_wep"`
|
||||
MacFilterRejections int `json:"mac_filter_rejections"`
|
||||
MapID interface{} `json:"map_id"`
|
||||
Name string `json:"name"`
|
||||
NumSatisfactionSta FlexInt `json:"num_satisfaction_sta"`
|
||||
NumSta int `json:"num_sta"`
|
||||
Radio string `json:"radio"`
|
||||
RadioName string `json:"radio_name"`
|
||||
RxBytes FlexInt `json:"rx_bytes"`
|
||||
RxCrypts FlexInt `json:"rx_crypts"`
|
||||
RxDropped FlexInt `json:"rx_dropped"`
|
||||
RxErrors FlexInt `json:"rx_errors"`
|
||||
RxFrags FlexInt `json:"rx_frags"`
|
||||
RxNwids FlexInt `json:"rx_nwids"`
|
||||
RxPackets FlexInt `json:"rx_packets"`
|
||||
Satisfaction FlexInt `json:"satisfaction"`
|
||||
SatisfactionNow FlexInt `json:"satisfaction_now"`
|
||||
SiteID string `json:"site_id"`
|
||||
State string `json:"state"`
|
||||
T string `json:"t"`
|
||||
TxBytes FlexInt `json:"tx_bytes"`
|
||||
TxCombinedRetries FlexInt `json:"tx_combined_retries"`
|
||||
TxDataMpduBytes FlexInt `json:"tx_data_mpdu_bytes"`
|
||||
TxDropped FlexInt `json:"tx_dropped"`
|
||||
TxErrors FlexInt `json:"tx_errors"`
|
||||
TxPackets FlexInt `json:"tx_packets"`
|
||||
TxPower FlexInt `json:"tx_power"`
|
||||
TxRetries FlexInt `json:"tx_retries"`
|
||||
TxRtsRetries FlexInt `json:"tx_rts_retries"`
|
||||
TxSuccess FlexInt `json:"tx_success"`
|
||||
TxTotal FlexInt `json:"tx_total"`
|
||||
Up FlexBool `json:"up"`
|
||||
Usage string `json:"usage"`
|
||||
WifiTxAttempts FlexInt `json:"wifi_tx_attempts"`
|
||||
WifiTxDropped FlexInt `json:"wifi_tx_dropped"`
|
||||
WlanconfID string `json:"wlanconf_id"`
|
||||
}
|
||||
|
||||
// RogueAP are your neighbors access points.
|
||||
type RogueAP struct {
|
||||
SourceName string `json:"-"`
|
||||
SiteName string `json:"-"`
|
||||
ID string `json:"_id"`
|
||||
ApMac string `json:"ap_mac"`
|
||||
Bssid string `json:"bssid"`
|
||||
SiteID string `json:"site_id"`
|
||||
Age FlexInt `json:"age"`
|
||||
Band string `json:"band"`
|
||||
Bw FlexInt `json:"bw"`
|
||||
CenterFreq FlexInt `json:"center_freq"`
|
||||
Channel int `json:"channel"`
|
||||
Essid string `json:"essid"`
|
||||
Freq FlexInt `json:"freq"`
|
||||
IsAdhoc FlexBool `json:"is_adhoc"`
|
||||
IsRogue FlexBool `json:"is_rogue"`
|
||||
IsUbnt FlexBool `json:"is_ubnt"`
|
||||
LastSeen FlexInt `json:"last_seen"`
|
||||
Noise FlexInt `json:"noise"`
|
||||
Radio string `json:"radio"`
|
||||
RadioName string `json:"radio_name"`
|
||||
ReportTime FlexInt `json:"report_time"`
|
||||
Rssi FlexInt `json:"rssi"`
|
||||
RssiAge FlexInt `json:"rssi_age"`
|
||||
Security string `json:"security"`
|
||||
Signal FlexInt `json:"signal"`
|
||||
Oui string `json:"oui"`
|
||||
}
|
||||
|
||||
// GetRogueAPs returns RogueAPs for a list of Sites.
|
||||
// Use GetRogueAPsSite if you want more control.
|
||||
func (u *Unifi) GetRogueAPs(sites []*Site) ([]*RogueAP, error) {
|
||||
data := []*RogueAP{}
|
||||
|
||||
for _, site := range sites {
|
||||
response, err := u.GetRogueAPsSite(site)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
data = append(data, response...)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// GetRogueAPsSite returns RogueAPs for a single Site.
|
||||
func (u *Unifi) GetRogueAPsSite(site *Site) ([]*RogueAP, error) {
|
||||
if site == nil || site.Name == "" {
|
||||
return nil, ErrNoSiteProvided
|
||||
}
|
||||
|
||||
u.DebugLog("Polling Controller for RogueAPs, site %s (%s)", site.SiteName, site.Desc)
|
||||
|
||||
var (
|
||||
path = fmt.Sprintf(APIRogueAP, site.Name)
|
||||
rogueaps struct {
|
||||
Data []*RogueAP `json:"data"`
|
||||
}
|
||||
)
|
||||
|
||||
if err := u.GetData(path, &rogueaps, ""); err != nil {
|
||||
return rogueaps.Data, err
|
||||
}
|
||||
|
||||
for i := range rogueaps.Data {
|
||||
// Add special SourceName value.
|
||||
rogueaps.Data[i].SourceName = u.URL
|
||||
// Add the special "Site Name" to each event. This becomes a Grafana filter somewhere.
|
||||
rogueaps.Data[i].SiteName = site.SiteName
|
||||
}
|
||||
|
||||
return rogueaps.Data, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshalls 5.10 or 5.11 formatted Access Point Stat data.
|
||||
func (v *UAPStat) UnmarshalJSON(data []byte) error {
|
||||
var n struct {
|
||||
Ap `json:"ap"`
|
||||
}
|
||||
|
||||
v.Ap = &n.Ap
|
||||
|
||||
err := json.Unmarshal(data, v.Ap) // controller version 5.10.
|
||||
if err != nil {
|
||||
return json.Unmarshal(data, &n) // controller version 5.11.
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package unifi // nolint: testpackage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUAPUnmarshalJSON(t *testing.T) {
|
||||
testcontroller511 := `{
|
||||
"ap": {
|
||||
"site_id": "mySite",
|
||||
"o": "ap",
|
||||
"oid": "00:00:00:00:00:00",
|
||||
"ap": "00:00:00:00:00:00",
|
||||
"time": 1577742600000,
|
||||
"datetime": "2019-12-30T09:50:00Z",
|
||||
"user-wifi1-rx_packets": 6596670,
|
||||
"user-wifi0-rx_packets": 42659527,
|
||||
"user-rx_packets": 49294197,
|
||||
"guest-rx_packets": 0,
|
||||
"wifi0-rx_packets": 42639527,
|
||||
"wifi1-rx_packets": 6591670,
|
||||
"rx_packets": 49299197}}`
|
||||
|
||||
testcontroller510 := `{
|
||||
"site_id": "mySite",
|
||||
"o": "ap",
|
||||
"oid": "00:00:00:00:00:00",
|
||||
"ap": "00:00:00:00:00:00",
|
||||
"time": 1577742600000,
|
||||
"datetime": "2019-12-30T09:50:00Z",
|
||||
"user-wifi1-rx_packets": 6596670,
|
||||
"user-wifi0-rx_packets": 42659527,
|
||||
"user-rx_packets": 49294197,
|
||||
"guest-rx_packets": 0,
|
||||
"wifi0-rx_packets": 42639527,
|
||||
"wifi1-rx_packets": 6591670,
|
||||
"rx_packets": 49299197}`
|
||||
|
||||
t.Parallel()
|
||||
a := assert.New(t)
|
||||
rxPakcets := 49299197
|
||||
u := &UAPStat{}
|
||||
err := u.UnmarshalJSON([]byte(testcontroller510))
|
||||
a.Nil(err, "must be no error unmarshaling test strings")
|
||||
a.Equal(float64(rxPakcets), u.RxPackets.Val, "data was not properly unmarshaled")
|
||||
|
||||
u = &UAPStat{} // reset
|
||||
err = u.UnmarshalJSON([]byte(testcontroller511))
|
||||
a.Nil(err, "must be no error unmarshaling test strings")
|
||||
a.Equal(float64(rxPakcets), u.RxPackets.Val, "data was not properly unmarshaled")
|
||||
}
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
package unifi
|
||||
|
||||
// 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.
|
||||
type UDM struct {
|
||||
site *Site
|
||||
SourceName string `json:"-"`
|
||||
SiteID string `json:"site_id"`
|
||||
SiteName string `json:"-"`
|
||||
Mac string `json:"mac"`
|
||||
Adopted FlexBool `json:"adopted"`
|
||||
Serial string `json:"serial"`
|
||||
IP string `json:"ip"`
|
||||
Uptime FlexInt `json:"uptime"`
|
||||
Model string `json:"model"`
|
||||
Version string `json:"version"`
|
||||
Name string `json:"name"`
|
||||
Default FlexBool `json:"default"`
|
||||
Locating FlexBool `json:"locating"`
|
||||
Type string `json:"type"`
|
||||
Unsupported FlexBool `json:"unsupported"`
|
||||
UnsupportedReason FlexInt `json:"unsupported_reason"`
|
||||
DiscoveredVia string `json:"discovered_via"`
|
||||
AdoptIP string `json:"adopt_ip"`
|
||||
AdoptURL string `json:"adopt_url"`
|
||||
State FlexInt `json:"state"`
|
||||
AdoptStatus FlexInt `json:"adopt_status"`
|
||||
UpgradeState FlexInt `json:"upgrade_state"`
|
||||
LastSeen FlexInt `json:"last_seen"`
|
||||
AdoptableWhenUpgraded FlexBool `json:"adoptable_when_upgraded"`
|
||||
Cfgversion string `json:"cfgversion"`
|
||||
ConfigNetwork *ConfigNetwork `json:"config_network"`
|
||||
VwireTable []interface{} `json:"vwire_table"`
|
||||
Dot1XPortctrlEnabled FlexBool `json:"dot1x_portctrl_enabled"`
|
||||
JumboframeEnabled FlexBool `json:"jumboframe_enabled"`
|
||||
FlowctrlEnabled FlexBool `json:"flowctrl_enabled"`
|
||||
StpVersion string `json:"stp_version"`
|
||||
StpPriority FlexInt `json:"stp_priority"`
|
||||
PowerSourceCtrlEnabled FlexBool `json:"power_source_ctrl_enabled"`
|
||||
LicenseState string `json:"license_state"`
|
||||
ID string `json:"_id"`
|
||||
DeviceID string `json:"device_id"`
|
||||
AdoptState FlexInt `json:"adopt_state"`
|
||||
AdoptTries FlexInt `json:"adopt_tries"`
|
||||
AdoptManual FlexBool `json:"adopt_manual"`
|
||||
InformURL string `json:"inform_url"`
|
||||
InformIP string `json:"inform_ip"`
|
||||
RequiredVersion string `json:"required_version"`
|
||||
BoardRev FlexInt `json:"board_rev"`
|
||||
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 struct {
|
||||
MaxMirrorSessions FlexInt `json:"max_mirror_sessions"`
|
||||
MaxAggregateSessions FlexInt `json:"max_aggregate_sessions"`
|
||||
} `json:"switch_caps"`
|
||||
HasFan FlexBool `json:"has_fan"`
|
||||
Temperatures []Temperature `json:"temperatures,omitempty"`
|
||||
RulesetInterfaces interface{} `json:"ruleset_interfaces"`
|
||||
/* struct {
|
||||
Br0 string `json:"br0"`
|
||||
Eth0 string `json:"eth0"`
|
||||
Eth1 string `json:"eth1"`
|
||||
Eth2 string `json:"eth2"`
|
||||
Eth3 string `json:"eth3"`
|
||||
Eth4 string `json:"eth4"`
|
||||
Eth5 string `json:"eth5"`
|
||||
Eth6 string `json:"eth6"`
|
||||
Eth7 string `json:"eth7"`
|
||||
Eth8 string `json:"eth8"`
|
||||
} */
|
||||
KnownCfgversion string `json:"known_cfgversion"`
|
||||
SysStats SysStats `json:"sys_stats"`
|
||||
SystemStats SystemStats `json:"system-stats"`
|
||||
GuestToken string `json:"guest_token"`
|
||||
Overheating FlexBool `json:"overheating"`
|
||||
SpeedtestStatus SpeedtestStatus `json:"speedtest-status"`
|
||||
SpeedtestStatusSaved FlexBool `json:"speedtest-status-saved"`
|
||||
Wan1 Wan `json:"wan1"`
|
||||
Wan2 Wan `json:"wan2"`
|
||||
Uplink Uplink `json:"uplink"`
|
||||
ConnectRequestIP string `json:"connect_request_ip"`
|
||||
ConnectRequestPort string `json:"connect_request_port"`
|
||||
DownlinkTable []*DownlinkTable `json:"downlink_table"`
|
||||
WlangroupIDNa string `json:"wlangroup_id_na"`
|
||||
WlangroupIDNg string `json:"wlangroup_id_ng"`
|
||||
BandsteeringMode string `json:"bandsteering_mode"`
|
||||
RadioTable *RadioTable `json:"radio_table,omitempty"`
|
||||
RadioTableStats *RadioTableStats `json:"radio_table_stats,omitempty"`
|
||||
VapTable *VapTable `json:"vap_table"`
|
||||
XInformAuthkey string `json:"x_inform_authkey"`
|
||||
NetworkTable NetworkTable `json:"network_table"`
|
||||
PortOverrides []struct {
|
||||
PortIdx FlexInt `json:"port_idx"`
|
||||
PortconfID string `json:"portconf_id"`
|
||||
} `json:"port_overrides"`
|
||||
Stat UDMStat `json:"stat"`
|
||||
Storage []*Storage `json:"storage"`
|
||||
TxBytes FlexInt `json:"tx_bytes"`
|
||||
RxBytes FlexInt `json:"rx_bytes"`
|
||||
Bytes FlexInt `json:"bytes"`
|
||||
BytesD FlexInt `json:"bytes-d"`
|
||||
TxBytesD FlexInt `json:"tx_bytes-d"`
|
||||
RxBytesD FlexInt `json:"rx_bytes-d"`
|
||||
BytesR FlexInt `json:"bytes-r"`
|
||||
NumSta FlexInt `json:"num_sta"` // USG
|
||||
WlanNumSta FlexInt `json:"wlan-num_sta"` // UAP
|
||||
LanNumSta FlexInt `json:"lan-num_sta"` // USW
|
||||
UserWlanNumSta FlexInt `json:"user-wlan-num_sta"` // UAP
|
||||
UserLanNumSta FlexInt `json:"user-lan-num_sta"` // USW
|
||||
UserNumSta FlexInt `json:"user-num_sta"` // USG
|
||||
GuestWlanNumSta FlexInt `json:"guest-wlan-num_sta"` // UAP
|
||||
GuestLanNumSta FlexInt `json:"guest-lan-num_sta"` // USW
|
||||
GuestNumSta FlexInt `json:"guest-num_sta"` // USG
|
||||
NumDesktop FlexInt `json:"num_desktop"` // 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.
|
||||
// Not all gateways have all features.
|
||||
type NetworkTable []struct {
|
||||
ID string `json:"_id"`
|
||||
AttrNoDelete FlexBool `json:"attr_no_delete"`
|
||||
AttrHiddenID string `json:"attr_hidden_id"`
|
||||
Name string `json:"name"`
|
||||
SiteID string `json:"site_id"`
|
||||
VlanEnabled FlexBool `json:"vlan_enabled"`
|
||||
Purpose string `json:"purpose"`
|
||||
IPSubnet string `json:"ip_subnet"`
|
||||
Ipv6InterfaceType string `json:"ipv6_interface_type"`
|
||||
DomainName string `json:"domain_name"`
|
||||
IsNat FlexBool `json:"is_nat"`
|
||||
DhcpdEnabled FlexBool `json:"dhcpd_enabled"`
|
||||
DhcpdStart string `json:"dhcpd_start"`
|
||||
DhcpdStop string `json:"dhcpd_stop"`
|
||||
Dhcpdv6Enabled FlexBool `json:"dhcpdv6_enabled"`
|
||||
Ipv6RaEnabled FlexBool `json:"ipv6_ra_enabled"`
|
||||
LteLanEnabled FlexBool `json:"lte_lan_enabled"`
|
||||
AutoScaleEnabled FlexBool `json:"auto_scale_enabled"`
|
||||
Networkgroup string `json:"networkgroup"`
|
||||
DhcpdLeasetime FlexInt `json:"dhcpd_leasetime"`
|
||||
DhcpdDNSEnabled FlexBool `json:"dhcpd_dns_enabled"`
|
||||
DhcpdGatewayEnabled FlexBool `json:"dhcpd_gateway_enabled"`
|
||||
DhcpdTimeOffsetEnabled FlexBool `json:"dhcpd_time_offset_enabled"`
|
||||
Ipv6PdStart string `json:"ipv6_pd_start"`
|
||||
Ipv6PdStop string `json:"ipv6_pd_stop"`
|
||||
DhcpdDNS1 string `json:"dhcpd_dns_1"`
|
||||
DhcpdDNS2 string `json:"dhcpd_dns_2"`
|
||||
DhcpdDNS3 string `json:"dhcpd_dns_3"`
|
||||
DhcpdDNS4 string `json:"dhcpd_dns_4"`
|
||||
Enabled FlexBool `json:"enabled"`
|
||||
DhcpRelayEnabled FlexBool `json:"dhcp_relay_enabled"`
|
||||
Mac string `json:"mac"`
|
||||
IsGuest FlexBool `json:"is_guest"`
|
||||
IP string `json:"ip"`
|
||||
Up FlexBool `json:"up"`
|
||||
ActiveDhcpLeaseCount int `json:"active_dhcp_lease_count"`
|
||||
GatewayInterfaceName string `json:"gateway_interface_name"`
|
||||
DPIStatsTable *DPITable `json:"dpistats_table"`
|
||||
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 {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
|
||||
// UDMStat holds the "stat" data for a dream machine.
|
||||
// A dream machine is a USG + USW + Controller.
|
||||
type UDMStat struct {
|
||||
*Gw `json:"gw"`
|
||||
*Sw `json:"sw"`
|
||||
*Ap `json:"ap,omitempty"`
|
||||
}
|
||||
|
|
@ -0,0 +1,382 @@
|
|||
// Package unifi provides a set of types to unload (unmarshal) Ubiquiti UniFi
|
||||
// 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"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/publicsuffix"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrAuthenticationFailed = fmt.Errorf("authentication failed")
|
||||
ErrInvalidStatusCode = fmt.Errorf("invalid status code from server")
|
||||
ErrNoParams = fmt.Errorf("requested PUT with no parameters")
|
||||
ErrInvalidSignature = fmt.Errorf("certificate signature does not match")
|
||||
)
|
||||
|
||||
// NewUnifi creates a http.Client with authenticated cookies.
|
||||
// Used to make additional, authenticated requests to the APIs.
|
||||
// Start here.
|
||||
func NewUnifi(config *Config) (*Unifi, error) {
|
||||
jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating cookiejar: %w", err)
|
||||
}
|
||||
|
||||
u := newUnifi(config, jar)
|
||||
|
||||
for i, cert := range config.SSLCert {
|
||||
p, _ := pem.Decode(cert)
|
||||
u.fingerprints[i] = fmt.Sprintf("%x", sha256.Sum256(p.Bytes))
|
||||
}
|
||||
|
||||
if err := u.checkNewStyleAPI(); err != nil {
|
||||
return u, err
|
||||
}
|
||||
|
||||
if err := u.Login(); err != nil {
|
||||
return u, err
|
||||
}
|
||||
|
||||
if err := u.GetServerData(); err != nil {
|
||||
return u, fmt.Errorf("unable to get server version: %w", err)
|
||||
}
|
||||
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func newUnifi(config *Config, jar http.CookieJar) *Unifi {
|
||||
config.URL = strings.TrimRight(config.URL, "/")
|
||||
|
||||
if config.ErrorLog == nil {
|
||||
config.ErrorLog = discardLogs
|
||||
}
|
||||
|
||||
if config.DebugLog == nil {
|
||||
config.DebugLog = discardLogs
|
||||
}
|
||||
|
||||
u := &Unifi{
|
||||
Config: config,
|
||||
Client: &http.Client{
|
||||
Timeout: config.Timeout,
|
||||
Jar: jar,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: !config.VerifySSL, // nolint: gosec
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if len(config.SSLCert) > 0 {
|
||||
u.fingerprints = make(fingerprints, len(config.SSLCert))
|
||||
u.Client.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true, // nolint: gosec
|
||||
VerifyPeerCertificate: u.verifyPeerCertificate,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return u
|
||||
}
|
||||
|
||||
func (u *Unifi) verifyPeerCertificate(certs [][]byte, chains [][]*x509.Certificate) error {
|
||||
if len(u.fingerprints) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, cert := range certs {
|
||||
if u.fingerprints.Contains(fmt.Sprintf("%x", sha256.Sum256(cert))) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return ErrInvalidSignature
|
||||
}
|
||||
|
||||
// Login is a helper method. It can be called to grab a new authentication cookie.
|
||||
func (u *Unifi) Login() error {
|
||||
start := time.Now()
|
||||
|
||||
// magic login.
|
||||
req, err := u.UniReq(APILoginPath, fmt.Sprintf(`{"username":"%s","password":"%s"}`, u.User, u.Pass))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := u.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("making request: %w", err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close() // we need no data here.
|
||||
_, _ = io.Copy(ioutil.Discard, resp.Body) // avoid leaking.
|
||||
u.DebugLog("Requested %s: elapsed %v, returned %d bytes",
|
||||
req.URL, time.Since(start).Round(time.Millisecond), resp.ContentLength)
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("(user: %s): %s (status: %s): %w",
|
||||
u.User, req.URL, resp.Status, ErrAuthenticationFailed)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Logout closes the current session.
|
||||
func (u *Unifi) Logout() error {
|
||||
// a post is needed for logout
|
||||
_, err := u.PostJSON(APILogoutPath)
|
||||
return err
|
||||
}
|
||||
|
||||
// with the release of controller version 5.12.55 on UDM in Jan 2020 the api paths
|
||||
// changed and broke this library. This function runs when `NewUnifi()` is called to
|
||||
// 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.
|
||||
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)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", u.URL+"/", nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating request: %w", err)
|
||||
}
|
||||
|
||||
// We can't share these cookies with other requests, so make a new client.
|
||||
// Checking the return code on the first request so don't follow a redirect.
|
||||
client := &http.Client{
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: !u.VerifySSL}, // nolint: gosec
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("making request: %w", err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close() // we need no data here.
|
||||
_, _ = io.Copy(ioutil.Discard, resp.Body) // avoid leaking.
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
// The new version returns a "200" for a / request.
|
||||
u.new = true
|
||||
u.DebugLog("Using NEW UniFi controller API paths for %s", req.URL)
|
||||
}
|
||||
|
||||
// The old version returns a "302" (to /manage) for a / request
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetServerData sets the controller's version and UUID. Only call this if you
|
||||
// previously called Login and suspect the controller version has changed.
|
||||
func (u *Unifi) GetServerData() error {
|
||||
var response struct {
|
||||
Data server `json:"meta"`
|
||||
}
|
||||
|
||||
u.server = &response.Data
|
||||
|
||||
return u.GetData(APIStatusPath, &response)
|
||||
}
|
||||
|
||||
// GetData makes a unifi request and unmarshals the response into a provided pointer.
|
||||
func (u *Unifi) GetData(apiPath string, v interface{}, params ...string) error {
|
||||
start := time.Now()
|
||||
|
||||
body, err := u.GetJSON(apiPath, params...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u.DebugLog("Requested %s: elapsed %v, returned %d bytes",
|
||||
u.URL+u.path(apiPath), time.Since(start).Round(time.Millisecond), len(body))
|
||||
|
||||
return json.Unmarshal(body, v)
|
||||
}
|
||||
|
||||
// PutData makes a unifi request and unmarshals the response into a provided pointer.
|
||||
func (u *Unifi) PutData(apiPath string, v interface{}, params ...string) error {
|
||||
start := time.Now()
|
||||
|
||||
body, err := u.PutJSON(apiPath, params...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u.DebugLog("Requested %s: elapsed %v, returned %d bytes",
|
||||
u.URL+u.path(apiPath), time.Since(start).Round(time.Millisecond), len(body))
|
||||
|
||||
return json.Unmarshal(body, v)
|
||||
}
|
||||
|
||||
// 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) (*http.Request, error) {
|
||||
var (
|
||||
req *http.Request
|
||||
err error
|
||||
)
|
||||
|
||||
switch apiPath = u.path(apiPath); params {
|
||||
case "":
|
||||
req, err = http.NewRequest(http.MethodGet, u.URL+apiPath, nil)
|
||||
default:
|
||||
req, err = http.NewRequest(http.MethodPost, u.URL+apiPath, bytes.NewBufferString(params))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating request: %w", err)
|
||||
}
|
||||
|
||||
u.setHeaders(req, params)
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// UniReqPut is the Put call equivalent to UniReq.
|
||||
func (u *Unifi) UniReqPut(apiPath string, params string) (*http.Request, error) {
|
||||
if params == "" {
|
||||
return nil, ErrNoParams
|
||||
}
|
||||
|
||||
apiPath = u.path(apiPath)
|
||||
|
||||
req, err := http.NewRequest(http.MethodPut, u.URL+apiPath, bytes.NewBufferString(params)) //nolint:noctx
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating request: %w", err)
|
||||
}
|
||||
|
||||
u.setHeaders(req, params)
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// UniReqPost is the Post call equivalent to UniReq.
|
||||
func (u *Unifi) UniReqPost(apiPath string, params string) (*http.Request, error) {
|
||||
apiPath = u.path(apiPath)
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, u.URL+apiPath, bytes.NewBufferString(params)) //nolint:noctx
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating request: %w", err)
|
||||
}
|
||||
|
||||
u.setHeaders(req, params)
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// GetJSON returns the raw JSON from a path. This is useful for debugging.
|
||||
func (u *Unifi) GetJSON(apiPath string, params ...string) ([]byte, error) {
|
||||
req, err := u.UniReq(apiPath, strings.Join(params, " "))
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
return u.do(req)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (u *Unifi) PutJSON(apiPath string, params ...string) ([]byte, error) {
|
||||
req, err := u.UniReqPut(apiPath, strings.Join(params, " "))
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
return u.do(req)
|
||||
}
|
||||
|
||||
// PostJSON uses a POST call and returns the raw JSON in the same way as GetData
|
||||
// Use this if you want to change data via the REST API.
|
||||
func (u *Unifi) PostJSON(apiPath string, params ...string) ([]byte, error) {
|
||||
req, err := u.UniReqPost(apiPath, strings.Join(params, " "))
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
return u.do(req)
|
||||
}
|
||||
|
||||
func (u *Unifi) do(req *http.Request) ([]byte, error) {
|
||||
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 {
|
||||
return []byte{}, fmt.Errorf("making request: %w", err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return body, fmt.Errorf("reading response: %w", err)
|
||||
}
|
||||
|
||||
// Save the returned CSRF header.
|
||||
if csrf := resp.Header.Get("x-csrf-token"); csrf != "" {
|
||||
u.csrf = resp.Header.Get("x-csrf-token")
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
err = fmt.Errorf("%s: %s: %w", req.URL, resp.Status, ErrInvalidStatusCode)
|
||||
}
|
||||
|
||||
return body, err
|
||||
}
|
||||
|
||||
func (u *Unifi) setHeaders(req *http.Request, params string) {
|
||||
// Add the saved CSRF header.
|
||||
req.Header.Set("X-CSRF-Token", u.csrf)
|
||||
req.Header.Add("Accept", "application/json")
|
||||
req.Header.Add("Content-Type", "application/json; charset=utf-8")
|
||||
|
||||
if u.Client.Jar != nil {
|
||||
parsedURL, _ := url.Parse(req.URL.String())
|
||||
u.DebugLog("Requesting %s, with params: %v, cookies: %d", req.URL, params != "", len(u.Client.Jar.Cookies(parsedURL)))
|
||||
} else {
|
||||
u.DebugLog("Requesting %s, with params: %v,", req.URL, params != "")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package unifi // nolint: testpackage
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewUnifi(t *testing.T) {
|
||||
t.Parallel()
|
||||
a := assert.New(t)
|
||||
u := "http://127.0.0.1:64431"
|
||||
c := &Config{
|
||||
User: "user1",
|
||||
Pass: "pass2",
|
||||
URL: u,
|
||||
VerifySSL: false,
|
||||
DebugLog: discardLogs,
|
||||
}
|
||||
authReq, err := NewUnifi(c)
|
||||
a.NotNil(err)
|
||||
a.EqualValues(u, authReq.URL)
|
||||
a.Contains(err.Error(), "connection refused", "an invalid destination should produce a connection error.")
|
||||
}
|
||||
|
||||
func TestUniReq(t *testing.T) {
|
||||
t.Parallel()
|
||||
a := assert.New(t)
|
||||
p := "/test/path"
|
||||
u := "http://some.url:8443"
|
||||
|
||||
// Test empty parameters.
|
||||
authReq := &Unifi{Client: &http.Client{}, Config: &Config{URL: u, DebugLog: discardLogs}}
|
||||
r, err := authReq.UniReq(p, "")
|
||||
|
||||
a.Nil(err, "newrequest must not produce an error")
|
||||
a.EqualValues(p, r.URL.Path,
|
||||
"the provided apiPath was not added to http request")
|
||||
a.EqualValues(u, r.URL.Scheme+"://"+r.URL.Host, "URL improperly encoded")
|
||||
a.EqualValues("GET", r.Method, "without parameters the method must be GET")
|
||||
a.EqualValues("application/json", r.Header.Get("Accept"), "Accept header must be set to application/json")
|
||||
|
||||
// Test with parameters
|
||||
k := "key1=value9&key2=value7"
|
||||
authReq = &Unifi{Client: &http.Client{}, Config: &Config{URL: "http://some.url:8443", DebugLog: discardLogs}}
|
||||
r, err = authReq.UniReq(p, k)
|
||||
a.Nil(err, "newrequest must not produce an error")
|
||||
|
||||
a.EqualValues(p, r.URL.Path,
|
||||
"the provided apiPath was not added to http request")
|
||||
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("application/json", r.Header.Get("Accept"), "Accept header must be set to application/json")
|
||||
|
||||
// Check the parameters.
|
||||
d, err := ioutil.ReadAll(r.Body)
|
||||
a.Nil(err, "problem reading request body, POST parameters may be malformed")
|
||||
a.EqualValues(k, string(d), "POST parameters improperly encoded")
|
||||
}
|
||||
|
||||
func TestUniReqPut(t *testing.T) {
|
||||
t.Parallel()
|
||||
a := assert.New(t)
|
||||
p := "/test/path"
|
||||
u := "http://some.url:8443"
|
||||
|
||||
// Test empty parameters.
|
||||
authReq := &Unifi{Client: &http.Client{}, Config: &Config{URL: u, DebugLog: discardLogs}}
|
||||
_, err := authReq.UniReqPut(p, "")
|
||||
a.NotNil(err, "empty params must produce an error")
|
||||
|
||||
// Test with parameters
|
||||
k := "key1=value9&key2=value7"
|
||||
authReq = &Unifi{Client: &http.Client{}, Config: &Config{URL: "http://some.url:8443", DebugLog: discardLogs}}
|
||||
r, err := authReq.UniReqPut(p, k)
|
||||
a.Nil(err, "newrequest must not produce an error")
|
||||
|
||||
a.EqualValues(p, r.URL.Path,
|
||||
"the provided apiPath was not added to http request")
|
||||
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("application/json", r.Header.Get("Accept"), "Accept header must be set to application/json")
|
||||
|
||||
// Check the parameters.
|
||||
d, err := ioutil.ReadAll(r.Body)
|
||||
a.Nil(err, "problem reading request body, PUT parameters may be malformed")
|
||||
a.EqualValues(k, string(d), "PUT parameters improperly encoded")
|
||||
}
|
||||
|
||||
/* NOT DONE: OPEN web server, check parameters posted, more. These tests are incomplete.
|
||||
a.EqualValues(`{"username": "user1","password": "pass2"}`, string(post_params),
|
||||
"user/pass json parameters improperly encoded")
|
||||
*/
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package unifi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GetUsers returns a response full of clients that connected to the UDM within the provided amount of time
|
||||
// using the insight historical connection data set.
|
||||
func (u *Unifi) GetUsers(sites []*Site, hours int) ([]*User, error) {
|
||||
data := make([]*User, 0)
|
||||
|
||||
for _, site := range sites {
|
||||
var (
|
||||
response struct {
|
||||
Data []*User `json:"data"`
|
||||
}
|
||||
params = fmt.Sprintf(`{ "type": "all:", "conn": "all", "within":%d }`, hours)
|
||||
)
|
||||
|
||||
u.DebugLog("Polling Controller, retrieving UniFi Users, site %s ", site.SiteName)
|
||||
|
||||
clientPath := fmt.Sprintf(APIAllUserPath, site.Name)
|
||||
if err := u.GetData(clientPath, &response, params); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, d := range response.Data {
|
||||
// Add special SourceName value.
|
||||
response.Data[i].SourceName = u.URL
|
||||
// Add the special "Site Name" to each client. This becomes a Grafana filter somewhere.
|
||||
response.Data[i].SiteName = site.SiteName
|
||||
// Fix name and hostname fields. Sometimes one or the other is blank.
|
||||
response.Data[i].Hostname = strings.TrimSpace(pick(d.Hostname, d.Name, d.Mac))
|
||||
response.Data[i].Name = strings.TrimSpace(pick(d.Name, d.Hostname))
|
||||
}
|
||||
|
||||
data = append(data, response.Data...)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// User defines the metadata available for previously connected clients.
|
||||
type User struct {
|
||||
SourceName string `json:"-"`
|
||||
SiteName string `json:"-"`
|
||||
ID string `json:"_id"`
|
||||
Mac string `json:"mac"`
|
||||
SiteID string `json:"site_id"`
|
||||
Oui string `json:"oui,omitempty"`
|
||||
IsGuest bool `json:"is_guest"`
|
||||
FirstSeen FlexInt `json:"first_seen,omitempty"`
|
||||
LastSeen FlexInt `json:"last_seen,omitempty"`
|
||||
IsWired bool `json:"is_wired,omitempty"`
|
||||
Hostname string `json:"hostname,omitempty"`
|
||||
Duration FlexInt `json:"duration,omitempty"`
|
||||
TxBytes FlexInt `json:"tx_bytes,omitempty"`
|
||||
TxPackets FlexInt `json:"tx_packets,omitempty"`
|
||||
RxBytes FlexInt `json:"rx_bytes,omitempty"`
|
||||
RxPackets FlexInt `json:"rx_packets,omitempty"`
|
||||
WifiTxAttempts FlexInt `json:"wifi_tx_attempts,omitempty"`
|
||||
TxRetries FlexInt `json:"tx_retries,omitempty"`
|
||||
UsergroupID string `json:"usergroup_id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Note string `json:"note,omitempty"`
|
||||
Noted FlexBool `json:"noted,omitempty"`
|
||||
Blocked FlexBool `json:"blocked,omitempty"`
|
||||
DevIDOverride FlexInt `json:"dev_id_override,omitempty"`
|
||||
FingerprintOverride FlexBool `json:"fingerprint_override,omitempty"`
|
||||
}
|
||||
|
|
@ -0,0 +1,252 @@
|
|||
package unifi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
// USG represents all the data from the Ubiquiti Controller for a Unifi Security Gateway.
|
||||
type USG struct {
|
||||
site *Site
|
||||
SourceName string `json:"-"`
|
||||
ID string `json:"_id"`
|
||||
Adopted FlexBool `json:"adopted"`
|
||||
Cfgversion string `json:"cfgversion"`
|
||||
ConfigNetwork *ConfigNetwork `json:"config_network"`
|
||||
EthernetTable []*EthernetTable `json:"ethernet_table"`
|
||||
FwCaps FlexInt `json:"fw_caps"`
|
||||
InformIP string `json:"inform_ip"`
|
||||
InformURL string `json:"inform_url"`
|
||||
IP string `json:"ip"`
|
||||
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"`
|
||||
Serial string `json:"serial"`
|
||||
SiteID string `json:"site_id"`
|
||||
SiteName string `json:"-"`
|
||||
Type string `json:"type"`
|
||||
UsgCaps FlexInt `json:"usg_caps"`
|
||||
Version string `json:"version"`
|
||||
RequiredVersion string `json:"required_version"`
|
||||
EthernetOverrides []*EthernetOverrides `json:"ethernet_overrides"`
|
||||
HwCaps FlexInt `json:"hw_caps"`
|
||||
BoardRev FlexInt `json:"board_rev"`
|
||||
Unsupported FlexBool `json:"unsupported"`
|
||||
UnsupportedReason FlexInt `json:"unsupported_reason"`
|
||||
DeviceID string `json:"device_id"`
|
||||
State FlexInt `json:"state"`
|
||||
LastSeen FlexInt `json:"last_seen"`
|
||||
Upgradable FlexBool `json:"upgradable"`
|
||||
AdoptableWhenUpgraded FlexBool `json:"adoptable_when_upgraded"`
|
||||
Rollupgrade FlexBool `json:"rollupgrade"`
|
||||
KnownCfgversion string `json:"known_cfgversion"`
|
||||
Uptime FlexInt `json:"uptime"`
|
||||
Locating FlexBool `json:"locating"`
|
||||
ConnectRequestIP string `json:"connect_request_ip"`
|
||||
ConnectRequestPort string `json:"connect_request_port"`
|
||||
SysStats SysStats `json:"sys_stats"`
|
||||
Temperatures []Temperature `json:"temperatures,omitempty"`
|
||||
SystemStats SystemStats `json:"system-stats"`
|
||||
GuestToken string `json:"guest_token"`
|
||||
SpeedtestStatus SpeedtestStatus `json:"speedtest-status"`
|
||||
SpeedtestStatusSaved FlexBool `json:"speedtest-status-saved"`
|
||||
Wan1 Wan `json:"wan1"`
|
||||
Wan2 Wan `json:"wan2"`
|
||||
PortTable []*Port `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.
|
||||
type Uplink struct {
|
||||
BytesR FlexInt `json:"bytes-r"`
|
||||
Drops FlexInt `json:"drops"`
|
||||
Enable FlexBool `json:"enable,omitempty"`
|
||||
FullDuplex FlexBool `json:"full_duplex"`
|
||||
Gateways []string `json:"gateways,omitempty"`
|
||||
IP string `json:"ip"`
|
||||
Latency FlexInt `json:"latency"`
|
||||
Mac string `json:"mac,omitempty"`
|
||||
MaxSpeed FlexInt `json:"max_speed"`
|
||||
Name string `json:"name"`
|
||||
Nameservers []string `json:"nameservers"`
|
||||
Netmask string `json:"netmask"`
|
||||
NumPort FlexInt `json:"num_port"`
|
||||
Media string `json:"media"`
|
||||
PortIdx FlexInt `json:"port_idx"`
|
||||
RxBytes FlexInt `json:"rx_bytes"`
|
||||
RxBytesR FlexInt `json:"rx_bytes-r"`
|
||||
RxDropped FlexInt `json:"rx_dropped"`
|
||||
RxErrors FlexInt `json:"rx_errors"`
|
||||
RxMulticast FlexInt `json:"rx_multicast"`
|
||||
RxPackets FlexInt `json:"rx_packets"`
|
||||
RxRate FlexInt `json:"rx_rate"`
|
||||
Speed FlexInt `json:"speed"`
|
||||
SpeedtestLastrun FlexInt `json:"speedtest_lastrun,omitempty"`
|
||||
SpeedtestPing FlexInt `json:"speedtest_ping,omitempty"`
|
||||
SpeedtestStatus string `json:"speedtest_status,omitempty"`
|
||||
TxBytes FlexInt `json:"tx_bytes"`
|
||||
TxBytesR FlexInt `json:"tx_bytes-r"`
|
||||
TxDropped FlexInt `json:"tx_dropped"`
|
||||
TxErrors FlexInt `json:"tx_errors"`
|
||||
TxPackets FlexInt `json:"tx_packets"`
|
||||
TxRate FlexInt `json:"tx_rate"`
|
||||
Type string `json:"type"`
|
||||
Up FlexBool `json:"up"`
|
||||
Uptime FlexInt `json:"uptime"`
|
||||
XputDown FlexInt `json:"xput_down,omitempty"`
|
||||
XputUp FlexInt `json:"xput_up,omitempty"`
|
||||
}
|
||||
|
||||
// Wan is a Wan interface on a USG or UDM.
|
||||
type Wan struct {
|
||||
Autoneg FlexBool `json:"autoneg"`
|
||||
BytesR FlexInt `json:"bytes-r"`
|
||||
DNS []string `json:"dns"` // may be deprecated
|
||||
Enable FlexBool `json:"enable"`
|
||||
FlowctrlRx FlexBool `json:"flowctrl_rx"`
|
||||
FlowctrlTx FlexBool `json:"flowctrl_tx"`
|
||||
FullDuplex FlexBool `json:"full_duplex"`
|
||||
Gateway string `json:"gateway"` // may be deprecated
|
||||
IP string `json:"ip"`
|
||||
Ifname string `json:"ifname"`
|
||||
IsUplink FlexBool `json:"is_uplink"`
|
||||
Mac string `json:"mac"`
|
||||
MaxSpeed FlexInt `json:"max_speed"`
|
||||
Media string `json:"media"`
|
||||
Name string `json:"name"`
|
||||
Netmask string `json:"netmask"` // may be deprecated
|
||||
NumPort int `json:"num_port"`
|
||||
PortIdx int `json:"port_idx"`
|
||||
PortPoe FlexBool `json:"port_poe"`
|
||||
RxBroadcast FlexInt `json:"rx_broadcast"`
|
||||
RxBytes FlexInt `json:"rx_bytes"`
|
||||
RxBytesR FlexInt `json:"rx_bytes-r"`
|
||||
RxDropped FlexInt `json:"rx_dropped"`
|
||||
RxErrors FlexInt `json:"rx_errors"`
|
||||
RxMulticast FlexInt `json:"rx_multicast"`
|
||||
RxPackets FlexInt `json:"rx_packets"`
|
||||
RxRate FlexInt `json:"rx_rate"`
|
||||
Speed FlexInt `json:"speed"`
|
||||
SpeedCaps FlexInt `json:"speed_caps"`
|
||||
TxBroadcast FlexInt `json:"tx_broadcast"`
|
||||
TxBytes FlexInt `json:"tx_bytes"`
|
||||
TxBytesR FlexInt `json:"tx_bytes-r"`
|
||||
TxDropped FlexInt `json:"tx_dropped"`
|
||||
TxErrors FlexInt `json:"tx_errors"`
|
||||
TxMulticast FlexInt `json:"tx_multicast"`
|
||||
TxPackets FlexInt `json:"tx_packets"`
|
||||
TxRate FlexInt `json:"tx_rate"`
|
||||
Type string `json:"type"`
|
||||
Up FlexBool `json:"up"`
|
||||
}
|
||||
|
||||
// SpeedtestStatus is the speed test info on a USG or UDM.
|
||||
type SpeedtestStatus struct {
|
||||
Latency FlexInt `json:"latency"`
|
||||
Rundate FlexInt `json:"rundate"`
|
||||
Runtime FlexInt `json:"runtime"`
|
||||
ServerDesc string `json:"server_desc,omitempty"`
|
||||
Server *SpeedtestServer `json:"server"`
|
||||
SourceInterface string `json:"source_interface"`
|
||||
StatusDownload FlexInt `json:"status_download"`
|
||||
StatusPing FlexInt `json:"status_ping"`
|
||||
StatusSummary FlexInt `json:"status_summary"`
|
||||
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.
|
||||
type SystemStats struct {
|
||||
CPU FlexInt `json:"cpu"`
|
||||
Mem FlexInt `json:"mem"`
|
||||
Uptime FlexInt `json:"uptime"`
|
||||
// This exists on at least USG4, may others, maybe not.
|
||||
// {"Board (CPU)":"51 C","Board (PHY)":"51 C","CPU":"72 C","PHY":"77 C"}
|
||||
Temps map[string]string `json:"temps,omitempty"`
|
||||
}
|
||||
|
||||
// SysStats is load info for a UDM, USG, USW.
|
||||
type SysStats struct {
|
||||
Loadavg1 FlexInt `json:"loadavg_1"`
|
||||
Loadavg15 FlexInt `json:"loadavg_15"`
|
||||
Loadavg5 FlexInt `json:"loadavg_5"`
|
||||
MemBuffer FlexInt `json:"mem_buffer"`
|
||||
MemTotal FlexInt `json:"mem_total"`
|
||||
MemUsed FlexInt `json:"mem_used"`
|
||||
}
|
||||
|
||||
// USGStat holds the "stat" data for a gateway.
|
||||
// This is split out because of a JSON data format change from 5.10 to 5.11.
|
||||
type USGStat struct {
|
||||
*Gw
|
||||
}
|
||||
|
||||
// Gw is a subtype of USGStat to make unmarshalling of different controller versions possible.
|
||||
type Gw struct {
|
||||
SiteID string `json:"site_id"`
|
||||
O string `json:"o"`
|
||||
Oid string `json:"oid"`
|
||||
Gw string `json:"gw"`
|
||||
Time FlexInt `json:"time"`
|
||||
Datetime time.Time `json:"datetime"`
|
||||
Duration FlexInt `json:"duration"`
|
||||
WanRxPackets FlexInt `json:"wan-rx_packets"`
|
||||
WanRxBytes FlexInt `json:"wan-rx_bytes"`
|
||||
WanRxDropped FlexInt `json:"wan-rx_dropped"`
|
||||
WanTxPackets FlexInt `json:"wan-tx_packets"`
|
||||
WanTxBytes FlexInt `json:"wan-tx_bytes"`
|
||||
LanRxPackets FlexInt `json:"lan-rx_packets"`
|
||||
LanRxBytes FlexInt `json:"lan-rx_bytes"`
|
||||
LanTxPackets FlexInt `json:"lan-tx_packets"`
|
||||
LanTxBytes FlexInt `json:"lan-tx_bytes"`
|
||||
LanRxDropped FlexInt `json:"lan-rx_dropped"`
|
||||
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.
|
||||
func (v *USGStat) UnmarshalJSON(data []byte) error {
|
||||
var n struct {
|
||||
Gw `json:"gw"`
|
||||
}
|
||||
|
||||
v.Gw = &n.Gw
|
||||
|
||||
err := json.Unmarshal(data, v.Gw) // controller version 5.10.
|
||||
if err != nil {
|
||||
return json.Unmarshal(data, &n) // controller version 5.11.
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package unifi // nolint: testpackage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUSGUnmarshalJSON(t *testing.T) {
|
||||
testcontroller511 := `{
|
||||
"gw": {
|
||||
"site_id": "mySite",
|
||||
"o": "gw",
|
||||
"oid": "00:00:00:00:00:00",
|
||||
"gw": "00:00:00:00:00:00",
|
||||
"time": 1577742600000,
|
||||
"datetime": "2019-12-30T09:50:00Z",
|
||||
"bytes": 0,
|
||||
"duration": 3590568000,
|
||||
"wan-rx_packets": 299729434558,
|
||||
"wan-rx_bytes": 299882768958208,
|
||||
"wan-tx_packets": 249639259523,
|
||||
"wan-tx_bytes": 169183252492369,
|
||||
"lan-rx_packets": 78912349453,
|
||||
"lan-rx_bytes": 37599596992669,
|
||||
"lan-tx_packets": 12991234992,
|
||||
"lan-tx_bytes": 11794664098210}}`
|
||||
|
||||
testcontroller510 := `{
|
||||
"site_id": "mySite",
|
||||
"o": "gw",
|
||||
"oid": "00:00:00:00:00:00",
|
||||
"gw": "00:00:00:00:00:00",
|
||||
"time": 1577742600000,
|
||||
"datetime": "2019-12-30T09:50:00Z",
|
||||
"bytes": 0,
|
||||
"duration": 3590568000,
|
||||
"wan-rx_packets": 299729434558,
|
||||
"wan-rx_bytes": 299882768958208,
|
||||
"wan-tx_packets": 249639259523,
|
||||
"wan-tx_bytes": 169183252492369,
|
||||
"lan-rx_packets": 78912349453,
|
||||
"lan-rx_bytes": 37599596992669,
|
||||
"lan-tx_packets": 12991234992,
|
||||
"lan-tx_bytes": 11794664098210}`
|
||||
|
||||
t.Parallel()
|
||||
a := assert.New(t)
|
||||
|
||||
u := &USGStat{}
|
||||
lanRx := 37599596992669
|
||||
err := u.UnmarshalJSON([]byte(testcontroller510))
|
||||
a.Nil(err, "must be no error unmarshaling test strings")
|
||||
a.Equal(float64(lanRx), u.LanRxBytes.Val, "data was not properly unmarshaled")
|
||||
|
||||
u = &USGStat{} // reset
|
||||
err = u.UnmarshalJSON([]byte(testcontroller511))
|
||||
a.Nil(err, "must be no error unmarshaling test strings")
|
||||
a.Equal(float64(lanRx), u.LanRxBytes.Val, "data was not properly unmarshaled")
|
||||
}
|
||||
|
|
@ -0,0 +1,400 @@
|
|||
package unifi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
// USW represents all the data from the Ubiquiti Controller for a Unifi Switch.
|
||||
type USW struct {
|
||||
site *Site
|
||||
SourceName string `json:"-"`
|
||||
SiteName string `json:"-"`
|
||||
ID string `json:"_id"`
|
||||
Adopted FlexBool `json:"adopted"`
|
||||
BoardRev FlexInt `json:"board_rev"`
|
||||
Cfgversion string `json:"cfgversion"`
|
||||
ConfigNetwork *ConfigNetwork `json:"config_network"`
|
||||
Dot1XPortctrlEnabled FlexBool `json:"dot1x_portctrl_enabled"`
|
||||
EthernetTable []*EthernetTable `json:"ethernet_table"`
|
||||
FlowctrlEnabled FlexBool `json:"flowctrl_enabled"`
|
||||
FwCaps FlexInt `json:"fw_caps"`
|
||||
HasFan FlexBool `json:"has_fan"`
|
||||
HasTemperature FlexBool `json:"has_temperature"`
|
||||
InformIP string `json:"inform_ip"`
|
||||
InformURL string `json:"inform_url"`
|
||||
IP string `json:"ip"`
|
||||
JumboframeEnabled FlexBool `json:"jumboframe_enabled"`
|
||||
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"`
|
||||
PoeMode string `json:"poe_mode,omitempty"`
|
||||
PortIdx FlexInt `json:"port_idx"`
|
||||
PortconfID string `json:"portconf_id"`
|
||||
} `json:"port_overrides"`
|
||||
PortTable []Port `json:"port_table"`
|
||||
Serial string `json:"serial"`
|
||||
SiteID string `json:"site_id"`
|
||||
StpPriority FlexInt `json:"stp_priority"`
|
||||
StpVersion string `json:"stp_version"`
|
||||
Type string `json:"type"`
|
||||
Version string `json:"version"`
|
||||
RequiredVersion string `json:"required_version"`
|
||||
SwitchCaps *SwitchCaps `json:"switch_caps"`
|
||||
HwCaps FlexInt `json:"hw_caps"`
|
||||
Unsupported FlexBool `json:"unsupported"`
|
||||
UnsupportedReason FlexInt `json:"unsupported_reason"`
|
||||
SysErrorCaps FlexInt `json:"sys_error_caps"`
|
||||
DeviceID string `json:"device_id"`
|
||||
State FlexInt `json:"state"`
|
||||
LastSeen FlexInt `json:"last_seen"`
|
||||
Upgradable FlexBool `json:"upgradable,omitempty"`
|
||||
AdoptableWhenUpgraded FlexBool `json:"adoptable_when_upgraded,omitempty"`
|
||||
Rollupgrade FlexBool `json:"rollupgrade,omitempty"`
|
||||
KnownCfgversion string `json:"known_cfgversion"`
|
||||
Uptime FlexInt `json:"uptime"`
|
||||
Locating FlexBool `json:"locating"`
|
||||
ConnectRequestIP string `json:"connect_request_ip"`
|
||||
ConnectRequestPort string `json:"connect_request_port"`
|
||||
SysStats SysStats `json:"sys_stats"`
|
||||
SystemStats SystemStats `json:"system-stats"`
|
||||
FanLevel FlexInt `json:"fan_level"`
|
||||
GeneralTemperature FlexInt `json:"general_temperature"`
|
||||
Overheating FlexBool `json:"overheating"`
|
||||
TotalMaxPower FlexInt `json:"total_max_power"`
|
||||
DownlinkTable []*DownlinkTable `json:"downlink_table"`
|
||||
Uplink Uplink `json:"uplink"`
|
||||
LastUplink struct {
|
||||
UplinkMac string `json:"uplink_mac"`
|
||||
} `json:"last_uplink"`
|
||||
UplinkDepth FlexInt `json:"uplink_depth"`
|
||||
Stat USWStat `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"`
|
||||
}
|
||||
|
||||
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 {
|
||||
AggregatedBy FlexBool `json:"aggregated_by"`
|
||||
Autoneg FlexBool `json:"autoneg,omitempty"`
|
||||
BytesR FlexInt `json:"bytes-r"`
|
||||
DNS []string `json:"dns,omitempty"`
|
||||
Dot1XMode string `json:"dot1x_mode"`
|
||||
Dot1XStatus string `json:"dot1x_status"`
|
||||
Enable FlexBool `json:"enable"`
|
||||
FlowctrlRx FlexBool `json:"flowctrl_rx"`
|
||||
FlowctrlTx FlexBool `json:"flowctrl_tx"`
|
||||
FullDuplex FlexBool `json:"full_duplex"`
|
||||
IP string `json:"ip,omitempty"`
|
||||
Ifname string `json:"ifname,omitempty"`
|
||||
IsUplink FlexBool `json:"is_uplink"`
|
||||
Mac string `json:"mac,omitempty"`
|
||||
MacTable []MacTable `json:"mac_table,omitempty"`
|
||||
Jumbo FlexBool `json:"jumbo,omitempty"`
|
||||
Masked FlexBool `json:"masked"`
|
||||
Media string `json:"media"`
|
||||
Name string `json:"name"`
|
||||
NetworkName string `json:"network_name,omitempty"`
|
||||
Netmask string `json:"netmask,omitempty"`
|
||||
NumPort int `json:"num_port,omitempty"`
|
||||
OpMode string `json:"op_mode"`
|
||||
PoeCaps FlexInt `json:"poe_caps"`
|
||||
PoeClass string `json:"poe_class,omitempty"`
|
||||
PoeCurrent FlexInt `json:"poe_current,omitempty"`
|
||||
PoeEnable FlexBool `json:"poe_enable,omitempty"`
|
||||
PoeGood FlexBool `json:"poe_good,omitempty"`
|
||||
PoeMode string `json:"poe_mode,omitempty"`
|
||||
PoePower FlexInt `json:"poe_power,omitempty"`
|
||||
PoeVoltage FlexInt `json:"poe_voltage,omitempty"`
|
||||
PortDelta PortDelta `json:"port_delta,omitempty"`
|
||||
PortIdx FlexInt `json:"port_idx"`
|
||||
PortPoe FlexBool `json:"port_poe"`
|
||||
PortconfID string `json:"portconf_id"`
|
||||
RxBroadcast FlexInt `json:"rx_broadcast"`
|
||||
RxBytes FlexInt `json:"rx_bytes"`
|
||||
RxBytesR FlexInt `json:"rx_bytes-r"`
|
||||
RxDropped FlexInt `json:"rx_dropped"`
|
||||
RxErrors FlexInt `json:"rx_errors"`
|
||||
RxMulticast FlexInt `json:"rx_multicast"`
|
||||
RxPackets FlexInt `json:"rx_packets"`
|
||||
RxRate FlexInt `json:"rx_rate,omitempty"`
|
||||
Satisfaction FlexInt `json:"satisfaction,omitempty"`
|
||||
SatisfactionReason FlexInt `json:"satisfaction_reason"`
|
||||
SFPCompliance string `json:"sfp_compliance"`
|
||||
SFPCurrent FlexInt `json:"sfp_current"`
|
||||
SFPFound FlexBool `json:"sfp_found"`
|
||||
SFPPart string `json:"sfp_part"`
|
||||
SFPRev string `json:"sfp_rev"`
|
||||
SFPRxfault FlexBool `json:"sfp_rxfault"`
|
||||
SFPRxpower FlexInt `json:"sfp_rxpower"`
|
||||
SFPSerial string `json:"sfp_serial"`
|
||||
SFPTemperature FlexInt `json:"sfp_temperature"`
|
||||
SFPTxfault FlexBool `json:"sfp_txfault"`
|
||||
SFPTxpower FlexInt `json:"sfp_txpower"`
|
||||
SFPVendor string `json:"sfp_vendor"`
|
||||
SFPVoltage FlexInt `json:"sfp_voltage"`
|
||||
Speed FlexInt `json:"speed"`
|
||||
SpeedCaps FlexInt `json:"speed_caps"`
|
||||
StpPathcost FlexInt `json:"stp_pathcost"`
|
||||
StpState string `json:"stp_state"`
|
||||
TxBroadcast FlexInt `json:"tx_broadcast"`
|
||||
TxBytes FlexInt `json:"tx_bytes"`
|
||||
TxBytesR FlexInt `json:"tx_bytes-r"`
|
||||
TxDropped FlexInt `json:"tx_dropped"`
|
||||
TxErrors FlexInt `json:"tx_errors"`
|
||||
TxMulticast FlexInt `json:"tx_multicast"`
|
||||
TxPackets FlexInt `json:"tx_packets"`
|
||||
TxRate FlexInt `json:"tx_rate,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Up FlexBool `json:"up"`
|
||||
}
|
||||
|
||||
// PortDelta is part of a Port.
|
||||
type PortDelta struct {
|
||||
TimeDelta FlexInt `json:"time_delta"`
|
||||
TimeDeltaActivity FlexInt `json:"time_delta_activity"`
|
||||
}
|
||||
|
||||
// USWStat holds the "stat" data for a switch.
|
||||
// This is split out because of a JSON data format change from 5.10 to 5.11.
|
||||
type USWStat struct {
|
||||
*Sw
|
||||
}
|
||||
|
||||
// Sw is a subtype of USWStat to make unmarshalling of different controller versions possible.
|
||||
type Sw struct {
|
||||
SiteID string `json:"site_id"`
|
||||
O string `json:"o"`
|
||||
Oid string `json:"oid"`
|
||||
Sw string `json:"sw"`
|
||||
Time FlexInt `json:"time"`
|
||||
Datetime time.Time `json:"datetime"`
|
||||
RxPackets FlexInt `json:"rx_packets"`
|
||||
RxBytes FlexInt `json:"rx_bytes"`
|
||||
RxErrors FlexInt `json:"rx_errors"`
|
||||
RxDropped FlexInt `json:"rx_dropped"`
|
||||
RxCrypts FlexInt `json:"rx_crypts"`
|
||||
RxFrags FlexInt `json:"rx_frags"`
|
||||
TxPackets FlexInt `json:"tx_packets"`
|
||||
TxBytes FlexInt `json:"tx_bytes"`
|
||||
TxErrors FlexInt `json:"tx_errors"`
|
||||
TxDropped FlexInt `json:"tx_dropped"`
|
||||
TxRetries FlexInt `json:"tx_retries"`
|
||||
RxMulticast FlexInt `json:"rx_multicast"`
|
||||
RxBroadcast FlexInt `json:"rx_broadcast"`
|
||||
TxMulticast FlexInt `json:"tx_multicast"`
|
||||
TxBroadcast FlexInt `json:"tx_broadcast"`
|
||||
Bytes FlexInt `json:"bytes"`
|
||||
Duration FlexInt `json:"duration"`
|
||||
/* These are all in port table */
|
||||
/*
|
||||
Port1RxPackets FlexInt `json:"port_1-rx_packets,omitempty"`
|
||||
Port1RxBytes FlexInt `json:"port_1-rx_bytes,omitempty"`
|
||||
Port1TxPackets FlexInt `json:"port_1-tx_packets,omitempty"`
|
||||
Port1TxBytes FlexInt `json:"port_1-tx_bytes,omitempty"`
|
||||
Port1TxMulticast FlexInt `json:"port_1-tx_multicast"`
|
||||
Port1TxBroadcast FlexInt `json:"port_1-tx_broadcast"`
|
||||
Port3RxPackets FlexInt `json:"port_3-rx_packets,omitempty"`
|
||||
Port3RxBytes FlexInt `json:"port_3-rx_bytes,omitempty"`
|
||||
Port3TxPackets FlexInt `json:"port_3-tx_packets,omitempty"`
|
||||
Port3TxBytes FlexInt `json:"port_3-tx_bytes,omitempty"`
|
||||
Port3RxBroadcast FlexInt `json:"port_3-rx_broadcast"`
|
||||
Port3TxMulticast FlexInt `json:"port_3-tx_multicast"`
|
||||
Port3TxBroadcast FlexInt `json:"port_3-tx_broadcast"`
|
||||
Port6RxPackets FlexInt `json:"port_6-rx_packets,omitempty"`
|
||||
Port6RxBytes FlexInt `json:"port_6-rx_bytes,omitempty"`
|
||||
Port6TxPackets FlexInt `json:"port_6-tx_packets,omitempty"`
|
||||
Port6TxBytes FlexInt `json:"port_6-tx_bytes,omitempty"`
|
||||
Port6RxMulticast FlexInt `json:"port_6-rx_multicast"`
|
||||
Port6TxMulticast FlexInt `json:"port_6-tx_multicast"`
|
||||
Port6TxBroadcast FlexInt `json:"port_6-tx_broadcast"`
|
||||
Port7RxPackets FlexInt `json:"port_7-rx_packets,omitempty"`
|
||||
Port7RxBytes FlexInt `json:"port_7-rx_bytes,omitempty"`
|
||||
Port7TxPackets FlexInt `json:"port_7-tx_packets,omitempty"`
|
||||
Port7TxBytes FlexInt `json:"port_7-tx_bytes,omitempty"`
|
||||
Port7TxMulticast FlexInt `json:"port_7-tx_multicast"`
|
||||
Port7TxBroadcast FlexInt `json:"port_7-tx_broadcast"`
|
||||
Port9RxPackets FlexInt `json:"port_9-rx_packets,omitempty"`
|
||||
Port9RxBytes FlexInt `json:"port_9-rx_bytes,omitempty"`
|
||||
Port9TxPackets FlexInt `json:"port_9-tx_packets,omitempty"`
|
||||
Port9TxBytes FlexInt `json:"port_9-tx_bytes,omitempty"`
|
||||
Port9TxMulticast FlexInt `json:"port_9-tx_multicast"`
|
||||
Port9TxBroadcast FlexInt `json:"port_9-tx_broadcast"`
|
||||
Port10RxPackets FlexInt `json:"port_10-rx_packets,omitempty"`
|
||||
Port10RxBytes FlexInt `json:"port_10-rx_bytes,omitempty"`
|
||||
Port10TxPackets FlexInt `json:"port_10-tx_packets,omitempty"`
|
||||
Port10TxBytes FlexInt `json:"port_10-tx_bytes,omitempty"`
|
||||
Port10RxMulticast FlexInt `json:"port_10-rx_multicast"`
|
||||
Port10TxMulticast FlexInt `json:"port_10-tx_multicast"`
|
||||
Port10TxBroadcast FlexInt `json:"port_10-tx_broadcast"`
|
||||
Port11RxPackets FlexInt `json:"port_11-rx_packets,omitempty"`
|
||||
Port11RxBytes FlexInt `json:"port_11-rx_bytes,omitempty"`
|
||||
Port11TxPackets FlexInt `json:"port_11-tx_packets,omitempty"`
|
||||
Port11TxBytes FlexInt `json:"port_11-tx_bytes,omitempty"`
|
||||
Port11TxMulticast FlexInt `json:"port_11-tx_multicast"`
|
||||
Port11TxBroadcast FlexInt `json:"port_11-tx_broadcast"`
|
||||
Port12RxPackets FlexInt `json:"port_12-rx_packets,omitempty"`
|
||||
Port12RxBytes FlexInt `json:"port_12-rx_bytes,omitempty"`
|
||||
Port12TxPackets FlexInt `json:"port_12-tx_packets,omitempty"`
|
||||
Port12TxBytes FlexInt `json:"port_12-tx_bytes,omitempty"`
|
||||
Port12TxMulticast FlexInt `json:"port_12-tx_multicast"`
|
||||
Port12TxBroadcast FlexInt `json:"port_12-tx_broadcast"`
|
||||
Port13RxPackets FlexInt `json:"port_13-rx_packets,omitempty"`
|
||||
Port13RxBytes FlexInt `json:"port_13-rx_bytes,omitempty"`
|
||||
Port13TxPackets FlexInt `json:"port_13-tx_packets,omitempty"`
|
||||
Port13TxBytes FlexInt `json:"port_13-tx_bytes,omitempty"`
|
||||
Port13RxMulticast FlexInt `json:"port_13-rx_multicast"`
|
||||
Port13RxBroadcast FlexInt `json:"port_13-rx_broadcast"`
|
||||
Port13TxMulticast FlexInt `json:"port_13-tx_multicast"`
|
||||
Port13TxBroadcast FlexInt `json:"port_13-tx_broadcast"`
|
||||
Port15RxPackets FlexInt `json:"port_15-rx_packets,omitempty"`
|
||||
Port15RxBytes FlexInt `json:"port_15-rx_bytes,omitempty"`
|
||||
Port15TxPackets FlexInt `json:"port_15-tx_packets,omitempty"`
|
||||
Port15TxBytes FlexInt `json:"port_15-tx_bytes,omitempty"`
|
||||
Port15RxBroadcast FlexInt `json:"port_15-rx_broadcast"`
|
||||
Port15TxMulticast FlexInt `json:"port_15-tx_multicast"`
|
||||
Port15TxBroadcast FlexInt `json:"port_15-tx_broadcast"`
|
||||
Port16RxPackets FlexInt `json:"port_16-rx_packets,omitempty"`
|
||||
Port16RxBytes FlexInt `json:"port_16-rx_bytes,omitempty"`
|
||||
Port16TxPackets FlexInt `json:"port_16-tx_packets,omitempty"`
|
||||
Port16TxBytes FlexInt `json:"port_16-tx_bytes,omitempty"`
|
||||
Port16TxMulticast FlexInt `json:"port_16-tx_multicast"`
|
||||
Port16TxBroadcast FlexInt `json:"port_16-tx_broadcast"`
|
||||
Port17RxPackets FlexInt `json:"port_17-rx_packets,omitempty"`
|
||||
Port17RxBytes FlexInt `json:"port_17-rx_bytes,omitempty"`
|
||||
Port17TxPackets FlexInt `json:"port_17-tx_packets,omitempty"`
|
||||
Port17TxBytes FlexInt `json:"port_17-tx_bytes,omitempty"`
|
||||
Port17TxMulticast FlexInt `json:"port_17-tx_multicast"`
|
||||
Port17TxBroadcast FlexInt `json:"port_17-tx_broadcast"`
|
||||
Port18RxPackets FlexInt `json:"port_18-rx_packets,omitempty"`
|
||||
Port18RxBytes FlexInt `json:"port_18-rx_bytes,omitempty"`
|
||||
Port18TxPackets FlexInt `json:"port_18-tx_packets,omitempty"`
|
||||
Port18TxBytes FlexInt `json:"port_18-tx_bytes,omitempty"`
|
||||
Port18RxMulticast FlexInt `json:"port_18-rx_multicast"`
|
||||
Port18TxMulticast FlexInt `json:"port_18-tx_multicast"`
|
||||
Port18TxBroadcast FlexInt `json:"port_18-tx_broadcast"`
|
||||
Port19RxPackets FlexInt `json:"port_19-rx_packets,omitempty"`
|
||||
Port19RxBytes FlexInt `json:"port_19-rx_bytes,omitempty"`
|
||||
Port19TxPackets FlexInt `json:"port_19-tx_packets,omitempty"`
|
||||
Port19TxBytes FlexInt `json:"port_19-tx_bytes,omitempty"`
|
||||
Port19TxMulticast FlexInt `json:"port_19-tx_multicast"`
|
||||
Port19TxBroadcast FlexInt `json:"port_19-tx_broadcast"`
|
||||
Port21RxPackets FlexInt `json:"port_21-rx_packets,omitempty"`
|
||||
Port21RxBytes FlexInt `json:"port_21-rx_bytes,omitempty"`
|
||||
Port21TxPackets FlexInt `json:"port_21-tx_packets,omitempty"`
|
||||
Port21TxBytes FlexInt `json:"port_21-tx_bytes,omitempty"`
|
||||
Port21RxBroadcast FlexInt `json:"port_21-rx_broadcast"`
|
||||
Port21TxMulticast FlexInt `json:"port_21-tx_multicast"`
|
||||
Port21TxBroadcast FlexInt `json:"port_21-tx_broadcast"`
|
||||
Port22RxPackets FlexInt `json:"port_22-rx_packets,omitempty"`
|
||||
Port22RxBytes FlexInt `json:"port_22-rx_bytes,omitempty"`
|
||||
Port22TxPackets FlexInt `json:"port_22-tx_packets,omitempty"`
|
||||
Port22TxBytes FlexInt `json:"port_22-tx_bytes,omitempty"`
|
||||
Port22RxMulticast FlexInt `json:"port_22-rx_multicast"`
|
||||
Port22TxMulticast FlexInt `json:"port_22-tx_multicast"`
|
||||
Port22TxBroadcast FlexInt `json:"port_22-tx_broadcast"`
|
||||
Port23RxPackets FlexInt `json:"port_23-rx_packets,omitempty"`
|
||||
Port23RxBytes FlexInt `json:"port_23-rx_bytes,omitempty"`
|
||||
Port23RxDropped FlexInt `json:"port_23-rx_dropped"`
|
||||
Port23TxPackets FlexInt `json:"port_23-tx_packets,omitempty"`
|
||||
Port23TxBytes FlexInt `json:"port_23-tx_bytes,omitempty"`
|
||||
Port23RxMulticast FlexInt `json:"port_23-rx_multicast"`
|
||||
Port23RxBroadcast FlexInt `json:"port_23-rx_broadcast"`
|
||||
Port23TxMulticast FlexInt `json:"port_23-tx_multicast"`
|
||||
Port23TxBroadcast FlexInt `json:"port_23-tx_broadcast"`
|
||||
Port24RxPackets FlexInt `json:"port_24-rx_packets,omitempty"`
|
||||
Port24RxBytes FlexInt `json:"port_24-rx_bytes,omitempty"`
|
||||
Port24TxPackets FlexInt `json:"port_24-tx_packets,omitempty"`
|
||||
Port24TxBytes FlexInt `json:"port_24-tx_bytes,omitempty"`
|
||||
Port24RxMulticast FlexInt `json:"port_24-rx_multicast"`
|
||||
Port24TxMulticast FlexInt `json:"port_24-tx_multicast"`
|
||||
Port24TxBroadcast FlexInt `json:"port_24-tx_broadcast"`
|
||||
Port1RxMulticast FlexInt `json:"port_1-rx_multicast"`
|
||||
Port3RxDropped FlexInt `json:"port_3-rx_dropped"`
|
||||
Port3RxMulticast FlexInt `json:"port_3-rx_multicast"`
|
||||
Port6RxDropped FlexInt `json:"port_6-rx_dropped"`
|
||||
Port7RxDropped FlexInt `json:"port_7-rx_dropped"`
|
||||
Port7RxMulticast FlexInt `json:"port_7-rx_multicast"`
|
||||
Port9RxDropped FlexInt `json:"port_9-rx_dropped"`
|
||||
Port9RxMulticast FlexInt `json:"port_9-rx_multicast"`
|
||||
Port9RxBroadcast FlexInt `json:"port_9-rx_broadcast"`
|
||||
Port10RxBroadcast FlexInt `json:"port_10-rx_broadcast"`
|
||||
Port12RxDropped FlexInt `json:"port_12-rx_dropped"`
|
||||
Port12RxMulticast FlexInt `json:"port_12-rx_multicast"`
|
||||
Port13RxDropped FlexInt `json:"port_13-rx_dropped"`
|
||||
Port17RxDropped FlexInt `json:"port_17-rx_dropped"`
|
||||
Port17RxMulticast FlexInt `json:"port_17-rx_multicast"`
|
||||
Port17RxBroadcast FlexInt `json:"port_17-rx_broadcast"`
|
||||
Port19RxDropped FlexInt `json:"port_19-rx_dropped"`
|
||||
Port19RxMulticast FlexInt `json:"port_19-rx_multicast"`
|
||||
Port19RxBroadcast FlexInt `json:"port_19-rx_broadcast"`
|
||||
Port21RxDropped FlexInt `json:"port_21-rx_dropped"`
|
||||
Port21RxMulticast FlexInt `json:"port_21-rx_multicast"`
|
||||
Port7RxBroadcast FlexInt `json:"port_7-rx_broadcast"`
|
||||
Port18RxBroadcast FlexInt `json:"port_18-rx_broadcast"`
|
||||
Port16RxMulticast FlexInt `json:"port_16-rx_multicast"`
|
||||
Port15RxDropped FlexInt `json:"port_15-rx_dropped"`
|
||||
Port15RxMulticast FlexInt `json:"port_15-rx_multicast"`
|
||||
Port16RxBroadcast FlexInt `json:"port_16-rx_broadcast"`
|
||||
Port11RxBroadcast FlexInt `json:"port_11-rx_broadcast"`
|
||||
Port12RxBroadcast FlexInt `json:"port_12-rx_broadcast"`
|
||||
Port6RxBroadcast FlexInt `json:"port_6-rx_broadcast"`
|
||||
Port24RxBroadcast FlexInt `json:"port_24-rx_broadcast"`
|
||||
Port22RxBroadcast FlexInt `json:"port_22-rx_broadcast"`
|
||||
Port10TxDropped FlexInt `json:"port_10-tx_dropped"`
|
||||
Port16TxDropped FlexInt `json:"port_16-tx_dropped"`
|
||||
Port1RxBroadcast FlexInt `json:"port_1-rx_broadcast"`
|
||||
Port4RxPackets FlexInt `json:"port_4-rx_packets,omitempty"`
|
||||
Port4RxBytes FlexInt `json:"port_4-rx_bytes,omitempty"`
|
||||
Port4RxDropped FlexInt `json:"port_4-rx_dropped"`
|
||||
Port4TxPackets FlexInt `json:"port_4-tx_packets,omitempty"`
|
||||
Port4TxBytes FlexInt `json:"port_4-tx_bytes,omitempty"`
|
||||
Port4TxDropped FlexInt `json:"port_4-tx_dropped"`
|
||||
Port4RxMulticast FlexInt `json:"port_4-rx_multicast"`
|
||||
Port4RxBroadcast FlexInt `json:"port_4-rx_broadcast"`
|
||||
Port4TxMulticast FlexInt `json:"port_4-tx_multicast"`
|
||||
Port4TxBroadcast FlexInt `json:"port_4-tx_broadcast"`
|
||||
*/
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshalls 5.10 or 5.11 formatted Switch Stat data.
|
||||
func (v *USWStat) UnmarshalJSON(data []byte) error {
|
||||
var n struct {
|
||||
Sw `json:"sw"`
|
||||
}
|
||||
|
||||
v.Sw = &n.Sw
|
||||
|
||||
err := json.Unmarshal(data, v.Sw) // controller version 5.10.
|
||||
if err != nil {
|
||||
return json.Unmarshal(data, &n) // controller version 5.11.
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package unifi // nolint: testpackage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func testGetControllerJSON() (string, string) {
|
||||
return `{
|
||||
"sw": {
|
||||
"site_id": "mySite",
|
||||
"o": "sw",
|
||||
"oid": "00:00:00:00:00:00",
|
||||
"sw": "00:00:00:00:00:00",
|
||||
"time": 1577742600000,
|
||||
"datetime": "2019-12-30T09:40:00Z",
|
||||
"rx_packets": 321,
|
||||
"rx_bytes": 321,
|
||||
"rx_errors": 123,
|
||||
"rx_dropped": 123,
|
||||
"rx_crypts": 123,
|
||||
"rx_frags": 123,
|
||||
"tx_packets": 123,
|
||||
"tx_bytes": 123,
|
||||
"tx_errors": 0,
|
||||
"tx_dropped": 0,
|
||||
"tx_retries": 0,
|
||||
"rx_multicast": 123,
|
||||
"rx_broadcast": 123,
|
||||
"tx_multicast": 123,
|
||||
"tx_broadcast": 123,
|
||||
"bytes": 123,
|
||||
"duration": 123}}`,
|
||||
`{
|
||||
"site_id": "mySite",
|
||||
"o": "sw",
|
||||
"oid": "00:00:00:00:00:00",
|
||||
"sw": "00:00:00:00:00:00",
|
||||
"time": 1577742600000,
|
||||
"datetime": "2019-12-30T09:40:00Z",
|
||||
"rx_packets": 321,
|
||||
"rx_bytes": 321,
|
||||
"rx_errors": 123,
|
||||
"rx_dropped": 123,
|
||||
"rx_crypts": 123,
|
||||
"rx_frags": 123,
|
||||
"tx_packets": 123,
|
||||
"tx_bytes": 123,
|
||||
"tx_errors": 0,
|
||||
"tx_dropped": 0,
|
||||
"tx_retries": 0,
|
||||
"rx_multicast": 123,
|
||||
"rx_broadcast": 123,
|
||||
"tx_multicast": 123,
|
||||
"tx_broadcast": 123,
|
||||
"bytes": 123,
|
||||
"duration": 123}`
|
||||
}
|
||||
|
||||
func TestUSWUnmarshalJSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
a := assert.New(t)
|
||||
testcontroller511, testcontroller510 := testGetControllerJSON()
|
||||
rxMulticast := 123
|
||||
u := &USWStat{}
|
||||
err := u.UnmarshalJSON([]byte(testcontroller510))
|
||||
a.Nil(err, "must be no error unmarshaling test strings")
|
||||
a.Equal(float64(rxMulticast), u.RxMulticast.Val, "data was not properly unmarshaled")
|
||||
|
||||
u = &USWStat{} // reset
|
||||
err = u.UnmarshalJSON([]byte(testcontroller511))
|
||||
a.Nil(err, "must be no error unmarshaling test strings")
|
||||
a.Equal(float64(rxMulticast), u.RxMulticast.Val, "data was not properly unmarshaled")
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
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 {
|
||||
site *Site
|
||||
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"`
|
||||
}
|
||||
Loading…
Reference in New Issue