Merge branch 'master' of ../promunifi into merge-them-all

This commit is contained in:
Cody Lee 2022-11-23 21:19:53 -06:00
commit 8aaed582ff
No known key found for this signature in database
15 changed files with 2629 additions and 0 deletions

View File

@ -0,0 +1,9 @@
language: go
go:
- 1.16.x
before_install:
# download super-linter: golangci-lint
- curl -sL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.38.0
script:
- go test ./...
- golangci-lint run --enable-all -D exhaustivestruct,nlreturn,wrapcheck,interfacer,maligned

View File

@ -0,0 +1,21 @@
MIT LICENSE.
Copyright (c) 2018-2020 David Newhall II
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,4 @@
# prometheus
This package provides the interface to turn UniFi measurements into prometheus
exported metrics. Requires the poller package for actual UniFi data collection.

View File

@ -0,0 +1,232 @@
package promunifi
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/unpoller/unifi"
)
type uclient struct {
Anomalies *prometheus.Desc
BytesR *prometheus.Desc
CCQ *prometheus.Desc
Satisfaction *prometheus.Desc
Noise *prometheus.Desc
RoamCount *prometheus.Desc
RSSI *prometheus.Desc
RxBytes *prometheus.Desc
RxBytesR *prometheus.Desc
RxPackets *prometheus.Desc
RxRate *prometheus.Desc
Signal *prometheus.Desc
TxBytes *prometheus.Desc
TxBytesR *prometheus.Desc
TxPackets *prometheus.Desc
TxRetries *prometheus.Desc
TxPower *prometheus.Desc
TxRate *prometheus.Desc
Uptime *prometheus.Desc
WifiTxAttempts *prometheus.Desc
WiredRxBytes *prometheus.Desc
WiredRxBytesR *prometheus.Desc
WiredRxPackets *prometheus.Desc
WiredTxBytes *prometheus.Desc
WiredTxBytesR *prometheus.Desc
WiredTxPackets *prometheus.Desc
DPITxPackets *prometheus.Desc
DPIRxPackets *prometheus.Desc
DPITxBytes *prometheus.Desc
DPIRxBytes *prometheus.Desc
}
func descClient(ns string) *uclient {
labels := []string{
"name", "mac", "site_name", "gw_name", "sw_name", "vlan",
"ip", "oui", "network", "sw_port", "ap_name", "source", "wired",
}
labelW := append([]string{"radio_name", "radio", "radio_proto", "channel", "essid", "bssid", "radio_desc"}, labels...)
labelDPI := []string{"name", "mac", "site_name", "source", "category", "application"}
return &uclient{
Anomalies: prometheus.NewDesc(ns+"anomalies", "Client Anomalies", labelW, nil),
BytesR: prometheus.NewDesc(ns+"transfer_rate_bytes", "Client Data Rate", labelW, nil),
CCQ: prometheus.NewDesc(ns+"ccq_ratio", "Client Connection Quality", labelW, nil),
Satisfaction: prometheus.NewDesc(ns+"satisfaction_ratio", "Client Satisfaction", labelW, nil),
Noise: prometheus.NewDesc(ns+"noise_db", "Client AP Noise", labelW, nil),
RoamCount: prometheus.NewDesc(ns+"roam_count_total", "Client Roam Counter", labelW, nil),
RSSI: prometheus.NewDesc(ns+"rssi_db", "Client RSSI", labelW, nil),
RxBytes: prometheus.NewDesc(ns+"receive_bytes_total", "Client Receive Bytes", labels, nil),
RxBytesR: prometheus.NewDesc(ns+"receive_rate_bytes", "Client Receive Data Rate", labels, nil),
RxPackets: prometheus.NewDesc(ns+"receive_packets_total", "Client Receive Packets", labels, nil),
RxRate: prometheus.NewDesc(ns+"radio_receive_rate_bps", "Client Receive Rate", labelW, nil),
Signal: prometheus.NewDesc(ns+"radio_signal_db", "Client Signal Strength", labelW, nil),
TxBytes: prometheus.NewDesc(ns+"transmit_bytes_total", "Client Transmit Bytes", labels, nil),
TxBytesR: prometheus.NewDesc(ns+"transmit_rate_bytes", "Client Transmit Data Rate", labels, nil),
TxPackets: prometheus.NewDesc(ns+"transmit_packets_total", "Client Transmit Packets", labels, nil),
TxRetries: prometheus.NewDesc(ns+"transmit_retries_total", "Client Transmit Retries", labels, nil),
TxPower: prometheus.NewDesc(ns+"radio_transmit_power_dbm", "Client Transmit Power", labelW, nil),
TxRate: prometheus.NewDesc(ns+"radio_transmit_rate_bps", "Client Transmit Rate", labelW, nil),
WifiTxAttempts: prometheus.NewDesc(ns+"wifi_attempts_transmit_total", "Client Wifi Transmit Attempts", labelW, nil),
Uptime: prometheus.NewDesc(ns+"uptime_seconds", "Client Uptime", labelW, nil),
DPITxPackets: prometheus.NewDesc(ns+"dpi_transmit_packets", "Client DPI Transmit Packets", labelDPI, nil),
DPIRxPackets: prometheus.NewDesc(ns+"dpi_receive_packets", "Client DPI Receive Packets", labelDPI, nil),
DPITxBytes: prometheus.NewDesc(ns+"dpi_transmit_bytes", "Client DPI Transmit Bytes", labelDPI, nil),
DPIRxBytes: prometheus.NewDesc(ns+"dpi_receive_bytes", "Client DPI Receive Bytes", labelDPI, nil),
}
}
func (u *promUnifi) exportClientDPI(r report, v interface{}, appTotal, catTotal totalsDPImap) {
s, ok := v.(*unifi.DPITable)
if !ok {
u.LogErrorf("invalid type given to ClientsDPI: %T", v)
return
}
for _, dpi := range s.ByApp {
labelDPI := []string{
s.Name, s.MAC, s.SiteName, s.SourceName,
unifi.DPICats.Get(dpi.Cat), unifi.DPIApps.GetApp(dpi.Cat, dpi.App),
}
fillDPIMapTotals(appTotal, labelDPI[5], s.SourceName, s.SiteName, dpi)
fillDPIMapTotals(catTotal, labelDPI[4], s.SourceName, s.SiteName, dpi)
// log.Println(labelDPI, dpi.Cat, dpi.App, dpi.TxBytes, dpi.RxBytes, dpi.TxPackets, dpi.RxPackets)
r.send([]*metric{
{u.Client.DPITxPackets, counter, dpi.TxPackets, labelDPI},
{u.Client.DPIRxPackets, counter, dpi.RxPackets, labelDPI},
{u.Client.DPITxBytes, counter, dpi.TxBytes, labelDPI},
{u.Client.DPIRxBytes, counter, dpi.RxBytes, labelDPI},
})
}
}
func (u *promUnifi) exportClient(r report, c *unifi.Client) {
labels := []string{
c.Name, c.Mac, c.SiteName, c.GwName, c.SwName, c.Vlan.Txt,
c.IP, c.Oui, c.Network, c.SwPort.Txt, c.ApName, c.SourceName, "",
}
labelW := append([]string{
c.RadioName, c.Radio, c.RadioProto, c.Channel.Txt, c.Essid, c.Bssid, c.RadioDescription,
}, labels...)
if c.IsWired.Val {
labels[len(labels)-1] = "true"
labelW[len(labelW)-1] = "true"
r.send([]*metric{
{u.Client.RxBytes, counter, c.WiredRxBytes, labels},
{u.Client.RxBytesR, gauge, c.WiredRxBytesR, labels},
{u.Client.RxPackets, counter, c.WiredRxPackets, labels},
{u.Client.TxBytes, counter, c.WiredTxBytes, labels},
{u.Client.TxBytesR, gauge, c.WiredTxBytesR, labels},
{u.Client.TxPackets, counter, c.WiredTxPackets, labels},
})
} else {
labels[len(labels)-1] = "false"
labelW[len(labelW)-1] = "false"
r.send([]*metric{
{u.Client.Anomalies, counter, c.Anomalies, labelW},
{u.Client.CCQ, gauge, float64(c.Ccq) / 1000.0, labelW},
{u.Client.Satisfaction, gauge, c.Satisfaction.Val / 100.0, labelW},
{u.Client.Noise, gauge, c.Noise, labelW},
{u.Client.RoamCount, counter, c.RoamCount, labelW},
{u.Client.RSSI, gauge, c.Rssi, labelW},
{u.Client.Signal, gauge, c.Signal, labelW},
{u.Client.TxPower, gauge, c.TxPower, labelW},
{u.Client.TxRate, gauge, c.TxRate * 1000, labelW},
{u.Client.WifiTxAttempts, counter, c.WifiTxAttempts, labelW},
{u.Client.RxRate, gauge, c.RxRate * 1000, labelW},
{u.Client.TxRetries, counter, c.TxRetries, labels},
{u.Client.TxBytes, counter, c.TxBytes, labels},
{u.Client.TxBytesR, gauge, c.TxBytesR, labels},
{u.Client.TxPackets, counter, c.TxPackets, labels},
{u.Client.RxBytes, counter, c.RxBytes, labels},
{u.Client.RxBytesR, gauge, c.RxBytesR, labels},
{u.Client.RxPackets, counter, c.RxPackets, labels},
{u.Client.BytesR, gauge, c.BytesR, labelW},
})
}
r.send([]*metric{{u.Client.Uptime, gauge, c.Uptime, labelW}})
}
// totalsDPImap: controller, site, name (app/cat name), dpi.
type totalsDPImap map[string]map[string]map[string]unifi.DPIData
// fillDPIMapTotals fills in totals for categories and applications. maybe clients too.
// This allows less processing in InfluxDB to produce total transfer data per cat or app.
func fillDPIMapTotals(m totalsDPImap, name, controller, site string, dpi unifi.DPIData) {
if _, ok := m[controller]; !ok {
m[controller] = make(map[string]map[string]unifi.DPIData)
}
if _, ok := m[controller][site]; !ok {
m[controller][site] = make(map[string]unifi.DPIData)
}
if _, ok := m[controller][site][name]; !ok {
m[controller][site][name] = dpi
return
}
oldDPI := m[controller][site][name]
oldDPI.TxPackets += dpi.TxPackets
oldDPI.RxPackets += dpi.RxPackets
oldDPI.TxBytes += dpi.TxBytes
oldDPI.RxBytes += dpi.RxBytes
m[controller][site][name] = oldDPI
}
func (u *promUnifi) exportClientDPItotals(r report, appTotal, catTotal totalsDPImap) {
type all []struct {
kind string
val totalsDPImap
}
// This produces 7000+ metrics per site. Disabled for now.
if appTotal != nil {
appTotal = nil
}
// This can allow us to aggregate other data types later, like `name` or `mac`, or anything else unifi adds.
a := all{
{
kind: "application",
val: appTotal,
},
{
kind: "category",
val: catTotal,
},
}
for _, k := range a {
for controller, s := range k.val {
for site, c := range s {
for name, m := range c {
labelDPI := []string{"TOTAL", "TOTAL", site, controller, "TOTAL", "TOTAL"}
switch k.kind {
case "application":
labelDPI[5] = name
case "category":
labelDPI[4] = name
case "name":
labelDPI[0] = name
case "mac":
labelDPI[1] = name
}
m := []*metric{
{u.Client.DPITxPackets, counter, m.TxPackets, labelDPI},
{u.Client.DPIRxPackets, counter, m.RxPackets, labelDPI},
{u.Client.DPITxBytes, counter, m.TxBytes, labelDPI},
{u.Client.DPIRxBytes, counter, m.RxBytes, labelDPI},
}
r.send(m)
}
}
}
}
}

View File

@ -0,0 +1,362 @@
// Package promunifi provides the bridge between unpoller metrics and prometheus.
package promunifi
import (
"fmt"
"net/http"
"reflect"
"strings"
"sync"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
promver "github.com/prometheus/common/version"
"github.com/unpoller/poller"
"github.com/unpoller/unifi"
"github.com/unpoller/webserver"
"golift.io/version"
)
// PluginName is the name of this plugin.
const PluginName = "prometheus"
const (
// channel buffer, fits at least one batch.
defaultBuffer = 50
defaultHTTPListen = "0.0.0.0:9130"
// simply fewer letters.
counter = prometheus.CounterValue
gauge = prometheus.GaugeValue
)
var ErrMetricFetchFailed = fmt.Errorf("metric fetch failed")
type promUnifi struct {
*Config `json:"prometheus" toml:"prometheus" xml:"prometheus" yaml:"prometheus"`
Client *uclient
Device *unifiDevice
UAP *uap
USG *usg
USW *usw
Site *site
RogueAP *rogueap
// This interface is passed to the Collect() method. The Collect method uses
// this interface to retrieve the latest UniFi measurements and export them.
Collector poller.Collect
}
// Config is the input (config file) data used to initialize this output plugin.
type Config struct {
// If non-empty, each of the collected metrics is prefixed by the
// provided string and an underscore ("_").
Namespace string `json:"namespace" toml:"namespace" xml:"namespace" yaml:"namespace"`
HTTPListen string `json:"http_listen" toml:"http_listen" xml:"http_listen" yaml:"http_listen"`
// If these are provided, the app will attempt to listen with an SSL connection.
SSLCrtPath string `json:"ssl_cert_path" toml:"ssl_cert_path" xml:"ssl_cert_path" yaml:"ssl_cert_path"`
SSLKeyPath string `json:"ssl_key_path" toml:"ssl_key_path" xml:"ssl_key_path" yaml:"ssl_key_path"`
// Buffer is a channel buffer.
// Default is probably 50. Seems fast there; try 1 to see if CPU usage goes down?
Buffer int `json:"buffer" toml:"buffer" xml:"buffer" yaml:"buffer"`
// If true, any error encountered during collection is reported as an
// invalid metric (see NewInvalidMetric). Otherwise, errors are ignored
// and the collected metrics will be incomplete. Possibly, no metrics
// will be collected at all.
ReportErrors bool `json:"report_errors" toml:"report_errors" xml:"report_errors" yaml:"report_errors"`
Disable bool `json:"disable" toml:"disable" xml:"disable" yaml:"disable"`
// Save data for dead ports? ie. ports that are down or disabled.
DeadPorts bool `json:"dead_ports" toml:"dead_ports" xml:"dead_ports" yaml:"dead_ports"`
}
type metric struct {
Desc *prometheus.Desc
ValueType prometheus.ValueType
Value interface{}
Labels []string
}
// Report accumulates counters that are printed to a log line.
type Report struct {
*Config
Total int // Total count of metrics recorded.
Errors int // Total count of errors recording metrics.
Zeros int // Total count of metrics equal to zero.
USG int // Total count of USG devices.
USW int // Total count of USW devices.
UAP int // Total count of UAP devices.
UDM int // Total count of UDM devices.
UXG int // Total count of UXG devices.
Metrics *poller.Metrics // Metrics collected and recorded.
Elapsed time.Duration // Duration elapsed collecting and exporting.
Fetch time.Duration // Duration elapsed making controller requests.
Start time.Time // Time collection began.
ch chan []*metric
wg sync.WaitGroup
}
// target is used for targeted (sometimes dynamic) metrics scrapes.
type target struct {
*poller.Filter
u *promUnifi
}
// init is how this modular code is initialized by the main app.
// This module adds itself as an output module to the poller core.
func init() { // nolint: gochecknoinits
u := &promUnifi{Config: &Config{}}
poller.NewOutput(&poller.Output{
Name: PluginName,
Config: u,
Method: u.Run,
})
}
// Run creates the collectors and starts the web server up.
// Should be run in a Go routine. Returns nil if not configured.
func (u *promUnifi) Run(c poller.Collect) error {
if u.Collector = c; u.Config == nil || u.Disable {
u.Logf("Prometheus config missing (or disabled), Prometheus HTTP listener disabled!")
return nil
}
u.Namespace = strings.Trim(strings.ReplaceAll(u.Namespace, "-", "_"), "_")
if u.Namespace == "" {
u.Namespace = strings.ReplaceAll(poller.AppName, "-", "")
}
if u.HTTPListen == "" {
u.HTTPListen = defaultHTTPListen
}
if u.Buffer == 0 {
u.Buffer = defaultBuffer
}
u.Client = descClient(u.Namespace + "_client_")
u.Device = descDevice(u.Namespace + "_device_") // stats for all device types.
u.UAP = descUAP(u.Namespace + "_device_")
u.USG = descUSG(u.Namespace + "_device_")
u.USW = descUSW(u.Namespace + "_device_")
u.Site = descSite(u.Namespace + "_site_")
u.RogueAP = descRogueAP(u.Namespace + "_rogueap_")
mux := http.NewServeMux()
promver.Version = version.Version
promver.Revision = version.Revision
promver.Branch = version.Branch
webserver.UpdateOutput(&webserver.Output{Name: PluginName, Config: u.Config})
prometheus.MustRegister(promver.NewCollector(u.Namespace))
prometheus.MustRegister(u)
mux.Handle("/metrics", promhttp.HandlerFor(prometheus.DefaultGatherer,
promhttp.HandlerOpts{ErrorHandling: promhttp.ContinueOnError},
))
mux.HandleFunc("/scrape", u.ScrapeHandler)
mux.HandleFunc("/", u.DefaultHandler)
switch u.SSLKeyPath == "" && u.SSLCrtPath == "" {
case true:
u.Logf("Prometheus exported at http://%s/ - namespace: %s", u.HTTPListen, u.Namespace)
return http.ListenAndServe(u.HTTPListen, mux)
default:
u.Logf("Prometheus exported at https://%s/ - namespace: %s", u.HTTPListen, u.Namespace)
return http.ListenAndServeTLS(u.HTTPListen, u.SSLCrtPath, u.SSLKeyPath, mux)
}
}
// ScrapeHandler allows prometheus to scrape a single source, instead of all sources.
func (u *promUnifi) ScrapeHandler(w http.ResponseWriter, r *http.Request) {
t := &target{u: u, Filter: &poller.Filter{
Name: r.URL.Query().Get("input"), // "unifi"
Path: r.URL.Query().Get("target"), // url: "https://127.0.0.1:8443"
}}
if t.Name == "" {
t.Name = "unifi" // the default
}
if pathOld := r.URL.Query().Get("path"); pathOld != "" {
u.LogErrorf("deprecated 'path' parameter used; update your config to use 'target'")
if t.Path == "" {
t.Path = pathOld
}
}
if roleOld := r.URL.Query().Get("role"); roleOld != "" {
u.LogErrorf("deprecated 'role' parameter used; update your config to use 'target'")
if t.Path == "" {
t.Path = roleOld
}
}
if t.Path == "" {
u.LogErrorf("'target' parameter missing on scrape from %v", r.RemoteAddr)
http.Error(w, "'target' parameter must be specified: configured OR unconfigured url", 400)
return
}
registry := prometheus.NewRegistry()
registry.MustRegister(t)
promhttp.HandlerFor(
registry, promhttp.HandlerOpts{ErrorHandling: promhttp.ContinueOnError},
).ServeHTTP(w, r)
}
func (u *promUnifi) DefaultHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(poller.AppName + "\n"))
}
// Describe satisfies the prometheus Collector. This returns all of the
// metric descriptions that this packages produces.
func (t *target) Describe(ch chan<- *prometheus.Desc) {
t.u.Describe(ch)
}
// Describe satisfies the prometheus Collector. This returns all of the
// metric descriptions that this packages produces.
func (u *promUnifi) Describe(ch chan<- *prometheus.Desc) {
for _, f := range []interface{}{u.Client, u.Device, u.UAP, u.USG, u.USW, u.Site} {
v := reflect.Indirect(reflect.ValueOf(f))
// Loop each struct member and send it to the provided channel.
for i := 0; i < v.NumField(); i++ {
desc, ok := v.Field(i).Interface().(*prometheus.Desc)
if ok && desc != nil {
ch <- desc
}
}
}
}
// Collect satisfies the prometheus Collector. This runs for a single controller poll.
func (t *target) Collect(ch chan<- prometheus.Metric) {
t.u.collect(ch, t.Filter)
}
// Collect satisfies the prometheus Collector. This runs the input method to get
// the current metrics (from another package) then exports them for prometheus.
func (u *promUnifi) Collect(ch chan<- prometheus.Metric) {
u.collect(ch, nil)
}
func (u *promUnifi) collect(ch chan<- prometheus.Metric, filter *poller.Filter) {
var err error
r := &Report{
Config: u.Config,
ch: make(chan []*metric, u.Config.Buffer),
Start: time.Now(),
}
defer r.close()
r.Metrics, err = u.Collector.Metrics(filter)
r.Fetch = time.Since(r.Start)
if err != nil {
r.error(ch, prometheus.NewInvalidDesc(err), ErrMetricFetchFailed)
u.LogErrorf("metric fetch failed: %v", err)
return
}
// Pass Report interface into our collecting and reporting methods.
go u.exportMetrics(r, ch, r.ch)
u.loopExports(r)
}
// This is closely tied to the method above with a sync.WaitGroup.
// This method runs in a go routine and exits when the channel closes.
// This is where our channels connects to the prometheus channel.
func (u *promUnifi) exportMetrics(r report, ch chan<- prometheus.Metric, ourChan chan []*metric) {
descs := make(map[*prometheus.Desc]bool) // used as a counter
defer r.report(u, descs)
for newMetrics := range ourChan {
for _, m := range newMetrics {
descs[m.Desc] = true
switch v := m.Value.(type) {
case unifi.FlexInt:
ch <- r.export(m, v.Val)
case float64:
ch <- r.export(m, v)
case int64:
ch <- r.export(m, float64(v))
case int:
ch <- r.export(m, float64(v))
default:
r.error(ch, m.Desc, fmt.Sprintf("not a number: %v", m.Value))
}
}
r.done()
}
}
func (u *promUnifi) loopExports(r report) {
m := r.metrics()
for _, s := range m.RogueAPs {
u.switchExport(r, s)
}
for _, s := range m.Sites {
u.switchExport(r, s)
}
for _, s := range m.SitesDPI {
u.exportSiteDPI(r, s)
}
for _, c := range m.Clients {
u.switchExport(r, c)
}
for _, d := range m.Devices {
u.switchExport(r, d)
}
appTotal := make(totalsDPImap)
catTotal := make(totalsDPImap)
for _, c := range m.ClientsDPI {
u.exportClientDPI(r, c, appTotal, catTotal)
}
u.exportClientDPItotals(r, appTotal, catTotal)
}
func (u *promUnifi) switchExport(r report, v interface{}) {
switch v := v.(type) {
case *unifi.RogueAP:
// r.addRogueAP()
u.exportRogueAP(r, v)
case *unifi.UAP:
r.addUAP()
u.exportUAP(r, v)
case *unifi.USW:
r.addUSW()
u.exportUSW(r, v)
case *unifi.USG:
r.addUSG()
u.exportUSG(r, v)
case *unifi.UXG:
r.addUXG()
u.exportUXG(r, v)
case *unifi.UDM:
r.addUDM()
u.exportUDM(r, v)
case *unifi.Site:
u.exportSite(r, v)
case *unifi.Client:
u.exportClient(r, v)
default:
u.LogErrorf("invalid type: %T", v)
}
}

View File

@ -0,0 +1,15 @@
module github.com/unpoller/promunifi
go 1.16
require (
github.com/go-kit/kit v0.10.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/prometheus/client_golang v1.11.0
github.com/prometheus/common v0.29.0
github.com/unpoller/poller v0.0.0-20210623101401-f12841d79a28
github.com/unpoller/unifi v0.0.9-0.20210623100314-3dccfdbc4c80
github.com/unpoller/webserver v0.0.0-20210623101543-90d89bb0acdf
golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect
golift.io/version v0.0.2
)

View File

