switch to dep, another test, add flags - minor features

This commit is contained in:
DN2 2019-01-11 20:36:29 -08:00
parent 424b060839
commit bd8c11ed50
10 changed files with 229 additions and 111 deletions

View File

@ -1,75 +0,0 @@
{
"ImportPath": "github.com/davidnewhall/unifi-poller",
"GoVersion": "go1.11",
"GodepVersion": "v79",
"Packages": [
"./..."
],
"Deps": [
{
"ImportPath": "github.com/davecgh/go-spew/spew",
"Comment": "v1.1.0",
"Rev": "346938d642f2ec3594ed81d874461961cd0faa76"
},
{
"ImportPath": "github.com/influxdata/influxdb/client/v2",
"Comment": "v1.5.0-149-g14dcc5d6e",
"Rev": "14dcc5d6e7a6b15e17aba7b104b8ad0ca6c91ad2"
},
{
"ImportPath": "github.com/influxdata/influxdb/models",
"Comment": "v1.5.0-149-g14dcc5d6e",
"Rev": "14dcc5d6e7a6b15e17aba7b104b8ad0ca6c91ad2"
},
{
"ImportPath": "github.com/influxdata/influxdb/pkg/escape",
"Comment": "v1.5.0-149-g14dcc5d6e",
"Rev": "14dcc5d6e7a6b15e17aba7b104b8ad0ca6c91ad2"
},
{
"ImportPath": "github.com/naoina/go-stringutil",
"Comment": "v0.1.0",
"Rev": "6b638e95a32d0c1131db0e7fe83775cbea4a0d0b"
},
{
"ImportPath": "github.com/naoina/toml",
"Comment": "v0.1.1-2-g9fafd69",
"Rev": "9fafd69674167c06933b1787ae235618431ce87f"
},
{
"ImportPath": "github.com/naoina/toml/ast",
"Comment": "v0.1.1-2-g9fafd69",
"Rev": "9fafd69674167c06933b1787ae235618431ce87f"
},
{
"ImportPath": "github.com/ogier/pflag",
"Comment": "v0.0.1-7-g45c278a",
"Rev": "45c278ab3607870051a2ea9040bb85fcb8557481"
},
{
"ImportPath": "github.com/pkg/errors",
"Comment": "v0.8.0-6-g2b3a18b",
"Rev": "2b3a18b5f0fb6b4f9190549597d3f962c02bc5eb"
},
{
"ImportPath": "github.com/pmezard/go-difflib/difflib",
"Comment": "v1.0.0",
"Rev": "792786c7400a136282c1664665ae0a8db921c6c2"
},
{
"ImportPath": "github.com/stretchr/testify/assert",
"Comment": "v1.1.4-27-g4d4bfba",
"Rev": "4d4bfba8f1d1027c4fdbe371823030df51419987"
},
{
"ImportPath": "github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/spew",
"Comment": "v1.1.4-27-g4d4bfba",
"Rev": "4d4bfba8f1d1027c4fdbe371823030df51419987"
},
{
"ImportPath": "github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/difflib",
"Comment": "v1.1.4-27-g4d4bfba",
"Rev": "4d4bfba8f1d1027c4fdbe371823030df51419987"
}
]
}

View File

@ -1,5 +0,0 @@
This directory tree is generated automatically by godep.
Please do not edit.
See https://github.com/tools/godep for more information.

73
integrations/influxunifi/Gopkg.lock generated Normal file
View File

@ -0,0 +1,73 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/davecgh/go-spew"
packages = ["spew"]
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.1"
[[projects]]
name = "github.com/influxdata/influxdb"
packages = [
"client/v2",
"models",
"pkg/escape"
]
revision = "76f907b0fada2f16931e37471da695349fcdf8c6"
version = "v1.7.2"
[[projects]]
branch = "master"
name = "github.com/influxdata/platform"
packages = [
"models",
"pkg/escape"
]
revision = "98469bf07613ffae6f025893761c1e7a5e96e4aa"
[[projects]]
name = "github.com/naoina/go-stringutil"
packages = ["."]
revision = "6b638e95a32d0c1131db0e7fe83775cbea4a0d0b"
version = "v0.1.0"
[[projects]]
name = "github.com/naoina/toml"
packages = [
".",
"ast"
]
revision = "e6f5723bf2a66af014955e0888881314cf294129"
version = "v0.1.1"
[[projects]]
name = "github.com/ogier/pflag"
packages = ["."]
revision = "32a05c62658bd1d7c7e75cbc8195de5d585fde0f"
version = "v0.0.1"
[[projects]]
name = "github.com/pkg/errors"
packages = ["."]
revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4"
version = "v0.8.1"
[[projects]]
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
name = "github.com/stretchr/testify"
packages = ["assert"]
revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053"
version = "v1.3.0"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "3b1d8ae8346fc10f0725163478eab3fa466a2064f4d44077102c5f7651625dc4"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -0,0 +1,50 @@
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
name = "github.com/influxdata/influxdb"
version = "1.7.2"
[[constraint]]
name = "github.com/naoina/toml"
version = "0.1.1"
[[constraint]]
name = "github.com/ogier/pflag"
version = "0.0.1"
[[constraint]]
name = "github.com/pkg/errors"
version = "0.8.1"
[[constraint]]
name = "github.com/stretchr/testify"
version = "1.3.0"
[prune]
go-tests = true
unused-packages = true

View File

@ -43,6 +43,4 @@ man:
script/build_manpages.sh ./
deps:
rm -rf Godeps vendor
godep save ./...
godep update ./...
dep ensure -update

View File

@ -12,11 +12,22 @@ unifi-poller(1) -- Utility to poll Unifi Metrics and drop them into InfluxDB
## OPTIONS
`unifi-poller [-c <config file>] [-h] [-v]`
`unifi-poller [-c <config file>] [-D] [-q] [-s] [-h] [-v]`
-c, --config <file_path>
Provide a configuration file (instead of the default).
-D, --debug
Turns on line numbers, microsecond logging, and a per-device log.
-q, --quiet
Turns off per-device log and per-interval log. Logs only errors.
Recommend using -D with this setting for better error logging.
-s, --verify-ssl
If your Unifi controller has a valid SSL certificate, you can enable
this option to validate it. Otherwise, any SSL certificate is valid.
-v, --version
Display version and exit.

View File

@ -21,12 +21,15 @@ func main() {
}
configFile := flg.StringP("config", "c", defaultConfFile, "Poller Config File (TOML Format)")
flg.BoolVarP(&unidev.Debug, "debug", "D", false, "Turn on the Spam (default false)")
version := flg.BoolP("version", "v", false, "Print the version and exit.")
quiet := flg.BoolP("quiet", "q", false, "Do not print logs on every poll, only errors")
version := flg.BoolP("version", "v", false, "Print the version and exit")
verifySSL := flg.BoolP("verify-ssl", "s", false, "If your controller has a valid SSL cert, require it with this flag")
if flg.Parse(); *version {
fmt.Println("unifi-poller version:", Version)
os.Exit(0) // don't run anything else.
}
log.Println("Unifi-Poller Starting Up! PID:", os.Getpid())
if log.SetFlags(0); unidev.Debug {
log.SetFlags(log.Lshortfile | log.Lmicroseconds | log.Ldate)
log.Println("Debug Logging Enabled")
@ -38,12 +41,13 @@ func main() {
}
log.Println("Loaded Configuration:", *configFile)
// Create an authenticated session to the Unifi Controller.
unifi, err := unidev.AuthController(config.UnifiUser, config.UnifiPass, config.UnifiBase)
unifi, err := unidev.AuthController(config.UnifiUser, config.UnifiPass, config.UnifiBase, *verifySSL)
if err != nil {
log.Fatalln("Unifi Controller Error:", err)
}
log.Println("Authenticated to Unifi Controller @", config.UnifiBase, "as user", config.UnifiUser)
if !*quiet {
log.Println("Authenticated to Unifi Controller @", config.UnifiBase, "as user", config.UnifiUser)
}
infdb, err := influx.NewHTTPClient(influx.HTTPConfig{
Addr: config.InfluxURL,
Username: config.InfluxUser,
@ -52,9 +56,15 @@ func main() {
if err != nil {
log.Fatalln("InfluxDB Error:", err)
}
log.Println("Logging Unifi Metrics to InfluXDB @", config.InfluxURL, "as user", config.InfluxUser)
log.Println("Polling Unifi Controller, interval:", config.Interval.value)
config.PollUnifiController(infdb, unifi)
if *quiet {
// Do it this way allows debug error logs (line numbers, etc)
unidev.Debug = false
} else {
log.Println("Logging Unifi Metrics to InfluXDB @", config.InfluxURL, "as user", config.InfluxUser)
log.Println("Polling Unifi Controller, interval:", config.Interval.value)
}
log.Println("Everyting checks out! Beginning Poller Routine.")
config.PollUnifiController(infdb, unifi, *quiet)
}
// GetConfig parses and returns our configuration data.
@ -80,7 +90,7 @@ func GetConfig(configFile string) (Config, error) {
}
// PollUnifiController runs forever, polling and pushing.
func (c *Config) PollUnifiController(infdb influx.Client, unifi *unidev.AuthedReq) {
func (c *Config) PollUnifiController(infdb influx.Client, unifi *unidev.AuthedReq, quiet bool) {
ticker := time.NewTicker(c.Interval.value)
for range ticker.C {
var clients, devices []unidev.Asset
@ -107,6 +117,8 @@ func (c *Config) PollUnifiController(infdb influx.Client, unifi *unidev.AuthedRe
log.Println("ERROR infdb.Write(bp):", err)
continue
}
log.Println("Logged client state. Clients:", len(clients), "- Devices:", len(devices))
if !quiet {
log.Println("Logged client state. Clients:", len(clients), "- Devices:", len(devices))
}
}
}

View File

@ -61,23 +61,23 @@ func (f *FlexInt) UnmarshalJSON(b []byte) error {
// AuthController creates a http.Client with authenticated cookies.
// Used to make additional, authenticated requests to the APIs.
func AuthController(user, pass, url string) (*AuthedReq, error) {
func AuthController(user, pass, url string, verifySSL bool) (*AuthedReq, error) {
json := `{"username": "` + user + `","password": "` + pass + `"}`
jar, err := cookiejar.New(nil)
if err != nil {
return nil, errors.Wrap(err, "cookiejar.New(nil)")
}
authReq := &AuthedReq{&http.Client{
Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}},
a := &AuthedReq{&http.Client{
Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: !verifySSL}},
Jar: jar,
}, url}
req, err := authReq.UniReq(LoginPath, json)
req, err := a.UniReq(LoginPath, json)
if err != nil {
return nil, errors.Wrap(err, "UniReq(LoginPath, json)")
return a, errors.Wrap(err, "UniReq(LoginPath, json)")
}
resp, err := authReq.Do(req)
resp, err := a.Do(req)
if err != nil {
return nil, errors.Wrap(err, "authReq.Do(req)")
return a, errors.Wrap(err, "authReq.Do(req)")
}
defer func() {
if err := resp.Body.Close(); err != nil {
@ -85,18 +85,18 @@ func AuthController(user, pass, url string) (*AuthedReq, error) {
}
}()
if resp.StatusCode != http.StatusOK {
return nil, errors.Errorf("authentication failed (%v): %v (status: %v/%v)",
return a, errors.Errorf("authentication failed (%v): %v (status: %v/%v)",
user, url+LoginPath, resp.StatusCode, resp.Status)
}
return authReq, nil
return a, nil
}
// UniReq is a small helper function that adds an Accept header.
func (c AuthedReq) UniReq(apiURL string, params string) (req *http.Request, err error) {
func (a AuthedReq) UniReq(apiPath string, params string) (req *http.Request, err error) {
if params != "" {
req, err = http.NewRequest("POST", c.baseURL+apiURL, bytes.NewBufferString(params))
req, err = http.NewRequest("POST", a.baseURL+apiPath, bytes.NewBufferString(params))
} else {
req, err = http.NewRequest("GET", c.baseURL+apiURL, nil)
req, err = http.NewRequest("GET", a.baseURL+apiPath, nil)
}
if err == nil {
req.Header.Add("Accept", "application/json")

View File

@ -2,6 +2,8 @@ package unidev
import (
"encoding/json"
"io/ioutil"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
@ -11,13 +13,15 @@ func TestFlexInt(t *testing.T) {
t.Parallel()
a := assert.New(t)
type testReply struct {
Five FlexInt `json:"five"`
Seven FlexInt `json:"seven"`
Auto FlexInt `json:"auto"`
Five FlexInt `json:"five"`
Seven FlexInt `json:"seven"`
Auto FlexInt `json:"auto"`
Channel FlexInt `json:"channel"`
}
var r testReply
// test unmarshalling the custom type three times with different values.
a.Nil(json.Unmarshal([]byte(`{"five": "5", "seven": 7, "auto": "auto"}`), &r))
// test number in string.
a.EqualValues(5, r.Five.Number)
a.EqualValues("5", r.Five.String)
@ -25,8 +29,54 @@ func TestFlexInt(t *testing.T) {
a.EqualValues(7, r.Seven.Number)
a.EqualValues("7", r.Seven.String)
// test string.
a.Nil(json.Unmarshal([]byte(`{"channel": "auto"}`), &r),
"a regular string must not produce an unmarshal error")
a.EqualValues(0, r.Auto.Number)
a.EqualValues("auto", r.Auto.String)
// test (error) struct.
a.NotNil(json.Unmarshal([]byte(`{"channel": {}}`), &r),
"a non-string and non-number must produce an error.")
a.EqualValues(0, r.Channel.Number)
}
func TestUniReq(t *testing.T) {
t.Parallel()
a := assert.New(t)
u := "/test/path"
url := "http://some.url:8443"
// Test empty parameters.
authReq := &AuthedReq{&http.Client{}, url}
r, err := authReq.UniReq(u, "")
a.Nil(err, "newrequest must not produce an error")
a.EqualValues(u, r.URL.Path,
"the provided apiPath was not added to http request")
a.EqualValues(url, r.URL.Scheme+"://"+r.URL.Host, "URL improperly encoded")
a.EqualValues("GET", r.Method, "without parameters the method must be GET")
a.EqualValues("application/json", r.Header.Get("Accept"), "Accept header must be set to application/json")
// Test with parameters
p := "key1=value9&key2=value7"
authReq = &AuthedReq{&http.Client{}, "http://some.url:8443"}
r, err = authReq.UniReq(u, p)
a.Nil(err, "newrequest must not produce an error")
a.EqualValues(u, r.URL.Path,
"the provided apiPath was not added to http request")
a.EqualValues(url, r.URL.Scheme+"://"+r.URL.Host, "URL improperly encoded")
a.EqualValues("POST", r.Method, "with parameters the method must be POST")
a.EqualValues("application/json", r.Header.Get("Accept"), "Accept header must be set to application/json")
// Check the parameters.
d, err := ioutil.ReadAll(r.Body)
a.Nil(err, "problem reading request body, POST parameters may be malformed")
a.EqualValues(p, string(d), "POST parameters improperly encoded")
}
func TestAuthController(t *testing.T) {
t.Parallel()
a := assert.New(t)
url := "http://127.0.0.1:64431"
authReq, err := AuthController("user1", "pass2", url, false)
a.NotNil(err)
a.EqualValues(url, authReq.baseURL)
a.Contains(err.Error(), "authReq.Do(req):", "an invalid destination should product a .Do(req) error.")
/* TODO: OPEN web server, check parameters posted, more. This test is incomplete.
a.EqualValues(`{"username": "user1","password": "pass2"}`, string(post_params), "user/pass json parameters improperly encoded")
*/
}

View File

@ -1,4 +1,8 @@
# The Unifi Controller only updates traffic stats about every 30 seconds.
# unifi-poller primary configuration file. #
# copy this file to: /usr/local/etc/unifi-poller/up.conf #
##########################################################
# The Unifi Controller only updates traffic stats about every 30 seconds.
# Setting this to something lower may lead to "zeros" in your data. You've been warned.
interval = "30s"