Use a config file instead of env vars.
This commit is contained in:
		
							parent
							
								
									74c64be507
								
							
						
					
					
						commit
						bb0e3ccfd8
					
				
							
								
								
									
										17
									
								
								.env.example
								
								
								
								
							
							
						
						
									
										17
									
								
								.env.example
								
								
								
								
							|  | @ -1,17 +0,0 @@ | |||
| UNIFI_ADDR="107.170.232.179" | ||||
| UNIFI_PORT="8443" | ||||
| # Go to Settings -> Admins and add (or use) a read-only user for this. | ||||
| UNIFI_USERNAME="username" | ||||
| UNIFI_PASSWORD="password" | ||||
| 
 | ||||
| # Can be 1m15s for 1 minute 0 seconds, 15s for 15 seconds, etc. | ||||
| INTERVAL="15s" | ||||
| 
 | ||||
| INFLUXDB_ADDR="http://hostname:8086" | ||||
| INFLUXDB_DATABASE="unifi" | ||||
| INFLUXDB_USERNAME="unifi" | ||||
| INFLUXDB_PASSWORD="password" | ||||
| 
 | ||||
| export UNIFI_ADDR UNIFI_PORT UNIFI_USERNAME UNIFI_PASSWORD | ||||
| export INTERVAL | ||||
| export INFLUXDB_ADDR INFLUXDB_DATABASE INFLUXDB_USERNAME INFLUXDB_PASSWORD | ||||
|  | @ -1,4 +1,4 @@ | |||
| .env | ||||
| /up.conf | ||||
| /unifi-poller | ||||
| /*.1.gz | ||||
| /*.1 | ||||
|  |  | |||
|  | @ -21,6 +21,26 @@ | |||
| 			"Comment": "v1.5.0-149-g14dcc5d", | ||||
| 			"Rev": "14dcc5d6e7a6b15e17aba7b104b8ad0ca6c91ad2" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "github.com/naoina/go-stringutil", | ||||
| 			"Comment": "v0.1.0", | ||||
| 			"Rev": "6b638e95a32d0c1131db0e7fe83775cbea4a0d0b" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "github.com/naoina/toml", | ||||
| 			"Comment": "v0.1.0", | ||||
| 			"Rev": "751171607256bb66e64c9f0220c00662420c38e9" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "github.com/naoina/toml/ast", | ||||
| 			"Comment": "v0.1.0", | ||||
| 			"Rev": "751171607256bb66e64c9f0220c00662420c38e9" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "github.com/ogier/pflag", | ||||
| 			"Comment": "v0.0.1-7-g45c278a", | ||||
| 			"Rev": "45c278ab3607870051a2ea9040bb85fcb8557481" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "github.com/pkg/errors", | ||||
| 			"Comment": "v0.8.0-6-g2b3a18b", | ||||
|  |  | |||
							
								
								
									
										16
									
								
								README.md
								
								
								
								
							
							
						
						
									
										16
									
								
								README.md
								
								
								
								
							|  | @ -1,20 +1,12 @@ | |||
| # Unifi | ||||
| 
 | ||||
| Collect your Unifi Controller Client data and send it to an InfluxDB instance. | ||||
| Collect your Unifi Controller Client data and send it to an InfluxDB instance. Grafana dashboard included. | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| ## Deploying | ||||
| ## Installation | ||||
| 
 | ||||
| 
 | ||||
| Clone the repository and using `.env.example` create your own `.env` file with your Unifi GUI and InfluxDB credentials. | ||||
| 
 | ||||
| 
 | ||||
| Set your environment variables before running: | ||||
| 
 | ||||
| ``` | ||||
| source .env ; ./unifi-poller | ||||
| ``` | ||||
| [See the Wiki!](https://github.com/davidnewhall/unifi-poller/wiki/Installation) | ||||
| 
 | ||||
| ## Copyright & License | ||||
| Copyright © 2016 Garrett Bjerkhoel. See [MIT-LICENSE](http://github.com/dewski/unifi/blob/master/MIT-LICENSE) for details. | ||||
| Copyright © 2016 Garrett Bjerkhoel. See [MIT-LICENSE](MIT-LICENSE) for details. | ||||
|  |  | |||
|  | @ -5,6 +5,9 @@ import ( | |||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // Version will be injected at build time.
 | ||||
| var Version = "v0.1" | ||||
| 
 | ||||
| const ( | ||||
| 	// LoginPath is Unifi Controller Login API Path
 | ||||
| 	LoginPath = "/api/login" | ||||
|  | @ -17,6 +20,7 @@ const ( | |||
| 	// UserGroupPath contains usergroup configurations.
 | ||||
| 	UserGroupPath = "/api/s/default/rest/usergroup" | ||||
| 	// App defaults in case they're missing from the config.
 | ||||
| 	defaultConfFile = "/usr/local/etc/unifi-poller/up.conf" | ||||
| 	defaultInterval = 30 * time.Second | ||||
| 	defaultInfxDb   = "unifi" | ||||
| 	defaultInfxUser = "unifi" | ||||
|  | @ -28,13 +32,28 @@ const ( | |||
| 
 | ||||
| // Config represents the data needed to poll a controller and report to influxdb.
 | ||||
| type Config struct { | ||||
| 	Interval   time.Duration `json:"interval" toml:"interval" xml:"interval" yaml:"interval"` | ||||
| 	InfluxURL  string        `json:"influx_url" toml:"influx_addr" xml:"influx_addr" yaml:"influx_addr"` | ||||
| 	InfluxUser string        `json:"influx_user" toml:"influx_user" xml:"influx_user" yaml:"influx_user"` | ||||
| 	InfluxPass string        `json:"influx_pass" toml:"influx_pass" xml:"influx_pass" yaml:"influx_pass"` | ||||
| 	InfluxDB   string        `json:"influx_db" toml:"influx_db" xml:"influx_db" yaml:"influx_db"` | ||||
| 	UnifiUser  string        `json:"unifi_user" toml:"unifi_user" xml:"unifi_user" yaml:"unifi_user"` | ||||
| 	UnifiPass  string        `json:"unifi_pass" toml:"unifi_pass" xml:"unifi_pass" yaml:"unifi_pass"` | ||||
| 	UnifiBase  string        `json:"unifi_url" toml:"unifi_url" xml:"unifi_url" yaml:"unifi_url"` | ||||
| 	Interval   Dur    `json:"interval" toml:"interval" xml:"interval" yaml:"interval"` | ||||
| 	InfluxURL  string `json:"influx_url" toml:"influx_url" xml:"influx_url" yaml:"influx_url"` | ||||
| 	InfluxUser string `json:"influx_user" toml:"influx_user" xml:"influx_user" yaml:"influx_user"` | ||||
| 	InfluxPass string `json:"influx_pass" toml:"influx_pass" xml:"influx_pass" yaml:"influx_pass"` | ||||
| 	InfluxDB   string `json:"influx_db" toml:"influx_db" xml:"influx_db" yaml:"influx_db"` | ||||
| 	UnifiUser  string `json:"unifi_user" toml:"unifi_user" xml:"unifi_user" yaml:"unifi_user"` | ||||
| 	UnifiPass  string `json:"unifi_pass" toml:"unifi_pass" xml:"unifi_pass" yaml:"unifi_pass"` | ||||
| 	UnifiBase  string `json:"unifi_url" toml:"unifi_url" xml:"unifi_url" yaml:"unifi_url"` | ||||
| 	uniClient  *http.Client | ||||
| } | ||||
| 
 | ||||
| // Dur is used to UnmarshalTOML into a time.Duration value.
 | ||||
| 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]) | ||||
| 	dur, err := time.ParseDuration(unquoted) | ||||
| 	if err == nil { | ||||
| 		v.value = dur | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import ( | |||
| 	"bytes" | ||||
| 	"crypto/tls" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
|  | @ -12,15 +13,36 @@ import ( | |||
| 	"time" | ||||
| 
 | ||||
| 	influx "github.com/influxdata/influxdb/client/v2" | ||||
| 	"github.com/naoina/toml" | ||||
| 	flg "github.com/ogier/pflag" | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
| 	config := GetConfig() | ||||
| 	flg.Usage = func() { | ||||
| 		fmt.Println("Usage: unifi-poller [--config=filepath] [--debug] [--version]") | ||||
| 		flg.PrintDefaults() | ||||
| 	} | ||||
| 	configFile := flg.StringP("config", "c", defaultConfFile, "Poller Config File (TOML Format)") | ||||
| 	debug := flg.BoolP("debug", "D", false, "Turn on the Spam (default false)") | ||||
| 	version := flg.BoolP("version", "v", false, "Print the version and exit.") | ||||
| 	flg.Parse() | ||||
| 	if *version { | ||||
| 		fmt.Println("unifi-poller version:", Version) | ||||
| 		os.Exit(0) // don't run anything else.
 | ||||
| 	} | ||||
| 	if log.SetFlags(0); *debug { | ||||
| 		log.SetFlags(log.Lshortfile | log.Lmicroseconds | log.Ldate) | ||||
| 	} | ||||
| 	config, errc := GetConfig(*configFile) | ||||
| 	if errc != nil { | ||||
| 		flg.Usage() | ||||
| 		log.Fatalln("Config Error:", errc) | ||||
| 	} | ||||
| 	if err := config.AuthController(); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	log.Println("Authenticated to Unifi Controller", config.UnifiBase, "as user", config.UnifiUser) | ||||
| 	log.Println("Authenticated to Unifi Controller @", config.UnifiBase, "as user", config.UnifiUser) | ||||
| 
 | ||||
| 	infdb, err := influx.NewHTTPClient(influx.HTTPConfig{ | ||||
| 		Addr:     config.InfluxURL, | ||||
|  | @ -31,28 +53,30 @@ func main() { | |||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	log.Println("Logging Unifi Metrics to InfluXDB @", config.InfluxURL, "as user", config.InfluxUser) | ||||
| 	log.Println("Polling Unifi Controller, interval:", config.Interval) | ||||
| 	log.Println("Polling Unifi Controller, interval:", config.Interval.value) | ||||
| 	config.PollUnifiController(infdb) | ||||
| } | ||||
| 
 | ||||
| // GetConfig parses and returns our configuration data.
 | ||||
| func GetConfig() Config { | ||||
| 	// TODO: A real config file.
 | ||||
| 	interval, err := time.ParseDuration(os.Getenv("INTERVAL")) | ||||
| 	if err != nil { | ||||
| 		log.Println("Invalid Interval, defaulting to", defaultInterval) | ||||
| 		interval = time.Duration(defaultInterval) | ||||
| 	} | ||||
| 	return Config{ | ||||
| 		InfluxURL:  os.Getenv("INFLUXDB_URL"), | ||||
| 		InfluxUser: os.Getenv("INFLUXDB_USERNAME"), | ||||
| 		InfluxPass: os.Getenv("INFLUXDB_PASSWORD"), | ||||
| 		InfluxDB:   os.Getenv("INFLUXDB_DATABASE"), | ||||
| 		UnifiUser:  os.Getenv("UNIFI_USERNAME"), | ||||
| func GetConfig(configFile string) (Config, error) { | ||||
| 	// Preload our defaults.
 | ||||
| 	config := Config{ | ||||
| 		InfluxURL:  defaultInfxURL, | ||||
| 		InfluxUser: defaultInfxUser, | ||||
| 		InfluxPass: defaultInfxPass, | ||||
| 		InfluxDB:   defaultInfxDb, | ||||
| 		UnifiUser:  defaultUnifUser, | ||||
| 		UnifiPass:  os.Getenv("UNIFI_PASSWORD"), | ||||
| 		UnifiBase:  "https://" + os.Getenv("UNIFI_ADDR") + ":" + os.Getenv("UNIFI_PORT"), | ||||
| 		Interval:   interval, | ||||
| 		UnifiBase:  defaultUnifURL, | ||||
| 		Interval:   Dur{value: defaultInterval}, | ||||
| 	} | ||||
| 	if buf, err := ioutil.ReadFile(configFile); err != nil { | ||||
| 		return config, err | ||||
| 		// This is where the defaults in the config variable are overwritten.
 | ||||
| 	} else if err := toml.Unmarshal(buf, &config); err != nil { | ||||
| 		return config, errors.Wrap(err, "invalid config") | ||||
| 	} | ||||
| 	return config, nil | ||||
| } | ||||
| 
 | ||||
| // AuthController creates a http.Client with authenticated cookies.
 | ||||
|  | @ -80,7 +104,7 @@ func (c *Config) AuthController() error { | |||
| 
 | ||||
| // PollUnifiController runs forever, polling and pushing.
 | ||||
| func (c *Config) PollUnifiController(infdb influx.Client) { | ||||
| 	ticker := time.NewTicker(c.Interval) | ||||
| 	ticker := time.NewTicker(c.Interval.value) | ||||
| 	for range ticker.C { | ||||
| 		clients, err := c.GetUnifiClients() | ||||
| 		if err != nil { | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 909 KiB After Width: | Height: | Size: 246 KiB | 
|  | @ -0,0 +1,15 @@ | |||
| # 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" | ||||
| 
 | ||||
| # InfluxDB does not require auth by default, so the user/password are probably unimportant. | ||||
| influx_url = "http://127.0.0.1:8086" | ||||
| influx_user = "unifi" | ||||
| influx_pass = "unifi" | ||||
| # Be sure to create this database. | ||||
| influx_db = "unifi" | ||||
| 
 | ||||
| # Make a read-only user in the Unifi Admin Settings. | ||||
| unifi_user = "influxdb" | ||||
| unifi_pass = "4BB9345C-2341-48D7-99F5-E01B583FF77F" | ||||
| unifi_url = "https://127.0.0.1:8443" | ||||
		Loading…
	
		Reference in New Issue