Remove struct inheritance.

This commit is contained in:
David Newhall II 2019-08-25 19:01:29 -07:00
parent 13b417c52f
commit 2a355c0677
5 changed files with 79 additions and 71 deletions

View File

@ -24,15 +24,20 @@ const (
// 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 {
Influx influx.Client
Unifi *unifi.Unifi
Flag *Flag
Config *Config
errorCount int
LastCheck time.Time
}
// Flag represents the CLI args available and their settings.
type Flag struct {
ConfigFile string ConfigFile string
DumpJSON string DumpJSON string
ShowVer bool ShowVer bool
Flag *pflag.FlagSet *pflag.FlagSet
errorCount int
LastCheck time.Time
influx.Client
*unifi.Unifi
*Config
} }
// Metrics contains all the data from the controller and an influx endpoint to send it to. // Metrics contains all the data from the controller and an influx endpoint to send it to.

View File

@ -10,17 +10,17 @@ import (
// DumpJSONPayload prints raw json from the UniFi Controller. // DumpJSONPayload prints raw json from the UniFi Controller.
func (u *UnifiPoller) DumpJSONPayload() (err error) { func (u *UnifiPoller) DumpJSONPayload() (err error) {
u.Quiet = true u.Config.Quiet = true
u.Unifi, err = unifi.NewUnifi(&unifi.Config{ u.Unifi, err = unifi.NewUnifi(&unifi.Config{
User: u.UnifiUser, User: u.Config.UnifiUser,
Pass: u.UnifiPass, Pass: u.Config.UnifiPass,
URL: u.UnifiBase, URL: u.Config.UnifiBase,
VerifySSL: u.VerifySSL, VerifySSL: u.Config.VerifySSL,
}) })
if err != nil { if err != nil {
return err return err
} }
fmt.Fprintln(os.Stderr, "[INFO] Authenticated to UniFi Controller @", u.UnifiBase, "as user", u.UnifiUser) fmt.Fprintln(os.Stderr, "[INFO] Authenticated to UniFi Controller @", u.Config.UnifiBase, "as user", u.Config.UnifiUser)
if err := u.CheckSites(); err != nil { if err := u.CheckSites(); err != nil {
return err return err
} }
@ -30,12 +30,12 @@ func (u *UnifiPoller) DumpJSONPayload() (err error) {
switch sites, err := u.GetFilteredSites(); { switch sites, err := u.GetFilteredSites(); {
case err != nil: case err != nil:
return err return err
case StringInSlice(u.DumpJSON, []string{"d", "device", "devices"}): case StringInSlice(u.Flag.DumpJSON, []string{"d", "device", "devices"}):
return u.dumpSitesJSON(unifi.DevicePath, "Devices", sites) return u.dumpSitesJSON(unifi.DevicePath, "Devices", sites)
case StringInSlice(u.DumpJSON, []string{"client", "clients", "c"}): case StringInSlice(u.Flag.DumpJSON, []string{"client", "clients", "c"}):
return u.dumpSitesJSON(unifi.ClientPath, "Clients", sites) return u.dumpSitesJSON(unifi.ClientPath, "Clients", sites)
case strings.HasPrefix(u.DumpJSON, "other "): case strings.HasPrefix(u.Flag.DumpJSON, "other "):
apiPath := strings.SplitN(u.DumpJSON, " ", 2)[1] apiPath := strings.SplitN(u.Flag.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(apiPath) return u.PrintRawAPIJSON(apiPath)
default: default:
@ -56,7 +56,7 @@ func (u *UnifiPoller) dumpSitesJSON(path, name string, sites unifi.Sites) error
// PrintRawAPIJSON prints the raw json for a user-provided path on a UniFi Controller. // PrintRawAPIJSON prints the raw json for a user-provided path on a UniFi Controller.
func (u *UnifiPoller) PrintRawAPIJSON(apiPath string) error { func (u *UnifiPoller) PrintRawAPIJSON(apiPath string) error {
body, err := u.GetJSON(apiPath) body, err := u.Unifi.GetJSON(apiPath)
fmt.Println(string(body)) fmt.Println(string(body))
return err return err
} }

View File

@ -11,7 +11,7 @@ import (
func (u *UnifiPoller) LogError(err error, prefix string) { func (u *UnifiPoller) LogError(err error, prefix string) {
if err != nil { if err != nil {
u.errorCount++ u.errorCount++
_ = log.Output(2, fmt.Sprintf("[ERROR] (%v/%v) %v: %v", u.errorCount, u.MaxErrors, prefix, err)) _ = log.Output(2, fmt.Sprintf("[ERROR] (%v/%v) %v: %v", u.errorCount, u.Config.MaxErrors, prefix, err))
} }
} }
@ -27,14 +27,14 @@ func StringInSlice(str string, slice []string) bool {
// Logf prints a log entry if quiet is false. // Logf prints a log entry if quiet is false.
func (u *UnifiPoller) Logf(m string, v ...interface{}) { func (u *UnifiPoller) Logf(m string, v ...interface{}) {
if !u.Quiet { if !u.Config.Quiet {
_ = log.Output(2, fmt.Sprintf("[INFO] "+m, v...)) _ = log.Output(2, fmt.Sprintf("[INFO] "+m, v...))
} }
} }
// LogDebugf prints a debug log entry if debug is true and quite is false // LogDebugf prints a debug log entry if debug is true and quite is false
func (u *UnifiPoller) LogDebugf(m string, v ...interface{}) { func (u *UnifiPoller) LogDebugf(m string, v ...interface{}) {
if u.Debug && !u.Quiet { if u.Config.Debug && !u.Config.Quiet {
_ = log.Output(2, fmt.Sprintf("[DEBUG] "+m, v...)) _ = log.Output(2, fmt.Sprintf("[DEBUG] "+m, v...))
} }
} }

View File

@ -21,8 +21,9 @@ import (
// Parses flags, parses config and executes Run(). // Parses flags, parses config and executes Run().
func Start() error { func Start() error {
log.SetFlags(log.LstdFlags) log.SetFlags(log.LstdFlags)
up := &UnifiPoller{} up := &UnifiPoller{Flag: &Flag{}}
if up.ParseFlags(os.Args[1:]); up.ShowVer { up.Flag.Parse(os.Args[1:])
if up.Flag.ShowVer {
fmt.Printf("unifi-poller v%s\n", Version) fmt.Printf("unifi-poller v%s\n", Version)
return nil // don't run anything else w/ version request. return nil // don't run anything else w/ version request.
} }
@ -33,18 +34,18 @@ func Start() error {
return up.Run() return up.Run()
} }
// ParseFlags runs the parser. // Parse turns CLI arguments into data structures. Called by Start() on startup.
func (u *UnifiPoller) ParseFlags(args []string) { func (f *Flag) Parse(args []string) {
u.Flag = pflag.NewFlagSet("unifi-poller", pflag.ExitOnError) f.FlagSet = pflag.NewFlagSet("unifi-poller", pflag.ExitOnError)
u.Flag.Usage = func() { f.Usage = func() {
fmt.Println("Usage: unifi-poller [--config=filepath] [--version]") fmt.Println("Usage: unifi-poller [--config=filepath] [--version]")
u.Flag.PrintDefaults() f.PrintDefaults()
} }
u.Flag.StringVarP(&u.DumpJSON, "dumpjson", "j", "", f.StringVarP(&f.DumpJSON, "dumpjson", "j", "",
"This debug option prints a json payload and exits. See man page for more.") "This debug option prints a json payload and exits. See man page for more.")
u.Flag.StringVarP(&u.ConfigFile, "config", "c", DefaultConfFile, "Poller Config File (TOML Format)") f.StringVarP(&f.ConfigFile, "config", "c", DefaultConfFile, "Poller Config File (TOML Format)")
u.Flag.BoolVarP(&u.ShowVer, "version", "v", false, "Print the version and exit") f.BoolVarP(&f.ShowVer, "version", "v", false, "Print the version and exit")
_ = u.Flag.Parse(args) _ = f.FlagSet.Parse(args)
} }
// GetConfig parses and returns our configuration data. // GetConfig parses and returns our configuration data.
@ -60,17 +61,17 @@ func (u *UnifiPoller) GetConfig() error {
UnifiBase: defaultUnifURL, UnifiBase: defaultUnifURL,
Interval: Duration{defaultInterval}, Interval: Duration{defaultInterval},
Sites: []string{"default"}, Sites: []string{"default"},
Quiet: u.DumpJSON != "", Quiet: u.Flag.DumpJSON != "",
} }
u.Logf("Loading Configuration File: %s", u.ConfigFile) u.Logf("Loading Configuration File: %s", u.Flag.ConfigFile)
switch buf, err := ioutil.ReadFile(u.ConfigFile); { switch buf, err := ioutil.ReadFile(u.Flag.ConfigFile); {
case err != nil: case err != nil:
return err return err
case strings.Contains(u.ConfigFile, ".json"): case strings.Contains(u.Flag.ConfigFile, ".json"):
return json.Unmarshal(buf, u.Config) return json.Unmarshal(buf, u.Config)
case strings.Contains(u.ConfigFile, ".xml"): case strings.Contains(u.Flag.ConfigFile, ".xml"):
return xml.Unmarshal(buf, u.Config) return xml.Unmarshal(buf, u.Config)
case strings.Contains(u.ConfigFile, ".yaml"): case strings.Contains(u.Flag.ConfigFile, ".yaml"):
return yaml.Unmarshal(buf, u.Config) return yaml.Unmarshal(buf, u.Config)
default: default:
return toml.Unmarshal(buf, u.Config) return toml.Unmarshal(buf, u.Config)
@ -79,10 +80,10 @@ func (u *UnifiPoller) GetConfig() error {
// Run invokes all the application logic and routines. // Run invokes all the application logic and routines.
func (u *UnifiPoller) Run() (err error) { func (u *UnifiPoller) Run() (err error) {
if u.DumpJSON != "" { if u.Flag.DumpJSON != "" {
return u.DumpJSONPayload() return u.DumpJSONPayload()
} }
if u.Debug { if u.Config.Debug {
log.SetFlags(log.Lshortfile | log.Lmicroseconds | log.Ldate) log.SetFlags(log.Lshortfile | log.Lmicroseconds | log.Ldate)
u.LogDebugf("Debug Logging Enabled") u.LogDebugf("Debug Logging Enabled")
} }
@ -90,12 +91,13 @@ func (u *UnifiPoller) Run() (err error) {
if err = u.GetUnifi(); err != nil { if err = u.GetUnifi(); err != nil {
return err return err
} }
u.Logf("Polling UniFi Controller at %s v%s as user %s. Sites: %v", u.UnifiBase, u.ServerVersion, u.UnifiUser, u.Sites) u.Logf("Polling UniFi Controller at %s v%s as user %s. Sites: %v",
u.Config.UnifiBase, u.Unifi.ServerVersion, u.Config.UnifiUser, u.Config.Sites)
if err = u.GetInfluxDB(); err != nil { if err = u.GetInfluxDB(); err != nil {
return err return err
} }
u.Logf("Logging Measurements to InfluxDB at %s as user %s", u.InfluxURL, u.InfluxUser) u.Logf("Logging Measurements to InfluxDB at %s as user %s", u.Config.InfluxURL, u.Config.InfluxUser)
switch strings.ToLower(u.Mode) { switch strings.ToLower(u.Config.Mode) {
case "influxlambda", "lambdainflux", "lambda_influx", "influx_lambda": case "influxlambda", "lambdainflux", "lambda_influx", "influx_lambda":
u.LogDebugf("Lambda Mode Enabled") u.LogDebugf("Lambda Mode Enabled")
u.LastCheck = time.Now() u.LastCheck = time.Now()
@ -107,10 +109,10 @@ func (u *UnifiPoller) Run() (err error) {
// GetInfluxDB returns an InfluxDB interface. // GetInfluxDB returns an InfluxDB interface.
func (u *UnifiPoller) GetInfluxDB() (err error) { func (u *UnifiPoller) GetInfluxDB() (err error) {
u.Client, err = influx.NewHTTPClient(influx.HTTPConfig{ u.Influx, err = influx.NewHTTPClient(influx.HTTPConfig{
Addr: u.InfluxURL, Addr: u.Config.InfluxURL,
Username: u.InfluxUser, Username: u.Config.InfluxUser,
Password: u.InfluxPass, Password: u.Config.InfluxPass,
}) })
if err != nil { if err != nil {
return fmt.Errorf("influxdb: %v", err) return fmt.Errorf("influxdb: %v", err)
@ -122,10 +124,10 @@ func (u *UnifiPoller) GetInfluxDB() (err error) {
func (u *UnifiPoller) GetUnifi() (err error) { func (u *UnifiPoller) GetUnifi() (err error) {
// Create an authenticated session to the Unifi Controller. // Create an authenticated session to the Unifi Controller.
u.Unifi, err = unifi.NewUnifi(&unifi.Config{ u.Unifi, err = unifi.NewUnifi(&unifi.Config{
User: u.UnifiUser, User: u.Config.UnifiUser,
Pass: u.UnifiPass, Pass: u.Config.UnifiPass,
URL: u.UnifiBase, URL: u.Config.UnifiBase,
VerifySSL: u.VerifySSL, VerifySSL: u.Config.VerifySSL,
ErrorLog: u.LogErrorf, // Log all errors. ErrorLog: u.LogErrorf, // Log all errors.
DebugLog: u.LogDebugf, // Log debug messages. DebugLog: u.LogDebugf, // Log debug messages.
}) })

View File

@ -13,10 +13,10 @@ import (
// CheckSites makes sure the list of provided sites exists on the controller. // CheckSites makes sure the list of provided sites exists on the controller.
// This does not run in Lambda (run-once) mode. // This does not run in Lambda (run-once) mode.
func (u *UnifiPoller) CheckSites() error { func (u *UnifiPoller) CheckSites() error {
if strings.Contains(strings.ToLower(u.Mode), "lambda") { if strings.Contains(strings.ToLower(u.Config.Mode), "lambda") {
return nil // Skip this in lambda mode. return nil // Skip this in lambda mode.
} }
sites, err := u.GetSites() sites, err := u.Unifi.GetSites()
if err != nil { if err != nil {
return err return err
} }
@ -25,12 +25,12 @@ func (u *UnifiPoller) CheckSites() error {
msg = append(msg, site.Name+" ("+site.Desc+")") msg = append(msg, site.Name+" ("+site.Desc+")")
} }
u.Logf("Found %d site(s) on controller: %v", len(msg), strings.Join(msg, ", ")) u.Logf("Found %d site(s) on controller: %v", len(msg), strings.Join(msg, ", "))
if StringInSlice("all", u.Sites) { if StringInSlice("all", u.Config.Sites) {
u.Sites = []string{"all"} u.Config.Sites = []string{"all"}
return nil return nil
} }
FIRST: FIRST:
for _, s := range u.Sites { for _, s := range u.Config.Sites {
for _, site := range sites { for _, site := range sites {
if s == site.Name { if s == site.Name {
continue FIRST continue FIRST
@ -45,14 +45,15 @@ FIRST:
// PollController runs forever, polling UniFi, and pushing to influx. // PollController runs forever, polling UniFi, and pushing to influx.
// This is started by Run() after everything checks out. // This is started by Run() after everything checks out.
func (u *UnifiPoller) PollController() error { func (u *UnifiPoller) PollController() error {
log.Println("[INFO] Everything checks out! Poller started, interval:", u.Interval.Round(time.Second)) interval := u.Config.Interval.Round(time.Second)
ticker := time.NewTicker(u.Interval.Round(time.Second)) log.Println("[INFO] Everything checks out! Poller started, interval:", interval)
ticker := time.NewTicker(interval)
for u.LastCheck = range ticker.C { for u.LastCheck = range ticker.C {
var err error var err error
if u.ReAuth { if u.Config.ReAuth {
u.LogDebugf("Re-authenticating to UniFi Controller") u.LogDebugf("Re-authenticating to UniFi Controller")
// Some users need to re-auth every interval because the cookie times out. // Some users need to re-auth every interval because the cookie times out.
if err = u.Login(); err != nil { if err = u.Unifi.Login(); err != nil {
u.LogError(err, "re-authenticating") u.LogError(err, "re-authenticating")
} }
} }
@ -60,8 +61,8 @@ func (u *UnifiPoller) PollController() error {
// Only run this if the authentication procedure didn't return error. // Only run this if the authentication procedure didn't return error.
_ = u.CollectAndReport() _ = u.CollectAndReport()
} }
if u.MaxErrors >= 0 && u.errorCount > u.MaxErrors { if u.Config.MaxErrors >= 0 && u.errorCount > u.Config.MaxErrors {
return fmt.Errorf("reached maximum error count, stopping poller (%d > %d)", u.errorCount, u.MaxErrors) return fmt.Errorf("reached maximum error count, stopping poller (%d > %d)", u.errorCount, u.Config.MaxErrors)
} }
} }
return nil return nil
@ -93,18 +94,18 @@ func (u *UnifiPoller) CollectMetrics() (*Metrics, error) {
// Get the sites we care about. // Get the sites we care about.
m.Sites, err = u.GetFilteredSites() m.Sites, err = u.GetFilteredSites()
u.LogError(err, "unifi.GetSites()") u.LogError(err, "unifi.GetSites()")
if u.CollectIDS { if u.Config.CollectIDS {
// Check back in time since twice the interval. Dups are discarded by InfluxDB. // Check back in time since twice the interval. Dups are discarded by InfluxDB.
m.IDSList, err = u.GetIDS(m.Sites, time.Now().Add(2*u.Interval.Duration), time.Now()) m.IDSList, err = u.Unifi.GetIDS(m.Sites, time.Now().Add(2*u.Config.Interval.Duration), time.Now())
u.LogError(err, "unifi.GetIDS()") u.LogError(err, "unifi.GetIDS()")
} }
// Get all the points. // Get all the points.
m.Clients, err = u.GetClients(m.Sites) m.Clients, err = u.Unifi.GetClients(m.Sites)
u.LogError(err, "unifi.GetClients()") u.LogError(err, "unifi.GetClients()")
m.Devices, err = u.GetDevices(m.Sites) m.Devices, err = u.Unifi.GetDevices(m.Sites)
u.LogError(err, "unifi.GetDevices()") u.LogError(err, "unifi.GetDevices()")
// Make a new Influx Points Batcher. // Make a new Influx Points Batcher.
m.BatchPoints, err = influx.NewBatchPoints(influx.BatchPointsConfig{Database: u.InfluxDB}) m.BatchPoints, err = influx.NewBatchPoints(influx.BatchPointsConfig{Database: u.Config.InfluxDB})
u.LogError(err, "influx.NewBatchPoints") u.LogError(err, "influx.NewBatchPoints")
return m, err return m, err
} }
@ -139,7 +140,7 @@ func (u *UnifiPoller) ReportMetrics(metrics *Metrics) error {
for _, err := range metrics.ProcessPoints() { for _, err := range metrics.ProcessPoints() {
u.LogError(err, "asset.Points()") u.LogError(err, "asset.Points()")
} }
err := u.Write(metrics.BatchPoints) err := u.Influx.Write(metrics.BatchPoints)
if err != nil { if err != nil {
return fmt.Errorf("influxdb.Write(points): %v", err) return fmt.Errorf("influxdb.Write(points): %v", err)
} }
@ -150,7 +151,7 @@ func (u *UnifiPoller) ReportMetrics(metrics *Metrics) error {
fields += len(i) fields += len(i)
} }
idsMsg := "" idsMsg := ""
if u.CollectIDS { if u.Config.CollectIDS {
idsMsg = fmt.Sprintf("IDS Events: %d, ", len(metrics.IDSList)) idsMsg = fmt.Sprintf("IDS Events: %d, ", len(metrics.IDSList))
} }
u.Logf("UniFi Measurements Recorded. Sites: %d, Clients: %d, "+ u.Logf("UniFi Measurements Recorded. Sites: %d, Clients: %d, "+
@ -213,16 +214,16 @@ func (m *Metrics) ProcessPoints() []error {
// Omits requested but unconfigured sites. Grabs the full list from the // Omits requested but unconfigured sites. Grabs the full list from the
// controller and returns the sites provided in the config file. // controller and returns the sites provided in the config file.
func (u *UnifiPoller) GetFilteredSites() (unifi.Sites, error) { func (u *UnifiPoller) GetFilteredSites() (unifi.Sites, error) {
sites, err := u.GetSites() sites, err := u.Unifi.GetSites()
if err != nil { if err != nil {
return nil, err return nil, err
} else if len(u.Sites) < 1 || StringInSlice("all", u.Sites) { } else if len(u.Config.Sites) < 1 || StringInSlice("all", u.Config.Sites) {
return sites, nil return sites, nil
} }
var i int var i int
for _, s := range sites { for _, s := range sites {
// Only include valid sites in the request filter. // Only include valid sites in the request filter.
if StringInSlice(s.Name, u.Sites) { if StringInSlice(s.Name, u.Config.Sites) {
sites[i] = s sites[i] = s
i++ i++
} }