make dumper work
This commit is contained in:
parent
aec4b12d3c
commit
d3d420597e
|
|
@ -107,11 +107,11 @@
|
|||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:d54a8d89f95a4d2a5a24ce63cb1835ccdff337fde7776c87ceacb6fdbe4349ae"
|
||||
digest = "1:7a90fad47972b5ae06013d4685eb2f3007e7c92609a1399d2adf59fe04cd9b63"
|
||||
name = "golift.io/config"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "fd8ffb02173aad2183e5555a03b1d1f909aca930"
|
||||
revision = "fe642c8392dc00d72ddcc47f05a06096bd5d054b"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:2883cea734f2766f41ff9c9d4aefccccc53e3d44f5c8b08893b9c218cf666722"
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
<verify_ssl>false</verify_ssl>
|
||||
</influxdb>
|
||||
|
||||
<unifi>
|
||||
<unifi disable="false">
|
||||
<!-- Repeat this stanza to poll additional controllers. -->
|
||||
<controller name="">
|
||||
<site>all</site>
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ influxdb:
|
|||
verify_ssl: false
|
||||
|
||||
unifi:
|
||||
disable: false
|
||||
controllers:
|
||||
- name: ""
|
||||
user: "influx"
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ const (
|
|||
// Config defines the data needed to store metrics in InfluxDB
|
||||
type Config struct {
|
||||
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,attr" yaml:"disable"`
|
||||
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"`
|
||||
User string `json:"user,omitempty" toml:"user,omitempty" xml:"user" yaml:"user"`
|
||||
|
|
|
|||
|
|
@ -8,14 +8,14 @@ import (
|
|||
"golift.io/unifi"
|
||||
)
|
||||
|
||||
func (u *InputUnifi) isNill(c Controller) bool {
|
||||
func (u *InputUnifi) isNill(c *Controller) bool {
|
||||
u.Config.RLock()
|
||||
defer u.Config.RUnlock()
|
||||
|
||||
return c.Unifi == nil
|
||||
}
|
||||
|
||||
func (u *InputUnifi) collectController(c Controller) (*poller.Metrics, error) {
|
||||
func (u *InputUnifi) collectController(c *Controller) (*poller.Metrics, error) {
|
||||
if u.isNill(c) {
|
||||
u.Logf("Re-authenticating to UniFi Controller: %s", c.URL)
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ func (u *InputUnifi) collectController(c Controller) (*poller.Metrics, error) {
|
|||
return u.pollController(c)
|
||||
}
|
||||
|
||||
func (u *InputUnifi) pollController(c Controller) (*poller.Metrics, error) {
|
||||
func (u *InputUnifi) pollController(c *Controller) (*poller.Metrics, error) {
|
||||
var err error
|
||||
|
||||
u.Config.RLock()
|
||||
|
|
@ -67,7 +67,7 @@ func (u *InputUnifi) pollController(c Controller) (*poller.Metrics, error) {
|
|||
// augmentMetrics is our middleware layer between collecting metrics and writing them.
|
||||
// This is where we can manipuate the returned data or make arbitrary decisions.
|
||||
// This function currently adds parent device names to client metrics.
|
||||
func (u *InputUnifi) augmentMetrics(c Controller, metrics *poller.Metrics) *poller.Metrics {
|
||||
func (u *InputUnifi) augmentMetrics(c *Controller, metrics *poller.Metrics) *poller.Metrics {
|
||||
if metrics == nil || metrics.Devices == nil || metrics.Clients == nil {
|
||||
return metrics
|
||||
}
|
||||
|
|
@ -113,22 +113,22 @@ func (u *InputUnifi) augmentMetrics(c Controller, metrics *poller.Metrics) *poll
|
|||
// getFilteredSites returns a list of sites to fetch data for.
|
||||
// Omits requested but unconfigured sites. Grabs the full list from the
|
||||
// controller and returns the sites provided in the config file.
|
||||
func (u *InputUnifi) getFilteredSites(c Controller) (unifi.Sites, error) {
|
||||
func (u *InputUnifi) getFilteredSites(c *Controller) (unifi.Sites, error) {
|
||||
u.Config.RLock()
|
||||
defer u.Config.RUnlock()
|
||||
|
||||
sites, err := c.Unifi.GetSites()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if len(c.Sites) < 1 || poller.StringInSlice("all", c.Sites) {
|
||||
} else if len(c.Sites) < 1 || StringInSlice("all", c.Sites) {
|
||||
return sites, nil
|
||||
}
|
||||
|
||||
var i int
|
||||
i := 0
|
||||
|
||||
for _, s := range sites {
|
||||
// Only include valid sites in the request filter.
|
||||
if poller.StringInSlice(s.Name, c.Sites) {
|
||||
if StringInSlice(s.Name, c.Sites) {
|
||||
sites[i] = s
|
||||
i++
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ package inputunifi
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"sync"
|
||||
|
||||
|
|
@ -13,7 +15,7 @@ import (
|
|||
|
||||
// InputUnifi contains the running data.
|
||||
type InputUnifi struct {
|
||||
Config Config `json:"unifi" toml:"unifi" xml:"unifi" yaml:"unifi"`
|
||||
Config *Config `json:"unifi" toml:"unifi" xml:"unifi" yaml:"unifi"`
|
||||
poller.Logger
|
||||
}
|
||||
|
||||
|
|
@ -33,9 +35,9 @@ type Controller struct {
|
|||
|
||||
// Config contains our configuration data
|
||||
type Config struct {
|
||||
sync.RWMutex // locks the Unifi struct member when re-authing to unifi.
|
||||
Disable bool `json:"disable" toml:"disable" xml:"disable" yaml:"disable"`
|
||||
Controllers []Controller `json:"controllers" toml:"controller" xml:"controller" yaml:"controllers"`
|
||||
sync.RWMutex // locks the Unifi struct member when re-authing to unifi.
|
||||
Disable bool `json:"disable" toml:"disable" xml:"disable" yaml:"disable"`
|
||||
Controllers []*Controller `json:"controllers" toml:"controller" xml:"controller" yaml:"controllers"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
@ -48,7 +50,7 @@ func init() {
|
|||
}
|
||||
|
||||
// getUnifi (re-)authenticates to a unifi controller.
|
||||
func (u *InputUnifi) getUnifi(c Controller) error {
|
||||
func (u *InputUnifi) getUnifi(c *Controller) error {
|
||||
var err error
|
||||
|
||||
u.Config.Lock()
|
||||
|
|
@ -76,3 +78,74 @@ func (u *InputUnifi) getUnifi(c Controller) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkSites makes sure the list of provided sites exists on the controller.
|
||||
// This only runs once during initialization.
|
||||
func (u *InputUnifi) checkSites(c *Controller) error {
|
||||
u.Config.RLock()
|
||||
defer u.Config.RUnlock()
|
||||
|
||||
if len(c.Sites) < 1 || c.Sites[0] == "" {
|
||||
c.Sites = []string{"all"}
|
||||
}
|
||||
|
||||
u.LogDebugf("Checking Controller Sites List")
|
||||
|
||||
sites, err := c.Unifi.GetSites()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := []string{}
|
||||
for _, site := range sites {
|
||||
msg = append(msg, site.Name+" ("+site.Desc+")")
|
||||
}
|
||||
|
||||
u.Logf("Found %d site(s) on controller %s: %v", len(msg), c.Name, strings.Join(msg, ", "))
|
||||
|
||||
if StringInSlice("all", c.Sites) {
|
||||
c.Sites = []string{"all"}
|
||||
return nil
|
||||
}
|
||||
|
||||
FIRST:
|
||||
for _, s := range c.Sites {
|
||||
for _, site := range sites {
|
||||
if s == site.Name {
|
||||
continue FIRST
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("configured site not found on controller: %v", s)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *InputUnifi) dumpSitesJSON(c *Controller, path, name string, sites unifi.Sites) ([]byte, error) {
|
||||
allJSON := []byte{}
|
||||
|
||||
for _, s := range sites {
|
||||
apiPath := fmt.Sprintf(path, s.Name)
|
||||
_, _ = fmt.Fprintf(os.Stderr, "[INFO] Dumping %s: '%s' JSON for site: %s (%s):\n", name, apiPath, s.Desc, s.Name)
|
||||
|
||||
body, err := c.Unifi.GetJSON(apiPath)
|
||||
if err != nil {
|
||||
return allJSON, err
|
||||
}
|
||||
|
||||
allJSON = append(allJSON, body...)
|
||||
}
|
||||
|
||||
return allJSON, nil
|
||||
}
|
||||
|
||||
// StringInSlice returns true if a string is in a slice.
|
||||
func StringInSlice(str string, slice []string) bool {
|
||||
for _, s := range slice {
|
||||
if strings.EqualFold(s, str) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,51 @@
|
|||
package inputunifi
|
||||
|
||||
/* This file contains the three poller.Input interface methods. */
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/davidnewhall/unifi-poller/pkg/poller"
|
||||
"golift.io/unifi"
|
||||
)
|
||||
|
||||
// Initialize gets called one time when starting up.
|
||||
// Satisfies poller.Input interface.
|
||||
func (u *InputUnifi) Initialize(l poller.Logger) error {
|
||||
if u.Config.Disable {
|
||||
l.Logf("unifi input disabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(u.Config.Controllers) < 1 {
|
||||
return fmt.Errorf("no unifi controllers defined for unifi input")
|
||||
}
|
||||
|
||||
u.Logger = l
|
||||
|
||||
for i, c := range u.Config.Controllers {
|
||||
if c.Name == "" {
|
||||
u.Config.Controllers[i].Name = c.URL
|
||||
}
|
||||
|
||||
switch err := u.getUnifi(c); err {
|
||||
case nil:
|
||||
if err := u.checkSites(c); err != nil {
|
||||
u.LogErrorf("checking sites on %s: %v", c.Name, err)
|
||||
}
|
||||
|
||||
u.Logf("Polling UniFi Controller at %s v%s as user %s. Sites: %v",
|
||||
c.URL, c.Unifi.ServerVersion, c.User, c.Sites)
|
||||
default:
|
||||
u.LogErrorf("Controller Auth or Connection failed, but continuing to retry! %s: %v", c.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Metrics grabs all the measurements from a UniFi controller and returns them.
|
||||
func (u *InputUnifi) Metrics() (*poller.Metrics, error) {
|
||||
if u.Config.Disable {
|
||||
|
|
@ -52,75 +90,35 @@ func (u *InputUnifi) Metrics() (*poller.Metrics, error) {
|
|||
return metrics, nil
|
||||
}
|
||||
|
||||
// Initialize gets called one time when starting up.
|
||||
// Satisfies poller.Input interface.
|
||||
func (u *InputUnifi) Initialize(l poller.Logger) error {
|
||||
if u.Config.Disable {
|
||||
l.Logf("unifi input disabled")
|
||||
return nil
|
||||
}
|
||||
// RawMetrics returns API output from the first configured unifi controller.
|
||||
func (u *InputUnifi) RawMetrics(filter poller.Filter) ([]byte, error) {
|
||||
c := u.Config.Controllers[0] // We could pull the controller number from the filter.
|
||||
if u.isNill(c) {
|
||||
u.Logf("Re-authenticating to UniFi Controller: %s", c.URL)
|
||||
|
||||
if len(u.Config.Controllers) < 1 {
|
||||
return fmt.Errorf("no unifi controllers defined for unifi input")
|
||||
}
|
||||
|
||||
u.Logger = l
|
||||
|
||||
for i, c := range u.Config.Controllers {
|
||||
if c.Name == "" {
|
||||
u.Config.Controllers[i].Name = c.URL
|
||||
}
|
||||
|
||||
switch err := u.getUnifi(c); err {
|
||||
case nil:
|
||||
if err := u.checkSites(c); err != nil {
|
||||
u.LogErrorf("checking sites on %s: %v", c.Name, err)
|
||||
}
|
||||
|
||||
u.Logf("Polling UniFi Controller at %s v%s as user %s. Sites: %v",
|
||||
c.URL, c.Unifi.ServerVersion, c.User, c.Sites)
|
||||
default:
|
||||
u.LogErrorf("Controller Auth or Connection failed, but continuing to retry! %s: %v", c.Name, err)
|
||||
if err := u.getUnifi(c); err != nil {
|
||||
return nil, fmt.Errorf("re-authenticating to %s: %v", c.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
if err := u.checkSites(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// checkSites makes sure the list of provided sites exists on the controller.
|
||||
// This only runs once during initialization.
|
||||
func (u *InputUnifi) checkSites(c Controller) error {
|
||||
u.Config.RLock()
|
||||
defer u.Config.RUnlock()
|
||||
u.LogDebugf("Checking Controller Sites List")
|
||||
|
||||
sites, err := c.Unifi.GetSites()
|
||||
sites, err := u.getFilteredSites(c)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg := []string{}
|
||||
|
||||
for _, site := range sites {
|
||||
msg = append(msg, site.Name+" ("+site.Desc+")")
|
||||
switch filter.Type {
|
||||
case "d", "device", "devices":
|
||||
return u.dumpSitesJSON(c, unifi.APIDevicePath, "Devices", sites)
|
||||
case "client", "clients", "c":
|
||||
return u.dumpSitesJSON(c, unifi.APIClientPath, "Clients", sites)
|
||||
case "other", "o":
|
||||
_, _ = fmt.Fprintf(os.Stderr, "[INFO] Dumping Path '%s':\n", filter.Term)
|
||||
return c.Unifi.GetJSON(filter.Term)
|
||||
default:
|
||||
return []byte{}, fmt.Errorf("must provide filter: devices, clients, other")
|
||||
}
|
||||
|
||||
u.Logf("Found %d site(s) on controller: %v", len(msg), strings.Join(msg, ", "))
|
||||
|
||||
if poller.StringInSlice("all", c.Sites) {
|
||||
c.Sites = []string{"all"}
|
||||
return nil
|
||||
}
|
||||
|
||||
FIRST:
|
||||
for _, s := range c.Sites {
|
||||
for _, site := range sites {
|
||||
if s == site.Name {
|
||||
continue FIRST
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("configured site not found on controller: %v", s)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,85 +1,28 @@
|
|||
package poller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DumpJSONPayload prints raw json from the UniFi Controller.
|
||||
// This only works with controller 0 (first one) in the config.
|
||||
func (u *UnifiPoller) DumpJSONPayload() (err error) {
|
||||
if true {
|
||||
return nil
|
||||
u.Config.Quiet = true
|
||||
|
||||
split := strings.SplitN(u.Flags.DumpJSON, " ", 2)
|
||||
filter := Filter{Type: split[0]}
|
||||
|
||||
if len(split) > 1 {
|
||||
filter.Term = split[1]
|
||||
}
|
||||
/*
|
||||
u.Config.Quiet = true
|
||||
config := u.Config.Controllers[0]
|
||||
|
||||
config.Unifi, err = unifi.NewUnifi(&unifi.Config{
|
||||
User: config.User,
|
||||
Pass: config.Pass,
|
||||
URL: config.URL,
|
||||
VerifySSL: config.VerifySSL,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := inputs[0].RawMetrics(filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "[INFO] Authenticated to UniFi Controller @ %v as user %v", config.URL, config.User)
|
||||
fmt.Println(string(m))
|
||||
|
||||
if err := u.CheckSites(config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config.Unifi.ErrorLog = func(m string, v ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, "[ERROR] "+m, v...)
|
||||
} // Log all errors to stderr.
|
||||
|
||||
switch sites, err := u.GetFilteredSites(config); {
|
||||
case err != nil:
|
||||
return err
|
||||
case StringInSlice(u.Flags.DumpJSON, []string{"d", "device", "devices"}):
|
||||
return u.dumpSitesJSON(config, unifi.APIDevicePath, "Devices", sites)
|
||||
case StringInSlice(u.Flags.DumpJSON, []string{"client", "clients", "c"}):
|
||||
return u.dumpSitesJSON(config, unifi.APIClientPath, "Clients", sites)
|
||||
case strings.HasPrefix(u.Flags.DumpJSON, "other "):
|
||||
apiPath := strings.SplitN(u.Flags.DumpJSON, " ", 2)[1]
|
||||
_, _ = fmt.Fprintf(os.Stderr, "[INFO] Dumping Path '%s':\n", apiPath)
|
||||
return u.PrintRawAPIJSON(config, apiPath)
|
||||
default:
|
||||
return fmt.Errorf("must provide filter: devices, clients, other")
|
||||
}
|
||||
*/
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
func (u *UnifiPoller) dumpSitesJSON(c Controller, path, name string, sites unifi.Sites) error {
|
||||
for _, s := range sites {
|
||||
apiPath := fmt.Sprintf(path, s.Name)
|
||||
_, _ = fmt.Fprintf(os.Stderr, "[INFO] Dumping %s: '%s' JSON for site: %s (%s):\n",
|
||||
name, apiPath, s.Desc, s.Name)
|
||||
if err := u.PrintRawAPIJSON(c, apiPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrintRawAPIJSON prints the raw json for a user-provided path on a UniFi Controller.
|
||||
func (u *UnifiPoller) PrintRawAPIJSON(c Controller, apiPath string) error {
|
||||
body, err := c.Unifi.GetJSON(apiPath)
|
||||
fmt.Println(string(body))
|
||||
return err
|
||||
}
|
||||
*/
|
||||
|
||||
// StringInSlice returns true if a string is in a slice.
|
||||
func StringInSlice(str string, slice []string) bool {
|
||||
for _, s := range slice {
|
||||
if strings.EqualFold(s, str) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ var (
|
|||
type Input interface {
|
||||
Initialize(Logger) error // Called once on startup to initialize the plugin.
|
||||
Metrics() (*Metrics, error) // Called every time new metrics are requested.
|
||||
RawMetrics(Filter) ([]byte, error)
|
||||
}
|
||||
|
||||
// InputPlugin describes an input plugin's consumable interface.
|
||||
|
|
@ -25,6 +26,12 @@ type InputPlugin struct {
|
|||
Input
|
||||
}
|
||||
|
||||
// Filter is used for raw metrics filters.
|
||||
type Filter struct {
|
||||
Type string
|
||||
Term string
|
||||
}
|
||||
|
||||
// NewInput creates a metric input. This should be called by input plugins
|
||||
// init() functions.
|
||||
func NewInput(i *InputPlugin) {
|
||||
|
|
|
|||
|
|
@ -16,14 +16,14 @@ type Logger interface {
|
|||
|
||||
// Logf prints a log entry if quiet is false.
|
||||
func (u *UnifiPoller) Logf(m string, v ...interface{}) {
|
||||
if !u.Config.Quiet {
|
||||
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.Config.Debug && !u.Config.Quiet {
|
||||
if u.Debug && !u.Quiet {
|
||||
_ = log.Output(callDepth, fmt.Sprintf("[DEBUG] "+m, v...))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,10 +61,14 @@ func (f *Flags) Parse(args []string) {
|
|||
// 3. Start a web server and wait for Prometheus to poll the application for metrics.
|
||||
func (u *UnifiPoller) Run() error {
|
||||
if u.Flags.DumpJSON != "" {
|
||||
if err := u.InitializeInputs(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return u.DumpJSONPayload()
|
||||
}
|
||||
|
||||
if u.Config.Debug {
|
||||
if u.Debug {
|
||||
log.SetFlags(log.Lshortfile | log.Lmicroseconds | log.Ldate)
|
||||
u.LogDebugf("Debug Logging Enabled")
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue