commit
2099cbffe9
|
|
@ -9,9 +9,9 @@ GHUSER="davidnewhall"
|
||||||
# Github repo containing homebrew formula repo.
|
# Github repo containing homebrew formula repo.
|
||||||
HBREPO="golift/homebrew-mugs"
|
HBREPO="golift/homebrew-mugs"
|
||||||
MAINT="David Newhall II <david at sleepers dot pro>"
|
MAINT="David Newhall II <david at sleepers dot pro>"
|
||||||
VENDOR="Go Lift"
|
VENDOR="Go Lift <code at golift dot io>"
|
||||||
DESC="Polls a UniFi controller and stores metrics in InfluxDB"
|
DESC="Polls a UniFi controller and exports metrics to InfluxDB"
|
||||||
GOLANGCI_LINT_ARGS="--enable-all -D gochecknoglobals -e dupl"
|
GOLANGCI_LINT_ARGS="--enable-all -D gochecknoglobals -e dupl -e G101"
|
||||||
# Example must exist at examples/$CONFIG_FILE.example
|
# Example must exist at examples/$CONFIG_FILE.example
|
||||||
CONFIG_FILE="up.conf"
|
CONFIG_FILE="up.conf"
|
||||||
LICENSE="MIT"
|
LICENSE="MIT"
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,19 @@
|
||||||
[](https://github.com/davidnewhall/unifi-poller)
|
[](https://github.com/davidnewhall/unifi-poller)
|
||||||
[](https://travis-ci.org/davidnewhall/unifi-poller)
|
[](https://travis-ci.org/davidnewhall/unifi-poller)
|
||||||
|
|
||||||
Collect your UniFi controller data and send it to an InfluxDB instance.
|
Collect your UniFi controller data and export it to an InfluxDB instance.
|
||||||
[Grafana Dashboards](http://grafana.com/dashboards?search=unifi-poller) included.
|
[Five Grafana Dashboards](http://grafana.com/dashboards?search=unifi-poller)
|
||||||
Updated 2019.
|
included; with screenshots. Updated 2019.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
[See the Wiki!](https://github.com/davidnewhall/unifi-poller/wiki/Installation)
|
||||||
|
We have a special place for [Docker Users](https://github.com/davidnewhall/unifi-poller/wiki/Docker).
|
||||||
|
|
||||||
|
I'm willing to help if you have troubles.
|
||||||
|
Open an [Issue](https://github.com/davidnewhall/unifi-poller/issues) and
|
||||||
|
we'll figure out how to get things working for you. You can also check out
|
||||||
|
my [Discord server](https://discord.gg/DyVsMyt).
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
|
|
@ -27,8 +37,8 @@ Ubiquiti also provides a dedicated hardware device called a
|
||||||
[CloudKey](https://www.ui.com/unifi/unifi-cloud-key/) that runs the controller software.
|
[CloudKey](https://www.ui.com/unifi/unifi-cloud-key/) that runs the controller software.
|
||||||
|
|
||||||
UniFi Poller is a small Golang application that runs on Windows, macOS, Linux or
|
UniFi Poller is a small Golang application that runs on Windows, macOS, Linux or
|
||||||
Docker. It polls a UniFi controller every 30 seconds for measurements and stores
|
Docker. It polls a UniFi controller every 30 seconds for measurements and exports
|
||||||
the data in an Influx database. A small setup with 2 access points, 1 switch, 1
|
the data to an Influx database. A small setup with 2 access points, 1 switch, 1
|
||||||
gateway and 40 clients produces over 3000 fields (metrics).
|
gateway and 40 clients produces over 3000 fields (metrics).
|
||||||
|
|
||||||
This application requires your controller to be running all the time. If you run
|
This application requires your controller to be running all the time. If you run
|
||||||
|
|
@ -38,11 +48,6 @@ a UniFi controller, there's no excuse not to install
|
||||||
You'll have a plethora of data at your fingertips and the ability to craft custom
|
You'll have a plethora of data at your fingertips and the ability to craft custom
|
||||||
graphs to slice the data any way you choose. Good luck!
|
graphs to slice the data any way you choose. Good luck!
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
[See the Wiki!](https://github.com/davidnewhall/unifi-poller/wiki/Installation)
|
|
||||||
We have a special place for [Docker Users](https://github.com/davidnewhall/unifi-poller/wiki/Docker).
|
|
||||||
|
|
||||||
# Backstory
|
# Backstory
|
||||||
|
|
||||||
Okay, so here's the deal. I found a simple piece of code on GitHub that
|
Okay, so here's the deal. I found a simple piece of code on GitHub that
|
||||||
|
|
@ -54,44 +59,46 @@ with me. I probably wouldn't have made it this far if
|
||||||
code I started with. Many props my man.
|
code I started with. Many props my man.
|
||||||
|
|
||||||
The original code pulled only the client data. This app now pulls data
|
The original code pulled only the client data. This app now pulls data
|
||||||
for clients, access points, security gateways and switches. I currently
|
for clients, access points, security gateways and switches. I used to
|
||||||
own two UAP-AC-PROs, one USG-3 and one US-24-250W. If your devices differ
|
own two UAP-AC-PROs, one USG-3 and one US-24-250W, but have since upgraded
|
||||||
this app may miss some data. I'm willing to help and make it better.
|
a few devices. Many other users have also provided feedback to improve this app,
|
||||||
Open an [Issue](https://github.com/davidnewhall/unifi-poller/issues) and
|
and we have reports of it working on nearly every switch, AP and gateway; UDM included.
|
||||||
we'll figure out how to get things working for you.
|
|
||||||
|
|
||||||
# What's this data good for?
|
# What's this data good for?
|
||||||
|
|
||||||
I've been trying to get my UAP data into Grafana. Sure, google search that.
|
I've been trying to get my UAP data into Grafana. Sure, google search that.
|
||||||
You'll find [this](https://community.ubnt.com/t5/UniFi-Wireless/Grafana-dashboard-for-UniFi-APs-now-available/td-p/1833532).
|
You'll find [this](https://community.ubnt.com/t5/UniFi-Wireless/Grafana-dashboard-for-UniFi-APs-now-available/td-p/1833532). What if you don't want to deal with SNMP?
|
||||||
And that's all you'll find. What if you don't want to deal with SNMP?
|
Well, here you go. I've replicated 400% of what you see on those SNMP-powered
|
||||||
Well, here you go. I've replicated 90% of what you see on those SNMP-powered
|
|
||||||
dashboards with this Go app running on the same mac as my UniFi controller.
|
dashboards with this Go app running on the same mac as my UniFi controller.
|
||||||
All without enabling SNMP nor trying to understand those OIDs. Mad props
|
All without enabling SNMP nor trying to understand those OIDs. Mad props
|
||||||
to [waterside](https://community.ubnt.com/t5/user/viewprofilepage/user-id/303058)
|
to [waterside](https://community.ubnt.com/t5/user/viewprofilepage/user-id/303058)
|
||||||
for making this dashboard; it gave me a fantastic start to making my own.
|
for making this dashboard; it gave me a fantastic start to making my own dashboards.
|
||||||
|
This app is up to five dashboards now!
|
||||||
|
|
||||||
|
Update 9/2019:
|
||||||
|
|
||||||
|
Some new "prometheus exporters" are showing up. I admit I don't know much about
|
||||||
|
Prometheus, but so far the prometheus exporting apps I've seen are missing many
|
||||||
|
data points. Let me know if Prometheus is something you'd like to see support for.
|
||||||
|
|
||||||
I've also created [another forum post](https://community.ui.com/questions/Unifi-Poller-Store-Unifi-Controller-Metrics-in-InfluxDB-without-SNMP/58a0ea34-d2b3-41cd-93bb-d95d3896d1a1) you may use to get additional help.
|
I've also created [another forum post](https://community.ui.com/questions/Unifi-Poller-Store-Unifi-Controller-Metrics-in-InfluxDB-without-SNMP/58a0ea34-d2b3-41cd-93bb-d95d3896d1a1) you may use to get additional help.
|
||||||
|
|
||||||
# Development
|
# Development
|
||||||
|
|
||||||
The "What now..." section below used to be a lot larger. I've received a lot of
|
The "What now..." section below used to be a lot longer. I've received a lot of
|
||||||
support, feedback and assistance from the community. Many thanks! This app is
|
support, feedback and assistance from the community. Many thanks! This app is
|
||||||
extremely stable with a tiny memory and cpu footprint. I imagine one day we'll
|
extremely stable with a tiny memory and cpu footprint. I imagine one day we'll
|
||||||
figure out how to make it run on a CloudKey device directly; once I have one
|
figure out how to make it run on a CloudKey or UDM directly; once I have one
|
||||||
personally that will be my goal. In addition to stability, this app provides
|
personally that will be my goal. In addition to stability, this app provides
|
||||||
an intuitive installation and configuration process. Maintenance is a breeze too.
|
an intuitive installation and configuration process. Maintenance is a breeze too.
|
||||||
|
|
||||||
I'm not a software engineer, I'm a a firm believer in operational excellence above
|
|
||||||
all else. To that end, this app shall remain easy, intuitive and highly adaptable.
|
|
||||||
I'm totally open to add more configuration options if someone raises a need or concern.
|
|
||||||
|
|
||||||
You can control this app with puppet, chef, saltstack, homebrew or a simple bash
|
You can control this app with puppet, chef, saltstack, homebrew or a simple bash
|
||||||
script if you needed to. It's available for macOS, Linux and Docker. It comes with
|
script if you needed to. It's available for macOS, Linux and Docker. It comes with
|
||||||
a systemd service unit that allows you automatically start it up on most Linux
|
a systemd service unit that allows you automatically start it up on most Linux
|
||||||
hosts. It works just fine on [Windows](https://github.com/davidnewhall/unifi-poller/wiki/Windows) too.
|
hosts. It works just fine on [Windows](https://github.com/davidnewhall/unifi-poller/wiki/Windows) too.
|
||||||
|
Most people prefer Docker, and this app is right at home in that environment.
|
||||||
|
|
||||||
The unifi data extraction is provided as an [external library](https://godoc.org/golift.io/unifi),
|
The UniFi data extraction is provided as an [external library](https://godoc.org/golift.io/unifi),
|
||||||
and you can import that code directly without futzing with this application. That
|
and you can import that code directly without futzing with this application. That
|
||||||
means, if you wanted to do something like make telegraf collect your data instead
|
means, if you wanted to do something like make telegraf collect your data instead
|
||||||
of UniFi Poller you can achieve that with a little bit of Go code. You could write
|
of UniFi Poller you can achieve that with a little bit of Go code. You could write
|
||||||
|
|
@ -102,20 +109,11 @@ and can be used in other projects.
|
||||||
|
|
||||||
# What now...
|
# What now...
|
||||||
|
|
||||||
### Are there other devices that need to be included?
|
We are at a point where the application works as intended, and we are trying to
|
||||||
|
maintain the status quo. Ubiquiti releases updates, things break, we fix it;
|
||||||
I have: switch, router, access point. Three total, and the type structs are
|
round and round we go. If you have new hardware or a new controller version, and
|
||||||
likely missing data for variants of these devices. e.g. Some UAPs have more
|
something is not showing up, please open an
|
||||||
radios, I probably didn't properly account for that. Some gateways have more
|
[Issue](https://github.com/davidnewhall/unifi-poller/issues) so we can fix it.
|
||||||
ports, some switches have 10Gb, etc. These are things I do not have data on
|
|
||||||
to write code for. If you have these devices, and want them graphed, open an
|
|
||||||
Issue and lets discuss.
|
|
||||||
|
|
||||||
### Radios, Frequencies, Interfaces, vAPs
|
|
||||||
|
|
||||||
My access points only seem to have two radios, one interface and vAP per radio.
|
|
||||||
I'm not sure if the graphs, as-is, provide enough insight into APs with other
|
|
||||||
configurations. Help me figure that out?
|
|
||||||
|
|
||||||
# What's it look like?
|
# What's it look like?
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,17 +22,43 @@ const (
|
||||||
defaultUnifURL = "https://127.0.0.1:8443"
|
defaultUnifURL = "https://127.0.0.1:8443"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// These are environment variables that can be used to override configuration.
|
||||||
|
// Useful for Docker users.
|
||||||
|
const (
|
||||||
|
ENVConfigMode = "UP_POLLING_MODE"
|
||||||
|
ENVConfigInfluxDB = "UP_INFLUX_DB"
|
||||||
|
ENVConfigInfluxUser = "UP_INFLUX_USER"
|
||||||
|
ENVConfigInfluxPass = "UP_INFLUX_PASS"
|
||||||
|
ENVConfigInfluxURL = "UP_INFLUX_URL"
|
||||||
|
ENVConfigUnifiUser = "UP_UNIFI_USER"
|
||||||
|
ENVConfigUnifiPass = "UP_UNIFI_PASS"
|
||||||
|
ENVConfigUnifiBase = "UP_UNIFI_URL"
|
||||||
|
ENVConfigReAuth = "UP_REAUTHENTICATE"
|
||||||
|
ENVConfigVerifySSL = "UP_VERIFY_SSL"
|
||||||
|
ENVConfigCollectIDS = "UP_COLLECT_IDS"
|
||||||
|
ENVConfigQuiet = "UP_QUIET_MODE"
|
||||||
|
ENVConfigDebug = "UP_DEBUG_MODE"
|
||||||
|
ENVConfigInterval = "UP_POLLING_INTERVAL"
|
||||||
|
ENVConfigMaxErrors = "UP_MAX_ERRORS"
|
||||||
|
ENVConfigSites = "UP_POLL_SITES"
|
||||||
|
)
|
||||||
|
|
||||||
// UnifiPoller contains the application startup data, and auth info for UniFi & Influx.
|
// UnifiPoller contains the application startup data, and auth info for UniFi & Influx.
|
||||||
type UnifiPoller struct {
|
type UnifiPoller struct {
|
||||||
|
Influx influx.Client
|
||||||
|
Unifi *unifi.Unifi
|
||||||
|
Flag *Flag
|
||||||
|
Config *Config
|
||||||
|
errorCount int
|
||||||
|
LastCheck time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag represents the CLI args available and their settings.
|
||||||
|
type Flag struct {
|
||||||
ConfigFile string
|
ConfigFile string
|
||||||
DumpJSON string
|
DumpJSON string
|
||||||
ShowVer bool
|
ShowVer bool
|
||||||
Flag *pflag.FlagSet
|
*pflag.FlagSet
|
||||||
errorCount int
|
|
||||||
LastCheck time.Time
|
|
||||||
influx.Client
|
|
||||||
*unifi.Unifi
|
|
||||||
*Config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metrics contains all the data from the controller and an influx endpoint to send it to.
|
// Metrics contains all the data from the controller and an influx endpoint to send it to.
|
||||||
|
|
|
||||||
|
|
@ -10,17 +10,18 @@ import (
|
||||||
|
|
||||||
// DumpJSONPayload prints raw json from the UniFi Controller.
|
// DumpJSONPayload prints raw json from the UniFi Controller.
|
||||||
func (u *UnifiPoller) DumpJSONPayload() (err error) {
|
func (u *UnifiPoller) DumpJSONPayload() (err error) {
|
||||||
u.Quiet = true
|
u.Config.Quiet = true
|
||||||
u.Unifi, err = unifi.NewUnifi(&unifi.Config{
|
u.Unifi, err = unifi.NewUnifi(&unifi.Config{
|
||||||
User: u.UnifiUser,
|
User: u.Config.UnifiUser,
|
||||||
Pass: u.UnifiPass,
|
Pass: u.Config.UnifiPass,
|
||||||
URL: u.UnifiBase,
|
URL: u.Config.UnifiBase,
|
||||||
VerifySSL: u.VerifySSL,
|
VerifySSL: u.Config.VerifySSL,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Fprintln(os.Stderr, "[INFO] Authenticated to UniFi Controller @", u.UnifiBase, "as user", u.UnifiUser)
|
fmt.Fprintf(os.Stderr, "[INFO] Authenticated to UniFi Controller @ %v as user %v",
|
||||||
|
u.Config.UnifiBase, u.Config.UnifiUser)
|
||||||
if err := u.CheckSites(); err != nil {
|
if err := u.CheckSites(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -30,12 +31,12 @@ func (u *UnifiPoller) DumpJSONPayload() (err error) {
|
||||||
switch sites, err := u.GetFilteredSites(); {
|
switch sites, err := u.GetFilteredSites(); {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return err
|
return err
|
||||||
case StringInSlice(u.DumpJSON, []string{"d", "device", "devices"}):
|
case StringInSlice(u.Flag.DumpJSON, []string{"d", "device", "devices"}):
|
||||||
return u.dumpSitesJSON(unifi.DevicePath, "Devices", sites)
|
return u.dumpSitesJSON(unifi.DevicePath, "Devices", sites)
|
||||||
case StringInSlice(u.DumpJSON, []string{"client", "clients", "c"}):
|
case StringInSlice(u.Flag.DumpJSON, []string{"client", "clients", "c"}):
|
||||||
return u.dumpSitesJSON(unifi.ClientPath, "Clients", sites)
|
return u.dumpSitesJSON(unifi.ClientPath, "Clients", sites)
|
||||||
case strings.HasPrefix(u.DumpJSON, "other "):
|
case strings.HasPrefix(u.Flag.DumpJSON, "other "):
|
||||||
apiPath := strings.SplitN(u.DumpJSON, " ", 2)[1]
|
apiPath := strings.SplitN(u.Flag.DumpJSON, " ", 2)[1]
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "[INFO] Dumping Path '%s':\n", apiPath)
|
_, _ = fmt.Fprintf(os.Stderr, "[INFO] Dumping Path '%s':\n", apiPath)
|
||||||
return u.PrintRawAPIJSON(apiPath)
|
return u.PrintRawAPIJSON(apiPath)
|
||||||
default:
|
default:
|
||||||
|
|
@ -46,7 +47,8 @@ func (u *UnifiPoller) DumpJSONPayload() (err error) {
|
||||||
func (u *UnifiPoller) dumpSitesJSON(path, name string, sites unifi.Sites) error {
|
func (u *UnifiPoller) dumpSitesJSON(path, name string, sites unifi.Sites) error {
|
||||||
for _, s := range sites {
|
for _, s := range sites {
|
||||||
apiPath := fmt.Sprintf(path, s.Name)
|
apiPath := fmt.Sprintf(path, s.Name)
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "[INFO] Dumping %s: '%s' JSON for site: %s (%s):\n", name, apiPath, s.Desc, s.Name)
|
_, _ = fmt.Fprintf(os.Stderr, "[INFO] Dumping %s: '%s' JSON for site: %s (%s):\n",
|
||||||
|
name, apiPath, s.Desc, s.Name)
|
||||||
if err := u.PrintRawAPIJSON(apiPath); err != nil {
|
if err := u.PrintRawAPIJSON(apiPath); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -56,7 +58,7 @@ func (u *UnifiPoller) dumpSitesJSON(path, name string, sites unifi.Sites) error
|
||||||
|
|
||||||
// PrintRawAPIJSON prints the raw json for a user-provided path on a UniFi Controller.
|
// PrintRawAPIJSON prints the raw json for a user-provided path on a UniFi Controller.
|
||||||
func (u *UnifiPoller) PrintRawAPIJSON(apiPath string) error {
|
func (u *UnifiPoller) PrintRawAPIJSON(apiPath string) error {
|
||||||
body, err := u.GetJSON(apiPath)
|
body, err := u.Unifi.GetJSON(apiPath)
|
||||||
fmt.Println(string(body))
|
fmt.Println(string(body))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import (
|
||||||
func (u *UnifiPoller) LogError(err error, prefix string) {
|
func (u *UnifiPoller) LogError(err error, prefix string) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
u.errorCount++
|
u.errorCount++
|
||||||
_ = log.Output(2, fmt.Sprintf("[ERROR] (%v/%v) %v: %v", u.errorCount, u.MaxErrors, prefix, err))
|
_ = log.Output(2, fmt.Sprintf("[ERROR] (%v/%v) %v: %v", u.errorCount, u.Config.MaxErrors, prefix, err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -27,14 +27,14 @@ func StringInSlice(str string, slice []string) bool {
|
||||||
|
|
||||||
// Logf prints a log entry if quiet is false.
|
// Logf prints a log entry if quiet is false.
|
||||||
func (u *UnifiPoller) Logf(m string, v ...interface{}) {
|
func (u *UnifiPoller) Logf(m string, v ...interface{}) {
|
||||||
if !u.Quiet {
|
if !u.Config.Quiet {
|
||||||
_ = log.Output(2, fmt.Sprintf("[INFO] "+m, v...))
|
_ = log.Output(2, fmt.Sprintf("[INFO] "+m, v...))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogDebugf prints a debug log entry if debug is true and quite is false
|
// LogDebugf prints a debug log entry if debug is true and quite is false
|
||||||
func (u *UnifiPoller) LogDebugf(m string, v ...interface{}) {
|
func (u *UnifiPoller) LogDebugf(m string, v ...interface{}) {
|
||||||
if u.Debug && !u.Quiet {
|
if u.Config.Debug && !u.Config.Quiet {
|
||||||
_ = log.Output(2, fmt.Sprintf("[DEBUG] "+m, v...))
|
_ = log.Output(2, fmt.Sprintf("[DEBUG] "+m, v...))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -43,3 +43,26 @@ func (u *UnifiPoller) LogDebugf(m string, v ...interface{}) {
|
||||||
func (u *UnifiPoller) LogErrorf(m string, v ...interface{}) {
|
func (u *UnifiPoller) LogErrorf(m string, v ...interface{}) {
|
||||||
_ = log.Output(2, fmt.Sprintf("[ERROR] "+m, v...))
|
_ = log.Output(2, fmt.Sprintf("[ERROR] "+m, v...))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pick returns the first non empty string in a list.
|
||||||
|
// used in a few places around this library.
|
||||||
|
func pick(strings ...string) string {
|
||||||
|
for _, s := range strings {
|
||||||
|
if s != "" {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseBool returns true/false if the string is "true" or "false", otherwise returns e value.
|
||||||
|
func parseBool(s string, e bool) bool {
|
||||||
|
switch s {
|
||||||
|
case "true", "t":
|
||||||
|
return true
|
||||||
|
case "false", "f":
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@ import (
|
||||||
// UAPPoints generates Wireless-Access-Point datapoints for InfluxDB.
|
// UAPPoints generates Wireless-Access-Point datapoints for InfluxDB.
|
||||||
// These points can be passed directly to influx.
|
// These points can be passed directly to influx.
|
||||||
func UAPPoints(u *unifi.UAP, now time.Time) ([]*influx.Point, error) {
|
func UAPPoints(u *unifi.UAP, now time.Time) ([]*influx.Point, error) {
|
||||||
|
if u.Stat.Ap == nil {
|
||||||
|
u.Stat.Ap = &unifi.Ap{}
|
||||||
|
}
|
||||||
tags := map[string]string{
|
tags := map[string]string{
|
||||||
"id": u.ID,
|
"id": u.ID,
|
||||||
"mac": u.Mac,
|
"mac": u.Mac,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,12 @@ import (
|
||||||
// UDMPoints generates Unifi Gateway datapoints for InfluxDB.
|
// UDMPoints generates Unifi Gateway datapoints for InfluxDB.
|
||||||
// These points can be passed directly to influx.
|
// These points can be passed directly to influx.
|
||||||
func UDMPoints(u *unifi.UDM, now time.Time) ([]*influx.Point, error) {
|
func UDMPoints(u *unifi.UDM, now time.Time) ([]*influx.Point, error) {
|
||||||
|
if u.Stat.Sw == nil {
|
||||||
|
u.Stat.Sw = &unifi.Sw{}
|
||||||
|
}
|
||||||
|
if u.Stat.Gw == nil {
|
||||||
|
u.Stat.Gw = &unifi.Gw{}
|
||||||
|
}
|
||||||
tags := map[string]string{
|
tags := map[string]string{
|
||||||
"id": u.ID,
|
"id": u.ID,
|
||||||
"mac": u.Mac,
|
"mac": u.Mac,
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@ import (
|
||||||
// USGPoints generates Unifi Gateway datapoints for InfluxDB.
|
// USGPoints generates Unifi Gateway datapoints for InfluxDB.
|
||||||
// These points can be passed directly to influx.
|
// These points can be passed directly to influx.
|
||||||
func USGPoints(u *unifi.USG, now time.Time) ([]*influx.Point, error) {
|
func USGPoints(u *unifi.USG, now time.Time) ([]*influx.Point, error) {
|
||||||
|
if u.Stat.Gw == nil {
|
||||||
|
u.Stat.Gw = &unifi.Gw{}
|
||||||
|
}
|
||||||
tags := map[string]string{
|
tags := map[string]string{
|
||||||
"id": u.ID,
|
"id": u.ID,
|
||||||
"mac": u.Mac,
|
"mac": u.Mac,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@ import (
|
||||||
// USWPoints generates Unifi Switch datapoints for InfluxDB.
|
// USWPoints generates Unifi Switch datapoints for InfluxDB.
|
||||||
// These points can be passed directly to influx.
|
// These points can be passed directly to influx.
|
||||||
func USWPoints(u *unifi.USW, now time.Time) ([]*influx.Point, error) {
|
func USWPoints(u *unifi.USW, now time.Time) ([]*influx.Point, error) {
|
||||||
|
if u.Stat.Sw == nil {
|
||||||
|
u.Stat.Sw = &unifi.Sw{}
|
||||||
|
}
|
||||||
tags := map[string]string{
|
tags := map[string]string{
|
||||||
"id": u.ID,
|
"id": u.ID,
|
||||||
"mac": u.Mac,
|
"mac": u.Mac,
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -21,8 +22,9 @@ import (
|
||||||
// Parses flags, parses config and executes Run().
|
// Parses flags, parses config and executes Run().
|
||||||
func Start() error {
|
func Start() error {
|
||||||
log.SetFlags(log.LstdFlags)
|
log.SetFlags(log.LstdFlags)
|
||||||
up := &UnifiPoller{}
|
up := &UnifiPoller{Flag: &Flag{}}
|
||||||
if up.ParseFlags(os.Args[1:]); up.ShowVer {
|
up.Flag.Parse(os.Args[1:])
|
||||||
|
if up.Flag.ShowVer {
|
||||||
fmt.Printf("unifi-poller v%s\n", Version)
|
fmt.Printf("unifi-poller v%s\n", Version)
|
||||||
return nil // don't run anything else w/ version request.
|
return nil // don't run anything else w/ version request.
|
||||||
}
|
}
|
||||||
|
|
@ -33,18 +35,46 @@ func Start() error {
|
||||||
return up.Run()
|
return up.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseFlags runs the parser.
|
// Parse turns CLI arguments into data structures. Called by Start() on startup.
|
||||||
func (u *UnifiPoller) ParseFlags(args []string) {
|
func (f *Flag) Parse(args []string) {
|
||||||
u.Flag = pflag.NewFlagSet("unifi-poller", pflag.ExitOnError)
|
f.FlagSet = pflag.NewFlagSet("unifi-poller", pflag.ExitOnError)
|
||||||
u.Flag.Usage = func() {
|
f.Usage = func() {
|
||||||
fmt.Println("Usage: unifi-poller [--config=filepath] [--version]")
|
fmt.Println("Usage: unifi-poller [--config=filepath] [--version]")
|
||||||
u.Flag.PrintDefaults()
|
f.PrintDefaults()
|
||||||
}
|
}
|
||||||
u.Flag.StringVarP(&u.DumpJSON, "dumpjson", "j", "",
|
f.StringVarP(&f.DumpJSON, "dumpjson", "j", "",
|
||||||
"This debug option prints a json payload and exits. See man page for more.")
|
"This debug option prints a json payload and exits. See man page for more.")
|
||||||
u.Flag.StringVarP(&u.ConfigFile, "config", "c", DefaultConfFile, "Poller Config File (TOML Format)")
|
f.StringVarP(&f.ConfigFile, "config", "c", DefaultConfFile, "Poller Config File (TOML Format)")
|
||||||
u.Flag.BoolVarP(&u.ShowVer, "version", "v", false, "Print the version and exit")
|
f.BoolVarP(&f.ShowVer, "version", "v", false, "Print the version and exit")
|
||||||
_ = u.Flag.Parse(args)
|
_ = f.FlagSet.Parse(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setEnvVarOptions copies environment variables into configuration values.
|
||||||
|
// This is useful for Docker users that find it easier to pass ENV variables
|
||||||
|
// that a specific configuration file.
|
||||||
|
func (u *UnifiPoller) setEnvVarOptions() {
|
||||||
|
u.Config.Mode = pick(os.Getenv(ENVConfigMode), u.Config.Mode)
|
||||||
|
u.Config.InfluxDB = pick(os.Getenv(ENVConfigInfluxDB), u.Config.InfluxDB)
|
||||||
|
u.Config.InfluxUser = pick(os.Getenv(ENVConfigInfluxUser), u.Config.InfluxUser)
|
||||||
|
u.Config.InfluxPass = pick(os.Getenv(ENVConfigInfluxPass), u.Config.InfluxPass)
|
||||||
|
u.Config.InfluxURL = pick(os.Getenv(ENVConfigInfluxURL), u.Config.InfluxURL)
|
||||||
|
u.Config.UnifiUser = pick(os.Getenv(ENVConfigUnifiUser), u.Config.UnifiUser)
|
||||||
|
u.Config.UnifiPass = pick(os.Getenv(ENVConfigUnifiPass), u.Config.UnifiPass)
|
||||||
|
u.Config.UnifiBase = pick(os.Getenv(ENVConfigUnifiBase), u.Config.UnifiBase)
|
||||||
|
u.Config.ReAuth = parseBool(os.Getenv(ENVConfigReAuth), u.Config.ReAuth)
|
||||||
|
u.Config.VerifySSL = parseBool(os.Getenv(ENVConfigVerifySSL), u.Config.VerifySSL)
|
||||||
|
u.Config.CollectIDS = parseBool(os.Getenv(ENVConfigCollectIDS), u.Config.CollectIDS)
|
||||||
|
u.Config.Quiet = parseBool(os.Getenv(ENVConfigQuiet), u.Config.Quiet)
|
||||||
|
u.Config.Debug = parseBool(os.Getenv(ENVConfigDebug), u.Config.Debug)
|
||||||
|
if e := os.Getenv(ENVConfigInterval); e != "" {
|
||||||
|
_ = u.Config.Interval.UnmarshalText([]byte(e))
|
||||||
|
}
|
||||||
|
if e := os.Getenv(ENVConfigMaxErrors); e != "" {
|
||||||
|
u.Config.MaxErrors, _ = strconv.Atoi(e)
|
||||||
|
}
|
||||||
|
if e := os.Getenv(ENVConfigSites); e != "" {
|
||||||
|
u.Config.Sites = strings.Split(e, ",")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConfig parses and returns our configuration data.
|
// GetConfig parses and returns our configuration data.
|
||||||
|
|
@ -56,21 +86,22 @@ func (u *UnifiPoller) GetConfig() error {
|
||||||
InfluxPass: defaultInfxPass,
|
InfluxPass: defaultInfxPass,
|
||||||
InfluxDB: defaultInfxDb,
|
InfluxDB: defaultInfxDb,
|
||||||
UnifiUser: defaultUnifUser,
|
UnifiUser: defaultUnifUser,
|
||||||
UnifiPass: os.Getenv("UNIFI_PASSWORD"),
|
UnifiPass: os.Getenv("UNIFI_PASSWORD"), // deprecated name.
|
||||||
UnifiBase: defaultUnifURL,
|
UnifiBase: defaultUnifURL,
|
||||||
Interval: Duration{defaultInterval},
|
Interval: Duration{defaultInterval},
|
||||||
Sites: []string{"default"},
|
Sites: []string{"default"},
|
||||||
Quiet: u.DumpJSON != "",
|
Quiet: u.Flag.DumpJSON != "", //s uppress the following u.Logf line.
|
||||||
}
|
}
|
||||||
u.Logf("Loading Configuration File: %s", u.ConfigFile)
|
u.Logf("Loading Configuration File: %s", u.Flag.ConfigFile)
|
||||||
switch buf, err := ioutil.ReadFile(u.ConfigFile); {
|
defer u.setEnvVarOptions() // Set env variable overrides when done here.
|
||||||
|
switch buf, err := ioutil.ReadFile(u.Flag.ConfigFile); {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return err
|
return err
|
||||||
case strings.Contains(u.ConfigFile, ".json"):
|
case strings.Contains(u.Flag.ConfigFile, ".json"):
|
||||||
return json.Unmarshal(buf, u.Config)
|
return json.Unmarshal(buf, u.Config)
|
||||||
case strings.Contains(u.ConfigFile, ".xml"):
|
case strings.Contains(u.Flag.ConfigFile, ".xml"):
|
||||||
return xml.Unmarshal(buf, u.Config)
|
return xml.Unmarshal(buf, u.Config)
|
||||||
case strings.Contains(u.ConfigFile, ".yaml"):
|
case strings.Contains(u.Flag.ConfigFile, ".yaml"):
|
||||||
return yaml.Unmarshal(buf, u.Config)
|
return yaml.Unmarshal(buf, u.Config)
|
||||||
default:
|
default:
|
||||||
return toml.Unmarshal(buf, u.Config)
|
return toml.Unmarshal(buf, u.Config)
|
||||||
|
|
@ -79,10 +110,10 @@ func (u *UnifiPoller) GetConfig() error {
|
||||||
|
|
||||||
// Run invokes all the application logic and routines.
|
// Run invokes all the application logic and routines.
|
||||||
func (u *UnifiPoller) Run() (err error) {
|
func (u *UnifiPoller) Run() (err error) {
|
||||||
if u.DumpJSON != "" {
|
if u.Flag.DumpJSON != "" {
|
||||||
return u.DumpJSONPayload()
|
return u.DumpJSONPayload()
|
||||||
}
|
}
|
||||||
if u.Debug {
|
if u.Config.Debug {
|
||||||
log.SetFlags(log.Lshortfile | log.Lmicroseconds | log.Ldate)
|
log.SetFlags(log.Lshortfile | log.Lmicroseconds | log.Ldate)
|
||||||
u.LogDebugf("Debug Logging Enabled")
|
u.LogDebugf("Debug Logging Enabled")
|
||||||
}
|
}
|
||||||
|
|
@ -90,12 +121,13 @@ func (u *UnifiPoller) Run() (err error) {
|
||||||
if err = u.GetUnifi(); err != nil {
|
if err = u.GetUnifi(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
u.Logf("Polling UniFi Controller at %s v%s as user %s. Sites: %v", u.UnifiBase, u.ServerVersion, u.UnifiUser, u.Sites)
|
u.Logf("Polling UniFi Controller at %s v%s as user %s. Sites: %v",
|
||||||
|
u.Config.UnifiBase, u.Unifi.ServerVersion, u.Config.UnifiUser, u.Config.Sites)
|
||||||
if err = u.GetInfluxDB(); err != nil {
|
if err = u.GetInfluxDB(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
u.Logf("Logging Measurements to InfluxDB at %s as user %s", u.InfluxURL, u.InfluxUser)
|
u.Logf("Logging Measurements to InfluxDB at %s as user %s", u.Config.InfluxURL, u.Config.InfluxUser)
|
||||||
switch strings.ToLower(u.Mode) {
|
switch strings.ToLower(u.Config.Mode) {
|
||||||
case "influxlambda", "lambdainflux", "lambda_influx", "influx_lambda":
|
case "influxlambda", "lambdainflux", "lambda_influx", "influx_lambda":
|
||||||
u.LogDebugf("Lambda Mode Enabled")
|
u.LogDebugf("Lambda Mode Enabled")
|
||||||
u.LastCheck = time.Now()
|
u.LastCheck = time.Now()
|
||||||
|
|
@ -107,10 +139,10 @@ func (u *UnifiPoller) Run() (err error) {
|
||||||
|
|
||||||
// GetInfluxDB returns an InfluxDB interface.
|
// GetInfluxDB returns an InfluxDB interface.
|
||||||
func (u *UnifiPoller) GetInfluxDB() (err error) {
|
func (u *UnifiPoller) GetInfluxDB() (err error) {
|
||||||
u.Client, err = influx.NewHTTPClient(influx.HTTPConfig{
|
u.Influx, err = influx.NewHTTPClient(influx.HTTPConfig{
|
||||||
Addr: u.InfluxURL,
|
Addr: u.Config.InfluxURL,
|
||||||
Username: u.InfluxUser,
|
Username: u.Config.InfluxUser,
|
||||||
Password: u.InfluxPass,
|
Password: u.Config.InfluxPass,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("influxdb: %v", err)
|
return fmt.Errorf("influxdb: %v", err)
|
||||||
|
|
@ -122,10 +154,10 @@ func (u *UnifiPoller) GetInfluxDB() (err error) {
|
||||||
func (u *UnifiPoller) GetUnifi() (err error) {
|
func (u *UnifiPoller) GetUnifi() (err error) {
|
||||||
// Create an authenticated session to the Unifi Controller.
|
// Create an authenticated session to the Unifi Controller.
|
||||||
u.Unifi, err = unifi.NewUnifi(&unifi.Config{
|
u.Unifi, err = unifi.NewUnifi(&unifi.Config{
|
||||||
User: u.UnifiUser,
|
User: u.Config.UnifiUser,
|
||||||
Pass: u.UnifiPass,
|
Pass: u.Config.UnifiPass,
|
||||||
URL: u.UnifiBase,
|
URL: u.Config.UnifiBase,
|
||||||
VerifySSL: u.VerifySSL,
|
VerifySSL: u.Config.VerifySSL,
|
||||||
ErrorLog: u.LogErrorf, // Log all errors.
|
ErrorLog: u.LogErrorf, // Log all errors.
|
||||||
DebugLog: u.LogDebugf, // Log debug messages.
|
DebugLog: u.LogDebugf, // Log debug messages.
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,10 @@ import (
|
||||||
// CheckSites makes sure the list of provided sites exists on the controller.
|
// CheckSites makes sure the list of provided sites exists on the controller.
|
||||||
// This does not run in Lambda (run-once) mode.
|
// This does not run in Lambda (run-once) mode.
|
||||||
func (u *UnifiPoller) CheckSites() error {
|
func (u *UnifiPoller) CheckSites() error {
|
||||||
if strings.Contains(strings.ToLower(u.Mode), "lambda") {
|
if strings.Contains(strings.ToLower(u.Config.Mode), "lambda") {
|
||||||
return nil // Skip this in lambda mode.
|
return nil // Skip this in lambda mode.
|
||||||
}
|
}
|
||||||
sites, err := u.GetSites()
|
sites, err := u.Unifi.GetSites()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -25,12 +25,12 @@ func (u *UnifiPoller) CheckSites() error {
|
||||||
msg = append(msg, site.Name+" ("+site.Desc+")")
|
msg = append(msg, site.Name+" ("+site.Desc+")")
|
||||||
}
|
}
|
||||||
u.Logf("Found %d site(s) on controller: %v", len(msg), strings.Join(msg, ", "))
|
u.Logf("Found %d site(s) on controller: %v", len(msg), strings.Join(msg, ", "))
|
||||||
if StringInSlice("all", u.Sites) {
|
if StringInSlice("all", u.Config.Sites) {
|
||||||
u.Sites = []string{"all"}
|
u.Config.Sites = []string{"all"}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
FIRST:
|
FIRST:
|
||||||
for _, s := range u.Sites {
|
for _, s := range u.Config.Sites {
|
||||||
for _, site := range sites {
|
for _, site := range sites {
|
||||||
if s == site.Name {
|
if s == site.Name {
|
||||||
continue FIRST
|
continue FIRST
|
||||||
|
|
@ -45,13 +45,15 @@ FIRST:
|
||||||
// PollController runs forever, polling UniFi, and pushing to influx.
|
// PollController runs forever, polling UniFi, and pushing to influx.
|
||||||
// This is started by Run() after everything checks out.
|
// This is started by Run() after everything checks out.
|
||||||
func (u *UnifiPoller) PollController() error {
|
func (u *UnifiPoller) PollController() error {
|
||||||
log.Println("[INFO] Everything checks out! Poller started, interval:", u.Interval.Round(time.Second))
|
interval := u.Config.Interval.Round(time.Second)
|
||||||
ticker := time.NewTicker(u.Interval.Round(time.Second))
|
log.Println("[INFO] Everything checks out! Poller started, interval:", interval)
|
||||||
|
ticker := time.NewTicker(interval)
|
||||||
for u.LastCheck = range ticker.C {
|
for u.LastCheck = range ticker.C {
|
||||||
var err error
|
var err error
|
||||||
if u.ReAuth {
|
if u.Config.ReAuth {
|
||||||
|
u.LogDebugf("Re-authenticating to UniFi Controller")
|
||||||
// Some users need to re-auth every interval because the cookie times out.
|
// Some users need to re-auth every interval because the cookie times out.
|
||||||
if err = u.Login(); err != nil {
|
if err = u.Unifi.Login(); err != nil {
|
||||||
u.LogError(err, "re-authenticating")
|
u.LogError(err, "re-authenticating")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -59,8 +61,8 @@ func (u *UnifiPoller) PollController() error {
|
||||||
// Only run this if the authentication procedure didn't return error.
|
// Only run this if the authentication procedure didn't return error.
|
||||||
_ = u.CollectAndReport()
|
_ = u.CollectAndReport()
|
||||||
}
|
}
|
||||||
if u.MaxErrors >= 0 && u.errorCount > u.MaxErrors {
|
if u.Config.MaxErrors >= 0 && u.errorCount > u.Config.MaxErrors {
|
||||||
return fmt.Errorf("reached maximum error count, stopping poller (%d > %d)", u.errorCount, u.MaxErrors)
|
return fmt.Errorf("reached maximum error count, stopping poller (%d > %d)", u.errorCount, u.Config.MaxErrors)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -92,18 +94,18 @@ func (u *UnifiPoller) CollectMetrics() (*Metrics, error) {
|
||||||
// Get the sites we care about.
|
// Get the sites we care about.
|
||||||
m.Sites, err = u.GetFilteredSites()
|
m.Sites, err = u.GetFilteredSites()
|
||||||
u.LogError(err, "unifi.GetSites()")
|
u.LogError(err, "unifi.GetSites()")
|
||||||
if u.CollectIDS {
|
if u.Config.CollectIDS {
|
||||||
// Check back in time since twice the interval. Dups are discarded by InfluxDB.
|
// Check back in time since twice the interval. Dups are discarded by InfluxDB.
|
||||||
m.IDSList, err = u.GetIDS(m.Sites, time.Now().Add(2*u.Interval.Duration), time.Now())
|
m.IDSList, err = u.Unifi.GetIDS(m.Sites, time.Now().Add(2*u.Config.Interval.Duration), time.Now())
|
||||||
u.LogError(err, "unifi.GetIDS()")
|
u.LogError(err, "unifi.GetIDS()")
|
||||||
}
|
}
|
||||||
// Get all the points.
|
// Get all the points.
|
||||||
m.Clients, err = u.GetClients(m.Sites)
|
m.Clients, err = u.Unifi.GetClients(m.Sites)
|
||||||
u.LogError(err, "unifi.GetClients()")
|
u.LogError(err, "unifi.GetClients()")
|
||||||
m.Devices, err = u.GetDevices(m.Sites)
|
m.Devices, err = u.Unifi.GetDevices(m.Sites)
|
||||||
u.LogError(err, "unifi.GetDevices()")
|
u.LogError(err, "unifi.GetDevices()")
|
||||||
// Make a new Influx Points Batcher.
|
// Make a new Influx Points Batcher.
|
||||||
m.BatchPoints, err = influx.NewBatchPoints(influx.BatchPointsConfig{Database: u.InfluxDB})
|
m.BatchPoints, err = influx.NewBatchPoints(influx.BatchPointsConfig{Database: u.Config.InfluxDB})
|
||||||
u.LogError(err, "influx.NewBatchPoints")
|
u.LogError(err, "influx.NewBatchPoints")
|
||||||
return m, err
|
return m, err
|
||||||
}
|
}
|
||||||
|
|
@ -138,7 +140,7 @@ func (u *UnifiPoller) ReportMetrics(metrics *Metrics) error {
|
||||||
for _, err := range metrics.ProcessPoints() {
|
for _, err := range metrics.ProcessPoints() {
|
||||||
u.LogError(err, "asset.Points()")
|
u.LogError(err, "asset.Points()")
|
||||||
}
|
}
|
||||||
err := u.Write(metrics.BatchPoints)
|
err := u.Influx.Write(metrics.BatchPoints)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("influxdb.Write(points): %v", err)
|
return fmt.Errorf("influxdb.Write(points): %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -149,7 +151,7 @@ func (u *UnifiPoller) ReportMetrics(metrics *Metrics) error {
|
||||||
fields += len(i)
|
fields += len(i)
|
||||||
}
|
}
|
||||||
idsMsg := ""
|
idsMsg := ""
|
||||||
if u.CollectIDS {
|
if u.Config.CollectIDS {
|
||||||
idsMsg = fmt.Sprintf("IDS Events: %d, ", len(metrics.IDSList))
|
idsMsg = fmt.Sprintf("IDS Events: %d, ", len(metrics.IDSList))
|
||||||
}
|
}
|
||||||
u.Logf("UniFi Measurements Recorded. Sites: %d, Clients: %d, "+
|
u.Logf("UniFi Measurements Recorded. Sites: %d, Clients: %d, "+
|
||||||
|
|
@ -212,16 +214,16 @@ func (m *Metrics) ProcessPoints() []error {
|
||||||
// Omits requested but unconfigured sites. Grabs the full list from the
|
// Omits requested but unconfigured sites. Grabs the full list from the
|
||||||
// controller and returns the sites provided in the config file.
|
// controller and returns the sites provided in the config file.
|
||||||
func (u *UnifiPoller) GetFilteredSites() (unifi.Sites, error) {
|
func (u *UnifiPoller) GetFilteredSites() (unifi.Sites, error) {
|
||||||
sites, err := u.GetSites()
|
sites, err := u.Unifi.GetSites()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if len(u.Sites) < 1 || StringInSlice("all", u.Sites) {
|
} else if len(u.Config.Sites) < 1 || StringInSlice("all", u.Config.Sites) {
|
||||||
return sites, nil
|
return sites, nil
|
||||||
}
|
}
|
||||||
var i int
|
var i int
|
||||||
for _, s := range sites {
|
for _, s := range sites {
|
||||||
// Only include valid sites in the request filter.
|
// Only include valid sites in the request filter.
|
||||||
if StringInSlice(s.Name, u.Sites) {
|
if StringInSlice(s.Name, u.Config.Sites) {
|
||||||
sites[i] = s
|
sites[i] = s
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue