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