@ -0,0 +1,734 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.10.0 h1:/o0BDeWzLWXNZ+4q5gXltUvaMpJqckTa+jTNoB+z4cg=
github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU=
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
github.com/prometheus/common v0.20.0 h1:pfeDeUdQcIxOMutNjCejsEFp7qeP+/iltHSSmLpE+hU=
github.com/prometheus/common v0.20.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.29.0 h1:3jqPBvKT4OHAbje2Ql7KeaaSicDBCxMYwEJU1zRJceE=
github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.1.1/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.6-0.20201009195203-85dd5c8bc61c h1:zqmyTlQyufRC65JnImJ6H1Sf7BDj8bG31EV919NVEQc=
github.com/spf13/pflag v1.0.6-0.20201009195203-85dd5c8bc61c/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/unifi-poller/poller v0.0.0-20210315011940-c43dc3c221b4/go.mod h1:fObadG7weiVnSpFu8pFpGfo2bYYFc7hUMe770FovSc8=
github.com/unifi-poller/poller v0.0.0-20210315075554-47d92433b172 h1:90zdY4RZ5ocdZuoYG4EceiMOvHP5Rfg1qmi/5LurEe8=
github.com/unifi-poller/poller v0.0.0-20210315075554-47d92433b172/go.mod h1:5DN7IF0zmDcN7LqyCMcFfn1dAWxPc1d0z5QDyWYOaqs=
github.com/unifi-poller/unifi v0.0.8 h1:FsIbkjESCeo30y8bxGxKgpyZ29HP77mSLpifK2vq/aI=
github.com/unifi-poller/unifi v0.0.8/go.mod h1:yRbmgFFG/RIMWVMgvkrzHAopW2adI4XctanAjxFdgRU=
github.com/unifi-poller/webserver v0.0.0-20210325090816-b4db5feaea83 h1:hkjrygMwANXCUdRBCwQ0Z7Sew4G4d30tjYuAYVmhdKc=
github.com/unifi-poller/webserver v0.0.0-20210325090816-b4db5feaea83/go.mod h1:elxTpcA1Zml/3BNjNAF2pmUM8QeE4Yn08uqraPnsTQw=
github.com/unpoller/poller v0.0.0-20210623101401-f12841d79a28 h1:YAv5naMdpOFahnxteFFRidZlrSEwLv8V2nBKJKmLmHg=
github.com/unpoller/poller v0.0.0-20210623101401-f12841d79a28/go.mod h1:AbDp60t5WlLSRELAliMJ0RFQpm/0yXpyolVSZqNtero=
github.com/unpoller/unifi v0.0.9-0.20210623100314-3dccfdbc4c80 h1:XjHGfJhMwnB63DYHgtWGJgDxLhxVcAOtf+cfuvpGoyo=
github.com/unpoller/unifi v0.0.9-0.20210623100314-3dccfdbc4c80/go.mod h1:K9QFFGfZws4gzB+Popix19S/rBKqrtqI+tyPORyg3F0=
github.com/unpoller/webserver v0.0.0-20210623101543-90d89bb0acdf h1:HhXi3qca3kyFEFPh0mqdr0bpQs94hJvMbUJztwPtf2A=
github.com/unpoller/webserver v0.0.0-20210623101543-90d89bb0acdf/go.mod h1:77PywuUvspdtoRuH1htFhR3Tp0pLyWj6kJlYR4tBYho=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc h1:+q90ECDSAQirdykUN6sPEiBXBsp8Csjcca8Oy7bgLTA=
golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d h1:BgJvlyh+UqCUaPlscHJ+PN8GcpfrFdr7NHjd1JL0+Gs=
golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210415045647-66c3f260301c h1:6L+uOeS3OQt/f4eFHXZcTxeZrGCuz+CLElgEBjbcTA4=
golang.org/x/sys v0.0.0-20210415045647-66c3f260301c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210317153231-de623e64d2a6/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72 h1:VqE9gduFZ4dbR7XoL77lHFp0/DyDUBKSXK7CMFkVcV0=
golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golift.io/cnfg v0.0.5/go.mod h1:ScFDIJg/rJGHbRaed/i7g1lBhywEjB0JiP2uZr3xC3A=
golift.io/cnfg v0.0.7 h1:qkNpP5Bq+5Gtoc6HcI8kapMD5zFOVan6qguxqBQF3OY=
golift.io/cnfg v0.0.7/go.mod h1:AsB0DJe7nv0bizKaoy3e3MjjOF7upTpMOMvsfv4CNNk=
golift.io/version v0.0.2 h1:i0gXRuSDHKs4O0sVDUg4+vNIuOxYoXhaxspftu2FRTE=
golift.io/version v0.0.2/go.mod h1:76aHNz8/Pm7CbuxIsDi97jABL5Zui3f2uZxDm4vB6hU=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=

View File

@ -0,0 +1,38 @@
package promunifi
import (
"fmt"
"time"
"github.com/unpoller/webserver"
)
// Logf logs a message.
func (u *promUnifi) Logf(msg string, v ...interface{}) {
webserver.NewOutputEvent(PluginName, PluginName, &webserver.Event{
Ts: time.Now(),
Msg: fmt.Sprintf(msg, v...),
Tags: map[string]string{"type": "info"},
})
u.Collector.Logf(msg, v...)
}
// LogErrorf logs an error message.
func (u *promUnifi) LogErrorf(msg string, v ...interface{}) {
webserver.NewOutputEvent(PluginName, PluginName, &webserver.Event{
Ts: time.Now(),
Msg: fmt.Sprintf(msg, v...),
Tags: map[string]string{"type": "error"},
})
u.Collector.LogErrorf(msg, v...)
}
// LogDebugf logs a debug message.
func (u *promUnifi) LogDebugf(msg string, v ...interface{}) {
webserver.NewOutputEvent(PluginName, PluginName, &webserver.Event{
Ts: time.Now(),
Msg: fmt.Sprintf(msg, v...),
Tags: map[string]string{"type": "debug"},
})
u.Collector.LogDebugf(msg, v...)
}

View File

@ -0,0 +1,100 @@
package promunifi
import (
"fmt"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/unpoller/poller"
)
// This file contains the report interface.
// This interface can be mocked and overridden for tests.
// report is an internal interface used to "process metrics".
type report interface {
done()
send([]*metric)
metrics() *poller.Metrics
report(c poller.Logger, descs map[*prometheus.Desc]bool)
export(m *metric, v float64) prometheus.Metric
error(ch chan<- prometheus.Metric, d *prometheus.Desc, v interface{})
addUDM()
addUXG()
addUSG()
addUAP()
addUSW()
}
// Satisfy gomnd.
const oneDecimalPoint = 10.0
func (r *Report) done() {
r.wg.Done()
}
func (r *Report) send(m []*metric) {
r.wg.Add(1) // notlint: gomnd
r.ch <- m
}
func (r *Report) metrics() *poller.Metrics {
return r.Metrics
}
func (r *Report) report(c poller.Logger, descs map[*prometheus.Desc]bool) {
m := r.Metrics
c.Logf("UniFi Measurements Exported. Site: %d, Client: %d, "+
"UAP: %d, USG/UDM: %d, USW: %d, DPI Site/Client: %d/%d, Desc: %d, "+
"Metric: %d, Err: %d, 0s: %d, Req/Total: %v / %v",
len(m.Sites), len(m.Clients), r.UAP, r.UDM+r.USG+r.UXG, r.USW, len(m.SitesDPI),
len(m.ClientsDPI), len(descs), r.Total, r.Errors, r.Zeros,
r.Fetch.Round(time.Millisecond/oneDecimalPoint),
r.Elapsed.Round(time.Millisecond/oneDecimalPoint))
}
func (r *Report) export(m *metric, v float64) prometheus.Metric {
r.Total++
if v == 0 {
r.Zeros++
}
return prometheus.MustNewConstMetric(m.Desc, m.ValueType, v, m.Labels...)
}
func (r *Report) error(ch chan<- prometheus.Metric, d *prometheus.Desc, v interface{}) {
r.Errors++
if r.ReportErrors {
ch <- prometheus.NewInvalidMetric(d, fmt.Errorf("error: %v", v)) // nolint: goerr113
}
}
func (r *Report) addUSW() {
r.USW++
}
func (r *Report) addUAP() {
r.UAP++
}
func (r *Report) addUSG() {
r.USG++
}
func (r *Report) addUDM() {
r.UDM++
}
func (r *Report) addUXG() {
r.UXG++
}
// close is not part of the interface.
func (r *Report) close() {
r.wg.Wait()
r.Elapsed = time.Since(r.Start)
close(r.ch)
}

View File

