From 6c19dce90d33971abbcea9d385e2c3b4eee5fc30 Mon Sep 17 00:00:00 2001 From: davidnewhall2 Date: Tue, 12 Nov 2019 00:04:01 -0800 Subject: [PATCH] this is just a start, nothing works yet --- integrations/influxunifi/Gopkg.lock | 77 +++++++++++++++++++ integrations/influxunifi/examples/MANUAL.md | 16 +++- .../influxunifi/examples/up.conf.example | 10 ++- .../influxunifi/examples/up.json.example | 1 + .../influxunifi/examples/up.xml.example | 11 ++- .../influxunifi/examples/up.yaml.example | 9 ++- .../influxunifi/unifipoller/config.go | 2 + integrations/influxunifi/unifipoller/start.go | 25 +++++- integrations/influxunifi/unifipoller/unifi.go | 27 +++++-- 9 files changed, 160 insertions(+), 18 deletions(-) diff --git a/integrations/influxunifi/Gopkg.lock b/integrations/influxunifi/Gopkg.lock index ebe365dd..2c504530 100644 --- a/integrations/influxunifi/Gopkg.lock +++ b/integrations/influxunifi/Gopkg.lock @@ -9,6 +9,22 @@ revision = "3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005" version = "v0.3.1" +[[projects]] + digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d" + name = "github.com/beorn7/perks" + packages = ["quantile"] + pruneopts = "UT" + revision = "37c8de3658fcb183f997c4e13e8337516ab753e6" + version = "v1.0.1" + +[[projects]] + digest = "1:573ca21d3669500ff845bdebee890eb7fc7f0f50c59f2132f2a0c6b03d85086a" + name = "github.com/golang/protobuf" + packages = ["proto"] + pruneopts = "UT" + revision = "6c65a5562fc06764971b7c5d05c76c75e84bdbf7" + version = "v1.3.2" + [[projects]] branch = "master" digest = "1:50708c8fc92aec981df5c446581cf9f90ba9e2a5692118e0ce75d4534aaa14a2" @@ -21,6 +37,58 @@ pruneopts = "UT" revision = "fc22c7df067eefd070157f157893fbce961d6359" +[[projects]] + digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc" + name = "github.com/matttproud/golang_protobuf_extensions" + packages = ["pbutil"] + pruneopts = "UT" + revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" + version = "v1.0.1" + +[[projects]] + digest = "1:eb04f69c8991e52eff33c428bd729e04208bf03235be88e4df0d88497c6861b9" + name = "github.com/prometheus/client_golang" + packages = [ + "prometheus", + "prometheus/internal", + "prometheus/promhttp", + ] + pruneopts = "UT" + revision = "170205fb58decfd011f1550d4cfb737230d7ae4f" + version = "v1.1.0" + +[[projects]] + branch = "master" + digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4" + name = "github.com/prometheus/client_model" + packages = ["go"] + pruneopts = "UT" + revision = "14fe0d1b01d4d5fc031dd4bec1823bd3ebbe8016" + +[[projects]] + digest = "1:f119e3205d3a1f0f19dbd7038eb37528e2c6f0933269dc344e305951fb87d632" + name = "github.com/prometheus/common" + packages = [ + "expfmt", + "internal/bitbucket.org/ww/goautoneg", + "model", + ] + pruneopts = "UT" + revision = "287d3e634a1e550c9e463dd7e5a75a422c614505" + version = "v0.7.0" + +[[projects]] + digest = "1:3e363393b80d8f142984a811464eb7e34064700626cc111887373ea76f8ea0bf" + name = "github.com/prometheus/procfs" + packages = [ + ".", + "internal/fs", + "internal/util", + ] + pruneopts = "UT" + revision = "8a055596020d692cf491851e47ba3e302d9f90ce" + version = "v0.0.6" + [[projects]] digest = "1:524b71991fc7d9246cc7dc2d9e0886ccb97648091c63e30eef619e6862c955dd" name = "github.com/spf13/pflag" @@ -29,6 +97,14 @@ revision = "2e9d26c8c37aae03e3f9d4e90b7116f5accb7cab" version = "v1.0.5" +[[projects]] + branch = "master" + digest = "1:71b15a23c3c47b97dd98f97c6afde8da4de9f21dec38c34e2ac2e04dc7b09167" + name = "golang.org/x/sys" + packages = ["windows"] + pruneopts = "UT" + revision = "d32e6e3b99c40f2bfaea45ea9596ed539eed1c0d" + [[projects]] digest = "1:e74d5f03545d51228b9539aaffc5eb8a692fcb22f38fa60253437b1fc063a73b" name = "golift.io/unifi" @@ -51,6 +127,7 @@ input-imports = [ "github.com/BurntSushi/toml", "github.com/influxdata/influxdb1-client/v2", + "github.com/prometheus/client_golang/prometheus/promhttp", "github.com/spf13/pflag", "golift.io/unifi", "gopkg.in/yaml.v2", diff --git a/integrations/influxunifi/examples/MANUAL.md b/integrations/influxunifi/examples/MANUAL.md index c9ecd0de..16038cf4 100644 --- a/integrations/influxunifi/examples/MANUAL.md +++ b/integrations/influxunifi/examples/MANUAL.md @@ -89,10 +89,10 @@ is provided so the application can be easily adapted to any environment. mode default: "influx" * Value: influx This default mode runs this application as a daemon. It will poll - the controller at the configured interval. Providing an invalid value - will run in this default mode. + the controller at the configured interval and report measurements to + InfluxDB. Providing an invalid value will run in this default mode. - * Value: influxlambda - (the only other available option right now) + * Value: influxlambda Setting this value will invoke a run-once mode where the application immediately polls the controller and reports the metrics to InfluxDB. Then it exits. This mode is useful in an AWS Lambda or a crontab where @@ -101,6 +101,16 @@ is provided so the application can be easily adapted to any environment. This mode can also be combined with a "test database" in InfluxDB to give yourself a "test config file" you may run ad-hoc to test changes. + * Value: prometheus + In this mode the application opens an http interface and exports the + measurements at /metrics for collection by prometheus. Enabling this + mode disables InfluxDB usage entirely. + + http_listen default: 0.0.0.0:61317 + This option controls the IP and port the http listener uses when the + mode is set to prometheus. This setting has no effect when other modes + are in use. + influx_url default: http://127.0.0.1:8086 This is the URL where the Influx web server is available. diff --git a/integrations/influxunifi/examples/up.conf.example b/integrations/influxunifi/examples/up.conf.example index ae6497e7..e702ec28 100644 --- a/integrations/influxunifi/examples/up.conf.example +++ b/integrations/influxunifi/examples/up.conf.example @@ -24,13 +24,21 @@ quiet = false # an invalid mode will also result in "influx". In this default mode the application # runs as a daemon and polls the controller at the configured interval. # -# There is only one other option at this time: "influxlambda" +# There are two other options at this time: "influxlambda" and "prometheus" # # Lambda mode makes the application exit after collecting and reporting metrics # to InfluxDB one time. This mode requires an external process like an AWS Lambda # or a simple crontab to keep the timings accurate on UniFi Poller run intervals. +# +# Prometheus mode opens an HTTP server on port 61317 and exports the metrics at +# /metrics for polling collection by a prometheus server. This disables influxdb. mode = "influx" + +# This controls on which ip and port /metrics is exported when mode is "prometheus". +# This has no effect in other modes. Must contain a colon and port. +http_listen = "0.0.0.0:61317" + # InfluxDB does not require auth by default, so the user/password are probably unimportant. influx_url = "http://127.0.0.1:8086" influx_user = "unifi" diff --git a/integrations/influxunifi/examples/up.json.example b/integrations/influxunifi/examples/up.json.example index e59ec7c3..7b67ac5b 100644 --- a/integrations/influxunifi/examples/up.json.example +++ b/integrations/influxunifi/examples/up.json.example @@ -4,6 +4,7 @@ "debug": false, "quiet": false, "mode": "influx", + "http_listen": "0.0.0.0:61317", "influx_url": "http://127.0.0.1:8086", "influx_user": "unifi", "influx_pass": "unifi", diff --git a/integrations/influxunifi/examples/up.xml.example b/integrations/influxunifi/examples/up.xml.example index 87f1cac5..f77503e5 100644 --- a/integrations/influxunifi/examples/up.xml.example +++ b/integrations/influxunifi/examples/up.xml.example @@ -40,14 +40,23 @@ # an invalid mode will also result in "influx". In this default mode the application # runs as a daemon and polls the controller at the configured interval. # - # There is only one other option at this time: "influxlambda" + # There are two other options at this time: "influxlambda" and "prometheus" # # Lambda mode makes the application exit after collecting and reporting metrics # to InfluxDB one time. This mode requires an external process like an AWS Lambda # or a simple crontab to keep the timings accurate on UniFi Poller run intervals. + # + # Prometheus mode opens an HTTP server on port 61317 and exports the metrics at + # /metrics for polling collection by a prometheus server. This disables influxdb. --> influx + + 0.0.0.0:61317 + diff --git a/integrations/influxunifi/examples/up.yaml.example b/integrations/influxunifi/examples/up.yaml.example index 1042c4e1..c55b8d95 100644 --- a/integrations/influxunifi/examples/up.yaml.example +++ b/integrations/influxunifi/examples/up.yaml.example @@ -25,13 +25,20 @@ quiet: false # an invalid mode will also result in "influx". In this default mode the application # runs as a daemon and polls the controller at the configured interval. # -# There is only one other option at this time: "influxlambda" +# There are two other options at this time: "influxlambda" and "prometheus" # # Lambda mode makes the application exit after collecting and reporting metrics # to InfluxDB one time. This mode requires an external process like an AWS Lambda # or a simple crontab to keep the timings accurate on UniFi Poller run intervals. +# +# Prometheus mode opens an HTTP server on port 61317 and exports the metrics at +# /metrics for polling collection by a prometheus server. This disables influxdb. mode: "influx" +# This controls on which ip and port /metrics is exported when mode is "prometheus". +# This has no effect in other modes. Must contain a colon and port. +http_listen: "0.0.0.0:61317" + # InfluxDB does not require auth by default, so the user/password are probably unimportant. influx_url: "http://127.0.0.1:8086" influx_user: "unifi" diff --git a/integrations/influxunifi/unifipoller/config.go b/integrations/influxunifi/unifipoller/config.go index 56516dda..7a4bdbfe 100644 --- a/integrations/influxunifi/unifipoller/config.go +++ b/integrations/influxunifi/unifipoller/config.go @@ -31,6 +31,7 @@ const ( defaultInfluxURL = "http://127.0.0.1:8086" defaultUnifiUser = "influx" defaultUnifiURL = "https://127.0.0.1:8443" + defaultHTTPListen = ":61317" ) // ENVConfigPrefix is the prefix appended to an env variable tag @@ -77,6 +78,7 @@ type Config struct { ReAuth bool `json:"reauthenticate" toml:"reauthenticate" xml:"reauthenticate" yaml:"reauthenticate" env:"REAUTHENTICATE"` InfxBadSSL bool `json:"influx_insecure_ssl" toml:"influx_insecure_ssl" xml:"influx_insecure_ssl" yaml:"influx_insecure_ssl" env:"INFLUX_INSECURE_SSL"` Mode string `json:"mode" toml:"mode" xml:"mode" yaml:"mode" env:"POLLING_MODE"` + HTTPListen string `json:"http_listen" toml:"http_listen" xml:"http_listen" yaml:"http_listen" env:"HTTP_LISTEN"` InfluxURL string `json:"influx_url,omitempty" toml:"influx_url,omitempty" xml:"influx_url" yaml:"influx_url" env:"INFLUX_URL"` InfluxUser string `json:"influx_user,omitempty" toml:"influx_user,omitempty" xml:"influx_user" yaml:"influx_user" env:"INFLUX_USER"` InfluxPass string `json:"influx_pass,omitempty" toml:"influx_pass,omitempty" xml:"influx_pass" yaml:"influx_pass" env:"INFLUX_PASS"` diff --git a/integrations/influxunifi/unifipoller/start.go b/integrations/influxunifi/unifipoller/start.go index 334119a1..d154cd88 100644 --- a/integrations/influxunifi/unifipoller/start.go +++ b/integrations/influxunifi/unifipoller/start.go @@ -4,11 +4,13 @@ import ( "crypto/tls" "fmt" "log" + "net/http" "os" "strings" "time" influx "github.com/influxdata/influxdb1-client/v2" + "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/spf13/pflag" "golift.io/unifi" ) @@ -30,6 +32,7 @@ func Start() error { UnifiBase: defaultUnifiURL, Interval: Duration{defaultInterval}, Sites: []string{"all"}, + HTTPListen: defaultHTTPListen, }} up.Flag.Parse(os.Args[1:]) if up.Flag.ShowVer { @@ -83,14 +86,28 @@ func (u *UnifiPoller) Run() (err error) { if err = u.GetInfluxDB(); err != nil { return err } - u.Logf("Logging Measurements to InfluxDB at %s as user %s", u.Config.InfluxURL, u.Config.InfluxUser) + switch strings.ToLower(u.Config.Mode) { case "influxlambda", "lambdainflux", "lambda_influx", "influx_lambda": - u.LogDebugf("Lambda Mode Enabled") + u.Logf("Logging Measurements to InfluxDB at %s as user %s one time (lambda mode)", + u.Config.InfluxURL, u.Config.InfluxUser) u.LastCheck = time.Now() - return u.CollectAndReport() + return u.CollectAndProcess(u.ReportMetrics) + case "prometheus", "exporter": + u.Logf("Exporting Measurements at https://%s/metrics for Prometheus", u.Config.HTTPListen) + u.Config.Mode = "http exporter" + http.Handle("/metrics", promhttp.Handler()) + go func() { + err = http.ListenAndServe(u.Config.HTTPListen, nil) + if err != http.ErrServerClosed { + log.Fatalf("[ERROR] http server: %v", err) + } + }() + return u.PollController(u.ExportMetrics) default: - return u.PollController() + u.Logf("Logging Measurements to InfluxDB at %s as user %s", u.Config.InfluxURL, u.Config.InfluxUser) + u.Config.Mode = "influx poller" + return u.PollController(u.ReportMetrics) } } diff --git a/integrations/influxunifi/unifipoller/unifi.go b/integrations/influxunifi/unifipoller/unifi.go index 57fd8c51..b74b7bc4 100644 --- a/integrations/influxunifi/unifipoller/unifi.go +++ b/integrations/influxunifi/unifipoller/unifi.go @@ -43,11 +43,12 @@ FIRST: return nil } -// PollController runs forever, polling UniFi, and pushing to influx. +// PollController runs forever, polling UniFi +// and pushing to influx OR exporting for prometheus. // This is started by Run() after everything checks out. -func (u *UnifiPoller) PollController() error { +func (u *UnifiPoller) PollController(process func(*Metrics) error) error { interval := u.Config.Interval.Round(time.Second) - log.Println("[INFO] Everything checks out! Poller started, interval:", interval) + log.Printf("[INFO] Everything checks out! Poller started in %v mode, interval: %v", u.Config.Mode, interval) ticker := time.NewTicker(interval) for u.LastCheck = range ticker.C { var err error @@ -60,7 +61,7 @@ func (u *UnifiPoller) PollController() error { } if err == nil { // Only run this if the authentication procedure didn't return error. - _ = u.CollectAndReport() + _ = u.CollectAndProcess(process) } if u.errorCount > 0 { return fmt.Errorf("controller or influxdb errors, stopping poller") @@ -69,12 +70,13 @@ func (u *UnifiPoller) PollController() error { return nil } -// CollectAndReport collects measurements and reports them to influxdb. +// CollectAndProcess collects measurements and then passese them into the +// provided method. The method is either an http exporter or an influxdb update. // Can be called once or in a ticker loop. This function and all the ones below // handle their own logging. An error is returned so the calling function may // determine if there was a read or write error and act on it. This is currently // called in two places in this library. One returns an error, one does not. -func (u *UnifiPoller) CollectAndReport() error { +func (u *UnifiPoller) CollectAndProcess(process func(*Metrics) error) error { metrics, err := u.CollectMetrics() if err != nil { return err @@ -82,8 +84,8 @@ func (u *UnifiPoller) CollectAndReport() error { if err := u.AugmentMetrics(metrics); err != nil { return err } - err = u.ReportMetrics(metrics) - u.LogError(err, "reporting metrics") + err = process(metrics) + u.LogError(err, "processing metrics") return err } @@ -145,6 +147,15 @@ func (u *UnifiPoller) AugmentMetrics(metrics *Metrics) error { return nil } +// ExportMetrics updates the internal metrics provided via +// HTTP at /metrics for prometheus collection. +func (u *UnifiPoller) ExportMetrics(metrics *Metrics) error { + /* + This is where it gets complicated, and probably deserves its own package. + */ + return nil +} + // ReportMetrics batches all the metrics and writes them to InfluxDB. // Returns an error if the write to influx fails. func (u *UnifiPoller) ReportMetrics(metrics *Metrics) error {