From 7ad8b26297972b54c360ac5219e0b78986e39ddb Mon Sep 17 00:00:00 2001
From: Michele Fadda <mikif70@gmail.com>
Date: Fri, 8 Apr 2016 18:49:39 +0200
Subject: [PATCH] dovecot: enabled global, user and ip queries

---
 plugins/inputs/dovecot/README.md       | 39 +++++++-----
 plugins/inputs/dovecot/dovecot.go      | 66 +++++++++++++-------
 plugins/inputs/dovecot/dovecot_test.go | 84 ++++++++++++++++++++++----
 3 files changed, 138 insertions(+), 51 deletions(-)

diff --git a/plugins/inputs/dovecot/README.md b/plugins/inputs/dovecot/README.md
index 3ec7ac02..865e2449 100644
--- a/plugins/inputs/dovecot/README.md
+++ b/plugins/inputs/dovecot/README.md
@@ -10,20 +10,25 @@ domains. You can read Dovecot's documentation
 ```
 # Read metrics about dovecot servers
 [[inputs.dovecot]]
-  # Dovecot servers
-  #  specify dovecot servers via an address:port list
-  #  e.g.
-  #    localhost:24242
-  #
-  # If no servers are specified, then localhost is used as the host.
+  ## specify dovecot servers via an address:port list
+  ##  e.g.
+  ##    localhost:24242
+  ##
+  ## If no servers are specified, then localhost is used as the host.
   servers = ["localhost:24242"]
-  # Only collect metrics for these domains, collect all if empty
-  domains = []
+  ## Type is one of "user", "domain", "ip", or "global"
+  type = "global"
+  ## Wildcard matches like "*.com". An empty string "" is same as "*"
+  ## If type = "ip" filters should be <IP/network>
+  filters = [""]
 ```
 
 
 ### Tags:
 	server: hostname
+	type: query type
+	ip: ip addr
+	user: username
 	domain: domain name
 
 
@@ -33,7 +38,7 @@ domains. You can read Dovecot's documentation
 	last_update            time.Time
 	num_logins             int64
 	num_cmds               int64
-	num_connected_sessions int64
+	num_connected_sessions int64				## not in <user> type
 	user_cpu               float32
 	sys_cpu                float32
 	clock_time             float64
@@ -57,11 +62,13 @@ domains. You can read Dovecot's documentation
 ### Example Output:
 
 ```
-telegraf -config telegraf.cfg -input-filter dovecot -test
+telegraf -config t.cfg -input-filter dovecot -test
 * Plugin: dovecot, Collection 1
-> dovecot,domain=xxxxx.it,server=dovecot--1.mail.sys clock_time=12105746411632.5,disk_input=115285225472i,disk_output=4885067755520i,invol_cs=169701886i,last_update="2016-02-09 08:49:47.000014113 +0100 CET",mail_cache_hits=441828i,mail_lookup_attr=0i,mail_lookup_path=25323i,mail_read_bytes=241188145i,mail_read_count=11719i,maj_faults=3168i,min_faults=321438988i,num_cmds=51635i,num_connected_sessions=2i,num_logins=17149i,read_bytes=7939026951110i,read_count=3716991752i,reset_timestamp="2016-01-28 09:34:36 +0100 CET",sys_cpu=222595.288,user_cpu=267468.08,vol_cs=3288715920i,write_bytes=4483648967059i,write_count=1640646952i 1455004219924838345
-> dovecot,domain=yyyyy.com,server=dovecot-1.mail.sys clock_time=6650794455331782,disk_input=61957695569920i,disk_output=2638244004487168i,invol_cs=2004805041i,last_update="2016-02-09 08:49:49.000251296 +0100 CET",mail_cache_hits=2499112513i,mail_lookup_attr=506730i,mail_lookup_path=39128227i,mail_read_bytes=1076496874501i,mail_read_count=32615262i,maj_faults=1643304i,min_faults=4216116325i,num_cmds=85785559i,num_connected_sessions=1177i,num_logins=11658255i,read_bytes=4289150974554145i,read_count=1112000703i,reset_timestamp="2016-01-28 09:31:26 +0100 CET",sys_cpu=121125923.032,user_cpu=145561336.428,vol_cs=205451885i,write_bytes=2420130526835796i,write_count=2991367252i 1455004219925152529
-> dovecot,domain=xxxxx.it,server=dovecot-2.mail.sys clock_time=10710826586999.143,disk_input=79792410624i,disk_output=4496066158592i,invol_cs=150426876i,last_update="2016-02-09 08:48:19.000209134 +0100 CET",mail_cache_hits=5480869i,mail_lookup_attr=0i,mail_lookup_path=122563i,mail_read_bytes=340746273i,mail_read_count=44275i,maj_faults=1722i,min_faults=288071875i,num_cmds=50098i,num_connected_sessions=0i,num_logins=16389i,read_bytes=7259551999517i,read_count=3396625369i,reset_timestamp="2016-01-28 09:31:29 +0100 CET",sys_cpu=200762.792,user_cpu=242477.664,vol_cs=2996657358i,write_bytes=4133381575263i,write_count=1497242759i 1455004219924888283
-> dovecot,domain=yyyyy.com,server=dovecot-2.mail.sys clock_time=6522131245483702,disk_input=48259150004224i,disk_output=2754333359087616i,invol_cs=2294595260i,last_update="2016-02-09 08:49:49.000251919 +0100 CET",mail_cache_hits=2139113611i,mail_lookup_attr=520276i,mail_lookup_path=37940318i,mail_read_bytes=1088002215022i,mail_read_count=31350271i,maj_faults=994420i,min_faults=1486260543i,num_cmds=40414997i,num_connected_sessions=978i,num_logins=11259672i,read_bytes=4445546612487315i,read_count=1763534543i,reset_timestamp="2016-01-28 09:31:24 +0100 CET",sys_cpu=123655962.668,user_cpu=149259327.032,vol_cs=4215130546i,write_bytes=2531186030222761i,write_count=2186579650i 1455004219925398372
-```
-
+> dovecot,ip=192.168.0.1,server=dovecot-1.domain.test,type=ip clock_time=0,disk_input=0i,disk_output=0i,invol_cs=0i,last_update="2016-04-08 10:59:47.000208479 +0200 CEST",mail_cache_hits=0i,mail_lookup_attr=0i,mail_lookup_path=0i,mail_read_bytes=0i,mail_read_count=0i,maj_faults=0i,min_faults=0i,num_cmds=12i,num_connected_sessions=0i,num_logins=6i,read_bytes=0i,read_count=0i,reset_timestamp="2016-04-08 10:33:34 +0200 CEST",sys_cpu=0,user_cpu=0,vol_cs=0i,write_bytes=0i,write_count=0i 1460106251633824223
+* Plugin: dovecot, Collection 1
+> dovecot,server=dovecot-1.domain.test,type=user,user=user-1@domain.test clock_time=0.00006,disk_input=405504i,disk_output=77824i,invol_cs=67i,last_update="2016-04-08 11:02:55.000111634 +0200 CEST",mail_cache_hits=26i,mail_lookup_attr=0i,mail_lookup_path=6i,mail_read_bytes=86233i,mail_read_count=5i,maj_faults=0i,min_faults=975i,num_cmds=41i,num_logins=3i,read_bytes=368833i,read_count=394i,reset_timestamp="2016-04-08 11:01:32 +0200 CEST",sys_cpu=0.008,user_cpu=0.004,vol_cs=323i,write_bytes=105086i,write_count=176i 1460106256637049167
+* Plugin: dovecot, Collection 1
+> dovecot,domain=domain.test,server=dovecot-1.domain.test,type=domain clock_time=100896189179847.7,disk_input=6467588263936i,disk_output=17933680439296i,invol_cs=1194808498i,last_update="2016-04-08 11:04:08.000377367 +0200 CEST",mail_cache_hits=46455781i,mail_lookup_attr=0i,mail_lookup_path=571490i,mail_read_bytes=79287033067i,mail_read_count=491243i,maj_faults=16992i,min_faults=1278442541i,num_cmds=606005i,num_connected_sessions=6597i,num_logins=166381i,read_bytes=30231409780721i,read_count=1624912080i,reset_timestamp="2016-04-08 10:28:45 +0200 CEST",sys_cpu=156440.372,user_cpu=216676.476,vol_cs=2749291157i,write_bytes=17097106707594i,write_count=944448998i 1460106261639672622
+* Plugin: dovecot, Collection 1
+> dovecot,server=dovecot-1.domain.test,type=global clock_time=101196971074203.94,disk_input=6493168218112i,disk_output=17978638815232i,invol_cs=1198855447i,last_update="2016-04-08 11:04:13.000379245 +0200 CEST",mail_cache_hits=68192209i,mail_lookup_attr=0i,mail_lookup_path=653861i,mail_read_bytes=86705151847i,mail_read_count=566125i,maj_faults=17208i,min_faults=1286179702i,num_cmds=917469i,num_connected_sessions=8896i,num_logins=174827i,read_bytes=30327690466186i,read_count=1772396430i,reset_timestamp="2016-04-08 10:28:45 +0200 CEST",sys_cpu=157965.692,user_cpu=219337.48,vol_cs=2827615787i,write_bytes=17150837661940i,write_count=992653220i 1460106266642153907
+```
\ No newline at end of file
diff --git a/plugins/inputs/dovecot/dovecot.go b/plugins/inputs/dovecot/dovecot.go
index bf1b2026..0347016d 100644
--- a/plugins/inputs/dovecot/dovecot.go
+++ b/plugins/inputs/dovecot/dovecot.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"fmt"
 	"io"
+	//	"log"
 	"net"
 	"strconv"
 	"strings"
@@ -15,8 +16,9 @@ import (
 )
 
 type Dovecot struct {
+	Type    string
+	Filters []string
 	Servers []string
-	Domains []string
 }
 
 func (d *Dovecot) Description() string {
@@ -30,12 +32,19 @@ var sampleConfig = `
   ##
   ## If no servers are specified, then localhost is used as the host.
   servers = ["localhost:24242"]
-  ## Only collect metrics for these domains, collect all if empty
-  domains = []
+  ## Type is one of "user", "domain", "ip", or "global"
+  type = "global"
+  ## Wildcard matches like "*.com". An empty string "" is same as "*"
+  ## If type = "ip" filters should be <IP/network>
+  filters = [""]
 `
 
 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 }
 
 const defaultPort = "24242"
@@ -43,6 +52,11 @@ const defaultPort = "24242"
 // Reads stats from all configured servers.
 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 {
 		d.Servers = append(d.Servers, "127.0.0.1:24242")
 	}
@@ -51,18 +65,18 @@ func (d *Dovecot) Gather(acc telegraf.Accumulator) error {
 
 	var outerr error
 
-	var domains = make(map[string]bool)
-
-	for _, dom := range d.Domains {
-		domains[dom] = true
+	if len(d.Filters) <= 0 {
+		d.Filters = append(d.Filters, "")
 	}
 
 	for _, serv := range d.Servers {
-		wg.Add(1)
-		go func(serv string) {
-			defer wg.Done()
-			outerr = d.gatherServer(serv, acc, domains)
-		}(serv)
+		for _, filter := range d.Filters {
+			wg.Add(1)
+			go func(serv string, filter string) {
+				defer wg.Done()
+				outerr = d.gatherServer(serv, acc, d.Type, filter)
+			}(serv, filter)
+		}
 	}
 
 	wg.Wait()
@@ -70,7 +84,8 @@ func (d *Dovecot) Gather(acc telegraf.Accumulator) error {
 	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)
 	if err != nil {
 		return fmt.Errorf("Error: %s on url %s\n", err, addr)
@@ -85,17 +100,22 @@ func (d *Dovecot) gatherServer(addr string, acc telegraf.Accumulator, doms map[s
 	// Extend connection
 	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
 	io.Copy(&buf, c)
-	//	buf := bufio.NewReader(c)
 
 	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")
 	head := strings.Split(lines[0], "\t")
@@ -106,16 +126,18 @@ func gatherStats(buf *bytes.Buffer, acc telegraf.Accumulator, doms map[string]bo
 			continue
 		}
 		val := strings.Split(vals[i], "\t")
+
 		fields := make(map[string]interface{})
-		if len(doms) > 0 && !doms[val[0]] {
-			continue
+		tags := map[string]string{"server": host, "type": qtype}
+
+		if qtype != "global" {
+			tags[qtype] = val[0]
 		}
-		tags := map[string]string{"server": host, "domain": val[0]}
+
 		for n := range val {
 			switch head[n] {
-			case "domain":
+			case qtype:
 				continue
-				//				fields[head[n]] = val[n]
 			case "user_cpu", "sys_cpu", "clock_time":
 				fields[head[n]] = secParser(val[n])
 			case "reset_timestamp", "last_update":
diff --git a/plugins/inputs/dovecot/dovecot_test.go b/plugins/inputs/dovecot/dovecot_test.go
index 76425c0d..c801d4f0 100644
--- a/plugins/inputs/dovecot/dovecot_test.go
+++ b/plugins/inputs/dovecot/dovecot_test.go
@@ -15,17 +15,6 @@ func TestDovecot(t *testing.T) {
 		t.Skip("Skipping integration test in short mode")
 	}
 
-	var acc testutil.Accumulator
-	tags := map[string]string{"server": "dovecot.test", "domain": "domain.test"}
-	buf := bytes.NewBufferString(sampleStats)
-
-	var doms = map[string]bool{
-		"domain.test": true,
-	}
-
-	err := gatherStats(buf, &acc, doms, "dovecot.test")
-	require.NoError(t, err)
-
 	fields := map[string]interface{}{
 		"reset_timestamp":        time.Unix(1453969886, 0),
 		"last_update":            time.Unix(1454603963, 39864),
@@ -52,10 +41,79 @@ func TestDovecot(t *testing.T) {
 		"mail_cache_hits":        int64(1557255080),
 	}
 
+	var acc testutil.Accumulator
+
+	// Test type=global
+	tags := map[string]string{"server": "dovecot.test", "type": "global"}
+	buf := bytes.NewBufferString(sampleGlobal)
+
+	err := gatherStats(buf, &acc, "dovecot.test", "global")
+	require.NoError(t, err)
+
+	acc.AssertContainsTaggedFields(t, "dovecot", fields, tags)
+
+	// Test type=domain
+	tags = map[string]string{"server": "dovecot.test", "type": "domain", "domain": "domain.test"}
+	buf = bytes.NewBufferString(sampleDomain)
+
+	err = gatherStats(buf, &acc, "dovecot.test", "domain")
+	require.NoError(t, err)
+
+	acc.AssertContainsTaggedFields(t, "dovecot", fields, tags)
+
+	// Test type=ip
+	tags = map[string]string{"server": "dovecot.test", "type": "ip", "ip": "192.168.0.100"}
+	buf = bytes.NewBufferString(sampleIp)
+
+	err = gatherStats(buf, &acc, "dovecot.test", "ip")
+	require.NoError(t, err)
+
+	acc.AssertContainsTaggedFields(t, "dovecot", fields, tags)
+
+	// Test type=user
+	fields = map[string]interface{}{
+		"reset_timestamp":  time.Unix(1453969886, 0),
+		"last_update":      time.Unix(1454603963, 39864),
+		"num_logins":       int64(7503897),
+		"num_cmds":         int64(52595715),
+		"user_cpu":         1.00831175372e+08,
+		"sys_cpu":          8.3849071112e+07,
+		"clock_time":       4.3260019315281835e+15,
+		"min_faults":       int64(763950011),
+		"maj_faults":       int64(1112443),
+		"vol_cs":           int64(4120386897),
+		"invol_cs":         int64(3685239306),
+		"disk_input":       int64(41679480946688),
+		"disk_output":      int64(1819070669176832),
+		"read_count":       int64(2368906465),
+		"read_bytes":       int64(2957928122981169),
+		"write_count":      int64(3545389615),
+		"write_bytes":      int64(1666822498251286),
+		"mail_lookup_path": int64(24396105),
+		"mail_lookup_attr": int64(302845),
+		"mail_read_count":  int64(20155768),
+		"mail_read_bytes":  int64(669946617705),
+		"mail_cache_hits":  int64(1557255080),
+	}
+
+	tags = map[string]string{"server": "dovecot.test", "type": "user", "user": "user.1@domain.test"}
+	buf = bytes.NewBufferString(sampleUser)
+
+	err = gatherStats(buf, &acc, "dovecot.test", "user")
+	require.NoError(t, err)
+
 	acc.AssertContainsTaggedFields(t, "dovecot", fields, tags)
 
 }
 
-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
+const sampleGlobal = `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
+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`
+
+const sampleDomain = `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.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`
+
+const sampleIp = `ip	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
+192.168.0.100	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`
+
+const sampleUser = `user	reset_timestamp	last_update	num_logins	num_cmds	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
+user.1@domain.test	1453969886	1454603963.039864	7503897	52595715	100831175.372000	83849071.112000	4326001931528183.495762	763950011	1112443	4120386897	3685239306	41679480946688	1819070669176832	2368906465	2957928122981169	3545389615	1666822498251286	24396105	302845	20155768	669946617705	1557255080`
-- 
GitLab