bring this up to recent specs
This commit is contained in:
		
							parent
							
								
									7833500d2a
								
							
						
					
					
						commit
						db85c29aba
					
				| 
						 | 
					@ -1 +1,88 @@
 | 
				
			||||||
package datadogunifi
 | 
					package datadogunifi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/unpoller/unifi"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						alarmT   = item("Alarm")
 | 
				
			||||||
 | 
						anomalyT = item("Anomaly")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// batchAlarms generates alarm events and logs for Datadog.
 | 
				
			||||||
 | 
					func (u *DatadogUnifi) 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.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tagMap := map[string]string{
 | 
				
			||||||
 | 
							"dest_port":            strconv.Itoa(event.DestPort),
 | 
				
			||||||
 | 
							"src_port":             strconv.Itoa(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":            fmt.Sprintf("%d", event.DestIPGeo.Asn),
 | 
				
			||||||
 | 
							"dstip_latitude":       fmt.Sprintf("%0.6f", event.DestIPGeo.Latitude),
 | 
				
			||||||
 | 
							"dstip_longitude":      fmt.Sprintf("%0.6f", 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":            fmt.Sprintf("%d", event.SourceIPGeo.Asn),
 | 
				
			||||||
 | 
							"srcip_latitude":       fmt.Sprintf("%0.6f", event.SourceIPGeo.Latitude),
 | 
				
			||||||
 | 
							"srcip_longitude":      fmt.Sprintf("%0.6f", 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,
 | 
				
			||||||
 | 
							"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,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						r.addCount(alarmT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tagMap = cleanTags(tagMap)
 | 
				
			||||||
 | 
						tags := tagMapToTags(tagMap)
 | 
				
			||||||
 | 
						title := fmt.Sprintf("[%s][%s] Alarm at %s from %s", event.EventType, event.Catname, event.SiteName, event.SourceName)
 | 
				
			||||||
 | 
						r.reportEvent(title, event.Datetime, event.Msg, tags)
 | 
				
			||||||
 | 
						r.reportWarnLog(fmt.Sprintf("[%d] %s: %s", event.Datetime.Unix(), title, event.Msg), tagMapToZapFields(tagMap))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// batchAnomaly generates Anomalies from UniFi for Datadog.
 | 
				
			||||||
 | 
					func (u *DatadogUnifi) 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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tagMap := cleanTags(map[string]string{
 | 
				
			||||||
 | 
							"application": "unifi_anomaly",
 | 
				
			||||||
 | 
							"source":      event.SourceName,
 | 
				
			||||||
 | 
							"site_name":   event.SiteName,
 | 
				
			||||||
 | 
							"device_mac":  event.DeviceMAC,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						tags := tagMapToTags(tagMap)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						title := fmt.Sprintf("Anomaly detected at %s from %s", event.SiteName, event.SourceName)
 | 
				
			||||||
 | 
						r.reportEvent(title, event.Datetime, event.Anomaly, tags)
 | 
				
			||||||
 | 
						r.reportWarnLog(fmt.Sprintf("[%d] %s: %s", event.Datetime.Unix(), title, event.Anomaly), tagMapToZapFields(tagMap))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,108 +1,122 @@
 | 
				
			||||||
package datadogunifi
 | 
					package datadogunifi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/unifi-poller/unifi"
 | 
						"github.com/unpoller/unifi"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// reportClient generates Unifi Client datapoints for InfluxDB.
 | 
					// batchClient generates Unifi Client datapoints for Datadog.
 | 
				
			||||||
// These points can be passed directly to influx.
 | 
					// These points can be passed directly to Datadog.
 | 
				
			||||||
func (u *DatadogUnifi) reportClient(r report, s *unifi.Client) { // nolint: funlen
 | 
					func (u *DatadogUnifi) batchClient(r report, s *unifi.Client) { // nolint: funlen
 | 
				
			||||||
	tags := []string{
 | 
						tags := map[string]string{
 | 
				
			||||||
		tag("mac", s.Mac),
 | 
							"mac":         s.Mac,
 | 
				
			||||||
		tag("site_name", s.SiteName),
 | 
							"site_name":   s.SiteName,
 | 
				
			||||||
		tag("source", s.SourceName),
 | 
							"source":      s.SourceName,
 | 
				
			||||||
		tag("ap_name", s.ApName),
 | 
							"ap_name":     s.ApName,
 | 
				
			||||||
		tag("gw_name", s.GwName),
 | 
							"gw_name":     s.GwName,
 | 
				
			||||||
		tag("sw_name", s.SwName),
 | 
							"sw_name":     s.SwName,
 | 
				
			||||||
		tag("oui", s.Oui),
 | 
							"oui":         s.Oui,
 | 
				
			||||||
		tag("radio_name", s.RadioName),
 | 
							"radio_name":  s.RadioName,
 | 
				
			||||||
		tag("radio", s.Radio),
 | 
							"radio":       s.Radio,
 | 
				
			||||||
		tag("radio_proto", s.RadioProto),
 | 
							"radio_proto": s.RadioProto,
 | 
				
			||||||
		tag("name", s.Name),
 | 
							"name":        s.Name,
 | 
				
			||||||
		tag("fixed_ip", s.FixedIP),
 | 
							"fixed_ip":    s.FixedIP,
 | 
				
			||||||
		tag("sw_port", s.SwPort.Txt),
 | 
							"sw_port":     s.SwPort.Txt,
 | 
				
			||||||
		tag("os_class", s.OsClass.Txt),
 | 
							"os_class":    s.OsClass.Txt,
 | 
				
			||||||
		tag("os_name", s.OsName.Txt),
 | 
							"os_name":     s.OsName.Txt,
 | 
				
			||||||
		tag("dev_cat", s.DevCat.Txt),
 | 
							"dev_cat":     s.DevCat.Txt,
 | 
				
			||||||
		tag("dev_id", s.DevID.Txt),
 | 
							"dev_id":      s.DevID.Txt,
 | 
				
			||||||
		tag("dev_vendor", s.DevVendor.Txt),
 | 
							"dev_vendor":  s.DevVendor.Txt,
 | 
				
			||||||
		tag("dev_family", s.DevFamily.Txt),
 | 
							"dev_family":  s.DevFamily.Txt,
 | 
				
			||||||
		tag("is_wired", s.IsWired.Txt),
 | 
							"is_wired":    s.IsWired.Txt,
 | 
				
			||||||
		tag("is_guest", s.IsGuest.Txt),
 | 
							"is_guest":    s.IsGuest.Txt,
 | 
				
			||||||
		tag("use_fixedip", s.UseFixedIP.Txt),
 | 
							"use_fixedip": s.UseFixedIP.Txt,
 | 
				
			||||||
		tag("channel", s.Channel.Txt),
 | 
							"channel":     s.Channel.Txt,
 | 
				
			||||||
		tag("vlan", s.Vlan.Txt),
 | 
							"vlan":        s.Vlan.Txt,
 | 
				
			||||||
		tag("hostname", s.Name),
 | 
							"hostname":    s.Name,
 | 
				
			||||||
		tag("radio_desc", s.RadioDescription),
 | 
							"essid":       s.Essid,
 | 
				
			||||||
		tag("ip", s.IP),
 | 
							"bssid":       s.Bssid,
 | 
				
			||||||
		tag("essid", s.Essid),
 | 
							"ip":          s.IP,
 | 
				
			||||||
		tag("bssid", s.Bssid),
 | 
						}
 | 
				
			||||||
 | 
						powerSaveEnabled := 0.0
 | 
				
			||||||
 | 
						if s.PowersaveEnabled.Val {
 | 
				
			||||||
 | 
							powerSaveEnabled = 1.0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						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),
 | 
				
			||||||
 | 
							"powersave_enabled": powerSaveEnabled,
 | 
				
			||||||
 | 
							"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),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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")
 | 
						metricName := metricNamespace("clients")
 | 
				
			||||||
	reportGaugeForMap(r, metricName, data, tags)
 | 
					
 | 
				
			||||||
 | 
						reportGaugeForFloat64Map(r, metricName, data, tags)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// totalsDPImap: controller, site, name (app/cat name), dpi.
 | 
					// totalsDPImap: controller, site, name (app/cat name), dpi.
 | 
				
			||||||
type totalsDPImap map[string]map[string]map[string]unifi.DPIData
 | 
					type totalsDPImap map[string]map[string]map[string]unifi.DPIData
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (u *DatadogUnifi) reportClientDPI(r report, s *unifi.DPITable, appTotal, catTotal totalsDPImap) {
 | 
					func (u *DatadogUnifi) 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 {
 | 
						for _, dpi := range s.ByApp {
 | 
				
			||||||
		category := unifi.DPICats.Get(dpi.Cat)
 | 
							category := unifi.DPICats.Get(dpi.Cat)
 | 
				
			||||||
		application := unifi.DPIApps.GetApp(dpi.Cat, dpi.App)
 | 
							application := unifi.DPIApps.GetApp(dpi.Cat, dpi.App)
 | 
				
			||||||
		fillDPIMapTotals(appTotal, application, s.SourceName, s.SiteName, dpi)
 | 
							fillDPIMapTotals(appTotal, application, s.SourceName, s.SiteName, dpi)
 | 
				
			||||||
		fillDPIMapTotals(catTotal, category, s.SourceName, s.SiteName, dpi)
 | 
							fillDPIMapTotals(catTotal, category, s.SourceName, s.SiteName, dpi)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		tags := []string{
 | 
							tags := map[string]string{
 | 
				
			||||||
			tag("category", category),
 | 
								"category":    category,
 | 
				
			||||||
			tag("application", application),
 | 
								"application": application,
 | 
				
			||||||
			tag("name", s.Name),
 | 
								"name":        s.Name,
 | 
				
			||||||
			tag("mac", s.MAC),
 | 
								"mac":         s.MAC,
 | 
				
			||||||
			tag("site_name", s.SiteName),
 | 
								"site_name":   s.SiteName,
 | 
				
			||||||
			tag("source", s.SourceName),
 | 
								"source":      s.SourceName,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		data := map[string]float64{
 | 
							data := map[string]float64{
 | 
				
			||||||
			"tx_packets": float64(dpi.TxPackets),
 | 
								"tx_packets": float64(dpi.TxPackets),
 | 
				
			||||||
			"rx_packets": float64(dpi.RxPackets),
 | 
								"rx_packets": float64(dpi.RxPackets),
 | 
				
			||||||
			"tx_bytes":   float64(dpi.TxBytes),
 | 
								"tx_bytes":   float64(dpi.TxBytes),
 | 
				
			||||||
			"rx_bytes":   float64(dpi.RxBytes),
 | 
								"rx_bytes":   float64(dpi.RxBytes),
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		metricName := metricNamespace("clientdpi")
 | 
					
 | 
				
			||||||
		reportGaugeForMap(r, metricName, data, tags)
 | 
							metricName := metricNamespace("client_dpi")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							reportGaugeForFloat64Map(r, metricName, data, tags)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// fillDPIMapTotals fills in totals for categories and applications. maybe clients too.
 | 
					// 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.
 | 
					// This allows less processing in Datadog to produce total transfer data per cat or app.
 | 
				
			||||||
func fillDPIMapTotals(m totalsDPImap, name, controller, site string, dpi unifi.DPIData) {
 | 
					func fillDPIMapTotals(m totalsDPImap, name, controller, site string, dpi unifi.DPIData) {
 | 
				
			||||||
	if m[controller] == nil {
 | 
						if m[controller] == nil {
 | 
				
			||||||
		m[controller] = make(map[string]map[string]unifi.DPIData)
 | 
							m[controller] = make(map[string]map[string]unifi.DPIData)
 | 
				
			||||||
| 
						 | 
					@ -148,19 +162,26 @@ func reportClientDPItotals(r report, appTotal, catTotal totalsDPImap) {
 | 
				
			||||||
		for controller, s := range k.val {
 | 
							for controller, s := range k.val {
 | 
				
			||||||
			for site, c := range s {
 | 
								for site, c := range s {
 | 
				
			||||||
				for name, m := range c {
 | 
									for name, m := range c {
 | 
				
			||||||
					tags := []string{
 | 
										tags := map[string]string{
 | 
				
			||||||
						tag("site_name", site),
 | 
											"category":    "TOTAL",
 | 
				
			||||||
						tag("source", controller),
 | 
											"application": "TOTAL",
 | 
				
			||||||
						tag("name", name),
 | 
											"name":        "TOTAL",
 | 
				
			||||||
 | 
											"mac":         "TOTAL",
 | 
				
			||||||
 | 
											"site_name":   site,
 | 
				
			||||||
 | 
											"source":      controller,
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
										tags[k.kind] = name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					data := map[string]float64{
 | 
										data := map[string]float64{
 | 
				
			||||||
						"tx_packets": float64(m.TxPackets),
 | 
											"tx_packets": float64(m.TxPackets),
 | 
				
			||||||
						"rx_packets": float64(m.RxPackets),
 | 
											"rx_packets": float64(m.RxPackets),
 | 
				
			||||||
						"tx_bytes":   float64(m.TxBytes),
 | 
											"tx_bytes":   float64(m.TxBytes),
 | 
				
			||||||
						"rx_bytes":   float64(m.RxBytes),
 | 
											"rx_bytes":   float64(m.RxBytes),
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
					metricName := metricNamespace("clientdpi.totals")
 | 
					
 | 
				
			||||||
					reportGaugeForMap(r, metricName, data, tags)
 | 
										metricName := metricNamespace("client_dpi")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										reportGaugeForFloat64Map(r, metricName, data, tags)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,13 +3,12 @@
 | 
				
			||||||
package datadogunifi
 | 
					package datadogunifi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"log"
 | 
					 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/DataDog/datadog-go/statsd"
 | 
						"github.com/DataDog/datadog-go/statsd"
 | 
				
			||||||
	"github.com/unifi-poller/poller"
 | 
						"github.com/unpoller/poller"
 | 
				
			||||||
	"github.com/unifi-poller/unifi"
 | 
						"github.com/unpoller/unifi"
 | 
				
			||||||
 | 
						"go.uber.org/zap"
 | 
				
			||||||
	"golift.io/cnfg"
 | 
						"golift.io/cnfg"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +24,9 @@ type Config struct {
 | 
				
			||||||
	// Interval controls the collection and reporting interval
 | 
						// Interval controls the collection and reporting interval
 | 
				
			||||||
	Interval cnfg.Duration `json:"interval,omitempty" toml:"interval,omitempty" xml:"interval,omitempty" yaml:"interval,omitempty"`
 | 
						Interval cnfg.Duration `json:"interval,omitempty" toml:"interval,omitempty" xml:"interval,omitempty" yaml:"interval,omitempty"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Save data for dead ports? ie. ports that are down or disabled.
 | 
				
			||||||
 | 
						DeadPorts bool `json:"dead_ports,omitempty" toml:"dead_ports,omitempty" xml:"dead_ports,omitempty" yaml:"dead_ports,omitempty"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Disable when true disables this output plugin
 | 
						// Disable when true disables this output plugin
 | 
				
			||||||
	Disable bool `json:"disable" toml:"disable" xml:"disable,attr" yaml:"disable"`
 | 
						Disable bool `json:"disable" toml:"disable" xml:"disable,attr" yaml:"disable"`
 | 
				
			||||||
	// Address determines how to talk to the Datadog agent
 | 
						// Address determines how to talk to the Datadog agent
 | 
				
			||||||
| 
						 | 
					@ -107,11 +109,13 @@ type DatadogUnifi struct {
 | 
				
			||||||
	Collector poller.Collect
 | 
						Collector poller.Collect
 | 
				
			||||||
	datadog   statsd.ClientInterface
 | 
						datadog   statsd.ClientInterface
 | 
				
			||||||
	LastCheck time.Time
 | 
						LastCheck time.Time
 | 
				
			||||||
 | 
						Logger    *zap.SugaredLogger
 | 
				
			||||||
	*Datadog
 | 
						*Datadog
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() { // nolint: gochecknoinits
 | 
					func init() { // nolint: gochecknoinits
 | 
				
			||||||
	u := &DatadogUnifi{Datadog: &Datadog{}, LastCheck: time.Now()}
 | 
						l, _ := zap.NewProduction()
 | 
				
			||||||
 | 
						u := &DatadogUnifi{Datadog: &Datadog{}, LastCheck: time.Now(), Logger: l.Sugar()}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	poller.NewOutput(&poller.Output{
 | 
						poller.NewOutput(&poller.Output{
 | 
				
			||||||
		Name:   "datadog",
 | 
							Name:   "datadog",
 | 
				
			||||||
| 
						 | 
					@ -188,8 +192,13 @@ func (u *DatadogUnifi) setConfigDefaults() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Run runs a ticker to poll the unifi server and update Datadog.
 | 
					// Run runs a ticker to poll the unifi server and update Datadog.
 | 
				
			||||||
func (u *DatadogUnifi) Run(c poller.Collect) error {
 | 
					func (u *DatadogUnifi) Run(c poller.Collect) error {
 | 
				
			||||||
	if u.Config == nil || u.Disable {
 | 
						defer u.Logger.Sync()
 | 
				
			||||||
		c.Logf("DataDog config is missing (or disabled): Datadog output is disabled!")
 | 
						if u.Disable {
 | 
				
			||||||
 | 
							u.Logger.Debug("Datadog config is disabled, output is disabled.")
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if u.Config == nil && !u.Disable {
 | 
				
			||||||
 | 
							u.Logger.Error("DataDog config is missing and is not disabled: Datadog output is disabled!")
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -212,26 +221,29 @@ func (u *DatadogUnifi) Run(c poller.Collect) error {
 | 
				
			||||||
func (u *DatadogUnifi) PollController() {
 | 
					func (u *DatadogUnifi) PollController() {
 | 
				
			||||||
	interval := u.Interval.Round(time.Second)
 | 
						interval := u.Interval.Round(time.Second)
 | 
				
			||||||
	ticker := time.NewTicker(interval)
 | 
						ticker := time.NewTicker(interval)
 | 
				
			||||||
	log.Printf("[INFO] Everything checks out! Poller started, Datadog interval: %v", interval)
 | 
						u.Logger.Info("Everything checks out! Poller started", zap.Duration("interval", interval))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for u.LastCheck = range ticker.C {
 | 
						for u.LastCheck = range ticker.C {
 | 
				
			||||||
		metrics, ok, collectErr := u.Collector.Metrics()
 | 
							metrics, err := u.Collector.Metrics(&poller.Filter{Name: "unifi"})
 | 
				
			||||||
		if collectErr != nil {
 | 
					 | 
				
			||||||
			u.Collector.LogErrorf("metric fetch for Datadog failed: %v", collectErr)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if !ok {
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		report, err := u.ReportMetrics(metrics)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			// Is the agent down?
 | 
								u.Logger.Error("metric fetch for Datadog failed", zap.Error(err))
 | 
				
			||||||
			u.Collector.LogErrorf("%v", err)
 | 
					 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		report.error(collectErr)
 | 
							events, err := u.Collector.Events(&poller.Filter{Name: "unifi", Dur: interval})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								u.Logger.Error("event fetch for Datadog failed", zap.Error(err))
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							report, err := u.ReportMetrics(metrics, events)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								// Is the agent down?
 | 
				
			||||||
 | 
								u.Logger.Error("unable to report metrics and events", zap.Error(err))
 | 
				
			||||||
 | 
								report.reportCount("unifi.collect.errors", 1, []string{})
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							report.reportCount("unifi.collect.success", 1, []string{})
 | 
				
			||||||
		u.LogDatadogReport(report)
 | 
							u.LogDatadogReport(report)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -239,12 +251,19 @@ func (u *DatadogUnifi) PollController() {
 | 
				
			||||||
// ReportMetrics batches all device and client data into datadog data points.
 | 
					// ReportMetrics batches all device and client data into datadog data points.
 | 
				
			||||||
// Call this after you've collected all the data you care about.
 | 
					// Call this after you've collected all the data you care about.
 | 
				
			||||||
// Returns an error if datadog statsd calls fail, otherwise returns a report.
 | 
					// Returns an error if datadog statsd calls fail, otherwise returns a report.
 | 
				
			||||||
func (u *DatadogUnifi) ReportMetrics(m *poller.Metrics) (*Report, error) {
 | 
					func (u *DatadogUnifi) ReportMetrics(m *poller.Metrics, e *poller.Events) (*Report, error) {
 | 
				
			||||||
	r := &Report{Metrics: m, Start: time.Now()}
 | 
						r := &Report{
 | 
				
			||||||
 | 
							Metrics: m,
 | 
				
			||||||
 | 
							Events:  e,
 | 
				
			||||||
 | 
							Start:   time.Now(),
 | 
				
			||||||
 | 
							Counts:  &Counts{Val: make(map[item]int)},
 | 
				
			||||||
 | 
							Logger:  u.Logger,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	// batch all the points.
 | 
						// batch all the points.
 | 
				
			||||||
	u.loopPoints(r)
 | 
						u.loopPoints(r)
 | 
				
			||||||
	r.End = time.Now()
 | 
						r.End = time.Now()
 | 
				
			||||||
	r.Elapsed = r.End.Sub(r.Start)
 | 
						r.Elapsed = r.End.Sub(r.Start)
 | 
				
			||||||
 | 
						r.reportTiming("unifi.collector_timing", r.Elapsed, []string{})
 | 
				
			||||||
	return r, nil
 | 
						return r, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -252,65 +271,82 @@ func (u *DatadogUnifi) ReportMetrics(m *poller.Metrics) (*Report, error) {
 | 
				
			||||||
func (u *DatadogUnifi) loopPoints(r report) {
 | 
					func (u *DatadogUnifi) loopPoints(r report) {
 | 
				
			||||||
	m := r.metrics()
 | 
						m := r.metrics()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, s := range m.SitesDPI {
 | 
						for _, s := range m.RogueAPs {
 | 
				
			||||||
		u.reportSiteDPI(r, s)
 | 
							u.switchExport(r, s)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, s := range m.Sites {
 | 
						for _, s := range m.Sites {
 | 
				
			||||||
		u.reportSite(r, s)
 | 
							u.switchExport(r, s)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, s := range m.SitesDPI {
 | 
				
			||||||
 | 
							u.reportSiteDPI(r, s.(*unifi.DPITable))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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)
 | 
						appTotal := make(totalsDPImap)
 | 
				
			||||||
	catTotal := make(totalsDPImap)
 | 
						catTotal := make(totalsDPImap)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, s := range m.ClientsDPI {
 | 
						for _, s := range m.ClientsDPI {
 | 
				
			||||||
		u.reportClientDPI(r, s, appTotal, catTotal)
 | 
							u.batchClientDPI(r, s, appTotal, catTotal)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	reportClientDPItotals(r, appTotal, catTotal)
 | 
						reportClientDPItotals(r, appTotal, catTotal)
 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, s := range m.Clients {
 | 
					 | 
				
			||||||
		u.reportClient(r, s)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, s := range m.IDSList {
 | 
					 | 
				
			||||||
		u.reportIDS(r, s)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	u.loopDevicePoints(r)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (u *DatadogUnifi) loopDevicePoints(r report) {
 | 
					func (u *DatadogUnifi) switchExport(r report, v interface{}) { //nolint:cyclop
 | 
				
			||||||
	m := r.metrics()
 | 
						switch v := v.(type) {
 | 
				
			||||||
	if m.Devices == nil {
 | 
						case *unifi.RogueAP:
 | 
				
			||||||
		m.Devices = &unifi.Devices{}
 | 
							u.batchRogueAP(r, v)
 | 
				
			||||||
		return
 | 
						case *unifi.UAP:
 | 
				
			||||||
	}
 | 
							u.batchUAP(r, v)
 | 
				
			||||||
 | 
						case *unifi.USW:
 | 
				
			||||||
	for _, s := range m.UAPs {
 | 
							u.batchUSW(r, v)
 | 
				
			||||||
		u.reportUAP(r, s)
 | 
						case *unifi.USG:
 | 
				
			||||||
	}
 | 
							u.batchUSG(r, v)
 | 
				
			||||||
 | 
						case *unifi.UXG:
 | 
				
			||||||
	for _, s := range m.USGs {
 | 
							u.batchUXG(r, v)
 | 
				
			||||||
		u.reportUSG(r, s)
 | 
						case *unifi.UDM:
 | 
				
			||||||
	}
 | 
							u.batchUDM(r, v)
 | 
				
			||||||
 | 
						case *unifi.Site:
 | 
				
			||||||
	for _, s := range m.USWs {
 | 
							u.reportSite(r, v)
 | 
				
			||||||
		u.reportUSW(r, s)
 | 
						case *unifi.Client:
 | 
				
			||||||
	}
 | 
							u.batchClient(r, v)
 | 
				
			||||||
 | 
						case *unifi.Event:
 | 
				
			||||||
	for _, s := range m.UDMs {
 | 
							u.batchEvent(r, v)
 | 
				
			||||||
		u.reportUDM(r, s)
 | 
						case *unifi.IDS:
 | 
				
			||||||
 | 
							u.batchIDS(r, v)
 | 
				
			||||||
 | 
						case *unifi.Alarm:
 | 
				
			||||||
 | 
							u.batchAlarms(r, v)
 | 
				
			||||||
 | 
						case *unifi.Anomaly:
 | 
				
			||||||
 | 
							u.batchAnomaly(r, v)
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							u.Logger.Error("invalid export", zap.Reflect("type", v))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LogInfluxReport writes a log message after exporting to influxdb.
 | 
					// LogDatadogReport writes a log message after exporting to Datadog.
 | 
				
			||||||
func (u *DatadogUnifi) LogDatadogReport(r *Report) {
 | 
					func (u *DatadogUnifi) LogDatadogReport(r *Report) {
 | 
				
			||||||
	m := r.Metrics
 | 
						m := r.Metrics
 | 
				
			||||||
	idsMsg := fmt.Sprintf("IDS Events: %d, ", len(m.IDSList))
 | 
						u.Logger.Info("UniFi Metrics Recorded",
 | 
				
			||||||
	u.Collector.Logf("UniFi Metrics Recorded. Sites: %d, Clients: %d, "+
 | 
							zap.Int("num_sites", len(m.Sites)),
 | 
				
			||||||
		"UAP: %d, USG/UDM: %d, USW: %d, %sPoints: %d, Fields: %d, Errs: %d, Elapsed: %v",
 | 
							zap.Int("num_sites_dpi", len(m.SitesDPI)),
 | 
				
			||||||
		len(m.Sites), len(m.Clients), len(m.UAPs),
 | 
							zap.Int("num_clients", len(m.Clients)),
 | 
				
			||||||
		len(m.UDMs)+len(m.USGs), len(m.USWs), idsMsg, r.Total,
 | 
							zap.Int("num_clients_dpi", len(m.ClientsDPI)),
 | 
				
			||||||
		r.Fields, len(r.Errors), r.Elapsed.Round(time.Millisecond))
 | 
							zap.Int("num_rogue_ap", len(m.RogueAPs)),
 | 
				
			||||||
 | 
							zap.Int("num_devices", len(m.Devices)),
 | 
				
			||||||
 | 
							zap.Errors("errors", r.Errors),
 | 
				
			||||||
 | 
							zap.Duration("elapsed", r.Elapsed),
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1 +1,143 @@
 | 
				
			||||||
package datadogunifi
 | 
					package datadogunifi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/unpoller/unifi"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// These constants are used as names for printed/logged counters.
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						eventT = item("Event")
 | 
				
			||||||
 | 
						idsT   = item("IDS")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// batchIDS generates intrusion detection datapoints for Datadog.
 | 
				
			||||||
 | 
					func (u *DatadogUnifi) 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.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tagMap := map[string]string{
 | 
				
			||||||
 | 
							"dest_port":            strconv.Itoa(i.DestPort),
 | 
				
			||||||
 | 
							"src_port":             strconv.Itoa(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":            fmt.Sprintf("%d", i.DestIPGeo.Asn),
 | 
				
			||||||
 | 
							"dstip_latitude":       fmt.Sprintf("%0.6f", i.DestIPGeo.Latitude),
 | 
				
			||||||
 | 
							"dstip_longitude":      fmt.Sprintf("%0.6f", 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":            fmt.Sprintf("%d", i.SourceIPGeo.Asn),
 | 
				
			||||||
 | 
							"srcip_latitude":       fmt.Sprintf("%0.6f", i.SourceIPGeo.Latitude),
 | 
				
			||||||
 | 
							"srcip_longitude":      fmt.Sprintf("%0.6f", 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,
 | 
				
			||||||
 | 
							"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,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						r.addCount(idsT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tagMap = cleanTags(tagMap)
 | 
				
			||||||
 | 
						tags := tagMapToTags(tagMap)
 | 
				
			||||||
 | 
						title := fmt.Sprintf("Intrusion Detection at %s from %s", i.SiteName, i.SourceName)
 | 
				
			||||||
 | 
						r.reportEvent(title, i.Datetime, i.Msg, tags)
 | 
				
			||||||
 | 
						r.reportWarnLog(fmt.Sprintf("[%d] %s: %s", i.Datetime.Unix(), title, i.Msg), tagMapToZapFields(tagMap))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// batchEvents generates events from UniFi for Datadog.
 | 
				
			||||||
 | 
					func (u *DatadogUnifi) 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.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tagMap := map[string]string{
 | 
				
			||||||
 | 
							"guest":                i.Guest,    // mac address
 | 
				
			||||||
 | 
							"user":                 i.User,     // mac address
 | 
				
			||||||
 | 
							"host":                 i.Host,     // usg device?
 | 
				
			||||||
 | 
							"hostname":             i.Hostname, // client name
 | 
				
			||||||
 | 
							"dest_port":            strconv.Itoa(i.DestPort),
 | 
				
			||||||
 | 
							"src_port":             strconv.Itoa(i.SrcPort),
 | 
				
			||||||
 | 
							"dest_ip":              i.DestIP,
 | 
				
			||||||
 | 
							"dst_mac":              i.DstMAC,
 | 
				
			||||||
 | 
							"ip":                   i.IP,
 | 
				
			||||||
 | 
							"src_ip":               i.SrcIP,
 | 
				
			||||||
 | 
							"src_mac":              i.SrcMAC,
 | 
				
			||||||
 | 
							"dstip_asn":            fmt.Sprintf("%d", i.DestIPGeo.Asn),
 | 
				
			||||||
 | 
							"dstip_latitude":       fmt.Sprintf("%0.6f", i.DestIPGeo.Latitude),
 | 
				
			||||||
 | 
							"dstip_longitude":      fmt.Sprintf("%0.6f", 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":            fmt.Sprintf("%d", i.SourceIPGeo.Asn),
 | 
				
			||||||
 | 
							"srcip_latitude":       fmt.Sprintf("%0.6f", i.SourceIPGeo.Latitude),
 | 
				
			||||||
 | 
							"srcip_longitude":      fmt.Sprintf("%0.6f", 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,
 | 
				
			||||||
 | 
							"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,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						r.addCount(eventT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tagMap = cleanTags(tagMap)
 | 
				
			||||||
 | 
						tags := tagMapToTags(tagMap)
 | 
				
			||||||
 | 
						title := fmt.Sprintf("Unifi Event at %s from %s", i.SiteName, i.SourceName)
 | 
				
			||||||
 | 
						r.reportEvent(title, i.Datetime, i.Msg, tags)
 | 
				
			||||||
 | 
						r.reportInfoLog(fmt.Sprintf("[%d] %s: %s", i.Datetime.Unix(), title, i.Msg), tagMapToZapFields(tagMap))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,10 @@ go 1.15
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/DataDog/datadog-go v4.0.0+incompatible
 | 
						github.com/DataDog/datadog-go v4.0.0+incompatible
 | 
				
			||||||
	github.com/pkg/errors v0.9.1 // indirect
 | 
						github.com/pkg/errors v0.9.1 // indirect
 | 
				
			||||||
	github.com/unifi-poller/poller v0.0.7
 | 
						github.com/unpoller/poller v0.0.0-20210623101401-f12841d79a28
 | 
				
			||||||
	github.com/unifi-poller/unifi v0.0.5
 | 
						github.com/unpoller/unifi v0.0.9-0.20210623100314-3dccfdbc4c80
 | 
				
			||||||
	golift.io/cnfg v0.0.6
 | 
						github.com/unpoller/webserver v0.0.0-20210623101543-90d89bb0acdf
 | 
				
			||||||
 | 
						go.uber.org/zap v1.19.1
 | 
				
			||||||
 | 
						golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect
 | 
				
			||||||
 | 
						golift.io/cnfg v0.0.7
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,7 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ
 | 
				
			||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 | 
					github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 | 
				
			||||||
github.com/DataDog/datadog-go v4.0.0+incompatible h1:Dq8Dr+4sV1gBO1sHDWdW+4G+PdsA+YSJOK925MxrrCY=
 | 
					github.com/DataDog/datadog-go v4.0.0+incompatible h1:Dq8Dr+4sV1gBO1sHDWdW+4G+PdsA+YSJOK925MxrrCY=
 | 
				
			||||||
github.com/DataDog/datadog-go v4.0.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
 | 
					github.com/DataDog/datadog-go v4.0.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
 | 
				
			||||||
 | 
					github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
 | 
					github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
 | 
				
			||||||
| 
						 | 
					@ -9,33 +10,53 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7
 | 
				
			||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 | 
					github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 | 
				
			||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
					github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
				
			||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
					github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
				
			||||||
 | 
					github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
				
			||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
					github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
					github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
				
			||||||
github.com/spf13/pflag v1.0.6-0.20201009195203-85dd5c8bc61c h1:zqmyTlQyufRC65JnImJ6H1Sf7BDj8bG31EV919NVEQc=
 | 
					github.com/spf13/pflag v1.0.6-0.20201009195203-85dd5c8bc61c h1:zqmyTlQyufRC65JnImJ6H1Sf7BDj8bG31EV919NVEQc=
 | 
				
			||||||
github.com/spf13/pflag v1.0.6-0.20201009195203-85dd5c8bc61c/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 | 
					github.com/spf13/pflag v1.0.6-0.20201009195203-85dd5c8bc61c/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 | 
				
			||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
					github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
				
			||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
					github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
				
			||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
					github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
				
			||||||
github.com/unpoller/poller v0.0.0-20210623101401-f12841d79a28 h1:YAv5naMdpOFahnxteFFRidZlrSEwLv8V2nBKJKmLmHg=
 | 
					github.com/unpoller/poller v0.0.0-20210623101401-f12841d79a28 h1:YAv5naMdpOFahnxteFFRidZlrSEwLv8V2nBKJKmLmHg=
 | 
				
			||||||
github.com/unpoller/poller v0.0.0-20210623101401-f12841d79a28/go.mod h1:AbDp60t5WlLSRELAliMJ0RFQpm/0yXpyolVSZqNtero=
 | 
					github.com/unpoller/poller v0.0.0-20210623101401-f12841d79a28/go.mod h1:AbDp60t5WlLSRELAliMJ0RFQpm/0yXpyolVSZqNtero=
 | 
				
			||||||
github.com/unpoller/unifi v0.0.9-0.20210623100314-3dccfdbc4c80 h1:XjHGfJhMwnB63DYHgtWGJgDxLhxVcAOtf+cfuvpGoyo=
 | 
					github.com/unpoller/unifi v0.0.9-0.20210623100314-3dccfdbc4c80 h1:XjHGfJhMwnB63DYHgtWGJgDxLhxVcAOtf+cfuvpGoyo=
 | 
				
			||||||
github.com/unpoller/unifi v0.0.9-0.20210623100314-3dccfdbc4c80/go.mod h1:K9QFFGfZws4gzB+Popix19S/rBKqrtqI+tyPORyg3F0=
 | 
					github.com/unpoller/unifi v0.0.9-0.20210623100314-3dccfdbc4c80/go.mod h1:K9QFFGfZws4gzB+Popix19S/rBKqrtqI+tyPORyg3F0=
 | 
				
			||||||
github.com/unpoller/webserver v0.0.0-20210623101543-90d89bb0acdf h1:HhXi3qca3kyFEFPh0mqdr0bpQs94hJvMbUJztwPtf2A=
 | 
					github.com/unpoller/webserver v0.0.0-20210623101543-90d89bb0acdf h1:HhXi3qca3kyFEFPh0mqdr0bpQs94hJvMbUJztwPtf2A=
 | 
				
			||||||
github.com/unpoller/webserver v0.0.0-20210623101543-90d89bb0acdf/go.mod h1:77PywuUvspdtoRuH1htFhR3Tp0pLyWj6kJlYR4tBYho=
 | 
					github.com/unpoller/webserver v0.0.0-20210623101543-90d89bb0acdf/go.mod h1:77PywuUvspdtoRuH1htFhR3Tp0pLyWj6kJlYR4tBYho=
 | 
				
			||||||
 | 
					github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 | 
				
			||||||
 | 
					go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
 | 
				
			||||||
 | 
					go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 | 
				
			||||||
 | 
					go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
 | 
				
			||||||
 | 
					go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
 | 
				
			||||||
 | 
					go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
 | 
				
			||||||
 | 
					go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
 | 
				
			||||||
 | 
					go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
					golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
				
			||||||
 | 
					golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
					golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
 | 
					golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 | 
					golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 | 
				
			||||||
 | 
					golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 | 
				
			||||||
 | 
					golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 | 
				
			||||||
 | 
					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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
				
			||||||
 | 
					golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
				
			||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
					golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 | 
				
			||||||
 | 
					golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 | 
				
			||||||
golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
 | 
					golang.org/x/net v0.0.0-20210415231046-e915ea6b2b7d/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
 | 
				
			||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
 | 
					golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
 | 
				
			||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 | 
					golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 | 
				
			||||||
 | 
					golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
 | 
					golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
 | 
					golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
| 
						 | 
					@ -46,13 +67,21 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
				
			||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
					golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
				
			||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
					golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
					golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
				
			||||||
 | 
					golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
				
			||||||
 | 
					golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
				
			||||||
 | 
					golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 | 
				
			||||||
 | 
					golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
				
			||||||
 | 
					golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
				
			||||||
 | 
					golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
				
			||||||
golift.io/cnfg v0.0.7 h1:qkNpP5Bq+5Gtoc6HcI8kapMD5zFOVan6qguxqBQF3OY=
 | 
					golift.io/cnfg v0.0.7 h1:qkNpP5Bq+5Gtoc6HcI8kapMD5zFOVan6qguxqBQF3OY=
 | 
				
			||||||
golift.io/cnfg v0.0.7/go.mod h1:AsB0DJe7nv0bizKaoy3e3MjjOF7upTpMOMvsfv4CNNk=
 | 
					golift.io/cnfg v0.0.7/go.mod h1:AsB0DJe7nv0bizKaoy3e3MjjOF7upTpMOMvsfv4CNNk=
 | 
				
			||||||
golift.io/version v0.0.2 h1:i0gXRuSDHKs4O0sVDUg4+vNIuOxYoXhaxspftu2FRTE=
 | 
					golift.io/version v0.0.2 h1:i0gXRuSDHKs4O0sVDUg4+vNIuOxYoXhaxspftu2FRTE=
 | 
				
			||||||
golift.io/version v0.0.2/go.mod h1:76aHNz8/Pm7CbuxIsDi97jABL5Zui3f2uZxDm4vB6hU=
 | 
					golift.io/version v0.0.2/go.mod h1:76aHNz8/Pm7CbuxIsDi97jABL5Zui3f2uZxDm4vB6hU=
 | 
				
			||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
 | 
					gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/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.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
					gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
				
			||||||
 | 
					gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
					gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
					gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
				
			||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
 | 
					gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
package datadogunifi
 | 
					package datadogunifi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/unifi-poller/unifi"
 | 
						"github.com/unpoller/unifi"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// reportIDS generates intrusion detection datapoints for Datadog.
 | 
					// reportIDS generates intrusion detection datapoints for Datadog.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1 +1,38 @@
 | 
				
			||||||
package datadogunifi
 | 
					package datadogunifi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/unpoller/webserver"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Logf logs a message.
 | 
				
			||||||
 | 
					func (u *DatadogUnifi) 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 *DatadogUnifi) 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 *DatadogUnifi) 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...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,20 +2,78 @@ package datadogunifi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/unpoller/unifi"
 | 
				
			||||||
 | 
						"go.uber.org/zap"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func tag(name string, value interface{}) string {
 | 
					func tag(name string, value interface{}) string {
 | 
				
			||||||
	return fmt.Sprintf("%s:%v", name, value)
 | 
						return fmt.Sprintf("%s:%v", name, value)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func tagMapToTags(tagMap map[string]string) []string {
 | 
				
			||||||
 | 
						tags := make([]string, 0)
 | 
				
			||||||
 | 
						for k, v := range tagMap {
 | 
				
			||||||
 | 
							tags = append(tags, tag(k, v))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return tags
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func tagMapToZapFields(tagMap map[string]string) []zap.Field {
 | 
				
			||||||
 | 
						fields := make([]zap.Field, 0)
 | 
				
			||||||
 | 
						for k, v := range tagMap {
 | 
				
			||||||
 | 
							fields = append(fields, zap.String(k, v))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return fields
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func metricNamespace(namespace string) func(string) string {
 | 
					func metricNamespace(namespace string) func(string) string {
 | 
				
			||||||
	return func(name string) string {
 | 
						return func(name string) string {
 | 
				
			||||||
		return fmt.Sprintf("%s.%s", namespace, name)
 | 
							return fmt.Sprintf("unifi.%s.%s", namespace, name)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func reportGaugeForMap(r report, metricName func(string) string, data map[string]float64, tags []string) {
 | 
					func reportGaugeForFloat64Map(r report, metricName func(string) string, data map[string]float64, tags map[string]string) {
 | 
				
			||||||
	for name, value := range data {
 | 
						for name, value := range data {
 | 
				
			||||||
		r.reportGauge(metricName(name), value, tags)
 | 
							r.reportGauge(metricName(name), value, tagMapToTags(tags))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,39 +1,90 @@
 | 
				
			||||||
package datadogunifi
 | 
					package datadogunifi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/DataDog/datadog-go/statsd"
 | 
						"github.com/DataDog/datadog-go/statsd"
 | 
				
			||||||
	"github.com/unifi-poller/poller"
 | 
						"github.com/unpoller/poller"
 | 
				
			||||||
 | 
						"go.uber.org/zap"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Report struct {
 | 
					type Report struct {
 | 
				
			||||||
	Metrics *poller.Metrics
 | 
						Metrics *poller.Metrics
 | 
				
			||||||
 | 
						Events  *poller.Events
 | 
				
			||||||
	Errors  []error
 | 
						Errors  []error
 | 
				
			||||||
	Total   int
 | 
						Counts  *Counts
 | 
				
			||||||
	Fields  int
 | 
					 | 
				
			||||||
	Start   time.Time
 | 
						Start   time.Time
 | 
				
			||||||
	End     time.Time
 | 
						End     time.Time
 | 
				
			||||||
	Elapsed time.Duration
 | 
						Elapsed time.Duration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Logger *zap.SugaredLogger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Total  int
 | 
				
			||||||
 | 
						Fields int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wg sync.WaitGroup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	client statsd.ClientInterface
 | 
						client statsd.ClientInterface
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Counts holds counters and has a lock to deal with routines.
 | 
				
			||||||
 | 
					type Counts struct {
 | 
				
			||||||
 | 
						Val map[item]int
 | 
				
			||||||
 | 
						sync.RWMutex
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type report interface {
 | 
					type report interface {
 | 
				
			||||||
 | 
						add()
 | 
				
			||||||
 | 
						done()
 | 
				
			||||||
	error(err error)
 | 
						error(err error)
 | 
				
			||||||
	metrics() *poller.Metrics
 | 
						metrics() *poller.Metrics
 | 
				
			||||||
 | 
						events() *poller.Events
 | 
				
			||||||
 | 
						addCount(item, ...int)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	reportGauge(name string, value float64, tags []string) error
 | 
						reportGauge(name string, value float64, tags []string) error
 | 
				
			||||||
	reportCount(name string, value int64, tags []string) error
 | 
						reportCount(name string, value int64, tags []string) error
 | 
				
			||||||
	reportDistribution(name string, value float64, tags []string) error
 | 
						reportDistribution(name string, value float64, tags []string) error
 | 
				
			||||||
	reportTiming(name string, value time.Duration, tags []string) error
 | 
						reportTiming(name string, value time.Duration, tags []string) error
 | 
				
			||||||
	reportEvent(title string, message string, tags []string) error
 | 
						reportEvent(title string, date time.Time, message string, tags []string) error
 | 
				
			||||||
 | 
						reportInfoLog(message string, f ...interface{})
 | 
				
			||||||
 | 
						reportWarnLog(message string, f ...interface{})
 | 
				
			||||||
	reportServiceCheck(name string, status statsd.ServiceCheckStatus, message string, tags []string) error
 | 
						reportServiceCheck(name string, status statsd.ServiceCheckStatus, message string, tags []string) error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Report) add() {
 | 
				
			||||||
 | 
						r.wg.Add(1)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Report) done() {
 | 
				
			||||||
 | 
						r.wg.Done()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *Report) metrics() *poller.Metrics {
 | 
					func (r *Report) metrics() *poller.Metrics {
 | 
				
			||||||
	return r.Metrics
 | 
						return r.Metrics
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Report) events() *poller.Events {
 | 
				
			||||||
 | 
						return r.Events
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 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) {
 | 
					func (r *Report) error(err error) {
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		r.Errors = append(r.Errors, err)
 | 
							r.Errors = append(r.Errors, err)
 | 
				
			||||||
| 
						 | 
					@ -56,15 +107,26 @@ func (r *Report) reportTiming(name string, value time.Duration, tags []string) e
 | 
				
			||||||
	return r.client.Timing(name, value, tags, 1.0)
 | 
						return r.client.Timing(name, value, tags, 1.0)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *Report) reportEvent(title string, message string, tags []string) error {
 | 
					func (r *Report) reportEvent(title string, date time.Time, message string, tags []string) error {
 | 
				
			||||||
 | 
						if date.IsZero() {
 | 
				
			||||||
 | 
							date = time.Now()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return r.client.Event(&statsd.Event{
 | 
						return r.client.Event(&statsd.Event{
 | 
				
			||||||
		Title:     title,
 | 
							Title:     title,
 | 
				
			||||||
		Text:      message,
 | 
							Text:      message,
 | 
				
			||||||
		Timestamp: time.Now(),
 | 
							Timestamp: date,
 | 
				
			||||||
		Tags:      tags,
 | 
							Tags:      tags,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Report) reportInfoLog(message string, f ...interface{}) {
 | 
				
			||||||
 | 
						r.Logger.Info(message, f)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r *Report) reportWarnLog(message string, f ...interface{}) {
 | 
				
			||||||
 | 
						r.Logger.Warn(message, f)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *Report) reportServiceCheck(name string, status statsd.ServiceCheckStatus, message string, tags []string) error {
 | 
					func (r *Report) reportServiceCheck(name string, status statsd.ServiceCheckStatus, message string, tags []string) error {
 | 
				
			||||||
	return r.client.ServiceCheck(&statsd.ServiceCheck{
 | 
						return r.client.ServiceCheck(&statsd.ServiceCheck{
 | 
				
			||||||
		Name:      name,
 | 
							Name:      name,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
package datadogunifi
 | 
					package datadogunifi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/unifi-poller/unifi"
 | 
						"github.com/unpoller/unifi"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// reportSite generates Unifi Sites' datapoints for Datadog.
 | 
					// reportSite generates Unifi Sites' datapoints for Datadog.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,57 +1,93 @@
 | 
				
			||||||
package datadogunifi
 | 
					package datadogunifi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/unifi-poller/unifi"
 | 
						"github.com/unpoller/unifi"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// reportUAP generates Wireless-Access-Point datapoints for InfluxDB.
 | 
					// uapT is used as a name for printed/logged counters.
 | 
				
			||||||
// These points can be passed directly to influx.
 | 
					const uapT = item("UAP")
 | 
				
			||||||
func (u *DatadogUnifi) reportUAP(r report, s *unifi.UAP) {
 | 
					
 | 
				
			||||||
 | 
					// batchRogueAP generates metric points for neighboring access points.
 | 
				
			||||||
 | 
					func (u *DatadogUnifi) batchRogueAP(r report, s *unifi.RogueAP) {
 | 
				
			||||||
 | 
						if s.Age.Val == 0 {
 | 
				
			||||||
 | 
							return // only keep metrics for things that are recent.
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tags := cleanTags(map[string]string{
 | 
				
			||||||
 | 
							"security":   s.Security,
 | 
				
			||||||
 | 
							"oui":        s.Oui,
 | 
				
			||||||
 | 
							"band":       s.Band,
 | 
				
			||||||
 | 
							"mac":        s.Bssid,
 | 
				
			||||||
 | 
							"ap_mac":     s.ApMac,
 | 
				
			||||||
 | 
							"radio":      s.Radio,
 | 
				
			||||||
 | 
							"radio_name": s.RadioName,
 | 
				
			||||||
 | 
							"site_name":  s.SiteName,
 | 
				
			||||||
 | 
							"name":       s.Essid,
 | 
				
			||||||
 | 
							"source":     s.SourceName,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data := map[string]float64{
 | 
				
			||||||
 | 
							"age":         s.Age.Val,
 | 
				
			||||||
 | 
							"bw":          s.Bw.Val,
 | 
				
			||||||
 | 
							"center_freq": s.CenterFreq.Val,
 | 
				
			||||||
 | 
							"channel":     float64(s.Channel),
 | 
				
			||||||
 | 
							"freq":        s.Freq.Val,
 | 
				
			||||||
 | 
							"noise":       s.Noise.Val,
 | 
				
			||||||
 | 
							"rssi":        s.Rssi.Val,
 | 
				
			||||||
 | 
							"rssi_age":    s.RssiAge.Val,
 | 
				
			||||||
 | 
							"signal":      s.Signal.Val,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						metricName := metricNamespace("uap_rogue")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reportGaugeForFloat64Map(r, metricName, data, tags)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// batchUAP generates Wireless-Access-Point datapoints for Datadog.
 | 
				
			||||||
 | 
					// These points can be passed directly to datadog.
 | 
				
			||||||
 | 
					func (u *DatadogUnifi) batchUAP(r report, s *unifi.UAP) {
 | 
				
			||||||
	if !s.Adopted.Val || s.Locating.Val {
 | 
						if !s.Adopted.Val || s.Locating.Val {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tags := []string{
 | 
						tags := cleanTags(map[string]string{
 | 
				
			||||||
		tag("ip", s.IP),
 | 
							"mac":       s.Mac,
 | 
				
			||||||
		tag("mac", s.Mac),
 | 
							"site_name": s.SiteName,
 | 
				
			||||||
		tag("site_name", s.SiteName),
 | 
							"source":    s.SourceName,
 | 
				
			||||||
		tag("source", s.SourceName),
 | 
							"name":      s.Name,
 | 
				
			||||||
		tag("name", s.Name),
 | 
							"version":   s.Version,
 | 
				
			||||||
		tag("version", s.Version),
 | 
							"model":     s.Model,
 | 
				
			||||||
		tag("model", s.Model),
 | 
							"serial":    s.Serial,
 | 
				
			||||||
		tag("serial", s.Serial),
 | 
							"type":      s.Type,
 | 
				
			||||||
		tag("type", s.Type),
 | 
							"ip":        s.IP,
 | 
				
			||||||
	}
 | 
						})
 | 
				
			||||||
 | 
						data := CombineFloat64(u.processUAPstats(s.Stat.Ap), u.batchSysStats(s.SysStats, s.SystemStats))
 | 
				
			||||||
 | 
						data["bytes"] = s.Bytes.Val
 | 
				
			||||||
 | 
						data["last_seen"] = s.LastSeen.Val
 | 
				
			||||||
 | 
						data["rx_bytes"] = s.RxBytes.Val
 | 
				
			||||||
 | 
						data["tx_bytes"] = s.TxBytes.Val
 | 
				
			||||||
 | 
						data["uptime"] = s.Uptime.Val
 | 
				
			||||||
 | 
						data["user_num_sta"] = s.UserNumSta.Val
 | 
				
			||||||
 | 
						data["guest_num_sta"] = s.GuestNumSta.Val
 | 
				
			||||||
 | 
						data["num_sta"] = s.NumSta.Val
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						r.addCount(uapT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	metricName := metricNamespace("uap")
 | 
						metricName := metricNamespace("uap")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	u.reportUAPstats(s.Stat.Ap, r, metricName, tags)
 | 
						reportGaugeForFloat64Map(r, metricName, data, tags)
 | 
				
			||||||
	u.reportSysStats(r, metricName, s.SysStats, s.SystemStats, tags)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data := map[string]float64{
 | 
						u.processVAPTable(r, tags, s.VapTable)
 | 
				
			||||||
		"bytes":         s.Bytes.Val,
 | 
						u.batchPortTable(r, tags, s.PortTable)
 | 
				
			||||||
		"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) {
 | 
					func (u *DatadogUnifi) processUAPstats(ap *unifi.Ap) map[string]float64 {
 | 
				
			||||||
	if ap == nil {
 | 
						if ap == nil {
 | 
				
			||||||
		return
 | 
							return map[string]float64{}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Accumulative Statistics.
 | 
						// Accumulative Statistics.
 | 
				
			||||||
	data := map[string]float64{
 | 
						return map[string]float64{
 | 
				
			||||||
		"stat_user-rx_packets":  ap.UserRxPackets.Val,
 | 
							"stat_user-rx_packets":  ap.UserRxPackets.Val,
 | 
				
			||||||
		"stat_guest-rx_packets": ap.GuestRxPackets.Val,
 | 
							"stat_guest-rx_packets": ap.GuestRxPackets.Val,
 | 
				
			||||||
		"stat_rx_packets":       ap.RxPackets.Val,
 | 
							"stat_rx_packets":       ap.RxPackets.Val,
 | 
				
			||||||
| 
						 | 
					@ -85,27 +121,26 @@ func (u *DatadogUnifi) reportUAPstats(ap *unifi.Ap, r report, metricName func(st
 | 
				
			||||||
		"stat_user-tx_retries":  ap.UserTxRetries.Val,
 | 
							"stat_user-tx_retries":  ap.UserTxRetries.Val,
 | 
				
			||||||
		"stat_guest-tx_retries": ap.GuestTxRetries.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.
 | 
					// processVAPTable 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
 | 
					func (u *DatadogUnifi) processVAPTable(r report, t map[string]string, vt unifi.VapTable) { // nolint: funlen
 | 
				
			||||||
	for _, s := range vt {
 | 
						for _, s := range vt {
 | 
				
			||||||
		tags := []string{
 | 
							tags := map[string]string{
 | 
				
			||||||
			tag("device_name", deviceName),
 | 
								"device_name": t["name"],
 | 
				
			||||||
			tag("site_name", siteName),
 | 
								"site_name":   t["site_name"],
 | 
				
			||||||
			tag("source", source),
 | 
								"source":      t["source"],
 | 
				
			||||||
			tag("ap_mac", s.ApMac),
 | 
								"ap_mac":      s.ApMac,
 | 
				
			||||||
			tag("bssid", s.Bssid),
 | 
								"bssid":       s.Bssid,
 | 
				
			||||||
			tag("id", s.ID),
 | 
								"id":          s.ID,
 | 
				
			||||||
			tag("name", s.Name),
 | 
								"name":        s.Name,
 | 
				
			||||||
			tag("radio_name", s.RadioName),
 | 
								"radio_name":  s.RadioName,
 | 
				
			||||||
			tag("radio", s.Radio),
 | 
								"radio":       s.Radio,
 | 
				
			||||||
			tag("essid", s.Essid),
 | 
								"essid":       s.Essid,
 | 
				
			||||||
			tag("site_id", s.SiteID),
 | 
								"site_id":     s.SiteID,
 | 
				
			||||||
			tag("usage", s.Usage),
 | 
								"usage":       s.Usage,
 | 
				
			||||||
			tag("state", s.State),
 | 
								"state":       s.State,
 | 
				
			||||||
			tag("is_guest", s.IsGuest.Txt),
 | 
								"is_guest":    s.IsGuest.Txt,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		data := map[string]float64{
 | 
							data := map[string]float64{
 | 
				
			||||||
			"ccq":                       float64(s.Ccq),
 | 
								"ccq":                       float64(s.Ccq),
 | 
				
			||||||
| 
						 | 
					@ -150,22 +185,23 @@ func (u *DatadogUnifi) reportVAPTable(r report, deviceName string, siteName stri
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		metricName := metricNamespace("uap_vaps")
 | 
							metricName := metricNamespace("uap_vaps")
 | 
				
			||||||
		reportGaugeForMap(r, metricName, data, tags)
 | 
					
 | 
				
			||||||
 | 
							reportGaugeForFloat64Map(r, metricName, data, tags)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (u *DatadogUnifi) reportRadTable(r report, deviceName string, siteName string, source string, rt unifi.RadioTable, rts unifi.RadioTableStats) {
 | 
					func (u *DatadogUnifi) processRadTable(r report, t map[string]string, rt unifi.RadioTable, rts unifi.RadioTableStats) {
 | 
				
			||||||
	for _, p := range rt {
 | 
						for _, p := range rt {
 | 
				
			||||||
		tags := []string{
 | 
							tags := map[string]string{
 | 
				
			||||||
			tag("device_name", deviceName),
 | 
								"device_name": t["name"],
 | 
				
			||||||
			tag("site_name", siteName),
 | 
								"site_name":   t["site_name"],
 | 
				
			||||||
			tag("source", source),
 | 
								"source":      t["source"],
 | 
				
			||||||
			tag("channel", p.Channel.Txt),
 | 
								"channel":     p.Channel.Txt,
 | 
				
			||||||
			tag("radio", p.Radio),
 | 
								"radio":       p.Radio,
 | 
				
			||||||
 | 
								"ht":          p.Ht.Txt,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		data := map[string]float64{
 | 
							data := map[string]float64{
 | 
				
			||||||
			"current_antenna_gain": p.CurrentAntennaGain.Val,
 | 
								"current_antenna_gain": p.CurrentAntennaGain.Val,
 | 
				
			||||||
			"ht":                   p.Ht.Val,
 | 
					 | 
				
			||||||
			"max_txpower":          p.MaxTxpower.Val,
 | 
								"max_txpower":          p.MaxTxpower.Val,
 | 
				
			||||||
			"min_txpower":          p.MinTxpower.Val,
 | 
								"min_txpower":          p.MinTxpower.Val,
 | 
				
			||||||
			"nss":                  p.Nss.Val,
 | 
								"nss":                  p.Nss.Val,
 | 
				
			||||||
| 
						 | 
					@ -181,12 +217,12 @@ func (u *DatadogUnifi) reportRadTable(r report, deviceName string, siteName stri
 | 
				
			||||||
				data["cu_total"] = t.CuTotal.Val
 | 
									data["cu_total"] = t.CuTotal.Val
 | 
				
			||||||
				data["extchannel"] = t.Extchannel.Val
 | 
									data["extchannel"] = t.Extchannel.Val
 | 
				
			||||||
				data["gain"] = t.Gain.Val
 | 
									data["gain"] = t.Gain.Val
 | 
				
			||||||
				data["guest-num_sta"] = t.GuestNumSta.Val
 | 
									data["guest_num_sta"] = t.GuestNumSta.Val
 | 
				
			||||||
				data["num_sta"] = t.NumSta.Val
 | 
									data["num_sta"] = t.NumSta.Val
 | 
				
			||||||
				data["tx_packets"] = t.TxPackets.Val
 | 
									data["tx_packets"] = t.TxPackets.Val
 | 
				
			||||||
				data["tx_power"] = t.TxPower.Val
 | 
									data["tx_power"] = t.TxPower.Val
 | 
				
			||||||
				data["tx_retries"] = t.TxRetries.Val
 | 
									data["tx_retries"] = t.TxRetries.Val
 | 
				
			||||||
				data["user-num_sta"] = t.UserNumSta.Val
 | 
									data["user_num_sta"] = t.UserNumSta.Val
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				break
 | 
									break
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -194,6 +230,6 @@ func (u *DatadogUnifi) reportRadTable(r report, deviceName string, siteName stri
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		metricName := metricNamespace("uap_radios")
 | 
							metricName := metricNamespace("uap_radios")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		reportGaugeForMap(r, metricName, data, tags)
 | 
							reportGaugeForFloat64Map(r, metricName, data, tags)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +1,44 @@
 | 
				
			||||||
package datadogunifi
 | 
					package datadogunifi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/unifi-poller/unifi"
 | 
						"github.com/unpoller/unifi"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// reportSysStats is used by all device types.
 | 
					// udmT is used as a name for printed/logged counters.
 | 
				
			||||||
func (u *DatadogUnifi) reportSysStats(r report, metricName func(string) string, s unifi.SysStats, ss unifi.SystemStats, tags []string) {
 | 
					const udmT = item("UDM")
 | 
				
			||||||
	data := map[string]float64{
 | 
					
 | 
				
			||||||
 | 
					// 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{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i := range in {
 | 
				
			||||||
 | 
							for k := range in[i] {
 | 
				
			||||||
 | 
								out[k] = in[i][k]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return out
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// CombineFloat64 concatenates N maps. This will delete things if not used with caution.
 | 
				
			||||||
 | 
					func CombineFloat64(in ...map[string]float64) map[string]float64 {
 | 
				
			||||||
 | 
						out := make(map[string]float64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i := range in {
 | 
				
			||||||
 | 
							for k := range in[i] {
 | 
				
			||||||
 | 
								out[k] = in[i][k]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return out
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// batchSysStats is used by all device types.
 | 
				
			||||||
 | 
					func (u *DatadogUnifi) batchSysStats(s unifi.SysStats, ss unifi.SystemStats) map[string]float64 {
 | 
				
			||||||
 | 
						m := map[string]float64{
 | 
				
			||||||
		"loadavg_1":     s.Loadavg1.Val,
 | 
							"loadavg_1":     s.Loadavg1.Val,
 | 
				
			||||||
		"loadavg_5":     s.Loadavg5.Val,
 | 
							"loadavg_5":     s.Loadavg5.Val,
 | 
				
			||||||
		"loadavg_15":    s.Loadavg15.Val,
 | 
							"loadavg_15":    s.Loadavg15.Val,
 | 
				
			||||||
| 
						 | 
					@ -19,123 +49,148 @@ func (u *DatadogUnifi) reportSysStats(r report, metricName func(string) string,
 | 
				
			||||||
		"mem":           ss.Mem.Val,
 | 
							"mem":           ss.Mem.Val,
 | 
				
			||||||
		"system_uptime": ss.Uptime.Val,
 | 
							"system_uptime": ss.Uptime.Val,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for name, value := range data {
 | 
					
 | 
				
			||||||
		r.reportGauge(metricName(name), value, tags)
 | 
						for k, v := range ss.Temps {
 | 
				
			||||||
 | 
							temp, _ := strconv.Atoi(strings.Split(v, " ")[0])
 | 
				
			||||||
 | 
							k = strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(k, " ", "_"), ")", ""), "(", "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if temp != 0 && k != "" {
 | 
				
			||||||
 | 
								m["temp_"+strings.ToLower(k)] = float64(temp)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return m
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (u *DatadogUnifi) reportUDMtemps(r report, metricName func(string) string, tags []string, temps []unifi.Temperature) {
 | 
					func (u *DatadogUnifi) batchUDMtemps(temps []unifi.Temperature) map[string]float64 {
 | 
				
			||||||
 | 
						output := make(map[string]float64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, t := range temps {
 | 
						for _, t := range temps {
 | 
				
			||||||
		name := fmt.Sprintf("temp_%s", t.Name)
 | 
							output["temp_"+t.Name] = t.Value
 | 
				
			||||||
		r.reportGauge(metricName(name), t.Value, tags)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return output
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// reportUDM generates Unifi Gateway datapoints for InfluxDB.
 | 
					func (u *DatadogUnifi) batchUDMstorage(storage []*unifi.Storage) map[string]float64 {
 | 
				
			||||||
// These points can be passed directly to influx.
 | 
						output := make(map[string]float64)
 | 
				
			||||||
func (u *DatadogUnifi) reportUDM(r report, s *unifi.UDM) { // nolint: funlen
 | 
					
 | 
				
			||||||
 | 
						for _, t := range storage {
 | 
				
			||||||
 | 
							output["storage_"+t.Name+"_size"] = t.Size.Val
 | 
				
			||||||
 | 
							output["storage_"+t.Name+"_used"] = t.Used.Val
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if t.Size.Val != 0 && t.Used.Val != 0 && t.Used.Val < t.Size.Val {
 | 
				
			||||||
 | 
								output["storage_"+t.Name+"_pct"] = t.Used.Val / t.Size.Val * 100 //nolint:gomnd
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								output["storage_"+t.Name+"_pct"] = 0
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return output
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// batchUDM generates Unifi Gateway datapoints for Datadog.
 | 
				
			||||||
 | 
					// These points can be passed directly to datadog.
 | 
				
			||||||
 | 
					func (u *DatadogUnifi) batchUDM(r report, s *unifi.UDM) { // nolint: funlen
 | 
				
			||||||
	if !s.Adopted.Val || s.Locating.Val {
 | 
						if !s.Adopted.Val || s.Locating.Val {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tags := cleanTags(map[string]string{
 | 
				
			||||||
 | 
							"source":        s.SourceName,
 | 
				
			||||||
 | 
							"mac":           s.Mac,
 | 
				
			||||||
 | 
							"site_name":     s.SiteName,
 | 
				
			||||||
 | 
							"name":          s.Name,
 | 
				
			||||||
 | 
							"version":       s.Version,
 | 
				
			||||||
 | 
							"model":         s.Model,
 | 
				
			||||||
 | 
							"serial":        s.Serial,
 | 
				
			||||||
 | 
							"type":          s.Type,
 | 
				
			||||||
 | 
							"ip":            s.IP,
 | 
				
			||||||
 | 
							"license_state": s.LicenseState,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						data := CombineFloat64(
 | 
				
			||||||
 | 
							u.batchUDMstorage(s.Storage),
 | 
				
			||||||
 | 
							u.batchUDMtemps(s.Temperatures),
 | 
				
			||||||
 | 
							u.batchUSGstats(s.SpeedtestStatus, s.Stat.Gw, s.Uplink),
 | 
				
			||||||
 | 
							u.batchSysStats(s.SysStats, s.SystemStats),
 | 
				
			||||||
 | 
							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,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						r.addCount(udmT)
 | 
				
			||||||
	metricName := metricNamespace("usg")
 | 
						metricName := metricNamespace("usg")
 | 
				
			||||||
 | 
						reportGaugeForFloat64Map(r, metricName, data, tags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tags := []string{
 | 
						u.batchNetTable(r, tags, s.NetworkTable)
 | 
				
			||||||
		tag("source", s.SourceName),
 | 
						u.batchUSGwans(r, tags, s.Wan1, s.Wan2)
 | 
				
			||||||
		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{
 | 
						tags = cleanTags(map[string]string{
 | 
				
			||||||
		"bytes":         s.Bytes.Val,
 | 
							"mac":       s.Mac,
 | 
				
			||||||
		"last_seen":     s.LastSeen.Val,
 | 
							"site_name": s.SiteName,
 | 
				
			||||||
		"guest-num_sta": s.GuestNumSta.Val,
 | 
							"source":    s.SourceName,
 | 
				
			||||||
		"rx_bytes":      s.RxBytes.Val,
 | 
							"name":      s.Name,
 | 
				
			||||||
		"tx_bytes":      s.TxBytes.Val,
 | 
							"version":   s.Version,
 | 
				
			||||||
		"uptime":        s.Uptime.Val,
 | 
							"model":     s.Model,
 | 
				
			||||||
		"state":         s.State.Val,
 | 
							"serial":    s.Serial,
 | 
				
			||||||
		"user-num_sta":  s.UserNumSta.Val,
 | 
							"type":      s.Type,
 | 
				
			||||||
		"num_desktop":   s.NumDesktop.Val,
 | 
							"ip":        s.IP,
 | 
				
			||||||
		"num_handheld":  s.NumHandheld.Val,
 | 
						})
 | 
				
			||||||
		"num_mobile":    s.NumMobile.Val,
 | 
						data = CombineFloat64(
 | 
				
			||||||
	}
 | 
							u.batchUSWstat(s.Stat.Sw),
 | 
				
			||||||
	for name, value := range data {
 | 
							map[string]float64{
 | 
				
			||||||
		r.reportGauge(metricName(name), value, tags)
 | 
								"guest-num_sta": s.GuestNumSta.Val,
 | 
				
			||||||
	}
 | 
								"bytes":         s.Bytes.Val,
 | 
				
			||||||
	u.reportNetTable(r, s.Name, s.SiteName, s.SourceName, s.NetworkTable)
 | 
								"last_seen":     s.LastSeen.Val,
 | 
				
			||||||
	u.reportUSGwans(r, s.Name, s.SiteName, s.SourceName, s.Wan1, s.Wan2)
 | 
								"rx_bytes":      s.RxBytes.Val,
 | 
				
			||||||
 | 
								"tx_bytes":      s.TxBytes.Val,
 | 
				
			||||||
 | 
								"uptime":        s.Uptime.Val,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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")
 | 
						metricName = metricNamespace("usw")
 | 
				
			||||||
	u.reportUSWstat(r, metricName, tags, s.Stat.Sw)
 | 
						reportGaugeForFloat64Map(r, metricName, data, tags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data = map[string]float64{
 | 
						u.batchPortTable(r, tags, s.PortTable) // udm has a usw in it.
 | 
				
			||||||
		"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 {
 | 
						if s.Stat.Ap == nil {
 | 
				
			||||||
		return // we're done now. the following code process UDM (non-pro) UAP data.
 | 
							return // we're done now. the following code process UDM (non-pro) UAP data.
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tags = []string{
 | 
						tags = cleanTags(map[string]string{
 | 
				
			||||||
		tag("mac", s.Mac),
 | 
							"mac":       s.Mac,
 | 
				
			||||||
		tag("site_name", s.SiteName),
 | 
							"site_name": s.SiteName,
 | 
				
			||||||
		tag("source", s.SourceName),
 | 
							"source":    s.SourceName,
 | 
				
			||||||
		tag("name", s.Name),
 | 
							"name":      s.Name,
 | 
				
			||||||
		tag("version", s.Version),
 | 
							"version":   s.Version,
 | 
				
			||||||
		tag("model", s.Model),
 | 
							"model":     s.Model,
 | 
				
			||||||
		tag("serial", s.Serial),
 | 
							"serial":    s.Serial,
 | 
				
			||||||
		tag("type", s.Type),
 | 
							"type":      s.Type,
 | 
				
			||||||
	}
 | 
							"ip":        s.IP,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						data = u.processUAPstats(s.Stat.Ap)
 | 
				
			||||||
 | 
						data["bytes"] = s.Bytes.Val
 | 
				
			||||||
 | 
						data["last_seen"] = s.LastSeen.Val
 | 
				
			||||||
 | 
						data["rx_bytes"] = s.RxBytes.Val
 | 
				
			||||||
 | 
						data["tx_bytes"] = s.TxBytes.Val
 | 
				
			||||||
 | 
						data["uptime"] = s.Uptime.Val
 | 
				
			||||||
 | 
						data["state"] = s.State.Val
 | 
				
			||||||
 | 
						data["user-num_sta"] = s.UserNumSta.Val
 | 
				
			||||||
 | 
						data["guest-num_sta"] = s.GuestNumSta.Val
 | 
				
			||||||
 | 
						data["num_sta"] = s.NumSta.Val
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	metricName = metricNamespace("uap")
 | 
						metricName = metricNamespace("uap")
 | 
				
			||||||
	u.reportUAPstats(s.Stat.Ap, r, metricName, tags)
 | 
						reportGaugeForFloat64Map(r, metricName, data, tags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data = map[string]float64{
 | 
						u.processRadTable(r, tags, *s.RadioTable, *s.RadioTableStats)
 | 
				
			||||||
		"bytes":         s.Bytes.Val,
 | 
						u.processVAPTable(r, tags, *s.VapTable)
 | 
				
			||||||
		"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)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,56 +1,65 @@
 | 
				
			||||||
package datadogunifi
 | 
					package datadogunifi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"github.com/unifi-poller/unifi"
 | 
						"github.com/unpoller/unifi"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// reportUSG generates Unifi Gateway datapoints for Datadog.
 | 
					// usgT is used as a name for printed/logged counters.
 | 
				
			||||||
 | 
					const usgT = item("USG")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// batchUSG generates Unifi Gateway datapoints for Datadog.
 | 
				
			||||||
// These points can be passed directly to datadog.
 | 
					// These points can be passed directly to datadog.
 | 
				
			||||||
func (u *DatadogUnifi) reportUSG(r report, s *unifi.USG) {
 | 
					func (u *DatadogUnifi) batchUSG(r report, s *unifi.USG) {
 | 
				
			||||||
	if !s.Adopted.Val || s.Locating.Val {
 | 
						if !s.Adopted.Val || s.Locating.Val {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tags := []string{
 | 
						tags := map[string]string{
 | 
				
			||||||
		tag("mac", s.Mac),
 | 
							"mac":           s.Mac,
 | 
				
			||||||
		tag("site_name", s.SiteName),
 | 
							"site_name":     s.SiteName,
 | 
				
			||||||
		tag("source", s.SourceName),
 | 
							"source":        s.SourceName,
 | 
				
			||||||
		tag("name", s.Name),
 | 
							"name":          s.Name,
 | 
				
			||||||
		tag("version", s.Version),
 | 
							"version":       s.Version,
 | 
				
			||||||
		tag("model", s.Model),
 | 
							"model":         s.Model,
 | 
				
			||||||
		tag("serial", s.Serial),
 | 
							"serial":        s.Serial,
 | 
				
			||||||
		tag("type", s.Type),
 | 
							"type":          s.Type,
 | 
				
			||||||
		tag("ip", s.IP),
 | 
							"ip":            s.IP,
 | 
				
			||||||
		tag("license_state", s.LicenseState),
 | 
							"license_state": s.LicenseState,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						data := CombineFloat64(
 | 
				
			||||||
 | 
							u.batchUDMtemps(s.Temperatures),
 | 
				
			||||||
 | 
							u.batchSysStats(s.SysStats, s.SystemStats),
 | 
				
			||||||
 | 
							u.batchUSGstats(s.SpeedtestStatus, s.Stat.Gw, s.Uplink),
 | 
				
			||||||
 | 
							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,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						r.addCount(usgT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	metricName := metricNamespace("usg")
 | 
						metricName := metricNamespace("usg")
 | 
				
			||||||
	u.reportSysStats(r, metricName, s.SysStats, s.SystemStats, tags)
 | 
						reportGaugeForFloat64Map(r, metricName, data, tags)
 | 
				
			||||||
	u.reportUSGstats(r, metricName, tags, s.SpeedtestStatus, s.Stat.Gw, s.Uplink)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data := map[string]float64{
 | 
						u.batchNetTable(r, tags, s.NetworkTable)
 | 
				
			||||||
		"bytes":         s.Bytes.Val,
 | 
						u.batchUSGwans(r, tags, s.Wan1, s.Wan2)
 | 
				
			||||||
		"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) {
 | 
					func (u *DatadogUnifi) batchUSGstats(ss unifi.SpeedtestStatus, gw *unifi.Gw, ul unifi.Uplink) map[string]float64 {
 | 
				
			||||||
	if gw == nil {
 | 
						if gw == nil {
 | 
				
			||||||
		return
 | 
							return map[string]float64{}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	data := map[string]float64{
 | 
					
 | 
				
			||||||
 | 
						return map[string]float64{
 | 
				
			||||||
		"uplink_latency":                 ul.Latency.Val,
 | 
							"uplink_latency":                 ul.Latency.Val,
 | 
				
			||||||
		"uplink_speed":                   ul.Speed.Val,
 | 
							"uplink_speed":                   ul.Speed.Val,
 | 
				
			||||||
		"speedtest-status_latency":       ss.Latency.Val,
 | 
							"speedtest-status_latency":       ss.Latency.Val,
 | 
				
			||||||
| 
						 | 
					@ -65,33 +74,32 @@ func (u *DatadogUnifi) reportUSGstats(r report, metricName func(string) string,
 | 
				
			||||||
		"lan-tx_packets":                 gw.LanTxPackets.Val,
 | 
							"lan-tx_packets":                 gw.LanTxPackets.Val,
 | 
				
			||||||
		"lan-rx_dropped":                 gw.LanRxDropped.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) {
 | 
					func (u *DatadogUnifi) batchUSGwans(r report, tags map[string]string, wans ...unifi.Wan) {
 | 
				
			||||||
	for _, wan := range wans {
 | 
						for _, wan := range wans {
 | 
				
			||||||
		if !wan.Up.Val {
 | 
							if !wan.Up.Val {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		tags := []string{
 | 
							tags := cleanTags(map[string]string{
 | 
				
			||||||
			tag("device_name", deviceName),
 | 
								"device_name": tags["name"],
 | 
				
			||||||
			tag("site_name", siteName),
 | 
								"site_name":   tags["site_name"],
 | 
				
			||||||
			tag("source", source),
 | 
								"source":      tags["source"],
 | 
				
			||||||
			tag("ip", wan.IP),
 | 
								"ip":          wan.IP,
 | 
				
			||||||
			tag("purpose", wan.Name),
 | 
								"purpose":     wan.Name,
 | 
				
			||||||
			tag("mac", wan.Mac),
 | 
								"mac":         wan.Mac,
 | 
				
			||||||
			tag("ifname", wan.Ifname),
 | 
								"ifname":      wan.Ifname,
 | 
				
			||||||
			tag("type", wan.Type),
 | 
								"type":        wan.Type,
 | 
				
			||||||
			tag("up", wan.Up.Txt),
 | 
								"up":          wan.Up.Txt,
 | 
				
			||||||
			tag("enabled", wan.Enable.Txt),
 | 
								"enabled":     wan.Enable.Txt,
 | 
				
			||||||
			tag("gateway", wan.Gateway),
 | 
								"gateway":     wan.Gateway,
 | 
				
			||||||
		}
 | 
							})
 | 
				
			||||||
		fullDuplex := float64(0)
 | 
					 | 
				
			||||||
		if wan.FullDuplex.Val {
 | 
					 | 
				
			||||||
			fullDuplex = 1
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							fullDuplex := 0.0
 | 
				
			||||||
 | 
							if wan.FullDuplex.Val {
 | 
				
			||||||
 | 
								fullDuplex = 1.0
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		data := map[string]float64{
 | 
							data := map[string]float64{
 | 
				
			||||||
			"bytes-r":      wan.BytesR.Val,
 | 
								"bytes-r":      wan.BytesR.Val,
 | 
				
			||||||
			"full_duplex":  fullDuplex,
 | 
								"full_duplex":  fullDuplex,
 | 
				
			||||||
| 
						 | 
					@ -112,26 +120,27 @@ func (u *DatadogUnifi) reportUSGwans(r report, deviceName string, siteName strin
 | 
				
			||||||
			"tx_broadcast": wan.TxBroadcast.Val,
 | 
								"tx_broadcast": wan.TxBroadcast.Val,
 | 
				
			||||||
			"tx_multicast": wan.TxMulticast.Val,
 | 
								"tx_multicast": wan.TxMulticast.Val,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		metricName := metricNamespace("usg_wan_ports")
 | 
					
 | 
				
			||||||
		reportGaugeForMap(r, metricName, data, tags)
 | 
							metricName := metricNamespace("usg.wan_ports")
 | 
				
			||||||
 | 
							reportGaugeForFloat64Map(r, metricName, data, tags)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (u *DatadogUnifi) reportNetTable(r report, deviceName string, siteName string, source string, nt unifi.NetworkTable) {
 | 
					func (u *DatadogUnifi) batchNetTable(r report, tags map[string]string, nt unifi.NetworkTable) {
 | 
				
			||||||
	for _, p := range nt {
 | 
						for _, p := range nt {
 | 
				
			||||||
		tags := []string{
 | 
							tags := cleanTags(map[string]string{
 | 
				
			||||||
			tag("device_name", deviceName),
 | 
								"device_name": tags["name"],
 | 
				
			||||||
			tag("site_name", siteName),
 | 
								"site_name":   tags["site_name"],
 | 
				
			||||||
			tag("source", source),
 | 
								"source":      tags["source"],
 | 
				
			||||||
			tag("up", p.Up.Txt),
 | 
								"up":          p.Up.Txt,
 | 
				
			||||||
			tag("enabled", p.Enabled.Txt),
 | 
								"enabled":     p.Enabled.Txt,
 | 
				
			||||||
			tag("ip", p.IP),
 | 
								"ip":          p.IP,
 | 
				
			||||||
			tag("mac", p.Mac),
 | 
								"mac":         p.Mac,
 | 
				
			||||||
			tag("name", p.Name),
 | 
								"name":        p.Name,
 | 
				
			||||||
			tag("domain_name", p.DomainName),
 | 
								"domain_name": p.DomainName,
 | 
				
			||||||
			tag("purpose", p.Purpose),
 | 
								"purpose":     p.Purpose,
 | 
				
			||||||
			tag("is_guest", p.IsGuest.Txt),
 | 
								"is_guest":    p.IsGuest.Txt,
 | 
				
			||||||
		}
 | 
							})
 | 
				
			||||||
		data := map[string]float64{
 | 
							data := map[string]float64{
 | 
				
			||||||
			"num_sta":    p.NumSta.Val,
 | 
								"num_sta":    p.NumSta.Val,
 | 
				
			||||||
			"rx_bytes":   p.RxBytes.Val,
 | 
								"rx_bytes":   p.RxBytes.Val,
 | 
				
			||||||
| 
						 | 
					@ -139,7 +148,8 @@ func (u *DatadogUnifi) reportNetTable(r report, deviceName string, siteName stri
 | 
				
			||||||
			"tx_bytes":   p.TxBytes.Val,
 | 
								"tx_bytes":   p.TxBytes.Val,
 | 
				
			||||||
			"tx_packets": p.TxPackets.Val,
 | 
								"tx_packets": p.TxPackets.Val,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		metricName := metricNamespace("usg_networks")
 | 
					
 | 
				
			||||||
		reportGaugeForMap(r, metricName, data, tags)
 | 
							metricName := metricNamespace("usg.networks")
 | 
				
			||||||
 | 
							reportGaugeForFloat64Map(r, metricName, data, tags)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,55 +1,60 @@
 | 
				
			||||||
package datadogunifi
 | 
					package datadogunifi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"github.com/unpoller/unifi"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/unifi-poller/unifi"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// reportUSW generates Unifi Switch datapoints for Datadog.
 | 
					// uswT is used as a name for printed/logged counters.
 | 
				
			||||||
 | 
					const uswT = item("USW")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// batchUSW generates Unifi Switch datapoints for Datadog.
 | 
				
			||||||
// These points can be passed directly to datadog.
 | 
					// These points can be passed directly to datadog.
 | 
				
			||||||
func (u *DatadogUnifi) reportUSW(r report, s *unifi.USW) {
 | 
					func (u *DatadogUnifi) batchUSW(r report, s *unifi.USW) {
 | 
				
			||||||
	if !s.Adopted.Val || s.Locating.Val {
 | 
						if !s.Adopted.Val || s.Locating.Val {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tags := []string{
 | 
						tags := cleanTags(map[string]string{
 | 
				
			||||||
		tag("mac", s.Mac),
 | 
							"mac":       s.Mac,
 | 
				
			||||||
		tag("site_name", s.SiteName),
 | 
							"site_name": s.SiteName,
 | 
				
			||||||
		tag("source", s.SourceName),
 | 
							"source":    s.SourceName,
 | 
				
			||||||
		tag("name", s.Name),
 | 
							"name":      s.Name,
 | 
				
			||||||
		tag("version", s.Version),
 | 
							"version":   s.Version,
 | 
				
			||||||
		tag("model", s.Model),
 | 
							"model":     s.Model,
 | 
				
			||||||
		tag("serial", s.Serial),
 | 
							"serial":    s.Serial,
 | 
				
			||||||
		tag("type", s.Type),
 | 
							"type":      s.Type,
 | 
				
			||||||
		tag("ip", s.IP),
 | 
							"ip":        s.IP,
 | 
				
			||||||
	}
 | 
						})
 | 
				
			||||||
	metricName := metricNamespace("usw")
 | 
						data := CombineFloat64(
 | 
				
			||||||
	u.reportUSWstat(r, metricName, tags, s.Stat.Sw)
 | 
							u.batchUSWstat(s.Stat.Sw),
 | 
				
			||||||
	u.reportSysStats(r, metricName, s.SysStats, s.SystemStats, tags)
 | 
							u.batchSysStats(s.SysStats, s.SystemStats),
 | 
				
			||||||
 | 
							map[string]float64{
 | 
				
			||||||
 | 
								"guest-num_sta": s.GuestNumSta.Val,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data := map[string]float64{
 | 
								"bytes":               s.Bytes.Val,
 | 
				
			||||||
		"guest-num_sta":       s.GuestNumSta.Val,
 | 
								"fan_level":           s.FanLevel.Val,
 | 
				
			||||||
		"bytes":               s.Bytes.Val,
 | 
								"general_temperature": s.GeneralTemperature.Val,
 | 
				
			||||||
		"fan_level":           s.FanLevel.Val,
 | 
								"last_seen":           s.LastSeen.Val,
 | 
				
			||||||
		"general_temperature": s.GeneralTemperature.Val,
 | 
								"rx_bytes":            s.RxBytes.Val,
 | 
				
			||||||
		"last_seen":           s.LastSeen.Val,
 | 
								"tx_bytes":            s.TxBytes.Val,
 | 
				
			||||||
		"rx_bytes":            s.RxBytes.Val,
 | 
								"uptime":              s.Uptime.Val,
 | 
				
			||||||
		"tx_bytes":            s.TxBytes.Val,
 | 
								"state":               s.State.Val,
 | 
				
			||||||
		"uptime":              s.Uptime.Val,
 | 
								"user-num_sta":        s.UserNumSta.Val,
 | 
				
			||||||
		"state":               s.State.Val,
 | 
							})
 | 
				
			||||||
		"user-num_sta":        s.UserNumSta.Val,
 | 
					
 | 
				
			||||||
	}
 | 
						r.addCount(uswT)
 | 
				
			||||||
	reportGaugeForMap(r, metricName, data, tags)
 | 
						metricName := metricNamespace("usw")
 | 
				
			||||||
	u.reportPortTable(r, s.Name, s.SiteName, s.SourceName, s.Type, s.PortTable)
 | 
						reportGaugeForFloat64Map(r, metricName, data, tags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u.batchPortTable(r, tags, s.PortTable)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (u *DatadogUnifi) reportUSWstat(r report, metricName func(string) string, tags []string, sw *unifi.Sw) {
 | 
					func (u *DatadogUnifi) batchUSWstat(sw *unifi.Sw) map[string]float64 {
 | 
				
			||||||
	if sw == nil {
 | 
						if sw == nil {
 | 
				
			||||||
		return
 | 
							return map[string]float64{}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data := map[string]float64{
 | 
						return map[string]float64{
 | 
				
			||||||
		"stat_bytes":      sw.Bytes.Val,
 | 
							"stat_bytes":      sw.Bytes.Val,
 | 
				
			||||||
		"stat_rx_bytes":   sw.RxBytes.Val,
 | 
							"stat_rx_bytes":   sw.RxBytes.Val,
 | 
				
			||||||
		"stat_rx_crypts":  sw.RxCrypts.Val,
 | 
							"stat_rx_crypts":  sw.RxCrypts.Val,
 | 
				
			||||||
| 
						 | 
					@ -63,30 +68,35 @@ func (u *DatadogUnifi) reportUSWstat(r report, metricName func(string) string, t
 | 
				
			||||||
		"stat_tx_packets": sw.TxPackets.Val,
 | 
							"stat_tx_packets": sw.TxPackets.Val,
 | 
				
			||||||
		"stat_tx_retries": sw.TxRetries.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) {
 | 
					//nolint:funlen
 | 
				
			||||||
 | 
					func (u *DatadogUnifi) batchPortTable(r report, t map[string]string, pt []unifi.Port) {
 | 
				
			||||||
	for _, p := range pt {
 | 
						for _, p := range pt {
 | 
				
			||||||
		if !p.Up.Val || !p.Enable.Val {
 | 
							if !u.DeadPorts && (!p.Up.Val || !p.Enable.Val) {
 | 
				
			||||||
			continue // only record UP ports.
 | 
								continue // only record UP ports.
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		tags := []string{
 | 
							tags := cleanTags(map[string]string{
 | 
				
			||||||
			tag("site_name", siteName),
 | 
								"site_name":      t["site_name"],
 | 
				
			||||||
			tag("device_name", deviceName),
 | 
								"device_name":    t["name"],
 | 
				
			||||||
			tag("source", source),
 | 
								"source":         t["source"],
 | 
				
			||||||
			tag("type", typeTag),
 | 
								"type":           t["type"],
 | 
				
			||||||
			tag("name", p.Name),
 | 
								"name":           p.Name,
 | 
				
			||||||
			tag("poe_mode", p.PoeMode),
 | 
								"poe_mode":       p.PoeMode,
 | 
				
			||||||
			tag("port_poe", p.PortPoe.Txt),
 | 
								"port_poe":       p.PortPoe.Txt,
 | 
				
			||||||
			tag("port_idx", p.PortIdx.Txt),
 | 
								"port_idx":       p.PortIdx.Txt,
 | 
				
			||||||
			tag("port_id", fmt.Sprintf("%s_port_%s", deviceName, p.PortIdx.Txt)),
 | 
								"port_id":        t["name"] + " Port " + p.PortIdx.Txt,
 | 
				
			||||||
			tag("poe_enable", p.PoeEnable.Txt),
 | 
								"poe_enable":     p.PoeEnable.Txt,
 | 
				
			||||||
			tag("flowctrl_rx", p.FlowctrlRx.Txt),
 | 
								"flowctrl_rx":    p.FlowctrlRx.Txt,
 | 
				
			||||||
			tag("flowctrl_tx", p.FlowctrlTx.Txt),
 | 
								"flowctrl_tx":    p.FlowctrlTx.Txt,
 | 
				
			||||||
			tag("media", p.Media),
 | 
								"media":          p.Media,
 | 
				
			||||||
		}
 | 
								"has_sfp":        p.SFPFound.Txt,
 | 
				
			||||||
 | 
								"sfp_compliance": p.SFPCompliance,
 | 
				
			||||||
 | 
								"sfp_serial":     p.SFPSerial,
 | 
				
			||||||
 | 
								"sfp_vendor":     p.SFPVendor,
 | 
				
			||||||
 | 
								"sfp_part":       p.SFPPart,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
		data := map[string]float64{
 | 
							data := map[string]float64{
 | 
				
			||||||
			"dbytes_r":     p.BytesR.Val,
 | 
								"dbytes_r":     p.BytesR.Val,
 | 
				
			||||||
			"rx_broadcast": p.RxBroadcast.Val,
 | 
								"rx_broadcast": p.RxBroadcast.Val,
 | 
				
			||||||
| 
						 | 
					@ -113,7 +123,15 @@ func (u *DatadogUnifi) reportPortTable(r report, deviceName string, siteName str
 | 
				
			||||||
			data["poe_voltage"] = p.PoeVoltage.Val
 | 
								data["poe_voltage"] = p.PoeVoltage.Val
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		metricName := metricNamespace("usw_ports")
 | 
							if p.SFPFound.Val {
 | 
				
			||||||
		reportGaugeForMap(r, metricName, data, tags)
 | 
								data["sfp_current"] = p.SFPCurrent.Val
 | 
				
			||||||
 | 
								data["sfp_voltage"] = p.SFPVoltage.Val
 | 
				
			||||||
 | 
								data["sfp_temperature"] = p.SFPTemperature.Val
 | 
				
			||||||
 | 
								data["sfp_txpower"] = p.SFPTxpower.Val
 | 
				
			||||||
 | 
								data["sfp_rxpower"] = p.SFPRxpower.Val
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							metricName := metricNamespace("usw.ports")
 | 
				
			||||||
 | 
							reportGaugeForFloat64Map(r, metricName, data, tags)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1 +1,83 @@
 | 
				
			||||||
package datadogunifi
 | 
					package datadogunifi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/unpoller/unifi"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// uxgT is used as a name for printed/logged counters.
 | 
				
			||||||
 | 
					const uxgT = item("UXG")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// batchUXG generates 10Gb Unifi Gateway datapoints for Datadog.
 | 
				
			||||||
 | 
					// These points can be passed directly to datadog.
 | 
				
			||||||
 | 
					func (u *DatadogUnifi) batchUXG(r report, s *unifi.UXG) { // nolint: funlen
 | 
				
			||||||
 | 
						if !s.Adopted.Val || s.Locating.Val {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tags := cleanTags(map[string]string{
 | 
				
			||||||
 | 
							"source":        s.SourceName,
 | 
				
			||||||
 | 
							"mac":           s.Mac,
 | 
				
			||||||
 | 
							"site_name":     s.SiteName,
 | 
				
			||||||
 | 
							"name":          s.Name,
 | 
				
			||||||
 | 
							"version":       s.Version,
 | 
				
			||||||
 | 
							"model":         s.Model,
 | 
				
			||||||
 | 
							"serial":        s.Serial,
 | 
				
			||||||
 | 
							"type":          s.Type,
 | 
				
			||||||
 | 
							"ip":            s.IP,
 | 
				
			||||||
 | 
							"license_state": s.LicenseState,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						data := CombineFloat64(
 | 
				
			||||||
 | 
							u.batchUDMstorage(s.Storage),
 | 
				
			||||||
 | 
							u.batchUDMtemps(s.Temperatures),
 | 
				
			||||||
 | 
							u.batchUSGstats(s.SpeedtestStatus, s.Stat.Gw, s.Uplink),
 | 
				
			||||||
 | 
							u.batchSysStats(s.SysStats, s.SystemStats),
 | 
				
			||||||
 | 
							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,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						r.addCount(uxgT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						metricName := metricNamespace("usg")
 | 
				
			||||||
 | 
						reportGaugeForFloat64Map(r, metricName, data, tags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u.batchNetTable(r, tags, s.NetworkTable)
 | 
				
			||||||
 | 
						u.batchUSGwans(r, tags, s.Wan1, s.Wan2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tags = cleanTags(map[string]string{
 | 
				
			||||||
 | 
							"mac":       s.Mac,
 | 
				
			||||||
 | 
							"site_name": s.SiteName,
 | 
				
			||||||
 | 
							"source":    s.SourceName,
 | 
				
			||||||
 | 
							"name":      s.Name,
 | 
				
			||||||
 | 
							"version":   s.Version,
 | 
				
			||||||
 | 
							"model":     s.Model,
 | 
				
			||||||
 | 
							"serial":    s.Serial,
 | 
				
			||||||
 | 
							"type":      s.Type,
 | 
				
			||||||
 | 
							"ip":        s.IP,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						data = CombineFloat64(
 | 
				
			||||||
 | 
							u.batchUSWstat(s.Stat.Sw),
 | 
				
			||||||
 | 
							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,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						metricName = metricNamespace("usw")
 | 
				
			||||||
 | 
						reportGaugeForFloat64Map(r, metricName, data, tags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u.batchPortTable(r, tags, s.PortTable) // udm has a usw in it.
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue