all stats should be reporting
This commit is contained in:
parent
ceeb63870e
commit
51916c19e7
|
|
@ -4,3 +4,165 @@ import (
|
|||
"github.com/unifi-poller/unifi"
|
||||
)
|
||||
|
||||
// reportClient generates Unifi Client datapoints for InfluxDB.
|
||||
// These points can be passed directly to influx.
|
||||
func (u *DatadogUnifi) reportClient(r report, s *unifi.Client) { // nolint: funlen
|
||||
tags := []string{
|
||||
tag("mac", s.Mac),
|
||||
tag("site_name", s.SiteName),
|
||||
tag("source", s.SourceName),
|
||||
tag("ap_name", s.ApName),
|
||||
tag("gw_name", s.GwName),
|
||||
tag("sw_name", s.SwName),
|
||||
tag("oui", s.Oui),
|
||||
tag("radio_name", s.RadioName),
|
||||
tag("radio", s.Radio),
|
||||
tag("radio_proto", s.RadioProto),
|
||||
tag("name", s.Name),
|
||||
tag("fixed_ip", s.FixedIP),
|
||||
tag("sw_port", s.SwPort.Txt),
|
||||
tag("os_class", s.OsClass.Txt),
|
||||
tag("os_name", s.OsName.Txt),
|
||||
tag("dev_cat", s.DevCat.Txt),
|
||||
tag("dev_id", s.DevID.Txt),
|
||||
tag("dev_vendor", s.DevVendor.Txt),
|
||||
tag("dev_family", s.DevFamily.Txt),
|
||||
tag("is_wired", s.IsWired.Txt),
|
||||
tag("is_guest", s.IsGuest.Txt),
|
||||
tag("use_fixedip", s.UseFixedIP.Txt),
|
||||
tag("channel", s.Channel.Txt),
|
||||
tag("vlan", s.Vlan.Txt),
|
||||
tag("hostname", s.Name),
|
||||
tag("radio_desc", s.RadioDescription),
|
||||
tag("ip", s.IP),
|
||||
tag("essid", s.Essid),
|
||||
tag("bssid", s.Bssid),
|
||||
}
|
||||
|
||||
data := map[string]float64{
|
||||
"anomalies": float64(s.Anomalies),
|
||||
"channel": s.Channel.Val,
|
||||
"satisfaction": s.Satisfaction.Val,
|
||||
"bytes_r": float64(s.BytesR),
|
||||
"ccq": float64(s.Ccq),
|
||||
"noise": float64(s.Noise),
|
||||
"roam_count": float64(s.RoamCount),
|
||||
"rssi": float64(s.Rssi),
|
||||
"rx_bytes": float64(s.RxBytes),
|
||||
"rx_bytes_r": float64(s.RxBytesR),
|
||||
"rx_packets": float64(s.RxPackets),
|
||||
"rx_rate": float64(s.RxRate),
|
||||
"signal": float64(s.Signal),
|
||||
"tx_bytes": float64(s.TxBytes),
|
||||
"tx_bytes_r": float64(s.TxBytesR),
|
||||
"tx_packets": float64(s.TxPackets),
|
||||
"tx_retries": float64(s.TxRetries),
|
||||
"tx_power": float64(s.TxPower),
|
||||
"tx_rate": float64(s.TxRate),
|
||||
"uptime": float64(s.Uptime),
|
||||
"wifi_tx_attempts": float64(s.WifiTxAttempts),
|
||||
"wired-rx_bytes": float64(s.WiredRxBytes),
|
||||
"wired-rx_bytes-r": float64(s.WiredRxBytesR),
|
||||
"wired-rx_packets": float64(s.WiredRxPackets),
|
||||
"wired-tx_bytes": float64(s.WiredTxBytes),
|
||||
"wired-tx_bytes-r": float64(s.WiredTxBytesR),
|
||||
"wired-tx_packets": float64(s.WiredTxPackets),
|
||||
}
|
||||
metricName := metricNamespace("clients")
|
||||
reportGaugeForMap(r, metricName, data, tags)
|
||||
}
|
||||
|
||||
// totalsDPImap: controller, site, name (app/cat name), dpi.
|
||||
type totalsDPImap map[string]map[string]map[string]unifi.DPIData
|
||||
|
||||
func (u *DatadogUnifi) reportClientDPI(r report, s *unifi.DPITable, appTotal, catTotal totalsDPImap) {
|
||||
for _, dpi := range s.ByApp {
|
||||
category := unifi.DPICats.Get(dpi.Cat)
|
||||
application := unifi.DPIApps.GetApp(dpi.Cat, dpi.App)
|
||||
fillDPIMapTotals(appTotal, application, s.SourceName, s.SiteName, dpi)
|
||||
fillDPIMapTotals(catTotal, category, s.SourceName, s.SiteName, dpi)
|
||||
|
||||
tags := []string{
|
||||
tag("category", category),
|
||||
tag("application", application),
|
||||
tag("name", s.Name),
|
||||
tag("mac", s.MAC),
|
||||
tag("site_name", s.SiteName),
|
||||
tag("source", s.SourceName),
|
||||
}
|
||||
data := map[string]float64{
|
||||
"tx_packets": float64(dpi.TxPackets),
|
||||
"rx_packets": float64(dpi.RxPackets),
|
||||
"tx_bytes": float64(dpi.TxBytes),
|
||||
"rx_bytes": float64(dpi.RxBytes),
|
||||
}
|
||||
metricName := metricNamespace("clientdpi")
|
||||
reportGaugeForMap(r, metricName, data, tags)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 m[controller] == nil {
|
||||
m[controller] = make(map[string]map[string]unifi.DPIData)
|
||||
}
|
||||
|
||||
if m[controller][site] == nil {
|
||||
m[controller][site] = make(map[string]unifi.DPIData)
|
||||
}
|
||||
|
||||
existing := m[controller][site][name]
|
||||
existing.TxPackets += dpi.TxPackets
|
||||
existing.RxPackets += dpi.RxPackets
|
||||
existing.TxBytes += dpi.TxBytes
|
||||
existing.RxBytes += dpi.RxBytes
|
||||
m[controller][site][name] = existing
|
||||
}
|
||||
|
||||
func reportClientDPItotals(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{
|
||||
// This produces 7000+ metrics per site. Disabled for now.
|
||||
{
|
||||
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 {
|
||||
tags := []string{
|
||||
tag("site_name", site),
|
||||
tag("source", controller),
|
||||
tag("name", name),
|
||||
}
|
||||
data := map[string]float64{
|
||||
"tx_packets": float64(m.TxPackets),
|
||||
"rx_packets": float64(m.RxPackets),
|
||||
"tx_bytes": float64(m.TxBytes),
|
||||
"rx_bytes": float64(m.RxBytes),
|
||||
}
|
||||
metricName := metricNamespace("clientdpi.totals")
|
||||
reportGaugeForMap(r, metricName, data, tags)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,35 +3,35 @@
|
|||
package datadogunifi
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/DataDog/datadog-go/statsd"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/unifi-poller/poller"
|
||||
"github.com/unifi-poller/unifi"
|
||||
"golift.io/cnfg"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
defaultInterval = 30 * time.Second
|
||||
minimumInterval = 10 * time.Second
|
||||
)
|
||||
|
||||
// Config defines the data needed to store metrics in Datadog.
|
||||
type Config struct {
|
||||
// Required Config
|
||||
|
||||
// Interval controls the collection and reporting interval
|
||||
Interval cnfg.Duration `json:"interval,omitempty" toml:"interval,omitempty" xml:"interval,omitempty" yaml:"interval,omitempty"`
|
||||
|
||||
// Disable when true disables this output plugin
|
||||
Disable bool `json:"disable" toml:"disable" xml:"disable,attr" yaml:"disable"`
|
||||
// Address determines how to talk to the Datadog agent
|
||||
Address string `json:"address" toml:"address" xml:"address,attr" yaml:"address"`
|
||||
|
||||
Address string `json:"address" toml:"address" xml:"address,attr" yaml:"address"`
|
||||
|
||||
// Optional Statsd Options - mirrored from statsd.Options
|
||||
|
||||
|
||||
// Namespace to prepend to all metrics, events and service checks name.
|
||||
Namespace *string `json:"namespace" toml:"namespace" xml:"namespace,attr" yaml:"namespace"`
|
||||
// Tags are global tags to be applied to every metrics, events and service checks.
|
||||
|
|
@ -48,7 +48,7 @@ type Config struct {
|
|||
// protocol used when creating the client: 2048 for UDP and 512 for UDS.
|
||||
BufferPoolSize *int `json:"buffer_pool_size" toml:"buffer_pool_size" xml:"buffer_pool_size,attr" yaml:"buffer_pool_size"`
|
||||
// BufferFlushInterval is the interval after which the current buffer will get flushed.
|
||||
BufferFlushInterval *time.Duration `json:"buffer_flush_interval" toml:"buffer_flush_interval" xml:"buffer_flush_interval,attr" yaml:"buffer_flush_interval"`
|
||||
BufferFlushInterval *cnfg.Duration `json:"buffer_flush_interval" toml:"buffer_flush_interval" xml:"buffer_flush_interval,attr" yaml:"buffer_flush_interval"`
|
||||
// BufferShardCount is the number of buffer "shards" that will be used.
|
||||
// Those shards allows the use of multiple buffers at the same time to reduce
|
||||
// lock contention.
|
||||
|
|
@ -58,10 +58,7 @@ type Config struct {
|
|||
// protocol used when creating the client: 2048 for UDP and 512 for UDS.
|
||||
SenderQueueSize *int `json:"sender_queue_size" toml:"sender_queue_size" xml:"sender_queue_size,attr" yaml:"sender_queue_size"`
|
||||
// WriteTimeoutUDS is the timeout after which a UDS packet is dropped.
|
||||
WriteTimeoutUDS *time.Duration `json:"write_timeout_uds" toml:"write_timeout_uds" xml:"write_timeout_uds,attr" yaml:"write_timeout_uds"`
|
||||
// Telemetry is a set of metrics automatically injected by the client in the
|
||||
// dogstatsd stream to be able to monitor the client itself.
|
||||
Telemetry *bool `json:"telemetry" toml:"telemetry" xml:"telemetry,attr" yaml:"telemetry"`
|
||||
WriteTimeoutUDS *cnfg.Duration `json:"write_timeout_uds" toml:"write_timeout_uds" xml:"write_timeout_uds,attr" yaml:"write_timeout_uds"`
|
||||
// ReceiveMode determins the behavior of the client when receiving to many
|
||||
// metrics. The client will either drop the metrics if its buffers are
|
||||
// full (ChannelMode mode) or block the caller until the metric can be
|
||||
|
|
@ -86,10 +83,6 @@ type Config struct {
|
|||
ChannelModeBufferSize *int `json:"channel_mode_buffer_size" toml:"channel_mode_buffer_size" xml:"channel_mode_buffer_size,attr" yaml:"channel_mode_buffer_size"`
|
||||
// AggregationFlushInterval is the interval for the aggregator to flush metrics
|
||||
AggregationFlushInterval *time.Duration `json:"aggregation_flush_interval" toml:"aggregation_flush_interval" xml:"aggregation_flush_interval,attr" yaml:"aggregation_flush_interval"`
|
||||
// [beta] Aggregation enables/disables client side aggregation
|
||||
Aggregation *bool `json:"aggregation" toml:"aggregation" xml:"aggregation,attr" yaml:"aggregation"`
|
||||
// TelemetryAddr specify a different endpoint for telemetry metrics.
|
||||
TelemetryAddr *string `json:"telemetry_address" toml:"telemetry_address" xml:"telemetry_address,attr" yaml:"telemetry_address"`
|
||||
}
|
||||
|
||||
// Datadog allows the data to be context aware with configuration
|
||||
|
|
@ -101,22 +94,16 @@ type Datadog struct {
|
|||
// DatadogUnifi is returned by New() after you provide a Config.
|
||||
type DatadogUnifi struct {
|
||||
Collector poller.Collect
|
||||
datadog statsd.ClientInterface
|
||||
datadog statsd.ClientInterface
|
||||
LastCheck time.Time
|
||||
*Datadog
|
||||
}
|
||||
|
||||
type metric struct {
|
||||
Name string
|
||||
Tags map[string]string
|
||||
Fields map[string]interface{}
|
||||
}
|
||||
|
||||
func init() { // nolint: gochecknoinits
|
||||
func init() { // nolint: gochecknoinits
|
||||
u := &DatadogUnifi{Datadog: &Datadog{}, LastCheck: time.Now()}
|
||||
|
||||
poller.NewOutput(&poller.Output{
|
||||
Name: "datadog",
|
||||
Name: "datadog",
|
||||
Config: u.Datadog,
|
||||
Method: u.Run,
|
||||
})
|
||||
|
|
@ -131,17 +118,62 @@ func (u *DatadogUnifi) setConfigDefaults() {
|
|||
|
||||
u.Interval = cnfg.Duration{Duration: u.Interval.Duration.Round(time.Second)}
|
||||
|
||||
u.options = make([]statsd.Option)
|
||||
u.options = make([]statsd.Option, 0)
|
||||
|
||||
if u.Namespace != nil {
|
||||
u.options = append(u.options, statsd.WithNamespace(*u.Namespace))
|
||||
}
|
||||
|
||||
if u.Tags != nil && len(u.Tags) > 0 {
|
||||
u.options = append(u.options, statsd.WithTags(u.Tags))
|
||||
}
|
||||
|
||||
}
|
||||
if u.MaxBytesPerPayload != nil {
|
||||
u.options = append(u.options, statsd.WithMaxBytesPerPayload(*u.MaxBytesPerPayload))
|
||||
}
|
||||
|
||||
if u.MaxMessagesPerPayload != nil {
|
||||
u.options = append(u.options, statsd.WithMaxMessagesPerPayload(*u.MaxMessagesPerPayload))
|
||||
}
|
||||
|
||||
if u.BufferPoolSize != nil {
|
||||
u.options = append(u.options, statsd.WithBufferPoolSize(*u.BufferPoolSize))
|
||||
}
|
||||
|
||||
if u.BufferFlushInterval != nil {
|
||||
u.options = append(u.options, statsd.WithBufferFlushInterval((*u.BufferFlushInterval).Duration))
|
||||
}
|
||||
|
||||
if u.BufferShardCount != nil {
|
||||
u.options = append(u.options, statsd.WithBufferShardCount(*u.BufferShardCount))
|
||||
}
|
||||
|
||||
if u.SenderQueueSize != nil {
|
||||
u.options = append(u.options, statsd.WithSenderQueueSize(*u.SenderQueueSize))
|
||||
}
|
||||
|
||||
if u.WriteTimeoutUDS != nil {
|
||||
u.options = append(u.options, statsd.WithWriteTimeoutUDS((*u.WriteTimeoutUDS).Duration))
|
||||
}
|
||||
|
||||
if u.ReceiveMode != nil {
|
||||
switch *u.ReceiveMode {
|
||||
case statsd.ChannelMode:
|
||||
u.options = append(u.options, statsd.WithChannelMode())
|
||||
case statsd.MutexMode:
|
||||
u.options = append(u.options, statsd.WithMutexMode())
|
||||
}
|
||||
}
|
||||
|
||||
if u.ChannelModeBufferSize != nil {
|
||||
u.options = append(u.options, statsd.WithChannelModeBufferSize(*u.ChannelModeBufferSize))
|
||||
}
|
||||
|
||||
if u.AggregationFlushInterval != nil {
|
||||
u.options = append(u.options, statsd.WithAggregationInterval(*u.AggregationFlushInterval))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Run runs a ticker to poll the unifi server and update Datadog.
|
||||
func (u *DatadogUnifi) Run(c poller.Collect) error {
|
||||
|
|
@ -153,18 +185,18 @@ func (u *DatadogUnifi) Run(c poller.Collect) error {
|
|||
u.Collector = c
|
||||
u.setConfigDefaults()
|
||||
|
||||
u.datadog, err := statsd.New(u.Address, u.options...)
|
||||
var err error
|
||||
u.datadog, err = statsd.New(u.Address, u.options...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u.PollController()
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// PollController runs fover, polling UniFi and pushing to Datadog
|
||||
// PollController runs forever, polling UniFi and pushing to Datadog
|
||||
// This is started by Run() or RunBoth() after everything is validated.
|
||||
func (u *DatadogUnifi) PollController() {
|
||||
interval := u.Interval.Round(time.Second)
|
||||
|
|
@ -197,53 +229,41 @@ func (u *DatadogUnifi) PollController() {
|
|||
// Call this after you've collected all the data you care about.
|
||||
// Returns an error if datadog statsd calls fail, otherwise returns a report.
|
||||
func (u *DatadogUnifi) ReportMetrics(m *poller.Metrics) (*Report, error) {
|
||||
r := &Report{Metrics: m, ch: make(chan *metric), Start: time.Now()}
|
||||
|
||||
r := &Report{Metrics: m, Start: time.Now()}
|
||||
// batch all the points.
|
||||
u.loopPoints(r)
|
||||
r.End = time.Now()
|
||||
r.Elapsed = r.End.Sub(r.Start)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
|
||||
// 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 err == nil {
|
||||
r.batch(m, pt)
|
||||
}
|
||||
|
||||
r.error(err)
|
||||
r.done()
|
||||
}
|
||||
}
|
||||
|
||||
// loopPoints kicks off 3 or 7 go routines to process metrics and send them
|
||||
// to the collect routine through the metric channel.
|
||||
// loopPoints collects all the data to immediately report to Datadog.
|
||||
func (u *DatadogUnifi) loopPoints(r report) {
|
||||
m := r.metrics()
|
||||
|
||||
for _, s := range m.SitesDPI {
|
||||
u.batchSiteDPI(r, s)
|
||||
u.reportSiteDPI(r, s)
|
||||
}
|
||||
|
||||
for _, s := range m.Sites {
|
||||
u.batchSite(r, s)
|
||||
u.reportSite(r, s)
|
||||
}
|
||||
|
||||
appTotal := make(totalsDPImap)
|
||||
catTotal := make(totalsDPImap)
|
||||
|
||||
for _, s := range m.ClientsDPI {
|
||||
u.batchClientDPI(r, s, appTotal, catTotal)
|
||||
u.reportClientDPI(r, s, appTotal, catTotal)
|
||||
}
|
||||
|
||||
reportClientDPItotals(r, appTotal, catTotal)
|
||||
|
||||
for _, s := range m.Clients {
|
||||
u.batchClient(r, s)
|
||||
u.reportClient(r, s)
|
||||
}
|
||||
|
||||
for _, s := range m.IDSList {
|
||||
u.batchIDS(r, s)
|
||||
u.reportIDS(r, s)
|
||||
}
|
||||
|
||||
u.loopDevicePoints(r)
|
||||
|
|
@ -257,19 +277,19 @@ func (u *DatadogUnifi) loopDevicePoints(r report) {
|
|||
}
|
||||
|
||||
for _, s := range m.UAPs {
|
||||
u.batchUAP(r, s)
|
||||
u.reportUAP(r, s)
|
||||
}
|
||||
|
||||
for _, s := range m.USGs {
|
||||
u.batchUSG(r, s)
|
||||
u.reportUSG(r, s)
|
||||
}
|
||||
|
||||
for _, s := range m.USWs {
|
||||
u.batchUSW(r, s)
|
||||
u.reportUSW(r, s)
|
||||
}
|
||||
|
||||
for _, s := range m.UDMs {
|
||||
u.batchUDM(r, s)
|
||||
u.reportUDM(r, s)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -277,7 +297,6 @@ func (u *DatadogUnifi) loopDevicePoints(r report) {
|
|||
func (u *DatadogUnifi) LogDatadogReport(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),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package datadogunifi
|
||||
|
||||
import (
|
||||
"github.com/unifi-poller/unifi"
|
||||
)
|
||||
|
||||
// reportIDS generates intrusion detection datapoints for Datadog.
|
||||
// These points can be passed directly to datadog.
|
||||
func (u *DatadogUnifi) reportIDS(r report, i *unifi.IDS) {
|
||||
tags := []string{
|
||||
tag("site_name", i.SiteName),
|
||||
tag("source", i.SourceName),
|
||||
tag("in_iface", i.InIface),
|
||||
tag("event_type", i.EventType),
|
||||
tag("proto", i.Proto),
|
||||
tag("app_proto", i.AppProto),
|
||||
tag("usgip", i.USGIP),
|
||||
tag("country_code", i.SourceIPGeo.CountryCode),
|
||||
tag("country_name", i.SourceIPGeo.CountryName),
|
||||
tag("city", i.SourceIPGeo.City),
|
||||
tag("organization", i.SourceIPGeo.Organization),
|
||||
tag("srcipASN", i.SrcIPASN),
|
||||
tag("usgipASN", i.USGIPASN),
|
||||
tag("alert_category", i.InnerAlertCategory),
|
||||
tag("subsystem", i.Subsystem),
|
||||
tag("catname", i.Catname),
|
||||
}
|
||||
|
||||
metricName := metricNamespace("intrusion_detect")
|
||||
r.reportCount(metricName("count"), 1, tags)
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package datadogunifi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func tag(name string, value interface{}) string {
|
||||
return fmt.Sprintf("%s:%v", name, value)
|
||||
}
|
||||
|
||||
func metricNamespace(namespace string) func(string) string {
|
||||
return func(name string) string {
|
||||
return fmt.Sprintf("%s.%s", namespace, name)
|
||||
}
|
||||
}
|
||||
|
||||
func reportGaugeForMap(r report, metricName func(string) string, data map[string]float64, tags []string) {
|
||||
for name, value := range data {
|
||||
r.reportGauge(metricName(name), value, tags)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package datadogunifi
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/DataDog/datadog-go/statsd"
|
||||
"github.com/unifi-poller/poller"
|
||||
)
|
||||
|
||||
type Report struct {
|
||||
Metrics *poller.Metrics
|
||||
Errors []error
|
||||
Total int
|
||||
Fields int
|
||||
Start time.Time
|
||||
End time.Time
|
||||
Elapsed time.Duration
|
||||
|
||||
client statsd.ClientInterface
|
||||
}
|
||||
|
||||
type report interface {
|
||||
error(err error)
|
||||
metrics() *poller.Metrics
|
||||
reportGauge(name string, value float64, tags []string) error
|
||||
reportCount(name string, value int64, tags []string) error
|
||||
reportDistribution(name string, value float64, tags []string) error
|
||||
reportTiming(name string, value time.Duration, tags []string) error
|
||||
reportEvent(title string, message string, tags []string) error
|
||||
reportServiceCheck(name string, status statsd.ServiceCheckStatus, message string, tags []string) error
|
||||
}
|
||||
|
||||
func (r *Report) metrics() *poller.Metrics {
|
||||
return r.Metrics
|
||||
}
|
||||
|
||||
func (r *Report) error(err error) {
|
||||
if err != nil {
|
||||
r.Errors = append(r.Errors, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Report) reportGauge(name string, value float64, tags []string) error {
|
||||
return r.client.Gauge(name, value, tags, 1.0)
|
||||
}
|
||||
|
||||
func (r *Report) reportCount(name string, value int64, tags []string) error {
|
||||
return r.client.Count(name, value, tags, 1.0)
|
||||
}
|
||||
|
||||
func (r *Report) reportDistribution(name string, value float64, tags []string) error {
|
||||
return r.client.Distribution(name, value, tags, 1.0)
|
||||
}
|
||||
|
||||
func (r *Report) reportTiming(name string, value time.Duration, tags []string) error {
|
||||
return r.client.Timing(name, value, tags, 1.0)
|
||||
}
|
||||
|
||||
func (r *Report) reportEvent(title string, message string, tags []string) error {
|
||||
return r.client.Event(&statsd.Event{
|
||||
Title: title,
|
||||
Text: message,
|
||||
Timestamp: time.Now(),
|
||||
Tags: tags,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Report) reportServiceCheck(name string, status statsd.ServiceCheckStatus, message string, tags []string) error {
|
||||
return r.client.ServiceCheck(&statsd.ServiceCheck{
|
||||
Name: name,
|
||||
Status: status,
|
||||
Timestamp: time.Now(),
|
||||
Message: message,
|
||||
Tags: tags,
|
||||
})
|
||||
}
|
||||
|
|
@ -4,5 +4,77 @@ import (
|
|||
"github.com/unifi-poller/unifi"
|
||||
)
|
||||
|
||||
// batchSite generates Unifi Sites' datapoints for Datadog.
|
||||
// These points can be passed directly to datadog
|
||||
// reportSite generates Unifi Sites' datapoints for Datadog.
|
||||
// These points can be passed directly to Datadog.
|
||||
func (u *DatadogUnifi) reportSite(r report, s *unifi.Site) {
|
||||
metricName := metricNamespace("subsystems")
|
||||
|
||||
for _, h := range s.Health {
|
||||
tags := []string{
|
||||
tag("name", s.Name),
|
||||
tag("site_name", s.SiteName),
|
||||
tag("source", s.SourceName),
|
||||
tag("desc", s.Desc),
|
||||
tag("status", h.Status),
|
||||
tag("subsystem", h.Subsystem),
|
||||
tag("wan_ip", h.WanIP),
|
||||
tag("gw_name", h.GwName),
|
||||
tag("lan_ip", h.LanIP),
|
||||
}
|
||||
|
||||
data := map[string]float64{
|
||||
"num_user": h.NumUser.Val,
|
||||
"num_guest": h.NumGuest.Val,
|
||||
"num_iot": h.NumIot.Val,
|
||||
"tx_bytes-r": h.TxBytesR.Val,
|
||||
"rx_bytes-r": h.RxBytesR.Val,
|
||||
"num_ap": h.NumAp.Val,
|
||||
"num_adopted": h.NumAdopted.Val,
|
||||
"num_disabled": h.NumDisabled.Val,
|
||||
"num_disconnected": h.NumDisconnected.Val,
|
||||
"num_pending": h.NumPending.Val,
|
||||
"num_gw": h.NumGw.Val,
|
||||
"num_sta": h.NumSta.Val,
|
||||
"gw_cpu": h.GwSystemStats.CPU.Val,
|
||||
"gw_mem": h.GwSystemStats.Mem.Val,
|
||||
"gw_uptime": h.GwSystemStats.Uptime.Val,
|
||||
"latency": h.Latency.Val,
|
||||
"uptime": h.Uptime.Val,
|
||||
"drops": h.Drops.Val,
|
||||
"xput_up": h.XputUp.Val,
|
||||
"xput_down": h.XputDown.Val,
|
||||
"speedtest_ping": h.SpeedtestPing.Val,
|
||||
"speedtest_lastrun": h.SpeedtestLastrun.Val,
|
||||
"num_sw": h.NumSw.Val,
|
||||
"remote_user_num_active": h.RemoteUserNumActive.Val,
|
||||
"remote_user_num_inactive": h.RemoteUserNumInactive.Val,
|
||||
"remote_user_rx_bytes": h.RemoteUserRxBytes.Val,
|
||||
"remote_user_tx_bytes": h.RemoteUserTxBytes.Val,
|
||||
"remote_user_rx_packets": h.RemoteUserRxPackets.Val,
|
||||
"remote_user_tx_packets": h.RemoteUserTxPackets.Val,
|
||||
"num_new_alarms": s.NumNewAlarms.Val,
|
||||
}
|
||||
|
||||
for name, value := range data {
|
||||
r.reportGauge(metricName(name), value, tags)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *DatadogUnifi) reportSiteDPI(r report, s *unifi.DPITable) {
|
||||
for _, dpi := range s.ByApp {
|
||||
metricName := metricNamespace("sitedpi")
|
||||
|
||||
tags := []string{
|
||||
tag("category", unifi.DPICats.Get(dpi.Cat)),
|
||||
tag("application", unifi.DPIApps.GetApp(dpi.Cat, dpi.App)),
|
||||
tag("site_name", s.SiteName),
|
||||
tag("source", s.SourceName),
|
||||
}
|
||||
|
||||
r.reportCount(metricName("tx_packets"), dpi.TxPackets, tags)
|
||||
r.reportCount(metricName("rx_packets"), dpi.RxPackets, tags)
|
||||
r.reportCount(metricName("tx_bytes"), dpi.TxBytes, tags)
|
||||
r.reportCount(metricName("rx_bytes"), dpi.RxBytes, tags)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,199 @@
|
|||
package datadogunifi
|
||||
|
||||
import (
|
||||
"github.com/unifi-poller/unifi"
|
||||
)
|
||||
|
||||
// reportUAP generates Wireless-Access-Point datapoints for InfluxDB.
|
||||
// These points can be passed directly to influx.
|
||||
func (u *DatadogUnifi) reportUAP(r report, s *unifi.UAP) {
|
||||
if !s.Adopted.Val || s.Locating.Val {
|
||||
return
|
||||
}
|
||||
|
||||
tags := []string{
|
||||
tag("ip", s.IP),
|
||||
tag("mac", s.Mac),
|
||||
tag("site_name", s.SiteName),
|
||||
tag("source", s.SourceName),
|
||||
tag("name", s.Name),
|
||||
tag("version", s.Version),
|
||||
tag("model", s.Model),
|
||||
tag("serial", s.Serial),
|
||||
tag("type", s.Type),
|
||||
}
|
||||
|
||||
metricName := metricNamespace("uap")
|
||||
|
||||
u.reportUAPstats(s.Stat.Ap, r, metricName, tags)
|
||||
u.reportSysStats(r, metricName, s.SysStats, s.SystemStats, tags)
|
||||
|
||||
data := map[string]float64{
|
||||
"bytes": s.Bytes.Val,
|
||||
"last_seen": s.LastSeen.Val,
|
||||
"rx_bytes": s.RxBytes.Val,
|
||||
"tx_bytes": s.TxBytes.Val,
|
||||
"uptime": s.Uptime.Val,
|
||||
"user-num_sta": s.UserNumSta.Val,
|
||||
"guest-num_sta": s.GuestNumSta.Val,
|
||||
"num_sta": s.NumSta.Val,
|
||||
}
|
||||
reportGaugeForMap(r, metricName, data, tags)
|
||||
|
||||
u.reportRadTable(r, s.Name, s.SiteName, s.SourceName, s.RadioTable, s.RadioTableStats)
|
||||
u.reportVAPTable(r, s.Name, s.SiteName, s.SourceName, s.VapTable)
|
||||
u.reportPortTable(r, s.Name, s.SiteName, s.SourceName, s.Type, s.PortTable)
|
||||
}
|
||||
|
||||
func (u *DatadogUnifi) reportUAPstats(ap *unifi.Ap, r report, metricName func(string) string, tags []string) {
|
||||
if ap == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Accumulative Statistics.
|
||||
data := map[string]float64{
|
||||
"stat_user-rx_packets": ap.UserRxPackets.Val,
|
||||
"stat_guest-rx_packets": ap.GuestRxPackets.Val,
|
||||
"stat_rx_packets": ap.RxPackets.Val,
|
||||
"stat_user-rx_bytes": ap.UserRxBytes.Val,
|
||||
"stat_guest-rx_bytes": ap.GuestRxBytes.Val,
|
||||
"stat_rx_bytes": ap.RxBytes.Val,
|
||||
"stat_user-rx_errors": ap.UserRxErrors.Val,
|
||||
"stat_guest-rx_errors": ap.GuestRxErrors.Val,
|
||||
"stat_rx_errors": ap.RxErrors.Val,
|
||||
"stat_user-rx_dropped": ap.UserRxDropped.Val,
|
||||
"stat_guest-rx_dropped": ap.GuestRxDropped.Val,
|
||||
"stat_rx_dropped": ap.RxDropped.Val,
|
||||
"stat_user-rx_crypts": ap.UserRxCrypts.Val,
|
||||
"stat_guest-rx_crypts": ap.GuestRxCrypts.Val,
|
||||
"stat_rx_crypts": ap.RxCrypts.Val,
|
||||
"stat_user-rx_frags": ap.UserRxFrags.Val,
|
||||
"stat_guest-rx_frags": ap.GuestRxFrags.Val,
|
||||
"stat_rx_frags": ap.RxFrags.Val,
|
||||
"stat_user-tx_packets": ap.UserTxPackets.Val,
|
||||
"stat_guest-tx_packets": ap.GuestTxPackets.Val,
|
||||
"stat_tx_packets": ap.TxPackets.Val,
|
||||
"stat_user-tx_bytes": ap.UserTxBytes.Val,
|
||||
"stat_guest-tx_bytes": ap.GuestTxBytes.Val,
|
||||
"stat_tx_bytes": ap.TxBytes.Val,
|
||||
"stat_user-tx_errors": ap.UserTxErrors.Val,
|
||||
"stat_guest-tx_errors": ap.GuestTxErrors.Val,
|
||||
"stat_tx_errors": ap.TxErrors.Val,
|
||||
"stat_user-tx_dropped": ap.UserTxDropped.Val,
|
||||
"stat_guest-tx_dropped": ap.GuestTxDropped.Val,
|
||||
"stat_tx_dropped": ap.TxDropped.Val,
|
||||
"stat_user-tx_retries": ap.UserTxRetries.Val,
|
||||
"stat_guest-tx_retries": ap.GuestTxRetries.Val,
|
||||
}
|
||||
reportGaugeForMap(r, metricName, data, tags)
|
||||
}
|
||||
|
||||
// reportVAPTable creates points for Wifi Radios. This works with several types of UAP-capable devices.
|
||||
func (u *DatadogUnifi) reportVAPTable(r report, deviceName string, siteName string, source string, vt unifi.VapTable) { // nolint: funlen
|
||||
for _, s := range vt {
|
||||
tags := []string{
|
||||
tag("device_name", deviceName),
|
||||
tag("site_name", siteName),
|
||||
tag("source", source),
|
||||
tag("ap_mac", s.ApMac),
|
||||
tag("bssid", s.Bssid),
|
||||
tag("id", s.ID),
|
||||
tag("name", s.Name),
|
||||
tag("radio_name", s.RadioName),
|
||||
tag("radio", s.Radio),
|
||||
tag("essid", s.Essid),
|
||||
tag("site_id", s.SiteID),
|
||||
tag("usage", s.Usage),
|
||||
tag("state", s.State),
|
||||
tag("is_guest", s.IsGuest.Txt),
|
||||
}
|
||||
data := map[string]float64{
|
||||
"ccq": float64(s.Ccq),
|
||||
"mac_filter_rejections": float64(s.MacFilterRejections),
|
||||
"num_satisfaction_sta": s.NumSatisfactionSta.Val,
|
||||
"avg_client_signal": s.AvgClientSignal.Val,
|
||||
"satisfaction": s.Satisfaction.Val,
|
||||
"satisfaction_now": s.SatisfactionNow.Val,
|
||||
"num_sta": float64(s.NumSta),
|
||||
"channel": s.Channel.Val,
|
||||
"rx_bytes": s.RxBytes.Val,
|
||||
"rx_crypts": s.RxCrypts.Val,
|
||||
"rx_dropped": s.RxDropped.Val,
|
||||
"rx_errors": s.RxErrors.Val,
|
||||
"rx_frags": s.RxFrags.Val,
|
||||
"rx_nwids": s.RxNwids.Val,
|
||||
"rx_packets": s.RxPackets.Val,
|
||||
"tx_bytes": s.TxBytes.Val,
|
||||
"tx_dropped": s.TxDropped.Val,
|
||||
"tx_errors": s.TxErrors.Val,
|
||||
"tx_packets": s.TxPackets.Val,
|
||||
"tx_power": s.TxPower.Val,
|
||||
"tx_retries": s.TxRetries.Val,
|
||||
"tx_combined_retries": s.TxCombinedRetries.Val,
|
||||
"tx_data_mpdu_bytes": s.TxDataMpduBytes.Val,
|
||||
"tx_rts_retries": s.TxRtsRetries.Val,
|
||||
"tx_success": s.TxSuccess.Val,
|
||||
"tx_total": s.TxTotal.Val,
|
||||
"tx_tcp_goodbytes": s.TxTCPStats.Goodbytes.Val,
|
||||
"tx_tcp_lat_avg": s.TxTCPStats.LatAvg.Val,
|
||||
"tx_tcp_lat_max": s.TxTCPStats.LatMax.Val,
|
||||
"tx_tcp_lat_min": s.TxTCPStats.LatMin.Val,
|
||||
"rx_tcp_goodbytes": s.RxTCPStats.Goodbytes.Val,
|
||||
"rx_tcp_lat_avg": s.RxTCPStats.LatAvg.Val,
|
||||
"rx_tcp_lat_max": s.RxTCPStats.LatMax.Val,
|
||||
"rx_tcp_lat_min": s.RxTCPStats.LatMin.Val,
|
||||
"wifi_tx_latency_mov_avg": s.WifiTxLatencyMov.Avg.Val,
|
||||
"wifi_tx_latency_mov_max": s.WifiTxLatencyMov.Max.Val,
|
||||
"wifi_tx_latency_mov_min": s.WifiTxLatencyMov.Min.Val,
|
||||
"wifi_tx_latency_mov_total": s.WifiTxLatencyMov.Total.Val,
|
||||
"wifi_tx_latency_mov_cuont": s.WifiTxLatencyMov.TotalCount.Val,
|
||||
}
|
||||
|
||||
metricName := metricNamespace("uap_vaps")
|
||||
reportGaugeForMap(r, metricName, data, tags)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *DatadogUnifi) reportRadTable(r report, deviceName string, siteName string, source string, rt unifi.RadioTable, rts unifi.RadioTableStats) {
|
||||
for _, p := range rt {
|
||||
tags := []string{
|
||||
tag("device_name", deviceName),
|
||||
tag("site_name", siteName),
|
||||
tag("source", source),
|
||||
tag("channel", p.Channel.Txt),
|
||||
tag("radio", p.Radio),
|
||||
}
|
||||
data := map[string]float64{
|
||||
"current_antenna_gain": p.CurrentAntennaGain.Val,
|
||||
"ht": p.Ht.Val,
|
||||
"max_txpower": p.MaxTxpower.Val,
|
||||
"min_txpower": p.MinTxpower.Val,
|
||||
"nss": p.Nss.Val,
|
||||
"radio_caps": p.RadioCaps.Val,
|
||||
}
|
||||
|
||||
for _, t := range rts {
|
||||
if t.Name == p.Name {
|
||||
data["ast_be_xmit"] = t.AstBeXmit.Val
|
||||
data["channel"] = t.Channel.Val
|
||||
data["cu_self_rx"] = t.CuSelfRx.Val
|
||||
data["cu_self_tx"] = t.CuSelfTx.Val
|
||||
data["cu_total"] = t.CuTotal.Val
|
||||
data["extchannel"] = t.Extchannel.Val
|
||||
data["gain"] = t.Gain.Val
|
||||
data["guest-num_sta"] = t.GuestNumSta.Val
|
||||
data["num_sta"] = t.NumSta.Val
|
||||
data["tx_packets"] = t.TxPackets.Val
|
||||
data["tx_power"] = t.TxPower.Val
|
||||
data["tx_retries"] = t.TxRetries.Val
|
||||
data["user-num_sta"] = t.UserNumSta.Val
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
metricName := metricNamespace("uap_radios")
|
||||
|
||||
reportGaugeForMap(r, metricName, data, tags)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
package datadogunifi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/unifi-poller/unifi"
|
||||
)
|
||||
|
||||
// reportSysStats is used by all device types.
|
||||
func (u *DatadogUnifi) reportSysStats(r report, metricName func(string) string, s unifi.SysStats, ss unifi.SystemStats, tags []string) {
|
||||
data := map[string]float64{
|
||||
"loadavg_1": s.Loadavg1.Val,
|
||||
"loadavg_5": s.Loadavg5.Val,
|
||||
"loadavg_15": s.Loadavg15.Val,
|
||||
"mem_used": s.MemUsed.Val,
|
||||
"mem_buffer": s.MemBuffer.Val,
|
||||
"mem_total": s.MemTotal.Val,
|
||||
"cpu": ss.CPU.Val,
|
||||
"mem": ss.Mem.Val,
|
||||
"system_uptime": ss.Uptime.Val,
|
||||
}
|
||||
for name, value := range data {
|
||||
r.reportGauge(metricName(name), value, tags)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *DatadogUnifi) reportUDMtemps(r report, metricName func(string) string, tags []string, temps []unifi.Temperature) {
|
||||
for _, t := range temps {
|
||||
name := fmt.Sprintf("temp_%s", t.Name)
|
||||
r.reportGauge(metricName(name), t.Value, tags)
|
||||
}
|
||||
}
|
||||
|
||||
// reportUDM generates Unifi Gateway datapoints for InfluxDB.
|
||||
// These points can be passed directly to influx.
|
||||
func (u *DatadogUnifi) reportUDM(r report, s *unifi.UDM) { // nolint: funlen
|
||||
if !s.Adopted.Val || s.Locating.Val {
|
||||
return
|
||||
}
|
||||
|
||||
metricName := metricNamespace("usg")
|
||||
|
||||
tags := []string{
|
||||
tag("source", s.SourceName),
|
||||
tag("ip", s.IP),
|
||||
tag("license_state", s.LicenseState),
|
||||
tag("mac", s.Mac),
|
||||
tag("site_name", s.SiteName),
|
||||
tag("name", s.Name),
|
||||
tag("version", s.Version),
|
||||
tag("model", s.Model),
|
||||
tag("serial", s.Serial),
|
||||
tag("type", s.Type),
|
||||
}
|
||||
u.reportUDMtemps(r, metricName, tags, s.Temperatures)
|
||||
u.reportUSGstats(r, metricName, tags, s.SpeedtestStatus, s.Stat.Gw, s.Uplink)
|
||||
u.reportSysStats(r, metricName, s.SysStats, s.SystemStats, tags)
|
||||
|
||||
data := map[string]float64{
|
||||
"bytes": s.Bytes.Val,
|
||||
"last_seen": s.LastSeen.Val,
|
||||
"guest-num_sta": s.GuestNumSta.Val,
|
||||
"rx_bytes": s.RxBytes.Val,
|
||||
"tx_bytes": s.TxBytes.Val,
|
||||
"uptime": s.Uptime.Val,
|
||||
"state": s.State.Val,
|
||||
"user-num_sta": s.UserNumSta.Val,
|
||||
"num_desktop": s.NumDesktop.Val,
|
||||
"num_handheld": s.NumHandheld.Val,
|
||||
"num_mobile": s.NumMobile.Val,
|
||||
}
|
||||
for name, value := range data {
|
||||
r.reportGauge(metricName(name), value, tags)
|
||||
}
|
||||
u.reportNetTable(r, s.Name, s.SiteName, s.SourceName, s.NetworkTable)
|
||||
u.reportUSGwans(r, s.Name, s.SiteName, s.SourceName, s.Wan1, s.Wan2)
|
||||
|
||||
tags = []string{
|
||||
tag("mac", s.Mac),
|
||||
tag("site_name", s.SiteName),
|
||||
tag("source", s.SourceName),
|
||||
tag("name", s.Name),
|
||||
tag("version", s.Version),
|
||||
tag("model", s.Model),
|
||||
tag("serial", s.Serial),
|
||||
tag("type", s.Type),
|
||||
tag("ip", s.IP),
|
||||
}
|
||||
metricName = metricNamespace("usw")
|
||||
u.reportUSWstat(r, metricName, tags, s.Stat.Sw)
|
||||
|
||||
data = map[string]float64{
|
||||
"guest-num_sta": s.GuestNumSta.Val,
|
||||
"bytes": s.Bytes.Val,
|
||||
"last_seen": s.LastSeen.Val,
|
||||
"rx_bytes": s.RxBytes.Val,
|
||||
"tx_bytes": s.TxBytes.Val,
|
||||
"uptime": s.Uptime.Val,
|
||||
}
|
||||
for name, value := range data {
|
||||
r.reportGauge(metricName(name), value, tags)
|
||||
}
|
||||
|
||||
u.reportPortTable(r, s.Name, s.SiteName, s.SourceName, s.Type, s.PortTable) // udm has a usw in it.
|
||||
|
||||
if s.Stat.Ap == nil {
|
||||
return // we're done now. the following code process UDM (non-pro) UAP data.
|
||||
}
|
||||
|
||||
tags = []string{
|
||||
tag("mac", s.Mac),
|
||||
tag("site_name", s.SiteName),
|
||||
tag("source", s.SourceName),
|
||||
tag("name", s.Name),
|
||||
tag("version", s.Version),
|
||||
tag("model", s.Model),
|
||||
tag("serial", s.Serial),
|
||||
tag("type", s.Type),
|
||||
}
|
||||
|
||||
metricName = metricNamespace("uap")
|
||||
u.reportUAPstats(s.Stat.Ap, r, metricName, tags)
|
||||
|
||||
data = map[string]float64{
|
||||
"bytes": s.Bytes.Val,
|
||||
"last_seen": s.LastSeen.Val,
|
||||
"rx_bytes": s.RxBytes.Val,
|
||||
"tx_bytes": s.TxBytes.Val,
|
||||
"uptime": s.Uptime.Val,
|
||||
"state": s.State.Val,
|
||||
"user-num_sta": s.UserNumSta.Val,
|
||||
"guest-num_sta": s.GuestNumSta.Val,
|
||||
"num_sta": s.NumSta.Val,
|
||||
}
|
||||
for name, value := range data {
|
||||
r.reportGauge(metricName(name), value, tags)
|
||||
}
|
||||
|
||||
u.reportRadTable(r, s.Name, s.SiteName, s.SourceName, *s.RadioTable, *s.RadioTableStats)
|
||||
u.reportVAPTable(r, s.Name, s.SiteName, s.SourceName, *s.VapTable)
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
package datadogunifi
|
||||
|
||||
import (
|
||||
"github.com/unifi-poller/unifi"
|
||||
)
|
||||
|
||||
// reportUSG generates Unifi Gateway datapoints for Datadog.
|
||||
// These points can be passed directly to datadog.
|
||||
func (u *DatadogUnifi) reportUSG(r report, s *unifi.USG) {
|
||||
if !s.Adopted.Val || s.Locating.Val {
|
||||
return
|
||||
}
|
||||
|
||||
tags := []string{
|
||||
tag("mac", s.Mac),
|
||||
tag("site_name", s.SiteName),
|
||||
tag("source", s.SourceName),
|
||||
tag("name", s.Name),
|
||||
tag("version", s.Version),
|
||||
tag("model", s.Model),
|
||||
tag("serial", s.Serial),
|
||||
tag("type", s.Type),
|
||||
tag("ip", s.IP),
|
||||
tag("license_state", s.LicenseState),
|
||||
}
|
||||
metricName := metricNamespace("usg")
|
||||
u.reportSysStats(r, metricName, s.SysStats, s.SystemStats, tags)
|
||||
u.reportUSGstats(r, metricName, tags, s.SpeedtestStatus, s.Stat.Gw, s.Uplink)
|
||||
|
||||
data := map[string]float64{
|
||||
"bytes": s.Bytes.Val,
|
||||
"last_seen": s.LastSeen.Val,
|
||||
"guest-num_sta": s.GuestNumSta.Val,
|
||||
"rx_bytes": s.RxBytes.Val,
|
||||
"tx_bytes": s.TxBytes.Val,
|
||||
"uptime": s.Uptime.Val,
|
||||
"state": s.State.Val,
|
||||
"user-num_sta": s.UserNumSta.Val,
|
||||
"num_desktop": s.NumDesktop.Val,
|
||||
"num_handheld": s.NumHandheld.Val,
|
||||
"num_mobile": s.NumMobile.Val,
|
||||
}
|
||||
reportGaugeForMap(r, metricName, data, tags)
|
||||
|
||||
u.reportNetTable(r, s.Name, s.SiteName, s.SourceName, s.NetworkTable)
|
||||
u.reportUSGwans(r, s.Name, s.SiteName, s.SourceName, s.Wan1, s.Wan2)
|
||||
}
|
||||
|
||||
func (u *DatadogUnifi) reportUSGstats(r report, metricName func(string) string, tags []string, ss unifi.SpeedtestStatus, gw *unifi.Gw, ul unifi.Uplink) {
|
||||
if gw == nil {
|
||||
return
|
||||
}
|
||||
data := map[string]float64{
|
||||
"uplink_latency": ul.Latency.Val,
|
||||
"uplink_speed": ul.Speed.Val,
|
||||
"speedtest-status_latency": ss.Latency.Val,
|
||||
"speedtest-status_runtime": ss.Runtime.Val,
|
||||
"speedtest-status_rundate": ss.Rundate.Val,
|
||||
"speedtest-status_ping": ss.StatusPing.Val,
|
||||
"speedtest-status_xput_download": ss.XputDownload.Val,
|
||||
"speedtest-status_xput_upload": ss.XputUpload.Val,
|
||||
"lan-rx_bytes": gw.LanRxBytes.Val,
|
||||
"lan-rx_packets": gw.LanRxPackets.Val,
|
||||
"lan-tx_bytes": gw.LanTxBytes.Val,
|
||||
"lan-tx_packets": gw.LanTxPackets.Val,
|
||||
"lan-rx_dropped": gw.LanRxDropped.Val,
|
||||
}
|
||||
reportGaugeForMap(r, metricName, data, tags)
|
||||
}
|
||||
|
||||
func (u *DatadogUnifi) reportUSGwans(r report, deviceName string, siteName string, source string, wans ...unifi.Wan) {
|
||||
for _, wan := range wans {
|
||||
if !wan.Up.Val {
|
||||
continue
|
||||
}
|
||||
|
||||
tags := []string{
|
||||
tag("device_name", deviceName),
|
||||
tag("site_name", siteName),
|
||||
tag("source", source),
|
||||
tag("ip", wan.IP),
|
||||
tag("purpose", wan.Name),
|
||||
tag("mac", wan.Mac),
|
||||
tag("ifname", wan.Ifname),
|
||||
tag("type", wan.Type),
|
||||
tag("up", wan.Up.Txt),
|
||||
tag("enabled", wan.Enable.Txt),
|
||||
tag("gateway", wan.Gateway),
|
||||
}
|
||||
fullDuplex := float64(0)
|
||||
if wan.FullDuplex.Val {
|
||||
fullDuplex = 1
|
||||
}
|
||||
|
||||
data := map[string]float64{
|
||||
"bytes-r": wan.BytesR.Val,
|
||||
"full_duplex": fullDuplex,
|
||||
"max_speed": wan.MaxSpeed.Val,
|
||||
"rx_bytes": wan.RxBytes.Val,
|
||||
"rx_bytes-r": wan.RxBytesR.Val,
|
||||
"rx_dropped": wan.RxDropped.Val,
|
||||
"rx_errors": wan.RxErrors.Val,
|
||||
"rx_broadcast": wan.RxBroadcast.Val,
|
||||
"rx_multicast": wan.RxMulticast.Val,
|
||||
"rx_packets": wan.RxPackets.Val,
|
||||
"speed": wan.Speed.Val,
|
||||
"tx_bytes": wan.TxBytes.Val,
|
||||
"tx_bytes-r": wan.TxBytesR.Val,
|
||||
"tx_dropped": wan.TxDropped.Val,
|
||||
"tx_errors": wan.TxErrors.Val,
|
||||
"tx_packets": wan.TxPackets.Val,
|
||||
"tx_broadcast": wan.TxBroadcast.Val,
|
||||
"tx_multicast": wan.TxMulticast.Val,
|
||||
}
|
||||
metricName := metricNamespace("usg_wan_ports")
|
||||
reportGaugeForMap(r, metricName, data, tags)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *DatadogUnifi) reportNetTable(r report, deviceName string, siteName string, source string, nt unifi.NetworkTable) {
|
||||
for _, p := range nt {
|
||||
tags := []string{
|
||||
tag("device_name", deviceName),
|
||||
tag("site_name", siteName),
|
||||
tag("source", source),
|
||||
tag("up", p.Up.Txt),
|
||||
tag("enabled", p.Enabled.Txt),
|
||||
tag("ip", p.IP),
|
||||
tag("mac", p.Mac),
|
||||
tag("name", p.Name),
|
||||
tag("domain_name", p.DomainName),
|
||||
tag("purpose", p.Purpose),
|
||||
tag("is_guest", p.IsGuest.Txt),
|
||||
}
|
||||
data := map[string]float64{
|
||||
"num_sta": p.NumSta.Val,
|
||||
"rx_bytes": p.RxBytes.Val,
|
||||
"rx_packets": p.RxPackets.Val,
|
||||
"tx_bytes": p.TxBytes.Val,
|
||||
"tx_packets": p.TxPackets.Val,
|
||||
}
|
||||
metricName := metricNamespace("usg_networks")
|
||||
reportGaugeForMap(r, metricName, data, tags)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
package datadogunifi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/unifi-poller/unifi"
|
||||
)
|
||||
|
||||
// reportUSW generates Unifi Switch datapoints for Datadog.
|
||||
// These points can be passed directly to datadog.
|
||||
func (u *DatadogUnifi) reportUSW(r report, s *unifi.USW) {
|
||||
if !s.Adopted.Val || s.Locating.Val {
|
||||
return
|
||||
}
|
||||
|
||||
tags := []string{
|
||||
tag("mac", s.Mac),
|
||||
tag("site_name", s.SiteName),
|
||||
tag("source", s.SourceName),
|
||||
tag("name", s.Name),
|
||||
tag("version", s.Version),
|
||||
tag("model", s.Model),
|
||||
tag("serial", s.Serial),
|
||||
tag("type", s.Type),
|
||||
tag("ip", s.IP),
|
||||
}
|
||||
metricName := metricNamespace("usw")
|
||||
u.reportUSWstat(r, metricName, tags, s.Stat.Sw)
|
||||
u.reportSysStats(r, metricName, s.SysStats, s.SystemStats, tags)
|
||||
|
||||
data := map[string]float64{
|
||||
"guest-num_sta": s.GuestNumSta.Val,
|
||||
"bytes": s.Bytes.Val,
|
||||
"fan_level": s.FanLevel.Val,
|
||||
"general_temperature": s.GeneralTemperature.Val,
|
||||
"last_seen": s.LastSeen.Val,
|
||||
"rx_bytes": s.RxBytes.Val,
|
||||
"tx_bytes": s.TxBytes.Val,
|
||||
"uptime": s.Uptime.Val,
|
||||
"state": s.State.Val,
|
||||
"user-num_sta": s.UserNumSta.Val,
|
||||
}
|
||||
reportGaugeForMap(r, metricName, data, tags)
|
||||
u.reportPortTable(r, s.Name, s.SiteName, s.SourceName, s.Type, s.PortTable)
|
||||
}
|
||||
|
||||
func (u *DatadogUnifi) reportUSWstat(r report, metricName func(string) string, tags []string, sw *unifi.Sw) {
|
||||
if sw == nil {
|
||||
return
|
||||
}
|
||||
|
||||
data := map[string]float64{
|
||||
"stat_bytes": sw.Bytes.Val,
|
||||
"stat_rx_bytes": sw.RxBytes.Val,
|
||||
"stat_rx_crypts": sw.RxCrypts.Val,
|
||||
"stat_rx_dropped": sw.RxDropped.Val,
|
||||
"stat_rx_errors": sw.RxErrors.Val,
|
||||
"stat_rx_frags": sw.RxFrags.Val,
|
||||
"stat_rx_packets": sw.TxPackets.Val,
|
||||
"stat_tx_bytes": sw.TxBytes.Val,
|
||||
"stat_tx_dropped": sw.TxDropped.Val,
|
||||
"stat_tx_errors": sw.TxErrors.Val,
|
||||
"stat_tx_packets": sw.TxPackets.Val,
|
||||
"stat_tx_retries": sw.TxRetries.Val,
|
||||
}
|
||||
reportGaugeForMap(r, metricName, data, tags)
|
||||
}
|
||||
|
||||
func (u *DatadogUnifi) reportPortTable(r report, deviceName string, siteName string, source string, typeTag string, pt []unifi.Port) {
|
||||
for _, p := range pt {
|
||||
if !p.Up.Val || !p.Enable.Val {
|
||||
continue // only record UP ports.
|
||||
}
|
||||
|
||||
tags := []string{
|
||||
tag("site_name", siteName),
|
||||
tag("device_name", deviceName),
|
||||
tag("source", source),
|
||||
tag("type", typeTag),
|
||||
tag("name", p.Name),
|
||||
tag("poe_mode", p.PoeMode),
|
||||
tag("port_poe", p.PortPoe.Txt),
|
||||
tag("port_idx", p.PortIdx.Txt),
|
||||
tag("port_id", fmt.Sprintf("%s_port_%s", deviceName, p.PortIdx.Txt)),
|
||||
tag("poe_enable", p.PoeEnable.Txt),
|
||||
tag("flowctrl_rx", p.FlowctrlRx.Txt),
|
||||
tag("flowctrl_tx", p.FlowctrlTx.Txt),
|
||||
tag("media", p.Media),
|
||||
}
|
||||
data := map[string]float64{
|
||||
"dbytes_r": p.BytesR.Val,
|
||||
"rx_broadcast": p.RxBroadcast.Val,
|
||||
"rx_bytes": p.RxBytes.Val,
|
||||
"rx_bytes-r": p.RxBytesR.Val,
|
||||
"rx_dropped": p.RxDropped.Val,
|
||||
"rx_errors": p.RxErrors.Val,
|
||||
"rx_multicast": p.RxMulticast.Val,
|
||||
"rx_packets": p.RxPackets.Val,
|
||||
"speed": p.Speed.Val,
|
||||
"stp_pathcost": p.StpPathcost.Val,
|
||||
"tx_broadcast": p.TxBroadcast.Val,
|
||||
"tx_bytes": p.TxBytes.Val,
|
||||
"tx_bytes-r": p.TxBytesR.Val,
|
||||
"tx_dropped": p.TxDropped.Val,
|
||||
"tx_errors": p.TxErrors.Val,
|
||||
"tx_multicast": p.TxMulticast.Val,
|
||||
"tx_packets": p.TxPackets.Val,
|
||||
}
|
||||
|
||||
if p.PoeEnable.Val && p.PortPoe.Val {
|
||||
data["poe_current"] = p.PoeCurrent.Val
|
||||
data["poe_power"] = p.PoePower.Val
|
||||
data["poe_voltage"] = p.PoeVoltage.Val
|
||||
}
|
||||
|
||||
metricName := metricNamespace("usw_ports")
|
||||
reportGaugeForMap(r, metricName, data, tags)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue