Merge branch 'master' into superq/metric_names
This commit is contained in:
		
						commit
						341e21efd2
					
				|  | @ -56,10 +56,6 @@ unifi_user = "influx" | |||
| unifi_pass = "4BB9345C-2341-48D7-99F5-E01B583FF77F" | ||||
| unifi_url = "https://127.0.0.1:8443" | ||||
| 
 | ||||
| # Enable collection of Intrusion Detection System Data (InfluxDB only). | ||||
| # Only useful if IDS or IPS are enabled on one of the sites. | ||||
| collect_ids = false | ||||
| 
 | ||||
| # Some controllers or reverse proxy configurations do not allow cookies to be | ||||
| # re-user on every request (every interval). This setting provides a workaround | ||||
| # That causes the poller to re-auth (login) to the controller on every interval. | ||||
|  | @ -67,6 +63,14 @@ collect_ids = false | |||
| # a few more requests to your controller every interval. | ||||
| reauthenticate = false | ||||
| 
 | ||||
| # Enable collection of Intrusion Detection System Data (InfluxDB only). | ||||
| # Only useful if IDS or IPS are enabled on one of the sites. | ||||
| save_ids = false | ||||
| 
 | ||||
| # Enable collection of site data. This data powers the Network Sites dashboard. | ||||
| # It's not valuable to everyone and setting this to false will save resources. | ||||
| save_sites = true | ||||
| 
 | ||||
| # If your UniFi controller has a valid SSL certificate (like lets encrypt), | ||||
| # you can enable this option to validate it. Otherwise, any SSL certificate is | ||||
| # valid. If you don't know if you have a valid SSL cert, then you don't have one. | ||||
|  |  | |||
|  | @ -13,7 +13,8 @@ | |||
|  "unifi_user": "influx", | ||||
|  "unifi_pass": "", | ||||
|  "unifi_url": "https://127.0.0.1:8443", | ||||
|  "collect_ids": false, | ||||
|  "save_ids": false, | ||||
|  "save_sites": true, | ||||
|  "reauthenticate": false, | ||||
|  "verify_ssl": false | ||||
| } | ||||
|  |  | |||
|  | @ -2,99 +2,33 @@ | |||
| <!-- | ||||
| ####################################################### | ||||
| # UniFi Poller primary configuration file. XML FORMAT # | ||||
| # provided values are defaults                        # | ||||
| # provided values are defaults. See up.conf.example!  # | ||||
| ####################################################### | ||||
| --> | ||||
| <unifi-poller> | ||||
|   <!-- | ||||
|   # If the controller has more than one site, specify which sites to poll here. | ||||
|   # Set this to "default" to poll only the first site on the controller. | ||||
|   # A setting of "all" will poll all sites; this works if you only have 1 site too. | ||||
|   Add more sites by adding additional lines: | ||||
|   <sites>site1</sites> | ||||
|   <sites>site2</sites> | ||||
|   --> | ||||
| 
 | ||||
|   <sites>all</sites> | ||||
|   <interval>60s</interval> | ||||
| 
 | ||||
|   <!-- | ||||
|   # The UniFi Controller only updates traffic stats about every 30 seconds. | ||||
|   # Setting this to something lower may lead to "zeros" in your data. | ||||
|   # If you're getting zeros now, set this to "1m" | ||||
|   --> | ||||
|   <interval>30s</interval> | ||||
| 
 | ||||
|   <!-- | ||||
|   # Turns on line numbers, microsecond logging, and a per-device log. | ||||
|   # The default is false, but I personally leave this on at home (four devices). | ||||
|   # This may be noisy if you have a lot of devices. It adds one line per device. | ||||
|   --> | ||||
|   <debug>false</debug> | ||||
| 
 | ||||
|   <!-- | ||||
|   # Turns off per-interval logs. Only startup and error logs will be emitted. | ||||
|   # Recommend enabling debug with this setting for better error logging. | ||||
|   --> | ||||
|   <quiet>false</quiet> | ||||
| 
 | ||||
|   <!-- | ||||
|   # Which mode to run this application in. The default mode is "influx". Providing | ||||
|   # an invalid mode will also result in "influx". In this default mode the application | ||||
|   # runs as a daemon and polls the controller at the configured interval. | ||||
|   # | ||||
|   # There are two other options at this time: "influxlambda" and "prometheus" | ||||
|   # | ||||
|   # Mode "influxlambda" makes the application exit after collecting and reporting metrics | ||||
|   # to InfluxDB one time. This mode requires an external process like an AWS Lambda | ||||
|   # or a simple crontab to keep the timings accurate on UniFi Poller run intervals. | ||||
|   # | ||||
|   # Mode "prometheus" opens an HTTP server on port 9130 and exports the metrics at | ||||
|   # /metrics for polling collection by a prometheus server. This disables influxdb. | ||||
|   # IMPORTANT: The prometheus mode is still beta. | ||||
|   # Please help us test and provide your feedback on the github repo! | ||||
|   --> | ||||
|   <mode>influx</mode> | ||||
| 
 | ||||
|   <!-- | ||||
|   # This controls on which ip and port /metrics is exported when mode is "prometheus". | ||||
|   # This has no effect in other modes. Must contain a colon and port. | ||||
|   --> | ||||
|   <http_listen>0.0.0.0:9130</http_listen> | ||||
| 
 | ||||
|   <!-- | ||||
|   # InfluxDB does not require auth by default, so the user/password are probably unimportant. | ||||
|   --> | ||||
|   <influx_db>unifi</influx_db> | ||||
|   <influx_pass>unifi</influx_pass> | ||||
|   <influx_url>http://127.0.0.1:8086</influx_url> | ||||
|   <influx_user>unifi</influx_user> | ||||
|   <!-- | ||||
|   # If your InfluxDB uses an invalid SSL cert, set this to true. | ||||
|   --> | ||||
|   <influx_insecure_ssl>false</influx_insecure_ssl> | ||||
| 
 | ||||
|   <!-- | ||||
|   # Make a read-only user in the UniFi Admin Settings. | ||||
|   --> | ||||
|   <unifi_user>influx</unifi_user> | ||||
|   <unifi_pass></unifi_pass> | ||||
|   <unifi_url>https://127.0.0.1:8443</unifi_url> | ||||
|   <!-- | ||||
|   # Enable collection of Intrusion Detection System Data (InfluxDB only). | ||||
|   # Only useful if IDS or IPS are enabled on one of the sites. | ||||
|   --> | ||||
|   <collect_ids>false</collect_ids> | ||||
|   <!-- | ||||
|   # Some controllers or reverse proxy configurations do not allow cookies to be | ||||
|   # re-user on every request (every interval). This setting provides a workaround | ||||
|   # That causes the poller to re-auth (login) to the controller on every interval. | ||||
|   # Only enable this if you get login errors after 30 seconds. This will generate | ||||
|   # a few more requests to your controller every interval. | ||||
|   --> | ||||
|   <reauthenticate>false</reauthenticate> | ||||
|   <!-- | ||||
|   # If your UniFi controller has a valid SSL certificate, you can enable | ||||
|   # this option to validate it. Otherwise, any SSL certificate is valid. | ||||
|   --> | ||||
|   <verify_ssl>false</verify_ssl> | ||||
| 
 | ||||
|   <save_ids>false</save_ids> | ||||
|   <save_sites>true</save_sites> | ||||
| 
 | ||||
| </unifi-poller> | ||||
|  |  | |||
|  | @ -1,73 +1,28 @@ | |||
| ######################################################## | ||||
| # UniFi Poller primary configuration file. YAML FORMAT # | ||||
| # provided values are defaults                         # | ||||
| # provided values are defaults. See up.conf.example!   # | ||||
| ######################################################## | ||||
| --- | ||||
| # If the controller has more than one site, specify which sites to poll here. | ||||
| # Set this to "default" to poll only the first site on the controller. | ||||
| # A setting of "all" will poll all sites; this works if you only have 1 site too. | ||||
| sites: | ||||
|   - all | ||||
| 
 | ||||
| # The UniFi Controller only updates traffic stats about every 30 seconds. | ||||
| # Setting this to something lower may lead to "zeros" in your data. | ||||
| # If you're getting zeros now, set this to "1m" | ||||
| # Only has effect if "mode" (below) is "influx" - other modes do not use interval. | ||||
| interval: "30s" | ||||
| 
 | ||||
| # Turns on line numbers, microsecond logging, and a per-device log. | ||||
| # The default is false, but I personally leave this on at home (four devices). | ||||
| # This may be noisy if you have a lot of devices. It adds one line per device. | ||||
| debug: false | ||||
| 
 | ||||
| # Turns off per-interval logs. Only startup and error logs will be emitted. | ||||
| # Recommend enabling debug with this setting for better error logging. | ||||
| quiet: false | ||||
| 
 | ||||
| # Which mode to run this application in. The default mode is "influx". Providing | ||||
| # an invalid mode will also result in "influx". In this default mode the application | ||||
| # runs as a daemon and polls the controller at the configured interval. | ||||
| # | ||||
| # There are two other options at this time: "influxlambda" and "prometheus" | ||||
| # | ||||
| # Mode "influxlambda" makes the application exit after collecting and reporting metrics | ||||
| # to InfluxDB one time. This mode requires an external process like an AWS Lambda | ||||
| # or a simple crontab to keep the timings accurate on UniFi Poller run intervals. | ||||
| # | ||||
| # Mode "prometheus" opens an HTTP server on port 9130 and exports the metrics at | ||||
| # /metrics for polling collection by a prometheus server. This disables influxdb. | ||||
| # IMPORTANT: The prometheus mode is still beta. | ||||
| # Please help us test and provide your feedback on the github repo! | ||||
| mode: "influx" | ||||
| 
 | ||||
| # This controls on which ip and port /metrics is exported when mode is "prometheus". | ||||
| # This has no effect in other modes. Must contain a colon and port. | ||||
| http_listen: "0.0.0.0:9130" | ||||
| 
 | ||||
| # InfluxDB does not require auth by default, so the user/password are probably unimportant. | ||||
| influx_url: "http://127.0.0.1:8086" | ||||
| influx_user: "unifi" | ||||
| influx_pass: "unifi" | ||||
| # Be sure to create this database. | ||||
| influx_db: "unifi" | ||||
| # If your InfluxDB uses an invalid SSL cert, set this to true. | ||||
| influx_insecure_ssl: false | ||||
| 
 | ||||
| # Make a read-only user in the UniFi Admin Settings. | ||||
| unifi_user: "influx" | ||||
| unifi_pass: "" | ||||
| unifi_url: "https://127.0.0.1:8443" | ||||
| 
 | ||||
| # Enable collection of Intrusion Detection System Data (InfluxDB only). | ||||
| # Only useful if IDS or IPS are enabled on one of the sites. | ||||
| collect_ids: false | ||||
| 
 | ||||
| # Some controllers or reverse proxy configurations do not allow cookies to be | ||||
| # re-user on every request (every interval). This setting provides a workaround | ||||
| # That causes the poller to re-auth (login) to the controller on every interval. | ||||
| # Only enable this if you get login errors after 30 seconds. This will generate | ||||
| # a few more requests to your controller every interval. | ||||
| reauthenticate: false | ||||
| 
 | ||||
| # If your UniFi controller has a valid SSL certificate, you can enable | ||||
| # this option to validate it. Otherwise, any SSL certificate is valid. | ||||
| verify_ssl: false | ||||
| save_ids: false | ||||
| save_sites: true | ||||
|  |  | |||
|  | @ -1,111 +1,79 @@ | |||
| package influxunifi | ||||
| 
 | ||||
| import ( | ||||
| 	"time" | ||||
| 
 | ||||
| 	influx "github.com/influxdata/influxdb1-client/v2" | ||||
| 	"golift.io/unifi" | ||||
| ) | ||||
| 
 | ||||
| // ClientPoints generates Unifi Client datapoints for InfluxDB.
 | ||||
| // batchClient generates Unifi Client datapoints for InfluxDB.
 | ||||
| // These points can be passed directly to influx.
 | ||||
