Add UXG, UDM storage, minor lint fixups.

This commit is contained in:
David Newhall II 2021-03-07 17:23:31 -08:00
parent bfe80a59e1
commit e1be3246f1
12 changed files with 362 additions and 275 deletions

View File

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

View File

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

View File

@ -72,6 +72,12 @@ func (u *Unifi) parseDevices(data []json.RawMessage, siteName string) *Devices {
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac)) dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
devices.UDMs = append(devices.UDMs, dev) devices.UDMs = append(devices.UDMs, dev)
} }
case "uxg":
dev := &UXG{SiteName: siteName, SourceName: u.URL}
if u.unmarshalDevice(assetType, r, dev) == nil {
dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac))
devices.UXGs = append(devices.UXGs, dev)
}
default: default:
u.ErrorLog("unknown asset type - %v - skipping", assetType) u.ErrorLog("unknown asset type - %v - skipping", assetType)
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -6,6 +6,7 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -79,6 +80,7 @@ type Devices struct {
USGs []*USG USGs []*USG
USWs []*USW USWs []*USW
UDMs []*UDM UDMs []*UDM
UXGs []*UXG
} }
// Config is the data passed into our library. This configures things and allows // Config is the data passed into our library. This configures things and allows
@ -91,6 +93,7 @@ type Config struct {
New bool New bool
ErrorLog Logger ErrorLog Logger
DebugLog Logger DebugLog Logger
Timeout time.Duration // how long to wait for replies, default: forever.
} }
// Unifi is what you get in return for providing a password! Unifi represents // Unifi is what you get in return for providing a password! Unifi represents

View File

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

View File

@ -7,6 +7,7 @@ package unifi
import ( import (
"bytes" "bytes"
"context"
"crypto/tls" "crypto/tls"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -18,13 +19,13 @@ import (
"strings" "strings"
"time" "time"
"github.com/pkg/errors"
"golang.org/x/net/publicsuffix" "golang.org/x/net/publicsuffix"
) )
var ( var (
errAuthenticationFailed = fmt.Errorf("authentication failed") ErrAuthenticationFailed = fmt.Errorf("authentication failed")
errInvalidStatusCode = fmt.Errorf("invalid status code from server") ErrInvalidStatusCode = fmt.Errorf("invalid status code from server")
ErrNoParams = fmt.Errorf("requedted PUT with no parameters")
) )
// NewUnifi creates a http.Client with authenticated cookies. // NewUnifi creates a http.Client with authenticated cookies.
@ -46,9 +47,11 @@ func NewUnifi(config *Config) (*Unifi, error) {
config.DebugLog = discardLogs config.DebugLog = discardLogs
} }
u := &Unifi{Config: config, u := &Unifi{
Config: config,
Client: &http.Client{ Client: &http.Client{
Jar: jar, Timeout: config.Timeout,
Jar: jar,
Transport: &http.Transport{ Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: !config.VerifySSL}, // nolint: gosec TLSClientConfig: &tls.Config{InsecureSkipVerify: !config.VerifySSL}, // nolint: gosec
}, },
@ -64,7 +67,7 @@ func NewUnifi(config *Config) (*Unifi, error) {
} }
if err := u.GetServerData(); err != nil { if err := u.GetServerData(); err != nil {
return u, errors.Wrap(err, "unable to get server version") return u, fmt.Errorf("unable to get server version: %w", err)
} }
return u, nil return u, nil
@ -91,8 +94,8 @@ func (u *Unifi) Login() error {
req.URL, time.Since(start).Round(time.Millisecond), resp.ContentLength) req.URL, time.Since(start).Round(time.Millisecond), resp.ContentLength)
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return errors.Wrapf(errAuthenticationFailed, "(user: %s): %s (status: %s)", return fmt.Errorf("(user: %s): %s (status: %s): %w",
u.User, req.URL, resp.Status) u.User, req.URL, resp.Status, ErrAuthenticationFailed)
} }
return nil return nil
@ -185,39 +188,44 @@ func (u *Unifi) PutData(apiPath string, v interface{}, params ...string) error {
// Use this if you're unmarshalling UniFi data into custom types. // Use this if you're unmarshalling UniFi data into custom types.
// And if you're doing that... sumbut a pull request with your new struct. :) // And if you're doing that... sumbut a pull request with your new struct. :)
// This is a helper method that is exposed for convenience. // This is a helper method that is exposed for convenience.
func (u *Unifi) UniReq(apiPath string, params string) (req *http.Request, err error) { func (u *Unifi) UniReq(apiPath string, params string) (*http.Request, error) {
var (
req *http.Request
err error
)
switch apiPath = u.path(apiPath); params { switch apiPath = u.path(apiPath); params {
case "": case "":
req, err = http.NewRequest("GET", u.URL+apiPath, nil) req, err = http.NewRequest(http.MethodGet, u.URL+apiPath, nil)
default: default:
req, err = http.NewRequest("POST", u.URL+apiPath, bytes.NewBufferString(params)) req, err = http.NewRequest(http.MethodPost, u.URL+apiPath, bytes.NewBufferString(params))
} }
if err != nil { if err != nil {
return return nil, fmt.Errorf("creating request: %w", err)
} }
u.setHeaders(req, params) u.setHeaders(req, params)
return return req, nil
} }
// UniReqPut is the Put call equivalent to UniReq // UniReqPut is the Put call equivalent to UniReq
func (u *Unifi) UniReqPut(apiPath string, params string) (req *http.Request, err error) { func (u *Unifi) UniReqPut(apiPath string, params string) (*http.Request, error) {
switch apiPath = u.path(apiPath); params { if params == "" {
case "": return nil, ErrNoParams
err = fmt.Errorf("Put with no parameters. Use UniReq()")
default:
req, err = http.NewRequest("PUT", u.URL+apiPath, bytes.NewBufferString(params))
} }
apiPath = u.path(apiPath)
req, err := http.NewRequest(http.MethodPut, u.URL+apiPath, bytes.NewBufferString(params)) //nolint:noctx
if err != nil { if err != nil {
return return nil, fmt.Errorf("creating request: %w", err)
} }
u.setHeaders(req, params) u.setHeaders(req, params)
return return req, nil
} }
// GetJSON returns the raw JSON from a path. This is useful for debugging. // GetJSON returns the raw JSON from a path. This is useful for debugging.
@ -242,7 +250,17 @@ func (u *Unifi) PutJSON(apiPath string, params ...string) ([]byte, error) {
} }
func (u *Unifi) do(req *http.Request) ([]byte, error) { func (u *Unifi) do(req *http.Request) ([]byte, error) {
resp, err := u.Do(req) var (
cancel func()
ctx = context.Background()
)
if u.Config.Timeout != 0 {
ctx, cancel = context.WithTimeout(ctx, u.Config.Timeout)
defer cancel()
}
resp, err := u.Do(req.WithContext(ctx))
if err != nil { if err != nil {
return []byte{}, err return []byte{}, err
} }
@ -260,7 +278,7 @@ func (u *Unifi) do(req *http.Request) ([]byte, error) {
} }
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
err = errors.Wrapf(errInvalidStatusCode, "%s: %s", req.URL, resp.Status) err = fmt.Errorf("%s: %s: %w", req.URL, resp.Status, ErrInvalidStatusCode)
} }
return body, err return body, err

View File

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

View File

@ -43,19 +43,15 @@ type USW struct {
PortIdx FlexInt `json:"port_idx"` PortIdx FlexInt `json:"port_idx"`
PortconfID string `json:"portconf_id"` PortconfID string `json:"portconf_id"`
} `json:"port_overrides"` } `json:"port_overrides"`
PortTable []Port `json:"port_table"` PortTable []Port `json:"port_table"`
Serial string `json:"serial"` Serial string `json:"serial"`
SiteID string `json:"site_id"` SiteID string `json:"site_id"`
StpPriority FlexInt `json:"stp_priority"` StpPriority FlexInt `json:"stp_priority"`
StpVersion string `json:"stp_version"` StpVersion string `json:"stp_version"`
Type string `json:"type"` Type string `json:"type"`
Version string `json:"version"` Version string `json:"version"`
RequiredVersion string `json:"required_version"` RequiredVersion string `json:"required_version"`
SwitchCaps struct { SwitchCaps *SwitchCaps `json:"switch_caps"`
FeatureCaps FlexInt `json:"feature_caps"`
MaxMirrorSessions FlexInt `json:"max_mirror_sessions"`
MaxAggregateSessions FlexInt `json:"max_aggregate_sessions"`
} `json:"switch_caps"`
HwCaps FlexInt `json:"hw_caps"` HwCaps FlexInt `json:"hw_caps"`
Unsupported FlexBool `json:"unsupported"` Unsupported FlexBool `json:"unsupported"`
UnsupportedReason FlexInt `json:"unsupported_reason"` UnsupportedReason FlexInt `json:"unsupported_reason"`
@ -97,39 +93,58 @@ type USW struct {
GuestNumSta FlexInt `json:"guest-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 UDM. // Port is a physical connection on a USW or UDM.
type Port struct { type Port struct {
AggregatedBy FlexBool `json:"aggregated_by"` AggregatedBy FlexBool `json:"aggregated_by"`
Autoneg FlexBool `json:"autoneg,omitempty"` Autoneg FlexBool `json:"autoneg,omitempty"`
BytesR FlexInt `json:"bytes-r"` BytesR FlexInt `json:"bytes-r"`
DNS []string `json:"dns,omitempty"` DNS []string `json:"dns,omitempty"`
Dot1XMode string `json:"dot1x_mode"` Dot1XMode string `json:"dot1x_mode"`
Dot1XStatus string `json:"dot1x_status"` Dot1XStatus string `json:"dot1x_status"`
Enable FlexBool `json:"enable"` Enable FlexBool `json:"enable"`
FlowctrlRx FlexBool `json:"flowctrl_rx"` FlowctrlRx FlexBool `json:"flowctrl_rx"`
FlowctrlTx FlexBool `json:"flowctrl_tx"` FlowctrlTx FlexBool `json:"flowctrl_tx"`
FullDuplex FlexBool `json:"full_duplex"` FullDuplex FlexBool `json:"full_duplex"`
IP string `json:"ip,omitempty"` IP string `json:"ip,omitempty"`
Ifname string `json:"ifname,omitempty"` Ifname string `json:"ifname,omitempty"`
IsUplink FlexBool `json:"is_uplink"` IsUplink FlexBool `json:"is_uplink"`
Mac string `json:"mac,omitempty"` Mac string `json:"mac,omitempty"`
Jumbo FlexBool `json:"jumbo,omitempty"` MacTable []MacTable `json:"mac_table,omitempty"`
Masked FlexBool `json:"masked"` Jumbo FlexBool `json:"jumbo,omitempty"`
Media string `json:"media"` Masked FlexBool `json:"masked"`
Name string `json:"name"` Media string `json:"media"`
NetworkName string `json:"network_name,omitempty"` Name string `json:"name"`
NumPort int `json:"num_port,omitempty"` NetworkName string `json:"network_name,omitempty"`
OpMode string `json:"op_mode"` Netmask string `json:"netmask,omitempty"`
PoeCaps FlexInt `json:"poe_caps"` NumPort int `json:"num_port,omitempty"`
PoeClass string `json:"poe_class,omitempty"` OpMode string `json:"op_mode"`
PoeCurrent FlexInt `json:"poe_current,omitempty"` PoeCaps FlexInt `json:"poe_caps"`
PoeEnable FlexBool `json:"poe_enable,omitempty"` PoeClass string `json:"poe_class,omitempty"`
PoeGood FlexBool `json:"poe_good,omitempty"` PoeCurrent FlexInt `json:"poe_current,omitempty"`
PoeMode string `json:"poe_mode,omitempty"` PoeEnable FlexBool `json:"poe_enable,omitempty"`
PoePower FlexInt `json:"poe_power,omitempty"` PoeGood FlexBool `json:"poe_good,omitempty"`
PoeVoltage FlexInt `json:"poe_voltage,omitempty"` PoeMode string `json:"poe_mode,omitempty"`
PoePower FlexInt `json:"poe_power,omitempty"`
PoeVoltage FlexInt `json:"poe_voltage,omitempty"`
PortDelta struct { PortDelta struct {
TimeDelta int64 `json:"time_delta"` TimeDelta int64 `json:"time_delta"`
TimeDeltaActivity int64 `json:"time_delta_activity"`
} `json:"port_delta,omitempty"` } `json:"port_delta,omitempty"`
PortIdx FlexInt `json:"port_idx"` PortIdx FlexInt `json:"port_idx"`
PortPoe FlexBool `json:"port_poe"` PortPoe FlexBool `json:"port_poe"`
@ -141,6 +156,7 @@ type Port struct {
RxErrors FlexInt `json:"rx_errors"` RxErrors FlexInt `json:"rx_errors"`
RxMulticast FlexInt `json:"rx_multicast"` RxMulticast FlexInt `json:"rx_multicast"`
RxPackets FlexInt `json:"rx_packets"` RxPackets FlexInt `json:"rx_packets"`
RxRate FlexInt `json:"rx_rate,omitempty"`
Satisfaction FlexInt `json:"satisfaction,omitempty"` Satisfaction FlexInt `json:"satisfaction,omitempty"`
SfpFound FlexBool `json:"sfp_found,omitempty"` SfpFound FlexBool `json:"sfp_found,omitempty"`
Speed FlexInt `json:"speed"` Speed FlexInt `json:"speed"`
@ -154,6 +170,7 @@ type Port struct {
TxErrors FlexInt `json:"tx_errors"` TxErrors FlexInt `json:"tx_errors"`
TxMulticast FlexInt `json:"tx_multicast"` TxMulticast FlexInt `json:"tx_multicast"`
TxPackets FlexInt `json:"tx_packets"` TxPackets FlexInt `json:"tx_packets"`
TxRate FlexInt `json:"tx_rate,omitempty"`
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
Up FlexBool `json:"up"` Up FlexBool `json:"up"`
} }