Merge pull request #18 from davidnewhall/dn2_multi_site

Add support for multiple sites.
This commit is contained in:
David Newhall II 2019-05-30 02:00:06 -07:00 committed by GitHub
commit ebda5b9d2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 77 additions and 38 deletions

8
Gopkg.lock generated
View File

@ -2,12 +2,12 @@
[[projects]] [[projects]]
digest = "1:e7f0acf99afe9a7b03d270164bd2976b687e1aef02ab3a0abd8db0b4de44b817" digest = "1:dbd1c953926f2aca29aeb24f13af5875a3b31f09ed7093cb18a5409d4b9fc9f1"
name = "github.com/golift/unifi" name = "github.com/golift/unifi"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "f8fec42fbe169dceb69f15276a2323fb007a4539" revision = "034a62e1cb6ef13e724cfab9b8f87dbb130d493c"
version = "v1.0.0" version = "v2.0.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -19,7 +19,7 @@
"v2", "v2",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "16c852ea613fa2d42fcdccc9a8b0802a8bdc6140" revision = "8ff2fc3824fcb533795f9a2f233275f0bb18d6c5"
[[projects]] [[projects]]
digest = "1:b56c589214f01a5601e0821387db484617392d0042f26234bf2da853a2f498a1" digest = "1:b56c589214f01a5601e0821387db484617392d0042f26234bf2da853a2f498a1"

View File

@ -30,6 +30,11 @@ unifi-poller(1) -- Utility to poll Unifi Metrics and drop them into InfluxDB
`Config File Parameters` `Config File Parameters`
`sites` default: ["default"]
This list of strings should represent the names of sites on the unifi
controller that will be polled for data. Pass `all` in the list to
poll all sites.
`interval` default: 30s `interval` default: 30s
How often to poll the controller for updated client and device data. How often to poll the controller for updated client and device data.
The Unifi Controller only updates traffic stats about every 30 seconds. The Unifi Controller only updates traffic stats about every 30 seconds.

View File

@ -7,32 +7,30 @@ var Version = "v1.0.0"
const ( const (
// App defaults in case they're missing from the config. // App defaults in case they're missing from the config.
defaultConfFile = "/usr/local/etc/unifi-poller/up.conf" defaultConfFile = "/usr/local/etc/unifi-poller/up.conf"
defaultInterval = 30 * time.Second defaultInterval = 30 * time.Second
defaultInfxDb = "unifi" defaultInfxDb = "unifi"
defaultInfxUser = "unifi" defaultInfxUser = "unifi"
defaultInfxPass = "unifi" defaultInfxPass = "unifi"
defaultInfxURL = "http://127.0.0.1:8086" defaultInfxURL = "http://127.0.0.1:8086"
defaultUnifUser = "influx" defaultUnifUser = "influx"
defaultUnifURL = "https://127.0.0.1:8443" defaultUnifURL = "https://127.0.0.1:8443"
defaultVerifySSL = false
defaultDebug = false
defaultQuiet = false
) )
// Config represents the data needed to poll a controller and report to influxdb. // Config represents the data needed to poll a controller and report to influxdb.
type Config struct { type Config struct {
Interval Dur `json:"interval" toml:"interval" xml:"interval" yaml:"interval"` Interval Dur `json:"interval,_omitempty" toml:"interval,_omitempty" xml:"interval" yaml:"interval"`
Debug bool `json:"debug" toml:"debug" xml:"debug" yaml:"debug"` Debug bool `json:"debug" toml:"debug" xml:"debug" yaml:"debug"`
Quiet bool `json:"quiet" toml:"quiet" xml:"quiet" yaml:"quiet"` Quiet bool `json:"quiet" toml:"quiet" xml:"quiet" yaml:"quiet"`
VerifySSL bool `json:"verify_ssl" toml:"verify_ssl" xml:"verify_ssl" yaml:"verify_ssl"` VerifySSL bool `json:"verify_ssl" toml:"verify_ssl" xml:"verify_ssl" yaml:"verify_ssl"`
InfluxURL string `json:"influx_url" toml:"influx_url" xml:"influx_url" yaml:"influx_url"` InfluxURL string `json:"influx_url,_omitempty" toml:"influx_url,_omitempty" xml:"influx_url" yaml:"influx_url"`
InfluxUser string `json:"influx_user" toml:"influx_user" xml:"influx_user" yaml:"influx_user"` InfluxUser string `json:"influx_user,_omitempty" toml:"influx_user,_omitempty" xml:"influx_user" yaml:"influx_user"`
InfluxPass string `json:"influx_pass" toml:"influx_pass" xml:"influx_pass" yaml:"influx_pass"` InfluxPass string `json:"influx_pass,_omitempty" toml:"influx_pass,_omitempty" xml:"influx_pass" yaml:"influx_pass"`
InfluxDB string `json:"influx_db" toml:"influx_db" xml:"influx_db" yaml:"influx_db"` InfluxDB string `json:"influx_db,_omitempty" toml:"influx_db,_omitempty" xml:"influx_db" yaml:"influx_db"`
UnifiUser string `json:"unifi_user" toml:"unifi_user" xml:"unifi_user" yaml:"unifi_user"` UnifiUser string `json:"unifi_user,_omitempty" toml:"unifi_user,_omitempty" xml:"unifi_user" yaml:"unifi_user"`
UnifiPass string `json:"unifi_pass" toml:"unifi_pass" xml:"unifi_pass" yaml:"unifi_pass"` UnifiPass string `json:"unifi_pass,_omitempty" toml:"unifi_pass,_omitempty" xml:"unifi_pass" yaml:"unifi_pass"`
UnifiBase string `json:"unifi_url" toml:"unifi_url" xml:"unifi_url" yaml:"unifi_url"` UnifiBase string `json:"unifi_url,_omitempty" toml:"unifi_url,_omitempty" xml:"unifi_url" yaml:"unifi_url"`
Sites []string `json:"sites,_omitempty" toml:"sites,_omitempty" xml:"sites" yaml:"sites"`
} }
// Dur is used to UnmarshalTOML into a time.Duration value. // Dur is used to UnmarshalTOML into a time.Duration value.

View File

@ -5,6 +5,7 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"strings"
"time" "time"
"github.com/golift/unifi" "github.com/golift/unifi"
@ -27,7 +28,7 @@ func main() {
log.Fatalf("Config Error '%v': %v", configFile, err) log.Fatalf("Config Error '%v': %v", configFile, err)
} }
// Create an authenticated session to the Unifi Controller. // Create an authenticated session to the Unifi Controller.
controller, err := unifi.GetController(config.UnifiUser, config.UnifiPass, config.UnifiBase, config.VerifySSL) controller, err := unifi.NewUnifi(config.UnifiUser, config.UnifiPass, config.UnifiBase, config.VerifySSL)
if err != nil { if err != nil {
log.Fatalln("Unifi Controller Error:", err) log.Fatalln("Unifi Controller Error:", err)
} else if !config.Quiet { } else if !config.Quiet {
@ -52,7 +53,7 @@ func main() {
controller.DebugLog = nil controller.DebugLog = nil
} else { } else {
log.Println("Logging Unifi Metrics to InfluXDB @", config.InfluxURL, "as user", config.InfluxUser) log.Println("Logging Unifi Metrics to InfluXDB @", config.InfluxURL, "as user", config.InfluxUser)
log.Println("Polling Unifi Controller, interval:", config.Interval.value) log.Printf("Polling Unifi Controller (sites %v), interval: %v", config.Sites, config.Interval.value)
} }
config.PollUnifiController(controller, infdb) config.PollUnifiController(controller, infdb)
} }
@ -82,10 +83,8 @@ func GetConfig(configFile string) (Config, error) {
UnifiUser: defaultUnifUser, UnifiUser: defaultUnifUser,
UnifiPass: os.Getenv("UNIFI_PASSWORD"), UnifiPass: os.Getenv("UNIFI_PASSWORD"),
UnifiBase: defaultUnifURL, UnifiBase: defaultUnifURL,
VerifySSL: defaultVerifySSL,
Debug: defaultDebug,
Quiet: defaultQuiet,
Interval: Dur{value: defaultInterval}, Interval: Dur{value: defaultInterval},
Sites: []string{"default"},
} }
if buf, err := ioutil.ReadFile(configFile); err != nil { if buf, err := ioutil.ReadFile(configFile); err != nil {
return config, err return config, err
@ -102,9 +101,11 @@ func (c *Config) PollUnifiController(controller *unifi.Unifi, infdb influx.Clien
log.Println("[INFO] Everyting checks out! Beginning Poller Routine.") log.Println("[INFO] Everyting checks out! Beginning Poller Routine.")
ticker := time.NewTicker(c.Interval.value) ticker := time.NewTicker(c.Interval.value)
for range ticker.C { for range ticker.C {
if clients, err := controller.GetClients(); err != nil { if sites, err := filterSites(controller, c.Sites); err != nil {
logErrors([]error{err}, "uni.GetSites()")
} else if clients, err := controller.GetClients(sites); err != nil {
logErrors([]error{err}, "uni.GetClients()") logErrors([]error{err}, "uni.GetClients()")
} else if devices, err := controller.GetDevices(); err != nil { } else if devices, err := controller.GetDevices(sites); err != nil {
logErrors([]error{err}, "uni.GetDevices()") logErrors([]error{err}, "uni.GetDevices()")
} else if bp, err := influx.NewBatchPoints(influx.BatchPointsConfig{Database: c.InfluxDB}); err != nil { } else if bp, err := influx.NewBatchPoints(influx.BatchPointsConfig{Database: c.InfluxDB}); err != nil {
logErrors([]error{err}, "influx.NewBatchPoints") logErrors([]error{err}, "influx.NewBatchPoints")
@ -113,12 +114,32 @@ func (c *Config) PollUnifiController(controller *unifi.Unifi, infdb influx.Clien
} else if err := infdb.Write(bp); err != nil { } else if err := infdb.Write(bp); err != nil {
logErrors([]error{err}, "infdb.Write(bp)") logErrors([]error{err}, "infdb.Write(bp)")
} else if !c.Quiet { } else if !c.Quiet {
log.Println("[INFO] Logged Unifi States. Clients:", len(clients.UCLs), "- Wireless APs:", log.Printf("[INFO] Logged Unifi States. Sites: %d Clients: %d, Wireless APs: %d, Gateways: %d, Switches: %d",
len(devices.UAPs), "Gateways:", len(devices.USGs), "Switches:", len(devices.USWs)) len(sites), len(clients.UCLs), len(devices.UAPs), len(devices.USGs), len(devices.USWs))
} }
} }
} }
// filterSites returns a list of sites to fetch data for.
// Omits requested but unconfigured sites.
func filterSites(controller *unifi.Unifi, filter []string) ([]unifi.Site, error) {
sites, err := controller.GetSites()
if err != nil {
return nil, err
} else if len(filter) < 1 || StringInSlice("all", filter) {
return sites, nil
}
var i int
for _, s := range sites {
// Only include valid sites in the request filter.
if StringInSlice(s.Name, filter) {
sites[i] = s
i++
}
}
return sites[:i], nil
}
// batchPoints combines all device and client data into influxdb data points. // batchPoints combines all device and client data into influxdb data points.
func batchPoints(devices *unifi.Devices, clients *unifi.Clients, batchPoints influx.BatchPoints) (errs []error) { func batchPoints(devices *unifi.Devices, clients *unifi.Clients, batchPoints influx.BatchPoints) (errs []error) {
process := func(asset Asset) error { process := func(asset Asset) error {
@ -162,3 +183,13 @@ func logErrors(errs []error, prefix string) {
} }
} }
} }
// StringInSlice returns true if a string is in a slice.
func StringInSlice(str string, slc []string) bool {
for _, s := range slc {
if strings.EqualFold(s, str) {
return true
}
}
return false
}

View File

@ -3,7 +3,7 @@
"list": [ "list": [
{ {
"builtIn": 1, "builtIn": 1,
"datasource": "-- Grafana --", "datasource": "Unifi",
"enable": true, "enable": true,
"hide": true, "hide": true,
"iconColor": "rgba(0, 211, 255, 1)", "iconColor": "rgba(0, 211, 255, 1)",

View File

@ -57,7 +57,7 @@
"list": [ "list": [
{ {
"builtIn": 1, "builtIn": 1,
"datasource": "-- Grafana --", "datasource": "${DS_UNIFI}",
"enable": true, "enable": true,
"hide": true, "hide": true,
"iconColor": "rgba(0, 211, 255, 1)", "iconColor": "rgba(0, 211, 255, 1)",

View File

@ -57,7 +57,7 @@
"list": [ "list": [
{ {
"builtIn": 1, "builtIn": 1,
"datasource": "-- Grafana --", "datasource": "${DS_UNIFI}",
"enable": true, "enable": true,
"hide": true, "hide": true,
"iconColor": "rgba(0, 211, 255, 1)", "iconColor": "rgba(0, 211, 255, 1)",

View File

@ -3,6 +3,11 @@
# commented lines are defaults, uncomment to change. # # commented lines are defaults, uncomment to change. #
########################################################## ##########################################################
# If the controller has more than one site, specify which sites to poll here.
# If only one site, "default" is likely the correct name.
# Set this to ["all"] to poll all sites, no matter their names.
#sites = ["default"]
# The Unifi Controller only updates traffic stats about every 30 seconds. # The Unifi Controller only updates traffic stats about every 30 seconds.
# Setting this to something lower may lead to "zeros" in your data. You've been warned. # Setting this to something lower may lead to "zeros" in your data. You've been warned.
#interval = "30s" #interval = "30s"