Merge branch 'master' of ../poller into merge-them-all

This commit is contained in:
Cody Lee 2022-11-23 20:29:08 -06:00
commit c475ed8d5b
No known key found for this signature in database
14 changed files with 739 additions and 0 deletions

9
core/poller/.travis.yml Normal file
View File

@ -0,0 +1,9 @@
language: go
go:
- 1.16.x
before_install:
# download super-linter: golangci-lint
- curl -sL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin latest
script:
- go test ./...
- golangci-lint run --enable-all -D exhaustivestruct,nlreturn,forbidigo

21
core/poller/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT LICENSE.
Copyright (c) 2018-2020 David Newhall II
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

26
core/poller/README.md Normal file
View File

@ -0,0 +1,26 @@
# poller
## UniFi Poller Core
This module ties the inputs together with the outputs.
Aggregates metrics on request. Provides CLI app and args parsing.
# Ideal
This library has no notion of "UniFi" or controllers, or Influx, or Prometheus.
This library simply provides an input interface and an output interface.
Each interface uses an `[]interface{}` type, so any type of data can be used.
That is to say, you could write input and output plugins that work with, say,
Cisco gear, or any other network (or even non-network) data. The existing plugins
should provide ample example of how to use this library, but at some point the
godoc will improve.
# Features
- Automatically unmarshal's plugin config structs from config file and/or env variables.
- Initializes all "imported" plugins on startup.
- Provides input plugins a Logger, requires an interface for Metrics and Events retrieval.
- Provides Output plugins an interface to retrieve Metrics and Events, and a Logger.
- Provides automatic aggregation of Metrics and Events from multiple sources.

9
core/poller/build_bsd.go Normal file
View File

@ -0,0 +1,9 @@
// +build darwin freebsd netbsd openbsd
package poller
// DefaultConfFile is where to find config if --config is not prvided.
const DefaultConfFile = "/etc/unifi-poller/up.conf,/usr/local/etc/unifi-poller/up.conf"
// DefaultObjPath is the path to look for shared object libraries (plugins).
const DefaultObjPath = "/usr/local/lib/unifi-poller"

View File

@ -0,0 +1,9 @@
// +build !windows,!darwin,!freebsd
package poller
// DefaultConfFile is where to find config if --config is not prvided.
const DefaultConfFile = "/config/unifi-poller.conf,/etc/unifi-poller/up.conf"
// DefaultObjPath is the path to look for shared object libraries (plugins).
const DefaultObjPath = "/usr/lib/unifi-poller"

View File

@ -0,0 +1,9 @@
// +build windows
package poller
// DefaultConfFile is where to find config if --config is not prvided.
const DefaultConfFile = `C:\ProgramData\unifi-poller\up.conf`
// DefaultObjPath is useless in this context. Bummer.
const DefaultObjPath = "PLUGINS_DO_NOT_WORK_ON_WINDOWS_SOWWWWWY"

56
core/poller/commands.go Normal file
View File

@ -0,0 +1,56 @@
package poller
import (
"fmt"
"os"
"strconv"
"strings"
"golang.org/x/crypto/bcrypt"
"golang.org/x/term"
)
// PrintRawMetrics prints raw json from the UniFi Controller. This is currently
// tied into the -j CLI arg, and is probably not very useful outside that context.
func (u *UnifiPoller) PrintRawMetrics() (err error) {
split := strings.SplitN(u.Flags.DumpJSON, " ", 2)
filter := &Filter{Kind: split[0]}
// Allows you to grab a controller other than 0 from config.
if split2 := strings.Split(filter.Kind, ":"); len(split2) > 1 {
filter.Kind = split2[0]
filter.Unit, _ = strconv.Atoi(split2[1])
}
// Used with "other"
if len(split) > 1 {
filter.Path = split[1]
}
// As of now we only have one input plugin, so target that [0].
m, err := inputs[0].RawMetrics(filter)
fmt.Println(string(m))
return err
}
// PrintPasswordHash prints a bcrypt'd password. Useful for the web server.
func (u *UnifiPoller) PrintPasswordHash() (err error) {
pwd := []byte(u.Flags.HashPW)
if u.Flags.HashPW == "-" {
fmt.Print("Enter Password: ")
pwd, err = term.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
return fmt.Errorf("reading stdin: %w", err)
}
fmt.Println() // print a newline.
}
hash, err := bcrypt.GenerateFromPassword(pwd, bcrypt.MinCost)
fmt.Println(string(hash))
return err //nolint:wrapcheck
}

163
core/poller/config.go Normal file
View File

@ -0,0 +1,163 @@
package poller
import (
"fmt"
"os"
"path"
"plugin"
"strings"
"time"
"github.com/spf13/pflag"
"golift.io/cnfg"
"golift.io/cnfg/cnfgfile"
)
const (
// AppName is the name of the application.
AppName = "unpoller"
// ENVConfigPrefix is the prefix appended to an env variable tag name.
ENVConfigPrefix = "UP"
)
// UnifiPoller contains the application startup data, and auth info for UniFi & Influx.
type UnifiPoller struct {
Flags *Flags
*Config
}
// Flags represents the CLI args available and their settings.
type Flags struct {
ConfigFile string
DumpJSON string
HashPW string
ShowVer bool
*pflag.FlagSet
}
// Metrics is a type shared by the exporting and reporting packages.
type Metrics struct {
TS time.Time
Sites []interface{}
Clients []interface{}
SitesDPI []interface{}
ClientsDPI []interface{}
Devices []interface{}
RogueAPs []interface{}
}
// Events defines the type for log entries.
type Events struct {
Logs []interface{}
}
// Config represents the core library input data.
type Config struct {
*Poller `json:"poller" toml:"poller" xml:"poller" yaml:"poller"`
}
// Poller is the global config values.
type Poller struct {
Plugins []string `json:"plugins" toml:"plugins" xml:"plugin" yaml:"plugins"`
Debug bool `json:"debug" toml:"debug" xml:"debug,attr" yaml:"debug"`
Quiet bool `json:"quiet" toml:"quiet" xml:"quiet,attr" yaml:"quiet"`
}
// LoadPlugins reads-in dynamic shared libraries.
// Not used very often, if at all.
func (u *UnifiPoller) LoadPlugins() error {
for _, p := range u.Plugins {
name := strings.TrimSuffix(p, ".so") + ".so"
if name == ".so" {
continue // Just ignore it. uhg.
}
if _, err := os.Stat(name); os.IsNotExist(err) {
name = path.Join(DefaultObjPath, name)
}
u.Logf("Loading Dynamic Plugin: %s", name)
if _, err := plugin.Open(name); err != nil {
return fmt.Errorf("opening plugin: %w", err)
}
}
return nil
}
// ParseConfigs parses the poller config and the config for each registered output plugin.
func (u *UnifiPoller) ParseConfigs() error {
// Parse core config.
if err := u.parseInterface(u.Config); err != nil {
return err
}
// Load dynamic plugins.
if err := u.LoadPlugins(); err != nil {
return err
}
if err := u.parseInputs(); err != nil {
return err
}
return u.parseOutputs()
}
// getFirstFile returns the first file that exists and is "reachable".
func getFirstFile(files []string) (string, error) {
var err error
for _, f := range files {
if _, err = os.Stat(f); err == nil {
return f, nil
}
}
return "", fmt.Errorf("finding file: %w", err)
}
// parseInterface parses the config file and environment variables into the provided interface.
func (u *UnifiPoller) parseInterface(i interface{}) error {
// Parse config file into provided interface.
if err := cnfgfile.Unmarshal(i, u.Flags.ConfigFile); err != nil {
return fmt.Errorf("cnfg unmarshal: %w", err)
}
// Parse environment variables into provided interface.
if _, err := cnfg.UnmarshalENV(i, ENVConfigPrefix); err != nil {
return fmt.Errorf("env unmarshal: %w", err)
}
return nil
}
// Parse input plugin configs.
func (u *UnifiPoller) parseInputs() error {
inputSync.Lock()
defer inputSync.Unlock()
for _, i := range inputs {
if err := u.parseInterface(i.Config); err != nil {
return err
}
}
return nil
}
// Parse output plugin configs.
func (u *UnifiPoller) parseOutputs() error {
outputSync.Lock()
defer outputSync.Unlock()
for _, o := range outputs {
if err := u.parseInterface(o.Config); err != nil {
return err
}
}
return nil
}

