trimming
This commit is contained in:
parent
426d4acc9b
commit
cfa3c439cf
|
|
@ -10,11 +10,12 @@ import (
|
||||||
|
|
||||||
"github.com/davidnewhall/unifi-poller/pkg/poller"
|
"github.com/davidnewhall/unifi-poller/pkg/poller"
|
||||||
influx "github.com/influxdata/influxdb1-client/v2"
|
influx "github.com/influxdata/influxdb1-client/v2"
|
||||||
conf "golift.io/config"
|
"golift.io/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultInterval = 30 * time.Second
|
defaultInterval = 30 * time.Second
|
||||||
|
minimumInterval = 10 * time.Second
|
||||||
defaultInfluxDB = "unifi"
|
defaultInfluxDB = "unifi"
|
||||||
defaultInfluxUser = "unifi"
|
defaultInfluxUser = "unifi"
|
||||||
defaultInfluxURL = "http://127.0.0.1:8086"
|
defaultInfluxURL = "http://127.0.0.1:8086"
|
||||||
|
|
@ -22,7 +23,7 @@ const (
|
||||||
|
|
||||||
// Config defines the data needed to store metrics in InfluxDB
|
// Config defines the data needed to store metrics in InfluxDB
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Interval conf.Duration `json:"interval,omitempty" toml:"interval,omitempty" xml:"interval" yaml:"interval"`
|
Interval config.Duration `json:"interval,omitempty" toml:"interval,omitempty" xml:"interval" yaml:"interval"`
|
||||||
Disable bool `json:"disable" toml:"disable" xml:"disable" yaml:"disable"`
|
Disable bool `json:"disable" toml:"disable" xml:"disable" yaml:"disable"`
|
||||||
VerifySSL bool `json:"verify_ssl" toml:"verify_ssl" xml:"verify_ssl" yaml:"verify_ssl"`
|
VerifySSL bool `json:"verify_ssl" toml:"verify_ssl" xml:"verify_ssl" yaml:"verify_ssl"`
|
||||||
URL string `json:"url,omitempty" toml:"url,omitempty" xml:"url" yaml:"url"`
|
URL string `json:"url,omitempty" toml:"url,omitempty" xml:"url" yaml:"url"`
|
||||||
|
|
@ -63,9 +64,9 @@ func init() {
|
||||||
// This is started by Run() or RunBoth() after everything checks out.
|
// This is started by Run() or RunBoth() after everything checks out.
|
||||||
func (u *InfluxUnifi) PollController() {
|
func (u *InfluxUnifi) PollController() {
|
||||||
interval := u.Config.Interval.Round(time.Second)
|
interval := u.Config.Interval.Round(time.Second)
|
||||||
|
ticker := time.NewTicker(interval)
|
||||||
log.Printf("[INFO] Everything checks out! Poller started, InfluxDB interval: %v", interval)
|
log.Printf("[INFO] Everything checks out! Poller started, InfluxDB interval: %v", interval)
|
||||||
|
|
||||||
ticker := time.NewTicker(interval)
|
|
||||||
for u.LastCheck = range ticker.C {
|
for u.LastCheck = range ticker.C {
|
||||||
metrics, err := u.Collector.Metrics()
|
metrics, err := u.Collector.Metrics()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -128,12 +129,12 @@ func (u *InfluxUnifi) setConfigDefaults() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.Config.Interval.Duration == 0 {
|
if u.Config.Interval.Duration == 0 {
|
||||||
u.Config.Interval = conf.Duration{Duration: defaultInterval}
|
u.Config.Interval = config.Duration{Duration: defaultInterval}
|
||||||
} else if u.Config.Interval.Duration < defaultInterval/2 {
|
} else if u.Config.Interval.Duration < minimumInterval {
|
||||||
u.Config.Interval = conf.Duration{Duration: defaultInterval / 2}
|
u.Config.Interval = config.Duration{Duration: minimumInterval}
|
||||||
}
|
}
|
||||||
|
|
||||||
u.Config.Interval = conf.Duration{Duration: u.Config.Interval.Duration.Round(time.Second)}
|
u.Config.Interval = config.Duration{Duration: u.Config.Interval.Duration.Round(time.Second)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReportMetrics batches all device and client data into influxdb data points.
|
// ReportMetrics batches all device and client data into influxdb data points.
|
||||||
|
|
@ -179,6 +180,7 @@ func (u *InfluxUnifi) collect(r report, ch chan *metric) {
|
||||||
// to the collect routine through the metric channel.
|
// to the collect routine through the metric channel.
|
||||||
func (u *InfluxUnifi) loopPoints(r report) {
|
func (u *InfluxUnifi) loopPoints(r report) {
|
||||||
m := r.metrics()
|
m := r.metrics()
|
||||||
|
|
||||||
r.add()
|
r.add()
|
||||||
go func() {
|
go func() {
|
||||||
defer r.done()
|
defer r.done()
|
||||||
|
|
@ -186,6 +188,7 @@ func (u *InfluxUnifi) loopPoints(r report) {
|
||||||
u.batchSite(r, s)
|
u.batchSite(r, s)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
r.add()
|
r.add()
|
||||||
go func() {
|
go func() {
|
||||||
defer r.done()
|
defer r.done()
|
||||||
|
|
@ -193,6 +196,7 @@ func (u *InfluxUnifi) loopPoints(r report) {
|
||||||
u.batchClient(r, s)
|
u.batchClient(r, s)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
r.add()
|
r.add()
|
||||||
go func() {
|
go func() {
|
||||||
defer r.done()
|
defer r.done()
|
||||||
|
|
@ -200,6 +204,7 @@ func (u *InfluxUnifi) loopPoints(r report) {
|
||||||
u.batchIDS(r, s)
|
u.batchIDS(r, s)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if m.Devices == nil {
|
if m.Devices == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -211,6 +216,7 @@ func (u *InfluxUnifi) loopPoints(r report) {
|
||||||
u.batchUAP(r, s)
|
u.batchUAP(r, s)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
r.add()
|
r.add()
|
||||||
go func() {
|
go func() {
|
||||||
defer r.done()
|
defer r.done()
|
||||||
|
|
@ -218,6 +224,7 @@ func (u *InfluxUnifi) loopPoints(r report) {
|
||||||
u.batchUSG(r, s)
|
u.batchUSG(r, s)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
r.add()
|
r.add()
|
||||||
go func() {
|
go func() {
|
||||||
defer r.done()
|
defer r.done()
|
||||||
|
|
@ -225,6 +232,7 @@ func (u *InfluxUnifi) loopPoints(r report) {
|
||||||
u.batchUSW(r, s)
|
u.batchUSW(r, s)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
r.add()
|
r.add()
|
||||||
go func() {
|
go func() {
|
||||||
defer r.done()
|
defer r.done()
|
||||||
|
|
|
||||||
|
|
@ -31,13 +31,13 @@ const ENVConfigPrefix = "UP"
|
||||||
|
|
||||||
// UnifiPoller contains the application startup data, and auth info for UniFi & Influx.
|
// UnifiPoller contains the application startup data, and auth info for UniFi & Influx.
|
||||||
type UnifiPoller struct {
|
type UnifiPoller struct {
|
||||||
Flag *Flag
|
Flags *Flags
|
||||||
Config *Config
|
Config *Config
|
||||||
sync.Mutex // locks the Unifi struct member when re-authing to unifi.
|
sync.Mutex // locks the Unifi struct member when re-authing to unifi.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flag represents the CLI args available and their settings.
|
// Flags represents the CLI args available and their settings.
|
||||||
type Flag struct {
|
type Flags struct {
|
||||||
ConfigFile string
|
ConfigFile string
|
||||||
DumpJSON string
|
DumpJSON string
|
||||||
ShowVer bool
|
ShowVer bool
|
||||||
|
|
@ -84,8 +84,8 @@ type Poller struct {
|
||||||
// ParseConfigs parses the poller config and the config for each registered output plugin.
|
// ParseConfigs parses the poller config and the config for each registered output plugin.
|
||||||
func (u *UnifiPoller) ParseConfigs() error {
|
func (u *UnifiPoller) ParseConfigs() error {
|
||||||
// Parse config file.
|
// Parse config file.
|
||||||
if err := config.ParseFile(u.Config, u.Flag.ConfigFile); err != nil {
|
if err := config.ParseFile(u.Config, u.Flags.ConfigFile); err != nil {
|
||||||
u.Flag.Usage()
|
u.Flags.Usage()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,7 +99,7 @@ func (u *UnifiPoller) ParseConfigs() error {
|
||||||
|
|
||||||
for _, o := range outputs {
|
for _, o := range outputs {
|
||||||
// Parse config file for each output plugin.
|
// Parse config file for each output plugin.
|
||||||
if err := config.ParseFile(o.Config, u.Flag.ConfigFile); err != nil {
|
if err := config.ParseFile(o.Config, u.Flags.ConfigFile); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,12 +37,12 @@ func (u *UnifiPoller) DumpJSONPayload() (err error) {
|
||||||
switch sites, err := u.GetFilteredSites(config); {
|
switch sites, err := u.GetFilteredSites(config); {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return err
|
return err
|
||||||
case StringInSlice(u.Flag.DumpJSON, []string{"d", "device", "devices"}):
|
case StringInSlice(u.Flags.DumpJSON, []string{"d", "device", "devices"}):
|
||||||
return u.dumpSitesJSON(config, unifi.APIDevicePath, "Devices", sites)
|
return u.dumpSitesJSON(config, unifi.APIDevicePath, "Devices", sites)
|
||||||
case StringInSlice(u.Flag.DumpJSON, []string{"client", "clients", "c"}):
|
case StringInSlice(u.Flags.DumpJSON, []string{"client", "clients", "c"}):
|
||||||
return u.dumpSitesJSON(config, unifi.APIClientPath, "Clients", sites)
|
return u.dumpSitesJSON(config, unifi.APIClientPath, "Clients", sites)
|
||||||
case strings.HasPrefix(u.Flag.DumpJSON, "other "):
|
case strings.HasPrefix(u.Flags.DumpJSON, "other "):
|
||||||
apiPath := strings.SplitN(u.Flag.DumpJSON, " ", 2)[1]
|
apiPath := strings.SplitN(u.Flags.DumpJSON, " ", 2)[1]
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "[INFO] Dumping Path '%s':\n", apiPath)
|
_, _ = fmt.Fprintf(os.Stderr, "[INFO] Dumping Path '%s':\n", apiPath)
|
||||||
return u.PrintRawAPIJSON(config, apiPath)
|
return u.PrintRawAPIJSON(config, apiPath)
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ func (u *UnifiPoller) InitializeOutputs() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if count < 1 {
|
if count < 1 {
|
||||||
return fmt.Errorf("no output plugins configured")
|
return fmt.Errorf("no output plugins imported")
|
||||||
}
|
}
|
||||||
|
|
||||||
for err := range v {
|
for err := range v {
|
||||||
|
|
@ -64,7 +64,7 @@ func (u *UnifiPoller) InitializeOutputs() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if count--; count < 1 {
|
if count--; count < 1 {
|
||||||
return fmt.Errorf("all output plugins have stopped")
|
return fmt.Errorf("all output plugins have stopped, or none enabled")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,9 @@ import (
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
// New returns a new poller struct preloaded with default values.
|
// New returns a new poller struct.
|
||||||
// No need to call this if you call Start.c
|
|
||||||
func New() *UnifiPoller {
|
func New() *UnifiPoller {
|
||||||
return &UnifiPoller{
|
return &UnifiPoller{Config: &Config{}, Flags: &Flags{}}
|
||||||
Config: &Config{},
|
|
||||||
Flag: &Flag{
|
|
||||||
ConfigFile: DefaultConfFile,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start begins the application from a CLI.
|
// Start begins the application from a CLI.
|
||||||
|
|
@ -27,15 +21,15 @@ func New() *UnifiPoller {
|
||||||
func (u *UnifiPoller) Start() error {
|
func (u *UnifiPoller) Start() error {
|
||||||
log.SetOutput(os.Stdout)
|
log.SetOutput(os.Stdout)
|
||||||
log.SetFlags(log.LstdFlags)
|
log.SetFlags(log.LstdFlags)
|
||||||
u.Flag.Parse(os.Args[1:])
|
u.Flags.Parse(os.Args[1:])
|
||||||
|
|
||||||
if u.Flag.ShowVer {
|
if u.Flags.ShowVer {
|
||||||
fmt.Printf("%s v%s\n", AppName, version.Version)
|
fmt.Printf("%s v%s\n", AppName, version.Version)
|
||||||
return nil // don't run anything else w/ version request.
|
return nil // don't run anything else w/ version request.
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.Flag.DumpJSON == "" { // do not print this when dumping JSON.
|
if u.Flags.DumpJSON == "" { // do not print this when dumping JSON.
|
||||||
u.Logf("Loading Configuration File: %s", u.Flag.ConfigFile)
|
u.Logf("Loading Configuration File: %s", u.Flags.ConfigFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse config file and ENV variables.
|
// Parse config file and ENV variables.
|
||||||
|
|
@ -53,7 +47,7 @@ func (u *UnifiPoller) Start() error {
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.Flag.DumpJSON != "" {
|
if u.Flags.DumpJSON != "" {
|
||||||
return u.DumpJSONPayload()
|
return u.DumpJSONPayload()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,7 +60,7 @@ func (u *UnifiPoller) Start() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse turns CLI arguments into data structures. Called by Start() on startup.
|
// Parse turns CLI arguments into data structures. Called by Start() on startup.
|
||||||
func (f *Flag) Parse(args []string) {
|
func (f *Flags) Parse(args []string) {
|
||||||
f.FlagSet = pflag.NewFlagSet(AppName, pflag.ExitOnError)
|
f.FlagSet = pflag.NewFlagSet(AppName, pflag.ExitOnError)
|
||||||
f.Usage = func() {
|
f.Usage = func() {
|
||||||
fmt.Printf("Usage: %s [--config=/path/to/up.conf] [--version]", AppName)
|
fmt.Printf("Usage: %s [--config=/path/to/up.conf] [--version]", AppName)
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ type metric struct {
|
||||||
|
|
||||||
// Report accumulates counters that are printed to a log line.
|
// Report accumulates counters that are printed to a log line.
|
||||||
type Report struct {
|
type Report struct {
|
||||||
|
Config
|
||||||
Total int // Total count of metrics recorded.
|
Total int // Total count of metrics recorded.
|
||||||
Errors int // Total count of errors recording metrics.
|
Errors int // Total count of errors recording metrics.
|
||||||
Zeros int // Total count of metrics equal to zero.
|
Zeros int // Total count of metrics equal to zero.
|
||||||
|
|
@ -74,7 +75,6 @@ type Report struct {
|
||||||
Start time.Time // Time collection began.
|
Start time.Time // Time collection began.
|
||||||
ch chan []*metric
|
ch chan []*metric
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
Config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
@ -97,27 +97,28 @@ func (u *promUnifi) Run(c poller.Collect) error {
|
||||||
u.Config.Namespace = strings.Replace(poller.AppName, "-", "", -1)
|
u.Config.Namespace = strings.Replace(poller.AppName, "-", "", -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u.Config.Namespace = strings.Replace(u.Config.Namespace, "-", "_", -1)
|
||||||
|
|
||||||
if u.Config.HTTPListen == "" {
|
if u.Config.HTTPListen == "" {
|
||||||
u.Config.HTTPListen = defaultHTTPListen
|
u.Config.HTTPListen = defaultHTTPListen
|
||||||
}
|
}
|
||||||
|
|
||||||
name := strings.Replace(u.Config.Namespace, "-", "_", -1)
|
prometheus.MustRegister(version.NewCollector(u.Config.Namespace))
|
||||||
|
|
||||||
ns := name
|
if u.Config.Namespace = strings.Trim(u.Config.Namespace, "_") + "_"; u.Config.Namespace == "_" {
|
||||||
if ns = strings.Trim(ns, "_") + "_"; ns == "_" {
|
u.Config.Namespace = ""
|
||||||
ns = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prometheus.MustRegister(version.NewCollector(name))
|
|
||||||
prometheus.MustRegister(&promUnifi{
|
prometheus.MustRegister(&promUnifi{
|
||||||
Collector: c,
|
Collector: c,
|
||||||
Client: descClient(ns + "client_"),
|
Client: descClient(u.Config.Namespace + "client_"),
|
||||||
Device: descDevice(ns + "device_"), // stats for all device types.
|
Device: descDevice(u.Config.Namespace + "device_"), // stats for all device types.
|
||||||
UAP: descUAP(ns + "device_"),
|
UAP: descUAP(u.Config.Namespace + "device_"),
|
||||||
USG: descUSG(ns + "device_"),
|
USG: descUSG(u.Config.Namespace + "device_"),
|
||||||
USW: descUSW(ns + "device_"),
|
USW: descUSW(u.Config.Namespace + "device_"),
|
||||||
Site: descSite(ns + "site_"),
|
Site: descSite(u.Config.Namespace + "site_"),
|
||||||
})
|
})
|
||||||
|
|
||||||
c.Logf("Exporting Measurements for Prometheus at https://%s/metrics, namespace: %s", u.Config.HTTPListen, u.Config.Namespace)
|
c.Logf("Exporting Measurements for Prometheus at https://%s/metrics, namespace: %s", u.Config.HTTPListen, u.Config.Namespace)
|
||||||
|
|
||||||
return http.ListenAndServe(u.Config.HTTPListen, nil)
|
return http.ListenAndServe(u.Config.HTTPListen, nil)
|
||||||
|
|
@ -185,12 +186,14 @@ func (u *promUnifi) exportMetrics(r report, ch chan<- prometheus.Metric, ourChan
|
||||||
r.error(ch, m.Desc, fmt.Sprintf("not a number: %v", m.Value))
|
r.error(ch, m.Desc, fmt.Sprintf("not a number: %v", m.Value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r.done()
|
r.done()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *promUnifi) loopExports(r report) {
|
func (u *promUnifi) loopExports(r report) {
|
||||||
m := r.metrics()
|
m := r.metrics()
|
||||||
|
|
||||||
r.add()
|
r.add()
|
||||||
go func() {
|
go func() {
|
||||||
defer r.done()
|
defer r.done()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue