diff --git a/integrations/influxunifi/.travis.yml b/integrations/influxunifi/.travis.yml index fa14fca0..ab7da6e9 100644 --- a/integrations/influxunifi/.travis.yml +++ b/integrations/influxunifi/.travis.yml @@ -1,9 +1,9 @@ language: go go: -- 1.14.x +- 1.15.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 latest script: - go test ./... -- golangci-lint run --enable-all +- golangci-lint run --enable-all -D exhaustivestruct,nlreturn diff --git a/integrations/influxunifi/alarms.go b/integrations/influxunifi/alarms.go new file mode 100644 index 00000000..db5e3b6f --- /dev/null +++ b/integrations/influxunifi/alarms.go @@ -0,0 +1,87 @@ +package influxunifi + +import ( + "time" + + "github.com/unifi-poller/unifi" +) + +const ( + alarmT = item("Alarm") + anomalyT = item("Anomaly") +) + +// batchAlarms generates alarm datapoints for InfluxDB. +func (u *InfluxUnifi) batchAlarms(r report, event *unifi.Alarm) { // nolint:dupl + if time.Since(event.Datetime) > u.Interval.Duration+time.Second { + return // The event is older than our interval, ignore it. + } + + fields := map[string]interface{}{ + "dest_port": event.DestPort, + "src_port": event.SrcPort, + "dest_ip": event.DestIP, + "dst_mac": event.DstMAC, + "host": event.Host, + "msg": event.Msg, + "src_ip": event.SrcIP, + "src_mac": event.SrcMAC, + "dstip_asn": event.DestIPGeo.Asn, + "dstip_latitude": event.DestIPGeo.Latitude, + "dstip_longitude": event.DestIPGeo.Longitude, + "dstip_city": event.DestIPGeo.City, + "dstip_continent_code": event.DestIPGeo.ContinentCode, + "dstip_country_code": event.DestIPGeo.CountryCode, + "dstip_country_name": event.DestIPGeo.CountryName, + "dstip_organization": event.DestIPGeo.Organization, + "srcip_asn": event.SourceIPGeo.Asn, + "srcip_latitude": event.SourceIPGeo.Latitude, + "srcip_longitude": event.SourceIPGeo.Longitude, + "srcip_city": event.SourceIPGeo.City, + "srcip_continent_code": event.SourceIPGeo.ContinentCode, + "srcip_country_code": event.SourceIPGeo.CountryCode, + "srcip_country_name": event.SourceIPGeo.CountryName, + "srcip_organization": event.SourceIPGeo.Organization, + } + + r.addCount(alarmT) + r.send(&metric{ + Table: "unifi_alarm", + TS: event.Datetime, + Fields: cleanFields(fields), + Tags: cleanTags(map[string]string{ + "site_name": event.SiteName, + "source": event.SourceName, + "in_iface": event.InIface, + "event_type": event.EventType, + "subsystem": event.Subsystem, + "archived": event.Archived.Txt, + "usgip": event.USGIP, + "proto": event.Proto, + "key": event.Key, + "catname": event.Catname, + "app_proto": event.AppProto, + "action": event.InnerAlertAction, + }), + }) +} + +// batchAnomaly generates Anomalies from UniFi for InfluxDB. +func (u *InfluxUnifi) batchAnomaly(r report, event *unifi.Anomaly) { + if time.Since(event.Datetime) > u.Interval.Duration+time.Second { + return // The event is older than our interval, ignore it. + } + + r.addCount(anomalyT) + r.send(&metric{ + TS: event.Datetime, + Table: "unifi_anomaly", + Fields: map[string]interface{}{"msg": event.Anomaly}, + Tags: cleanTags(map[string]string{ + "application": "unifi_anomaly", + "source": event.SourceName, + "site_name": event.SiteName, + "device_mac": event.DeviceMAC, + }), + }) +} diff --git a/integrations/influxunifi/clients.go b/integrations/influxunifi/clients.go index a1b71efb..d40c926e 100644 --- a/integrations/influxunifi/clients.go +++ b/integrations/influxunifi/clients.go @@ -76,7 +76,13 @@ func (u *InfluxUnifi) batchClient(r report, s *unifi.Client) { // nolint: funlen // totalsDPImap: controller, site, name (app/cat name), dpi. type totalsDPImap map[string]map[string]map[string]unifi.DPIData -func (u *InfluxUnifi) batchClientDPI(r report, s *unifi.DPITable, appTotal, catTotal totalsDPImap) { +func (u *InfluxUnifi) batchClientDPI(r report, v interface{}, appTotal, catTotal totalsDPImap) { + s, ok := v.(*unifi.DPITable) + if !ok { + u.LogErrorf("invalid type given to batchClientDPI: %T", v) + return + } + for _, dpi := range s.ByApp { category := unifi.DPICats.Get(dpi.Cat) application := unifi.DPIApps.GetApp(dpi.Cat, dpi.App) @@ -98,8 +104,8 @@ func (u *InfluxUnifi) batchClientDPI(r report, s *unifi.DPITable, appTotal, catT "rx_packets": dpi.RxPackets, "tx_bytes": dpi.TxBytes, "rx_bytes": dpi.RxBytes, - }}, - ) + }, + }) } } diff --git a/integrations/influxunifi/events.go b/integrations/influxunifi/events.go new file mode 100644 index 00000000..070a1c98 --- /dev/null +++ b/integrations/influxunifi/events.go @@ -0,0 +1,185 @@ +package influxunifi + +import ( + "time" + + "github.com/unifi-poller/unifi" +) + +// These constants are used as names for printed/logged counters. +const ( + eventT = item("Event") + idsT = item("IDS") +) + +// batchIDS generates intrusion detection datapoints for InfluxDB. +func (u *InfluxUnifi) batchIDS(r report, i *unifi.IDS) { // nolint:dupl + if time.Since(i.Datetime) > u.Interval.Duration+time.Second { + return // The event is older than our interval, ignore it. + } + + fields := map[string]interface{}{ + "dest_port": i.DestPort, + "src_port": i.SrcPort, + "dest_ip": i.DestIP, + "dst_mac": i.DstMAC, + "host": i.Host, + "msg": i.Msg, + "src_ip": i.SrcIP, + "src_mac": i.SrcMAC, + "dstip_asn": i.DestIPGeo.Asn, + "dstip_latitude": i.DestIPGeo.Latitude, + "dstip_longitude": i.DestIPGeo.Longitude, + "dstip_city": i.DestIPGeo.City, + "dstip_continent_code": i.DestIPGeo.ContinentCode, + "dstip_country_code": i.DestIPGeo.CountryCode, + "dstip_country_name": i.DestIPGeo.CountryName, + "dstip_organization": i.DestIPGeo.Organization, + "srcip_asn": i.SourceIPGeo.Asn, + "srcip_latitude": i.SourceIPGeo.Latitude, + "srcip_longitude": i.SourceIPGeo.Longitude, + "srcip_city": i.SourceIPGeo.City, + "srcip_continent_code": i.SourceIPGeo.ContinentCode, + "srcip_country_code": i.SourceIPGeo.CountryCode, + "srcip_country_name": i.SourceIPGeo.CountryName, + "srcip_organization": i.SourceIPGeo.Organization, + } + + r.addCount(idsT) + r.send(&metric{ + Table: "unifi_ids", + TS: i.Datetime, + Fields: cleanFields(fields), + Tags: cleanTags(map[string]string{ + "site_name": i.SiteName, + "source": i.SourceName, + "in_iface": i.InIface, + "event_type": i.EventType, + "subsystem": i.Subsystem, + "archived": i.Archived.Txt, + "usgip": i.USGIP, + "proto": i.Proto, + "key": i.Key, + "catname": i.Catname, + "app_proto": i.AppProto, + "action": i.InnerAlertAction, + }), + }) +} + +// batchEvents generates events from UniFi for InfluxDB. +func (u *InfluxUnifi) batchEvent(r report, i *unifi.Event) { // nolint: funlen + if time.Since(i.Datetime) > u.Interval.Duration+time.Second { + return // The event is older than our interval, ignore it. + } + + fields := map[string]interface{}{ + "msg": i.Msg, // contains user[] or guest[] or admin[] + "duration": i.Duration.Val, // probably microseconds? + "guest": i.Guest, // mac address + "user": i.User, // mac address + "host": i.Host, // usg device? + "hostname": i.Hostname, // client name + "dest_port": i.DestPort, + "src_port": i.SrcPort, + "bytes": i.Bytes.Val, + "dest_ip": i.DestIP, + "dst_mac": i.DstMAC, + "ip": i.IP, + "src_ip": i.SrcIP, + "src_mac": i.SrcMAC, + "dstip_asn": i.DestIPGeo.Asn, + "dstip_latitude": i.DestIPGeo.Latitude, + "dstip_longitude": i.DestIPGeo.Longitude, + "dstip_city": i.DestIPGeo.City, + "dstip_continent_code": i.DestIPGeo.ContinentCode, + "dstip_country_code": i.DestIPGeo.CountryCode, + "dstip_country_name": i.DestIPGeo.CountryName, + "dstip_organization": i.DestIPGeo.Organization, + "srcip_asn": i.SourceIPGeo.Asn, + "srcip_latitude": i.SourceIPGeo.Latitude, + "srcip_longitude": i.SourceIPGeo.Longitude, + "srcip_city": i.SourceIPGeo.City, + "srcip_continent_code": i.SourceIPGeo.ContinentCode, + "srcip_country_code": i.SourceIPGeo.CountryCode, + "srcip_country_name": i.SourceIPGeo.CountryName, + "srcip_organization": i.SourceIPGeo.Organization, + } + + r.addCount(eventT) + r.send(&metric{ + TS: i.Datetime, + Table: "unifi_events", + Fields: cleanFields(fields), + Tags: cleanTags(map[string]string{ + "admin": i.Admin, // username + "site_name": i.SiteName, + "source": i.SourceName, + "ap_from": i.ApFrom, + "ap_to": i.ApTo, + "ap": i.Ap, + "ap_name": i.ApName, + "gw": i.Gw, + "gw_name": i.GwName, + "sw": i.Sw, + "sw_name": i.SwName, + "catname": i.Catname, + "radio": i.Radio, + "radio_from": i.RadioFrom, + "radio_to": i.RadioTo, + "key": i.Key, + "in_iface": i.InIface, + "event_type": i.EventType, + "subsystem": i.Subsystem, + "ssid": i.SSID, + "is_admin": i.IsAdmin.Txt, + "channel": i.Channel.Txt, + "channel_from": i.ChannelFrom.Txt, + "channel_to": i.ChannelTo.Txt, + "usgip": i.USGIP, + "network": i.Network, + "app_proto": i.AppProto, + "proto": i.Proto, + "action": i.InnerAlertAction, + }), + }) +} + +// cleanTags removes any tag that is empty. +func cleanTags(tags map[string]string) map[string]string { + for i := range tags { + if tags[i] == "" { + delete(tags, i) + } + } + + return tags +} + +// cleanFields removes any field with a default (or empty) value. +func cleanFields(fields map[string]interface{}) map[string]interface{} { //nolint:cyclop + for s := range fields { + switch v := fields[s].(type) { + case nil: + delete(fields, s) + case int, int64, float64: + if v == 0 { + delete(fields, s) + } + case unifi.FlexBool: + if v.Txt == "" { + delete(fields, s) + } + case unifi.FlexInt: + if v.Txt == "" { + delete(fields, s) + } + case string: + if v == "" { + delete(fields, s) + } + } + } + + return fields +} diff --git a/integrations/influxunifi/go.mod b/integrations/influxunifi/go.mod index 025d2aac..28580b88 100644 --- a/integrations/influxunifi/go.mod +++ b/integrations/influxunifi/go.mod @@ -5,8 +5,12 @@ go 1.14 require ( github.com/influxdata/influxdb1-client v0.0.0-20200515024757-02f0bf5dbca3 github.com/pkg/errors v0.9.1 - github.com/prometheus/procfs v0.1.2 // indirect - github.com/unifi-poller/poller v0.0.6 - github.com/unifi-poller/unifi v0.0.5 + github.com/prometheus/client_golang v1.7.1 // indirect + github.com/unifi-poller/poller v0.0.8-0.20200628131550-26430cac16c1 + github.com/unifi-poller/unifi v0.0.6-0.20200625090439-421046871a37 + github.com/unifi-poller/webserver v0.0.0-20200628212441-340749c94743 + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect + golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae // indirect golift.io/cnfg v0.0.5 + google.golang.org/protobuf v1.25.0 // indirect ) diff --git a/integrations/influxunifi/go.sum b/integrations/influxunifi/go.sum index f3d9eaf4..6798f2f3 100644 --- a/integrations/influxunifi/go.sum +++ b/integrations/influxunifi/go.sum @@ -6,7 +6,6 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy 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/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= 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= @@ -28,12 +27,7 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 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 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 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= @@ -46,13 +40,15 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a 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.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d h1:/WZQPMZNsjZ7IlCpsLGdQBINg5bxKQ1K1sh6awxLtkA= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= +github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/influxdata/influxdb1-client v0.0.0-20200515024757-02f0bf5dbca3 h1:k3/6a1Shi7GGCp9QpyYuXsMM6ncTOjCzOE9Fd6CDA+Q= github.com/influxdata/influxdb1-client v0.0.0-20200515024757-02f0bf5dbca3/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -67,45 +63,34 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN 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/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.4.0 h1:YVIb/fVcOTMSqtqZWSKnHpSLBxu8DKgxq8z6RuBZwqI= -github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.5.0 h1:Ctq0iGpCmr3jeP77kbF2UxgvRwzWWz+4Bh9/vJTyg1A= -github.com/prometheus/client_golang v1.5.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.6.0 h1:YVPodQOcK15POxhgARIvnDRVpLcuK8mglnMrWfyrw6A= github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4= +github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= 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.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= -github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.0.10 h1:QJQN3jYQhkamO4mhfUWqdDH2asK7ONOI9MTWjyAxNKM= -github.com/prometheus/procfs v0.0.10/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 h1:/ZKcW+ixpq2dOl4yeH4qvACNXnkiDCp5e/F5Tq07X7o= github.com/prometheus/procfs v0.1.1/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.1.2 h1:DS2izEFqEVp1gPg5yvEF/YzS6Ajo8YdKRJjQKiQm59A= github.com/prometheus/procfs v0.1.2/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 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/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -115,30 +100,41 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ 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/unifi-poller/poller v0.0.2/go.mod h1:H1YErnDRLaUQBH09kzd7kNTL3bMNCPuYbg94aJBun7Y= -github.com/unifi-poller/poller v0.0.3 h1:VrVx+1jvVcRh11TyO9nn58el/nvWcRKtGwzrg80a0SQ= -github.com/unifi-poller/poller v0.0.3/go.mod h1:ugy3FyZEH1rFyC3panBiJpXbLf7EZ4GkjiOtydB2CwQ= -github.com/unifi-poller/poller v0.0.5 h1:qnofARTx0JveNc9PqGJmNUs7xsjwFqCWpS8pwDSjW78= -github.com/unifi-poller/poller v0.0.5/go.mod h1:45TyAHk+xYF4KoFKaaZyjMmSdhbq2I1pLniYWfOXdHs= -github.com/unifi-poller/poller v0.0.6 h1:KhOWUeYI029Nn/4NOIk/yblQ3tEd9QhS+u/8/S9ZoDs= -github.com/unifi-poller/poller v0.0.6/go.mod h1:RkRJ4pAc2dAN8Xu9+VOumeE3BdN5QDQ3PC+jBx8hWW0= -github.com/unifi-poller/unifi v0.0.1/go.mod h1:DagVD/I+VMnVUHmTT4Fi76lPI+DHbuMwwtMIzanwMxM= -github.com/unifi-poller/unifi v0.0.2 h1:FRa6G+VcxOdvy0/u/QLCHGCRYF6EDcxVYij0dDRCKxg= -github.com/unifi-poller/unifi v0.0.2/go.mod h1:DagVD/I+VMnVUHmTT4Fi76lPI+DHbuMwwtMIzanwMxM= -github.com/unifi-poller/unifi v0.0.3 h1:6pmjW7MuEEDKKvYoxjL3EZlaLOgmhYyxJBAg4X7GliI= -github.com/unifi-poller/unifi v0.0.3/go.mod h1:DagVD/I+VMnVUHmTT4Fi76lPI+DHbuMwwtMIzanwMxM= -github.com/unifi-poller/unifi v0.0.4 h1:NDTxHTdF0MAt1y1RU8J+MSqdYlO0CEIqlrktcj3y/og= -github.com/unifi-poller/unifi v0.0.4/go.mod h1:bTUtctrf56aapjKH+L+98eThBaVFbQXw5iNGZI0g/+E= -github.com/unifi-poller/unifi v0.0.5-0.20200614033708-a51badfce839 h1:B3eWCYQdLLKaBTxQASv1VztTp4ZuGbDc0XHOpcgkZ74= -github.com/unifi-poller/unifi v0.0.5-0.20200614033708-a51badfce839/go.mod h1:L1kMRH2buZhB31vZnRC1im7Tk/4uD3ET4biwl2faYy8= -github.com/unifi-poller/unifi v0.0.5-0.20200614034449-3f18c1be228b h1:bpQVeurqplilZ8Rdw+w7mNMmR9v2OkENfytdUngrKHA= -github.com/unifi-poller/unifi v0.0.5-0.20200614034449-3f18c1be228b/go.mod h1:L1kMRH2buZhB31vZnRC1im7Tk/4uD3ET4biwl2faYy8= -github.com/unifi-poller/unifi v0.0.5-0.20200614034623-f4c1d18157c2 h1:iDOUe8UECHQoOyzWIarbGNVdwEth16iiUsw+1UgQ+sw= -github.com/unifi-poller/unifi v0.0.5-0.20200614034623-f4c1d18157c2/go.mod h1:L1kMRH2buZhB31vZnRC1im7Tk/4uD3ET4biwl2faYy8= -github.com/unifi-poller/unifi v0.0.5 h1:Izeun32YxcQOeKZUXY0Sy4ltKYFuYxWGcN9JS6xkIJU= -github.com/unifi-poller/unifi v0.0.5/go.mod h1:L1kMRH2buZhB31vZnRC1im7Tk/4uD3ET4biwl2faYy8= +github.com/unifi-poller/poller v0.0.8-0.20200619104117-a5e263a36ac9 h1:BYNOSvttR91a0F+ttG2ecD3DenVI9p9oi1W7m36zH98= +github.com/unifi-poller/poller v0.0.8-0.20200619104117-a5e263a36ac9/go.mod h1:q2tufLljemUR/blyhjIj+T0sdAOZo0kdlv3h79kEMgU= +github.com/unifi-poller/poller v0.0.8-0.20200621091816-fd5c7abd9f4b h1:AJKt/ZIDtlEOWxwpL/a7pcrWmqdJaaNE9odNn4JSRj0= +github.com/unifi-poller/poller v0.0.8-0.20200621091816-fd5c7abd9f4b/go.mod h1:+Ppksi2wBCrByJke0B0lTutxFtKfv1zx6L1haALBrN4= +github.com/unifi-poller/poller v0.0.8-0.20200621095143-fae3e391ade8 h1:GqWHa6Al2BJkbfF9SAEJIokpIOJjKIPvEXyMdppVGYo= +github.com/unifi-poller/poller v0.0.8-0.20200621095143-fae3e391ade8/go.mod h1:+Ppksi2wBCrByJke0B0lTutxFtKfv1zx6L1haALBrN4= +github.com/unifi-poller/poller v0.0.8-0.20200621101255-6d0d0b288ece h1:EsyR6cKuwAKzddS4gsKDugfN+OEHCm7bhNOvEfBCWWA= +github.com/unifi-poller/poller v0.0.8-0.20200621101255-6d0d0b288ece/go.mod h1:+Ppksi2wBCrByJke0B0lTutxFtKfv1zx6L1haALBrN4= +github.com/unifi-poller/poller v0.0.8-0.20200621103717-5f3d60890ed6 h1:V19WgXwjXxGY75Mn8Hc5Whl3+BC71YSGatRvKVRh9pA= +github.com/unifi-poller/poller v0.0.8-0.20200621103717-5f3d60890ed6/go.mod h1:+Ppksi2wBCrByJke0B0lTutxFtKfv1zx6L1haALBrN4= +github.com/unifi-poller/poller v0.0.8-0.20200621110949-33f1a1454d10 h1:1rGP4ISFpBj9xjJDXNak7EdaQtyoy3MwMZzo2+W1PLo= +github.com/unifi-poller/poller v0.0.8-0.20200621110949-33f1a1454d10/go.mod h1:+Ppksi2wBCrByJke0B0lTutxFtKfv1zx6L1haALBrN4= +github.com/unifi-poller/poller v0.0.8-0.20200621214016-5d1ed3324a46 h1:OhbVj3VVgbpUMQFSwD0NszDsbEL7DdbTcJuU+p9DwIM= +github.com/unifi-poller/poller v0.0.8-0.20200621214016-5d1ed3324a46/go.mod h1:pJ/MeYaakLOOpbyc7s4zeZ92UzNK/rir5jkA7t5jIjo= +github.com/unifi-poller/poller v0.0.8-0.20200626082958-a9a7092a5684 h1:r1B8GoI47czgGnQ7WY89qlSKqSE1d1pQmcLfdXVW/+Y= +github.com/unifi-poller/poller v0.0.8-0.20200626082958-a9a7092a5684/go.mod h1:pJ/MeYaakLOOpbyc7s4zeZ92UzNK/rir5jkA7t5jIjo= +github.com/unifi-poller/poller v0.0.8-0.20200628131550-26430cac16c1 h1:SHKYtAu4yB5bVhMuRkoHr8Ss1Ffu5dKLJ13rGWufLFI= +github.com/unifi-poller/poller v0.0.8-0.20200628131550-26430cac16c1/go.mod h1:fObadG7weiVnSpFu8pFpGfo2bYYFc7hUMe770FovSc8= +github.com/unifi-poller/promunifi v0.0.9-0.20200620104707-26208eb4336b h1:HgmbS5cKfvw3x0ie6IV/FfhxNtKwAvICKxXL7gg2sgM= +github.com/unifi-poller/promunifi v0.0.9-0.20200620104707-26208eb4336b/go.mod h1:jOcYehhsOrs4ctswSKEqGuqSgVBpConaWmRYskycbUc= +github.com/unifi-poller/unifi v0.0.5-0.20200619092006-d24c776a42f5/go.mod h1:L1kMRH2buZhB31vZnRC1im7Tk/4uD3ET4biwl2faYy8= +github.com/unifi-poller/unifi v0.0.5-0.20200620103801-b927287ea1cd/go.mod h1:L1kMRH2buZhB31vZnRC1im7Tk/4uD3ET4biwl2faYy8= +github.com/unifi-poller/unifi v0.0.5-0.20200621075746-253ccae7e106 h1:eKErSqWD656pLSWgxFwhDhHe/zfAXrm7F39Zn4R+si8= +github.com/unifi-poller/unifi v0.0.5-0.20200621075746-253ccae7e106/go.mod h1:L1kMRH2buZhB31vZnRC1im7Tk/4uD3ET4biwl2faYy8= +github.com/unifi-poller/unifi v0.0.6-0.20200625090439-421046871a37 h1:T2y8JWkjZd1vz2ZKu4vmmAk9s6PUwupuTldwhfww5xY= +github.com/unifi-poller/unifi v0.0.6-0.20200625090439-421046871a37/go.mod h1:L1kMRH2buZhB31vZnRC1im7Tk/4uD3ET4biwl2faYy8= +github.com/unifi-poller/webserver v0.0.0-20200628115531-e071827d7598 h1:Nmo1arOOln7V7YhjcwrT2AEFN0dl43tl5NgoNF4SgiA= +github.com/unifi-poller/webserver v0.0.0-20200628115531-e071827d7598/go.mod h1:rlM8tRx7wCxqj4+6ZkuFVt2voFoAlHhS/XTrHd7T57s= +github.com/unifi-poller/webserver v0.0.0-20200628212441-340749c94743 h1:B0K4P+dCLW5qG8hRhdMlrNEU+Yy0kAm9C2lCUmfBhPI= +github.com/unifi-poller/webserver v0.0.0-20200628212441-340749c94743/go.mod h1:ekoFVYoCWwn2CjQVTC9PNF6OaXYzr7HSnfc1BDQxxjA= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 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= @@ -148,11 +144,8 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/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-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -165,19 +158,16 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= +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-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= -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-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200610111108-226ff32320da h1:bGb80FudwxpeucJUjPYJXuJ8Hk91vNtfvrymzwiei38= 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 h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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= @@ -204,16 +194,15 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 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.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/integrations/influxunifi/ids.go b/integrations/influxunifi/ids.go deleted file mode 100644 index f5e41d0b..00000000 --- a/integrations/influxunifi/ids.go +++ /dev/null @@ -1,42 +0,0 @@ -package influxunifi - -import ( - "github.com/unifi-poller/unifi" -) - -// batchIDS generates intrusion detection datapoints for InfluxDB. -// These points can be passed directly to influx. -func (u *InfluxUnifi) batchIDS(r report, i *unifi.IDS) { - tags := map[string]string{ - "site_name": i.SiteName, - "source": i.SourceName, - "in_iface": i.InIface, - "event_type": i.EventType, - "proto": i.Proto, - "app_proto": i.AppProto, - "usgip": i.Usgip, - "country_code": i.SrcipGeo.CountryCode, - "country_name": i.SrcipGeo.CountryName, - "region": i.SrcipGeo.Region, - "city": i.SrcipGeo.City, - "postal_code": i.SrcipGeo.PostalCode, - "srcipASN": i.SrcipASN, - "usgipASN": i.UsgipASN, - "alert_category": i.InnerAlertCategory, - "subsystem": i.Subsystem, - "catname": i.Catname, - } - fields := map[string]interface{}{ - "event_type": i.EventType, - "proto": i.Proto, - "app_proto": i.AppProto, - "usgip": i.Usgip, - "country_name": i.SrcipGeo.CountryName, - "city": i.SrcipGeo.City, - "postal_code": i.SrcipGeo.PostalCode, - "srcipASN": i.SrcipASN, - "usgipASN": i.UsgipASN, - } - - r.send(&metric{Table: "intrusion_detect", Tags: tags, Fields: fields}) -} diff --git a/integrations/influxunifi/influxdb.go b/integrations/influxunifi/influxdb.go index 03398931..eaa6a73d 100644 --- a/integrations/influxunifi/influxdb.go +++ b/integrations/influxunifi/influxdb.go @@ -7,6 +7,7 @@ import ( "fmt" "io/ioutil" "log" + "strconv" "strings" "time" @@ -14,9 +15,13 @@ import ( "github.com/pkg/errors" "github.com/unifi-poller/poller" "github.com/unifi-poller/unifi" + "github.com/unifi-poller/webserver" "golift.io/cnfg" ) +// PluginName is the name of this plugin. +const PluginName = "influxdb" + const ( defaultInterval = 30 * time.Second minimumInterval = 10 * time.Second @@ -53,13 +58,14 @@ type metric struct { Table string Tags map[string]string Fields map[string]interface{} + TS time.Time } func init() { // nolint: gochecknoinits u := &InfluxUnifi{InfluxDB: &InfluxDB{}, LastCheck: time.Now()} poller.NewOutput(&poller.Output{ - Name: "influxdb", + Name: PluginName, Config: u.InfluxDB, Method: u.Run, }) @@ -73,24 +79,26 @@ func (u *InfluxUnifi) PollController() { log.Printf("[INFO] Everything checks out! Poller started, InfluxDB interval: %v", interval) for u.LastCheck = range ticker.C { - metrics, ok, collectErr := u.Collector.Metrics() - if collectErr != nil { - u.Collector.LogErrorf("metric fetch for InfluxDB failed: %v", collectErr) - - if !ok { - continue - } - } - - report, err := u.ReportMetrics(metrics) + metrics, err := u.Collector.Metrics(&poller.Filter{Name: "unifi"}) if err != nil { - // XXX: reset and re-auth? not sure.. - u.Collector.LogErrorf("%v", err) + u.LogErrorf("metric fetch for InfluxDB failed: %v", err) continue } - report.error(collectErr) - u.LogInfluxReport(report) + events, err := u.Collector.Events(&poller.Filter{Name: "unifi", Dur: interval}) + if err != nil { + u.LogErrorf("event fetch for InfluxDB failed: %v", err) + continue + } + + report, err := u.ReportMetrics(metrics, events) + if err != nil { + // XXX: reset and re-auth? not sure.. + u.LogErrorf("%v", err) + continue + } + + u.Logf("UniFi Metrics Recorded. %v", report) } } @@ -98,12 +106,11 @@ func (u *InfluxUnifi) PollController() { func (u *InfluxUnifi) Run(c poller.Collect) error { var err error - if u.Config == nil || u.Disable { - c.Logf("InfluxDB config missing (or disabled), InfluxDB output disabled!") + if u.Collector = c; u.Config == nil || u.Disable { + u.Logf("InfluxDB config missing (or disabled), InfluxDB output disabled!") return nil } - u.Collector = c u.setConfigDefaults() u.influx, err = influx.NewHTTPClient(influx.HTTPConfig{ @@ -113,9 +120,13 @@ func (u *InfluxUnifi) Run(c poller.Collect) error { TLSConfig: &tls.Config{InsecureSkipVerify: !u.VerifySSL}, // nolint: gosec }) if err != nil { - return err + return fmt.Errorf("making client: %w", err) } + fake := *u.Config + fake.Pass = strconv.FormatBool(fake.Pass != "") + + webserver.UpdateOutput(&webserver.Output{Name: PluginName, Config: fake}) u.PollController() return nil @@ -154,7 +165,7 @@ func (u *InfluxUnifi) setConfigDefaults() { func (u *InfluxUnifi) getPassFromFile(filename string) string { b, err := ioutil.ReadFile(filename) if err != nil { - u.Collector.LogErrorf("Reading InfluxDB Password File: %v", err) + u.LogErrorf("Reading InfluxDB Password File: %v", err) } return strings.TrimSpace(string(b)) @@ -163,8 +174,14 @@ func (u *InfluxUnifi) getPassFromFile(filename string) string { // ReportMetrics batches all device and client data into influxdb data points. // Call this after you've collected all the data you care about. // Returns an error if influxdb calls fail, otherwise returns a report. -func (u *InfluxUnifi) ReportMetrics(m *poller.Metrics) (*Report, error) { - r := &Report{Metrics: m, ch: make(chan *metric), Start: time.Now()} +func (u *InfluxUnifi) ReportMetrics(m *poller.Metrics, e *poller.Events) (*Report, error) { + r := &Report{ + Metrics: m, + Events: e, + ch: make(chan *metric), + Start: time.Now(), + Counts: &Counts{Val: make(map[item]int)}, + } defer close(r.ch) var err error @@ -194,7 +211,11 @@ func (u *InfluxUnifi) ReportMetrics(m *poller.Metrics) (*Report, error) { // collect runs in a go routine and batches all the points. func (u *InfluxUnifi) collect(r report, ch chan *metric) { for m := range ch { - pt, err := influx.NewPoint(m.Table, m.Tags, m.Fields, r.metrics().TS) + if m.TS.IsZero() { + m.TS = r.metrics().TS + } + + pt, err := influx.NewPoint(m.Table, m.Tags, m.Fields, m.TS) if err == nil { r.batch(m, pt) } @@ -209,12 +230,24 @@ func (u *InfluxUnifi) collect(r report, ch chan *metric) { func (u *InfluxUnifi) loopPoints(r report) { m := r.metrics() + for _, s := range m.Sites { + u.switchExport(r, s) + } + for _, s := range m.SitesDPI { u.batchSiteDPI(r, s) } - for _, s := range m.Sites { - u.batchSite(r, s) + for _, s := range m.Clients { + u.switchExport(r, s) + } + + for _, s := range m.Devices { + u.switchExport(r, s) + } + + for _, s := range r.events().Logs { + u.switchExport(r, s) } appTotal := make(totalsDPImap) @@ -225,50 +258,31 @@ func (u *InfluxUnifi) loopPoints(r report) { } reportClientDPItotals(r, appTotal, catTotal) - - for _, s := range m.Clients { - u.batchClient(r, s) - } - - for _, s := range m.IDSList { - u.batchIDS(r, s) - } - - u.loopDevicePoints(r) } -func (u *InfluxUnifi) loopDevicePoints(r report) { - m := r.metrics() - if m.Devices == nil { - m.Devices = &unifi.Devices{} - return - } - - for _, s := range m.UAPs { - u.batchUAP(r, s) - } - - for _, s := range m.USGs { - u.batchUSG(r, s) - } - - for _, s := range m.USWs { - u.batchUSW(r, s) - } - - for _, s := range m.UDMs { - u.batchUDM(r, s) +func (u *InfluxUnifi) switchExport(r report, v interface{}) { //nolint:cyclop + switch v := v.(type) { + case *unifi.UAP: + u.batchUAP(r, v) + case *unifi.USW: + u.batchUSW(r, v) + case *unifi.USG: + u.batchUSG(r, v) + case *unifi.UDM: + u.batchUDM(r, v) + case *unifi.Site: + u.batchSite(r, v) + case *unifi.Client: + u.batchClient(r, v) + case *unifi.Event: + u.batchEvent(r, v) + case *unifi.IDS: + u.batchIDS(r, v) + case *unifi.Alarm: + u.batchAlarms(r, v) + case *unifi.Anomaly: + u.batchAnomaly(r, v) + default: + u.LogErrorf("invalid export type: %T", v) } } - -// LogInfluxReport writes a log message after exporting to influxdb. -func (u *InfluxUnifi) LogInfluxReport(r *Report) { - m := r.Metrics - idsMsg := fmt.Sprintf("IDS Events: %d, ", len(m.IDSList)) - - u.Collector.Logf("UniFi Metrics Recorded. Sites: %d, Clients: %d, "+ - "UAP: %d, USG/UDM: %d, USW: %d, %sPoints: %d, Fields: %d, Errs: %d, Elapsed: %v", - len(m.Sites), len(m.Clients), len(m.UAPs), - len(m.UDMs)+len(m.USGs), len(m.USWs), idsMsg, r.Total, - r.Fields, len(r.Errors), r.Elapsed.Round(time.Millisecond)) -} diff --git a/integrations/influxunifi/logger.go b/integrations/influxunifi/logger.go new file mode 100644 index 00000000..092d5fb9 --- /dev/null +++ b/integrations/influxunifi/logger.go @@ -0,0 +1,38 @@ +package influxunifi + +import ( + "fmt" + "time" + + "github.com/unifi-poller/webserver" +) + +// Logf logs a message. +func (u *InfluxUnifi) 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 *InfluxUnifi) 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 *InfluxUnifi) 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...) +} diff --git a/integrations/influxunifi/report.go b/integrations/influxunifi/report.go index cd564334..e485c769 100644 --- a/integrations/influxunifi/report.go +++ b/integrations/influxunifi/report.go @@ -1,6 +1,7 @@ package influxunifi import ( + "fmt" "sync" "time" @@ -11,9 +12,9 @@ import ( // Report is returned to the calling procedure after everything is processed. type Report struct { Metrics *poller.Metrics + Events *poller.Events Errors []error - Total int - Fields int + Counts *Counts Start time.Time Elapsed time.Duration ch chan *metric @@ -21,7 +22,13 @@ type Report struct { bp influx.BatchPoints } -// report is an internal interface that can be mocked and overrridden for tests. +// Counts holds counters and has a lock to deal with routines. +type Counts struct { + Val map[item]int + sync.RWMutex +} + +// report is an internal interface that can be mocked and overridden for tests. type report interface { add() done() @@ -29,12 +36,18 @@ type report interface { error(err error) batch(m *metric, pt *influx.Point) metrics() *poller.Metrics + events() *poller.Events + addCount(item, ...int) } func (r *Report) metrics() *poller.Metrics { return r.Metrics } +func (r *Report) events() *poller.Events { + return r.Events +} + func (r *Report) add() { r.wg.Add(1) } @@ -50,14 +63,51 @@ func (r *Report) send(m *metric) { /* The following methods are not thread safe. */ +type item string + +func (r *Report) addCount(name item, counts ...int) { + r.Counts.Lock() + defer r.Counts.Unlock() + + if len(counts) == 0 { + r.Counts.Val[name]++ + } + + for _, c := range counts { + r.Counts.Val[name] += c + } +} + func (r *Report) error(err error) { if err != nil { r.Errors = append(r.Errors, err) } } +// These constants are used as names for printed/logged counters. +const ( + pointT = item("Point") + fieldT = item("Fields") +) + func (r *Report) batch(m *metric, p *influx.Point) { - r.Total++ - r.Fields += len(m.Fields) + r.addCount(pointT) + r.addCount(fieldT, len(m.Fields)) r.bp.AddPoint(p) } + +func (r *Report) String() string { + r.Counts.RLock() + defer r.Counts.RUnlock() + + m, c := r.Metrics, r.Counts.Val + + return fmt.Sprintf("Site: %d, Client: %d, "+ + "%s: %d, %s/%s: %d, %s: %d, %s/%s/%s/%s: %d/%d/%d/%d, "+ + "DPI Site/Client: %d/%d, %s: %d, %s: %d, Err: %d, Dur: %v", + len(m.Sites), len(m.Clients), + uapT, c[uapT], udmT, usgT, c[udmT]+c[usgT], uswT, c[uswT], + idsT, eventT, alarmT, anomalyT, c[idsT], c[eventT], c[alarmT], c[anomalyT], + len(m.SitesDPI), len(m.ClientsDPI), pointT, c[pointT], fieldT, c[fieldT], + len(r.Errors), r.Elapsed.Round(time.Millisecond)) +} diff --git a/integrations/influxunifi/site.go b/integrations/influxunifi/site.go index 5b6fbc03..671e4d7c 100644 --- a/integrations/influxunifi/site.go +++ b/integrations/influxunifi/site.go @@ -57,7 +57,13 @@ func (u *InfluxUnifi) batchSite(r report, s *unifi.Site) { } } -func (u *InfluxUnifi) batchSiteDPI(r report, s *unifi.DPITable) { +func (u *InfluxUnifi) batchSiteDPI(r report, v interface{}) { + s, ok := v.(*unifi.DPITable) + if !ok { + u.LogErrorf("invalid type given to batchSiteDPI: %T", v) + return + } + for _, dpi := range s.ByApp { r.send(&metric{ Table: "sitedpi", @@ -72,7 +78,7 @@ func (u *InfluxUnifi) batchSiteDPI(r report, s *unifi.DPITable) { "rx_packets": dpi.RxPackets, "tx_bytes": dpi.TxBytes, "rx_bytes": dpi.RxBytes, - }}, - ) + }, + }) } } diff --git a/integrations/influxunifi/uap.go b/integrations/influxunifi/uap.go index 638f36e5..69da6939 100644 --- a/integrations/influxunifi/uap.go +++ b/integrations/influxunifi/uap.go @@ -4,6 +4,9 @@ import ( "github.com/unifi-poller/unifi" ) +// uapT is used as a name for printed/logged counters. +const uapT = item("UAP") + // batchUAP generates Wireless-Access-Point datapoints for InfluxDB. // These points can be passed directly to influx. func (u *InfluxUnifi) batchUAP(r report, s *unifi.UAP) { @@ -32,6 +35,7 @@ func (u *InfluxUnifi) batchUAP(r report, s *unifi.UAP) { fields["guest-num_sta"] = int(s.GuestNumSta.Val) fields["num_sta"] = s.NumSta.Val + r.addCount(uapT) r.send(&metric{Table: "uap", Tags: tags, Fields: fields}) u.processRadTable(r, tags, s.RadioTable, s.RadioTableStats) u.processVAPTable(r, tags, s.VapTable) diff --git a/integrations/influxunifi/udm.go b/integrations/influxunifi/udm.go index 0b2c9b0b..cd34b9c9 100644 --- a/integrations/influxunifi/udm.go +++ b/integrations/influxunifi/udm.go @@ -4,6 +4,9 @@ import ( "github.com/unifi-poller/unifi" ) +// udmT is used as a name for printed/logged counters. +const udmT = item("UDM") + // Combine concatenates N maps. This will delete things if not used with caution. func Combine(in ...map[string]interface{}) map[string]interface{} { out := make(map[string]interface{}) @@ -82,6 +85,7 @@ func (u *InfluxUnifi) batchUDM(r report, s *unifi.UDM) { // nolint: funlen }, ) + r.addCount(udmT) r.send(&metric{Table: "usg", Tags: tags, Fields: fields}) u.batchNetTable(r, tags, s.NetworkTable) u.batchUSGwans(r, tags, s.Wan1, s.Wan2) diff --git a/integrations/influxunifi/usg.go b/integrations/influxunifi/usg.go index a2e9848e..5b57e9a7 100644 --- a/integrations/influxunifi/usg.go +++ b/integrations/influxunifi/usg.go @@ -4,6 +4,9 @@ import ( "github.com/unifi-poller/unifi" ) +// usgT is used as a name for printed/logged counters. +const usgT = item("USG") + // batchUSG generates Unifi Gateway datapoints for InfluxDB. // These points can be passed directly to influx. func (u *InfluxUnifi) batchUSG(r report, s *unifi.USG) { @@ -43,6 +46,7 @@ func (u *InfluxUnifi) batchUSG(r report, s *unifi.USG) { }, ) + r.addCount(usgT) r.send(&metric{Table: "usg", Tags: tags, Fields: fields}) u.batchNetTable(r, tags, s.NetworkTable) u.batchUSGwans(r, tags, s.Wan1, s.Wan2) diff --git a/integrations/influxunifi/usw.go b/integrations/influxunifi/usw.go index e6e626a3..5f3ed9cf 100644 --- a/integrations/influxunifi/usw.go +++ b/integrations/influxunifi/usw.go @@ -4,6 +4,9 @@ import ( "github.com/unifi-poller/unifi" ) +// uswT is used as a name for printed/logged counters. +const uswT = item("USW") + // batchUSW generates Unifi Switch datapoints for InfluxDB. // These points can be passed directly to influx. func (u *InfluxUnifi) batchUSW(r report, s *unifi.USW) { @@ -38,6 +41,7 @@ func (u *InfluxUnifi) batchUSW(r report, s *unifi.USW) { "user-num_sta": s.UserNumSta.Val, }) + r.addCount(uswT) r.send(&metric{Table: "usw", Tags: tags, Fields: fields}) u.batchPortTable(r, tags, s.PortTable) }