dovecot: enable global and user queries

Breaks dovecot configuration format. Not tested.
This commit is contained in:
Martti Rannanjärvi 2016-04-06 15:04:01 +03:00
parent 1a612bcae9
commit d317d4dd63
2 changed files with 50 additions and 32 deletions

View File

@ -15,8 +15,9 @@ import (
) )
type Dovecot struct { type Dovecot struct {
Type string
Filters []string
Servers []string Servers []string
Domains []string
} }
func (d *Dovecot) Description() string { func (d *Dovecot) Description() string {
@ -30,12 +31,18 @@ var sampleConfig = `
## ##
## If no servers are specified, then localhost is used as the host. ## If no servers are specified, then localhost is used as the host.
servers = ["localhost:24242"] servers = ["localhost:24242"]
## Only collect metrics for these domains, collect all if empty ## Type is one of "user", "domain", "ip", or "global"
domains = [] type = "global"
## Wildcard matches like "*.com". An empty string "" is same as "*"
filters = [""]
` `
var defaultTimeout = time.Second * time.Duration(5) var defaultTimeout = time.Second * time.Duration(5)
var validQuery = map[string]bool{
"user": true, "domain": true, "global": true, "ip": true,
}
func (d *Dovecot) SampleConfig() string { return sampleConfig } func (d *Dovecot) SampleConfig() string { return sampleConfig }
const defaultPort = "24242" const defaultPort = "24242"
@ -43,6 +50,11 @@ const defaultPort = "24242"
// Reads stats from all configured servers. // Reads stats from all configured servers.
func (d *Dovecot) Gather(acc telegraf.Accumulator) error { func (d *Dovecot) Gather(acc telegraf.Accumulator) error {
if !validQuery[d.Type] {
return fmt.Errorf("Error: %s is not a valid query type\n",
d.Type)
}
if len(d.Servers) == 0 { if len(d.Servers) == 0 {
d.Servers = append(d.Servers, "127.0.0.1:24242") d.Servers = append(d.Servers, "127.0.0.1:24242")
} }
@ -51,18 +63,15 @@ func (d *Dovecot) Gather(acc telegraf.Accumulator) error {
var outerr error var outerr error
var domains = make(map[string]bool)
for _, dom := range d.Domains {
domains[dom] = true
}
for _, serv := range d.Servers { for _, serv := range d.Servers {
wg.Add(1) for _, filter := range d.Filters {
go func(serv string) { wg.Add(1)
defer wg.Done() go func(serv string, filt string) {
outerr = d.gatherServer(serv, acc, domains) defer wg.Done()
}(serv) outerr = d.gatherServer(serv, acc,
d.Type, filt)
}(serv, filter)
}
} }
wg.Wait() wg.Wait()
@ -70,7 +79,8 @@ func (d *Dovecot) Gather(acc telegraf.Accumulator) error {
return outerr return outerr
} }
func (d *Dovecot) gatherServer(addr string, acc telegraf.Accumulator, doms map[string]bool) error { func (d *Dovecot) gatherServer(addr string, acc telegraf.Accumulator,
qtype string, filter string) error {
_, _, err := net.SplitHostPort(addr) _, _, err := net.SplitHostPort(addr)
if err != nil { if err != nil {
return fmt.Errorf("Error: %s on url %s\n", err, addr) return fmt.Errorf("Error: %s on url %s\n", err, addr)
@ -85,17 +95,24 @@ func (d *Dovecot) gatherServer(addr string, acc telegraf.Accumulator, doms map[s
// Extend connection // Extend connection
c.SetDeadline(time.Now().Add(defaultTimeout)) c.SetDeadline(time.Now().Add(defaultTimeout))
c.Write([]byte("EXPORT\tdomain\n")) msg := fmt.Sprintf("EXPORT\t%s", qtype)
if len(filter) > 0 {
msg += fmt.Sprintf("\t%s=%s", qtype, filter)
}
msg += "\n"
c.Write([]byte(msg))
var buf bytes.Buffer var buf bytes.Buffer
io.Copy(&buf, c) io.Copy(&buf, c)
// buf := bufio.NewReader(c) // buf := bufio.NewReader(c)
host, _, _ := net.SplitHostPort(addr) host, _, _ := net.SplitHostPort(addr)
return gatherStats(&buf, acc, doms, host) return gatherStats(&buf, acc, host, qtype)
} }
func gatherStats(buf *bytes.Buffer, acc telegraf.Accumulator, doms map[string]bool, host string) error { func gatherStats(buf *bytes.Buffer, acc telegraf.Accumulator,
host string, qtype string) error {
lines := strings.Split(buf.String(), "\n") lines := strings.Split(buf.String(), "\n")
head := strings.Split(lines[0], "\t") head := strings.Split(lines[0], "\t")
@ -107,17 +124,13 @@ func gatherStats(buf *bytes.Buffer, acc telegraf.Accumulator, doms map[string]bo
} }
val := strings.Split(vals[i], "\t") val := strings.Split(vals[i], "\t")
fields := make(map[string]interface{}) fields := make(map[string]interface{})
if len(doms) > 0 && !doms[val[0]] { tags := map[string]string{"server": host}
continue
}
tags := map[string]string{"server": host, "domain": val[0]}
for n := range val { for n := range val {
switch head[n] { switch head[n] {
case "domain": case qtype:
continue tags[head[n]] = val[n]
// fields[head[n]] = val[n]
case "user_cpu", "sys_cpu", "clock_time": case "user_cpu", "sys_cpu", "clock_time":
fields[head[n]] = secParser(val[n]) fields[head[n]], _ = strconv.ParseFloat(val[n], 64)
case "reset_timestamp", "last_update": case "reset_timestamp", "last_update":
fields[head[n]] = timeParser(val[n]) fields[head[n]] = timeParser(val[n])
default: default:

View File

@ -4,9 +4,11 @@ import (
"bytes" "bytes"
"testing" "testing"
"time" "time"
"strconv"
"github.com/influxdata/telegraf/testutil" "github.com/influxdata/telegraf/testutil"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/stretchr/testify/assert"
) )
func TestDovecot(t *testing.T) { func TestDovecot(t *testing.T) {
@ -16,14 +18,11 @@ func TestDovecot(t *testing.T) {
} }
var acc testutil.Accumulator var acc testutil.Accumulator
tags := map[string]string{"server": "dovecot.test", "domain": "domain.test"} tags := map[string]string{"server": "dovecot.test",
"domain": "domain.bad"}
buf := bytes.NewBufferString(sampleStats) buf := bytes.NewBufferString(sampleStats)
var doms = map[string]bool{ err := gatherStats(buf, &acc, "dovecot.test", "domain")
"domain.test": true,
}
err := gatherStats(buf, &acc, doms, "dovecot.test")
require.NoError(t, err) require.NoError(t, err)
fields := map[string]interface{}{ fields := map[string]interface{}{
@ -56,6 +55,12 @@ func TestDovecot(t *testing.T) {
} }
func TestFloatConversion(t *testing.T) {
f, err := strconv.ParseFloat("4326001931528183.495762", 64)
require.NoError(t, err)
assert.Equal(t, 4326001931528183.495762, f)
}
const sampleStats = `domain reset_timestamp last_update num_logins num_cmds num_connected_sessions user_cpu sys_cpu clock_time min_faults maj_faults vol_cs invol_cs disk_input disk_output read_count read_bytes write_count write_bytes mail_lookup_path mail_lookup_attr mail_read_count mail_read_bytes mail_cache_hits const sampleStats = `domain reset_timestamp last_update num_logins num_cmds num_connected_sessions user_cpu sys_cpu clock_time min_faults maj_faults vol_cs invol_cs disk_input disk_output read_count read_bytes write_count write_bytes mail_lookup_path mail_lookup_attr mail_read_count mail_read_bytes mail_cache_hits
domain.bad 1453970076 1454603947.383029 10749 33828 0 177988.524000 148071.772000 7531838964717.193706 212491179 2125 2190386067 112779200 74487934976 3221808119808 2469948401 5237602841760 1091171292 2951966459802 15363 0 2922 136403379 334372 domain.bad 1453970076 1454603947.383029 10749 33828 0 177988.524000 148071.772000 7531838964717.193706 212491179 2125 2190386067 112779200 74487934976 3221808119808 2469948401 5237602841760 1091171292 2951966459802 15363 0 2922 136403379 334372
domain.test 1453969886 1454603963.039864 7503897 52595715 1204 100831175.372000 83849071.112000 4326001931528183.495762 763950011 1112443 4120386897 3685239306 41679480946688 1819070669176832 2368906465 2957928122981169 3545389615 1666822498251286 24396105 302845 20155768 669946617705 1557255080` domain.test 1453969886 1454603963.039864 7503897 52595715 1204 100831175.372000 83849071.112000 4326001931528183.495762 763950011 1112443 4120386897 3685239306 41679480946688 1819070669176832 2368906465 2957928122981169 3545389615 1666822498251286 24396105 302845 20155768 669946617705 1557255080`