From e1be3246f1afa5f31a6c0fade3c7196004703f0e Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Sun, 7 Mar 2021 17:23:31 -0800 Subject: [PATCH 1/5] Add UXG, UDM storage, minor lint fixups. --- core/unifi/alarms.go | 2 +- core/unifi/anomalies.go | 4 +- core/unifi/devices.go | 6 + core/unifi/dpi.go | 23 +++- core/unifi/events.go | 6 +- core/unifi/ids.go | 9 +- core/unifi/site.go | 6 +- core/unifi/types.go | 3 + core/unifi/udm.go | 262 +++++++++++++++++++++------------------- core/unifi/unifi.go | 64 ++++++---- core/unifi/usg.go | 149 +++++++++++++---------- core/unifi/usw.go | 103 +++++++++------- 12 files changed, 362 insertions(+), 275 deletions(-) diff --git a/core/unifi/alarms.go b/core/unifi/alarms.go index 63c558e9..863e27ca 100644 --- a/core/unifi/alarms.go +++ b/core/unifi/alarms.go @@ -73,7 +73,7 @@ func (u *Unifi) GetAlarms(sites []*Site) ([]*Alarm, error) { // GetAlarmsSite retreives the Alarms for a single Site. func (u *Unifi) GetAlarmsSite(site *Site) ([]*Alarm, error) { if site == nil || site.Name == "" { - return nil, errNoSiteProvided + return nil, ErrNoSiteProvided } u.DebugLog("Polling Controller for Alarms, site %s (%s)", site.Name, site.Desc) diff --git a/core/unifi/anomalies.go b/core/unifi/anomalies.go index 9f7de0e2..2dfb5238 100644 --- a/core/unifi/anomalies.go +++ b/core/unifi/anomalies.go @@ -44,7 +44,7 @@ func (u *Unifi) GetAnomalies(sites []*Site, timeRange ...time.Time) ([]*Anomaly, // 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 + return nil, ErrNoSiteProvided } 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) out = append(out, "end="+strconv.FormatInt(end, 10), "start="+strconv.FormatInt(start, 10)) default: - return "", errInvalidTimeRange + return "", ErrInvalidTimeRange } if len(out) == 0 { diff --git a/core/unifi/devices.go b/core/unifi/devices.go index 045e43d7..505d2743 100644 --- a/core/unifi/devices.go +++ b/core/unifi/devices.go @@ -72,6 +72,12 @@ func (u *Unifi) parseDevices(data []json.RawMessage, siteName string) *Devices { dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac)) 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: u.ErrorLog("unknown asset type - %v - skipping", assetType) } diff --git a/core/unifi/dpi.go b/core/unifi/dpi.go index 6dc3d843..f487c877 100644 --- a/core/unifi/dpi.go +++ b/core/unifi/dpi.go @@ -15,12 +15,23 @@ type DPITable struct { // DPIData is the DPI data in the DPI table. type DPIData struct { - Cat int `json:"cat"` - App int `json:"app"` - RxBytes int64 `json:"rx_bytes"` - TxBytes int64 `json:"tx_bytes"` - RxPackets int64 `json:"rx_packets"` - TxPackets int64 `json:"tx_packets"` + Cat int `json:"cat"` + App int `json:"app"` + RxBytes int64 `json:"rx_bytes"` + TxBytes int64 `json:"tx_bytes"` + RxPackets int64 `json:"rx_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. diff --git a/core/unifi/events.go b/core/unifi/events.go index 1f5f8458..d716daee 100644 --- a/core/unifi/events.go +++ b/core/unifi/events.go @@ -8,8 +8,8 @@ import ( ) 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") + 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 ( @@ -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. func (u *Unifi) GetSiteEvents(site *Site, hours time.Duration) ([]*Event, error) { if site == nil || site.Name == "" { - return nil, errNoSiteProvided + return nil, ErrNoSiteProvided } if hours < time.Hour { diff --git a/core/unifi/ids.go b/core/unifi/ids.go index 0140e157..c290cbe2 100644 --- a/core/unifi/ids.go +++ b/core/unifi/ids.go @@ -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(). func (u *Unifi) GetIDSSite(site *Site, timeRange ...time.Time) ([]*IDS, error) { 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) @@ -126,12 +126,15 @@ func makeEventParams(timeRange ...time.Time) (string, error) { rp.Start = timeRange[0].Unix() * int64(time.Microsecond) rp.End = timeRange[1].Unix() * int64(time.Microsecond) default: - return "", errInvalidTimeRange + return "", ErrInvalidTimeRange } 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 diff --git a/core/unifi/site.go b/core/unifi/site.go index 7939cb06..0d3826a1 100644 --- a/core/unifi/site.go +++ b/core/unifi/site.go @@ -5,9 +5,7 @@ import ( "strings" ) -var ( - errDPIDataBug = fmt.Errorf("dpi data table contains more than 1 item; please open a bug report") -) +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) { @@ -53,7 +51,7 @@ func (u *Unifi) GetSiteDPI(sites []*Site) ([]*DPITable, error) { } if l := len(response.Data); l > 1 { - return nil, errDPIDataBug + return nil, ErrDPIDataBug } else if l == 0 { u.DebugLog("Site DPI data missing! Is DPI enabled in UniFi controller? Site %s (%s) ", site.Name, site.Desc) continue diff --git a/core/unifi/types.go b/core/unifi/types.go index 626ce333..b3ce54a8 100644 --- a/core/unifi/types.go +++ b/core/unifi/types.go @@ -6,6 +6,7 @@ import ( "net/http" "strconv" "strings" + "time" "github.com/pkg/errors" ) @@ -79,6 +80,7 @@ type Devices struct { USGs []*USG USWs []*USW UDMs []*UDM + UXGs []*UXG } // Config is the data passed into our library. This configures things and allows @@ -91,6 +93,7 @@ type Config struct { New bool ErrorLog 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 diff --git a/core/unifi/udm.go b/core/unifi/udm.go index 041eb2b5..49053539 100644 --- a/core/unifi/udm.go +++ b/core/unifi/udm.go @@ -3,69 +3,59 @@ 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 { - 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 struct { - Type string `json:"type"` - IP string `json:"ip"` - } `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 []struct { - Mac string `json:"mac"` - NumPort FlexInt `json:"num_port"` - Name string `json:"name"` - } `json:"ethernet_table"` - PortTable []Port `json:"port_table"` - EthernetOverrides []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 { + 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"` @@ -96,7 +86,7 @@ type UDM struct { Uplink Uplink `json:"uplink"` ConnectRequestIP string `json:"connect_request_ip"` ConnectRequestPort string `json:"connect_request_port"` - DownlinkTable []interface{} `json:"downlink_table"` + DownlinkTable []*DownlinkTable `json:"downlink_table"` WlangroupIDNa string `json:"wlangroup_id_na"` WlangroupIDNg string `json:"wlangroup_id_ng"` BandsteeringMode string `json:"bandsteering_mode"` @@ -109,69 +99,95 @@ type UDM struct { PortIdx FlexInt `json:"port_idx"` PortconfID string `json:"portconf_id"` } `json:"port_overrides"` - Stat UDMStat `json:"stat"` - 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 + 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"` - 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"` - 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"` + 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 { diff --git a/core/unifi/unifi.go b/core/unifi/unifi.go index 79e0c6a4..b5e8aa21 100644 --- a/core/unifi/unifi.go +++ b/core/unifi/unifi.go @@ -7,6 +7,7 @@ package unifi import ( "bytes" + "context" "crypto/tls" "encoding/json" "fmt" @@ -18,13 +19,13 @@ import ( "strings" "time" - "github.com/pkg/errors" "golang.org/x/net/publicsuffix" ) var ( - errAuthenticationFailed = fmt.Errorf("authentication failed") - errInvalidStatusCode = fmt.Errorf("invalid status code from server") + ErrAuthenticationFailed = fmt.Errorf("authentication failed") + ErrInvalidStatusCode = fmt.Errorf("invalid status code from server") + ErrNoParams = fmt.Errorf("requedted PUT with no parameters") ) // NewUnifi creates a http.Client with authenticated cookies. @@ -46,9 +47,11 @@ func NewUnifi(config *Config) (*Unifi, error) { config.DebugLog = discardLogs } - u := &Unifi{Config: config, + u := &Unifi{ + Config: config, Client: &http.Client{ - Jar: jar, + Timeout: config.Timeout, + Jar: jar, Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: !config.VerifySSL}, // nolint: gosec }, @@ -64,7 +67,7 @@ func NewUnifi(config *Config) (*Unifi, error) { } 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 @@ -91,8 +94,8 @@ func (u *Unifi) Login() error { req.URL, time.Since(start).Round(time.Millisecond), resp.ContentLength) if resp.StatusCode != http.StatusOK { - return errors.Wrapf(errAuthenticationFailed, "(user: %s): %s (status: %s)", - u.User, req.URL, resp.Status) + return fmt.Errorf("(user: %s): %s (status: %s): %w", + u.User, req.URL, resp.Status, ErrAuthenticationFailed) } 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. // And if you're doing that... sumbut a pull request with your new struct. :) // This is a helper method that is exposed for convenience. -func (u *Unifi) UniReq(apiPath string, params string) (req *http.Request, err error) { +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("GET", u.URL+apiPath, nil) + req, err = http.NewRequest(http.MethodGet, u.URL+apiPath, nil) 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 { - return + return nil, fmt.Errorf("creating request: %w", err) } u.setHeaders(req, params) - return + return req, nil } // UniReqPut is the Put call equivalent to UniReq -func (u *Unifi) UniReqPut(apiPath string, params string) (req *http.Request, err error) { - switch apiPath = u.path(apiPath); params { - case "": - err = fmt.Errorf("Put with no parameters. Use UniReq()") - default: - req, err = http.NewRequest("PUT", u.URL+apiPath, bytes.NewBufferString(params)) +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 + return nil, fmt.Errorf("creating request: %w", err) } u.setHeaders(req, params) - return + return req, nil } // 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) { - 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 { return []byte{}, err } @@ -260,7 +278,7 @@ func (u *Unifi) do(req *http.Request) ([]byte, error) { } 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 diff --git a/core/unifi/usg.go b/core/unifi/usg.go index d7aff5a1..60815209 100644 --- a/core/unifi/usg.go +++ b/core/unifi/usg.go @@ -7,62 +7,52 @@ import ( // USG represents all the data from the Ubiquiti Controller for a Unifi Security Gateway. type USG struct { - SourceName string `json:"-"` - ID string `json:"_id"` - Adopted FlexBool `json:"adopted"` - Cfgversion string `json:"cfgversion"` - ConfigNetwork struct { - Type string `json:"type"` - IP string `json:"ip"` - } `json:"config_network"` - EthernetTable []struct { - Mac string `json:"mac"` - NumPort FlexInt `json:"num_port"` - Name string `json:"name"` - } `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 []struct { - Ifname string `json:"ifname"` - Networkgroup string `json:"networkgroup"` - } `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"` - 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"` + 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"` + 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 { Name string `json:"name"` Ifname string `json:"ifname"` @@ -114,12 +104,15 @@ type Uplink struct { 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"` @@ -129,6 +122,7 @@ type Uplink struct { 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"` @@ -140,7 +134,7 @@ type Uplink struct { type Wan struct { Autoneg FlexBool `json:"autoneg"` BytesR FlexInt `json:"bytes-r"` - DNS []string `json:"dns"` + DNS []string `json:"dns"` // may be deprecated Enable FlexBool `json:"enable"` FlowctrlRx FlexBool `json:"flowctrl_rx"` FlowctrlTx FlexBool `json:"flowctrl_tx"` @@ -164,7 +158,9 @@ type Wan struct { 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"` @@ -172,22 +168,40 @@ type Wan struct { 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"` - 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"` + Latency FlexInt `json:"latency"` + Rundate FlexInt `json:"rundate"` + Runtime FlexInt `json:"runtime"` + ServerDesc string `json:"server_desc,omitempty"` + Server *SpeedtestServer `json:"server"` + 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. @@ -233,6 +247,7 @@ type Gw struct { 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. diff --git a/core/unifi/usw.go b/core/unifi/usw.go index 242b2053..fcd75c9c 100644 --- a/core/unifi/usw.go +++ b/core/unifi/usw.go @@ -43,19 +43,15 @@ type USW struct { 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 struct { - FeatureCaps FlexInt `json:"feature_caps"` - MaxMirrorSessions FlexInt `json:"max_mirror_sessions"` - MaxAggregateSessions FlexInt `json:"max_aggregate_sessions"` - } `json:"switch_caps"` + 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"` @@ -97,39 +93,58 @@ type USW struct { 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. 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"` - Jumbo FlexBool `json:"jumbo,omitempty"` - Masked FlexBool `json:"masked"` - Media string `json:"media"` - Name string `json:"name"` - NetworkName string `json:"network_name,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"` + 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 struct { - TimeDelta int64 `json:"time_delta"` + TimeDelta int64 `json:"time_delta"` + TimeDeltaActivity int64 `json:"time_delta_activity"` } `json:"port_delta,omitempty"` PortIdx FlexInt `json:"port_idx"` PortPoe FlexBool `json:"port_poe"` @@ -141,6 +156,7 @@ type Port struct { 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"` SfpFound FlexBool `json:"sfp_found,omitempty"` Speed FlexInt `json:"speed"` @@ -154,6 +170,7 @@ type Port struct { 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"` } From 068ee31dfa5e8f9e270271b2753b99e25d21e4d9 Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Sun, 7 Mar 2021 17:23:47 -0800 Subject: [PATCH 2/5] need the uxg file --- core/unifi/uxg.go | 155 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 core/unifi/uxg.go diff --git a/core/unifi/uxg.go b/core/unifi/uxg.go new file mode 100644 index 00000000..41604087 --- /dev/null +++ b/core/unifi/uxg.go @@ -0,0 +1,155 @@ +package unifi + +// UXG represents all the data from the Ubiquiti Controller for a UniFi 10Gb Gateway. +// The UDM shares several structs/type-data with USW and USG. +type UXG struct { + SourceName string `json:"-"` + SiteName string `json:"-"` + ID string `json:"_id"` + IP string `json:"ip"` + Mac string `json:"mac"` + Model string `json:"model"` + ModelInLts bool `json:"model_in_lts"` + ModelInEol bool `json:"model_in_eol"` + Type string `json:"type"` + Version string `json:"version"` + Adopted bool `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 bool `json:"internet"` + ModelIncompatible bool `json:"model_incompatible"` + EthernetTable []*EthernetTable `json:"ethernet_table"` + PortTable []*Port `json:"port_table"` + EthernetOverrides []*EthernetOverrides `json:"ethernet_overrides"` + UsgCaps FlexInt `json:"usg_caps"` + HasSpeaker bool `json:"has_speaker"` + HasEth1 bool `json:"has_eth1"` + FwCaps FlexInt `json:"fw_caps"` + HwCaps FlexInt `json:"hw_caps"` + WifiCaps FlexInt `json:"wifi_caps"` + SwitchCaps *SwitchCaps `json:"switch_caps"` + HasFan bool `json:"has_fan"` + HasTemperature bool `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 bool `json:"lcm_brightness_override"` + LcmIdleTimeoutOverride bool `json:"lcm_idle_timeout_override"` + Name string `json:"name"` + Unsupported bool `json:"unsupported"` + UnsupportedReason FlexInt `json:"unsupported_reason"` + Serial string `json:"serial"` + HashID string `json:"hash_id"` + TwoPhaseAdopt bool `json:"two_phase_adopt"` + DeviceID string `json:"device_id"` + State FlexInt `json:"state"` + StartDisconnectedMillis int64 `json:"start_disconnected_millis"` + UpgradeState FlexInt `json:"upgrade_state"` + StartConnectedMillis int64 `json:"start_connected_millis"` + LastSeen FlexInt `json:"last_seen"` + Uptime FlexInt `json:"uptime"` + UnderscoreUptime FlexInt `json:"_uptime"` + Locating bool `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 bool `json:"overheating"` + GeoInfo map[string]*GeoInfo `json:"geo_info"` + LedState *LedState `json:"led_state"` + SpeedtestStatus *SpeedtestStatus `json:"speedtest-status"` + SpeedtestStatusSaved bool `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 int64 `json:"rx_bytes"` + Bytes int64 `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"` +} + +type LedState struct { + Pattern string `json:"pattern"` + Tempo FlexInt `json:"tempo"` +} + +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"` +} + +type UptimeStats struct { + Availability FlexInt `json:"availability"` + LatencyAverage FlexInt `json:"latency_average"` + TimePeriod FlexInt `json:"time_period"` +} + +// UDMStat holds the "stat" data for a dream machine. +// A dream machine is a USG + USW + Controller. +type UXGStat struct { + *Gw `json:"gw"` + *Sw `json:"sw"` +} From 8d9af25d472b8309ff0d122b6697ef92b831fb64 Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Sun, 7 Mar 2021 17:57:16 -0800 Subject: [PATCH 3/5] a fe wmore tweaks to align outputs.a --- core/unifi/unifi_test.go | 13 +++- core/unifi/usg.go | 70 ++++++++------------- core/unifi/usw.go | 129 ++++++++++++++++++--------------------- core/unifi/uxg.go | 52 ++++++++-------- 4 files changed, 121 insertions(+), 143 deletions(-) diff --git a/core/unifi/unifi_test.go b/core/unifi/unifi_test.go index 1ef3725f..f466a950 100644 --- a/core/unifi/unifi_test.go +++ b/core/unifi/unifi_test.go @@ -30,9 +30,11 @@ func TestUniReq(t *testing.T) { 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") @@ -45,11 +47,13 @@ func TestUniReq(t *testing.T) { 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") @@ -61,28 +65,31 @@ func TestUniReqPut(t *testing.T) { 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.UniReqPut(p, "") + _, 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) + 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. This test is incomplete. +/* NOT DONE: OPEN web server, check parameters posted, more. These tests are incomplete. a.EqualValues(`{"username": "user1","password": "pass2"}`, string(post_params), "user/pass json parameters improperly encoded") */ diff --git a/core/unifi/usg.go b/core/unifi/usg.go index 60815209..751b76f8 100644 --- a/core/unifi/usg.go +++ b/core/unifi/usg.go @@ -53,40 +53,19 @@ type USG struct { SpeedtestStatusSaved FlexBool `json:"speedtest-status-saved"` Wan1 Wan `json:"wan1"` Wan2 Wan `json:"wan2"` - PortTable []struct { - Name string `json:"name"` - Ifname string `json:"ifname"` - IP string `json:"ip"` - Netmask string `json:"netmask"` - Mac string `json:"mac"` - Up FlexBool `json:"up"` - Speed FlexInt `json:"speed"` - FullDuplex FlexBool `json:"full_duplex"` - RxBytes FlexInt `json:"rx_bytes"` - RxDropped FlexInt `json:"rx_dropped"` - RxErrors FlexInt `json:"rx_errors"` - RxPackets FlexInt `json:"rx_packets"` - TxBytes FlexInt `json:"tx_bytes"` - TxDropped FlexInt `json:"tx_dropped"` - TxErrors FlexInt `json:"tx_errors"` - TxPackets FlexInt `json:"tx_packets"` - RxMulticast FlexInt `json:"rx_multicast"` - Enable FlexBool `json:"enable"` - DNS []string `json:"dns,omitempty"` - Gateway string `json:"gateway,omitempty"` - } `json:"port_table"` - NetworkTable NetworkTable `json:"network_table"` - Uplink Uplink `json:"uplink"` - Stat USGStat `json:"stat"` - TxBytes FlexInt `json:"tx_bytes"` - RxBytes FlexInt `json:"rx_bytes"` - Bytes FlexInt `json:"bytes"` - NumSta FlexInt `json:"num_sta"` - UserNumSta FlexInt `json:"user-num_sta"` - GuestNumSta FlexInt `json:"guest-num_sta"` - NumDesktop FlexInt `json:"num_desktop"` - NumMobile FlexInt `json:"num_mobile"` - NumHandheld FlexInt `json:"num_handheld"` + 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. @@ -175,17 +154,18 @@ type Wan struct { // 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"` - 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"` + 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 { diff --git a/core/unifi/usw.go b/core/unifi/usw.go index fcd75c9c..444577f2 100644 --- a/core/unifi/usw.go +++ b/core/unifi/usw.go @@ -7,80 +7,68 @@ import ( // USW represents all the data from the Ubiquiti Controller for a Unifi Switch. type USW struct { - SourceName string `json:"-"` - SiteName string `json:"-"` - ID string `json:"_id"` - Adopted FlexBool `json:"adopted"` - BoardRev FlexInt `json:"board_rev"` - Cfgversion string `json:"cfgversion"` - ConfigNetwork struct { - Type string `json:"type"` - IP string `json:"ip"` - } `json:"config_network"` - Dot1XPortctrlEnabled FlexBool `json:"dot1x_portctrl_enabled"` - EthernetTable []struct { - Mac string `json:"mac"` - NumPort FlexInt `json:"num_port,omitempty"` - Name string `json:"name"` - } `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 { + 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 []struct { - PortIdx FlexInt `json:"port_idx"` - Speed FlexInt `json:"speed"` - FullDuplex FlexBool `json:"full_duplex"` - Mac string `json:"mac"` - } `json:"downlink_table"` - Uplink Uplink `json:"uplink"` - LastUplink struct { + 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"` @@ -109,7 +97,8 @@ type MacTable struct { Mac string `json:"mac"` } -// Port is a physical connection on a USW or UDM. +// 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"` diff --git a/core/unifi/uxg.go b/core/unifi/uxg.go index 41604087..41ee2046 100644 --- a/core/unifi/uxg.go +++ b/core/unifi/uxg.go @@ -9,15 +9,15 @@ type UXG struct { IP string `json:"ip"` Mac string `json:"mac"` Model string `json:"model"` - ModelInLts bool `json:"model_in_lts"` - ModelInEol bool `json:"model_in_eol"` + ModelInLts FlexBool `json:"model_in_lts"` + ModelInEol FlexBool `json:"model_in_eol"` Type string `json:"type"` Version string `json:"version"` - Adopted bool `json:"adopted"` + Adopted FlexBool `json:"adopted"` SiteID string `json:"site_id"` Cfgversion string `json:"cfgversion"` SyslogKey string `json:"syslog_key"` - ConfigNetwork ConfigNetwork `json:"config_network"` + ConfigNetwork *ConfigNetwork `json:"config_network"` SetupID string `json:"setup_id"` LicenseState string `json:"license_state"` ConfigNetworkLan *ConfigNetworkLan `json:"config_network_lan"` @@ -28,22 +28,22 @@ type UXG struct { Architecture string `json:"architecture"` BoardRev FlexInt `json:"board_rev"` ManufacturerID FlexInt `json:"manufacturer_id"` - Internet bool `json:"internet"` - ModelIncompatible bool `json:"model_incompatible"` + 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 bool `json:"has_speaker"` - HasEth1 bool `json:"has_eth1"` + 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 bool `json:"has_fan"` - HasTemperature bool `json:"has_temperature"` + HasFan FlexBool `json:"has_fan"` + HasTemperature FlexBool `json:"has_temperature"` Temperatures []*Temperature `json:"temperatures"` - Storage []Storage `json:"storage"` + Storage []*Storage `json:"storage"` RulesetInterfaces interface{} `json:"ruleset_interfaces"` ConnectedAt FlexInt `json:"connected_at"` ProvisionedAt FlexInt `json:"provisioned_at"` @@ -51,36 +51,36 @@ type UXG struct { LedOverrideColor string `json:"led_override_color"` LedOverrideColorBrightness FlexInt `json:"led_override_color_brightness"` OutdoorModeOverride string `json:"outdoor_mode_override"` - LcmBrightnessOverride bool `json:"lcm_brightness_override"` - LcmIdleTimeoutOverride bool `json:"lcm_idle_timeout_override"` + LcmBrightnessOverride FlexBool `json:"lcm_brightness_override"` + LcmIdleTimeoutOverride FlexBool `json:"lcm_idle_timeout_override"` Name string `json:"name"` - Unsupported bool `json:"unsupported"` + Unsupported FlexBool `json:"unsupported"` UnsupportedReason FlexInt `json:"unsupported_reason"` Serial string `json:"serial"` HashID string `json:"hash_id"` - TwoPhaseAdopt bool `json:"two_phase_adopt"` + TwoPhaseAdopt FlexBool `json:"two_phase_adopt"` DeviceID string `json:"device_id"` State FlexInt `json:"state"` - StartDisconnectedMillis int64 `json:"start_disconnected_millis"` + StartDisconnectedMillis FlexInt `json:"start_disconnected_millis"` UpgradeState FlexInt `json:"upgrade_state"` - StartConnectedMillis int64 `json:"start_connected_millis"` + StartConnectedMillis FlexInt `json:"start_connected_millis"` LastSeen FlexInt `json:"last_seen"` Uptime FlexInt `json:"uptime"` UnderscoreUptime FlexInt `json:"_uptime"` - Locating bool `json:"locating"` + 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 bool `json:"overheating"` + Overheating FlexBool `json:"overheating"` GeoInfo map[string]*GeoInfo `json:"geo_info"` LedState *LedState `json:"led_state"` SpeedtestStatus *SpeedtestStatus `json:"speedtest-status"` - SpeedtestStatusSaved bool `json:"speedtest-status-saved"` + SpeedtestStatusSaved FlexBool `json:"speedtest-status-saved"` Wan1 *Wan `json:"wan1"` Wan2 *Wan `json:"wan2"` - Uplink Uplink `json:"uplink"` + Uplink *Uplink `json:"uplink"` DownlinkTable []*DownlinkTable `json:"downlink_table"` NetworkTable []*NetworkTable `json:"network_table"` KnownCfgversion string `json:"known_cfgversion"` @@ -91,8 +91,8 @@ type UXG struct { ConsideredLostAt FlexInt `json:"considered_lost_at"` Stat *UXGStat `json:"stat"` TxBytes FlexInt `json:"tx_bytes"` - RxBytes int64 `json:"rx_bytes"` - Bytes int64 `json:"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"` @@ -121,11 +121,13 @@ type DownlinkTable struct { 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"` @@ -141,14 +143,14 @@ type GeoInfo struct { 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"` } -// UDMStat holds the "stat" data for a dream machine. -// A dream machine is a USG + USW + Controller. +// UXGStat holds the "stat" data for a 10Gb gateway. type UXGStat struct { *Gw `json:"gw"` *Sw `json:"sw"` From 77279ea31c5ac4ff7ad30d0c9d68521c5e6fdbea Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Sun, 7 Mar 2021 18:23:16 -0800 Subject: [PATCH 4/5] fix all lint --- core/unifi/.travis.yml | 4 +- core/unifi/devices.go | 85 +++++++++++++++++++++++++++--------------- core/unifi/events.go | 6 ++- core/unifi/go.mod | 6 +-- core/unifi/go.sum | 6 +++ core/unifi/networks.go | 2 +- core/unifi/types.go | 18 ++++----- core/unifi/unifi.go | 28 +++++++++----- 8 files changed, 97 insertions(+), 58 deletions(-) diff --git a/core/unifi/.travis.yml b/core/unifi/.travis.yml index 654efb02..b8e3b955 100644 --- a/core/unifi/.travis.yml +++ b/core/unifi/.travis.yml @@ -1,9 +1,9 @@ language: go go: -- 1.14.x +- 1.15.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 latest script: -- golangci-lint run --enable-all +- golangci-lint run --enable-all -D exhaustivestruct,nlreturn - go test ./... diff --git a/core/unifi/devices.go b/core/unifi/devices.go index 505d2743..13a5bac0 100644 --- a/core/unifi/devices.go +++ b/core/unifi/devices.go @@ -36,48 +36,27 @@ func (u *Unifi) parseDevices(data []json.RawMessage, siteName string) *Devices { for _, r := range data { // Loop each item in the raw JSON message, detect its type and unmarshal it. - assetType := "" - - if o := make(map[string]interface{}); u.unmarshalDevice("map", r, &o) != nil { + 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 - } else if t, ok := o["type"].(string); ok { - assetType = t } + assetType, _ := o["type"].(string) u.DebugLog("Unmarshalling Device Type: %v, site %s ", assetType, siteName) // Choose which type to unmarshal into based on the "type" json key. switch assetType { // Unmarshal again into the correct type.. case "uap": - dev := &UAP{SiteName: siteName, SourceName: u.URL} - if u.unmarshalDevice(assetType, r, dev) == nil { - dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac)) - devices.UAPs = append(devices.UAPs, dev) - } + u.unmarshallUAP(siteName, r, devices) case "ugw", "usg": // in case they ever fix the name in the api. - dev := &USG{SiteName: siteName, SourceName: u.URL} - if u.unmarshalDevice(assetType, r, dev) == nil { - dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac)) - devices.USGs = append(devices.USGs, dev) - } + u.unmarshallUSG(siteName, r, devices) case "usw": - dev := &USW{SiteName: siteName, SourceName: u.URL} - if u.unmarshalDevice(assetType, r, dev) == nil { - dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac)) - devices.USWs = append(devices.USWs, dev) - } + u.unmarshallUSW(siteName, r, devices) case "udm": - dev := &UDM{SiteName: siteName, SourceName: u.URL} - if u.unmarshalDevice(assetType, r, dev) == nil { - dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac)) - devices.UDMs = append(devices.UDMs, dev) - } + u.unmarshallUDM(siteName, r, devices) 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) - } + u.unmarshallUXG(siteName, r, devices) default: u.ErrorLog("unknown asset type - %v - skipping", assetType) } @@ -86,6 +65,46 @@ func (u *Unifi) parseDevices(data []json.RawMessage, siteName string) *Devices { return devices } +func (u *Unifi) unmarshallUAP(siteName string, payload json.RawMessage, devices *Devices) { + dev := &UAP{SiteName: siteName, SourceName: u.URL} + if u.unmarshalDevice("uap", payload, dev) == nil { + dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac)) + devices.UAPs = append(devices.UAPs, dev) + } +} + +func (u *Unifi) unmarshallUSG(siteName string, payload json.RawMessage, devices *Devices) { + dev := &USG{SiteName: siteName, SourceName: u.URL} + if u.unmarshalDevice("ugw", payload, dev) == nil { + dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac)) + devices.USGs = append(devices.USGs, dev) + } +} + +func (u *Unifi) unmarshallUSW(siteName string, payload json.RawMessage, devices *Devices) { + dev := &USW{SiteName: siteName, SourceName: u.URL} + if u.unmarshalDevice("usw", payload, dev) == nil { + dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac)) + devices.USWs = append(devices.USWs, dev) + } +} + +func (u *Unifi) unmarshallUXG(siteName string, payload json.RawMessage, devices *Devices) { + dev := &UXG{SiteName: siteName, SourceName: u.URL} + if u.unmarshalDevice("uxg", payload, dev) == nil { + dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac)) + devices.UXGs = append(devices.UXGs, dev) + } +} + +func (u *Unifi) unmarshallUDM(siteName string, payload json.RawMessage, devices *Devices) { + dev := &UDM{SiteName: siteName, SourceName: u.URL} + if u.unmarshalDevice("udm", payload, dev) == nil { + dev.Name = strings.TrimSpace(pick(dev.Name, dev.Mac)) + devices.UDMs = append(devices.UDMs, dev) + } +} + // unmarshalDevice handles logging for the unmarshal operations in parseDevices(). func (u *Unifi) unmarshalDevice(dev string, data json.RawMessage, v interface{}) (err error) { if err = json.Unmarshal(data, v); err != nil { @@ -98,7 +117,11 @@ func (u *Unifi) unmarshalDevice(dev string, data json.RawMessage, v interface{}) u.DebugLog("==- https://github.com/unifi-poller/unifi/issues/new -==") } - return err + if err != nil { + return fmt.Errorf("json unmarshal: %w", err) + } + + return nil } // pick returns the first non empty string in a list. diff --git a/core/unifi/events.go b/core/unifi/events.go index d716daee..d43cf4f9 100644 --- a/core/unifi/events.go +++ b/core/unifi/events.go @@ -196,5 +196,9 @@ func (v *IPGeo) UnmarshalJSON(data []byte) error { v.CountryName = g.CountryName v.Organization = g.Organization - return err + if err != nil { + return fmt.Errorf("json unmarshal: %w", err) + } + + return nil } diff --git a/core/unifi/go.mod b/core/unifi/go.mod index 219ef9dc..9aade56e 100644 --- a/core/unifi/go.mod +++ b/core/unifi/go.mod @@ -1,10 +1,10 @@ module github.com/unifi-poller/unifi -go 1.14 +go 1.15 require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.4.0 - golang.org/x/net v0.0.0-20200625001655-4c5254603344 + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect + golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 ) diff --git a/core/unifi/go.sum b/core/unifi/go.sum index 52fd963e..d2c30146 100644 --- a/core/unifi/go.sum +++ b/core/unifi/go.sum @@ -13,10 +13,16 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= diff --git a/core/unifi/networks.go b/core/unifi/networks.go index 05915432..f0e4a876 100644 --- a/core/unifi/networks.go +++ b/core/unifi/networks.go @@ -38,7 +38,7 @@ func (u *Unifi) parseNetwork(data json.RawMessage, siteName string) (*Network, e return network, u.unmarshalDevice(siteName, data, network) } -// Network is metadata about a network managed by a UniFi controller +// Network is metadata about a network managed by a UniFi controller. type Network struct { DhcpdDNSEnabled FlexBool `json:"dhcpd_dns_enabled"` DhcpdEnabled FlexBool `json:"dhcpd_enabled"` diff --git a/core/unifi/types.go b/core/unifi/types.go index b3ce54a8..1413402a 100644 --- a/core/unifi/types.go +++ b/core/unifi/types.go @@ -7,13 +7,9 @@ import ( "strconv" "strings" "time" - - "github.com/pkg/errors" ) -var ( - errCannotUnmarshalFlexInt = fmt.Errorf("cannot unmarshal to FlexInt") -) +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. @@ -28,17 +24,17 @@ const ( 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 is Unifi Clients API Path. APIClientPath string = "/api/s/%s/stat/sta" // 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 is Unifi Controller Login API Path. APILoginPath string = "/api/login" - // APILoginPathNew is how we log into UDM 5.12.55+ + // APILoginPathNew is how we log into UDM 5.12.55+. APILoginPathNew string = "/api/auth/login" - // APIEventPathIDS returns Intrusion Detection/Prevention Systems Events + // 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" @@ -127,7 +123,7 @@ func (f *FlexInt) UnmarshalJSON(b []byte) error { var unk interface{} if err := json.Unmarshal(b, &unk); err != nil { - return err + return fmt.Errorf("json unmarshal: %w", err) } switch i := unk.(type) { @@ -141,7 +137,7 @@ func (f *FlexInt) UnmarshalJSON(b []byte) error { f.Txt = "0" f.Val = 0 default: - return errors.Wrapf(errCannotUnmarshalFlexInt, "%v", b) + return fmt.Errorf("%v: %w", b, ErrCannotUnmarshalFlexInt) } return nil diff --git a/core/unifi/unifi.go b/core/unifi/unifi.go index b5e8aa21..4f05463b 100644 --- a/core/unifi/unifi.go +++ b/core/unifi/unifi.go @@ -34,7 +34,7 @@ var ( func NewUnifi(config *Config) (*Unifi, error) { jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List}) if err != nil { - return nil, err + return nil, fmt.Errorf("creating cookiejar: %w", err) } config.URL = strings.TrimRight(config.URL, "/") @@ -85,7 +85,7 @@ func (u *Unifi) Login() error { resp, err := u.Do(req) if err != nil { - return err + return fmt.Errorf("making request: %w", err) } defer resp.Body.Close() // we need no data here. @@ -106,11 +106,21 @@ func (u *Unifi) Login() error { // check if this is a newer controller or not. If it is, we set new to true. // 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.NewRequest("GET", u.URL+"/", nil) + req, err := http.NewRequestWithContext(ctx, "GET", u.URL+"/", nil) if err != nil { - return err + return fmt.Errorf("creating request: %w", err) } // We can't share these cookies with other requests, so make a new client. @@ -126,7 +136,7 @@ func (u *Unifi) checkNewStyleAPI() error { resp, err := client.Do(req) if err != nil { - return err + return fmt.Errorf("making request: %w", err) } defer resp.Body.Close() // we need no data here. @@ -210,7 +220,7 @@ func (u *Unifi) UniReq(apiPath string, params string) (*http.Request, error) { 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) (*http.Request, error) { if params == "" { return nil, ErrNoParams @@ -239,7 +249,7 @@ func (u *Unifi) GetJSON(apiPath string, params ...string) ([]byte, error) { } // PutJSON uses a PUT call and returns the raw JSON in the same way as GetData -// Use this if you want to change data via the REST API +// Use this if you want to change data via the REST API. func (u *Unifi) PutJSON(apiPath string, params ...string) ([]byte, error) { req, err := u.UniReqPut(apiPath, strings.Join(params, " ")) if err != nil { @@ -262,14 +272,14 @@ func (u *Unifi) do(req *http.Request) ([]byte, error) { resp, err := u.Do(req.WithContext(ctx)) if err != nil { - return []byte{}, err + return []byte{}, fmt.Errorf("making request: %w", err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { - return body, err + return body, fmt.Errorf("reading response: %w", err) } // Save the returned CSRF header. From adba3633f80e0f447549bc013efea7d621ed7f57 Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Sun, 7 Mar 2021 18:34:41 -0800 Subject: [PATCH 5/5] add temperature to usg --- core/unifi/usg.go | 1 + 1 file changed, 1 insertion(+) diff --git a/core/unifi/usg.go b/core/unifi/usg.go index 751b76f8..9b265a5a 100644 --- a/core/unifi/usg.go +++ b/core/unifi/usg.go @@ -47,6 +47,7 @@ type USG struct { 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"`