diff --git a/Gopkg.lock b/Gopkg.lock index 50d3fbde..f43a439c 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,12 +2,12 @@ [[projects]] - digest = "1:fda9365965d38b80007d47efbf516adbc65d266515f263e9c43336ab47ef0f69" + digest = "1:f42822e830b569d8527ad6e57585e6ccc094296bc9d648ccd256f95249453ce1" name = "github.com/golift/unifi" packages = ["."] pruneopts = "UT" - revision = "1d74eaae61aad0558126b6ade64753ebcc5982ef" - version = "v2.0.4" + revision = "fc5a69d27d7527038ae55ee5112bc755db380879" + version = "v2.1.0" [[projects]] branch = "master" diff --git a/pkg/unifi-poller/config.go b/pkg/unifi-poller/config.go index 98918935..53769af5 100644 --- a/pkg/unifi-poller/config.go +++ b/pkg/unifi-poller/config.go @@ -1,6 +1,7 @@ package unifipoller import ( + "strings" "time" "github.com/golift/unifi" @@ -39,6 +40,14 @@ type UnifiPoller struct { *Config } +// Metrics contains all the data from the controller. +type Metrics struct { + unifi.Sites + unifi.Clients + *unifi.Devices + influx.BatchPoints +} + // Config represents the data needed to poll a controller and report to influxdb. type Config struct { Interval Dur `json:"interval,_omitempty" toml:"interval,_omitempty" xml:"interval" yaml:"interval"` @@ -60,7 +69,7 @@ type Dur struct{ value time.Duration } // UnmarshalTOML parses a duration type from a config file. func (v *Dur) UnmarshalTOML(data []byte) error { - unquoted := string(data[1 : len(data)-1]) + unquoted := strings.Trim(string(data), `"`) dur, err := time.ParseDuration(unquoted) if err == nil { v.value = dur diff --git a/pkg/unifi-poller/jsondebug.go b/pkg/unifi-poller/jsondebug.go index 66cf6a5f..a02f2730 100644 --- a/pkg/unifi-poller/jsondebug.go +++ b/pkg/unifi-poller/jsondebug.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "os" + "strings" "github.com/golift/unifi" "github.com/pkg/errors" @@ -24,13 +25,15 @@ func (u *UnifiPoller) DumpJSONPayload() (err error) { fmt.Fprintf(os.Stderr, "[ERROR] "+m, v...) } // Log all errors to stderr. - switch sites, err := u.filterSites(u.Sites); { + switch sites, err := u.GetFilteredSites(); { case err != nil: return err case StringInSlice(u.DumpJSON, []string{"d", "device", "devices"}): return u.DumpDeviceJSON(sites) case StringInSlice(u.DumpJSON, []string{"client", "clients", "c"}): return u.DumpClientsJSON(sites) + case strings.HasPrefix(u.DumpJSON, "other "): + return u.DumpOtherJSON(sites) default: return errors.New("must provide filter: devices, clients") } @@ -58,6 +61,20 @@ func (u *UnifiPoller) DumpDeviceJSON(sites []unifi.Site) error { return nil } +// DumpOtherJSON prints the raw json for a user-provided path in a Unifi Controller. +func (u *UnifiPoller) DumpOtherJSON(sites []unifi.Site) error { + for _, s := range sites { + path := strings.SplitN(u.DumpJSON, " ", 2)[1] + if strings.Contains(path, "%s") { + path = fmt.Sprintf(path, s.Name) + } + if err := u.dumpJSON(path, "Other", s); err != nil { + return err + } + } + return nil +} + func (u *UnifiPoller) dumpJSON(path, what string, site unifi.Site) error { req, err := u.UniReq(path, "") if err != nil { diff --git a/pkg/unifi-poller/unifi.go b/pkg/unifi-poller/unifi.go index d06e1b9a..4014f8f1 100644 --- a/pkg/unifi-poller/unifi.go +++ b/pkg/unifi-poller/unifi.go @@ -40,95 +40,100 @@ FIRST: func (u *UnifiPoller) PollController() { log.Println("[INFO] Everything checks out! Poller started, interval:", u.Interval.value) ticker := time.NewTicker(u.Interval.value) - + var err error for range ticker.C { + m := &Metrics{} // Get the sites we care about. - sites, err := u.filterSites(u.Sites) - if err != nil { + if m.Sites, err = u.GetFilteredSites(); err != nil { logErrors([]error{err}, "uni.GetSites()") } // Get all the points. - clients, err := u.GetClients(sites) - if err != nil { + if m.Clients, err = u.GetClients(m.Sites); err != nil { logErrors([]error{err}, "uni.GetClients()") } - devices, err := u.GetDevices(sites) - if err != nil { + if m.Devices, err = u.GetDevices(m.Sites); err != nil { logErrors([]error{err}, "uni.GetDevices()") } + // Make a new Points Batcher. - bp, err := influx.NewBatchPoints(influx.BatchPointsConfig{Database: u.InfluxDB}) + m.BatchPoints, err = influx.NewBatchPoints(influx.BatchPointsConfig{Database: u.InfluxDB}) if err != nil { logErrors([]error{err}, "influx.NewBatchPoints") continue } // Batch (and send) all the points. - if errs := batchPoints(devices, clients, bp); errs != nil && hasErr(errs) { + if errs := m.SendPoints(); errs != nil && hasErr(errs) { logErrors(errs, "asset.Points()") } - if err := u.Write(bp); err != nil { + if err := u.Write(m.BatchPoints); err != nil { logErrors([]error{err}, "infdb.Write(bp)") } // Talk about the data. var fieldcount, pointcount int - for _, p := range bp.Points() { + for _, p := range m.Points() { pointcount++ i, _ := p.Fields() fieldcount += len(i) } - u.Logf("Unifi Measurements Recorded. Sites: %d Clients: %d, "+ + u.Logf("Unifi Measurements Recorded. Sites: %d, Clients: %d, "+ "Wireless APs: %d, Gateways: %d, Switches: %d, Points: %d, Fields: %d", - len(sites), len(clients.UCLs), - len(devices.UAPs), len(devices.USGs), len(devices.USWs), pointcount, fieldcount) + len(m.Sites), len(m.Clients), len(m.UAPs), len(m.USGs), len(m.USWs), pointcount, fieldcount) } } -// batchPoints combines all device and client data into influxdb data points. -func batchPoints(devices *unifi.Devices, clients *unifi.Clients, bp influx.BatchPoints) (errs []error) { - process := func(asset Asset) error { - if asset == nil { - return nil - } - influxPoints, err := asset.Points() - if err != nil { - return err - } - bp.AddPoints(influxPoints) - return nil +// SendPoints combines all device and client data into influxdb data points. +// Call this after you've collected all the data you care about. +// This sends all the batched points to InfluxDB. +func (m *Metrics) SendPoints() (errs []error) { + for _, asset := range m.Sites { + errs = append(errs, m.processPoints(asset)) } - if devices != nil { - for _, asset := range devices.UAPs { - errs = append(errs, process(asset)) - } - for _, asset := range devices.USGs { - errs = append(errs, process(asset)) - } - for _, asset := range devices.USWs { - errs = append(errs, process(asset)) - } + for _, asset := range m.Clients { + errs = append(errs, m.processPoints(asset)) } - if clients != nil { - for _, asset := range clients.UCLs { - errs = append(errs, process(asset)) - } + if m.Devices == nil { + return + } + for _, asset := range m.UAPs { + errs = append(errs, m.processPoints(asset)) + } + for _, asset := range m.USGs { + errs = append(errs, m.processPoints(asset)) + } + for _, asset := range m.USWs { + errs = append(errs, m.processPoints(asset)) } return } -// filterSites returns a list of sites to fetch data for. -// Omits requested but unconfigured sites. -func (u *UnifiPoller) filterSites(filter []string) ([]unifi.Site, error) { +// processPoints is helper function for SendPoints. +func (m *Metrics) processPoints(asset Asset) error { + if asset == nil { + return nil + } + influxPoints, err := asset.Points() + if err != nil || influxPoints == nil { + return err + } + m.BatchPoints.AddPoints(influxPoints) + return nil +} + +// GetFilteredSites returns a list of sites to fetch data for. +// Omits requested but unconfigured sites. Grabs the full list from the +// controller and filters the sites provided in the config file. +func (u *UnifiPoller) GetFilteredSites() (unifi.Sites, error) { sites, err := u.GetSites() if err != nil { return nil, err - } else if len(filter) < 1 || StringInSlice("all", filter) { + } else if len(u.Sites) < 1 || StringInSlice("all", u.Sites) { return sites, nil } var i int for _, s := range sites { // Only include valid sites in the request filter. - if StringInSlice(s.Name, filter) { + if StringInSlice(s.Name, u.Sites) { sites[i] = s i++ } diff --git a/pkg/unifi-poller/unifipoller.go b/pkg/unifi-poller/unifipoller.go index 594dcbc6..4d754f34 100644 --- a/pkg/unifi-poller/unifipoller.go +++ b/pkg/unifi-poller/unifipoller.go @@ -101,7 +101,11 @@ func (u *UnifiPoller) GetUnifi() (err error) { if u.Debug && !u.Quiet { u.Unifi.DebugLog = log.Printf // Log debug messages. } - u.Logf("Authenticated to Unifi Controller at %s as user %s", u.UnifiBase, u.UnifiUser) + v, err := u.GetServer() + if err != nil { + v.ServerVersion = "unknown" + } + u.Logf("Authenticated to Unifi Controller at %s version %s as user %s", u.UnifiBase, v.ServerVersion, u.UnifiUser) if err = u.CheckSites(); err != nil { return err }