@ -0,0 +1,158 @@
package promunifi
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/unpoller/unifi"
)
type site struct {
NumUser *prometheus.Desc
NumGuest *prometheus.Desc
NumIot *prometheus.Desc
TxBytesR *prometheus.Desc
RxBytesR *prometheus.Desc
NumAp *prometheus.Desc
NumAdopted *prometheus.Desc
NumDisabled *prometheus.Desc
NumDisconnected *prometheus.Desc
NumPending *prometheus.Desc
NumGw *prometheus.Desc
NumSw *prometheus.Desc
NumSta *prometheus.Desc
Latency *prometheus.Desc
Drops *prometheus.Desc
Uptime *prometheus.Desc
XputUp *prometheus.Desc
XputDown *prometheus.Desc
SpeedtestPing *prometheus.Desc
RemoteUserNumActive *prometheus.Desc
RemoteUserNumInactive *prometheus.Desc
RemoteUserRxBytes *prometheus.Desc
RemoteUserTxBytes *prometheus.Desc
RemoteUserRxPackets *prometheus.Desc
RemoteUserTxPackets *prometheus.Desc
DPITxPackets *prometheus.Desc
DPIRxPackets *prometheus.Desc
DPITxBytes *prometheus.Desc
DPIRxBytes *prometheus.Desc
}
func descSite(ns string) *site {
labels := []string{"subsystem", "status", "site_name", "source"}
labelDPI := []string{"category", "application", "site_name", "source"}
nd := prometheus.NewDesc
return &site{
NumUser: nd(ns+"users", "Number of Users", labels, nil),
NumGuest: nd(ns+"guests", "Number of Guests", labels, nil),
NumIot: nd(ns+"iots", "Number of IoT Devices", labels, nil),
TxBytesR: nd(ns+"transmit_rate_bytes", "Bytes Transmit Rate", labels, nil),
RxBytesR: nd(ns+"receive_rate_bytes", "Bytes Receive Rate", labels, nil),
NumAp: nd(ns+"aps", "Access Point Count", labels, nil),
NumAdopted: nd(ns+"adopted", "Adoption Count", labels, nil),
NumDisabled: nd(ns+"disabled", "Disabled Count", labels, nil),
NumDisconnected: nd(ns+"disconnected", "Disconnected Count", labels, nil),
NumPending: nd(ns+"pending", "Pending Count", labels, nil),
NumGw: nd(ns+"gateways", "Gateway Count", labels, nil),
NumSw: nd(ns+"switches", "Switch Count", labels, nil),
NumSta: nd(ns+"stations", "Station Count", labels, nil),
Latency: nd(ns+"latency_seconds", "Latency", labels, nil),
Uptime: nd(ns+"uptime_seconds", "Uptime", labels, nil),
Drops: nd(ns+"intenet_drops_total", "Internet (WAN) Disconnections", labels, nil),
XputUp: nd(ns+"xput_up_rate", "Speedtest Upload", labels, nil),
XputDown: nd(ns+"xput_down_rate", "Speedtest Download", labels, nil),
SpeedtestPing: nd(ns+"speedtest_ping", "Speedtest Ping", labels, nil),
RemoteUserNumActive: nd(ns+"remote_user_active", "Remote Users Active", labels, nil),
RemoteUserNumInactive: nd(ns+"remote_user_inactive", "Remote Users Inactive", labels, nil),
RemoteUserRxBytes: nd(ns+"remote_user_receive_bytes_total", "Remote Users Receive Bytes", labels, nil),
RemoteUserTxBytes: nd(ns+"remote_user_transmit_bytes_total", "Remote Users Transmit Bytes", labels, nil),
RemoteUserRxPackets: nd(ns+"remote_user_receive_packets_total", "Remote Users Receive Packets", labels, nil),
RemoteUserTxPackets: nd(ns+"remote_user_transmit_packets_total", "Remote Users Transmit Packets", labels, nil),
DPITxPackets: nd(ns+"dpi_transmit_packets", "Site DPI Transmit Packets", labelDPI, nil),
DPIRxPackets: nd(ns+"dpi_receive_packets", "Site DPI Receive Packets", labelDPI, nil),
DPITxBytes: nd(ns+"dpi_transmit_bytes", "Site DPI Transmit Bytes", labelDPI, nil),
DPIRxBytes: nd(ns+"dpi_receive_bytes", "Site DPI Receive Bytes", labelDPI, nil),
}
}
func (u *promUnifi) exportSiteDPI(r report, v interface{}) {
s, ok := v.(*unifi.DPITable)
if !ok {
u.LogErrorf("invalid type given to SiteDPI: %T", v)
return
}
for _, dpi := range s.ByApp {
labelDPI := []string{unifi.DPICats.Get(dpi.Cat), unifi.DPIApps.GetApp(dpi.Cat, dpi.App), s.SiteName, s.SourceName}
// log.Println(labelsDPI, dpi.Cat, dpi.App, dpi.TxBytes, dpi.RxBytes, dpi.TxPackets, dpi.RxPackets)
r.send([]*metric{
{u.Site.DPITxPackets, gauge, dpi.TxPackets, labelDPI},
{u.Site.DPIRxPackets, gauge, dpi.RxPackets, labelDPI},
{u.Site.DPITxBytes, gauge, dpi.TxBytes, labelDPI},
{u.Site.DPIRxBytes, gauge, dpi.RxBytes, labelDPI},
})
}
}
func (u *promUnifi) exportSite(r report, s *unifi.Site) {
for _, h := range s.Health {
switch labels := []string{h.Subsystem, h.Status, s.SiteName, s.SourceName}; labels[0] {
case "www":
r.send([]*metric{
{u.Site.TxBytesR, gauge, h.TxBytesR, labels},
{u.Site.RxBytesR, gauge, h.RxBytesR, labels},
{u.Site.Uptime, gauge, h.Uptime, labels},
{u.Site.Latency, gauge, h.Latency.Val / 1000, labels},
{u.Site.XputUp, gauge, h.XputUp, labels},
{u.Site.XputDown, gauge, h.XputDown, labels},
{u.Site.SpeedtestPing, gauge, h.SpeedtestPing, labels},
{u.Site.Drops, counter, h.Drops, labels},
})
case "wlan":
r.send([]*metric{
{u.Site.TxBytesR, gauge, h.TxBytesR, labels},
{u.Site.RxBytesR, gauge, h.RxBytesR, labels},
{u.Site.NumAdopted, gauge, h.NumAdopted, labels},
{u.Site.NumDisconnected, gauge, h.NumDisconnected, labels},
{u.Site.NumPending, gauge, h.NumPending, labels},
{u.Site.NumUser, gauge, h.NumUser, labels},
{u.Site.NumGuest, gauge, h.NumGuest, labels},
{u.Site.NumIot, gauge, h.NumIot, labels},
{u.Site.NumAp, gauge, h.NumAp, labels},
{u.Site.NumDisabled, gauge, h.NumDisabled, labels},
})
case "wan":
r.send([]*metric{
{u.Site.TxBytesR, gauge, h.TxBytesR, labels},
{u.Site.RxBytesR, gauge, h.RxBytesR, labels},
{u.Site.NumAdopted, gauge, h.NumAdopted, labels},
{u.Site.NumDisconnected, gauge, h.NumDisconnected, labels},
{u.Site.NumPending, gauge, h.NumPending, labels},
{u.Site.NumGw, gauge, h.NumGw, labels},
{u.Site.NumSta, gauge, h.NumSta, labels},
})
case "lan":
r.send([]*metric{
{u.Site.TxBytesR, gauge, h.TxBytesR, labels},
{u.Site.RxBytesR, gauge, h.RxBytesR, labels},
{u.Site.NumAdopted, gauge, h.NumAdopted, labels},
{u.Site.NumDisconnected, gauge, h.NumDisconnected, labels},
{u.Site.NumPending, gauge, h.NumPending, labels},
{u.Site.NumUser, gauge, h.NumUser, labels},
{u.Site.NumGuest, gauge, h.NumGuest, labels},
{u.Site.NumIot, gauge, h.NumIot, labels},
{u.Site.NumSw, gauge, h.NumSw, labels},
})
case "vpn":
r.send([]*metric{
{u.Site.RemoteUserNumActive, gauge, h.RemoteUserNumActive, labels},
{u.Site.RemoteUserNumInactive, gauge, h.RemoteUserNumInactive, labels},
{u.Site.RemoteUserRxBytes, counter, h.RemoteUserRxBytes, labels},
{u.Site.RemoteUserTxBytes, counter, h.RemoteUserTxBytes, labels},
{u.Site.RemoteUserRxPackets, counter, h.RemoteUserRxPackets, labels},
{u.Site.RemoteUserTxPackets, counter, h.RemoteUserTxPackets, labels},
})
}
}
}

View File

@ -0,0 +1,376 @@
package promunifi
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/unpoller/unifi"
)
type uap struct {
// Ap Traffic Stats
ApWifiTxDropped *prometheus.Desc
ApRxErrors *prometheus.Desc
ApRxDropped *prometheus.Desc
ApRxFrags *prometheus.Desc
ApRxCrypts *prometheus.Desc
ApTxPackets *prometheus.Desc
ApTxBytes *prometheus.Desc
ApTxErrors *prometheus.Desc
ApTxDropped *prometheus.Desc
ApTxRetries *prometheus.Desc
ApRxPackets *prometheus.Desc
ApRxBytes *prometheus.Desc
WifiTxAttempts *prometheus.Desc
MacFilterRejections *prometheus.Desc
// VAP Stats
VAPCcq *prometheus.Desc
VAPMacFilterRejections *prometheus.Desc
VAPNumSatisfactionSta *prometheus.Desc
VAPAvgClientSignal *prometheus.Desc
VAPSatisfaction *prometheus.Desc
VAPSatisfactionNow *prometheus.Desc
VAPDNSAvgLatency *prometheus.Desc
VAPRxBytes *prometheus.Desc
VAPRxCrypts *prometheus.Desc
VAPRxDropped *prometheus.Desc
VAPRxErrors *prometheus.Desc
VAPRxFrags *prometheus.Desc
VAPRxNwids *prometheus.Desc
VAPRxPackets *prometheus.Desc
VAPTxBytes *prometheus.Desc
VAPTxDropped *prometheus.Desc
VAPTxErrors *prometheus.Desc
VAPTxPackets *prometheus.Desc
VAPTxPower *prometheus.Desc
VAPTxRetries *prometheus.Desc
VAPTxCombinedRetries *prometheus.Desc
VAPTxDataMpduBytes *prometheus.Desc
VAPTxRtsRetries *prometheus.Desc
VAPTxSuccess *prometheus.Desc
VAPTxTotal *prometheus.Desc
VAPTxGoodbytes *prometheus.Desc
VAPTxLatAvg *prometheus.Desc
VAPTxLatMax *prometheus.Desc
VAPTxLatMin *prometheus.Desc
VAPRxGoodbytes *prometheus.Desc
VAPRxLatAvg *prometheus.Desc
VAPRxLatMax *prometheus.Desc
VAPRxLatMin *prometheus.Desc
VAPWifiTxLatencyMovAvg *prometheus.Desc
VAPWifiTxLatencyMovMax *prometheus.Desc
VAPWifiTxLatencyMovMin *prometheus.Desc
VAPWifiTxLatencyMovTotal *prometheus.Desc
VAPWifiTxLatencyMovCount *prometheus.Desc
// Radio Stats
RadioCurrentAntennaGain *prometheus.Desc
RadioHt *prometheus.Desc
RadioMaxTxpower *prometheus.Desc
RadioMinTxpower *prometheus.Desc
RadioNss *prometheus.Desc
RadioRadioCaps *prometheus.Desc
RadioTxPower *prometheus.Desc
RadioAstBeXmit *prometheus.Desc
RadioChannel *prometheus.Desc
RadioCuSelfRx *prometheus.Desc
RadioCuSelfTx *prometheus.Desc
RadioCuTotal *prometheus.Desc
RadioExtchannel *prometheus.Desc
RadioGain *prometheus.Desc
RadioNumSta *prometheus.Desc
RadioTxPackets *prometheus.Desc
RadioTxRetries *prometheus.Desc
}
type rogueap struct {
Age *prometheus.Desc
BW *prometheus.Desc
CenterFreq *prometheus.Desc
Channel *prometheus.Desc
Freq *prometheus.Desc
Noise *prometheus.Desc
RSSI *prometheus.Desc
RSSIAge *prometheus.Desc
Signal *prometheus.Desc
}
func descRogueAP(ns string) *rogueap {
label := []string{
"security", "oui", "band", "mac", "ap_mac", "radio", "radio_name", "site_name", "name", "source",
}
return &rogueap{
Age: prometheus.NewDesc(ns+"age", "RogueAP Age", label, nil),
BW: prometheus.NewDesc(ns+"bw", "RogueAP BW", label, nil),
CenterFreq: prometheus.NewDesc(ns+"center_freq", "RogueAP Center Frequency", label, nil),
Channel: prometheus.NewDesc(ns+"channel", "RogueAP Channel", label, nil),
Freq: prometheus.NewDesc(ns+"frequency", "RogueAP Frequency", label, nil),
Noise: prometheus.NewDesc(ns+"noise", "RogueAP Noise", label, nil),
RSSI: prometheus.NewDesc(ns+"rssi", "RogueAP RSSI", label, nil),
RSSIAge: prometheus.NewDesc(ns+"rssi_age", "RogueAP RSSI Age", label, nil),
Signal: prometheus.NewDesc(ns+"signal", "RogueAP Signal", label, nil),
}
}
func descUAP(ns string) *uap { // nolint: funlen
labelA := []string{"stat", "site_name", "name", "source"} // stat + labels[1:]
labelV := []string{"vap_name", "bssid", "radio", "radio_name", "essid", "usage", "site_name", "name", "source"}
labelR := []string{"radio_name", "radio", "site_name", "name", "source"}
nd := prometheus.NewDesc
return &uap{
// 3x each - stat table: total, guest, user
ApWifiTxDropped: nd(ns+"stat_wifi_transmt_dropped_total", "Wifi Transmissions Dropped", labelA, nil),
ApRxErrors: nd(ns+"stat_receive_errors_total", "Receive Errors", labelA, nil),
ApRxDropped: nd(ns+"stat_receive_dropped_total", "Receive Dropped", labelA, nil),
ApRxFrags: nd(ns+"stat_receive_frags_total", "Received Frags", labelA, nil),
ApRxCrypts: nd(ns+"stat_receive_crypts_total", "Receive Crypts", labelA, nil),
ApTxPackets: nd(ns+"stat_transmit_packets_total", "Transmit Packets", labelA, nil),
ApTxBytes: nd(ns+"stat_transmit_bytes_total", "Transmit Bytes", labelA, nil),
ApTxErrors: nd(ns+"stat_transmit_errors_total", "Transmit Errors", labelA, nil),
ApTxDropped: nd(ns+"stat_transmit_dropped_total", "Transmit Dropped", labelA, nil),
ApTxRetries: nd(ns+"stat_retries_tx_total", "Transmit Retries", labelA, nil),
ApRxPackets: nd(ns+"stat_receive_packets_total", "Receive Packets", labelA, nil),
ApRxBytes: nd(ns+"stat_receive_bytes_total", "Receive Bytes", labelA, nil),
WifiTxAttempts: nd(ns+"stat_wifi_transmit_attempts_total", "Wifi Transmission Attempts", labelA, nil),
MacFilterRejections: nd(ns+"stat_mac_filter_rejects_total", "MAC Filter Rejections", labelA, nil),
// N each - 1 per Virtual AP (VAP)
VAPCcq: nd(ns+"vap_ccq_ratio", "VAP Client Connection Quality", labelV, nil),
VAPMacFilterRejections: nd(ns+"vap_mac_filter_rejects_total", "VAP MAC Filter Rejections", labelV, nil),
VAPNumSatisfactionSta: nd(ns+"vap_satisfaction_stations", "VAP Number Satisifaction Stations", labelV, nil),
VAPAvgClientSignal: nd(ns+"vap_average_client_signal", "VAP Average Client Signal", labelV, nil),
VAPSatisfaction: nd(ns+"vap_satisfaction_ratio", "VAP Satisfaction", labelV, nil),
VAPSatisfactionNow: nd(ns+"vap_satisfaction_now_ratio", "VAP Satisfaction Now", labelV, nil),
VAPDNSAvgLatency: nd(ns+"vap_dns_latency_average_seconds", "VAP DNS Latency Average", labelV, nil),
VAPRxBytes: nd(ns+"vap_receive_bytes_total", "VAP Bytes Received", labelV, nil),
VAPRxCrypts: nd(ns+"vap_receive_crypts_total", "VAP Crypts Received", labelV, nil),
VAPRxDropped: nd(ns+"vap_receive_dropped_total", "VAP Dropped Received", labelV, nil),
VAPRxErrors: nd(ns+"vap_receive_errors_total", "VAP Errors Received", labelV, nil),
VAPRxFrags: nd(ns+"vap_receive_frags_total", "VAP Frags Received", labelV, nil),
VAPRxNwids: nd(ns+"vap_receive_nwids_total", "VAP Nwids Received", labelV, nil),
VAPRxPackets: nd(ns+"vap_receive_packets_total", "VAP Packets Received", labelV, nil),
VAPTxBytes: nd(ns+"vap_transmit_bytes_total", "VAP Bytes Transmitted", labelV, nil),
VAPTxDropped: nd(ns+"vap_transmit_dropped_total", "VAP Dropped Transmitted", labelV, nil),
VAPTxErrors: nd(ns+"vap_transmit_errors_total", "VAP Errors Transmitted", labelV, nil),
VAPTxPackets: nd(ns+"vap_transmit_packets_total", "VAP Packets Transmitted", labelV, nil),
VAPTxPower: nd(ns+"vap_transmit_power", "VAP Transmit Power", labelV, nil),
VAPTxRetries: nd(ns+"vap_transmit_retries_total", "VAP Retries Transmitted", labelV, nil),
VAPTxCombinedRetries: nd(ns+"vap_transmit_retries_combined_total", "VAP Retries Combined Tx", labelV, nil),
VAPTxDataMpduBytes: nd(ns+"vap_data_mpdu_transmit_bytes_total", "VAP Data MPDU Bytes Tx", labelV, nil),
VAPTxRtsRetries: nd(ns+"vap_transmit_rts_retries_total", "VAP RTS Retries Transmitted", labelV, nil),
VAPTxSuccess: nd(ns+"vap_transmit_success_total", "VAP Success Transmits", labelV, nil),
VAPTxTotal: nd(ns+"vap_transmit_total", "VAP Transmit Total", labelV, nil),
VAPTxGoodbytes: nd(ns+"vap_transmit_goodbyes", "VAP Goodbyes Transmitted", labelV, nil),
VAPTxLatAvg: nd(ns+"vap_transmit_latency_average_seconds", "VAP Latency Average Tx", labelV, nil),
VAPTxLatMax: nd(ns+"vap_transmit_latency_maximum_seconds", "VAP Latency Maximum Tx", labelV, nil),
VAPTxLatMin: nd(ns+"vap_transmit_latency_minimum_seconds", "VAP Latency Minimum Tx", labelV, nil),
VAPRxGoodbytes: nd(ns+"vap_receive_goodbyes", "VAP Goodbyes Received", labelV, nil),
VAPRxLatAvg: nd(ns+"vap_receive_latency_average_seconds", "VAP Latency Average Rx", labelV, nil),
VAPRxLatMax: nd(ns+"vap_receive_latency_maximum_seconds", "VAP Latency Maximum Rx", labelV, nil),
VAPRxLatMin: nd(ns+"vap_receive_latency_minimum_seconds", "VAP Latency Minimum Rx", labelV, nil),
VAPWifiTxLatencyMovAvg: nd(ns+"vap_transmit_latency_moving_avg_seconds", "VAP Latency Moving Avg Tx", labelV, nil),
VAPWifiTxLatencyMovMax: nd(ns+"vap_transmit_latency_moving_max_seconds", "VAP Latency Moving Min Tx", labelV, nil),
VAPWifiTxLatencyMovMin: nd(ns+"vap_transmit_latency_moving_min_seconds", "VAP Latency Moving Max Tx", labelV, nil),
VAPWifiTxLatencyMovTotal: nd(ns+"vap_transmit_latency_moving_total", "VAP Latency Moving Total Tramsit", labelV, nil),
VAPWifiTxLatencyMovCount: nd(ns+"vap_transmit_latency_moving_count", "VAP Latency Moving Count Tramsit", labelV, nil),
// N each - 1 per Radio. 1-4 radios per AP usually
RadioCurrentAntennaGain: nd(ns+"radio_current_antenna_gain", "Radio Current Antenna Gain", labelR, nil),
RadioHt: nd(ns+"radio_ht", "Radio HT", labelR, nil),
RadioMaxTxpower: nd(ns+"radio_max_transmit_power", "Radio Maximum Transmit Power", labelR, nil),
RadioMinTxpower: nd(ns+"radio_min_transmit_power", "Radio Minimum Transmit Power", labelR, nil),
RadioNss: nd(ns+"radio_nss", "Radio Nss", labelR, nil),
RadioRadioCaps: nd(ns+"radio_caps", "Radio Capabilities", labelR, nil),
RadioTxPower: nd(ns+"radio_transmit_power", "Radio Transmit Power", labelR, nil),
RadioAstBeXmit: nd(ns+"radio_ast_be_xmit", "Radio AstBe Transmit", labelR, nil),
RadioChannel: nd(ns+"radio_channel", "Radio Channel", labelR, nil),
RadioCuSelfRx: nd(ns+"radio_channel_utilization_receive_ratio", "Channel Utilization Rx", labelR, nil),
RadioCuSelfTx: nd(ns+"radio_channel_utilization_transmit_ratio", "Channel Utilization Tx", labelR, nil),
RadioCuTotal: nd(ns+"radio_channel_utilization_total_ratio", "Channel Utilization Total", labelR, nil),
RadioExtchannel: nd(ns+"radio_ext_channel", "Radio Ext Channel", labelR, nil),
RadioGain: nd(ns+"radio_gain", "Radio Gain", labelR, nil),
RadioNumSta: nd(ns+"radio_stations", "Radio Total Station Count", append(labelR, "station_type"), nil),
RadioTxPackets: nd(ns+"radio_transmit_packets", "Radio Transmitted Packets", labelR, nil),
RadioTxRetries: nd(ns+"radio_transmit_retries", "Radio Transmit Retries", labelR, nil),
}
}
func (u *promUnifi) exportRogueAP(r report, d *unifi.RogueAP) {
if d.Age.Val == 0 {
return // only keep things that are recent.
}
labels := []string{
d.Security, d.Oui, d.Band, d.Bssid, d.ApMac, d.Radio, d.RadioName, d.SiteName, d.Essid, d.SourceName,
}
r.send([]*metric{
{u.RogueAP.Age, gauge, d.Age.Val, labels},
{u.RogueAP.BW, gauge, d.Bw.Val, labels},
{u.RogueAP.CenterFreq, gauge, d.CenterFreq.Val, labels},
{u.RogueAP.Channel, gauge, d.Channel, labels},
{u.RogueAP.Freq, gauge, d.Freq.Val, labels},
{u.RogueAP.Noise, gauge, d.Noise.Val, labels},
{u.RogueAP.RSSI, gauge, d.Rssi.Val, labels},
{u.RogueAP.RSSIAge, gauge, d.RssiAge.Val, labels},
{u.RogueAP.Signal, gauge, d.Signal.Val, labels},
})
}
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, d.SourceName}
infoLabels := []string{d.Version, d.Model, d.Serial, d.Mac, d.IP, d.ID}
u.exportUAPstats(r, labels, d.Stat.Ap, d.BytesD, d.TxBytesD, d.RxBytesD, d.BytesR)
u.exportVAPtable(r, labels, d.VapTable)
u.exportPRTtable(r, labels, d.PortTable)
u.exportBYTstats(r, labels, d.TxBytes, d.RxBytes)
u.exportSYSstats(r, labels, d.SysStats, d.SystemStats)
u.exportSTAcount(r, labels, d.UserNumSta, d.GuestNumSta)
u.exportRADtable(r, labels, d.RadioTable, d.RadioTableStats)
r.send([]*metric{
{u.Device.Info, gauge, 1.0, append(labels, infoLabels...)},
{u.Device.Uptime, gauge, d.Uptime, labels},
})
}
// 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 {
return
}
labelU := []string{"user", labels[1], labels[2], labels[3]}
labelG := []string{"guest", labels[1], labels[2], labels[3]}
r.send([]*metric{
// ap only stuff.
{u.Device.BytesD, counter, bytes[0], labels}, // not sure if these 3 Ds are counters or gauges.
{u.Device.TxBytesD, counter, bytes[1], labels}, // not sure if these 3 Ds are counters or gauges.
{u.Device.RxBytesD, counter, bytes[2], labels}, // not sure if these 3 Ds are counters or gauges.
{u.Device.BytesR, gauge, bytes[3], labels}, // only UAP has this one, and those ^ weird.
// user
{u.UAP.ApWifiTxDropped, counter, ap.UserWifiTxDropped, labelU},
{u.UAP.ApRxErrors, counter, ap.UserRxErrors, labelU},
{u.UAP.ApRxDropped, counter, ap.UserRxDropped, labelU},
{u.UAP.ApRxFrags, counter, ap.UserRxFrags, labelU},
{u.UAP.ApRxCrypts, counter, ap.UserRxCrypts, labelU},
{u.UAP.ApTxPackets, counter, ap.UserTxPackets, labelU},
{u.UAP.ApTxBytes, counter, ap.UserTxBytes, labelU},
{u.UAP.ApTxErrors, counter, ap.UserTxErrors, labelU},
{u.UAP.ApTxDropped, counter, ap.UserTxDropped, labelU},
{u.UAP.ApTxRetries, counter, ap.UserTxRetries, labelU},
{u.UAP.ApRxPackets, counter, ap.UserRxPackets, labelU},
{u.UAP.ApRxBytes, counter, ap.UserRxBytes, labelU},
{u.UAP.WifiTxAttempts, counter, ap.UserWifiTxAttempts, labelU},
{u.UAP.MacFilterRejections, counter, ap.UserMacFilterRejections, labelU},
// guest
{u.UAP.ApWifiTxDropped, counter, ap.GuestWifiTxDropped, labelG},
{u.UAP.ApRxErrors, counter, ap.GuestRxErrors, labelG},
{u.UAP.ApRxDropped, counter, ap.GuestRxDropped, labelG},
{u.UAP.ApRxFrags, counter, ap.GuestRxFrags, labelG},
{u.UAP.ApRxCrypts, counter, ap.GuestRxCrypts, labelG},
{u.UAP.ApTxPackets, counter, ap.GuestTxPackets, labelG},
{u.UAP.ApTxBytes, counter, ap.GuestTxBytes, labelG},
{u.UAP.ApTxErrors, counter, ap.GuestTxErrors, labelG},
{u.UAP.ApTxDropped, counter, ap.GuestTxDropped, labelG},
{u.UAP.ApTxRetries, counter, ap.GuestTxRetries, labelG},
{u.UAP.ApRxPackets, counter, ap.GuestRxPackets, labelG},
{u.UAP.ApRxBytes, counter, ap.GuestRxBytes, labelG},
{u.UAP.WifiTxAttempts, counter, ap.GuestWifiTxAttempts, labelG},
{u.UAP.MacFilterRejections, counter, ap.GuestMacFilterRejections, labelG},
})
}
// UAP VAP Table.
func (u *promUnifi) exportVAPtable(r report, labels []string, vt unifi.VapTable) {
// vap table stats
for _, v := range vt {
if !v.Up.Val {
continue
}
labelV := []string{v.Name, v.Bssid, v.Radio, v.RadioName, v.Essid, v.Usage, labels[1], labels[2], labels[3]}
r.send([]*metric{
{u.UAP.VAPCcq, gauge, float64(v.Ccq) / 1000.0, labelV},
{u.UAP.VAPMacFilterRejections, counter, v.MacFilterRejections, labelV},
{u.UAP.VAPNumSatisfactionSta, gauge, v.NumSatisfactionSta, labelV},
{u.UAP.VAPAvgClientSignal, gauge, v.AvgClientSignal.Val, labelV},
{u.UAP.VAPSatisfaction, gauge, v.Satisfaction.Val / 100.0, labelV},
{u.UAP.VAPSatisfactionNow, gauge, v.SatisfactionNow.Val / 100.0, labelV},
{u.UAP.VAPDNSAvgLatency, gauge, v.DNSAvgLatency.Val / 1000, labelV},
{u.UAP.VAPRxBytes, counter, v.RxBytes, labelV},
{u.UAP.VAPRxCrypts, counter, v.RxCrypts, labelV},
{u.UAP.VAPRxDropped, counter, v.RxDropped, labelV},
{u.UAP.VAPRxErrors, counter, v.RxErrors, labelV},
{u.UAP.VAPRxFrags, counter, v.RxFrags, labelV},
{u.UAP.VAPRxNwids, counter, v.RxNwids, labelV},
{u.UAP.VAPRxPackets, counter, v.RxPackets, labelV},
{u.UAP.VAPTxBytes, counter, v.TxBytes, labelV},
{u.UAP.VAPTxDropped, counter, v.TxDropped, labelV},
{u.UAP.VAPTxErrors, counter, v.TxErrors, labelV},
{u.UAP.VAPTxPackets, counter, v.TxPackets, labelV},
{u.UAP.VAPTxPower, gauge, v.TxPower, labelV},
{u.UAP.VAPTxRetries, counter, v.TxRetries, labelV},
{u.UAP.VAPTxCombinedRetries, counter, v.TxCombinedRetries, labelV},
{u.UAP.VAPTxDataMpduBytes, counter, v.TxDataMpduBytes, labelV},
{u.UAP.VAPTxRtsRetries, counter, v.TxRtsRetries, labelV},
{u.UAP.VAPTxTotal, counter, v.TxTotal, labelV},
{u.UAP.VAPTxGoodbytes, counter, v.TxTCPStats.Goodbytes, labelV},
{u.UAP.VAPTxLatAvg, gauge, v.TxTCPStats.LatAvg.Val / 1000, labelV},
{u.UAP.VAPTxLatMax, gauge, v.TxTCPStats.LatMax.Val / 1000, labelV},
{u.UAP.VAPTxLatMin, gauge, v.TxTCPStats.LatMin.Val / 1000, labelV},
{u.UAP.VAPRxGoodbytes, counter, v.RxTCPStats.Goodbytes, labelV},
{u.UAP.VAPRxLatAvg, gauge, v.RxTCPStats.LatAvg.Val / 1000, labelV},
{u.UAP.VAPRxLatMax, gauge, v.RxTCPStats.LatMax.Val / 1000, labelV},
{u.UAP.VAPRxLatMin, gauge, v.RxTCPStats.LatMin.Val / 1000, labelV},
{u.UAP.VAPWifiTxLatencyMovAvg, gauge, v.WifiTxLatencyMov.Avg.Val / 1000, labelV},
{u.UAP.VAPWifiTxLatencyMovMax, gauge, v.WifiTxLatencyMov.Max.Val / 1000, labelV},
{u.UAP.VAPWifiTxLatencyMovMin, gauge, v.WifiTxLatencyMov.Min.Val / 1000, labelV},
{u.UAP.VAPWifiTxLatencyMovTotal, counter, v.WifiTxLatencyMov.Total, labelV}, // not sure if gauge or counter.
{u.UAP.VAPWifiTxLatencyMovCount, counter, v.WifiTxLatencyMov.TotalCount, labelV}, // not sure if gauge or counter.
})
}
}
// UAP Radio Table.
func (u *promUnifi) exportRADtable(r report, labels []string, rt unifi.RadioTable, rts unifi.RadioTableStats) {
// radio table
for _, p := range rt {
labelR := []string{p.Name, p.Radio, labels[1], labels[2], labels[3]}
labelRUser := append(labelR, "user")
labelRGuest := append(labelR, "guest")
r.send([]*metric{
{u.UAP.RadioCurrentAntennaGain, gauge, p.CurrentAntennaGain, labelR},
{u.UAP.RadioHt, gauge, p.Ht, labelR},
{u.UAP.RadioMaxTxpower, gauge, p.MaxTxpower, labelR},
{u.UAP.RadioMinTxpower, gauge, p.MinTxpower, labelR},
{u.UAP.RadioNss, gauge, p.Nss, labelR},
{u.UAP.RadioRadioCaps, gauge, p.RadioCaps, labelR},
})
// combine radio table with radio stats table.
for _, t := range rts {
if t.Name != p.Name {
continue
}
r.send([]*metric{
{u.UAP.RadioTxPower, gauge, t.TxPower, labelR},
{u.UAP.RadioAstBeXmit, gauge, t.AstBeXmit, labelR},
{u.UAP.RadioChannel, gauge, t.Channel, labelR},
{u.UAP.RadioCuSelfRx, gauge, t.CuSelfRx.Val / 100.0, labelR},
{u.UAP.RadioCuSelfTx, gauge, t.CuSelfTx.Val / 100.0, labelR},
{u.UAP.RadioCuTotal, gauge, t.CuTotal.Val / 100.0, labelR},
{u.UAP.RadioExtchannel, gauge, t.Extchannel, labelR},
{u.UAP.RadioGain, gauge, t.Gain, labelR},
{u.UAP.RadioNumSta, gauge, t.GuestNumSta, labelRGuest},
{u.UAP.RadioNumSta, gauge, t.UserNumSta, labelRUser},
{u.UAP.RadioTxPackets, gauge, t.TxPackets, labelR},
{u.UAP.RadioTxRetries, gauge, t.TxRetries, labelR},
})
break
}
}
}

View File

@ -0,0 +1,148 @@
package promunifi
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/unpoller/unifi"
)
// These are shared by all four device types: UDM, UAP, USG, USW.
type unifiDevice struct {
Info *prometheus.Desc
Uptime *prometheus.Desc
Temperature *prometheus.Desc
Storage *prometheus.Desc
TotalMaxPower *prometheus.Desc // sw only
FanLevel *prometheus.Desc // sw only
TotalTxBytes *prometheus.Desc
TotalRxBytes *prometheus.Desc
TotalBytes *prometheus.Desc
BytesR *prometheus.Desc // ap only
BytesD *prometheus.Desc // ap only
TxBytesD *prometheus.Desc // ap only
RxBytesD *prometheus.Desc // ap only
Counter *prometheus.Desc
Loadavg1 *prometheus.Desc
Loadavg5 *prometheus.Desc
Loadavg15 *prometheus.Desc
MemBuffer *prometheus.Desc
MemTotal *prometheus.Desc
MemUsed *prometheus.Desc
CPU *prometheus.Desc
Mem *prometheus.Desc
}
func descDevice(ns string) *unifiDevice {
labels := []string{"type", "site_name", "name", "source"}
infoLabels := []string{"version", "model", "serial", "mac", "ip", "id"}
return &unifiDevice{
Info: prometheus.NewDesc(ns+"info", "Device Information", append(labels, infoLabels...), nil),
Uptime: prometheus.NewDesc(ns+"uptime_seconds", "Device Uptime", labels, nil),
Temperature: prometheus.NewDesc(ns+"temperature_celsius", "Temperature",
append(labels, "temp_area", "temp_type"), nil),
Storage: prometheus.NewDesc(ns+"storage", "Storage",
append(labels, "mountpoint", "storage_name", "storage_reading"), nil),
TotalMaxPower: prometheus.NewDesc(ns+"max_power_total", "Total Max Power", labels, nil),
FanLevel: prometheus.NewDesc(ns+"fan_level", "Fan Level", labels, nil),
TotalTxBytes: prometheus.NewDesc(ns+"transmit_bytes_total", "Total Transmitted Bytes", labels, nil),
TotalRxBytes: prometheus.NewDesc(ns+"receive_bytes_total", "Total Received Bytes", labels, nil),
TotalBytes: prometheus.NewDesc(ns+"bytes_total", "Total Bytes Transferred", labels, nil),
BytesR: prometheus.NewDesc(ns+"rate_bytes", "Transfer Rate", labels, nil),
BytesD: prometheus.NewDesc(ns+"d_bytes", "Total Bytes D???", labels, nil),
TxBytesD: prometheus.NewDesc(ns+"d_tranmsit_bytes", "Transmit Bytes D???", labels, nil),
RxBytesD: prometheus.NewDesc(ns+"d_receive_bytes", "Receive Bytes D???", labels, nil),
Counter: prometheus.NewDesc(ns+"stations", "Number of Stations", append(labels, "station_type"), nil),
Loadavg1: prometheus.NewDesc(ns+"load_average_1", "System Load Average 1 Minute", labels, nil),
Loadavg5: prometheus.NewDesc(ns+"load_average_5", "System Load Average 5 Minutes", labels, nil),
Loadavg15: prometheus.NewDesc(ns+"load_average_15", "System Load Average 15 Minutes", labels, nil),
MemUsed: prometheus.NewDesc(ns+"memory_used_bytes", "System Memory Used", labels, nil),
MemTotal: prometheus.NewDesc(ns+"memory_installed_bytes", "System Installed Memory", labels, nil),
MemBuffer: prometheus.NewDesc(ns+"memory_buffer_bytes", "System Memory Buffer", labels, nil),
CPU: prometheus.NewDesc(ns+"cpu_utilization_ratio", "System CPU % Utilized", labels, nil),
Mem: prometheus.NewDesc(ns+"memory_utilization_ratio", "System Memory % Utilized", labels, nil),
}
}
// 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, d.SourceName}
infoLabels := []string{d.Version, d.Model, d.Serial, d.Mac, d.IP, d.ID}
// Shared data (all devices do this).
u.exportBYTstats(r, labels, d.TxBytes, d.RxBytes)
u.exportSYSstats(r, labels, d.SysStats, d.SystemStats)
u.exportSTAcount(r, labels, d.UserNumSta, d.GuestNumSta, d.NumDesktop, d.NumMobile, d.NumHandheld)
// Switch Data
u.exportUSWstats(r, labels, d.Stat.Sw)
u.exportPRTtable(r, labels, d.PortTable)
// Gateway Data
u.exportWANPorts(r, labels, d.Wan1, d.Wan2)
u.exportUSGstats(r, labels, d.Stat.Gw, d.SpeedtestStatus, d.Uplink)
// Dream Machine System Data.
r.send([]*metric{
{u.Device.Info, gauge, 1.0, append(labels, infoLabels...)},
{u.Device.Uptime, gauge, d.Uptime, labels},
})
// UDM pro has special temp sensors. UDM non-pro may not have temp; not sure.
for _, t := range d.Temperatures {
r.send([]*metric{{u.Device.Temperature, gauge, t.Value, append(labels, t.Name, t.Type)}})
}
// UDM pro and UXG have hard drives.
for _, t := range d.Storage {
r.send([]*metric{
{u.Device.Storage, gauge, t.Size.Val, append(labels, t.MountPoint, t.Name, "size")},
{u.Device.Storage, gauge, t.Used.Val, append(labels, t.MountPoint, t.Name, "used")},
})
}
// Wireless Data - UDM (non-pro) only
if d.Stat.Ap != nil && d.VapTable != nil {
u.exportUAPstats(r, labels, d.Stat.Ap, d.BytesD, d.TxBytesD, d.RxBytesD, d.BytesR)
u.exportVAPtable(r, labels, *d.VapTable)
u.exportRADtable(r, labels, *d.RadioTable, *d.RadioTableStats)
}
}
// Shared by all.
func (u *promUnifi) exportBYTstats(r report, labels []string, tx, rx unifi.FlexInt) {
r.send([]*metric{
{u.Device.TotalTxBytes, counter, tx, labels},
{u.Device.TotalRxBytes, counter, rx, labels},
{u.Device.TotalBytes, counter, tx.Val + rx.Val, labels},
})
}
// Shared by all, pass 2 or 5 stats.
func (u *promUnifi) exportSTAcount(r report, labels []string, stas ...unifi.FlexInt) {
r.send([]*metric{
{u.Device.Counter, gauge, stas[0], append(labels, "user")},
{u.Device.Counter, gauge, stas[1], append(labels, "guest")},
})
if len(stas) > 2 { // nolint: gomnd
r.send([]*metric{
{u.Device.Counter, gauge, stas[2], append(labels, "desktop")},
{u.Device.Counter, gauge, stas[3], append(labels, "mobile")},
{u.Device.Counter, gauge, stas[4], append(labels, "handheld")},
})
}
}
// Shared by all.
func (u *promUnifi) exportSYSstats(r report, labels []string, s unifi.SysStats, ss unifi.SystemStats) {
r.send([]*metric{
{u.Device.Loadavg1, gauge, s.Loadavg1, labels},
{u.Device.Loadavg5, gauge, s.Loadavg5, labels},
{u.Device.Loadavg15, gauge, s.Loadavg15, labels},
{u.Device.MemUsed, gauge, s.MemUsed, labels},
{u.Device.MemTotal, gauge, s.MemTotal, labels},
{u.Device.MemBuffer, gauge, s.MemBuffer, labels},
{u.Device.CPU, gauge, ss.CPU.Val / 100.0, labels},
{u.Device.Mem, gauge, ss.Mem.Val / 100.0, labels},
})
}

View File

@ -0,0 +1,164 @@
package promunifi
import (
"strconv"
"strings"
"github.com/prometheus/client_golang/prometheus"
"github.com/unpoller/unifi"
)
type usg struct {
WanRxPackets *prometheus.Desc
WanRxBytes *prometheus.Desc
WanRxDropped *prometheus.Desc
WanRxErrors *prometheus.Desc
WanTxPackets *prometheus.Desc
WanTxBytes *prometheus.Desc
LanRxPackets *prometheus.Desc
LanRxBytes *prometheus.Desc
LanRxDropped *prometheus.Desc
LanTxPackets *prometheus.Desc
LanTxBytes *prometheus.Desc
WanRxBroadcast *prometheus.Desc
WanRxBytesR *prometheus.Desc
WanRxMulticast *prometheus.Desc
WanSpeed *prometheus.Desc
WanTxBroadcast *prometheus.Desc
WanTxBytesR *prometheus.Desc
WanTxDropped *prometheus.Desc
WanTxErrors *prometheus.Desc
WanTxMulticast *prometheus.Desc
WanBytesR *prometheus.Desc
Latency *prometheus.Desc
UplinkLatency *prometheus.Desc
UplinkSpeed *prometheus.Desc
Runtime *prometheus.Desc
Rundate *prometheus.Desc
XputDownload *prometheus.Desc
XputUpload *prometheus.Desc
}
func descUSG(ns string) *usg {
labels := []string{"port", "site_name", "name", "source"}
return &usg{
WanRxPackets: prometheus.NewDesc(ns+"wan_receive_packets_total", "WAN Receive Packets Total", labels, nil),
WanRxBytes: prometheus.NewDesc(ns+"wan_receive_bytes_total", "WAN Receive Bytes Total", labels, nil),
WanRxDropped: prometheus.NewDesc(ns+"wan_receive_dropped_total", "WAN Receive Dropped Total", labels, nil),
WanRxErrors: prometheus.NewDesc(ns+"wan_receive_errors_total", "WAN Receive Errors Total", labels, nil),
WanTxPackets: prometheus.NewDesc(ns+"wan_transmit_packets_total", "WAN Transmit Packets Total", labels, nil),
WanTxBytes: prometheus.NewDesc(ns+"wan_transmit_bytes_total", "WAN Transmit Bytes Total", labels, nil),
WanRxBroadcast: prometheus.NewDesc(ns+"wan_receive_broadcast_total", "WAN Receive Broadcast Total", labels, nil),
WanRxBytesR: prometheus.NewDesc(ns+"wan_receive_rate_bytes", "WAN Receive Bytes Rate", labels, nil),
WanRxMulticast: prometheus.NewDesc(ns+"wan_receive_multicast_total", "WAN Receive Multicast Total", labels, nil),
WanSpeed: prometheus.NewDesc(ns+"wan_speed_bps", "WAN Speed", labels, nil),
WanTxBroadcast: prometheus.NewDesc(ns+"wan_transmit_broadcast_total", "WAN Transmit Broadcast Total", labels, nil),
WanTxBytesR: prometheus.NewDesc(ns+"wan_transmit_rate_bytes", "WAN Transmit Bytes Rate", labels, nil),
WanTxDropped: prometheus.NewDesc(ns+"wan_transmit_dropped_total", "WAN Transmit Dropped Total", labels, nil),
WanTxErrors: prometheus.NewDesc(ns+"wan_transmit_errors_total", "WAN Transmit Errors Total", labels, nil),
WanTxMulticast: prometheus.NewDesc(ns+"wan_transmit_multicast_total", "WAN Transmit Multicast Total", labels, nil),
WanBytesR: prometheus.NewDesc(ns+"wan_rate_bytes", "WAN Transfer Rate", labels, nil),
LanRxPackets: prometheus.NewDesc(ns+"lan_receive_packets_total", "LAN Receive Packets Total", labels, nil),
LanRxBytes: prometheus.NewDesc(ns+"lan_receive_bytes_total", "LAN Receive Bytes Total", labels, nil),
LanRxDropped: prometheus.NewDesc(ns+"lan_receive_dropped_total", "LAN Receive Dropped Total", labels, nil),
LanTxPackets: prometheus.NewDesc(ns+"lan_transmit_packets_total", "LAN Transmit Packets Total", labels, nil),
LanTxBytes: prometheus.NewDesc(ns+"lan_transmit_bytes_total", "LAN Transmit Bytes Total", labels, nil),
UplinkLatency: prometheus.NewDesc(ns+"uplink_latency_seconds", "Uplink Latency", labels, nil),
UplinkSpeed: prometheus.NewDesc(ns+"uplink_speed_mbps", "Uplink Speed", labels, nil),
Latency: prometheus.NewDesc(ns+"speedtest_latency_seconds", "Speedtest Latency", labels, nil),
Runtime: prometheus.NewDesc(ns+"speedtest_runtime_seconds", "Speedtest Run Time", labels, nil),
Rundate: prometheus.NewDesc(ns+"speedtest_rundate_seconds", "Speedtest Run Date", labels, nil),
XputDownload: prometheus.NewDesc(ns+"speedtest_download", "Speedtest Download Rate", labels, nil),
XputUpload: prometheus.NewDesc(ns+"speedtest_upload", "Speedtest Upload Rate", labels, nil),
}
}
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, d.SourceName}
infoLabels := []string{d.Version, d.Model, d.Serial, d.Mac, d.IP, d.ID}
for _, t := range d.Temperatures {
r.send([]*metric{{u.Device.Temperature, gauge, t.Value, append(labels, t.Name, t.Type)}})
}
for k, v := range d.SystemStats.Temps {
temp, _ := strconv.ParseInt(strings.Split(v, " ")[0], 10, 64)
k = strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(k, " ", "_"), ")", ""), "(", "")
if k = strings.ToLower(k); temp != 0 && k != "" {
r.send([]*metric{{u.Device.Temperature, gauge, temp, append(labels, k, k)}})
}
}
// Gateway System Data.
u.exportWANPorts(r, labels, d.Wan1, d.Wan2)
u.exportBYTstats(r, labels, d.TxBytes, d.RxBytes)
u.exportSYSstats(r, labels, d.SysStats, d.SystemStats)
u.exportUSGstats(r, labels, d.Stat.Gw, d.SpeedtestStatus, d.Uplink)
u.exportSTAcount(r, labels, d.UserNumSta, d.GuestNumSta, d.NumDesktop, d.UserNumSta, d.GuestNumSta)
r.send([]*metric{
{u.Device.Info, gauge, 1.0, append(labels, infoLabels...)},
{u.Device.Uptime, gauge, d.Uptime, labels},
})
}
// Gateway Stats.
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], labels[3]}
labelWan := []string{"all", labels[1], labels[2], labels[3]}
r.send([]*metric{
{u.USG.LanRxPackets, counter, gw.LanRxPackets, labelLan},
{u.USG.LanRxBytes, counter, gw.LanRxBytes, labelLan},
{u.USG.LanTxPackets, counter, gw.LanTxPackets, labelLan},
{u.USG.LanTxBytes, counter, gw.LanTxBytes, labelLan},
{u.USG.LanRxDropped, counter, gw.LanRxDropped, labelLan},
{u.USG.UplinkLatency, gauge, ul.Latency.Val / 1000, labelWan},
{u.USG.UplinkSpeed, gauge, ul.Speed, labelWan},
// Speed Test Stats
{u.USG.Latency, gauge, st.Latency.Val / 1000, labelWan},
{u.USG.Runtime, gauge, st.Runtime, labelWan},
{u.USG.Rundate, gauge, st.Rundate, labelWan},
{u.USG.XputDownload, gauge, st.XputDownload, labelWan},
{u.USG.XputUpload, gauge, st.XputUpload, labelWan},
})
}
// WAN Stats.
func (u *promUnifi) exportWANPorts(r report, labels []string, wans ...unifi.Wan) {
for _, wan := range wans {
if !wan.Up.Val {
continue // only record UP interfaces.
}
labelWan := []string{wan.Name, labels[1], labels[2], labels[3]}
r.send([]*metric{
{u.USG.WanRxPackets, counter, wan.RxPackets, labelWan},
{u.USG.WanRxBytes, counter, wan.RxBytes, labelWan},
{u.USG.WanRxDropped, counter, wan.RxDropped, labelWan},
{u.USG.WanRxErrors, counter, wan.RxErrors, labelWan},
{u.USG.WanTxPackets, counter, wan.TxPackets, labelWan},
{u.USG.WanTxBytes, counter, wan.TxBytes, labelWan},
{u.USG.WanRxBroadcast, counter, wan.RxBroadcast, labelWan},
{u.USG.WanRxMulticast, counter, wan.RxMulticast, labelWan},
{u.USG.WanSpeed, gauge, wan.Speed.Val * 1000000, labelWan},
{u.USG.WanTxBroadcast, counter, wan.TxBroadcast, labelWan},
{u.USG.WanTxBytesR, gauge, wan.TxBytesR, labelWan},
{u.USG.WanRxBytesR, gauge, wan.RxBytesR, labelWan},
{u.USG.WanTxDropped, counter, wan.TxDropped, labelWan},
{u.USG.WanTxErrors, counter, wan.TxErrors, labelWan},
{u.USG.WanTxMulticast, counter, wan.TxMulticast, labelWan},
{u.USG.WanBytesR, gauge, wan.BytesR, labelWan},
})
}
}

View File

@ -0,0 +1,226 @@
package promunifi
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/unpoller/unifi"
)
type usw struct {
// Switch "total" traffic stats
SwRxPackets *prometheus.Desc
SwRxBytes *prometheus.Desc
SwRxErrors *prometheus.Desc
SwRxDropped *prometheus.Desc
SwRxCrypts *prometheus.Desc
SwRxFrags *prometheus.Desc
SwTxPackets *prometheus.Desc
SwTxBytes *prometheus.Desc
SwTxErrors *prometheus.Desc
SwTxDropped *prometheus.Desc
SwTxRetries *prometheus.Desc
SwRxMulticast *prometheus.Desc
SwRxBroadcast *prometheus.Desc
SwTxMulticast *prometheus.Desc
SwTxBroadcast *prometheus.Desc
SwBytes *prometheus.Desc
// Port data.
PoeCurrent *prometheus.Desc
PoePower *prometheus.Desc
PoeVoltage *prometheus.Desc
RxBroadcast *prometheus.Desc
RxBytes *prometheus.Desc
RxBytesR *prometheus.Desc
RxDropped *prometheus.Desc
RxErrors *prometheus.Desc
RxMulticast *prometheus.Desc
RxPackets *prometheus.Desc
Satisfaction *prometheus.Desc
Speed *prometheus.Desc
TxBroadcast *prometheus.Desc
TxBytes *prometheus.Desc
TxBytesR *prometheus.Desc
TxDropped *prometheus.Desc
TxErrors *prometheus.Desc
TxMulticast *prometheus.Desc
TxPackets *prometheus.Desc
SFPCurrent *prometheus.Desc
SFPRxPower *prometheus.Desc
SFPTemperature *prometheus.Desc
SFPTxPower *prometheus.Desc
SFPVoltage *prometheus.Desc
}
func descUSW(ns string) *usw {
pns := ns + "port_"
sfp := pns + "sfp_"
labelS := []string{"site_name", "name", "source"}
labelP := []string{"port_id", "port_num", "port_name", "port_mac", "port_ip", "site_name", "name", "source"}
labelF := []string{
"sfp_part", "sfp_vendor", "sfp_serial", "sfp_compliance",
"port_id", "port_num", "port_name", "port_mac", "port_ip", "site_name", "name", "source",
}
nd := prometheus.NewDesc
return &usw{
// This data may be derivable by sum()ing the port data.
SwRxPackets: nd(ns+"switch_receive_packets_total", "Switch Packets Received Total", labelS, nil),
SwRxBytes: nd(ns+"switch_receive_bytes_total", "Switch Bytes Received Total", labelS, nil),
SwRxErrors: nd(ns+"switch_receive_errors_total", "Switch Errors Received Total", labelS, nil),
SwRxDropped: nd(ns+"switch_receive_dropped_total", "Switch Dropped Received Total", labelS, nil),
SwRxCrypts: nd(ns+"switch_receive_crypts_total", "Switch Crypts Received Total", labelS, nil),
SwRxFrags: nd(ns+"switch_receive_frags_total", "Switch Frags Received Total", labelS, nil),
SwTxPackets: nd(ns+"switch_transmit_packets_total", "Switch Packets Transmit Total", labelS, nil),
SwTxBytes: nd(ns+"switch_transmit_bytes_total", "Switch Bytes Transmit Total", labelS, nil),
SwTxErrors: nd(ns+"switch_transmit_errors_total", "Switch Errors Transmit Total", labelS, nil),
SwTxDropped: nd(ns+"switch_transmit_dropped_total", "Switch Dropped Transmit Total", labelS, nil),
SwTxRetries: nd(ns+"switch_transmit_retries_total", "Switch Retries Transmit Total", labelS, nil),
SwRxMulticast: nd(ns+"switch_receive_multicast_total", "Switch Multicast Receive Total", labelS, nil),
SwRxBroadcast: nd(ns+"switch_receive_broadcast_total", "Switch Broadcast Receive Total", labelS, nil),
SwTxMulticast: nd(ns+"switch_transmit_multicast_total", "Switch Multicast Transmit Total", labelS, nil),
SwTxBroadcast: nd(ns+"switch_transmit_broadcast_total", "Switch Broadcast Transmit Total", labelS, nil),
SwBytes: nd(ns+"switch_bytes_total", "Switch Bytes Transferred Total", labelS, nil),
// per-port data
PoeCurrent: nd(pns+"poe_amperes", "POE Current", labelP, nil),
PoePower: nd(pns+"poe_watts", "POE Power", labelP, nil),
PoeVoltage: nd(pns+"poe_volts", "POE Voltage", labelP, nil),
RxBroadcast: nd(pns+"receive_broadcast_total", "Receive Broadcast", labelP, nil),
RxBytes: nd(pns+"receive_bytes_total", "Total Receive Bytes", labelP, nil),
RxBytesR: nd(pns+"receive_rate_bytes", "Receive Bytes Rate", labelP, nil),
RxDropped: nd(pns+"receive_dropped_total", "Total Receive Dropped", labelP, nil),
RxErrors: nd(pns+"receive_errors_total", "Total Receive Errors", labelP, nil),
RxMulticast: nd(pns+"receive_multicast_total", "Total Receive Multicast", labelP, nil),
RxPackets: nd(pns+"receive_packets_total", "Total Receive Packets", labelP, nil),
Satisfaction: nd(pns+"satisfaction_ratio", "Satisfaction", labelP, nil),
Speed: nd(pns+"port_speed_bps", "Speed", labelP, nil),
TxBroadcast: nd(pns+"transmit_broadcast_total", "Total Transmit Broadcast", labelP, nil),
TxBytes: nd(pns+"transmit_bytes_total", "Total Transmit Bytes", labelP, nil),
TxBytesR: nd(pns+"transmit_rate_bytes", "Transmit Bytes Rate", labelP, nil),
TxDropped: nd(pns+"transmit_dropped_total", "Total Transmit Dropped", labelP, nil),
TxErrors: nd(pns+"transmit_errors_total", "Total Transmit Errors", labelP, nil),
TxMulticast: nd(pns+"transmit_multicast_total", "Total Tranmist Multicast", labelP, nil),
TxPackets: nd(pns+"transmit_packets_total", "Total Transmit Packets", labelP, nil),
SFPCurrent: nd(sfp+"current", "SFP Current", labelF, nil),
SFPRxPower: nd(sfp+"rx_power", "SFP Receive Power", labelF, nil),
SFPTemperature: nd(sfp+"temperature", "SFP Temperature", labelF, nil),
SFPTxPower: nd(sfp+"tx_power", "SFP Transmit Power", labelF, nil),
SFPVoltage: nd(sfp+"voltage", "SFP Voltage", labelF, nil),
}
}
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, d.SourceName}
infoLabels := []string{d.Version, d.Model, d.Serial, d.Mac, d.IP, d.ID}
u.exportUSWstats(r, labels, d.Stat.Sw)
u.exportPRTtable(r, labels, d.PortTable)
u.exportBYTstats(r, labels, d.TxBytes, d.RxBytes)
u.exportSYSstats(r, labels, d.SysStats, d.SystemStats)
u.exportSTAcount(r, labels, d.UserNumSta, d.GuestNumSta)
r.send([]*metric{
{u.Device.Info, gauge, 1.0, append(labels, infoLabels...)},
{u.Device.Uptime, gauge, d.Uptime, labels},
})
// Switch System Data.
if d.HasTemperature.Val {
r.send([]*metric{{u.Device.Temperature, gauge, d.GeneralTemperature, append(labels, "general", "board")}})
}
if d.HasFan.Val {
r.send([]*metric{{u.Device.FanLevel, gauge, d.FanLevel, labels}})
}
if d.TotalMaxPower.Txt != "" {
r.send([]*metric{{u.Device.TotalMaxPower, gauge, d.TotalMaxPower, labels}})
}
}
// 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},
{u.USW.SwRxBytes, counter, sw.RxBytes, labelS},
{u.USW.SwRxErrors, counter, sw.RxErrors, labelS},
{u.USW.SwRxDropped, counter, sw.RxDropped, labelS},
{u.USW.SwRxCrypts, counter, sw.RxCrypts, labelS},
{u.USW.SwRxFrags, counter, sw.RxFrags, labelS},
{u.USW.SwTxPackets, counter, sw.TxPackets, labelS},
{u.USW.SwTxBytes, counter, sw.TxBytes, labelS},
{u.USW.SwTxErrors, counter, sw.TxErrors, labelS},
{u.USW.SwTxDropped, counter, sw.TxDropped, labelS},
{u.USW.SwTxRetries, counter, sw.TxRetries, labelS},
{u.USW.SwRxMulticast, counter, sw.RxMulticast, labelS},
{u.USW.SwRxBroadcast, counter, sw.RxBroadcast, labelS},
{u.USW.SwTxMulticast, counter, sw.TxMulticast, labelS},
{u.USW.SwTxBroadcast, counter, sw.TxBroadcast, labelS},
{u.USW.SwBytes, counter, sw.Bytes, labelS},
})
}
// Switch Port Table.
func (u *promUnifi) exportPRTtable(r report, labels []string, pt []unifi.Port) {
// Per-port data on a switch
for _, p := range pt {
if !u.DeadPorts && (!p.Up.Val || !p.Enable.Val) {
continue
}
// Copy labels, and add four new ones.
labelP := []string{
labels[2] + " Port " + p.PortIdx.Txt, p.PortIdx.Txt,
p.Name, p.Mac, p.IP, labels[1], labels[2], labels[3],
}
if p.PoeEnable.Val && p.PortPoe.Val {
r.send([]*metric{
{u.USW.PoeCurrent, gauge, p.PoeCurrent, labelP},
{u.USW.PoePower, gauge, p.PoePower, labelP},
{u.USW.PoeVoltage, gauge, p.PoeVoltage, labelP},
})
}
if p.SFPFound.Val {
labelF := []string{
p.SFPPart, p.SFPVendor, p.SFPSerial, p.SFPCompliance,
labelP[0], labelP[1], labelP[2], labelP[3], labelP[4], labelP[5], labelP[6], labelP[7],
}
r.send([]*metric{
{u.USW.SFPCurrent, gauge, p.SFPCurrent.Val, labelF},
{u.USW.SFPVoltage, gauge, p.SFPVoltage.Val, labelF},
{u.USW.SFPTemperature, gauge, p.SFPTemperature.Val, labelF},
{u.USW.SFPRxPower, gauge, p.SFPRxpower.Val, labelF},
{u.USW.SFPTxPower, gauge, p.SFPTxpower.Val, labelF},
})
}
r.send([]*metric{
{u.USW.RxBroadcast, counter, p.RxBroadcast, labelP},
{u.USW.RxBytes, counter, p.RxBytes, labelP},
{u.USW.RxBytesR, gauge, p.RxBytesR, labelP},
{u.USW.RxDropped, counter, p.RxDropped, labelP},
{u.USW.RxErrors, counter, p.RxErrors, labelP},
{u.USW.RxMulticast, counter, p.RxMulticast, labelP},
{u.USW.RxPackets, counter, p.RxPackets, labelP},
{u.USW.Satisfaction, gauge, p.Satisfaction.Val / 100.0, labelP},
{u.USW.Speed, gauge, p.Speed.Val * 1000000, labelP},
{u.USW.TxBroadcast, counter, p.TxBroadcast, labelP},
{u.USW.TxBytes, counter, p.TxBytes, labelP},
{u.USW.TxBytesR, gauge, p.TxBytesR, labelP},
{u.USW.TxDropped, counter, p.TxDropped, labelP},
{u.USW.TxErrors, counter, p.TxErrors, labelP},
{u.USW.TxMulticast, counter, p.TxMulticast, labelP},
{u.USW.TxPackets, counter, p.TxPackets, labelP},
})
}
}

View File

@ -0,0 +1,42 @@
package promunifi
import (
"github.com/unpoller/unifi"
)
// exportUXG is a collection of stats from USG and USW. It has no unique stats.
func (u *promUnifi) exportUXG(r report, d *unifi.UXG) {
if !d.Adopted.Val || d.Locating.Val {
return
}
labels := []string{d.Type, d.SiteName, d.Name, d.SourceName}
infoLabels := []string{d.Version, d.Model, d.Serial, d.Mac, d.IP, d.ID}
// Shared data (all devices do this).
u.exportBYTstats(r, labels, d.TxBytes, d.RxBytes)
u.exportSYSstats(r, labels, d.SysStats, d.SystemStats)
u.exportSTAcount(r, labels, d.UserNumSta, d.GuestNumSta, d.NumDesktop, d.NumMobile, d.NumHandheld)
// Switch Data
u.exportUSWstats(r, labels, d.Stat.Sw)
u.exportPRTtable(r, labels, d.PortTable)
// Gateway Data
u.exportWANPorts(r, labels, d.Wan1, d.Wan2)
u.exportUSGstats(r, labels, d.Stat.Gw, d.SpeedtestStatus, d.Uplink)
// Dream Machine System Data.
r.send([]*metric{
{u.Device.Info, gauge, 1.0, append(labels, infoLabels...)},
{u.Device.Uptime, gauge, d.Uptime, labels},
})
for _, t := range d.Temperatures {
r.send([]*metric{{u.Device.Temperature, gauge, t.Value, append(labels, t.Name, t.Type)}})
}
// UDM pro and UXG have hard drives.
for _, t := range d.Storage {
r.send([]*metric{
{u.Device.Storage, gauge, t.Size.Val, append(labels, t.MountPoint, t.Name, "size")},
{u.Device.Storage, gauge, t.Used.Val, append(labels, t.MountPoint, t.Name, "used")},
})
}
}