| func ClientPoints(c *unifi.Client, now time.Time) ([]*influx.Point, error) { | ||||
| func (u *InfluxUnifi) batchClient(r report, s *unifi.Client) { | ||||
| 	tags := map[string]string{ | ||||
| 		"id":                 c.ID, | ||||
| 		"mac":                c.Mac, | ||||
| 		"user_id":            c.UserID, | ||||
| 		"site_id":            c.SiteID, | ||||
| 		"site_name":          c.SiteName, | ||||
| 		"network_id":         c.NetworkID, | ||||
| 		"usergroup_id":       c.UserGroupID, | ||||
| 		"ap_mac":             c.ApMac, | ||||
| 		"gw_mac":             c.GwMac, | ||||
| 		"sw_mac":             c.SwMac, | ||||
| 		"ap_name":            c.ApName, | ||||
| 		"gw_name":            c.GwName, | ||||
| 		"sw_name":            c.SwName, | ||||
| 		"oui":                c.Oui, | ||||
| 		"radio_name":         c.RadioName, | ||||
| 		"radio":              c.Radio, | ||||
| 		"radio_proto":        c.RadioProto, | ||||
| 		"name":               c.Name, | ||||
| 		"fixed_ip":           c.FixedIP, | ||||
| 		"sw_port":            c.SwPort.Txt, | ||||
| 		"os_class":           c.OsClass.Txt, | ||||
| 		"os_name":            c.OsName.Txt, | ||||
| 		"dev_cat":            c.DevCat.Txt, | ||||
| 		"dev_id":             c.DevID.Txt, | ||||
| 		"dev_vendor":         c.DevVendor.Txt, | ||||
| 		"dev_family":         c.DevFamily.Txt, | ||||
| 		"is_11r":             c.Is11R.Txt, | ||||
| 		"is_wired":           c.IsWired.Txt, | ||||
| 		"is_guest":           c.IsGuest.Txt, | ||||
| 		"is_guest_by_uap":    c.IsGuestByUAP.Txt, | ||||
| 		"is_guest_by_ugw":    c.IsGuestByUGW.Txt, | ||||
| 		"is_guest_by_usw":    c.IsGuestByUSW.Txt, | ||||
| 		"noted":              c.Noted.Txt, | ||||
| 		"powersave_enabled":  c.PowersaveEnabled.Txt, | ||||
| 		"qos_policy_applied": c.QosPolicyApplied.Txt, | ||||
| 		"use_fixedip":        c.UseFixedIP.Txt, | ||||
| 		"channel":            c.Channel.Txt, | ||||
| 		"vlan":               c.Vlan.Txt, | ||||
| 		"mac":         s.Mac, | ||||
| 		"site_name":   s.SiteName, | ||||
| 		"ap_name":     s.ApName, | ||||
| 		"gw_name":     s.GwName, | ||||
| 		"sw_name":     s.SwName, | ||||
| 		"oui":         s.Oui, | ||||
| 		"radio_name":  s.RadioName, | ||||
| 		"radio":       s.Radio, | ||||
| 		"radio_proto": s.RadioProto, | ||||
| 		"name":        s.Name, | ||||
| 		"fixed_ip":    s.FixedIP, | ||||
| 		"sw_port":     s.SwPort.Txt, | ||||
| 		"os_class":    s.OsClass.Txt, | ||||
| 		"os_name":     s.OsName.Txt, | ||||
| 		"dev_cat":     s.DevCat.Txt, | ||||
| 		"dev_id":      s.DevID.Txt, | ||||
| 		"dev_vendor":  s.DevVendor.Txt, | ||||
| 		"dev_family":  s.DevFamily.Txt, | ||||
| 		"is_wired":    s.IsWired.Txt, | ||||
| 		"is_guest":    s.IsGuest.Txt, | ||||
| 		"use_fixedip": s.UseFixedIP.Txt, | ||||
| 		"channel":     s.Channel.Txt, | ||||
| 		"vlan":        s.Vlan.Txt, | ||||
| 	} | ||||
| 	fields := map[string]interface{}{ | ||||
| 		"anomalies":              c.Anomalies, | ||||
| 		"ip":                     c.IP, | ||||
| 		"essid":                  c.Essid, | ||||
| 		"bssid":                  c.Bssid, | ||||
| 		"radio_desc":             c.RadioDescription, | ||||
| 		"satisfaction":           c.Satisfaction.Val, | ||||
| 		"hostname":               c.Hostname, | ||||
| 		"dpi_stats_last_updated": c.DpiStatsLastUpdated, | ||||
| 		"last_seen_by_uap":       c.LastSeenByUAP, | ||||
| 		"last_seen_by_ugw":       c.LastSeenByUGW, | ||||
| 		"last_seen_by_usw":       c.LastSeenByUSW, | ||||
| 		"uptime_by_uap":          c.UptimeByUAP, | ||||
| 		"uptime_by_ugw":          c.UptimeByUGW, | ||||
| 		"uptime_by_usw":          c.UptimeByUSW, | ||||
| 		"assoc_time":             c.AssocTime, | ||||
| 		"bytes_r":                c.BytesR, | ||||
| 		"ccq":                    c.Ccq, | ||||
| 		"first_seen":             c.FirstSeen, | ||||
| 		"idle_time":              c.IdleTime, | ||||
| 		"last_seen":              c.LastSeen, | ||||
| 		"latest_assoc_time":      c.LatestAssocTime, | ||||
| 		"network":                c.Network, | ||||
| 		"noise":                  c.Noise, | ||||
| 		"note":                   c.Note, | ||||
| 		"roam_count":             c.RoamCount, | ||||
| 		"rssi":                   c.Rssi, | ||||
| 		"rx_bytes":               c.RxBytes, | ||||
| 		"rx_bytes_r":             c.RxBytesR, | ||||
| 		"rx_packets":             c.RxPackets, | ||||
| 		"rx_rate":                c.RxRate, | ||||
| 		"signal":                 c.Signal, | ||||
| 		"tx_bytes":               c.TxBytes, | ||||
| 		"tx_bytes_r":             c.TxBytesR, | ||||
| 		"tx_packets":             c.TxPackets, | ||||
| 		"tx_retries":             c.TxRetries, | ||||
| 		"tx_power":               c.TxPower, | ||||
| 		"tx_rate":                c.TxRate, | ||||
| 		"uptime":                 c.Uptime, | ||||
| 		"wifi_tx_attempts":       c.WifiTxAttempts, | ||||
| 		"wired-rx_bytes":         c.WiredRxBytes, | ||||
| 		"wired-rx_bytes-r":       c.WiredRxBytesR, | ||||
| 		"wired-rx_packets":       c.WiredRxPackets, | ||||
| 		"wired-tx_bytes":         c.WiredTxBytes, | ||||
| 		"wired-tx_bytes-r":       c.WiredTxBytesR, | ||||
| 		"wired-tx_packets":       c.WiredTxPackets, | ||||
| 		"dpi_app":                c.DpiStats.App.Val, | ||||
| 		"dpi_cat":                c.DpiStats.Cat.Val, | ||||
| 		"dpi_rx_bytes":           c.DpiStats.RxBytes.Val, | ||||
| 		"dpi_rx_packets":         c.DpiStats.RxPackets.Val, | ||||
| 		"dpi_tx_bytes":           c.DpiStats.TxBytes.Val, | ||||
| 		"dpi_tx_packets":         c.DpiStats.TxPackets.Val, | ||||
| 		"anomalies":        s.Anomalies, | ||||
| 		"ip":               s.IP, | ||||
| 		"essid":            s.Essid, | ||||
| 		"bssid":            s.Bssid, | ||||
| 		"channel":          s.Channel.Val, | ||||
| 		"hostname":         s.Name, | ||||
| 		"radio_desc":       s.RadioDescription, | ||||
| 		"satisfaction":     s.Satisfaction.Val, | ||||
| 		"bytes_r":          s.BytesR, | ||||
| 		"ccq":              s.Ccq, | ||||
| 		"noise":            s.Noise, | ||||
| 		"note":             s.Note, | ||||
| 		"roam_count":       s.RoamCount, | ||||
| 		"rssi":             s.Rssi, | ||||
| 		"rx_bytes":         s.RxBytes, | ||||
| 		"rx_bytes_r":       s.RxBytesR, | ||||
| 		"rx_packets":       s.RxPackets, | ||||
| 		"rx_rate":          s.RxRate, | ||||
| 		"signal":           s.Signal, | ||||
| 		"tx_bytes":         s.TxBytes, | ||||
| 		"tx_bytes_r":       s.TxBytesR, | ||||
| 		"tx_packets":       s.TxPackets, | ||||
| 		"tx_retries":       s.TxRetries, | ||||
| 		"tx_power":         s.TxPower, | ||||
| 		"tx_rate":          s.TxRate, | ||||
| 		"uptime":           s.Uptime, | ||||
| 		"wifi_tx_attempts": s.WifiTxAttempts, | ||||
| 		"wired-rx_bytes":   s.WiredRxBytes, | ||||
| 		"wired-rx_bytes-r": s.WiredRxBytesR, | ||||
| 		"wired-rx_packets": s.WiredRxPackets, | ||||
| 		"wired-tx_bytes":   s.WiredTxBytes, | ||||
| 		"wired-tx_bytes-r": s.WiredTxBytesR, | ||||
| 		"wired-tx_packets": s.WiredTxPackets, | ||||
| 		/* | ||||
| 			"dpi_app":          c.DpiStats.App.Val, | ||||
| 			"dpi_cat":          c.DpiStats.Cat.Val, | ||||
| 			"dpi_rx_bytes":     c.DpiStats.RxBytes.Val, | ||||
| 			"dpi_rx_packets":   c.DpiStats.RxPackets.Val, | ||||
| 			"dpi_tx_bytes":     c.DpiStats.TxBytes.Val, | ||||
| 			"dpi_tx_packets":   c.DpiStats.TxPackets.Val, | ||||
| 		*/ | ||||
| 	} | ||||
| 	pt, err := influx.NewPoint("clients", tags, fields, now) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return []*influx.Point{pt}, nil | ||||
| 	r.send(&metric{Table: "clients", Tags: tags, Fields: fields}) | ||||
| } | ||||
|  |  | |||
|  | @ -1,13 +1,12 @@ | |||
| package influxunifi | ||||
| 
 | ||||
| import ( | ||||
| 	influx "github.com/influxdata/influxdb1-client/v2" | ||||
| 	"golift.io/unifi" | ||||
| ) | ||||
| 
 | ||||
| // IDSPoints generates intrusion detection datapoints for InfluxDB.
 | ||||
| // batchIDS generates intrusion detection datapoints for InfluxDB.
 | ||||
| // These points can be passed directly to influx.
 | ||||
| func IDSPoints(i *unifi.IDS) ([]*influx.Point, error) { | ||||
| func (u *InfluxUnifi) batchIDS(r report, i *unifi.IDS) { | ||||
| 	tags := map[string]string{ | ||||
| 		"in_iface":       i.InIface, | ||||
| 		"event_type":     i.EventType, | ||||
|  | @ -36,9 +35,5 @@ func IDSPoints(i *unifi.IDS) ([]*influx.Point, error) { | |||
| 		"srcipASN":     i.SrcipASN, | ||||
| 		"usgipASN":     i.UsgipASN, | ||||
| 	} | ||||
| 	pt, err := influx.NewPoint("intrusion_detect", tags, fields, i.Datetime) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return []*influx.Point{pt}, nil | ||||
| 	r.send(&metric{Table: "intrusion_detect", Tags: tags, Fields: fields}) | ||||
| } | ||||
|  |  | |||
|  | @ -3,67 +3,140 @@ | |||
| package influxunifi | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/tls" | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/davidnewhall/unifi-poller/pkg/metrics" | ||||
| 	client "github.com/influxdata/influxdb1-client/v2" | ||||
| 	influx "github.com/influxdata/influxdb1-client/v2" | ||||
| ) | ||||
| 
 | ||||
| // Metrics contains all the data from the controller and an influx endpoint to send it to.
 | ||||
| type Metrics struct { | ||||
| 	*metrics.Metrics | ||||
| 	client.BatchPoints | ||||
| // Config defines the data needed to store metrics in InfluxDB
 | ||||
| type Config struct { | ||||
| 	Database string | ||||
| 	URL      string | ||||
| 	User     string | ||||
| 	Pass     string | ||||
| 	BadSSL   bool | ||||
| } | ||||
| 
 | ||||
| // ProcessPoints batches all device and client data into influxdb data points.
 | ||||
| // InfluxUnifi is returned by New() after you provide a Config.
 | ||||
| type InfluxUnifi struct { | ||||
| 	cf     *Config | ||||
| 	influx influx.Client | ||||
| } | ||||
| 
 | ||||
| type metric struct { | ||||
| 	Table  string | ||||
| 	Tags   map[string]string | ||||
| 	Fields map[string]interface{} | ||||
| } | ||||
| 
 | ||||
| // New returns an InfluxDB interface.
 | ||||
| func New(c *Config) (*InfluxUnifi, error) { | ||||
| 	i, err := influx.NewHTTPClient(influx.HTTPConfig{ | ||||
| 		Addr:      c.URL, | ||||
| 		Username:  c.User, | ||||
| 		Password:  c.Pass, | ||||
| 		TLSConfig: &tls.Config{InsecureSkipVerify: c.BadSSL}, | ||||
| 	}) | ||||
| 	return &InfluxUnifi{cf: c, influx: i}, err | ||||
| } | ||||
| 
 | ||||
| // ReportMetrics batches all device and client data into influxdb data points.
 | ||||
| // Call this after you've collected all the data you care about.
 | ||||
| // This function is sorta weird and returns a slice of errors. The reasoning is
 | ||||
| // that some points may process while others fail, so we attempt to process them
 | ||||
| // all. This is (usually) run in a loop, so we can't really exit on error,
 | ||||
| // we just log the errors and tally them on a counter. In reality, this never
 | ||||
| // returns any errors because we control the data going in; cool right? But we
 | ||||
| // still check&log it in case the data going is skewed up and causes errors!
 | ||||
| func (m *Metrics) ProcessPoints() []error { | ||||
| 	errs := []error{} | ||||
| 	processPoints := func(m *Metrics, p []*client.Point, err error) { | ||||
| 		switch { | ||||
| 		case err != nil: | ||||
| 			errs = append(errs, err) | ||||
| 		case p == nil: | ||||
| 		default: | ||||
| 			m.AddPoints(p) | ||||
| 		} | ||||
| // Returns an error if influxdb calls fail, otherwise returns a report.
 | ||||
| func (u *InfluxUnifi) ReportMetrics(m *metrics.Metrics) (*Report, error) { | ||||
| 	r := &Report{Metrics: m, ch: make(chan *metric), Start: time.Now()} | ||||
| 	defer close(r.ch) | ||||
| 	// Make a new Influx Points Batcher.
 | ||||
| 	var err error | ||||
| 	r.bp, err = influx.NewBatchPoints(influx.BatchPointsConfig{Database: u.cf.Database}) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("influx.NewBatchPoints: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	for _, asset := range m.Sites { | ||||
| 		pts, err := SitePoints(asset, m.TS) | ||||
| 		processPoints(m, pts, err) | ||||
| 	} | ||||
| 	for _, asset := range m.Clients { | ||||
| 		pts, err := ClientPoints(asset, m.TS) | ||||
| 		processPoints(m, pts, err) | ||||
| 	} | ||||
| 	for _, asset := range m.IDSList { | ||||
| 		pts, err := IDSPoints(asset) // no m.TS.
 | ||||
| 		processPoints(m, pts, err) | ||||
| 	} | ||||
| 	go u.collect(r, r.ch) | ||||
| 	// Batch all the points.
 | ||||
| 	u.loopPoints(r) | ||||
| 	r.wg.Wait() // wait for all points to finish batching!
 | ||||
| 
 | ||||
| 	if m.Devices == nil { | ||||
| 		return errs | ||||
| 	// Send all the points.
 | ||||
| 	if err = u.influx.Write(r.bp); err != nil { | ||||
| 		return nil, fmt.Errorf("influxdb.Write(points): %v", err) | ||||
| 	} | ||||
| 	for _, asset := range m.Devices.UAPs { | ||||
| 		pts, err := UAPPoints(asset, m.TS) | ||||
| 		processPoints(m, pts, err) | ||||
| 	} | ||||
| 	for _, asset := range m.Devices.USGs { | ||||
| 		pts, err := USGPoints(asset, m.TS) | ||||
| 		processPoints(m, pts, err) | ||||
| 	} | ||||
| 	for _, asset := range m.Devices.USWs { | ||||
| 		pts, err := USWPoints(asset, m.TS) | ||||
| 		processPoints(m, pts, err) | ||||
| 	} | ||||
| 	for _, asset := range m.Devices.UDMs { | ||||
| 		pts, err := UDMPoints(asset, m.TS) | ||||
| 		processPoints(m, pts, err) | ||||
| 	} | ||||
| 	return errs | ||||
| 	r.Elapsed = time.Since(r.Start) | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| // collect runs in a go routine and batches all the points.
 | ||||
| func (u *InfluxUnifi) collect(r report, ch chan *metric) { | ||||
| 	for m := range ch { | ||||
| 		pt, err := influx.NewPoint(m.Table, m.Tags, m.Fields, r.metrics().TS) | ||||
| 		if err != nil { | ||||
| 			r.error(err) | ||||
| 		} else { | ||||
| 			r.batch(m, pt) | ||||
| 		} | ||||
| 		r.done() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // loopPoints kicks off 3 or 7 go routines to process metrics and send them
 | ||||
| // to the collect routine through the metric channel.
 | ||||
| func (u *InfluxUnifi) loopPoints(r report) { | ||||
| 	m := r.metrics() | ||||
| 	r.add() | ||||
| 	go func() { | ||||
| 		defer r.done() | ||||
| 		for _, s := range m.Sites { | ||||
| 			u.batchSite(r, s) | ||||
| 		} | ||||
| 	}() | ||||
| 	r.add() | ||||
| 	go func() { | ||||
| 		defer r.done() | ||||
| 		for _, s := range m.Clients { | ||||
| 			u.batchClient(r, s) | ||||
| 		} | ||||
| 	}() | ||||
| 	r.add() | ||||
| 	go func() { | ||||
| 		defer r.done() | ||||
| 		for _, s := range m.IDSList { | ||||
| 			u.batchIDS(r, s) | ||||
| 		} | ||||
| 	}() | ||||
| 	if m.Devices == nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	r.add() | ||||
| 	go func() { | ||||
| 		defer r.done() | ||||
| 		for _, s := range m.UAPs { | ||||
| 			u.batchUAP(r, s) | ||||
| 		} | ||||
| 	}() | ||||
| 	r.add() | ||||
| 	go func() { | ||||
| 		defer r.done() | ||||
| 		for _, s := range m.USGs { | ||||
| 			u.batchUSG(r, s) | ||||
| 		} | ||||
| 	}() | ||||
| 	r.add() | ||||
| 	go func() { | ||||
| 		defer r.done() | ||||
| 		for _, s := range m.USWs { | ||||
| 			u.batchUSW(r, s) | ||||
| 		} | ||||
| 	}() | ||||
| 	r.add() | ||||
| 	go func() { | ||||
| 		defer r.done() | ||||
| 		for _, s := range m.UDMs { | ||||
| 			u.batchUDM(r, s) | ||||
| 		} | ||||
| 	}() | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,64 @@ | |||
| package influxunifi | ||||
| 
 | ||||
| import ( | ||||
| 	"sync" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/davidnewhall/unifi-poller/pkg/metrics" | ||||
| 	influx "github.com/influxdata/influxdb1-client/v2" | ||||
| ) | ||||
| 
 | ||||
| // Report is returned to the calling procedure after everything is processed.
 | ||||
| type Report struct { | ||||
| 	Metrics *metrics.Metrics | ||||
| 	Errors  []error | ||||
| 	Total   int | ||||
| 	Fields  int | ||||
| 	Start   time.Time | ||||
| 	Elapsed time.Duration | ||||
| 	ch      chan *metric | ||||
| 	wg      sync.WaitGroup | ||||
| 	bp      influx.BatchPoints | ||||
| } | ||||
| 
 | ||||
| // report is an internal interface that can be mocked and overrridden for tests.
 | ||||
| type report interface { | ||||
| 	add() | ||||
| 	done() | ||||
| 	send(m *metric) | ||||
| 	error(err error) | ||||
| 	batch(m *metric, pt *influx.Point) | ||||
| 	metrics() *metrics.Metrics | ||||
| } | ||||
| 
 | ||||
| func (r *Report) metrics() *metrics.Metrics { | ||||
| 	return r.Metrics | ||||
| } | ||||
| 
 | ||||
| // satisfy gomnd
 | ||||
| const one = 1 | ||||
| 
 | ||||
| func (r *Report) add() { | ||||
| 	r.wg.Add(one) | ||||
| } | ||||
| 
 | ||||
| func (r *Report) done() { | ||||
| 	r.wg.Add(-one) | ||||
| } | ||||
| 
 | ||||
| func (r *Report) send(m *metric) { | ||||
| 	r.wg.Add(one) | ||||
| 	r.ch <- m | ||||
| } | ||||
| 
 | ||||
| /* The following methods are not thread safe. */ | ||||
| 
 | ||||
| func (r *Report) error(err error) { | ||||
| 	r.Errors = append(r.Errors, err) | ||||
| } | ||||
| 
 | ||||
| func (r *Report) batch(m *metric, p *influx.Point) { | ||||
| 	r.Total++ | ||||
| 	r.Fields += len(m.Fields) | ||||
| 	r.bp.AddPoint(p) | ||||
| } | ||||
|  | @ -1,83 +1,56 @@ | |||
| package influxunifi | ||||
| 
 | ||||
| import ( | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	influx "github.com/influxdata/influxdb1-client/v2" | ||||
| 	"golift.io/unifi" | ||||
| ) | ||||
| 
 | ||||
| // SitePoints generates Unifi Sites' datapoints for InfluxDB.
 | ||||
| // batchSite generates Unifi Sites' datapoints for InfluxDB.
 | ||||
| // These points can be passed directly to influx.
 | ||||
| func SitePoints(u *unifi.Site, now time.Time) ([]*influx.Point, error) { | ||||
| 	points := []*influx.Point{} | ||||
| 	for _, s := range u.Health { | ||||
| func (u *InfluxUnifi) batchSite(r report, s *unifi.Site) { | ||||
| 	for _, h := range s.Health { | ||||
| 		tags := map[string]string{ | ||||
| 			"id":                   u.ID, | ||||
| 			"name":                 u.Name, | ||||
| 			"site_name":            u.SiteName, | ||||
| 			"desc":                 u.Desc, | ||||
| 			"status":               s.Status, | ||||
| 			"subsystem":            s.Subsystem, | ||||
| 			"wan_ip":               s.WanIP, | ||||
| 			"netmask":              s.Netmask, | ||||
| 			"gw_name":              s.GwName, | ||||
| 			"gw_mac":               s.GwMac, | ||||
| 			"gw_version":           s.GwVersion, | ||||
| 			"speedtest_status":     s.SpeedtestStatus, | ||||
| 			"lan_ip":               s.LanIP, | ||||
| 			"remote_user_enabled":  s.RemoteUserEnabled.Txt, | ||||
| 			"site_to_site_enabled": s.SiteToSiteEnabled.Txt, | ||||
| 			"nameservers":          strings.Join(s.Nameservers, ","), | ||||
| 			"gateways":             strings.Join(s.Gateways, ","), | ||||
| 			"num_new_alarms":       u.NumNewAlarms.Txt, | ||||
| 			"attr_hidden_id":       u.AttrHiddenID, | ||||
| 			"attr_no_delete":       u.AttrNoDelete.Txt, | ||||
| 			"name":      s.Name, | ||||
| 			"site_name": s.SiteName, | ||||
| 			"desc":      s.Desc, | ||||
| 			"status":    h.Status, | ||||
| 			"subsystem": h.Subsystem, | ||||
| 			"wan_ip":    h.WanIP, | ||||
| 			"gw_name":   h.GwName, | ||||
| 			"lan_ip":    h.LanIP, | ||||
| 		} | ||||
| 		fields := map[string]interface{}{ | ||||
| 			"attr_hidden_id":           u.AttrHiddenID, | ||||
| 			"attr_no_delete":           u.AttrNoDelete.Val, | ||||
| 			"num_user":                 s.NumUser.Val, | ||||
| 			"num_guest":                s.NumGuest.Val, | ||||
| 			"num_iot":                  s.NumIot.Val, | ||||
| 			"tx_bytes-r":               s.TxBytesR.Val, | ||||
| 			"rx_bytes-r":               s.RxBytesR.Val, | ||||
| 			"status":                   s.Status, | ||||
| 			"num_ap":                   s.NumAp.Val, | ||||
| 			"num_adopted":              s.NumAdopted.Val, | ||||
| 			"num_disabled":             s.NumDisabled.Val, | ||||
| 			"num_disconnected":         s.NumDisconnected.Val, | ||||
| 			"num_pending":              s.NumPending.Val, | ||||
| 			"num_gw":                   s.NumGw.Val, | ||||
| 			"wan_ip":                   s.WanIP, | ||||
| 			"num_sta":                  s.NumSta.Val, | ||||
| 			"gw_cpu":                   s.GwSystemStats.CPU.Val, | ||||
| 			"gw_mem":                   s.GwSystemStats.Mem.Val, | ||||
| 			"gw_uptime":                s.GwSystemStats.Uptime.Val, | ||||
| 			"latency":                  s.Latency.Val, | ||||
| 			"uptime":                   s.Uptime.Val, | ||||
| 			"drops":                    s.Drops.Val, | ||||
| 			"xput_up":                  s.XputUp.Val, | ||||
| 			"xput_down":                s.XputDown.Val, | ||||
| 			"speedtest_ping":           s.SpeedtestPing.Val, | ||||
| 			"speedtest_lastrun":        s.SpeedtestLastrun.Val, | ||||
| 			"num_sw":                   s.NumSw.Val, | ||||
| 			"remote_user_num_active":   s.RemoteUserNumActive.Val, | ||||
| 			"remote_user_num_inactive": s.RemoteUserNumInactive.Val, | ||||
| 			"remote_user_rx_bytes":     s.RemoteUserRxBytes.Val, | ||||
| 			"remote_user_tx_bytes":     s.RemoteUserTxBytes.Val, | ||||
| 			"remote_user_rx_packets":   s.RemoteUserRxPackets.Val, | ||||
| 			"remote_user_tx_packets":   s.RemoteUserTxPackets.Val, | ||||
| 			"num_new_alarms":           u.NumNewAlarms.Val, | ||||
| 			"nameservers":              len(s.Nameservers), | ||||
| 			"gateways":                 len(s.Gateways), | ||||
| 			"num_user":                 h.NumUser.Val, | ||||
| 			"num_guest":                h.NumGuest.Val, | ||||
| 			"num_iot":                  h.NumIot.Val, | ||||
| 			"tx_bytes-r":               h.TxBytesR.Val, | ||||
| 			"rx_bytes-r":               h.RxBytesR.Val, | ||||
| 			"num_ap":                   h.NumAp.Val, | ||||
| 			"num_adopted":              h.NumAdopted.Val, | ||||
| 			"num_disabled":             h.NumDisabled.Val, | ||||
| 			"num_disconnected":         h.NumDisconnected.Val, | ||||
| 			"num_pending":              h.NumPending.Val, | ||||
| 			"num_gw":                   h.NumGw.Val, | ||||
| 			"wan_ip":                   h.WanIP, | ||||
| 			"num_sta":                  h.NumSta.Val, | ||||
| 			"gw_cpu":                   h.GwSystemStats.CPU.Val, | ||||
| 			"gw_mem":                   h.GwSystemStats.Mem.Val, | ||||
| 			"gw_uptime":                h.GwSystemStats.Uptime.Val, | ||||
| 			"latency":                  h.Latency.Val, | ||||
| 			"uptime":                   h.Uptime.Val, | ||||
| 			"drops":                    h.Drops.Val, | ||||
| 			"xput_up":                  h.XputUp.Val, | ||||
| 			"xput_down":                h.XputDown.Val, | ||||
| 			"speedtest_ping":           h.SpeedtestPing.Val, | ||||
| 			"speedtest_lastrun":        h.SpeedtestLastrun.Val, | ||||
| 			"num_sw":                   h.NumSw.Val, | ||||
| 			"remote_user_num_active":   h.RemoteUserNumActive.Val, | ||||
| 			"remote_user_num_inactive": h.RemoteUserNumInactive.Val, | ||||
| 			"remote_user_rx_bytes":     h.RemoteUserRxBytes.Val, | ||||
| 			"remote_user_tx_bytes":     h.RemoteUserTxBytes.Val, | ||||
| 			"remote_user_rx_packets":   h.RemoteUserRxPackets.Val, | ||||
| 			"remote_user_tx_packets":   h.RemoteUserTxPackets.Val, | ||||
| 			"num_new_alarms":           s.NumNewAlarms.Val, | ||||
| 		} | ||||
| 		pt, err := influx.NewPoint("subsystems", tags, fields, time.Now()) | ||||
| 		if err != nil { | ||||
| 			return points, err | ||||
| 		} | ||||
| 		points = append(points, pt) | ||||
| 		r.send(&metric{Table: "subsystems", Tags: tags, Fields: fields}) | ||||
| 	} | ||||
| 	return points, nil | ||||
| } | ||||
|  |  | |||
|  | @ -1,214 +1,176 @@ | |||
| package influxunifi | ||||
| 
 | ||||
| import ( | ||||
| 	"time" | ||||
| 
 | ||||
| 	influx "github.com/influxdata/influxdb1-client/v2" | ||||
| 	"golift.io/unifi" | ||||
| ) | ||||
| 
 | ||||
| // UAPPoints generates Wireless-Access-Point datapoints for InfluxDB.
 | ||||
| // batchUAP generates Wireless-Access-Point datapoints for InfluxDB.
 | ||||
| // These points can be passed directly to influx.
 | ||||
| func UAPPoints(u *unifi.UAP, now time.Time) ([]*influx.Point, error) { | ||||
| 	if u.Stat.Ap == nil { | ||||
| 		u.Stat.Ap = &unifi.Ap{} | ||||
| func (u *InfluxUnifi) batchUAP(r report, s *unifi.UAP) { | ||||
| 	if s.Stat.Ap == nil { | ||||
| 		s.Stat.Ap = &unifi.Ap{} | ||||
| 	} | ||||
| 	tags := map[string]string{ | ||||
| 		"id":                  u.ID, | ||||
| 		"ip":                  u.IP, | ||||
| 		"mac":                 u.Mac, | ||||
| 		"device_type":         u.Stat.Ap.O, | ||||
| 		"device_oid":          u.Stat.Ap.Oid, | ||||
| 		"device_ap":           u.Stat.Ap.Ap, | ||||
| 		"site_id":             u.SiteID, | ||||
| 		"site_name":           u.SiteName, | ||||
| 		"name":                u.Name, | ||||
| 		"adopted":             u.Adopted.Txt, | ||||
| 		"cfgversion":          u.Cfgversion, | ||||
| 		"config_network_ip":   u.ConfigNetwork.IP, | ||||
| 		"config_network_type": u.ConfigNetwork.Type, | ||||
| 		"connect_request_ip":  u.ConnectRequestIP, | ||||
| 		"device_id":           u.DeviceID, | ||||
| 		"has_eth1":            u.HasEth1.Txt, | ||||
| 		"inform_ip":           u.InformIP, | ||||
| 		"known_cfgversion":    u.KnownCfgversion, | ||||
| 		"model":               u.Model, | ||||
| 		"serial":              u.Serial, | ||||
| 		"type":                u.Type, | ||||
| 		"mac":       s.Mac, | ||||
| 		"site_name": s.SiteName, | ||||
| 		"name":      s.Name, | ||||
| 		"version":   s.Version, | ||||
| 		"model":     s.Model, | ||||
| 		"serial":    s.Serial, | ||||
| 		"type":      s.Type, | ||||
| 	} | ||||
| 	fields := map[string]interface{}{ | ||||
| 		"ip":            u.IP, | ||||
| 		"bytes":         u.Bytes.Val, | ||||
| 		"last_seen":     u.LastSeen.Val, | ||||
| 		"rx_bytes":      u.RxBytes.Val, | ||||
| 		"tx_bytes":      u.TxBytes.Val, | ||||
| 		"uptime":        u.Uptime.Val, | ||||
| 		"state":         u.State, | ||||
| 		"user-num_sta":  int(u.UserNumSta.Val), | ||||
| 		"guest-num_sta": int(u.GuestNumSta.Val), | ||||
| 		"num_sta":       u.NumSta.Val, | ||||
| 		"version":       u.Version, | ||||
| 		"loadavg_1":     u.SysStats.Loadavg1.Val, | ||||
| 		"loadavg_5":     u.SysStats.Loadavg5.Val, | ||||
| 		"loadavg_15":    u.SysStats.Loadavg15.Val, | ||||
| 		"mem_buffer":    u.SysStats.MemBuffer.Val, | ||||
| 		"mem_total":     u.SysStats.MemTotal.Val, | ||||
| 		"mem_used":      u.SysStats.MemUsed.Val, | ||||
| 		"cpu":           u.SystemStats.CPU.Val, | ||||
| 		"mem":           u.SystemStats.Mem.Val, | ||||
| 		"system_uptime": u.SystemStats.Uptime.Val, | ||||
| 		// Accumulative Statistics.
 | ||||
| 		"stat_user-rx_packets":  u.Stat.Ap.UserRxPackets.Val, | ||||
| 		"stat_guest-rx_packets": u.Stat.Ap.GuestRxPackets.Val, | ||||
| 		"stat_rx_packets":       u.Stat.Ap.RxPackets.Val, | ||||
| 		"stat_user-rx_bytes":    u.Stat.Ap.UserRxBytes.Val, | ||||
| 		"stat_guest-rx_bytes":   u.Stat.Ap.GuestRxBytes.Val, | ||||
| 		"stat_rx_bytes":         u.Stat.Ap.RxBytes.Val, | ||||
| 		"stat_user-rx_errors":   u.Stat.Ap.UserRxErrors.Val, | ||||
| 		"stat_guest-rx_errors":  u.Stat.Ap.GuestRxErrors.Val, | ||||
| 		"stat_rx_errors":        u.Stat.Ap.RxErrors.Val, | ||||
| 		"stat_user-rx_dropped":  u.Stat.Ap.UserRxDropped.Val, | ||||
| 		"stat_guest-rx_dropped": u.Stat.Ap.GuestRxDropped.Val, | ||||
| 		"stat_rx_dropped":       u.Stat.Ap.RxDropped.Val, | ||||
| 		"stat_user-rx_crypts":   u.Stat.Ap.UserRxCrypts.Val, | ||||
| 		"stat_guest-rx_crypts":  u.Stat.Ap.GuestRxCrypts.Val, | ||||
| 		"stat_rx_crypts":        u.Stat.Ap.RxCrypts.Val, | ||||
| 		"stat_user-rx_frags":    u.Stat.Ap.UserRxFrags.Val, | ||||
| 		"stat_guest-rx_frags":   u.Stat.Ap.GuestRxFrags.Val, | ||||
| 		"stat_rx_frags":         u.Stat.Ap.RxFrags.Val, | ||||
| 		"stat_user-tx_packets":  u.Stat.Ap.UserTxPackets.Val, | ||||
| 		"stat_guest-tx_packets": u.Stat.Ap.GuestTxPackets.Val, | ||||
| 		"stat_tx_packets":       u.Stat.Ap.TxPackets.Val, | ||||
| 		"stat_user-tx_bytes":    u.Stat.Ap.UserTxBytes.Val, | ||||
| 		"stat_guest-tx_bytes":   u.Stat.Ap.GuestTxBytes.Val, | ||||
| 		"stat_tx_bytes":         u.Stat.Ap.TxBytes.Val, | ||||
| 		"stat_user-tx_errors":   u.Stat.Ap.UserTxErrors.Val, | ||||
| 		"stat_guest-tx_errors":  u.Stat.Ap.GuestTxErrors.Val, | ||||
| 		"stat_tx_errors":        u.Stat.Ap.TxErrors.Val, | ||||
| 		"stat_user-tx_dropped":  u.Stat.Ap.UserTxDropped.Val, | ||||
| 		"stat_guest-tx_dropped": u.Stat.Ap.GuestTxDropped.Val, | ||||
| 		"stat_tx_dropped":       u.Stat.Ap.TxDropped.Val, | ||||
| 		"stat_user-tx_retries":  u.Stat.Ap.UserTxRetries.Val, | ||||
| 		"stat_guest-tx_retries": u.Stat.Ap.GuestTxRetries.Val, | ||||
| 	} | ||||
| 	pt, err := influx.NewPoint("uap", tags, fields, now) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	morePoints, err := processVAPs(u.VapTable, u.RadioTable, u.RadioTableStats, u.Name, u.ID, u.Mac, u.SiteName, now) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return append(morePoints, pt), nil | ||||
| 	fields := Combine(u.processUAPstats(s.Stat.Ap), u.batchSysStats(s.SysStats, s.SystemStats)) | ||||
| 	fields["ip"] = s.IP | ||||
| 	fields["bytes"] = s.Bytes.Val | ||||
| 	fields["last_seen"] = s.LastSeen.Val | ||||
| 	fields["rx_bytes"] = s.RxBytes.Val | ||||
| 	fields["tx_bytes"] = s.TxBytes.Val | ||||
| 	fields["uptime"] = s.Uptime.Val | ||||
| 	fields["state"] = s.State | ||||
| 	fields["user-num_sta"] = int(s.UserNumSta.Val) | ||||
| 	fields["guest-num_sta"] = int(s.GuestNumSta.Val) | ||||
| 	fields["num_sta"] = s.NumSta.Val | ||||
| 	r.send(&metric{Table: "uap", Tags: tags, Fields: fields}) | ||||
| 	u.processRadTable(r, tags, s.RadioTable, s.RadioTableStats) | ||||
| 	u.processVAPTable(r, tags, s.VapTable) | ||||
| } | ||||
| 
 | ||||
| // processVAPs creates points for Wifi Radios. This works with several types of UAP-capable devices.
 | ||||
| func processVAPs(vt unifi.VapTable, rt unifi.RadioTable, rts unifi.RadioTableStats, name, id, mac, sitename string, ts time.Time) ([]*influx.Point, error) { | ||||
| 	tags := make(map[string]string) | ||||
| 	fields := make(map[string]interface{}) | ||||
| 	points := []*influx.Point{} | ||||
| func (u *InfluxUnifi) processUAPstats(ap *unifi.Ap) map[string]interface{} { | ||||
| 	// Accumulative Statistics.
 | ||||
| 	return map[string]interface{}{ | ||||
| 		"stat_user-rx_packets":  ap.UserRxPackets.Val, | ||||
| 		"stat_guest-rx_packets": ap.GuestRxPackets.Val, | ||||
| 		"stat_rx_packets":       ap.RxPackets.Val, | ||||
| 		"stat_user-rx_bytes":    ap.UserRxBytes.Val, | ||||
| 		"stat_guest-rx_bytes":   ap.GuestRxBytes.Val, | ||||
| 		"stat_rx_bytes":         ap.RxBytes.Val, | ||||
| 		"stat_user-rx_errors":   ap.UserRxErrors.Val, | ||||
| 		"stat_guest-rx_errors":  ap.GuestRxErrors.Val, | ||||
| 		"stat_rx_errors":        ap.RxErrors.Val, | ||||
| 		"stat_user-rx_dropped":  ap.UserRxDropped.Val, | ||||
| 		"stat_guest-rx_dropped": ap.GuestRxDropped.Val, | ||||
| 		"stat_rx_dropped":       ap.RxDropped.Val, | ||||
| 		"stat_user-rx_crypts":   ap.UserRxCrypts.Val, | ||||
| 		"stat_guest-rx_crypts":  ap.GuestRxCrypts.Val, | ||||
| 		"stat_rx_crypts":        ap.RxCrypts.Val, | ||||
| 		"stat_user-rx_frags":    ap.UserRxFrags.Val, | ||||
| 		"stat_guest-rx_frags":   ap.GuestRxFrags.Val, | ||||
| 		"stat_rx_frags":         ap.RxFrags.Val, | ||||
| 		"stat_user-tx_packets":  ap.UserTxPackets.Val, | ||||
| 		"stat_guest-tx_packets": ap.GuestTxPackets.Val, | ||||
| 		"stat_tx_packets":       ap.TxPackets.Val, | ||||
| 		"stat_user-tx_bytes":    ap.UserTxBytes.Val, | ||||
| 		"stat_guest-tx_bytes":   ap.GuestTxBytes.Val, | ||||
| 		"stat_tx_bytes":         ap.TxBytes.Val, | ||||
| 		"stat_user-tx_errors":   ap.UserTxErrors.Val, | ||||
| 		"stat_guest-tx_errors":  ap.GuestTxErrors.Val, | ||||
| 		"stat_tx_errors":        ap.TxErrors.Val, | ||||
| 		"stat_user-tx_dropped":  ap.UserTxDropped.Val, | ||||
| 		"stat_guest-tx_dropped": ap.GuestTxDropped.Val, | ||||
| 		"stat_tx_dropped":       ap.TxDropped.Val, | ||||
| 		"stat_user-tx_retries":  ap.UserTxRetries.Val, | ||||
| 		"stat_guest-tx_retries": ap.GuestTxRetries.Val, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 	// Loop each virtual AP (ESSID) and extract data for it
 | ||||
| 	// from radio_tables and radio_table_stats.
 | ||||
| // processVAPTable creates points for Wifi Radios. This works with several types of UAP-capable devices.
 | ||||
| func (u *InfluxUnifi) processVAPTable(r report, t map[string]string, vt unifi.VapTable) { | ||||
| 	for _, s := range vt { | ||||
| 		tags["device_name"] = name | ||||
| 		tags["device_id"] = id | ||||
| 		tags["device_mac"] = mac | ||||
| 		tags["site_name"] = sitename | ||||
| 		tags["ap_mac"] = s.ApMac | ||||
| 		tags["bssid"] = s.Bssid | ||||
| 		tags["id"] = s.ID | ||||
| 		tags["name"] = s.Name | ||||
| 		tags["radio_name"] = s.RadioName | ||||
| 		tags["wlanconf_id"] = s.WlanconfID | ||||
| 		tags["essid"] = s.Essid | ||||
| 		tags["site_id"] = s.SiteID | ||||
| 		tags["usage"] = s.Usage | ||||
| 		tags["state"] = s.State | ||||
| 		tags["is_guest"] = s.IsGuest.Txt | ||||
| 		tags["is_wep"] = s.IsWep.Txt | ||||
| 
 | ||||
| 		fields["ccq"] = s.Ccq | ||||
| 		fields["extchannel"] = s.Extchannel | ||||
| 		fields["mac_filter_rejections"] = s.MacFilterRejections | ||||
| 		fields["num_satisfaction_sta"] = s.NumSatisfactionSta.Val | ||||
| 		fields["avg_client_signal"] = s.AvgClientSignal.Val | ||||
| 		fields["satisfaction"] = s.Satisfaction.Val | ||||
| 		fields["satisfaction_now"] = s.SatisfactionNow.Val | ||||
| 		fields["rx_bytes"] = s.RxBytes.Val | ||||
| 		fields["rx_crypts"] = s.RxCrypts.Val | ||||
| 		fields["rx_dropped"] = s.RxDropped.Val | ||||
| 		fields["rx_errors"] = s.RxErrors.Val | ||||
| 		fields["rx_frags"] = s.RxFrags.Val | ||||
| 		fields["rx_nwids"] = s.RxNwids.Val | ||||
| 		fields["rx_packets"] = s.RxPackets.Val | ||||
| 		fields["tx_bytes"] = s.TxBytes.Val | ||||
| 		fields["tx_dropped"] = s.TxDropped.Val | ||||
| 		fields["tx_errors"] = s.TxErrors.Val | ||||
| 		fields["tx_packets"] = s.TxPackets.Val | ||||
| 		fields["tx_power"] = s.TxPower.Val | ||||
| 		fields["tx_retries"] = s.TxRetries.Val | ||||
| 		fields["tx_combined_retries"] = s.TxCombinedRetries.Val | ||||
| 		fields["tx_data_mpdu_bytes"] = s.TxDataMpduBytes.Val | ||||
| 		fields["tx_rts_retries"] = s.TxRtsRetries.Val | ||||
| 		fields["tx_success"] = s.TxSuccess.Val | ||||
| 		fields["tx_total"] = s.TxTotal.Val | ||||
| 		fields["tx_tcp_goodbytes"] = s.TxTCPStats.Goodbytes.Val | ||||
| 		fields["tx_tcp_lat_avg"] = s.TxTCPStats.LatAvg.Val | ||||
| 		fields["tx_tcp_lat_max"] = s.TxTCPStats.LatMax.Val | ||||
| 		fields["tx_tcp_lat_min"] = s.TxTCPStats.LatMin.Val | ||||
| 		fields["rx_tcp_goodbytes"] = s.RxTCPStats.Goodbytes.Val | ||||
| 		fields["rx_tcp_lat_avg"] = s.RxTCPStats.LatAvg.Val | ||||
| 		fields["rx_tcp_lat_max"] = s.RxTCPStats.LatMax.Val | ||||
| 		fields["rx_tcp_lat_min"] = s.RxTCPStats.LatMin.Val | ||||
| 		fields["wifi_tx_latency_mov_avg"] = s.WifiTxLatencyMov.Avg.Val | ||||
| 		fields["wifi_tx_latency_mov_max"] = s.WifiTxLatencyMov.Max.Val | ||||
| 		fields["wifi_tx_latency_mov_min"] = s.WifiTxLatencyMov.Min.Val | ||||
| 		fields["wifi_tx_latency_mov_total"] = s.WifiTxLatencyMov.Total.Val | ||||
| 		fields["wifi_tx_latency_mov_cuont"] = s.WifiTxLatencyMov.TotalCount.Val | ||||
| 
 | ||||
| 		for _, p := range rt { | ||||
| 			if p.Name != s.RadioName { | ||||
| 				continue | ||||
| 			} | ||||
| 			tags["wlangroup_id"] = p.WlangroupID | ||||
| 			tags["channel"] = p.Channel.Txt | ||||
| 			tags["radio"] = p.Radio | ||||
| 			fields["current_antenna_gain"] = p.CurrentAntennaGain.Val | ||||
| 			fields["ht"] = p.Ht.Txt | ||||
| 			fields["max_txpower"] = p.MaxTxpower.Val | ||||
| 			fields["min_rssi_enabled"] = p.MinRssiEnabled.Val | ||||
| 			fields["min_txpower"] = p.MinTxpower.Val | ||||
| 			fields["nss"] = p.Nss.Val | ||||
| 			fields["radio_caps"] = p.RadioCaps.Val | ||||
| 			fields["tx_power"] = p.TxPower.Val | ||||
| 		tags := map[string]string{ | ||||
| 			"device_name": t["name"], | ||||
| 			"site_name":   t["site_name"], | ||||
| 			"ap_mac":      s.ApMac, | ||||
| 			"bssid":       s.Bssid, | ||||
| 			"id":          s.ID, | ||||
| 			"name":        s.Name, | ||||
| 			"radio_name":  s.RadioName, | ||||
| 			"radio":       s.Radio, | ||||
| 			"essid":       s.Essid, | ||||
| 			"site_id":     s.SiteID, | ||||
| 			"usage":       s.Usage, | ||||
| 			"state":       s.State, | ||||
| 			"is_guest":    s.IsGuest.Txt, | ||||
| 		} | ||||
| 
 | ||||
| 		for _, p := range rts { | ||||
| 			if p.Name != s.RadioName { | ||||
| 				continue | ||||
| 			} | ||||
| 			fields["ast_be_xmit"] = p.AstBeXmit.Val | ||||
| 			fields["channel"] = p.Channel.Val | ||||
| 			fields["cu_self_rx"] = p.CuSelfRx.Val | ||||
| 			fields["cu_self_tx"] = p.CuSelfTx.Val | ||||
| 			fields["cu_total"] = p.CuTotal.Val | ||||
| 			fields["extchannel"] = p.Extchannel.Val | ||||
| 			fields["gain"] = p.Gain.Val | ||||
| 			fields["guest-num_sta"] = p.GuestNumSta.Val | ||||
| 			fields["num_sta"] = p.NumSta.Val | ||||
| 			fields["radio"] = p.Radio | ||||
| 			fields["tx_packets"] = p.TxPackets.Val | ||||
| 			fields["tx_power"] = p.TxPower.Val | ||||
| 			fields["tx_retries"] = p.TxRetries.Val | ||||
| 			fields["user-num_sta"] = p.UserNumSta.Val | ||||
| 		fields := map[string]interface{}{ | ||||
| 			"ccq":                       s.Ccq, | ||||
| 			"mac_filter_rejections":     s.MacFilterRejections, | ||||
| 			"num_satisfaction_sta":      s.NumSatisfactionSta.Val, | ||||
| 			"avg_client_signal":         s.AvgClientSignal.Val, | ||||
| 			"satisfaction":              s.Satisfaction.Val, | ||||
| 			"satisfaction_now":          s.SatisfactionNow.Val, | ||||
| 			"num_sta":                   s.NumSta, | ||||
| 			"channel":                   s.Channel, | ||||
| 			"rx_bytes":                  s.RxBytes.Val, | ||||
| 			"rx_crypts":                 s.RxCrypts.Val, | ||||
| 			"rx_dropped":                s.RxDropped.Val, | ||||
| 			"rx_errors":                 s.RxErrors.Val, | ||||
| 			"rx_frags":                  s.RxFrags.Val, | ||||
| 			"rx_nwids":                  s.RxNwids.Val, | ||||
| 			"rx_packets":                s.RxPackets.Val, | ||||
| 			"tx_bytes":                  s.TxBytes.Val, | ||||
| 			"tx_dropped":                s.TxDropped.Val, | ||||
| 			"tx_errors":                 s.TxErrors.Val, | ||||
| 			"tx_packets":                s.TxPackets.Val, | ||||
| 			"tx_power":                  s.TxPower.Val, | ||||
| 			"tx_retries":                s.TxRetries.Val, | ||||
| 			"tx_combined_retries":       s.TxCombinedRetries.Val, | ||||
| 			"tx_data_mpdu_bytes":        s.TxDataMpduBytes.Val, | ||||
| 			"tx_rts_retries":            s.TxRtsRetries.Val, | ||||
| 			"tx_success":                s.TxSuccess.Val, | ||||
| 			"tx_total":                  s.TxTotal.Val, | ||||
| 			"tx_tcp_goodbytes":          s.TxTCPStats.Goodbytes.Val, | ||||
| 			"tx_tcp_lat_avg":            s.TxTCPStats.LatAvg.Val, | ||||
| 			"tx_tcp_lat_max":            s.TxTCPStats.LatMax.Val, | ||||
| 			"tx_tcp_lat_min":            s.TxTCPStats.LatMin.Val, | ||||
| 			"rx_tcp_goodbytes":          s.RxTCPStats.Goodbytes.Val, | ||||
| 			"rx_tcp_lat_avg":            s.RxTCPStats.LatAvg.Val, | ||||
| 			"rx_tcp_lat_max":            s.RxTCPStats.LatMax.Val, | ||||
| 			"rx_tcp_lat_min":            s.RxTCPStats.LatMin.Val, | ||||
| 			"wifi_tx_latency_mov_avg":   s.WifiTxLatencyMov.Avg.Val, | ||||
| 			"wifi_tx_latency_mov_max":   s.WifiTxLatencyMov.Max.Val, | ||||
| 			"wifi_tx_latency_mov_min":   s.WifiTxLatencyMov.Min.Val, | ||||
| 			"wifi_tx_latency_mov_total": s.WifiTxLatencyMov.Total.Val, | ||||
| 			"wifi_tx_latency_mov_cuont": s.WifiTxLatencyMov.TotalCount.Val, | ||||
| 		} | ||||
| 
 | ||||
| 		pt, err := influx.NewPoint("uap_vaps", tags, fields, ts) | ||||
| 		if err != nil { | ||||
| 			return points, err | ||||
| 		} | ||||
| 		points = append(points, pt) | ||||
| 		r.send(&metric{Table: "uap_vaps", Tags: tags, Fields: fields}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (u *InfluxUnifi) processRadTable(r report, t map[string]string, rt unifi.RadioTable, rts unifi.RadioTableStats) { | ||||
| 	for _, p := range rt { | ||||
| 		tags := map[string]string{ | ||||
| 			"device_name": t["name"], | ||||
| 			"site_name":   t["site_name"], | ||||
| 			"channel":     p.Channel.Txt, | ||||
| 			"radio":       p.Radio, | ||||
| 		} | ||||
| 		fields := map[string]interface{}{ | ||||
| 			"current_antenna_gain": p.CurrentAntennaGain.Val, | ||||
| 			"ht":                   p.Ht.Txt, | ||||
| 			"max_txpower":          p.MaxTxpower.Val, | ||||
| 			"min_txpower":          p.MinTxpower.Val, | ||||
| 			"nss":                  p.Nss.Val, | ||||
| 			"radio_caps":           p.RadioCaps.Val, | ||||
| 		} | ||||
| 		for _, t := range rts { | ||||
| 			if t.Name == p.Name { | ||||
| 				fields["ast_be_xmit"] = t.AstBeXmit.Val | ||||
| 				fields["channel"] = t.Channel.Val | ||||
| 				fields["cu_self_rx"] = t.CuSelfRx.Val | ||||
| 				fields["cu_self_tx"] = t.CuSelfTx.Val | ||||
| 				fields["cu_total"] = t.CuTotal.Val | ||||
| 				fields["extchannel"] = t.Extchannel.Val | ||||
| 				fields["gain"] = t.Gain.Val | ||||
| 				fields["guest-num_sta"] = t.GuestNumSta.Val | ||||
| 				fields["num_sta"] = t.NumSta.Val | ||||
| 				fields["radio"] = t.Radio | ||||
| 				fields["tx_packets"] = t.TxPackets.Val | ||||
| 				fields["tx_power"] = t.TxPower.Val | ||||
| 				fields["tx_retries"] = t.TxRetries.Val | ||||
| 				fields["user-num_sta"] = t.UserNumSta.Val | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		r.send(&metric{Table: "uap_radios", Tags: tags, Fields: fields}) | ||||
| 	} | ||||
| 	return points, nil | ||||
| } | ||||
|  |  | |||
|  | @ -1,416 +1,141 @@ | |||
| package influxunifi | ||||
| 
 | ||||
| import ( | ||||
| 	"time" | ||||
| 
 | ||||
| 	influx "github.com/influxdata/influxdb1-client/v2" | ||||
| 	"golift.io/unifi" | ||||
| ) | ||||
| 
 | ||||
| // UDMPoints generates Unifi Gateway datapoints for InfluxDB.
 | ||||
| // These points can be passed directly to influx.
 | ||||
| func UDMPoints(u *unifi.UDM, now time.Time) ([]*influx.Point, error) { | ||||
| 	if u.Stat.Sw == nil { | ||||
| 		u.Stat.Sw = &unifi.Sw{} | ||||
| // Combines 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] | ||||
| 		} | ||||
| 	} | ||||
| 	if u.Stat.Gw == nil { | ||||
| 		u.Stat.Gw = &unifi.Gw{} | ||||
| 	return out | ||||
| } | ||||
| 
 | ||||
| // batchSysStats is used by all device types.
 | ||||
| func (u *InfluxUnifi) batchSysStats(s unifi.SysStats, ss unifi.SystemStats) map[string]interface{} { | ||||
| 	return map[string]interface{}{ | ||||
| 		"loadavg_1":     s.Loadavg1.Val, | ||||
| 		"loadavg_5":     s.Loadavg5.Val, | ||||
| 		"loadavg_15":    s.Loadavg15.Val, | ||||
| 		"mem_used":      s.MemUsed.Val, | ||||
| 		"mem_buffer":    s.MemBuffer.Val, | ||||
| 		"mem_total":     s.MemTotal.Val, | ||||
| 		"cpu":           ss.CPU.Val, | ||||
| 		"mem":           ss.Mem.Val, | ||||
| 		"system_uptime": ss.Uptime.Val, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // batchUDM generates Unifi Gateway datapoints for InfluxDB.
 | ||||
| // These points can be passed directly to influx.
 | ||||
| func (u *InfluxUnifi) batchUDM(r report, s *unifi.UDM) { | ||||
| 	if s.Stat.Sw == nil { | ||||
| 		s.Stat.Sw = &unifi.Sw{} | ||||
| 	} | ||||
| 	if s.Stat.Gw == nil { | ||||
| 		s.Stat.Gw = &unifi.Gw{} | ||||
| 	} | ||||
| 	tags := map[string]string{ | ||||
| 		"id":                     u.ID, | ||||
| 		"mac":                    u.Mac, | ||||
| 		"device_oid":             u.Stat.Gw.Oid, | ||||
| 		"site_id":                u.SiteID, | ||||
| 		"site_name":              u.SiteName, | ||||
| 		"adopted":                u.Adopted.Txt, | ||||
| 		"name":                   u.Name, | ||||
| 		"cfgversion":             u.Cfgversion, | ||||
| 		"config_network_ip":      u.ConfigNetwork.IP, | ||||
| 		"config_network_type":    u.ConfigNetwork.Type, | ||||
| 		"connect_request_ip":     u.ConnectRequestIP, | ||||
| 		"connect_request_port":   u.ConnectRequestPort, | ||||
| 		"device_id":              u.DeviceID, | ||||
| 		"guest_token":            u.GuestToken, | ||||
| 		"inform_ip":              u.InformIP, | ||||
| 		"known_cfgversion":       u.KnownCfgversion, | ||||
| 		"model":                  u.Model, | ||||
| 		"serial":                 u.Serial, | ||||
| 		"type":                   u.Type, | ||||
| 		"usg_caps":               u.UsgCaps.Txt, | ||||
| 		"speedtest-status-saved": u.SpeedtestStatusSaved.Txt, | ||||
| 		"wan1_up":                u.Wan1.Up.Txt, | ||||
| 		"wan2_up":                u.Wan2.Up.Txt, | ||||
| 		"mac":       s.Mac, | ||||
| 		"site_name": s.SiteName, | ||||
| 		"name":      s.Name, | ||||
| 		"version":   s.Version, | ||||
| 		"model":     s.Model, | ||||
| 		"serial":    s.Serial, | ||||
| 		"type":      s.Type, | ||||
| 	} | ||||
| 	fields := map[string]interface{}{ | ||||
| 		"ip":                             u.IP, | ||||
| 		"bytes":                          u.Bytes.Val, | ||||
| 		"last_seen":                      u.LastSeen.Val, | ||||
| 		"license_state":                  u.LicenseState, | ||||
| 		"fw_caps":                        u.FwCaps.Val, | ||||
| 		"guest-num_sta":                  u.GuestNumSta.Val, | ||||
| 		"rx_bytes":                       u.RxBytes.Val, | ||||
| 		"tx_bytes":                       u.TxBytes.Val, | ||||
| 		"uptime":                         u.Uptime.Val, | ||||
| 		"state":                          u.State.Val, | ||||
| 		"user-num_sta":                   u.UserNumSta.Val, | ||||
| 		"num_sta":                        u.NumSta.Val, | ||||
| 		"version":                        u.Version, | ||||
| 		"num_desktop":                    u.NumDesktop.Val, | ||||
| 		"num_handheld":                   u.NumHandheld.Val, | ||||
| 		"num_mobile":                     u.NumMobile.Val, | ||||
| 		"speedtest-status_latency":       u.SpeedtestStatus.Latency.Val, | ||||
| 		"speedtest-status_rundate":       u.SpeedtestStatus.Rundate.Val, | ||||
| 		"speedtest-status_runtime":       u.SpeedtestStatus.Runtime.Val, | ||||
| 		"speedtest-status_download":      u.SpeedtestStatus.StatusDownload.Val, | ||||
| 		"speedtest-status_ping":          u.SpeedtestStatus.StatusPing.Val, | ||||
| 		"speedtest-status_summary":       u.SpeedtestStatus.StatusSummary.Val, | ||||
| 		"speedtest-status_upload":        u.SpeedtestStatus.StatusUpload.Val, | ||||
| 		"speedtest-status_xput_download": u.SpeedtestStatus.XputDownload.Val, | ||||
| 		"speedtest-status_xput_upload":   u.SpeedtestStatus.XputUpload.Val, | ||||
| 		"config_network_wan_type":        u.ConfigNetwork.Type, | ||||
| 		"wan1_bytes-r":                   u.Wan1.BytesR.Val, | ||||
| 		"wan1_enable":                    u.Wan1.Enable.Val, | ||||
| 		"wan1_full_duplex":               u.Wan1.FullDuplex.Val, | ||||
| 		"wan1_gateway":                   u.Wan1.Gateway, | ||||
| 		"wan1_ifname":                    u.Wan1.Ifname, | ||||
| 		"wan1_ip":                        u.Wan1.IP, | ||||
| 		"wan1_mac":                       u.Wan1.Mac, | ||||
| 		"wan1_max_speed":                 u.Wan1.MaxSpeed.Val, | ||||
| 		"wan1_name":                      u.Wan1.Name, | ||||
| 		"wan1_netmask":                   u.Wan1.Netmask, | ||||
| 		"wan1_rx_bytes":                  u.Wan1.RxBytes.Val, | ||||
| 		"wan1_rx_bytes-r":                u.Wan1.RxBytesR.Val, | ||||
| 		"wan1_rx_dropped":                u.Wan1.RxDropped.Val, | ||||
| 		"wan1_rx_errors":                 u.Wan1.RxErrors.Val, | ||||
| 		"wan1_rx_multicast":              u.Wan1.RxMulticast.Val, | ||||
| 		"wan1_rx_packets":                u.Wan1.RxPackets.Val, | ||||
| 		"wan1_type":                      u.Wan1.Type, | ||||
| 		"wan1_speed":                     u.Wan1.Speed.Val, | ||||
| 		"wan1_up":                        u.Wan1.Up.Val, | ||||
| 		"wan1_tx_bytes":                  u.Wan1.TxBytes.Val, | ||||
| 		"wan1_tx_bytes-r":                u.Wan1.TxBytesR.Val, | ||||
| 		"wan1_tx_dropped":                u.Wan1.TxDropped.Val, | ||||
| 		"wan1_tx_errors":                 u.Wan1.TxErrors.Val, | ||||
| 		"wan1_tx_packets":                u.Wan1.TxPackets.Val, | ||||
| 		"wan2_bytes-r":                   u.Wan2.BytesR.Val, | ||||
| 		"wan2_enable":                    u.Wan2.Enable.Val, | ||||
| 		"wan2_full_duplex":               u.Wan2.FullDuplex.Val, | ||||
| 		"wan2_gateway":                   u.Wan2.Gateway, | ||||
| 		"wan2_ifname":                    u.Wan2.Ifname, | ||||
| 		"wan2_ip":                        u.Wan2.IP, | ||||
| 		"wan2_mac":                       u.Wan2.Mac, | ||||
| 		"wan2_max_speed":                 u.Wan2.MaxSpeed.Val, | ||||
| 		"wan2_name":                      u.Wan2.Name, | ||||
| 		"wan2_netmask":                   u.Wan2.Netmask, | ||||
| 		"wan2_rx_bytes":                  u.Wan2.RxBytes.Val, | ||||
| 		"wan2_rx_bytes-r":                u.Wan2.RxBytesR.Val, | ||||
| 		"wan2_rx_dropped":                u.Wan2.RxDropped.Val, | ||||
| 		"wan2_rx_errors":                 u.Wan2.RxErrors.Val, | ||||
| 		"wan2_rx_multicast":              u.Wan2.RxMulticast.Val, | ||||
| 		"wan2_rx_packets":                u.Wan2.RxPackets.Val, | ||||
| 		"wan2_type":                      u.Wan2.Type, | ||||
| 		"wan2_speed":                     u.Wan2.Speed.Val, | ||||
| 		"wan2_up":                        u.Wan2.Up.Val, | ||||
| 		"wan2_tx_bytes":                  u.Wan2.TxBytes.Val, | ||||
| 		"wan2_tx_bytes-r":                u.Wan2.TxBytesR.Val, | ||||
| 		"wan2_tx_dropped":                u.Wan2.TxDropped.Val, | ||||
| 		"wan2_tx_errors":                 u.Wan2.TxErrors.Val, | ||||
| 		"wan2_tx_packets":                u.Wan2.TxPackets.Val, | ||||
| 		"loadavg_1":                      u.SysStats.Loadavg1.Val, | ||||
| 		"loadavg_5":                      u.SysStats.Loadavg5.Val, | ||||
| 		"loadavg_15":                     u.SysStats.Loadavg15.Val, | ||||
| 		"mem_used":                       u.SysStats.MemUsed.Val, | ||||
| 		"mem_buffer":                     u.SysStats.MemBuffer.Val, | ||||
| 		"mem_total":                      u.SysStats.MemTotal.Val, | ||||
| 		"cpu":                            u.SystemStats.CPU.Val, | ||||
| 		"mem":                            u.SystemStats.Mem.Val, | ||||
| 		"system_uptime":                  u.SystemStats.Uptime.Val, | ||||
| 		"gw":                             u.Stat.Gw, | ||||
| 		"lan-rx_bytes":                   u.Stat.LanRxBytes.Val, | ||||
| 		"lan-rx_packets":                 u.Stat.LanRxPackets.Val, | ||||
| 		"lan-tx_bytes":                   u.Stat.LanTxBytes.Val, | ||||
| 		"lan-tx_packets":                 u.Stat.LanTxPackets.Val, | ||||
| 		"wan-rx_bytes":                   u.Stat.WanRxBytes.Val, | ||||
| 		"wan-rx_dropped":                 u.Stat.WanRxDropped.Val, | ||||
| 		"wan-rx_packets":                 u.Stat.WanRxPackets.Val, | ||||
| 		"wan-tx_bytes":                   u.Stat.WanTxBytes.Val, | ||||
| 		"wan-tx_packets":                 u.Stat.WanTxPackets.Val, | ||||
| 		"uplink_name":                    u.Uplink.Name, | ||||
| 		"uplink_latency":                 u.Uplink.Latency.Val, | ||||
| 		"uplink_speed":                   u.Uplink.Speed.Val, | ||||
| 		"uplink_num_ports":               u.Uplink.NumPort.Val, | ||||
| 		"uplink_max_speed":               u.Uplink.MaxSpeed.Val, | ||||
| 	} | ||||
| 	pt, err := influx.NewPoint("usg", tags, fields, now) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	points := []*influx.Point{pt} | ||||
| 	fields := Combine(map[string]interface{}{ | ||||
| 		"ip":                             s.IP, | ||||
| 		"bytes":                          s.Bytes.Val, | ||||
| 		"last_seen":                      s.LastSeen.Val, | ||||
| 		"license_state":                  s.LicenseState, | ||||
| 		"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, | ||||
| 		"version":                        s.Version, | ||||
| 		"num_desktop":                    s.NumDesktop.Val, | ||||
| 		"num_handheld":                   s.NumHandheld.Val, | ||||
| 		"num_mobile":                     s.NumMobile.Val, | ||||
| 		"speedtest-status_latency":       s.SpeedtestStatus.Latency.Val, | ||||
| 		"speedtest-status_runtime":       s.SpeedtestStatus.Runtime.Val, | ||||
| 		"speedtest-status_ping":          s.SpeedtestStatus.StatusPing.Val, | ||||
| 		"speedtest-status_xput_download": s.SpeedtestStatus.XputDownload.Val, | ||||
| 		"speedtest-status_xput_upload":   s.SpeedtestStatus.XputUpload.Val, | ||||
| 		"lan-rx_bytes":                   s.Stat.Gw.LanRxBytes.Val, | ||||
| 		"lan-rx_packets":                 s.Stat.Gw.LanRxPackets.Val, | ||||
| 		"lan-tx_bytes":                   s.Stat.Gw.LanTxBytes.Val, | ||||
| 		"lan-tx_packets":                 s.Stat.Gw.LanTxPackets.Val, | ||||
| 	}, u.batchSysStats(s.SysStats, s.SystemStats)) | ||||
| 	r.send(&metric{Table: "usg", Tags: tags, Fields: fields}) | ||||
| 	u.batchNetTable(r, tags, s.NetworkTable) | ||||
| 	u.batchUSGwans(r, tags, s.Wan1, s.Wan2) | ||||
| 
 | ||||
| 	tags = map[string]string{ | ||||
| 		"id":                     u.ID, | ||||
| 		"mac":                    u.Mac, | ||||
| 		"device_oid":             u.Stat.Sw.Oid, | ||||
| 		"site_id":                u.SiteID, | ||||
| 		"site_name":              u.SiteName, | ||||
| 		"name":                   u.Name, | ||||
| 		"adopted":                u.Adopted.Txt, | ||||
| 		"cfgversion":             u.Cfgversion, | ||||
| 		"config_network_ip":      u.ConfigNetwork.IP, | ||||
| 		"config_network_type":    u.ConfigNetwork.Type, | ||||
| 		"device_id":              u.DeviceID, | ||||
| 		"inform_ip":              u.InformIP, | ||||
| 		"known_cfgversion":       u.KnownCfgversion, | ||||
| 		"locating":               u.Locating.Txt, | ||||
| 		"model":                  u.Model, | ||||
| 		"serial":                 u.Serial, | ||||
| 		"type":                   u.Type, | ||||
| 		"dot1x_portctrl_enabled": u.Dot1XPortctrlEnabled.Txt, | ||||
| 		"flowctrl_enabled":       u.FlowctrlEnabled.Txt, | ||||
| 		"has_fan":                u.HasFan.Txt, | ||||
| 		"has_temperature":        u.HasTemperature.Txt, | ||||
| 		"jumboframe_enabled":     u.JumboframeEnabled.Txt, | ||||
| 		"stp_version":            u.StpVersion, | ||||
| 		"mac":       s.Mac, | ||||
| 		"site_name": s.SiteName, | ||||
| 		"name":      s.Name, | ||||
| 		"version":   s.Version, | ||||
| 		"model":     s.Model, | ||||
| 		"serial":    s.Serial, | ||||
| 		"type":      s.Type, | ||||
| 	} | ||||
| 	fields = map[string]interface{}{ | ||||
| 		"fw_caps":             u.FwCaps.Val, | ||||
| 		"guest-num_sta":       u.GuestLanNumSta.Val, | ||||
| 		"ip":                  u.IP, | ||||
| 		"bytes":               u.Bytes.Val, | ||||
| 		"fan_level":           float64(0), | ||||
| 		"general_temperature": float64(0), | ||||
| 		"last_seen":           u.LastSeen.Val, | ||||
| 		"license_state":       u.LicenseState, | ||||
| 		"overheating":         u.Overheating.Val, | ||||
| 		"rx_bytes":            u.RxBytes.Val, | ||||
| 		"tx_bytes":            u.TxBytes.Val, | ||||
| 		"uptime":              u.Uptime.Val, | ||||
| 		"state":               u.State.Val, | ||||
| 		"user-num_sta":        u.UserLanNumSta.Val, | ||||
| 		"num_sta":             u.LanNumSta.Val, | ||||
| 		"version":             u.Version, | ||||
| 		"loadavg_1":           u.SysStats.Loadavg1.Val, | ||||
| 		"loadavg_5":           u.SysStats.Loadavg5.Val, | ||||
| 		"loadavg_15":          u.SysStats.Loadavg15.Val, | ||||
| 		"mem_buffer":          u.SysStats.MemBuffer.Val, | ||||
| 		"mem_used":            u.SysStats.MemUsed.Val, | ||||
| 		"mem_total":           u.SysStats.MemTotal.Val, | ||||
| 		"cpu":                 u.SystemStats.CPU.Val, | ||||
| 		"mem":                 u.SystemStats.Mem.Val, | ||||
| 		"system_uptime":       u.SystemStats.Uptime.Val, | ||||
| 		"stp_priority":        u.StpPriority.Val, | ||||
| 		"stat_bytes":          u.Stat.Sw.Bytes.Val, | ||||
| 		"stat_rx_bytes":       u.Stat.Sw.RxBytes.Val, | ||||
| 		"stat_rx_crypts":      u.Stat.Sw.RxCrypts.Val, | ||||
| 		"stat_rx_dropped":     u.Stat.Sw.RxDropped.Val, | ||||
| 		"stat_rx_errors":      u.Stat.Sw.RxErrors.Val, | ||||
| 		"stat_rx_frags":       u.Stat.Sw.RxFrags.Val, | ||||
| 		"stat_rx_packets":     u.Stat.Sw.TxPackets.Val, | ||||
| 		"stat_tx_bytes":       u.Stat.Sw.TxBytes.Val, | ||||
| 		"stat_tx_dropped":     u.Stat.Sw.TxDropped.Val, | ||||
| 		"stat_tx_errors":      u.Stat.Sw.TxErrors.Val, | ||||
| 		"stat_tx_packets":     u.Stat.Sw.TxPackets.Val, | ||||
| 		"stat_tx_retries":     u.Stat.Sw.TxRetries.Val, | ||||
| 		"guest-num_sta":   s.GuestNumSta.Val, | ||||
| 		"ip":              s.IP, | ||||
| 		"bytes":           s.Bytes.Val, | ||||
| 		"last_seen":       s.LastSeen.Val, | ||||
| 		"rx_bytes":        s.RxBytes.Val, | ||||
| 		"tx_bytes":        s.TxBytes.Val, | ||||
| 		"uptime":          s.Uptime.Val, | ||||
| 		"state":           s.State.Val, | ||||
| 		"stat_bytes":      s.Stat.Sw.Bytes.Val, | ||||
| 		"stat_rx_bytes":   s.Stat.Sw.RxBytes.Val, | ||||
| 		"stat_rx_crypts":  s.Stat.Sw.RxCrypts.Val, | ||||
| 		"stat_rx_dropped": s.Stat.Sw.RxDropped.Val, | ||||
| 		"stat_rx_errors":  s.Stat.Sw.RxErrors.Val, | ||||
| 		"stat_rx_frags":   s.Stat.Sw.RxFrags.Val, | ||||
| 		"stat_rx_packets": s.Stat.Sw.TxPackets.Val, | ||||
| 		"stat_tx_bytes":   s.Stat.Sw.TxBytes.Val, | ||||
| 		"stat_tx_dropped": s.Stat.Sw.TxDropped.Val, | ||||
| 		"stat_tx_errors":  s.Stat.Sw.TxErrors.Val, | ||||
| 		"stat_tx_packets": s.Stat.Sw.TxPackets.Val, | ||||
| 		"stat_tx_retries": s.Stat.Sw.TxRetries.Val, | ||||
| 	} | ||||
| 	pt, err = influx.NewPoint("usw", tags, fields, now) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	points = append(points, pt) | ||||
| 	r.send(&metric{Table: "usw", Tags: tags, Fields: fields}) | ||||
| 	u.batchPortTable(r, tags, s.PortTable) | ||||
| 
 | ||||
| 	for _, p := range u.NetworkTable { | ||||
| 		tags := map[string]string{ | ||||
| 			"device_name":               u.Name, | ||||
| 			"device_id":                 u.ID, | ||||
| 			"device_mac":                u.Mac, | ||||
| 			"site_name":                 u.SiteName, | ||||
| 			"up":                        p.Up.Txt, | ||||
| 			"dhcpd_dns_enabled":         p.DhcpdDNSEnabled.Txt, | ||||
| 			"dhcpd_enabled":             p.DhcpdEnabled.Txt, | ||||
| 			"dhcpd_time_offset_enabled": p.DhcpdTimeOffsetEnabled.Txt, | ||||
| 			"dhcp_relay_enabledy":       p.DhcpRelayEnabled.Txt, | ||||
| 			"dhcpd_gateway_enabled":     p.DhcpdGatewayEnabled.Txt, | ||||
| 			"enabled":                   p.Enabled.Txt, | ||||
| 			"vlan_enabled":              p.VlanEnabled.Txt, | ||||
| 			"attr_no_delete":            p.AttrNoDelete.Txt, | ||||
| 			"is_guest":                  p.IsGuest.Txt, | ||||
| 			"is_nat":                    p.IsNat.Txt, | ||||
| 			"networkgroup":              p.Networkgroup, | ||||
| 			"site_id":                   p.SiteID, | ||||
| 		} | ||||
| 		fields := map[string]interface{}{ | ||||
| 			"domain_name":         p.DomainName, | ||||
| 			"dhcpd_start":         p.DhcpdStart, | ||||
| 			"dhcpd_stop":          p.DhcpdStop, | ||||
| 			"ip":                  p.IP, | ||||
| 			"ip_subnet":           p.IPSubnet, | ||||
| 			"mac":                 p.Mac, | ||||
| 			"name":                p.Name, | ||||
| 			"num_sta":             p.NumSta.Val, | ||||
| 			"purpose":             p.Purpose, | ||||
| 			"rx_bytes":            p.RxBytes.Val, | ||||
| 			"rx_packets":          p.RxPackets.Val, | ||||
| 			"tx_bytes":            p.TxBytes.Val, | ||||
| 			"tx_packets":          p.TxPackets.Val, | ||||
| 			"ipv6_interface_type": p.Ipv6InterfaceType, | ||||
| 			"attr_hidden_id":      p.AttrHiddenID, | ||||
| 		} | ||||
| 		pt, err = influx.NewPoint("usg_networks", tags, fields, now) | ||||
| 		if err != nil { | ||||
| 			return points, err | ||||
| 		} | ||||
| 		points = append(points, pt) | ||||
| 	} | ||||
| 
 | ||||
| 	for _, p := range u.PortTable { | ||||
| 		tags := map[string]string{ | ||||
| 			"site_id":       u.SiteID, | ||||
| 			"site_name":     u.SiteName, | ||||
| 			"device_name":   u.Name, | ||||
| 			"name":          p.Name, | ||||
| 			"enable":        p.Enable.Txt, | ||||
| 			"is_uplink":     p.IsUplink.Txt, | ||||
| 			"up":            p.Up.Txt, | ||||
| 			"portconf_id":   p.PortconfID, | ||||
| 			"dot1x_mode":    p.Dot1XMode, | ||||
| 			"dot1x_status":  p.Dot1XStatus, | ||||
| 			"stp_state":     p.StpState, | ||||
| 			"sfp_found":     p.SfpFound.Txt, | ||||
| 			"op_mode":       p.OpMode, | ||||
| 			"poe_mode":      p.PoeMode, | ||||
| 			"port_poe":      p.PortPoe.Txt, | ||||
| 			"port_idx":      p.PortIdx.Txt, | ||||
| 			"port_id":       u.Name + " Port " + p.PortIdx.Txt, | ||||
| 			"poe_enable":    p.PoeEnable.Txt, | ||||
| 			"flowctrl_rx":   p.FlowctrlRx.Txt, | ||||
| 			"flowctrl_tx":   p.FlowctrlTx.Txt, | ||||
| 			"autoneg":       p.Autoneg.Txt, | ||||
| 			"full_duplex":   p.FullDuplex.Txt, | ||||
| 			"jumbo":         p.Jumbo.Txt, | ||||
| 			"masked":        p.Masked.Txt, | ||||
| 			"poe_good":      p.PoeGood.Txt, | ||||
| 			"media":         p.Media, | ||||
| 			"poe_class":     p.PoeClass, | ||||
| 			"poe_caps":      p.PoeCaps.Txt, | ||||
| 			"aggregated_by": p.AggregatedBy.Txt, | ||||
| 		} | ||||
| 		fields := map[string]interface{}{ | ||||
| 			"dbytes_r":     p.BytesR.Val, | ||||
| 			"rx_broadcast": p.RxBroadcast.Val, | ||||
| 			"rx_bytes":     p.RxBytes.Val, | ||||
| 			"rx_bytes-r":   p.RxBytesR.Val, | ||||
| 			"rx_dropped":   p.RxDropped.Val, | ||||
| 			"rx_errors":    p.RxErrors.Val, | ||||
| 			"rx_multicast": p.RxMulticast.Val, | ||||
| 			"rx_packets":   p.RxPackets.Val, | ||||
| 			"speed":        p.Speed.Val, | ||||
| 			"stp_pathcost": p.StpPathcost.Val, | ||||
| 			"tx_broadcast": p.TxBroadcast.Val, | ||||
| 			"tx_bytes":     p.TxBytes.Val, | ||||
| 			"tx_bytes-r":   p.TxBytesR.Val, | ||||
| 			"tx_dropped":   p.TxDropped.Val, | ||||
| 			"tx_errors":    p.TxErrors.Val, | ||||
| 			"tx_multicast": p.TxMulticast.Val, | ||||
| 			"tx_packets":   p.TxPackets.Val, | ||||
| 			"poe_current":  p.PoeCurrent.Val, | ||||
| 			"poe_power":    p.PoePower.Val, | ||||
| 			"poe_voltage":  p.PoeVoltage.Val, | ||||
| 			"full_duplex":  p.FullDuplex.Val, | ||||
| 		} | ||||
| 		pt, err = influx.NewPoint("usw_ports", tags, fields, now) | ||||
| 		if err != nil { | ||||
| 			return points, err | ||||
| 		} | ||||
| 		points = append(points, pt) | ||||
| 	} | ||||
| 	if u.Stat.Ap == nil { | ||||
| 		return points, nil | ||||
| 	if s.Stat.Ap == nil { | ||||
| 		return | ||||
| 		// we're done now. the following code process UDM (non-pro) UAP data.
 | ||||
| 	} | ||||
| 	tags = map[string]string{ | ||||
| 		"id":                  u.ID, | ||||
| 		"ip":                  u.IP, | ||||
| 		"mac":                 u.Mac, | ||||
| 		"device_type":         u.Stat.Ap.O, | ||||
| 		"device_oid":          u.Stat.Ap.Oid, | ||||
| 		"device_ap":           u.Stat.Ap.Ap, | ||||
| 		"site_id":             u.SiteID, | ||||
| 		"site_name":           u.SiteName, | ||||
| 		"name":                u.Name, | ||||
| 		"adopted":             u.Adopted.Txt, | ||||
| 		"cfgversion":          u.Cfgversion, | ||||
| 		"config_network_ip":   u.ConfigNetwork.IP, | ||||
| 		"config_network_type": u.ConfigNetwork.Type, | ||||
| 		"connect_request_ip":  u.ConnectRequestIP, | ||||
| 		"device_id":           u.DeviceID, | ||||
| 		"has_eth1":            u.HasEth1.Txt, | ||||
| 		"inform_ip":           u.InformIP, | ||||
| 		"known_cfgversion":    u.KnownCfgversion, | ||||
| 		"model":               u.Model, | ||||
| 		"serial":              u.Serial, | ||||
| 		"type":                u.Type, | ||||
| 		"mac":       s.Mac, | ||||
| 		"site_name": s.SiteName, | ||||
| 		"name":      s.Name, | ||||
| 		"version":   s.Version, | ||||
| 		"model":     s.Model, | ||||
| 		"serial":    s.Serial, | ||||
| 		"type":      s.Type, | ||||
| 	} | ||||
| 	fields = map[string]interface{}{ | ||||
| 		"ip":            u.IP, | ||||
| 		"bytes":         u.Bytes.Val, | ||||
| 		"last_seen":     u.LastSeen.Val, | ||||
| 		"rx_bytes":      u.RxBytes.Val, | ||||
| 		"tx_bytes":      u.TxBytes.Val, | ||||
| 		"uptime":        u.Uptime.Val, | ||||
| 		"state":         int(u.State.Val), | ||||
| 		"user-num_sta":  int(u.UserWlanNumSta.Val), | ||||
| 		"guest-num_sta": int(u.GuestWlanNumSta.Val), | ||||
| 		"num_sta":       u.WlanNumSta.Val, | ||||
| 		"version":       u.Version, | ||||
| 		"loadavg_1":     u.SysStats.Loadavg1.Val, | ||||
| 		"loadavg_5":     u.SysStats.Loadavg5.Val, | ||||
| 		"loadavg_15":    u.SysStats.Loadavg15.Val, | ||||
| 		"mem_buffer":    u.SysStats.MemBuffer.Val, | ||||
| 		"mem_total":     u.SysStats.MemTotal.Val, | ||||
| 		"mem_used":      u.SysStats.MemUsed.Val, | ||||
| 		"cpu":           u.SystemStats.CPU.Val, | ||||
| 		"mem":           u.SystemStats.Mem.Val, | ||||
| 		"system_uptime": u.SystemStats.Uptime.Val, | ||||
| 		// Accumulative Statistics.
 | ||||
| 		"stat_user-rx_packets":  u.Stat.Ap.UserRxPackets.Val, | ||||
| 		"stat_guest-rx_packets": u.Stat.Ap.GuestRxPackets.Val, | ||||
| 		"stat_rx_packets":       u.Stat.Ap.RxPackets.Val, | ||||
| 		"stat_user-rx_bytes":    u.Stat.Ap.UserRxBytes.Val, | ||||
| 		"stat_guest-rx_bytes":   u.Stat.Ap.GuestRxBytes.Val, | ||||
| 		"stat_rx_bytes":         u.Stat.Ap.RxBytes.Val, | ||||
| 		"stat_user-rx_errors":   u.Stat.Ap.UserRxErrors.Val, | ||||
| 		"stat_guest-rx_errors":  u.Stat.Ap.GuestRxErrors.Val, | ||||
| 		"stat_rx_errors":        u.Stat.Ap.RxErrors.Val, | ||||
| 		"stat_user-rx_dropped":  u.Stat.Ap.UserRxDropped.Val, | ||||
| 		"stat_guest-rx_dropped": u.Stat.Ap.GuestRxDropped.Val, | ||||
| 		"stat_rx_dropped":       u.Stat.Ap.RxDropped.Val, | ||||
| 		"stat_user-rx_crypts":   u.Stat.Ap.UserRxCrypts.Val, | ||||
| 		"stat_guest-rx_crypts":  u.Stat.Ap.GuestRxCrypts.Val, | ||||
| 		"stat_rx_crypts":        u.Stat.Ap.RxCrypts.Val, | ||||
| 		"stat_user-rx_frags":    u.Stat.Ap.UserRxFrags.Val, | ||||
| 		"stat_guest-rx_frags":   u.Stat.Ap.GuestRxFrags.Val, | ||||
| 		"stat_rx_frags":         u.Stat.Ap.RxFrags.Val, | ||||
| 		"stat_user-tx_packets":  u.Stat.Ap.UserTxPackets.Val, | ||||
| 		"stat_guest-tx_packets": u.Stat.Ap.GuestTxPackets.Val, | ||||
| 		"stat_tx_packets":       u.Stat.Ap.TxPackets.Val, | ||||
| 		"stat_user-tx_bytes":    u.Stat.Ap.UserTxBytes.Val, | ||||
| 		"stat_guest-tx_bytes":   u.Stat.Ap.GuestTxBytes.Val, | ||||
| 		"stat_tx_bytes":         u.Stat.Ap.TxBytes.Val, | ||||
| 		"stat_user-tx_errors":   u.Stat.Ap.UserTxErrors.Val, | ||||
| 		"stat_guest-tx_errors":  u.Stat.Ap.GuestTxErrors.Val, | ||||
| 		"stat_tx_errors":        u.Stat.Ap.TxErrors.Val, | ||||
| 		"stat_user-tx_dropped":  u.Stat.Ap.UserTxDropped.Val, | ||||
| 		"stat_guest-tx_dropped": u.Stat.Ap.GuestTxDropped.Val, | ||||
| 		"stat_tx_dropped":       u.Stat.Ap.TxDropped.Val, | ||||
| 		"stat_user-tx_retries":  u.Stat.Ap.UserTxRetries.Val, | ||||
| 		"stat_guest-tx_retries": u.Stat.Ap.GuestTxRetries.Val, | ||||
| 	} | ||||
| 	pt, err = influx.NewPoint("uap", tags, fields, now) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	uapPoints, err := processVAPs(*u.VapTable, *u.RadioTable, *u.RadioTableStats, u.Name, u.ID, u.Mac, u.SiteName, now) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return append(append(points, pt), uapPoints...), nil | ||||
| 	fields = u.processUAPstats(s.Stat.Ap) | ||||
| 	fields["ip"] = s.IP | ||||
| 	fields["bytes"] = s.Bytes.Val | ||||
| 	fields["last_seen"] = s.LastSeen.Val | ||||
| 	fields["rx_bytes"] = s.RxBytes.Val | ||||
| 	fields["tx_bytes"] = s.TxBytes.Val | ||||
| 	fields["uptime"] = s.Uptime.Val | ||||
| 	fields["state"] = s.State | ||||
| 	fields["user-num_sta"] = int(s.UserNumSta.Val) | ||||
| 	fields["guest-num_sta"] = int(s.GuestNumSta.Val) | ||||
| 	fields["num_sta"] = s.NumSta.Val | ||||
| 	r.send(&metric{Table: "uap", Tags: tags, Fields: fields}) | ||||
| 	u.processRadTable(r, tags, *s.RadioTable, *s.RadioTableStats) | ||||
| 	u.processVAPTable(r, tags, *s.VapTable) | ||||
| } | ||||
|  |  | |||
|  | @ -1,185 +1,140 @@ | |||
| package influxunifi | ||||
| 
 | ||||
| import ( | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	influx "github.com/influxdata/influxdb1-client/v2" | ||||
| 	"golift.io/unifi" | ||||
| ) | ||||
| 
 | ||||
| // USGPoints generates Unifi Gateway datapoints for InfluxDB.
 | ||||
| // batchUSG generates Unifi Gateway datapoints for InfluxDB.
 | ||||
| // These points can be passed directly to influx.
 | ||||
| func USGPoints(u *unifi.USG, now time.Time) ([]*influx.Point, error) { | ||||
| 	if u.Stat.Gw == nil { | ||||
| 		u.Stat.Gw = &unifi.Gw{} | ||||
| func (u *InfluxUnifi) batchUSG(r report, s *unifi.USG) { | ||||
| 	if s.Stat.Gw == nil { | ||||
| 		s.Stat.Gw = &unifi.Gw{} | ||||
| 	} | ||||
| 	tags := map[string]string{ | ||||
| 		"id":                     u.ID, | ||||
| 		"mac":                    u.Mac, | ||||
| 		"device_type":            u.Stat.O, | ||||
| 		"device_oid":             u.Stat.Oid, | ||||
| 		"site_id":                u.SiteID, | ||||
| 		"site_name":              u.SiteName, | ||||
| 		"adopted":                u.Adopted.Txt, | ||||
| 		"name":                   u.Name, | ||||
| 		"cfgversion":             u.Cfgversion, | ||||
| 		"config_network_ip":      u.ConfigNetwork.IP, | ||||
| 		"config_network_type":    u.ConfigNetwork.Type, | ||||
| 		"connect_request_ip":     u.ConnectRequestIP, | ||||
| 		"connect_request_port":   u.ConnectRequestPort, | ||||
| 		"device_id":              u.DeviceID, | ||||
| 		"guest_token":            u.GuestToken, | ||||
| 		"inform_ip":              u.InformIP, | ||||
| 		"known_cfgversion":       u.KnownCfgversion, | ||||
| 		"led_override":           u.LedOverride, | ||||
| 		"locating":               u.Locating.Txt, | ||||
| 		"model":                  u.Model, | ||||
| 		"outdoor_mode_override":  u.OutdoorModeOverride, | ||||
| 		"serial":                 u.Serial, | ||||
| 		"type":                   u.Type, | ||||
| 		"usg_caps":               u.UsgCaps.Txt, | ||||
| 		"speedtest-status-saved": u.SpeedtestStatusSaved.Txt, | ||||
| 		"wan1_up":                u.Wan1.Up.Txt, | ||||
| 		"wan2_up":                u.Wan2.Up.Txt, | ||||
| 		"mac":       s.Mac, | ||||
| 		"site_name": s.SiteName, | ||||
| 		"name":      s.Name, | ||||
| 		"version":   s.Version, | ||||
| 		"model":     s.Model, | ||||
| 		"serial":    s.Serial, | ||||
| 		"type":      s.Type, | ||||
| 	} | ||||
| 	fields := map[string]interface{}{ | ||||
| 		"ip":                             u.IP, | ||||
| 		"bytes":                          u.Bytes.Val, | ||||
| 		"last_seen":                      u.LastSeen.Val, | ||||
| 		"license_state":                  u.LicenseState, | ||||
| 		"fw_caps":                        u.FwCaps.Val, | ||||
| 		"guest-num_sta":                  u.GuestNumSta.Val, | ||||
| 		"rx_bytes":                       u.RxBytes.Val, | ||||
| 		"tx_bytes":                       u.TxBytes.Val, | ||||
| 		"uptime":                         u.Uptime.Val, | ||||
| 		"roll_upgrade":                   u.Rollupgrade.Val, | ||||
| 		"state":                          u.State.Val, | ||||
| 		"upgradable":                     u.Upgradable.Val, | ||||
| 		"user-num_sta":                   u.UserNumSta.Val, | ||||
| 		"version":                        u.Version, | ||||
| 		"num_desktop":                    u.NumDesktop.Val, | ||||
| 		"num_handheld":                   u.NumHandheld.Val, | ||||
| 		"num_mobile":                     u.NumMobile.Val, | ||||
| 		"speedtest-status_latency":       u.SpeedtestStatus.Latency.Val, | ||||
| 		"speedtest-status_rundate":       u.SpeedtestStatus.Rundate.Val, | ||||
| 		"speedtest-status_runtime":       u.SpeedtestStatus.Runtime.Val, | ||||
| 		"speedtest-status_download":      u.SpeedtestStatus.StatusDownload.Val, | ||||
| 		"speedtest-status_ping":          u.SpeedtestStatus.StatusPing.Val, | ||||
| 		"speedtest-status_summary":       u.SpeedtestStatus.StatusSummary.Val, | ||||
| 		"speedtest-status_upload":        u.SpeedtestStatus.StatusUpload.Val, | ||||
| 		"speedtest-status_xput_download": u.SpeedtestStatus.XputDownload.Val, | ||||
| 		"speedtest-status_xput_upload":   u.SpeedtestStatus.XputUpload.Val, | ||||
| 		"config_network_wan_type":        u.ConfigNetwork.Type, | ||||
| 		"wan1_bytes-r":                   u.Wan1.BytesR.Val, | ||||
| 		"wan1_enable":                    u.Wan1.Enable.Val, | ||||
| 		"wan1_full_duplex":               u.Wan1.FullDuplex.Val, | ||||
| 		"wan1_gateway":                   u.Wan1.Gateway, | ||||
| 		"wan1_ifname":                    u.Wan1.Ifname, | ||||
| 		"wan1_ip":                        u.Wan1.IP, | ||||
| 		"wan1_mac":                       u.Wan1.Mac, | ||||
| 		"wan1_max_speed":                 u.Wan1.MaxSpeed.Val, | ||||
| 		"wan1_name":                      u.Wan1.Name, | ||||
| 		"wan1_netmask":                   u.Wan1.Netmask, | ||||
| 		"wan1_rx_bytes":                  u.Wan1.RxBytes.Val, | ||||
| 		"wan1_rx_bytes-r":                u.Wan1.RxBytesR.Val, | ||||
| 		"wan1_rx_dropped":                u.Wan1.RxDropped.Val, | ||||
| 		"wan1_rx_errors":                 u.Wan1.RxErrors.Val, | ||||
| 		"wan1_rx_multicast":              u.Wan1.RxMulticast.Val, | ||||
| 		"wan1_rx_packets":                u.Wan1.RxPackets.Val, | ||||
| 		"wan1_type":                      u.Wan1.Type, | ||||
| 		"wan1_speed":                     u.Wan1.Speed.Val, | ||||
| 		"wan1_up":                        u.Wan1.Up.Val, | ||||
| 		"wan1_tx_bytes":                  u.Wan1.TxBytes.Val, | ||||
| 		"wan1_tx_bytes-r":                u.Wan1.TxBytesR.Val, | ||||
| 		"wan1_tx_dropped":                u.Wan1.TxDropped.Val, | ||||
| 		"wan1_tx_errors":                 u.Wan1.TxErrors.Val, | ||||
| 		"wan1_tx_packets":                u.Wan1.TxPackets.Val, | ||||
| 		"wan2_bytes-r":                   u.Wan2.BytesR.Val, | ||||
| 		"wan2_enable":                    u.Wan2.Enable.Val, | ||||
| 		"wan2_full_duplex":               u.Wan2.FullDuplex.Val, | ||||
| 		"wan2_gateway":                   u.Wan2.Gateway, | ||||
| 		"wan2_ifname":                    u.Wan2.Ifname, | ||||
| 		"wan2_ip":                        u.Wan2.IP, | ||||
| 		"wan2_mac":                       u.Wan2.Mac, | ||||
| 		"wan2_max_speed":                 u.Wan2.MaxSpeed.Val, | ||||
| 		"wan2_name":                      u.Wan2.Name, | ||||
| 		"wan2_netmask":                   u.Wan2.Netmask, | ||||
| 		"wan2_rx_bytes":                  u.Wan2.RxBytes.Val, | ||||
| 		"wan2_rx_bytes-r":                u.Wan2.RxBytesR.Val, | ||||
| 		"wan2_rx_dropped":                u.Wan2.RxDropped.Val, | ||||
| 		"wan2_rx_errors":                 u.Wan2.RxErrors.Val, | ||||
| 		"wan2_rx_multicast":              u.Wan2.RxMulticast.Val, | ||||
| 		"wan2_rx_packets":                u.Wan2.RxPackets.Val, | ||||
| 		"wan2_type":                      u.Wan2.Type, | ||||
| 		"wan2_speed":                     u.Wan2.Speed.Val, | ||||
| 		"wan2_up":                        u.Wan2.Up.Val, | ||||
| 		"wan2_tx_bytes":                  u.Wan2.TxBytes.Val, | ||||
| 		"wan2_tx_bytes-r":                u.Wan2.TxBytesR.Val, | ||||
| 		"wan2_tx_dropped":                u.Wan2.TxDropped.Val, | ||||
| 		"wan2_tx_errors":                 u.Wan2.TxErrors.Val, | ||||
| 		"wan2_tx_packets":                u.Wan2.TxPackets.Val, | ||||
| 		"loadavg_1":                      u.SysStats.Loadavg1.Val, | ||||
| 		"loadavg_5":                      u.SysStats.Loadavg5.Val, | ||||
| 		"loadavg_15":                     u.SysStats.Loadavg15.Val, | ||||
| 		"mem_used":                       u.SysStats.MemUsed.Val, | ||||
| 		"mem_buffer":                     u.SysStats.MemBuffer.Val, | ||||
| 		"mem_total":                      u.SysStats.MemTotal.Val, | ||||
| 		"cpu":                            u.SystemStats.CPU.Val, | ||||
| 		"mem":                            u.SystemStats.Mem.Val, | ||||
| 		"system_uptime":                  u.SystemStats.Uptime.Val, | ||||
| 		"stat_duration":                  u.Stat.Duration.Val, | ||||
| 		"stat_datetime":                  u.Stat.Datetime, | ||||
| 		"gw":                             u.Stat.Gw, | ||||
| 		"lan-rx_bytes":                   u.Stat.LanRxBytes.Val, | ||||
| 		"lan-rx_packets":                 u.Stat.LanRxPackets.Val, | ||||
| 		"lan-tx_bytes":                   u.Stat.LanTxBytes.Val, | ||||
| 		"lan-tx_packets":                 u.Stat.LanTxPackets.Val, | ||||
| 		"wan-rx_bytes":                   u.Stat.WanRxBytes.Val, | ||||
| 		"wan-rx_dropped":                 u.Stat.WanRxDropped.Val, | ||||
| 		"wan-rx_packets":                 u.Stat.WanRxPackets.Val, | ||||
| 		"wan-tx_bytes":                   u.Stat.WanTxBytes.Val, | ||||
| 		"wan-tx_packets":                 u.Stat.WanTxPackets.Val, | ||||
| 		"uplink_name":                    u.Uplink.Name, | ||||
| 		"uplink_latency":                 u.Uplink.Latency.Val, | ||||
| 		"uplink_speed":                   u.Uplink.Speed.Val, | ||||
| 		"uplink_num_ports":               u.Uplink.NumPort.Val, | ||||
| 		"uplink_max_speed":               u.Uplink.MaxSpeed.Val, | ||||
| 	} | ||||
| 	pt, err := influx.NewPoint("usg", tags, fields, now) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	points := []*influx.Point{pt} | ||||
| 	for _, p := range u.NetworkTable { | ||||
| 	fields := Combine(map[string]interface{}{ | ||||
| 		"ip":                             s.IP, | ||||
| 		"bytes":                          s.Bytes.Val, | ||||
| 		"last_seen":                      s.LastSeen.Val, | ||||
| 		"license_state":                  s.LicenseState, | ||||
| 		"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, | ||||
| 		"version":                        s.Version, | ||||
| 		"num_desktop":                    s.NumDesktop.Val, | ||||
| 		"num_handheld":                   s.NumHandheld.Val, | ||||
| 		"uplink_latency":                 s.Uplink.Latency.Val, | ||||
| 		"uplink_speed":                   s.Uplink.Speed.Val, | ||||
| 		"num_mobile":                     s.NumMobile.Val, | ||||
| 		"speedtest-status_latency":       s.SpeedtestStatus.Latency.Val, | ||||
| 		"speedtest-status_runtime":       s.SpeedtestStatus.Runtime.Val, | ||||
| 		"speedtest-status_ping":          s.SpeedtestStatus.StatusPing.Val, | ||||
| 		"speedtest-status_xput_download": s.SpeedtestStatus.XputDownload.Val, | ||||
| 		"speedtest-status_xput_upload":   s.SpeedtestStatus.XputUpload.Val, | ||||
| 		"lan-rx_bytes":                   s.Stat.Gw.LanRxBytes.Val, | ||||
| 		"lan-rx_packets":                 s.Stat.Gw.LanRxPackets.Val, | ||||
| 		"lan-tx_bytes":                   s.Stat.Gw.LanTxBytes.Val, | ||||
| 		"lan-tx_packets":                 s.Stat.Gw.LanTxPackets.Val, | ||||
| 		"lan-rx_dropped":                 s.Stat.Gw.LanRxDropped.Val, | ||||
| 	}, u.batchSysStats(s.SysStats, s.SystemStats)) | ||||
| 	r.send(&metric{Table: "usg", Tags: tags, Fields: fields}) | ||||
| 	u.batchNetTable(r, tags, s.NetworkTable) | ||||
| 	u.batchUSGwans(r, tags, s.Wan1, s.Wan2) | ||||
| 	/* | ||||
| 		for _, p := range s.PortTable { | ||||
| 			t := map[string]string{ | ||||
| 				"device_name": tags["name"], | ||||
| 				"site_name":   tags["site_name"], | ||||
| 				"name":        p.Name, | ||||
| 				"ifname":      p.Ifname, | ||||
| 				"ip":          p.IP, | ||||
| 				"mac":         p.Mac, | ||||
| 				"up":          p.Up.Txt, | ||||
| 				"speed":       p.Speed.Txt, | ||||
| 				"full_duplex": p.FullDuplex.Txt, | ||||
| 				"enable":      p.Enable.Txt, | ||||
| 			} | ||||
| 			f := map[string]interface{}{ | ||||
| 				"rx_bytes":     p.RxBytes.Val, | ||||
| 				"rx_dropped":   p.RxDropped.Val, | ||||
| 				"rx_errors":    p.RxErrors.Val, | ||||
| 				"rx_packets":   p.RxBytes.Val, | ||||
| 				"tx_bytes":     p.TxBytes.Val, | ||||
| 				"tx_dropped":   p.TxDropped.Val, | ||||
| 				"tx_errors":    p.TxErrors.Val, | ||||
| 				"tx_packets":   p.TxPackets.Val, | ||||
| 				"rx_multicast": p.RxMulticast.Val, | ||||
| 				"dns_servers":  strings.Join(p.DNS, ","), | ||||
| 			} | ||||
| 			r.send(&metric{Table: "usg_ports", Tags: t, Fields: f}) | ||||
| 		} | ||||
| 	*/ | ||||
| } | ||||
| 
 | ||||
| func (u *InfluxUnifi) batchUSGwans(r report, tags map[string]string, wans ...unifi.Wan) { | ||||
| 	for _, wan := range wans { | ||||
| 		if !wan.Up.Val { | ||||
| 			continue | ||||
| 		} | ||||
| 		tags := map[string]string{ | ||||
| 			"device_name":               u.Name, | ||||
| 			"device_id":                 u.ID, | ||||
| 			"device_mac":                u.Mac, | ||||
| 			"site_name":                 u.SiteName, | ||||
| 			"up":                        p.Up.Txt, | ||||
| 			"dhcpd_dns_enabled":         p.DhcpdDNSEnabled.Txt, | ||||
| 			"dhcpd_enabled":             p.DhcpdEnabled.Txt, | ||||
| 			"dhcpd_time_offset_enabled": p.DhcpdTimeOffsetEnabled.Txt, | ||||
| 			"dhcp_relay_enabledy":       p.DhcpRelayEnabled.Txt, | ||||
| 			"dhcpd_gateway_enabled":     p.DhcpdGatewayEnabled.Txt, | ||||
| 			"enabled":                   p.Enabled.Txt, | ||||
| 			"vlan_enabled":              p.VlanEnabled.Txt, | ||||
| 			"attr_no_delete":            p.AttrNoDelete.Txt, | ||||
| 			"is_guest":                  p.IsGuest.Txt, | ||||
| 			"is_nat":                    p.IsNat.Txt, | ||||
| 			"networkgroup":              p.Networkgroup, | ||||
| 			"site_id":                   p.SiteID, | ||||
| 			"ip":                        p.IP, | ||||
| 			"ip_subnet":                 p.IPSubnet, | ||||
| 			"mac":                       p.Mac, | ||||
| 			"name":                      p.Name, | ||||
| 			"domain_name":               p.DomainName, | ||||
| 			"dhcpd_start":               p.DhcpdStart, | ||||
| 			"dhcpd_stop":                p.DhcpdStop, | ||||
| 			"ipv6_interface_type":       p.Ipv6InterfaceType, | ||||
| 			"attr_hidden_id":            p.AttrHiddenID, | ||||
| 			"purpose":                   p.Purpose, | ||||
| 			"device_name": tags["name"], | ||||
| 			"site_name":   tags["site_name"], | ||||
| 			"ip":          wan.IP, | ||||
| 			"purpose":     wan.Name, | ||||
| 			"mac":         wan.Mac, | ||||
| 			"ifname":      wan.Ifname, | ||||
| 			"type":        wan.Type, | ||||
| 			"up":          wan.Up.Txt, | ||||
| 			"enabled":     wan.Enable.Txt, | ||||
| 		} | ||||
| 		fields := map[string]interface{}{ | ||||
| 			"bytes-r":      wan.BytesR.Val, | ||||
| 			"full_duplex":  wan.FullDuplex.Val, | ||||
| 			"gateway":      wan.Gateway, | ||||
| 			"max_speed":    wan.MaxSpeed.Val, | ||||
| 			"rx_bytes":     wan.RxBytes.Val, | ||||
| 			"rx_bytes-r":   wan.RxBytesR.Val, | ||||
| 			"rx_dropped":   wan.RxDropped.Val, | ||||
| 			"rx_errors":    wan.RxErrors.Val, | ||||
| 			"rx_broadcast": wan.RxBroadcast.Val, | ||||
| 			"rx_multicast": wan.RxMulticast.Val, | ||||
| 			"rx_packets":   wan.RxPackets.Val, | ||||
| 			"speed":        wan.Speed.Val, | ||||
| 			"tx_bytes":     wan.TxBytes.Val, | ||||
| 			"tx_bytes-r":   wan.TxBytesR.Val, | ||||
| 			"tx_dropped":   wan.TxDropped.Val, | ||||
| 			"tx_errors":    wan.TxErrors.Val, | ||||
| 			"tx_packets":   wan.TxPackets.Val, | ||||
| 			"tx_broadcast": wan.TxBroadcast.Val, | ||||
| 			"tx_multicast": wan.TxMulticast.Val, | ||||
| 		} | ||||
| 		r.send(&metric{Table: "usg_wan_ports", Tags: tags, Fields: fields}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (u *InfluxUnifi) batchNetTable(r report, tags map[string]string, nt unifi.NetworkTable) { | ||||
| 	for _, p := range nt { | ||||
| 		tags := map[string]string{ | ||||
| 			"device_name": tags["name"], | ||||
| 			"site_name":   tags["site_name"], | ||||
| 			"up":          p.Up.Txt, | ||||
| 			"enabled":     p.Enabled.Txt, | ||||
| 			"ip":          p.IP, | ||||
| 			"mac":         p.Mac, | ||||
| 			"name":        p.Name, | ||||
| 			"domain_name": p.DomainName, | ||||
| 			"purpose":     p.Purpose, | ||||
| 			"is_guest":    p.IsGuest.Txt, | ||||
| 		} | ||||
| 		fields := map[string]interface{}{ | ||||
| 			"num_sta":    p.NumSta.Val, | ||||
|  | @ -188,44 +143,6 @@ func USGPoints(u *unifi.USG, now time.Time) ([]*influx.Point, error) { | |||
| 			"tx_bytes":   p.TxBytes.Val, | ||||
| 			"tx_packets": p.TxPackets.Val, | ||||
| 		} | ||||
| 		pt, err = influx.NewPoint("usg_networks", tags, fields, now) | ||||
| 		if err != nil { | ||||
| 			return points, err | ||||
| 		} | ||||
| 		points = append(points, pt) | ||||
| 		r.send(&metric{Table: "usg_networks", Tags: tags, Fields: fields}) | ||||
| 	} | ||||
| 	for _, p := range u.PortTable { | ||||
| 		tags := map[string]string{ | ||||
| 			"device_name": u.Name, | ||||
| 			"device_id":   u.ID, | ||||
| 			"device_mac":  u.Mac, | ||||
| 			"site_name":   u.SiteName, | ||||
| 			"name":        p.Name, | ||||
| 			"ifname":      p.Ifname, | ||||
| 			"ip":          p.IP, | ||||
| 			"mac":         p.Mac, | ||||
| 			"up":          p.Up.Txt, | ||||
| 			"speed":       p.Speed.Txt, | ||||
| 			"full_duplex": p.FullDuplex.Txt, | ||||
| 			"enable":      p.Enable.Txt, | ||||
| 		} | ||||
| 		fields := map[string]interface{}{ | ||||
| 			"rx_bytes":     p.RxBytes.Val, | ||||
| 			"rx_dropped":   p.RxDropped.Val, | ||||
| 			"rx_errors":    p.RxErrors.Val, | ||||
| 			"rx_packets":   p.RxBytes.Val, | ||||
| 			"tx_bytes":     p.TxBytes.Val, | ||||
| 			"tx_dropped":   p.TxDropped.Val, | ||||
| 			"tx_errors":    p.TxErrors.Val, | ||||
| 			"tx_packets":   p.TxPackets.Val, | ||||
| 			"rx_multicast": p.RxMulticast.Val, | ||||
| 			"dns_servers":  strings.Join(p.DNS, ","), | ||||
| 		} | ||||
| 		pt, err = influx.NewPoint("usg_ports", tags, fields, now) | ||||
| 		if err != nil { | ||||
| 			return points, err | ||||
| 		} | ||||
| 		points = append(points, pt) | ||||
| 	} | ||||
| 	return points, nil | ||||
| } | ||||
|  |  | |||
|  | @ -1,119 +1,70 @@ | |||
| package influxunifi | ||||
| 
 | ||||
| import ( | ||||
| 	"time" | ||||
| 
 | ||||
| 	influx "github.com/influxdata/influxdb1-client/v2" | ||||
| 	"golift.io/unifi" | ||||
| ) | ||||
| 
 | ||||
| // USWPoints generates Unifi Switch datapoints for InfluxDB.
 | ||||
| // batchUSW generates Unifi Switch datapoints for InfluxDB.
 | ||||
| // These points can be passed directly to influx.
 | ||||
| func USWPoints(u *unifi.USW, now time.Time) ([]*influx.Point, error) { | ||||
| 	if u.Stat.Sw == nil { | ||||
| 		u.Stat.Sw = &unifi.Sw{} | ||||
| func (u *InfluxUnifi) batchUSW(r report, s *unifi.USW) { | ||||
| 	if s.Stat.Sw == nil { | ||||
| 		s.Stat.Sw = &unifi.Sw{} | ||||
| 	} | ||||
| 	tags := map[string]string{ | ||||
| 		"id":                     u.ID, | ||||
| 		"mac":                    u.Mac, | ||||
| 		"device_oid":             u.Stat.Oid, | ||||
| 		"site_id":                u.SiteID, | ||||
| 		"site_name":              u.SiteName, | ||||
| 		"name":                   u.Name, | ||||
| 		"adopted":                u.Adopted.Txt, | ||||
| 		"cfgversion":             u.Cfgversion, | ||||
| 		"config_network_ip":      u.ConfigNetwork.IP, | ||||
| 		"config_network_type":    u.ConfigNetwork.Type, | ||||
| 		"device_id":              u.DeviceID, | ||||
| 		"inform_ip":              u.InformIP, | ||||
| 		"known_cfgversion":       u.KnownCfgversion, | ||||
| 		"locating":               u.Locating.Txt, | ||||
| 		"model":                  u.Model, | ||||
| 		"serial":                 u.Serial, | ||||
| 		"type":                   u.Type, | ||||
| 		"dot1x_portctrl_enabled": u.Dot1XPortctrlEnabled.Txt, | ||||
| 		"flowctrl_enabled":       u.FlowctrlEnabled.Txt, | ||||
| 		"has_fan":                u.HasFan.Txt, | ||||
| 		"has_temperature":        u.HasTemperature.Txt, | ||||
| 		"jumboframe_enabled":     u.JumboframeEnabled.Txt, | ||||
| 		"stp_version":            u.StpVersion, | ||||
| 		"mac":       s.Mac, | ||||
| 		"site_name": s.SiteName, | ||||
| 		"name":      s.Name, | ||||
| 		"version":   s.Version, | ||||
| 		"model":     s.Model, | ||||
| 		"serial":    s.Serial, | ||||
| 		"type":      s.Type, | ||||
| 	} | ||||
| 	fields := map[string]interface{}{ | ||||
| 		"fw_caps":             u.FwCaps.Val, | ||||
| 		"guest-num_sta":       u.GuestNumSta.Val, | ||||
| 		"ip":                  u.IP, | ||||
| 		"bytes":               u.Bytes.Val, | ||||
| 		"fan_level":           u.FanLevel.Val, | ||||
| 		"general_temperature": u.GeneralTemperature.Val, | ||||
| 		"last_seen":           u.LastSeen.Val, | ||||
| 		"license_state":       u.LicenseState, | ||||
| 		"overheating":         u.Overheating.Val, | ||||
| 		"rx_bytes":            u.RxBytes.Val, | ||||
| 		"tx_bytes":            u.TxBytes.Val, | ||||
| 		"uptime":              u.Uptime.Val, | ||||
| 		"state":               u.State.Val, | ||||
| 		"user-num_sta":        u.UserNumSta.Val, | ||||
| 		"version":             u.Version, | ||||
| 		"loadavg_1":           u.SysStats.Loadavg1.Val, | ||||
| 		"loadavg_5":           u.SysStats.Loadavg5.Val, | ||||
| 		"loadavg_15":          u.SysStats.Loadavg15.Val, | ||||
| 		"mem_buffer":          u.SysStats.MemBuffer.Val, | ||||
| 		"mem_used":            u.SysStats.MemUsed.Val, | ||||
| 		"mem_total":           u.SysStats.MemTotal.Val, | ||||
| 		"cpu":                 u.SystemStats.CPU.Val, | ||||
| 		"mem":                 u.SystemStats.Mem.Val, | ||||
| 		"stp_priority":        u.StpPriority.Val, | ||||
| 		"system_uptime":       u.SystemStats.Uptime.Val, | ||||
| 		"stat_bytes":          u.Stat.Bytes.Val, | ||||
| 		"stat_rx_bytes":       u.Stat.RxBytes.Val, | ||||
| 		"stat_rx_crypts":      u.Stat.RxCrypts.Val, | ||||
| 		"stat_rx_dropped":     u.Stat.RxDropped.Val, | ||||
| 		"stat_rx_errors":      u.Stat.RxErrors.Val, | ||||
| 		"stat_rx_frags":       u.Stat.RxFrags.Val, | ||||
| 		"stat_rx_packets":     u.Stat.TxPackets.Val, | ||||
| 		"stat_tx_bytes":       u.Stat.TxBytes.Val, | ||||
| 		"stat_tx_dropped":     u.Stat.TxDropped.Val, | ||||
| 		"stat_tx_errors":      u.Stat.TxErrors.Val, | ||||
| 		"stat_tx_packets":     u.Stat.TxPackets.Val, | ||||
| 		"stat_tx_retries":     u.Stat.TxRetries.Val, | ||||
| 		"uplink_depth":        u.UplinkDepth.Txt, | ||||
| 	} | ||||
| 	pt, err := influx.NewPoint("usw", tags, fields, now) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	points := []*influx.Point{pt} | ||||
| 	for _, p := range u.PortTable { | ||||
| 	fields := Combine(map[string]interface{}{ | ||||
| 		"guest-num_sta":       s.GuestNumSta.Val, | ||||
| 		"ip":                  s.IP, | ||||
| 		"bytes":               s.Bytes.Val, | ||||
| 		"fan_level":           s.FanLevel.Val, | ||||
| 		"general_temperature": s.GeneralTemperature.Val, | ||||
| 		"last_seen":           s.LastSeen.Val, | ||||
| 		"rx_bytes":            s.RxBytes.Val, | ||||
| 		"tx_bytes":            s.TxBytes.Val, | ||||
| 		"uptime":              s.Uptime.Val, | ||||
| 		"state":               s.State.Val, | ||||
| 		"user-num_sta":        s.UserNumSta.Val, | ||||
| 		"stat_bytes":          s.Stat.Sw.Bytes.Val, | ||||
| 		"stat_rx_bytes":       s.Stat.Sw.RxBytes.Val, | ||||
| 		"stat_rx_crypts":      s.Stat.Sw.RxCrypts.Val, | ||||
| 		"stat_rx_dropped":     s.Stat.Sw.RxDropped.Val, | ||||
| 		"stat_rx_errors":      s.Stat.Sw.RxErrors.Val, | ||||
| 		"stat_rx_frags":       s.Stat.Sw.RxFrags.Val, | ||||
| 		"stat_rx_packets":     s.Stat.Sw.TxPackets.Val, | ||||
| 		"stat_tx_bytes":       s.Stat.Sw.TxBytes.Val, | ||||
| 		"stat_tx_dropped":     s.Stat.Sw.TxDropped.Val, | ||||
| 		"stat_tx_errors":      s.Stat.Sw.TxErrors.Val, | ||||
| 		"stat_tx_packets":     s.Stat.Sw.TxPackets.Val, | ||||
| 		"stat_tx_retries":     s.Stat.Sw.TxRetries.Val, | ||||
| 	}, u.batchSysStats(s.SysStats, s.SystemStats)) | ||||
| 	r.send(&metric{Table: "usw", Tags: tags, Fields: fields}) | ||||
| 	u.batchPortTable(r, tags, s.PortTable) | ||||
| } | ||||
| 
 | ||||
| func (u *InfluxUnifi) batchPortTable(r report, t map[string]string, pt []unifi.Port) { | ||||
| 	for _, p := range pt { | ||||
| 		if !p.Up.Val || !p.Enable.Val { | ||||
| 			continue // only record UP ports.
 | ||||
| 		} | ||||
| 		tags := map[string]string{ | ||||
| 			"site_id":       u.SiteID, | ||||
| 			"site_name":     u.SiteName, | ||||
| 			"device_name":   u.Name, | ||||
| 			"name":          p.Name, | ||||
| 			"enable":        p.Enable.Txt, | ||||
| 			"is_uplink":     p.IsUplink.Txt, | ||||
| 			"up":            p.Up.Txt, | ||||
| 			"portconf_id":   p.PortconfID, | ||||
| 			"dot1x_mode":    p.Dot1XMode, | ||||
| 			"dot1x_status":  p.Dot1XStatus, | ||||
| 			"stp_state":     p.StpState, | ||||
| 			"sfp_found":     p.SfpFound.Txt, | ||||
| 			"op_mode":       p.OpMode, | ||||
| 			"poe_mode":      p.PoeMode, | ||||
| 			"port_poe":      p.PortPoe.Txt, | ||||
| 			"port_idx":      p.PortIdx.Txt, | ||||
| 			"port_id":       u.Name + " Port " + p.PortIdx.Txt, | ||||
| 			"poe_enable":    p.PoeEnable.Txt, | ||||
| 			"flowctrl_rx":   p.FlowctrlRx.Txt, | ||||
| 			"flowctrl_tx":   p.FlowctrlTx.Txt, | ||||
| 			"autoneg":       p.Autoneg.Txt, | ||||
| 			"full_duplex":   p.FullDuplex.Txt, | ||||
| 			"jumbo":         p.Jumbo.Txt, | ||||
| 			"masked":        p.Masked.Txt, | ||||
| 			"poe_good":      p.PoeGood.Txt, | ||||
| 			"media":         p.Media, | ||||
| 			"poe_class":     p.PoeClass, | ||||
| 			"poe_caps":      p.PoeCaps.Txt, | ||||
| 			"aggregated_by": p.AggregatedBy.Txt, | ||||
| 			"site_name":   t["site_name"], | ||||
| 			"device_name": t["name"], | ||||
| 			"name":        p.Name, | ||||
| 			"poe_mode":    p.PoeMode, | ||||
| 			"port_poe":    p.PortPoe.Txt, | ||||
| 			"port_idx":    p.PortIdx.Txt, | ||||
| 			"port_id":     t["name"] + " Port " + p.PortIdx.Txt, | ||||
| 			"poe_enable":  p.PoeEnable.Txt, | ||||
| 			"flowctrl_rx": p.FlowctrlRx.Txt, | ||||
| 			"flowctrl_tx": p.FlowctrlTx.Txt, | ||||
| 			"media":       p.Media, | ||||
| 		} | ||||
| 		fields := map[string]interface{}{ | ||||
| 			"dbytes_r":     p.BytesR.Val, | ||||
|  | @ -133,16 +84,12 @@ func USWPoints(u *unifi.USW, now time.Time) ([]*influx.Point, error) { | |||
| 			"tx_errors":    p.TxErrors.Val, | ||||
| 			"tx_multicast": p.TxMulticast.Val, | ||||
| 			"tx_packets":   p.TxPackets.Val, | ||||
| 			"poe_current":  p.PoeCurrent.Val, | ||||
| 			"poe_power":    p.PoePower.Val, | ||||
| 			"poe_voltage":  p.PoeVoltage.Val, | ||||
| 			"full_duplex":  p.FullDuplex.Val, | ||||
| 		} | ||||
| 		pt, err = influx.NewPoint("usw_ports", tags, fields, now) | ||||
| 		if err != nil { | ||||
| 			return points, err | ||||
| 		if p.PoeEnable.Val && p.PortPoe.Val { | ||||
| 			fields["poe_current"] = p.PoeCurrent.Val | ||||
| 			fields["poe_power"] = p.PoePower.Val | ||||
| 			fields["poe_voltage"] = p.PoeVoltage.Val | ||||
| 		} | ||||
| 		points = append(points, pt) | ||||
| 		r.send(&metric{Table: "usw_ports", Tags: tags, Fields: fields}) | ||||
| 	} | ||||
| 	return points, nil | ||||
| } | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ import ( | |||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/BurntSushi/toml" | ||||
| 	influx "github.com/influxdata/influxdb1-client/v2" | ||||
| 	"github.com/davidnewhall/unifi-poller/pkg/influxunifi" | ||||
| 	"github.com/spf13/pflag" | ||||
| 	"golift.io/unifi" | ||||
| 	yaml "gopkg.in/yaml.v2" | ||||
|  | @ -41,7 +41,7 @@ const ENVConfigPrefix = "UP_" | |||
| 
 | ||||
| // UnifiPoller contains the application startup data, and auth info for UniFi & Influx.
 | ||||
| type UnifiPoller struct { | ||||
| 	Influx     influx.Client | ||||
| 	Influx     *influxunifi.InfluxUnifi | ||||
| 	Unifi      *unifi.Unifi | ||||
| 	Flag       *Flag | ||||
| 	Config     *Config | ||||
|  | @ -65,9 +65,10 @@ type Config struct { | |||
| 	Debug      bool     `json:"debug" toml:"debug" xml:"debug" yaml:"debug"` | ||||
| 	Quiet      bool     `json:"quiet,omitempty" toml:"quiet,omitempty" xml:"quiet" yaml:"quiet"` | ||||
| 	VerifySSL  bool     `json:"verify_ssl" toml:"verify_ssl" xml:"verify_ssl" yaml:"verify_ssl"` | ||||
| 	CollectIDS bool     `json:"collect_ids" toml:"collect_ids" xml:"collect_ids" yaml:"collect_ids"` | ||||
| 	SaveIDS    bool     `json:"save_ids" toml:"save_ids" xml:"save_ids" yaml:"save_ids"` | ||||
| 	ReAuth     bool     `json:"reauthenticate" toml:"reauthenticate" xml:"reauthenticate" yaml:"reauthenticate"` | ||||
| 	InfxBadSSL bool     `json:"influx_insecure_ssl" toml:"influx_insecure_ssl" xml:"influx_insecure_ssl" yaml:"influx_insecure_ssl"` | ||||
| 	SaveSites  bool     `json:"save_sites,omitempty" toml:"save_sites,omitempty" xml:"save_sites" yaml:"save_sites"` | ||||
| 	Mode       string   `json:"mode" toml:"mode" xml:"mode" yaml:"mode"` | ||||
| 	HTTPListen string   `json:"http_listen" toml:"http_listen" xml:"http_listen" yaml:"http_listen"` | ||||
| 	Namespace  string   `json:"namespace" toml:"namespace" xml:"namespace" yaml:"namespace"` | ||||
|  |  | |||
|  | @ -1,21 +1,23 @@ | |||
| package poller | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/tls" | ||||
| 	"fmt" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/davidnewhall/unifi-poller/pkg/influxunifi" | ||||
| 	"github.com/davidnewhall/unifi-poller/pkg/metrics" | ||||
| 	influx "github.com/influxdata/influxdb1-client/v2" | ||||
| ) | ||||
| 
 | ||||
| // GetInfluxDB returns an InfluxDB interface.
 | ||||
| func (u *UnifiPoller) GetInfluxDB() (err error) { | ||||
| 	u.Influx, err = influx.NewHTTPClient(influx.HTTPConfig{ | ||||
| 		Addr:      u.Config.InfluxURL, | ||||
| 		Username:  u.Config.InfluxUser, | ||||
| 		Password:  u.Config.InfluxPass, | ||||
| 		TLSConfig: &tls.Config{InsecureSkipVerify: u.Config.InfxBadSSL}, | ||||
| 	if u.Influx != nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	u.Influx, err = influxunifi.New(&influxunifi.Config{ | ||||
| 		Database: u.Config.InfluxDB, | ||||
| 		User:     u.Config.InfluxUser, | ||||
| 		Pass:     u.Config.InfluxPass, | ||||
| 		BadSSL:   u.Config.InfxBadSSL, | ||||
| 		URL:      u.Config.InfluxURL, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("influxdb: %v", err) | ||||
|  | @ -30,51 +32,32 @@ func (u *UnifiPoller) GetInfluxDB() (err error) { | |||
| // determine if there was a read or write error and act on it. This is currently
 | ||||
| // called in two places in this library. One returns an error, one does not.
 | ||||
| func (u *UnifiPoller) CollectAndProcess() error { | ||||
| 	if err := u.GetInfluxDB(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	metrics, err := u.CollectMetrics() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	u.AugmentMetrics(metrics) | ||||
| 	err = u.ReportMetrics(metrics) | ||||
| 	u.LogError(err, "processing metrics") | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // ReportMetrics batches all the metrics and writes them to InfluxDB.
 | ||||
| // This creates an InfluxDB writer, and returns an error if the write fails.
 | ||||
| func (u *UnifiPoller) ReportMetrics(metrics *metrics.Metrics) error { | ||||
| 	// Batch (and send) all the points.
 | ||||
| 	m := &influxunifi.Metrics{Metrics: metrics} | ||||
| 	// Make a new Influx Points Batcher.
 | ||||
| 	var err error | ||||
| 	m.BatchPoints, err = influx.NewBatchPoints(influx.BatchPointsConfig{Database: u.Config.InfluxDB}) | ||||
| 	report, err := u.Influx.ReportMetrics(metrics) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("influx.NewBatchPoints: %v", err) | ||||
| 		u.LogError(err, "processing metrics") | ||||
| 		return err | ||||
| 	} | ||||
| 	for _, err := range m.ProcessPoints() { | ||||
| 		u.LogError(err, "influx.ProcessPoints") | ||||
| 	} | ||||
| 	if err = u.Influx.Write(m.BatchPoints); err != nil { | ||||
| 		return fmt.Errorf("influxdb.Write(points): %v", err) | ||||
| 	} | ||||
| 	u.LogInfluxReport(m) | ||||
| 	u.LogInfluxReport(report) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // LogInfluxReport writes a log message after exporting to influxdb.
 | ||||
| func (u *UnifiPoller) LogInfluxReport(m *influxunifi.Metrics) { | ||||
| 	var fields, points int | ||||
| 	for _, p := range m.Points() { | ||||
| 		points++ | ||||
| 		i, _ := p.Fields() | ||||
| 		fields += len(i) | ||||
| 	} | ||||
| func (u *UnifiPoller) LogInfluxReport(r *influxunifi.Report) { | ||||
| 	idsMsg := "" | ||||
| 	if u.Config.CollectIDS { | ||||
| 		idsMsg = fmt.Sprintf("IDS Events: %d, ", len(m.IDSList)) | ||||
| 	if u.Config.SaveIDS { | ||||
| 		idsMsg = fmt.Sprintf("IDS Events: %d, ", len(r.Metrics.IDSList)) | ||||
| 	} | ||||
| 	u.Logf("UniFi Measurements Recorded. Sites: %d, Clients: %d, "+ | ||||
| 		"Wireless APs: %d, Gateways: %d, Switches: %d, %sPoints: %d, Fields: %d", | ||||
| 		len(m.Sites), len(m.Clients), len(m.UAPs), | ||||
| 		len(m.UDMs)+len(m.USGs), len(m.USWs), idsMsg, points, fields) | ||||
| 	u.Logf("UniFi Metrics Recorded. Sites: %d, Clients: %d, "+ | ||||
| 		"UAP: %d, USG/UDM: %d, USW: %d, %sPoints: %d, Fields: %d, Errs: %d, Elapsed: %v", | ||||
| 		len(r.Metrics.Sites), len(r.Metrics.Clients), len(r.Metrics.UAPs), | ||||
| 		len(r.Metrics.UDMs)+len(r.Metrics.USGs), len(r.Metrics.USWs), idsMsg, r.Total, | ||||
| 		r.Fields, len(r.Errors), r.Elapsed.Round(time.Millisecond)) | ||||
| } | ||||
|  |  | |||
|  | @ -1,7 +1,6 @@ | |||
| package poller | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | @ -54,16 +53,11 @@ func (u *UnifiPoller) ExportMetrics() (*metrics.Metrics, error) { | |||
| // This is run by Prometheus as LoggingFn
 | ||||
| func (u *UnifiPoller) LogExportReport(report *promunifi.Report) { | ||||
| 	m := report.Metrics | ||||
| 	idsMsg := "" | ||||
| 	if u.Config.CollectIDS { | ||||
| 		idsMsg = fmt.Sprintf(", IDS Events: %d, ", len(m.IDSList)) | ||||
| 	} | ||||
| 
 | ||||
| 	u.Logf("UniFi Measurements Exported. Site: %d, Client: %d, "+ | ||||
| 		"UAP: %d, USG/UDM: %d, USW: %d%s, Descs: %d, "+ | ||||
| 		"UAP: %d, USG/UDM: %d, USW: %d, Descs: %d, "+ | ||||
| 		"Metrics: %d, Errs: %d, 0s: %d, Reqs/Total: %v / %v", | ||||
| 		len(m.Sites), len(m.Clients), len(m.UAPs), len(m.UDMs)+len(m.USGs), len(m.USWs), | ||||
| 		idsMsg, report.Descs, report.Total, report.Errors, report.Zeros, | ||||
| 		report.Descs, report.Total, report.Errors, report.Zeros, | ||||
| 		report.Fetch.Round(time.Millisecond/oneDecimalPoint), | ||||
| 		report.Elapsed.Round(time.Millisecond/oneDecimalPoint)) | ||||
| } | ||||
|  |  | |||
|  | @ -21,13 +21,14 @@ func New() *UnifiPoller { | |||
| 			InfluxPass: defaultInfluxPass, | ||||
| 			InfluxDB:   defaultInfluxDB, | ||||
| 			UnifiUser:  defaultUnifiUser, | ||||
| 			UnifiPass:  defaultUnifiUser, | ||||
| 			UnifiPass:  "", | ||||
| 			UnifiBase:  defaultUnifiURL, | ||||
| 			Interval:   Duration{defaultInterval}, | ||||
| 			Sites:      []string{"all"}, | ||||
| 			SaveSites:  true, | ||||
| 			HTTPListen: defaultHTTPListen, | ||||
| 			Namespace:  appName, | ||||
| 		}, Flag: &Flag{}, | ||||
| 		}, Flag: &Flag{ConfigFile: DefaultConfFile}, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -99,43 +100,52 @@ func (u *UnifiPoller) Run() error { | |||
| 
 | ||||
| 	switch strings.ToLower(u.Config.Mode) { | ||||
| 	default: | ||||
| 		if err := u.GetInfluxDB(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return u.PollController() | ||||
| 
 | ||||
| 	case "influxlambda", "lambdainflux", "lambda_influx", "influx_lambda": | ||||
| 		if err := u.GetInfluxDB(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		u.LastCheck = time.Now() | ||||
| 		return u.CollectAndProcess() | ||||
| 
 | ||||
| 	case "prometheus", "exporter": | ||||
| 		return u.RunPrometheus() | ||||
| 	case "both": | ||||
| 		return u.RunBoth() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // RunBoth starts the prometheus exporter and influxdb exporter at the same time.
 | ||||
| // This will likely double the amount of polls your controller receives.
 | ||||
| func (u *UnifiPoller) RunBoth() error { | ||||
| 	e := make(chan error) | ||||
| 	defer close(e) | ||||
| 	go func() { | ||||
| 		e <- u.RunPrometheus() | ||||
| 	}() | ||||
| 	go func() { | ||||
| 		e <- u.PollController() | ||||
| 	}() | ||||
| 	// If either method returns an error (even nil), bail out.
 | ||||
| 	return <-e | ||||
| } | ||||
| 
 | ||||
| // PollController runs forever, polling UniFi and pushing to InfluxDB
 | ||||
| // This is started by Run() after everything checks out.
 | ||||
| // This is started by Run() or RunBoth() after everything checks out.
 | ||||
| func (u *UnifiPoller) PollController() error { | ||||
| 	interval := u.Config.Interval.Round(time.Second) | ||||
| 	log.Printf("[INFO] Everything checks out! Poller started, interval: %v", interval) | ||||
| 	ticker := time.NewTicker(interval) | ||||
| 	defer ticker.Stop() | ||||
| 
 | ||||
| 	for u.LastCheck = range ticker.C { | ||||
| 		var err error | ||||
| 		// Some users need to re-auth every interval because the cookie times out.
 | ||||
| 		if u.Config.ReAuth { | ||||
| 			u.LogDebugf("Re-authenticating to UniFi Controller") | ||||
| 			// Some users need to re-auth every interval because the cookie times out.
 | ||||
| 			if err = u.Unifi.Login(); err != nil { | ||||
| 				u.LogError(err, "re-authenticating") | ||||
| 			if err := u.Unifi.Login(); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 		if err == nil { | ||||
| 			// Only run this if the authentication procedure didn't return error.
 | ||||
| 			_ = u.CollectAndProcess() | ||||
| 		if err := u.CollectAndProcess(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		// check for errors from the unifi polls.
 | ||||
| 		if u.errorCount > 0 { | ||||
| 			return fmt.Errorf("too many errors, stopping poller") | ||||
| 		} | ||||
|  |  | |||
|  | @ -68,7 +68,7 @@ func (u *UnifiPoller) CollectMetrics() (*metrics.Metrics, error) { | |||
| 	// Get the sites we care about.
 | ||||
| 	m.Sites, err = u.GetFilteredSites() | ||||
| 	u.LogError(err, "unifi.GetSites()") | ||||
| 	if u.Config.CollectIDS { | ||||
| 	if u.Config.SaveIDS { | ||||
| 		m.IDSList, err = u.Unifi.GetIDS(m.Sites, time.Now().Add(u.Config.Interval.Duration), time.Now()) | ||||
| 		u.LogError(err, "unifi.GetIDS()") | ||||
| 	} | ||||
|  | @ -111,6 +111,9 @@ func (u *UnifiPoller) AugmentMetrics(metrics *metrics.Metrics) { | |||
| 		metrics.Clients[i].GwName = devices[c.GwMac] | ||||
| 		metrics.Clients[i].RadioDescription = bssdIDs[metrics.Clients[i].Bssid] + metrics.Clients[i].RadioProto | ||||
| 	} | ||||
| 	if !u.Config.SaveSites { | ||||
| 		metrics.Sites = nil | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // GetFilteredSites returns a list of sites to fetch data for.
 | ||||
|  |  | |||
|  | @ -72,7 +72,7 @@ func (u *promUnifi) exportSite(r report, s *unifi.Site) { | |||
| 			r.send([]*metric{ | ||||
| 				{u.Site.TxBytesR, prometheus.GaugeValue, h.TxBytesR, labels}, | ||||
| 				{u.Site.RxBytesR, prometheus.GaugeValue, h.RxBytesR, labels}, | ||||
| 				{u.Site.Uptime, prometheus.GaugeValue, h.Latency, labels}, | ||||
| 				{u.Site.Uptime, prometheus.GaugeValue, h.Uptime, labels}, | ||||
| 				{u.Site.Latency, prometheus.GaugeValue, h.Latency.Val / 1000, labels}, | ||||
| 				{u.Site.XputUp, prometheus.GaugeValue, h.XputUp, labels}, | ||||
| 				{u.Site.XputDown, prometheus.GaugeValue, h.XputDown, labels}, | ||||
|  |  | |||
|  | @ -96,7 +96,7 @@ func (u *promUnifi) exportUDM(r report, d *unifi.UDM) { | |||
| 	u.exportUSWstats(r, labels, d.Stat.Sw) | ||||
| 	u.exportPortTable(r, labels, d.PortTable) | ||||
| 	// Gateway Data
 | ||||
| 	u.exportUSGstats(r, labels, d.Stat.Gw, d.SpeedtestStatus) | ||||
| 	u.exportUSGstats(r, labels, d.Stat.Gw, d.SpeedtestStatus, d.Uplink) | ||||
| 	u.exportWANPorts(r, labels, d.Wan1, d.Wan2) | ||||
| 	// Wireless Data - UDM (non-pro) only
 | ||||
| 	if d.Stat.Ap != nil && d.VapTable != nil { | ||||
|  |  | |||
|  | @ -28,6 +28,8 @@ type usg struct { | |||
| 	WanTxMulticast *prometheus.Desc | ||||
| 	WanBytesR      *prometheus.Desc | ||||
| 	Latency        *prometheus.Desc | ||||
| 	UplinkLatency  *prometheus.Desc | ||||
| 	UplinkSpeed    *prometheus.Desc | ||||
| 	Runtime        *prometheus.Desc | ||||
| 	XputDownload   *prometheus.Desc | ||||
| 	XputUpload     *prometheus.Desc | ||||
|  | @ -60,6 +62,8 @@ func descUSG(ns string) *usg { | |||
| 		LanTxPackets:   prometheus.NewDesc(ns+"lan_transmit_packets_total", "LAN Transmit Packets Total", labels, nil), | ||||
| 		LanTxBytes:     prometheus.NewDesc(ns+"lan_transmit_bytes_total", "LAN Transmit Bytes Total", labels, nil), | ||||
| 		Latency:        prometheus.NewDesc(ns+"speedtest_latency_seconds", "Speedtest Latency", labels, nil), | ||||
| 		UplinkLatency:  prometheus.NewDesc(ns+"uplink_latency_seconds", "Uplink Latency", labels, nil), | ||||
| 		UplinkSpeed:    prometheus.NewDesc(ns+"uplink_speed_mbps", "Uplink Speed", labels, nil), | ||||
| 		Runtime:        prometheus.NewDesc(ns+"speedtest_runtime", "Speedtest Run Time", labels, nil), | ||||
| 		XputDownload:   prometheus.NewDesc(ns+"speedtest_download", "Speedtest Download Rate", labels, nil), | ||||
| 		XputUpload:     prometheus.NewDesc(ns+"speedtest_upload", "Speedtest Upload Rate", labels, nil), | ||||
|  | @ -90,10 +94,10 @@ func (u *promUnifi) exportUSG(r report, d *unifi.USG) { | |||
| 		{u.Device.Mem, prometheus.GaugeValue, d.SystemStats.Mem, labels}, | ||||
| 	}) | ||||
| 	u.exportWANPorts(r, labels, d.Wan1, d.Wan2) | ||||
| 	u.exportUSGstats(r, labels, d.Stat.Gw, d.SpeedtestStatus) | ||||
| 	u.exportUSGstats(r, labels, d.Stat.Gw, d.SpeedtestStatus, d.Uplink) | ||||
| } | ||||
| 
 | ||||
| func (u *promUnifi) exportUSGstats(r report, labels []string, gw *unifi.Gw, st unifi.SpeedtestStatus) { | ||||
| func (u *promUnifi) exportUSGstats(r report, labels []string, gw *unifi.Gw, st unifi.SpeedtestStatus, ul unifi.Uplink) { | ||||
| 	labelLan := []string{"lan", labels[6], labels[7]} | ||||
| 	labelWan := []string{"all", labels[6], labels[7]} | ||||
| 	r.send([]*metric{ | ||||
|  | @ -110,6 +114,8 @@ func (u *promUnifi) exportUSGstats(r report, labels []string, gw *unifi.Gw, st u | |||
| 		{u.USG.LanTxPackets, prometheus.CounterValue, gw.LanTxPackets, labelLan}, | ||||
| 		{u.USG.LanTxBytes, prometheus.CounterValue, gw.LanTxBytes, labelLan}, | ||||
| 		{u.USG.LanRxDropped, prometheus.CounterValue, gw.LanRxDropped, labelLan}, | ||||
| 		{u.USG.UplinkLatency, prometheus.GaugeValue, ul.Latency.Val / 1000, labelWan}, | ||||
| 		{u.USG.UplinkSpeed, prometheus.GaugeValue, ul.Speed, labelWan}, | ||||
| 		// Speed Test Stats
 | ||||
| 		{u.USG.Latency, prometheus.GaugeValue, st.Latency.Val / 1000, labelWan}, | ||||
| 		{u.USG.Runtime, prometheus.GaugeValue, st.Runtime, labelWan}, | ||||
|  |  | |||
|  | @ -49,7 +49,7 @@ func descUSW(ns string) *usw { | |||
| 	pns := ns + "port_" | ||||
| 	// labels := []string{"ip", "version", "model", "serial", "type", "mac", "site_name", "name"}
 | ||||
| 	labelS := []string{"site_name", "name"} // labels[6:]
 | ||||
| 	labelP := []string{"port_num", "port_name", "port_mac", "port_ip", "site_name", "name"} | ||||
| 	labelP := []string{"port_id", "port_num", "port_name", "port_mac", "port_ip", "site_name", "name"} | ||||
| 	return &usw{ | ||||
| 		SwRxPackets:   prometheus.NewDesc(ns+"switch_receive_packets_total", "Switch Packets Received Total", labelS, nil), | ||||
| 		SwRxBytes:     prometheus.NewDesc(ns+"switch_receive_bytes_total", "Switch Bytes Received Total", labelS, nil), | ||||
|  | @ -147,11 +147,11 @@ func (u *promUnifi) exportUSWstats(r report, labels []string, sw *unifi.Sw) { | |||
| func (u *promUnifi) exportPortTable(r report, labels []string, pt []unifi.Port) { | ||||
| 	// Per-port data on a switch
 | ||||
| 	for _, p := range pt { | ||||
| 		if !p.Up.Val { | ||||
| 		if !p.Up.Val || !p.Enable.Val { | ||||
| 			continue | ||||
| 		} | ||||
| 		// Copy labels, and add four new ones.
 | ||||
| 		labelP := []string{p.PortIdx.Txt, p.Name, p.Mac, p.IP, labels[6], labels[7]} | ||||
| 		labelP := []string{labels[7] + " Port " + p.PortIdx.Txt, p.PortIdx.Txt, p.Name, p.Mac, p.IP, labels[6], labels[7]} | ||||
| 		if p.PoeEnable.Val && p.PortPoe.Val { | ||||
| 			r.send([]*metric{ | ||||
| 				{u.USW.PoeCurrent, prometheus.GaugeValue, p.PoeCurrent, labelP}, | ||||
|  | @ -176,6 +176,7 @@ func (u *promUnifi) exportPortTable(r report, labels []string, pt []unifi.Port) | |||
| 			{u.USW.TxDropped, prometheus.CounterValue, p.TxDropped, labelP}, | ||||
| 			{u.USW.TxErrors, prometheus.CounterValue, p.TxErrors, labelP}, | ||||
| 			{u.USW.TxMulticast, prometheus.CounterValue, p.TxMulticast, labelP}, | ||||
| 			{u.USW.TxPackets, prometheus.CounterValue, p.TxPackets, labelP}, | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue