From e2682bc07b36cdc4936c921438fba6c5b07000b7 Mon Sep 17 00:00:00 2001 From: davidnewhall2 Date: Mon, 9 Dec 2019 00:45:46 -0800 Subject: [PATCH 01/13] Prevent poller from dying from an error --- integrations/influxunifi/Gopkg.lock | 2 +- integrations/influxunifi/pkg/poller/config.go | 47 ++++++++++++------- .../influxunifi/pkg/poller/helpers.go | 11 +---- integrations/influxunifi/pkg/poller/influx.go | 2 +- .../influxunifi/pkg/poller/prometheus.go | 5 +- integrations/influxunifi/pkg/poller/start.go | 39 ++++----------- integrations/influxunifi/pkg/poller/unifi.go | 8 ++-- 7 files changed, 50 insertions(+), 64 deletions(-) diff --git a/integrations/influxunifi/Gopkg.lock b/integrations/influxunifi/Gopkg.lock index 08130d43..2d8bf8a7 100644 --- a/integrations/influxunifi/Gopkg.lock +++ b/integrations/influxunifi/Gopkg.lock @@ -103,7 +103,7 @@ name = "golang.org/x/sys" packages = ["windows"] pruneopts = "UT" - revision = "ce4227a45e2eb77e5c847278dcc6a626742e2945" + revision = "eeba5f6aabab6d6594a9191d6bfeaca5fa6a8248" [[projects]] digest = "1:87738e338f505d3e3be1f80d36b53f3c4e73be9b7ad4ccae46abbe9ef04f3f71" diff --git a/integrations/influxunifi/pkg/poller/config.go b/integrations/influxunifi/pkg/poller/config.go index fd914f9f..41847831 100644 --- a/integrations/influxunifi/pkg/poller/config.go +++ b/integrations/influxunifi/pkg/poller/config.go @@ -1,5 +1,13 @@ package poller +/* + I consider this file the pinacle example of how to allow a Go application to be configured from a file. + You can put your configuration into any file format: XML, YAML, JSON, TOML, and you can override any + struct member using an environment variable. The Duration type is also supported. All of the Config{} + and Duration{} types and methods are reusable in other projects. Just adjust the data in the struct to + meet your app's needs. See the New() procedure and Start() method in start.go for example usage. +*/ + import ( "encoding/json" "encoding/xml" @@ -41,12 +49,11 @@ const ENVConfigPrefix = "UP_" // UnifiPoller contains the application startup data, and auth info for UniFi & Influx. type UnifiPoller struct { - Influx *influxunifi.InfluxUnifi - Unifi *unifi.Unifi - Flag *Flag - Config *Config - errorCount int - LastCheck time.Time + Influx *influxunifi.InfluxUnifi + Unifi *unifi.Unifi + Flag *Flag + Config *Config + LastCheck time.Time } // Flag represents the CLI args available and their settings. @@ -110,44 +117,50 @@ func (c *Config) ParseFile(configFile string) error { // ParseENV copies environment variables into configuration values. // This is useful for Docker users that find it easier to pass ENV variables // than a specific configuration file. Uses reflection to find struct tags. +// This method uses the json struct tag member to match environment variables. +// Use a custom tag name by changing "json" below, but that's overkill for this app. func (c *Config) ParseENV() error { - t := reflect.TypeOf(Config{}) // Get tag names from the Config struct. - // Loop each Config struct member; get reflect tag & env var value; update config. - for i := 0; i < t.NumField(); i++ { + t := reflect.TypeOf(*c) // Get "types" from the Config struct. + for i := 0; i < t.NumField(); i++ { // Loop each Config struct member tag := t.Field(i).Tag.Get("json") // Get the ENV variable name from "json" struct tag tag = strings.Split(strings.ToUpper(tag), ",")[0] // Capitalize and remove ,omitempty suffix env := os.Getenv(ENVConfigPrefix + tag) // Then pull value from OS. - if tag == "" || env == "" { - continue // Skip if either are empty. + if tag == "" || env == "" { // Skip if either are empty. + continue } // Reflect and update the u.Config struct member at position i. - switch c := reflect.ValueOf(c).Elem().Field(i); c.Type().String() { + switch field := reflect.ValueOf(c).Elem().Field(i); field.Type().String() { // Handle each member type appropriately (differently). case "string": // This is a reflect package method to update a struct member by index. - c.SetString(env) + field.SetString(env) + case "int": val, err := strconv.Atoi(env) if err != nil { return fmt.Errorf("%s: %v", tag, err) } - c.Set(reflect.ValueOf(val)) + field.Set(reflect.ValueOf(val)) + case "[]string": - c.Set(reflect.ValueOf(strings.Split(env, ","))) + field.Set(reflect.ValueOf(strings.Split(env, ","))) + case path.Base(t.PkgPath()) + ".Duration": val, err := time.ParseDuration(env) if err != nil { return fmt.Errorf("%s: %v", tag, err) } - c.Set(reflect.ValueOf(Duration{val})) + field.Set(reflect.ValueOf(Duration{val})) + case "bool": val, err := strconv.ParseBool(env) if err != nil { return fmt.Errorf("%s: %v", tag, err) } - c.SetBool(val) + field.SetBool(val) } + // Add more types here if more types are added to the config struct. } return nil diff --git a/integrations/influxunifi/pkg/poller/helpers.go b/integrations/influxunifi/pkg/poller/helpers.go index 71421b16..92acd223 100644 --- a/integrations/influxunifi/pkg/poller/helpers.go +++ b/integrations/influxunifi/pkg/poller/helpers.go @@ -8,15 +8,6 @@ import ( const callDepth = 2 -// LogError logs an error and increments the error counter. -// Should be used in the poller loop. -func (u *UnifiPoller) LogError(err error, prefix string) { - if err != nil { - u.errorCount++ - _ = log.Output(callDepth, fmt.Sprintf("[ERROR] %v: %v", prefix, err)) - } -} - // StringInSlice returns true if a string is in a slice. func StringInSlice(str string, slice []string) bool { for _, s := range slice { @@ -41,7 +32,7 @@ func (u *UnifiPoller) LogDebugf(m string, v ...interface{}) { } } -// LogErrorf prints an error log entry. This is used for external library logging. +// LogErrorf prints an error log entry. func (u *UnifiPoller) LogErrorf(m string, v ...interface{}) { _ = log.Output(callDepth, fmt.Sprintf("[ERROR] "+m, v...)) } diff --git a/integrations/influxunifi/pkg/poller/influx.go b/integrations/influxunifi/pkg/poller/influx.go index 2abd83ad..c1fef118 100644 --- a/integrations/influxunifi/pkg/poller/influx.go +++ b/integrations/influxunifi/pkg/poller/influx.go @@ -42,7 +42,7 @@ func (u *UnifiPoller) CollectAndProcess() error { u.AugmentMetrics(metrics) report, err := u.Influx.ReportMetrics(metrics) if err != nil { - u.LogError(err, "processing metrics") + u.LogErrorf("processing metrics: %v", err) return err } u.LogInfluxReport(report) diff --git a/integrations/influxunifi/pkg/poller/prometheus.go b/integrations/influxunifi/pkg/poller/prometheus.go index 45dd1176..944e4c34 100644 --- a/integrations/influxunifi/pkg/poller/prometheus.go +++ b/integrations/influxunifi/pkg/poller/prometheus.go @@ -15,7 +15,7 @@ const oneDecimalPoint = 10 // RunPrometheus starts the web server and registers the collector. func (u *UnifiPoller) RunPrometheus() error { - u.Logf("Exporting Measurements at https://%s/metrics for Prometheus", u.Config.HTTPListen) + u.Logf("Exporting Measurements for Prometheus at https://%s/metrics", u.Config.HTTPListen) http.Handle("/metrics", promhttp.Handler()) prometheus.MustRegister(promunifi.NewUnifiCollector(promunifi.UnifiCollectorCnfg{ Namespace: strings.Replace(u.Config.Namespace, "-", "", -1), @@ -34,8 +34,9 @@ func (u *UnifiPoller) ExportMetrics() (*metrics.Metrics, error) { if err != nil { u.LogErrorf("collecting metrics: %v", err) u.Logf("Re-authenticating to UniFi Controller") + if err := u.Unifi.Login(); err != nil { - u.LogError(err, "re-authenticating") + u.LogErrorf("re-authenticating: %v", err) return nil, err } diff --git a/integrations/influxunifi/pkg/poller/start.go b/integrations/influxunifi/pkg/poller/start.go index 1e1719c8..a4953475 100644 --- a/integrations/influxunifi/pkg/poller/start.go +++ b/integrations/influxunifi/pkg/poller/start.go @@ -100,55 +100,36 @@ func (u *UnifiPoller) Run() error { switch strings.ToLower(u.Config.Mode) { default: - return u.PollController() + u.PollController() + return nil case "influxlambda", "lambdainflux", "lambda_influx", "influx_lambda": u.LastCheck = time.Now() return u.CollectAndProcess() + case "both": + go u.PollController() + fallthrough case "prometheus", "exporter": return u.RunPrometheus() - case "both": - return u.RunBoth() } } -// RunBoth starts the prometheus exporter and influxdb exporter at the same time. -// This will likely double the amount of polls your controller receives. -func (u *UnifiPoller) RunBoth() error { - e := make(chan error) - defer close(e) - go func() { - e <- u.RunPrometheus() - }() - go func() { - e <- u.PollController() - }() - // If either method returns an error (even nil), bail out. - return <-e -} - // PollController runs forever, polling UniFi and pushing to InfluxDB // This is started by Run() or RunBoth() after everything checks out. -func (u *UnifiPoller) PollController() error { +func (u *UnifiPoller) PollController() { interval := u.Config.Interval.Round(time.Second) - log.Printf("[INFO] Everything checks out! Poller started, interval: %v", interval) + log.Printf("[INFO] Everything checks out! Poller started, InfluxDB interval: %v", interval) ticker := time.NewTicker(interval) - defer ticker.Stop() - for u.LastCheck = range ticker.C { // Some users need to re-auth every interval because the cookie times out. if u.Config.ReAuth { u.LogDebugf("Re-authenticating to UniFi Controller") if err := u.Unifi.Login(); err != nil { - return err + u.LogErrorf("%v", err) + continue } } if err := u.CollectAndProcess(); err != nil { - return err - } - // check for errors from the unifi polls. - if u.errorCount > 0 { - return fmt.Errorf("too many errors, stopping poller") + u.LogErrorf("%v", err) } } - return nil } diff --git a/integrations/influxunifi/pkg/poller/unifi.go b/integrations/influxunifi/pkg/poller/unifi.go index 73708243..5873ffa0 100644 --- a/integrations/influxunifi/pkg/poller/unifi.go +++ b/integrations/influxunifi/pkg/poller/unifi.go @@ -67,16 +67,16 @@ func (u *UnifiPoller) CollectMetrics() (*metrics.Metrics, error) { var err error // Get the sites we care about. m.Sites, err = u.GetFilteredSites() - u.LogError(err, "unifi.GetSites()") + u.LogErrorf("unifi.GetSites(): %v", err) if u.Config.SaveIDS { m.IDSList, err = u.Unifi.GetIDS(m.Sites, time.Now().Add(u.Config.Interval.Duration), time.Now()) - u.LogError(err, "unifi.GetIDS()") + u.LogErrorf("unifi.GetIDS(): %v", err) } // Get all the points. m.Clients, err = u.Unifi.GetClients(m.Sites) - u.LogError(err, "unifi.GetClients()") + u.LogErrorf("unifi.GetClients(): %v", err) m.Devices, err = u.Unifi.GetDevices(m.Sites) - u.LogError(err, "unifi.GetDevices()") + u.LogErrorf("unifi.GetDevices(): %v", err) return m, err } From d2832579334a8f85ba63ec940c4fee6ec07bfa9a Mon Sep 17 00:00:00 2001 From: davidnewhall2 Date: Mon, 9 Dec 2019 00:49:25 -0800 Subject: [PATCH 02/13] add a retry --- integrations/influxunifi/pkg/poller/start.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/integrations/influxunifi/pkg/poller/start.go b/integrations/influxunifi/pkg/poller/start.go index a4953475..c15afddf 100644 --- a/integrations/influxunifi/pkg/poller/start.go +++ b/integrations/influxunifi/pkg/poller/start.go @@ -116,20 +116,23 @@ func (u *UnifiPoller) Run() error { // PollController runs forever, polling UniFi and pushing to InfluxDB // This is started by Run() or RunBoth() after everything checks out. func (u *UnifiPoller) PollController() { + var tryAgain bool interval := u.Config.Interval.Round(time.Second) log.Printf("[INFO] Everything checks out! Poller started, InfluxDB interval: %v", interval) ticker := time.NewTicker(interval) for u.LastCheck = range ticker.C { // Some users need to re-auth every interval because the cookie times out. - if u.Config.ReAuth { + if u.Config.ReAuth || tryAgain { u.LogDebugf("Re-authenticating to UniFi Controller") if err := u.Unifi.Login(); err != nil { u.LogErrorf("%v", err) continue } + tryAgain = false } if err := u.CollectAndProcess(); err != nil { u.LogErrorf("%v", err) + tryAgain = true } } } From 0c9713efe71a40b3dc0501d0b44113452d0533de Mon Sep 17 00:00:00 2001 From: davidnewhall2 Date: Mon, 9 Dec 2019 02:15:03 -0800 Subject: [PATCH 03/13] trigger docker build/tag --- integrations/influxunifi/pkg/poller/influx.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/integrations/influxunifi/pkg/poller/influx.go b/integrations/influxunifi/pkg/poller/influx.go index c1fef118..8d89e7ba 100644 --- a/integrations/influxunifi/pkg/poller/influx.go +++ b/integrations/influxunifi/pkg/poller/influx.go @@ -35,16 +35,19 @@ func (u *UnifiPoller) CollectAndProcess() error { if err := u.GetInfluxDB(); err != nil { return err } + metrics, err := u.CollectMetrics() if err != nil { return err } u.AugmentMetrics(metrics) + report, err := u.Influx.ReportMetrics(metrics) if err != nil { u.LogErrorf("processing metrics: %v", err) return err } + u.LogInfluxReport(report) return nil } From 2dee6143cf182c50a5f0b479f76dc82e0fc3fba9 Mon Sep 17 00:00:00 2001 From: davidnewhall2 Date: Mon, 9 Dec 2019 21:23:18 -0800 Subject: [PATCH 04/13] re-arrange pieces --- integrations/influxunifi/pkg/poller/influx.go | 1 - integrations/influxunifi/pkg/poller/start.go | 22 ++++++----------- integrations/influxunifi/pkg/poller/unifi.go | 24 ++++++++++++++----- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/integrations/influxunifi/pkg/poller/influx.go b/integrations/influxunifi/pkg/poller/influx.go index 8d89e7ba..3660cf38 100644 --- a/integrations/influxunifi/pkg/poller/influx.go +++ b/integrations/influxunifi/pkg/poller/influx.go @@ -44,7 +44,6 @@ func (u *UnifiPoller) CollectAndProcess() error { report, err := u.Influx.ReportMetrics(metrics) if err != nil { - u.LogErrorf("processing metrics: %v", err) return err } diff --git a/integrations/influxunifi/pkg/poller/start.go b/integrations/influxunifi/pkg/poller/start.go index c15afddf..4ef96124 100644 --- a/integrations/influxunifi/pkg/poller/start.go +++ b/integrations/influxunifi/pkg/poller/start.go @@ -92,11 +92,13 @@ func (f *Flag) Parse(args []string) { // 2. Run the collector one time and report the metrics to influxdb. (lambda) // 3. Start a web server and wait for Prometheus to poll the application for metrics. func (u *UnifiPoller) Run() error { - if err := u.GetUnifi(); err != nil { - return err + switch err := u.GetUnifi(); err { + case nil: + 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) + default: + u.LogErrorf("Controller Auth or Connection failed, but continuing to retry! %v", err) } - 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) switch strings.ToLower(u.Config.Mode) { default: @@ -116,23 +118,13 @@ func (u *UnifiPoller) Run() error { // PollController runs forever, polling UniFi and pushing to InfluxDB // This is started by Run() or RunBoth() after everything checks out. func (u *UnifiPoller) PollController() { - var tryAgain bool interval := u.Config.Interval.Round(time.Second) log.Printf("[INFO] Everything checks out! Poller started, InfluxDB interval: %v", interval) ticker := time.NewTicker(interval) for u.LastCheck = range ticker.C { - // Some users need to re-auth every interval because the cookie times out. - if u.Config.ReAuth || tryAgain { - u.LogDebugf("Re-authenticating to UniFi Controller") - if err := u.Unifi.Login(); err != nil { - u.LogErrorf("%v", err) - continue - } - tryAgain = false - } if err := u.CollectAndProcess(); err != nil { u.LogErrorf("%v", err) - tryAgain = true + u.Unifi = nil // trigger re-auth in unifi.go. } } } diff --git a/integrations/influxunifi/pkg/poller/unifi.go b/integrations/influxunifi/pkg/poller/unifi.go index 5873ffa0..9c924441 100644 --- a/integrations/influxunifi/pkg/poller/unifi.go +++ b/integrations/influxunifi/pkg/poller/unifi.go @@ -21,6 +21,7 @@ func (u *UnifiPoller) GetUnifi() (err error) { DebugLog: u.LogDebugf, // Log debug messages. }) if err != nil { + u.Unifi = nil return fmt.Errorf("unifi controller: %v", err) } u.LogDebugf("Authenticated with controller successfully") @@ -63,20 +64,31 @@ FIRST: // CollectMetrics grabs all the measurements from a UniFi controller and returns them. func (u *UnifiPoller) CollectMetrics() (*metrics.Metrics, error) { + if u.Unifi == nil || u.Config.ReAuth { + // Some users need to re-auth every interval because the cookie times out. + // Sometimes we hit this path when the controller dies. + u.LogDebugf("Re-authenticating to UniFi Controller") + if err := u.GetUnifi(); err != nil { + return nil, err + } + } m := &metrics.Metrics{TS: u.LastCheck} // At this point, it's the Current Check. var err error // Get the sites we care about. - m.Sites, err = u.GetFilteredSites() - u.LogErrorf("unifi.GetSites(): %v", err) + if m.Sites, err = u.GetFilteredSites(); err != nil { + u.LogErrorf("unifi.GetSites(): %v", err) + } if u.Config.SaveIDS { m.IDSList, err = u.Unifi.GetIDS(m.Sites, time.Now().Add(u.Config.Interval.Duration), time.Now()) u.LogErrorf("unifi.GetIDS(): %v", err) } // Get all the points. - m.Clients, err = u.Unifi.GetClients(m.Sites) - u.LogErrorf("unifi.GetClients(): %v", err) - m.Devices, err = u.Unifi.GetDevices(m.Sites) - u.LogErrorf("unifi.GetDevices(): %v", err) + if m.Clients, err = u.Unifi.GetClients(m.Sites); err != nil { + u.LogErrorf("unifi.GetClients(): %v", err) + } + if m.Devices, err = u.Unifi.GetDevices(m.Sites); err != nil { + u.LogErrorf("unifi.GetDevices(): %v", err) + } return m, err } From 6c873e8cd2ed4cdd71f351db784a4978b06aa5b1 Mon Sep 17 00:00:00 2001 From: davidnewhall2 Date: Tue, 10 Dec 2019 00:18:51 -0800 Subject: [PATCH 05/13] pass collector errors up --- integrations/influxunifi/pkg/poller/prometheus.go | 2 +- integrations/influxunifi/pkg/poller/unifi.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/integrations/influxunifi/pkg/poller/prometheus.go b/integrations/influxunifi/pkg/poller/prometheus.go index 944e4c34..28371c9a 100644 --- a/integrations/influxunifi/pkg/poller/prometheus.go +++ b/integrations/influxunifi/pkg/poller/prometheus.go @@ -35,7 +35,7 @@ func (u *UnifiPoller) ExportMetrics() (*metrics.Metrics, error) { u.LogErrorf("collecting metrics: %v", err) u.Logf("Re-authenticating to UniFi Controller") - if err := u.Unifi.Login(); err != nil { + if err := u.GetUnifi(); err != nil { u.LogErrorf("re-authenticating: %v", err) return nil, err } diff --git a/integrations/influxunifi/pkg/poller/unifi.go b/integrations/influxunifi/pkg/poller/unifi.go index 9c924441..ce9ac2e7 100644 --- a/integrations/influxunifi/pkg/poller/unifi.go +++ b/integrations/influxunifi/pkg/poller/unifi.go @@ -76,20 +76,20 @@ func (u *UnifiPoller) CollectMetrics() (*metrics.Metrics, error) { var err error // Get the sites we care about. if m.Sites, err = u.GetFilteredSites(); err != nil { - u.LogErrorf("unifi.GetSites(): %v", err) + return m, fmt.Errorf("unifi.GetSites(): %v", err) } if u.Config.SaveIDS { m.IDSList, err = u.Unifi.GetIDS(m.Sites, time.Now().Add(u.Config.Interval.Duration), time.Now()) - u.LogErrorf("unifi.GetIDS(): %v", err) + return m, fmt.Errorf("unifi.GetIDS(): %v", err) } // Get all the points. if m.Clients, err = u.Unifi.GetClients(m.Sites); err != nil { - u.LogErrorf("unifi.GetClients(): %v", err) + return m, fmt.Errorf("unifi.GetClients(): %v", err) } if m.Devices, err = u.Unifi.GetDevices(m.Sites); err != nil { - u.LogErrorf("unifi.GetDevices(): %v", err) + return m, fmt.Errorf("unifi.GetDevices(): %v", err) } - return m, err + return m, nil } // AugmentMetrics is our middleware layer between collecting metrics and writing them. From fc5c61244845be75223cf5692edb4a3543dcd20e Mon Sep 17 00:00:00 2001 From: davidnewhall2 Date: Tue, 10 Dec 2019 00:30:27 -0800 Subject: [PATCH 06/13] :( --- integrations/influxunifi/pkg/promunifi/uap.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/integrations/influxunifi/pkg/promunifi/uap.go b/integrations/influxunifi/pkg/promunifi/uap.go index 2dba5a6f..3a582bca 100644 --- a/integrations/influxunifi/pkg/promunifi/uap.go +++ b/integrations/influxunifi/pkg/promunifi/uap.go @@ -1,6 +1,8 @@ package promunifi import ( + "log" + "github.com/prometheus/client_golang/prometheus" "golift.io/unifi" ) @@ -175,6 +177,10 @@ func (u *promUnifi) exportUAP(r report, d *unifi.UAP) { // udm doesn't have these stats exposed yet, so pass 2 or 6 metrics. func (u *promUnifi) exportUAPstats(r report, labels []string, ap *unifi.Ap, bytes ...unifi.FlexInt) { + if ap == nil { + log.Println("ap was nil?!", labels[2]) + return + } labelU := []string{"user", labels[1], labels[2]} labelG := []string{"guest", labels[1], labels[2]} r.send([]*metric{ From 6c3100b2f2388b9c2f1642763b15363593f62b23 Mon Sep 17 00:00:00 2001 From: davidnewhall2 Date: Tue, 10 Dec 2019 00:50:46 -0800 Subject: [PATCH 07/13] :( --- integrations/influxunifi/pkg/promunifi/uap.go | 1 + 1 file changed, 1 insertion(+) diff --git a/integrations/influxunifi/pkg/promunifi/uap.go b/integrations/influxunifi/pkg/promunifi/uap.go index 3a582bca..b21d8491 100644 --- a/integrations/influxunifi/pkg/promunifi/uap.go +++ b/integrations/influxunifi/pkg/promunifi/uap.go @@ -181,6 +181,7 @@ func (u *promUnifi) exportUAPstats(r report, labels []string, ap *unifi.Ap, byte log.Println("ap was nil?!", labels[2]) return } + log.Println("ap not nil") labelU := []string{"user", labels[1], labels[2]} labelG := []string{"guest", labels[1], labels[2]} r.send([]*metric{ From f0357b88b3ff0b66c973c2c31e7aabbbe656b83d Mon Sep 17 00:00:00 2001 From: davidnewhall2 Date: Tue, 10 Dec 2019 00:56:38 -0800 Subject: [PATCH 08/13] jeez --- integrations/influxunifi/pkg/promunifi/collector.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/integrations/influxunifi/pkg/promunifi/collector.go b/integrations/influxunifi/pkg/promunifi/collector.go index aa14b1a8..e54ef6c4 100644 --- a/integrations/influxunifi/pkg/promunifi/collector.go +++ b/integrations/influxunifi/pkg/promunifi/collector.go @@ -3,6 +3,7 @@ package promunifi import ( "fmt" + "log" "reflect" "strings" "sync" @@ -112,6 +113,7 @@ func (u *promUnifi) Collect(ch chan<- prometheus.Metric) { defer r.close() if r.Metrics, err = r.cf.CollectFn(); err != nil { + log.Println("Error", err) r.error(ch, prometheus.NewInvalidDesc(fmt.Errorf("metric fetch failed")), err) return } From d2e4662841d7f2d622d290fd0808d340143671de Mon Sep 17 00:00:00 2001 From: davidnewhall2 Date: Tue, 10 Dec 2019 01:01:54 -0800 Subject: [PATCH 09/13] got it --- integrations/influxunifi/pkg/poller/start.go | 1 + integrations/influxunifi/pkg/promunifi/collector.go | 2 -- integrations/influxunifi/pkg/promunifi/uap.go | 4 ---- integrations/influxunifi/pkg/promunifi/usg.go | 3 +++ integrations/influxunifi/pkg/promunifi/usw.go | 3 +++ 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/integrations/influxunifi/pkg/poller/start.go b/integrations/influxunifi/pkg/poller/start.go index 4ef96124..d2c63083 100644 --- a/integrations/influxunifi/pkg/poller/start.go +++ b/integrations/influxunifi/pkg/poller/start.go @@ -36,6 +36,7 @@ func New() *UnifiPoller { // Parses cli flags, parses config file, parses env vars, sets up logging, then: // - dumps a json payload OR - executes Run(). func (u *UnifiPoller) Start() error { + log.SetOutput(os.Stdout) log.SetFlags(log.LstdFlags) u.Flag.Parse(os.Args[1:]) diff --git a/integrations/influxunifi/pkg/promunifi/collector.go b/integrations/influxunifi/pkg/promunifi/collector.go index e54ef6c4..aa14b1a8 100644 --- a/integrations/influxunifi/pkg/promunifi/collector.go +++ b/integrations/influxunifi/pkg/promunifi/collector.go @@ -3,7 +3,6 @@ package promunifi import ( "fmt" - "log" "reflect" "strings" "sync" @@ -113,7 +112,6 @@ func (u *promUnifi) Collect(ch chan<- prometheus.Metric) { defer r.close() if r.Metrics, err = r.cf.CollectFn(); err != nil { - log.Println("Error", err) r.error(ch, prometheus.NewInvalidDesc(fmt.Errorf("metric fetch failed")), err) return } diff --git a/integrations/influxunifi/pkg/promunifi/uap.go b/integrations/influxunifi/pkg/promunifi/uap.go index b21d8491..658162d3 100644 --- a/integrations/influxunifi/pkg/promunifi/uap.go +++ b/integrations/influxunifi/pkg/promunifi/uap.go @@ -1,8 +1,6 @@ package promunifi import ( - "log" - "github.com/prometheus/client_golang/prometheus" "golift.io/unifi" ) @@ -178,10 +176,8 @@ func (u *promUnifi) exportUAP(r report, d *unifi.UAP) { // udm doesn't have these stats exposed yet, so pass 2 or 6 metrics. func (u *promUnifi) exportUAPstats(r report, labels []string, ap *unifi.Ap, bytes ...unifi.FlexInt) { if ap == nil { - log.Println("ap was nil?!", labels[2]) return } - log.Println("ap not nil") labelU := []string{"user", labels[1], labels[2]} labelG := []string{"guest", labels[1], labels[2]} r.send([]*metric{ diff --git a/integrations/influxunifi/pkg/promunifi/usg.go b/integrations/influxunifi/pkg/promunifi/usg.go index 6c2ffd01..b4005b1c 100644 --- a/integrations/influxunifi/pkg/promunifi/usg.go +++ b/integrations/influxunifi/pkg/promunifi/usg.go @@ -85,6 +85,9 @@ func (u *promUnifi) exportUSG(r report, d *unifi.USG) { // Gateway States func (u *promUnifi) exportUSGstats(r report, labels []string, gw *unifi.Gw, st unifi.SpeedtestStatus, ul unifi.Uplink) { + if gw == nil { + return + } labelLan := []string{"lan", labels[1], labels[2]} labelWan := []string{"all", labels[1], labels[2]} r.send([]*metric{ diff --git a/integrations/influxunifi/pkg/promunifi/usw.go b/integrations/influxunifi/pkg/promunifi/usw.go index 831e71ee..b77821d1 100644 --- a/integrations/influxunifi/pkg/promunifi/usw.go +++ b/integrations/influxunifi/pkg/promunifi/usw.go @@ -116,6 +116,9 @@ func (u *promUnifi) exportUSW(r report, d *unifi.USW) { // Switch Stats func (u *promUnifi) exportUSWstats(r report, labels []string, sw *unifi.Sw) { + if sw == nil { + return + } labelS := labels[1:] r.send([]*metric{ {u.USW.SwRxPackets, counter, sw.RxPackets, labelS}, From a9a7b2f8ba16b941ca9438a68275ca4da0bd5714 Mon Sep 17 00:00:00 2001 From: davidnewhall2 Date: Tue, 10 Dec 2019 01:19:56 -0800 Subject: [PATCH 10/13] Updates --- .../influxunifi/pkg/influxunifi/uap.go | 7 +- .../influxunifi/pkg/influxunifi/udm.go | 41 ++++-------- .../influxunifi/pkg/influxunifi/usg.go | 7 +- .../influxunifi/pkg/influxunifi/usw.go | 65 +++++++++++-------- integrations/influxunifi/pkg/promunifi/uap.go | 3 + integrations/influxunifi/pkg/promunifi/udm.go | 3 + integrations/influxunifi/pkg/promunifi/usg.go | 3 + integrations/influxunifi/pkg/promunifi/usw.go | 3 + 8 files changed, 74 insertions(+), 58 deletions(-) diff --git a/integrations/influxunifi/pkg/influxunifi/uap.go b/integrations/influxunifi/pkg/influxunifi/uap.go index f6f8e8ad..46c25c32 100644 --- a/integrations/influxunifi/pkg/influxunifi/uap.go +++ b/integrations/influxunifi/pkg/influxunifi/uap.go @@ -7,8 +7,8 @@ import ( // batchUAP generates Wireless-Access-Point datapoints for InfluxDB. // These points can be passed directly to influx. func (u *InfluxUnifi) batchUAP(r report, s *unifi.UAP) { - if s.Stat.Ap == nil { - s.Stat.Ap = &unifi.Ap{} + if !s.Adopted.Val || s.Locating.Val { + return } tags := map[string]string{ "mac": s.Mac, @@ -36,6 +36,9 @@ func (u *InfluxUnifi) batchUAP(r report, s *unifi.UAP) { } func (u *InfluxUnifi) processUAPstats(ap *unifi.Ap) map[string]interface{} { + if ap == nil { + return map[string]interface{}{} + } // Accumulative Statistics. return map[string]interface{}{ "stat_user-rx_packets": ap.UserRxPackets.Val, diff --git a/integrations/influxunifi/pkg/influxunifi/udm.go b/integrations/influxunifi/pkg/influxunifi/udm.go index 2e93ce8e..af42e5d0 100644 --- a/integrations/influxunifi/pkg/influxunifi/udm.go +++ b/integrations/influxunifi/pkg/influxunifi/udm.go @@ -33,11 +33,8 @@ func (u *InfluxUnifi) batchSysStats(s unifi.SysStats, ss unifi.SystemStats) map[ // batchUDM generates Unifi Gateway datapoints for InfluxDB. // These points can be passed directly to influx. func (u *InfluxUnifi) batchUDM(r report, s *unifi.UDM) { - if s.Stat.Sw == nil { - s.Stat.Sw = &unifi.Sw{} - } - if s.Stat.Gw == nil { - s.Stat.Gw = &unifi.Gw{} + if !s.Adopted.Val || s.Locating.Val { + return } tags := map[string]string{ "mac": s.Mac, @@ -81,28 +78,18 @@ func (u *InfluxUnifi) batchUDM(r report, s *unifi.UDM) { "serial": s.Serial, "type": s.Type, } - fields = map[string]interface{}{ - "guest-num_sta": s.GuestNumSta.Val, - "ip": s.IP, - "bytes": s.Bytes.Val, - "last_seen": s.LastSeen.Val, - "rx_bytes": s.RxBytes.Val, - "tx_bytes": s.TxBytes.Val, - "uptime": s.Uptime.Val, - "state": s.State.Val, - "stat_bytes": s.Stat.Sw.Bytes.Val, - "stat_rx_bytes": s.Stat.Sw.RxBytes.Val, - "stat_rx_crypts": s.Stat.Sw.RxCrypts.Val, - "stat_rx_dropped": s.Stat.Sw.RxDropped.Val, - "stat_rx_errors": s.Stat.Sw.RxErrors.Val, - "stat_rx_frags": s.Stat.Sw.RxFrags.Val, - "stat_rx_packets": s.Stat.Sw.TxPackets.Val, - "stat_tx_bytes": s.Stat.Sw.TxBytes.Val, - "stat_tx_dropped": s.Stat.Sw.TxDropped.Val, - "stat_tx_errors": s.Stat.Sw.TxErrors.Val, - "stat_tx_packets": s.Stat.Sw.TxPackets.Val, - "stat_tx_retries": s.Stat.Sw.TxRetries.Val, - } + fields = Combine( + u.batchUSWstat(s.Stat.Sw), + map[string]interface{}{ + "guest-num_sta": s.GuestNumSta.Val, + "ip": s.IP, + "bytes": s.Bytes.Val, + "last_seen": s.LastSeen.Val, + "rx_bytes": s.RxBytes.Val, + "tx_bytes": s.TxBytes.Val, + "uptime": s.Uptime.Val, + "state": s.State.Val, + }) r.send(&metric{Table: "usw", Tags: tags, Fields: fields}) u.batchPortTable(r, tags, s.PortTable) diff --git a/integrations/influxunifi/pkg/influxunifi/usg.go b/integrations/influxunifi/pkg/influxunifi/usg.go index 0993be1a..221e0e40 100644 --- a/integrations/influxunifi/pkg/influxunifi/usg.go +++ b/integrations/influxunifi/pkg/influxunifi/usg.go @@ -7,8 +7,8 @@ import ( // batchUSG generates Unifi Gateway datapoints for InfluxDB. // These points can be passed directly to influx. func (u *InfluxUnifi) batchUSG(r report, s *unifi.USG) { - if s.Stat.Gw == nil { - s.Stat.Gw = &unifi.Gw{} + if !s.Adopted.Val || s.Locating.Val { + return } tags := map[string]string{ "mac": s.Mac, @@ -74,6 +74,9 @@ func (u *InfluxUnifi) batchUSG(r report, s *unifi.USG) { */ } func (u *InfluxUnifi) batchUSGstat(ss unifi.SpeedtestStatus, gw *unifi.Gw, ul unifi.Uplink) map[string]interface{} { + if gw == nil { + return map[string]interface{}{} + } return map[string]interface{}{ "uplink_latency": ul.Latency.Val, "uplink_speed": ul.Speed.Val, diff --git a/integrations/influxunifi/pkg/influxunifi/usw.go b/integrations/influxunifi/pkg/influxunifi/usw.go index a62f5f81..7bc31c37 100644 --- a/integrations/influxunifi/pkg/influxunifi/usw.go +++ b/integrations/influxunifi/pkg/influxunifi/usw.go @@ -7,9 +7,10 @@ import ( // batchUSW generates Unifi Switch datapoints for InfluxDB. // These points can be passed directly to influx. func (u *InfluxUnifi) batchUSW(r report, s *unifi.USW) { - if s.Stat.Sw == nil { - s.Stat.Sw = &unifi.Sw{} + if !s.Adopted.Val || s.Locating.Val { + return } + tags := map[string]string{ "mac": s.Mac, "site_name": s.SiteName, @@ -19,35 +20,45 @@ func (u *InfluxUnifi) batchUSW(r report, s *unifi.USW) { "serial": s.Serial, "type": s.Type, } - fields := Combine(map[string]interface{}{ - "guest-num_sta": s.GuestNumSta.Val, - "ip": s.IP, - "bytes": s.Bytes.Val, - "fan_level": s.FanLevel.Val, - "general_temperature": s.GeneralTemperature.Val, - "last_seen": s.LastSeen.Val, - "rx_bytes": s.RxBytes.Val, - "tx_bytes": s.TxBytes.Val, - "uptime": s.Uptime.Val, - "state": s.State.Val, - "user-num_sta": s.UserNumSta.Val, - "stat_bytes": s.Stat.Sw.Bytes.Val, - "stat_rx_bytes": s.Stat.Sw.RxBytes.Val, - "stat_rx_crypts": s.Stat.Sw.RxCrypts.Val, - "stat_rx_dropped": s.Stat.Sw.RxDropped.Val, - "stat_rx_errors": s.Stat.Sw.RxErrors.Val, - "stat_rx_frags": s.Stat.Sw.RxFrags.Val, - "stat_rx_packets": s.Stat.Sw.TxPackets.Val, - "stat_tx_bytes": s.Stat.Sw.TxBytes.Val, - "stat_tx_dropped": s.Stat.Sw.TxDropped.Val, - "stat_tx_errors": s.Stat.Sw.TxErrors.Val, - "stat_tx_packets": s.Stat.Sw.TxPackets.Val, - "stat_tx_retries": s.Stat.Sw.TxRetries.Val, - }, u.batchSysStats(s.SysStats, s.SystemStats)) + fields := Combine( + u.batchUSWstat(s.Stat.Sw), + u.batchSysStats(s.SysStats, s.SystemStats), + map[string]interface{}{ + "guest-num_sta": s.GuestNumSta.Val, + "ip": s.IP, + "bytes": s.Bytes.Val, + "fan_level": s.FanLevel.Val, + "general_temperature": s.GeneralTemperature.Val, + "last_seen": s.LastSeen.Val, + "rx_bytes": s.RxBytes.Val, + "tx_bytes": s.TxBytes.Val, + "uptime": s.Uptime.Val, + "state": s.State.Val, + "user-num_sta": s.UserNumSta.Val, + }) r.send(&metric{Table: "usw", Tags: tags, Fields: fields}) u.batchPortTable(r, tags, s.PortTable) } +func (u *InfluxUnifi) batchUSWstat(sw *unifi.Sw) map[string]interface{} { + if sw == nil { + return map[string]interface{}{} + } + return map[string]interface{}{ + "stat_bytes": sw.Bytes.Val, + "stat_rx_bytes": sw.RxBytes.Val, + "stat_rx_crypts": sw.RxCrypts.Val, + "stat_rx_dropped": sw.RxDropped.Val, + "stat_rx_errors": sw.RxErrors.Val, + "stat_rx_frags": sw.RxFrags.Val, + "stat_rx_packets": sw.TxPackets.Val, + "stat_tx_bytes": sw.TxBytes.Val, + "stat_tx_dropped": sw.TxDropped.Val, + "stat_tx_errors": sw.TxErrors.Val, + "stat_tx_packets": sw.TxPackets.Val, + "stat_tx_retries": sw.TxRetries.Val, + } +} func (u *InfluxUnifi) batchPortTable(r report, t map[string]string, pt []unifi.Port) { for _, p := range pt { if !p.Up.Val || !p.Enable.Val { diff --git a/integrations/influxunifi/pkg/promunifi/uap.go b/integrations/influxunifi/pkg/promunifi/uap.go index 658162d3..9998df30 100644 --- a/integrations/influxunifi/pkg/promunifi/uap.go +++ b/integrations/influxunifi/pkg/promunifi/uap.go @@ -159,6 +159,9 @@ func descUAP(ns string) *uap { } func (u *promUnifi) exportUAP(r report, d *unifi.UAP) { + if !d.Adopted.Val || d.Locating.Val { + return + } labels := []string{d.Type, d.SiteName, d.Name} infoLabels := []string{d.Version, d.Model, d.Serial, d.Mac, d.IP, d.ID, d.Bytes.Txt, d.Uptime.Txt} u.exportUAPstats(r, labels, d.Stat.Ap, d.BytesD, d.TxBytesD, d.RxBytesD, d.BytesR) diff --git a/integrations/influxunifi/pkg/promunifi/udm.go b/integrations/influxunifi/pkg/promunifi/udm.go index 83f27526..3ce95117 100644 --- a/integrations/influxunifi/pkg/promunifi/udm.go +++ b/integrations/influxunifi/pkg/promunifi/udm.go @@ -60,6 +60,9 @@ func descDevice(ns string) *unifiDevice { // UDM is a collection of stats from USG, USW and UAP. It has no unique stats. func (u *promUnifi) exportUDM(r report, d *unifi.UDM) { + if !d.Adopted.Val || d.Locating.Val { + return + } labels := []string{d.Type, d.SiteName, d.Name} infoLabels := []string{d.Version, d.Model, d.Serial, d.Mac, d.IP, d.ID, d.Bytes.Txt, d.Uptime.Txt} // Shared data (all devices do this). diff --git a/integrations/influxunifi/pkg/promunifi/usg.go b/integrations/influxunifi/pkg/promunifi/usg.go index b4005b1c..94a43f80 100644 --- a/integrations/influxunifi/pkg/promunifi/usg.go +++ b/integrations/influxunifi/pkg/promunifi/usg.go @@ -69,6 +69,9 @@ func descUSG(ns string) *usg { } func (u *promUnifi) exportUSG(r report, d *unifi.USG) { + if !d.Adopted.Val || d.Locating.Val { + return + } labels := []string{d.Type, d.SiteName, d.Name} infoLabels := []string{d.Version, d.Model, d.Serial, d.Mac, d.IP, d.ID, d.Bytes.Txt, d.Uptime.Txt} // Gateway System Data. diff --git a/integrations/influxunifi/pkg/promunifi/usw.go b/integrations/influxunifi/pkg/promunifi/usw.go index b77821d1..fd089ea7 100644 --- a/integrations/influxunifi/pkg/promunifi/usw.go +++ b/integrations/influxunifi/pkg/promunifi/usw.go @@ -91,6 +91,9 @@ func descUSW(ns string) *usw { } func (u *promUnifi) exportUSW(r report, d *unifi.USW) { + if !d.Adopted.Val || d.Locating.Val { + return + } labels := []string{d.Type, d.SiteName, d.Name} infoLabels := []string{d.Version, d.Model, d.Serial, d.Mac, d.IP, d.ID, d.Bytes.Txt, d.Uptime.Txt} u.exportUSWstats(r, labels, d.Stat.Sw) From 1df6a7132037c3d2ea688e009a82e019f2143dab Mon Sep 17 00:00:00 2001 From: davidnewhall2 Date: Wed, 11 Dec 2019 01:14:09 -0800 Subject: [PATCH 11/13] add lock to getunifi --- integrations/influxunifi/Gopkg.lock | 12 ++++++------ integrations/influxunifi/pkg/poller/config.go | 12 +++++++----- integrations/influxunifi/pkg/poller/dumper.go | 4 ++-- integrations/influxunifi/pkg/poller/start.go | 6 +++++- integrations/influxunifi/pkg/poller/unifi.go | 5 +++++ 5 files changed, 25 insertions(+), 14 deletions(-) diff --git a/integrations/influxunifi/Gopkg.lock b/integrations/influxunifi/Gopkg.lock index 2d8bf8a7..615d1159 100644 --- a/integrations/influxunifi/Gopkg.lock +++ b/integrations/influxunifi/Gopkg.lock @@ -27,7 +27,7 @@ [[projects]] branch = "master" - digest = "1:50708c8fc92aec981df5c446581cf9f90ba9e2a5692118e0ce75d4534aaa14a2" + digest = "1:00e5ad58045d6d2a6c9e65d1809ff2594bc396e911712ae892a93976fdece115" name = "github.com/influxdata/influxdb1-client" packages = [ "models", @@ -35,7 +35,7 @@ "v2", ] pruneopts = "UT" - revision = "fc22c7df067eefd070157f157893fbce961d6359" + revision = "8bf82d3c094dc06be9da8e5bf9d3589b6ea032ae" [[projects]] digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc" @@ -103,15 +103,15 @@ name = "golang.org/x/sys" packages = ["windows"] pruneopts = "UT" - revision = "eeba5f6aabab6d6594a9191d6bfeaca5fa6a8248" + revision = "ac6580df4449443a05718fd7858c1f91ad5f8d20" [[projects]] - digest = "1:87738e338f505d3e3be1f80d36b53f3c4e73be9b7ad4ccae46abbe9ef04f3f71" + digest = "1:2883cea734f2766f41ff9c9d4aefccccc53e3d44f5c8b08893b9c218cf666722" name = "golift.io/unifi" packages = ["."] pruneopts = "UT" - revision = "ba857a3a04311fed362cb43fa7bf4066bc3a7e55" - version = "v4.1.5" + revision = "a607fe940c6a563c6994f2c945394b19d2183b1c" + version = "v4.1.6" [[projects]] digest = "1:b75b3deb2bce8bc079e16bb2aecfe01eb80098f5650f9e93e5643ca8b7b73737" diff --git a/integrations/influxunifi/pkg/poller/config.go b/integrations/influxunifi/pkg/poller/config.go index 41847831..1725b03a 100644 --- a/integrations/influxunifi/pkg/poller/config.go +++ b/integrations/influxunifi/pkg/poller/config.go @@ -18,6 +18,7 @@ import ( "reflect" "strconv" "strings" + "sync" "time" "github.com/BurntSushi/toml" @@ -49,11 +50,12 @@ const ENVConfigPrefix = "UP_" // UnifiPoller contains the application startup data, and auth info for UniFi & Influx. type UnifiPoller struct { - Influx *influxunifi.InfluxUnifi - Unifi *unifi.Unifi - Flag *Flag - Config *Config - LastCheck time.Time + Influx *influxunifi.InfluxUnifi + Unifi *unifi.Unifi + Flag *Flag + Config *Config + LastCheck time.Time + sync.Mutex // locks the Unifi struct member when re-authing to unifi. } // Flag represents the CLI args available and their settings. diff --git a/integrations/influxunifi/pkg/poller/dumper.go b/integrations/influxunifi/pkg/poller/dumper.go index da825485..09514e57 100644 --- a/integrations/influxunifi/pkg/poller/dumper.go +++ b/integrations/influxunifi/pkg/poller/dumper.go @@ -35,9 +35,9 @@ func (u *UnifiPoller) DumpJSONPayload() (err error) { case err != nil: return err case StringInSlice(u.Flag.DumpJSON, []string{"d", "device", "devices"}): - return u.dumpSitesJSON(unifi.DevicePath, "Devices", sites) + return u.dumpSitesJSON(unifi.APIDevicePath, "Devices", sites) case StringInSlice(u.Flag.DumpJSON, []string{"client", "clients", "c"}): - return u.dumpSitesJSON(unifi.ClientPath, "Clients", sites) + return u.dumpSitesJSON(unifi.APIClientPath, "Clients", sites) case strings.HasPrefix(u.Flag.DumpJSON, "other "): apiPath := strings.SplitN(u.Flag.DumpJSON, " ", 2)[1] _, _ = fmt.Fprintf(os.Stderr, "[INFO] Dumping Path '%s':\n", apiPath) diff --git a/integrations/influxunifi/pkg/poller/start.go b/integrations/influxunifi/pkg/poller/start.go index d2c63083..8d8b9b5d 100644 --- a/integrations/influxunifi/pkg/poller/start.go +++ b/integrations/influxunifi/pkg/poller/start.go @@ -28,7 +28,10 @@ func New() *UnifiPoller { SaveSites: true, HTTPListen: defaultHTTPListen, Namespace: appName, - }, Flag: &Flag{ConfigFile: DefaultConfFile}, + }, + Flag: &Flag{ + ConfigFile: DefaultConfFile, + }, } } @@ -125,6 +128,7 @@ func (u *UnifiPoller) PollController() { for u.LastCheck = range ticker.C { if err := u.CollectAndProcess(); err != nil { u.LogErrorf("%v", err) + u.Unifi.CloseIdleConnections() u.Unifi = nil // trigger re-auth in unifi.go. } } diff --git a/integrations/influxunifi/pkg/poller/unifi.go b/integrations/influxunifi/pkg/poller/unifi.go index ce9ac2e7..ba38a371 100644 --- a/integrations/influxunifi/pkg/poller/unifi.go +++ b/integrations/influxunifi/pkg/poller/unifi.go @@ -11,6 +11,11 @@ import ( // GetUnifi returns a UniFi controller interface. func (u *UnifiPoller) GetUnifi() (err error) { + u.Lock() + defer u.Unlock() + if u.Unifi != nil { + u.Unifi.CloseIdleConnections() + } // Create an authenticated session to the Unifi Controller. u.Unifi, err = unifi.NewUnifi(&unifi.Config{ User: u.Config.UnifiUser, From 98a1f5633c8dbb25d9db840a518b0e6ef55f2c90 Mon Sep 17 00:00:00 2001 From: davidnewhall2 Date: Wed, 11 Dec 2019 01:47:32 -0800 Subject: [PATCH 12/13] fix nil pointer --- integrations/influxunifi/pkg/poller/start.go | 4 +++- integrations/influxunifi/pkg/poller/unifi.go | 10 ++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/integrations/influxunifi/pkg/poller/start.go b/integrations/influxunifi/pkg/poller/start.go index 8d8b9b5d..3ea0ad28 100644 --- a/integrations/influxunifi/pkg/poller/start.go +++ b/integrations/influxunifi/pkg/poller/start.go @@ -128,7 +128,9 @@ func (u *UnifiPoller) PollController() { for u.LastCheck = range ticker.C { if err := u.CollectAndProcess(); err != nil { u.LogErrorf("%v", err) - u.Unifi.CloseIdleConnections() + if u.Unifi != nil { + u.Unifi.CloseIdleConnections() + } u.Unifi = nil // trigger re-auth in unifi.go. } } diff --git a/integrations/influxunifi/pkg/poller/unifi.go b/integrations/influxunifi/pkg/poller/unifi.go index ba38a371..dcbd5c12 100644 --- a/integrations/influxunifi/pkg/poller/unifi.go +++ b/integrations/influxunifi/pkg/poller/unifi.go @@ -69,31 +69,37 @@ FIRST: // CollectMetrics grabs all the measurements from a UniFi controller and returns them. func (u *UnifiPoller) CollectMetrics() (*metrics.Metrics, error) { + var err error + if u.Unifi == nil || u.Config.ReAuth { // Some users need to re-auth every interval because the cookie times out. // Sometimes we hit this path when the controller dies. - u.LogDebugf("Re-authenticating to UniFi Controller") + u.Logf("Re-authenticating to UniFi Controller") if err := u.GetUnifi(); err != nil { return nil, err } } + m := &metrics.Metrics{TS: u.LastCheck} // At this point, it's the Current Check. - var err error // Get the sites we care about. if m.Sites, err = u.GetFilteredSites(); err != nil { return m, fmt.Errorf("unifi.GetSites(): %v", err) } + if u.Config.SaveIDS { m.IDSList, err = u.Unifi.GetIDS(m.Sites, time.Now().Add(u.Config.Interval.Duration), time.Now()) return m, fmt.Errorf("unifi.GetIDS(): %v", err) } + // Get all the points. if m.Clients, err = u.Unifi.GetClients(m.Sites); err != nil { return m, fmt.Errorf("unifi.GetClients(): %v", err) } + if m.Devices, err = u.Unifi.GetDevices(m.Sites); err != nil { return m, fmt.Errorf("unifi.GetDevices(): %v", err) } + return m, nil } From 2353ab627696925829dfe84c1194fd479b6a60a2 Mon Sep 17 00:00:00 2001 From: davidnewhall2 Date: Wed, 11 Dec 2019 02:25:26 -0800 Subject: [PATCH 13/13] a litle lint --- integrations/influxunifi/pkg/poller/influx.go | 6 +++++ .../influxunifi/pkg/poller/prometheus.go | 1 + integrations/influxunifi/pkg/poller/start.go | 4 +++- integrations/influxunifi/pkg/poller/unifi.go | 22 ++++++++++++++++++- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/integrations/influxunifi/pkg/poller/influx.go b/integrations/influxunifi/pkg/poller/influx.go index 3660cf38..ff2384e8 100644 --- a/integrations/influxunifi/pkg/poller/influx.go +++ b/integrations/influxunifi/pkg/poller/influx.go @@ -12,6 +12,7 @@ func (u *UnifiPoller) GetInfluxDB() (err error) { if u.Influx != nil { return nil } + u.Influx, err = influxunifi.New(&influxunifi.Config{ Database: u.Config.InfluxDB, User: u.Config.InfluxUser, @@ -22,7 +23,9 @@ func (u *UnifiPoller) GetInfluxDB() (err error) { if err != nil { return fmt.Errorf("influxdb: %v", err) } + u.Logf("Logging Measurements to InfluxDB at %s as user %s", u.Config.InfluxURL, u.Config.InfluxUser) + return nil } @@ -40,6 +43,7 @@ func (u *UnifiPoller) CollectAndProcess() error { if err != nil { return err } + u.AugmentMetrics(metrics) report, err := u.Influx.ReportMetrics(metrics) @@ -54,9 +58,11 @@ func (u *UnifiPoller) CollectAndProcess() error { // LogInfluxReport writes a log message after exporting to influxdb. func (u *UnifiPoller) LogInfluxReport(r *influxunifi.Report) { idsMsg := "" + if u.Config.SaveIDS { idsMsg = fmt.Sprintf("IDS Events: %d, ", len(r.Metrics.IDSList)) } + u.Logf("UniFi Metrics Recorded. Sites: %d, Clients: %d, "+ "UAP: %d, USG/UDM: %d, USW: %d, %sPoints: %d, Fields: %d, Errs: %d, Elapsed: %v", len(r.Metrics.Sites), len(r.Metrics.Clients), len(r.Metrics.UAPs), diff --git a/integrations/influxunifi/pkg/poller/prometheus.go b/integrations/influxunifi/pkg/poller/prometheus.go index 28371c9a..59aba109 100644 --- a/integrations/influxunifi/pkg/poller/prometheus.go +++ b/integrations/influxunifi/pkg/poller/prometheus.go @@ -23,6 +23,7 @@ func (u *UnifiPoller) RunPrometheus() error { LoggingFn: u.LogExportReport, ReportErrors: true, // XXX: Does this need to be configurable? })) + return http.ListenAndServe(u.Config.HTTPListen, nil) } diff --git a/integrations/influxunifi/pkg/poller/start.go b/integrations/influxunifi/pkg/poller/start.go index 3ea0ad28..3f1a4d74 100644 --- a/integrations/influxunifi/pkg/poller/start.go +++ b/integrations/influxunifi/pkg/poller/start.go @@ -124,14 +124,16 @@ func (u *UnifiPoller) Run() error { func (u *UnifiPoller) PollController() { interval := u.Config.Interval.Round(time.Second) log.Printf("[INFO] Everything checks out! Poller started, InfluxDB interval: %v", interval) + ticker := time.NewTicker(interval) for u.LastCheck = range ticker.C { if err := u.CollectAndProcess(); err != nil { u.LogErrorf("%v", err) + if u.Unifi != nil { u.Unifi.CloseIdleConnections() + u.Unifi = nil // trigger re-auth in unifi.go. } - u.Unifi = nil // trigger re-auth in unifi.go. } } } diff --git a/integrations/influxunifi/pkg/poller/unifi.go b/integrations/influxunifi/pkg/poller/unifi.go index dcbd5c12..beeacec5 100644 --- a/integrations/influxunifi/pkg/poller/unifi.go +++ b/integrations/influxunifi/pkg/poller/unifi.go @@ -13,9 +13,11 @@ import ( func (u *UnifiPoller) GetUnifi() (err error) { u.Lock() defer u.Unlock() + if u.Unifi != nil { u.Unifi.CloseIdleConnections() } + // Create an authenticated session to the Unifi Controller. u.Unifi, err = unifi.NewUnifi(&unifi.Config{ User: u.Config.UnifiUser, @@ -29,6 +31,7 @@ func (u *UnifiPoller) GetUnifi() (err error) { u.Unifi = nil return fmt.Errorf("unifi controller: %v", err) } + u.LogDebugf("Authenticated with controller successfully") return u.CheckSites() @@ -40,20 +43,26 @@ func (u *UnifiPoller) CheckSites() error { if strings.Contains(strings.ToLower(u.Config.Mode), "lambda") { return nil // Skip this in lambda mode. } + u.LogDebugf("Checking Controller Sites List") + sites, err := u.Unifi.GetSites() if err != nil { return err } + msg := []string{} + for _, site := range sites { msg = append(msg, site.Name+" ("+site.Desc+")") } u.Logf("Found %d site(s) on controller: %v", len(msg), strings.Join(msg, ", ")) + if StringInSlice("all", u.Config.Sites) { u.Config.Sites = []string{"all"} return nil } + FIRST: for _, s := range u.Config.Sites { for _, site := range sites { @@ -64,6 +73,7 @@ FIRST: // This is fine, it may get added later. u.LogErrorf("configured site not found on controller: %v", s) } + return nil } @@ -110,23 +120,29 @@ func (u *UnifiPoller) AugmentMetrics(metrics *metrics.Metrics) { if metrics == nil || metrics.Devices == nil || metrics.Clients == nil { return } + devices := make(map[string]string) bssdIDs := make(map[string]string) + for _, r := range metrics.UAPs { devices[r.Mac] = r.Name for _, v := range r.VapTable { bssdIDs[v.Bssid] = fmt.Sprintf("%s %s %s:", r.Name, v.Radio, v.RadioName) } } + for _, r := range metrics.USGs { devices[r.Mac] = r.Name } + for _, r := range metrics.USWs { devices[r.Mac] = r.Name } + for _, r := range metrics.UDMs { devices[r.Mac] = r.Name } + // These come blank, so set them here. for i, c := range metrics.Clients { metrics.Clients[i].SwName = devices[c.SwMac] @@ -134,6 +150,7 @@ func (u *UnifiPoller) AugmentMetrics(metrics *metrics.Metrics) { metrics.Clients[i].GwName = devices[c.GwMac] metrics.Clients[i].RadioDescription = bssdIDs[metrics.Clients[i].Bssid] + metrics.Clients[i].RadioProto } + if !u.Config.SaveSites { metrics.Sites = nil } @@ -143,13 +160,15 @@ func (u *UnifiPoller) AugmentMetrics(metrics *metrics.Metrics) { // Omits requested but unconfigured sites. Grabs the full list from the // controller and returns the sites provided in the config file. func (u *UnifiPoller) GetFilteredSites() (unifi.Sites, error) { + var i int + sites, err := u.Unifi.GetSites() if err != nil { return nil, err } else if len(u.Config.Sites) < 1 || StringInSlice("all", u.Config.Sites) { return sites, nil } - var i int + for _, s := range sites { // Only include valid sites in the request filter. if StringInSlice(s.Name, u.Config.Sites) { @@ -157,5 +176,6 @@ func (u *UnifiPoller) GetFilteredSites() (unifi.Sites, error) { i++ } } + return sites[:i], nil }