13
core/poller/go.mod Normal file
View File

@ -0,0 +1,13 @@
module github.com/unpoller/poller
go 1.16
require (
github.com/spf13/pflag v1.0.6-0.20201009195203-85dd5c8bc61c
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b
golift.io/cnfg v0.0.7
golift.io/version v0.0.2
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

33
core/poller/go.sum Normal file
View File

@ -0,0 +1,33 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/spf13/pflag v1.0.6-0.20201009195203-85dd5c8bc61c h1:zqmyTlQyufRC65JnImJ6H1Sf7BDj8bG31EV919NVEQc=
github.com/spf13/pflag v1.0.6-0.20201009195203-85dd5c8bc61c/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golift.io/cnfg v0.0.7 h1:qkNpP5Bq+5Gtoc6HcI8kapMD5zFOVan6qguxqBQF3OY=
golift.io/cnfg v0.0.7/go.mod h1:AsB0DJe7nv0bizKaoy3e3MjjOF7upTpMOMvsfv4CNNk=
golift.io/version v0.0.2 h1:i0gXRuSDHKs4O0sVDUg4+vNIuOxYoXhaxspftu2FRTE=
golift.io/version v0.0.2/go.mod h1:76aHNz8/Pm7CbuxIsDi97jABL5Zui3f2uZxDm4vB6hU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

156
core/poller/inputs.go Normal file
View File

@ -0,0 +1,156 @@
package poller
import (
"strings"
"sync"
"time"
)
var (
// These are used ot keep track of loaded input plugins.
inputs []*InputPlugin // nolint: gochecknoglobals
inputSync sync.RWMutex // nolint: gochecknoglobals
)
// Input plugins must implement this interface.
type Input interface {
Initialize(Logger) error // Called once on startup to initialize the plugin.
Metrics(*Filter) (*Metrics, error) // Called every time new metrics are requested.
Events(*Filter) (*Events, error) // This is new.
RawMetrics(*Filter) ([]byte, error)
}
// InputPlugin describes an input plugin's consumable interface.
type InputPlugin struct {
Name string
Config interface{} // Each config is passed into an unmarshaller later.
Input
}
// Filter is used for metrics filters. Many fields for lots of expansion.
type Filter struct {
Type string
Term string
Name string
Role string
Kind string
Path string
Text string
Unit int
Pass bool
Skip bool
Time time.Time
Dur time.Duration
}
// NewInput creates a metric input. This should be called by input plugins
// init() functions.
func NewInput(i *InputPlugin) {
inputSync.Lock()
defer inputSync.Unlock()
if i == nil || i.Input == nil {
panic("nil output or method passed to poller.NewOutput")
}
inputs = append(inputs, i)
}
// InitializeInputs runs the passed-in initializer method for each input plugin.
func (u *UnifiPoller) InitializeInputs() error {
inputSync.RLock()
defer inputSync.RUnlock()
for _, input := range inputs {
// This must return, or the app locks up here.
if err := input.Initialize(u); err != nil {
return err
}
}
return nil
}
// Events aggregates log messages (events) from one or more sources.
func (u *UnifiPoller) Events(filter *Filter) (*Events, error) {
inputSync.RLock()
defer inputSync.RUnlock()
events := Events{}
for _, input := range inputs {
if filter != nil &&
filter.Name != "" &&
!strings.EqualFold(input.Name, filter.Name) {
continue
}
e, err := input.Events(filter)
if err != nil {
return &events, err
}
// Logs is the only member to extend at this time.
events.Logs = append(events.Logs, e.Logs...)
}
return &events, nil
}
// Metrics aggregates all the measurements from filtered inputs and returns them.
// Passing a null filter returns everything!
func (u *UnifiPoller) Metrics(filter *Filter) (*Metrics, error) {
inputSync.RLock()
defer inputSync.RUnlock()
metrics := &Metrics{}
for _, input := range inputs {
if filter != nil &&
filter.Name != "" &&
!strings.EqualFold(input.Name, filter.Name) {
continue
}
m, err := input.Metrics(filter)
if err != nil {
return metrics, err
}
metrics = AppendMetrics(metrics, m)
}
return metrics, nil
}
// AppendMetrics combines the metrics from two sources.
func AppendMetrics(existing *Metrics, m *Metrics) *Metrics {
if existing == nil {
return m
}
if m == nil {
return existing
}
existing.SitesDPI = append(existing.SitesDPI, m.SitesDPI...)
existing.Sites = append(existing.Sites, m.Sites...)
existing.ClientsDPI = append(existing.ClientsDPI, m.ClientsDPI...)
existing.RogueAPs = append(existing.RogueAPs, m.RogueAPs...)
existing.Clients = append(existing.Clients, m.Clients...)
existing.Devices = append(existing.Devices, m.Devices...)
return existing
}
// Inputs allows output plugins to see the list of loaded input plugins.
func (u *UnifiPoller) Inputs() (names []string) {
inputSync.RLock()
defer inputSync.RUnlock()
for i := range inputs {
names = append(names, inputs[i].Name)
}
return names
}

35
core/poller/logger.go Normal file
View File

@ -0,0 +1,35 @@
package poller
import (
"fmt"
"log"
)
// Log the command that called these commands.
const callDepth = 2
// Logger is passed into input packages so they may write logs.
type Logger interface {
Logf(m string, v ...interface{})
LogErrorf(m string, v ...interface{})
LogDebugf(m string, v ...interface{})
}
// Logf prints a log entry if quiet is false.
func (u *UnifiPoller) Logf(m string, v ...interface{}) {
if !u.Quiet {
_ = log.Output(callDepth, fmt.Sprintf("[INFO] "+m, v...))
}
}
// LogDebugf prints a debug log entry if debug is true and quite is false.
func (u *UnifiPoller) LogDebugf(m string, v ...interface{}) {
if u.Debug && !u.Quiet {
_ = log.Output(callDepth, fmt.Sprintf("[DEBUG] "+m, v...))
}
}
// LogErrorf prints an error log entry.
func (u *UnifiPoller) LogErrorf(m string, v ...interface{}) {
_ = log.Output(callDepth, fmt.Sprintf("[ERROR] "+m, v...))
}

102
core/poller/outputs.go Normal file
View File

@ -0,0 +1,102 @@
package poller
import (
"fmt"
"sync"
)
var (
// These are used to keep track of loaded output plugins.
outputs []*Output // nolint: gochecknoglobals
outputSync sync.RWMutex // nolint: gochecknoglobals
errNoOutputPlugins = fmt.Errorf("no output plugins imported")
errAllOutputStopped = fmt.Errorf("all output plugins have stopped, or none enabled")
)
// Collect is passed into output packages so they may collect metrics to output.
type Collect interface {
Logger
Metrics(*Filter) (*Metrics, error)
Events(*Filter) (*Events, error)
// These get used by the webserver output plugin.
Poller() Poller
Inputs() []string
Outputs() []string
}
// Output defines the output data for a metric exporter like influx or prometheus.
// Output packages should call NewOutput with this struct in init().
type Output struct {
Name string
Config interface{} // Each config is passed into an unmarshaller later.
Method func(Collect) error // Called on startup for each configured output.
}
// NewOutput should be called by each output package's init function.
func NewOutput(o *Output) {
outputSync.Lock()
defer outputSync.Unlock()
if o == nil || o.Method == nil {
panic("nil output or method passed to poller.NewOutput")
}
outputs = append(outputs, o)
}
// Poller returns the poller config.
func (u *UnifiPoller) Poller() Poller {
return *u.Config.Poller
}
// InitializeOutputs runs all the configured output plugins.
// If none exist, or they all exit an error is returned.
func (u *UnifiPoller) InitializeOutputs() error {
count, errChan := u.runOutputMethods()
defer close(errChan)
if count == 0 {
return errNoOutputPlugins
}
// Wait for and return an error from any output plugin.
for err := range errChan {
if err != nil {
return err
}
if count--; count == 0 {
return errAllOutputStopped
}
}
return nil
}
func (u *UnifiPoller) runOutputMethods() (int, chan error) {
// Output plugin errors go into this channel.
err := make(chan error)
outputSync.RLock()
defer outputSync.RUnlock()
for _, o := range outputs {
go func(o *Output) {
err <- o.Method(u) // Run each output plugin
}(o)
}
return len(outputs), err
}
// Outputs allows other output plugins to see the list of loaded output plugins.
func (u *UnifiPoller) Outputs() (names []string) {
outputSync.RLock()
defer outputSync.RUnlock()
for i := range outputs {
names = append(names, outputs[i].Name)
}
return names
}

98
core/poller/start.go Normal file
View File

@ -0,0 +1,98 @@
// Package poller provides the CLI interface to setup unifi-poller.
package poller
import (
"fmt"
"log"
"os"
"strings"
"github.com/spf13/pflag"
"golift.io/version"
)
// New returns a new poller struct.
func New() *UnifiPoller {
return &UnifiPoller{Config: &Config{Poller: &Poller{}}, Flags: &Flags{}}
}
// Start begins the application from a CLI.
// Parses cli flags, parses config file, parses env vars, sets up logging, then:
// - dumps a json payload OR - executes Run().
func (u *UnifiPoller) Start() error {
log.SetOutput(os.Stdout)
log.SetFlags(log.LstdFlags)
u.Flags.Parse(os.Args[1:])
if u.Flags.ShowVer {
fmt.Println(version.Print(AppName))
return nil // don't run anything else w/ version request.
}
if u.Flags.HashPW != "" {
return u.PrintPasswordHash()
}
cfile, err := getFirstFile(strings.Split(u.Flags.ConfigFile, ","))
if err != nil {
return err
}
u.Flags.ConfigFile = cfile
if u.Flags.DumpJSON == "" { // do not print this when dumping JSON.
u.Logf("Loading Configuration File: %s", u.Flags.ConfigFile)
}
// Parse config file and ENV variables.
if err := u.ParseConfigs(); err != nil {
return err
}
return u.Run()
}
// Parse turns CLI arguments into data structures. Called by Start() on startup.
func (f *Flags) Parse(args []string) {
f.FlagSet = pflag.NewFlagSet(AppName, pflag.ExitOnError)
f.Usage = func() {
fmt.Printf("Usage: %s [--config=/path/to/up.conf] [--version]", AppName)
f.PrintDefaults()
}
f.StringVarP(&f.HashPW, "encrypt", "e", "",
"This option bcrypts a provided string. Useful for the webserver password. Use - to be prompted.")
f.StringVarP(&f.DumpJSON, "dumpjson", "j", "",
"This debug option prints a json payload and exits. See man page for more info.")
f.StringVarP(&f.ConfigFile, "config", "c", DefaultConfFile,
"Poller config file path. Separating multiple paths with a comma will load the first config file found.")
f.BoolVarP(&f.ShowVer, "version", "v", false, "Print the version and exit.")
_ = f.FlagSet.Parse(args) // pflag.ExitOnError means this will never return error.
}
// Run picks a mode and executes the associated functions. This will do one of three things:
// 1. Start the collector routine that polls unifi and reports to influx on an interval. (default)
// 2. Run the collector one time and report the metrics to influxdb. (lambda)
// 3. Start a web server and wait for Prometheus to poll the application for metrics.
func (u *UnifiPoller) Run() error {
if u.Flags.DumpJSON != "" {
u.Config.Quiet = true
if err := u.InitializeInputs(); err != nil {
return err
}
return u.PrintRawMetrics()
}
if u.Debug {
log.SetFlags(log.Lshortfile | log.Lmicroseconds | log.Ldate)
u.LogDebugf("Debug Logging Enabled")
}
log.Printf("[INFO] UniFi Poller v%v Starting Up! PID: %d", version.Version, os.Getpid())
if err := u.InitializeInputs(); err != nil {
return err
}
return u.InitializeOutputs()
}