From 82cbc4daa22cf63c47c086eab0a58d1997733bbb Mon Sep 17 00:00:00 2001 From: Evan Phoenix Date: Mon, 18 May 2015 15:10:11 -0700 Subject: [PATCH] Add ability to generate config from available plugins --- cmd/tivan/tivan.go | 6 +++ config.go | 72 ++++++++++++++++++++++++++++++++ plugins/mysql/mysql.go | 22 +++++++--- plugins/postgresql/postgresql.go | 33 +++++++++++++++ plugins/redis/redis.go | 35 ++++++++-------- plugins/registry.go | 2 + plugins/system/system.go | 48 +++++++++++++++++++++ 7 files changed, 195 insertions(+), 23 deletions(-) diff --git a/cmd/tivan/tivan.go b/cmd/tivan/tivan.go index 6a11657a0..552df22b1 100644 --- a/cmd/tivan/tivan.go +++ b/cmd/tivan/tivan.go @@ -16,6 +16,7 @@ var fDebug = flag.Bool("debug", false, "show metrics as they're generated to std var fTest = flag.Bool("test", false, "gather metrics, print them out, and exit") var fConfig = flag.String("config", "", "configuration file to load") var fVersion = flag.Bool("version", false, "display the version") +var fSampleConfig = flag.Bool("sample-config", false, "print out full sample configuration") var Version = "unreleased" @@ -27,6 +28,11 @@ func main() { return } + if *fSampleConfig { + tivan.PrintSampleConfig() + return + } + var ( config *tivan.Config err error diff --git a/config.go b/config.go index 5c4a3e591..9cfeb1213 100644 --- a/config.go +++ b/config.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "github.com/influxdb/tivan/plugins" "github.com/naoina/toml" "github.com/naoina/toml/ast" ) @@ -113,3 +114,74 @@ func (c *Config) ListTags() string { return strings.Join(tags, " ") } + +type hasConfig interface { + BasicConfig() string +} + +type hasDescr interface { + Description() string +} + +var header = `# Tivan configuration + +# Tivan is entirely plugin driven. All metrics are gathered from the +# declared plugins. + +# Even if a plugin has no configuration, it must be declared in here +# to be active. Declaring a plugin means just specifying the name +# as a section with no variables. + +# Configuration for influxdb server to send metrics to +# [influxdb] +# url = "http://10.20.2.4" +# username = "tivan" +# password = "metricsmetricsmetricsmetrics" +# database = "tivan" +# user_agent = "tivan" +# tags = { "dc": "us-east-1" } + +# Tags can also be specified via a normal map, but only one form at a time: + +# [influxdb.tags] +# dc = "us-east-1" + +# PLUGINS + +` + +func PrintSampleConfig() { + fmt.Printf(header) + + var names []string + + for name, _ := range plugins.Plugins { + names = append(names, name) + } + + sort.Strings(names) + + for _, name := range names { + creator := plugins.Plugins[name] + + plugin := creator() + + fmt.Printf("# %s\n[%s]\n", plugin.Description(), name) + + var config string + + config = strings.TrimSpace(plugin.SampleConfig()) + + if config == "" { + fmt.Printf(" # no configuration\n\n") + } else { + fmt.Printf("\n") + lines := strings.Split(config, "\n") + for _, line := range lines { + fmt.Printf("%s\n", line) + } + + fmt.Printf("\n") + } + } +} diff --git a/plugins/mysql/mysql.go b/plugins/mysql/mysql.go index db6515f81..2f0ae9291 100644 --- a/plugins/mysql/mysql.go +++ b/plugins/mysql/mysql.go @@ -14,17 +14,27 @@ type Server struct { } type Mysql struct { - Disabled bool - Servers []*Server + Servers []*Server +} + +var sampleConfig = ` +# specify servers via a url matching: +# [username[:password]@][protocol[(address)]]/[?tls=[true|false|skip-verify]] +# +# If no servers are specified, then localhost is used as the host. +servers = ["localhost"]` + +func (m *Mysql) SampleConfig() string { + return sampleConfig +} + +func (m *Mysql) Description() string { + return "Read metrics from one or many mysql servers" } var localhost = &Server{} func (m *Mysql) Gather(acc plugins.Accumulator) error { - if m.Disabled { - return nil - } - if len(m.Servers) == 0 { // if we can't get stats in this case, thats fine, don't report // an error. diff --git a/plugins/postgresql/postgresql.go b/plugins/postgresql/postgresql.go index b67e92c69..b1e3a90c9 100644 --- a/plugins/postgresql/postgresql.go +++ b/plugins/postgresql/postgresql.go @@ -17,6 +17,39 @@ type Postgresql struct { Servers []*Server } +var sampleConfig = ` +# specify servers via an array of tables +[[postgresql.servers]] + +# specify address via a url matching: +# postgres://[pqgotest[:password]]@localhost?sslmode=[disable|verify-ca|verify-full] +# or a simple string: +# host=localhost user=pqotest password=... sslmode=... +# +# All connection parameters are optional. By default, the host is localhost +# and the user is the currently running user. For localhost, we default +# to sslmode=disable as well. +# + +address = "localhost" + +# A list of databases to pull metrics about. If not specified, metrics for all +# databases are gathered. + +# databases = ["app_production", "blah_testing"] + +# [[postgresql.servers]] +# address = "influx@remoteserver" +` + +func (p *Postgresql) SampleConfig() string { + return sampleConfig +} + +func (p *Postgresql) Description() string { + return "Read metrics from one or many postgresql servers" +} + var localhost = &Server{Address: "sslmode=disable"} func (p *Postgresql) Gather(acc plugins.Accumulator) error { diff --git a/plugins/redis/redis.go b/plugins/redis/redis.go index 75d776a25..b08dcf23b 100644 --- a/plugins/redis/redis.go +++ b/plugins/redis/redis.go @@ -13,14 +13,27 @@ import ( ) type Redis struct { - Disabled bool - Address string - Servers []string + Servers []string c net.Conn buf []byte } +var sampleConfig = ` +# An array of address to gather stats about. Specify an ip on hostname +# with optional port. ie localhost, 10.10.3.33:18832, etc. +# +# If no servers are specified, then localhost is used as the host. +servers = ["localhost"]` + +func (r *Redis) SampleConfig() string { + return sampleConfig +} + +func (r *Redis) Description() string { + return "Read metrics from one or many redis servers" +} + var Tracking = map[string]string{ "uptime_in_seconds": "uptime", "connected_clients": "clients", @@ -59,19 +72,7 @@ var ErrProtocolError = errors.New("redis protocol error") // Reads stats from all configured servers accumulates stats. // Returns one of the errors encountered while gather stats (if any). func (g *Redis) Gather(acc plugins.Accumulator) error { - if g.Disabled { - return nil - } - - var servers []string - - if g.Address != "" { - servers = append(servers, g.Address) - } - - servers = append(servers, g.Servers...) - - if len(servers) == 0 { + if len(g.Servers) == 0 { g.gatherServer(":6379", acc) return nil } @@ -80,7 +81,7 @@ func (g *Redis) Gather(acc plugins.Accumulator) error { var outerr error - for _, serv := range servers { + for _, serv := range g.Servers { wg.Add(1) go func(serv string) { defer wg.Done() diff --git a/plugins/registry.go b/plugins/registry.go index 9f17662a4..632f54c24 100644 --- a/plugins/registry.go +++ b/plugins/registry.go @@ -5,6 +5,8 @@ type Accumulator interface { } type Plugin interface { + SampleConfig() string + Description() string Gather(Accumulator) error } diff --git a/plugins/system/system.go b/plugins/system/system.go index 75822b0ad..a224b7728 100644 --- a/plugins/system/system.go +++ b/plugins/system/system.go @@ -46,6 +46,12 @@ type CPUStats struct { ps PS } +func (_ *CPUStats) Description() string { + return "Read metrics about cpu usage" +} + +func (_ *CPUStats) SampleConfig() string { return "" } + func (s *CPUStats) Gather(acc plugins.Accumulator) error { times, err := s.ps.CPUTimes() if err != nil { @@ -77,6 +83,12 @@ type DiskStats struct { ps PS } +func (_ *DiskStats) Description() string { + return "Read metrics about disk usage by mount point" +} + +func (_ *DiskStats) SampleConfig() string { return "" } + func (s *DiskStats) Gather(acc plugins.Accumulator) error { disks, err := s.ps.DiskUsage() if err != nil { @@ -103,6 +115,12 @@ type DiskIOStats struct { ps PS } +func (_ *DiskIOStats) Description() string { + return "Read metrics about disk IO by device" +} + +func (_ *DiskIOStats) SampleConfig() string { return "" } + func (s *DiskIOStats) Gather(acc plugins.Accumulator) error { diskio, err := s.ps.DiskIO() if err != nil { @@ -131,6 +149,12 @@ type NetIOStats struct { ps PS } +func (_ *NetIOStats) Description() string { + return "Read metrics about network interface usage" +} + +func (_ *NetIOStats) SampleConfig() string { return "" } + func (s *NetIOStats) Gather(acc plugins.Accumulator) error { netio, err := s.ps.NetIO() if err != nil { @@ -159,6 +183,12 @@ type MemStats struct { ps PS } +func (_ *MemStats) Description() string { + return "Read metrics about memory usage" +} + +func (_ *MemStats) SampleConfig() string { return "" } + func (s *MemStats) Gather(acc plugins.Accumulator) error { vm, err := s.ps.VMStat() if err != nil { @@ -186,6 +216,12 @@ type SwapStats struct { ps PS } +func (_ *SwapStats) Description() string { + return "Read metrics about swap memory usage" +} + +func (_ *SwapStats) SampleConfig() string { return "" } + func (s *SwapStats) Gather(acc plugins.Accumulator) error { swap, err := s.ps.SwapStat() if err != nil { @@ -208,6 +244,12 @@ type DockerStats struct { ps PS } +func (_ *DockerStats) Description() string { + return "Read metrics about docker containers" +} + +func (_ *DockerStats) SampleConfig() string { return "" } + func (s *DockerStats) Gather(acc plugins.Accumulator) error { containers, err := s.ps.DockerStat() if err != nil { @@ -271,6 +313,12 @@ type SystemStats struct { ps PS } +func (_ *SystemStats) Description() string { + return "Read metrics about system load" +} + +func (_ *SystemStats) SampleConfig() string { return "" } + func (s *SystemStats) add(acc plugins.Accumulator, name string, val float64, tags map[string]string) { if val >= 0 {