diff --git a/README.md b/README.md
index 0e0f8db..f59e62b 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,7 @@ Current collectors are:
- s3roundtrip (S3 roundtrip check using AWS SDK)
- redis (redis metrics)
- memcached (memcached metrics)
+- beanstalk (beanstalk metrics)
Install
---
@@ -45,6 +46,7 @@ $ go build ./cmd/oiofs.plugin/oiofs.plugin.go
$ go build ./cmd/s3roundtrip.plugin/s3roundtrip.plugin.go
$ go build ./cmd/redis.plugin/redis.plugin.go
$ go build ./cmd/memcached.plugin/memcached.plugin.go
+$ go build ./cmd/beanstalk.plugin/beanstalk.plugin.go
```
Type in `./[name].plugin -h` to get all available options for each plugin
@@ -61,6 +63,7 @@ $ cp oiofs.plugin /usr/libexec/netdata/plugins.d/
$ cp s3roundtrip.plugin /usr/libexec/netdata/plugins.d/
$ cp redis.plugin /usr/libexec/netdata/plugins.d/
$ cp memcached.plugin /usr/libexec/netdata/plugins.d/
+$ cp beanstalk.plugin /usr/libexec/netdata/plugins.d/
```
Ubuntu Xenial
@@ -73,6 +76,7 @@ $ cp oiofs.plugin /usr/libexec/netdata/plugins.d/
$ cp s3roundtrip.plugin /usr/libexec/netdata/plugins.d/
$ cp redis.plugin /usr/libexec/netdata/plugins.d/
$ cp memcached.plugin /usr/libexec/netdata/plugins.d/
+$ cp beanstalk.plugin /usr/libexec/netdata/plugins.d/
```
Add the following /etc/netdata/netdata.conf:
@@ -105,7 +109,11 @@ Add the following /etc/netdata/netdata.conf:
[plugin:memcached]
update every = 10
- command options = --targets 172.30.2.106:6011:memcached
+ command options = --targets 172.30.2.106:6011
+
+[plugin:beanstalk]
+ update every = 10
+ command options = --targets 172.30.2.106:6014:tube1:tube2:tube3
```
Create and configure plugin config files
diff --git a/beanstalk/beanstalk.go b/beanstalk/beanstalk.go
new file mode 100644
index 0000000..88762f0
--- /dev/null
+++ b/beanstalk/beanstalk.go
@@ -0,0 +1,84 @@
+// OpenIO netdata collectors
+// Copyright (C) 2020 OpenIO SAS
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 3.0 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public
+// License along with this program. If not, see .
+
+package beanstalk
+
+import (
+ "bufio"
+ "net"
+ "strings"
+)
+
+type collector struct {
+ addr string
+ tubes []string
+}
+
+func NewCollector(addr string, tubes []string) *collector {
+ return &collector{
+ addr: addr,
+ tubes: tubes,
+ }
+}
+
+func SendCommand(conn net.Conn, cmd string, prefix string, data map[string]string) error {
+ if _, err := conn.Write([]byte(cmd + "\r\n")); err != nil {
+ return err
+ }
+
+ scanner := bufio.NewScanner(conn)
+ for scanner.Scan() {
+ line := scanner.Text()
+ if line == "" {
+ break
+ }
+ if line == "NOT_FOUND" {
+ break
+ }
+ kv := strings.Split(line, ": ")
+ if len(kv) != 2 {
+ continue
+ }
+ data[prefix+kv[0]] = kv[1]
+ }
+
+ if err := scanner.Err(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (c *collector) Collect() (map[string]string, error) {
+ conn, err := net.Dial("tcp", c.addr)
+ if err != nil {
+ return nil, err
+ }
+ defer conn.Close()
+
+ data := map[string]string{}
+
+ if err = SendCommand(conn, "stats", "", data); err != nil {
+ return nil, err
+ }
+ for _, tube := range c.tubes {
+ if err = SendCommand(conn, "stats-tube "+tube, "_"+tube+"_", data); err != nil {
+ return nil, err
+ }
+ }
+
+ return data, nil
+}
diff --git a/beanstalk/beanstalk.spec.stats-tube_default.txt b/beanstalk/beanstalk.spec.stats-tube_default.txt
new file mode 100644
index 0000000..454d54b
--- /dev/null
+++ b/beanstalk/beanstalk.spec.stats-tube_default.txt
@@ -0,0 +1,17 @@
+OK 267
+---
+name: default
+current-jobs-urgent: 0
+current-jobs-ready: 0
+current-jobs-reserved: 0
+current-jobs-delayed: 0
+current-jobs-buried: 0
+total-jobs: 0
+current-using: 3
+current-watching: 31
+current-waiting: 12
+cmd-delete: 0
+cmd-pause-tube: 0
+pause: 0
+pause-time-left: 0
+
diff --git a/beanstalk/beanstalk.spec.stats-tube_oio-delete.txt b/beanstalk/beanstalk.spec.stats-tube_oio-delete.txt
new file mode 100644
index 0000000..d5e00df
--- /dev/null
+++ b/beanstalk/beanstalk.spec.stats-tube_oio-delete.txt
@@ -0,0 +1,17 @@
+OK 276
+---
+name: oio-delete
+current-jobs-urgent: 0
+current-jobs-ready: 0
+current-jobs-reserved: 0
+current-jobs-delayed: 0
+current-jobs-buried: 0
+total-jobs: 90110
+current-using: 2
+current-watching: 1
+current-waiting: 1
+cmd-delete: 90110
+cmd-pause-tube: 0
+pause: 0
+pause-time-left: 0
+
diff --git a/beanstalk/beanstalk.spec.stats-tube_oio-rebuild.txt b/beanstalk/beanstalk.spec.stats-tube_oio-rebuild.txt
new file mode 100644
index 0000000..3e85541
--- /dev/null
+++ b/beanstalk/beanstalk.spec.stats-tube_oio-rebuild.txt
@@ -0,0 +1,17 @@
+OK 269
+---
+name: oio-rebuild
+current-jobs-urgent: 0
+current-jobs-ready: 0
+current-jobs-reserved: 0
+current-jobs-delayed: 0
+current-jobs-buried: 0
+total-jobs: 0
+current-using: 2
+current-watching: 2
+current-waiting: 1
+cmd-delete: 0
+cmd-pause-tube: 0
+pause: 0
+pause-time-left: 0
+
diff --git a/beanstalk/beanstalk.spec.stats-tube_oio.txt b/beanstalk/beanstalk.spec.stats-tube_oio.txt
new file mode 100644
index 0000000..f98cb84
--- /dev/null
+++ b/beanstalk/beanstalk.spec.stats-tube_oio.txt
@@ -0,0 +1,17 @@
+OK 274
+---
+name: oio
+current-jobs-urgent: 0
+current-jobs-ready: 0
+current-jobs-reserved: 0
+current-jobs-delayed: 0
+current-jobs-buried: 0
+total-jobs: 640670
+current-using: 24
+current-watching: 10
+current-waiting: 10
+cmd-delete: 640670
+cmd-pause-tube: 0
+pause: 0
+pause-time-left: 0
+
diff --git a/beanstalk/beanstalk.spec.stats.txt b/beanstalk/beanstalk.spec.stats.txt
new file mode 100644
index 0000000..5db7bcc
--- /dev/null
+++ b/beanstalk/beanstalk.spec.stats.txt
@@ -0,0 +1,51 @@
+OK 971
+---
+current-jobs-urgent: 0
+current-jobs-ready: 0
+current-jobs-reserved: 0
+current-jobs-delayed: 0
+current-jobs-buried: 0
+cmd-put: 728737
+cmd-peek: 0
+cmd-peek-ready: 0
+cmd-peek-delayed: 0
+cmd-peek-buried: 0
+cmd-reserve: 728752
+cmd-reserve-with-timeout: 173970
+cmd-delete: 728737
+cmd-release: 4
+cmd-use: 28
+cmd-watch: 13
+cmd-ignore: 0
+cmd-bury: 0
+cmd-kick: 0
+cmd-touch: 0
+cmd-stats: 128258
+cmd-stats-job: 0
+cmd-stats-tube: 129094
+cmd-list-tubes: 32282
+cmd-list-tube-used: 0
+cmd-list-tubes-watched: 0
+cmd-pause-tube: 0
+job-timeouts: 0
+total-jobs: 728737
+max-job-size: 65535
+current-tubes: 4
+current-connections: 31
+current-producers: 10
+current-workers: 12
+current-waiting: 12
+total-connections: 122597
+pid: 2892
+version: 1.10
+rusage-utime: 35.948646
+rusage-stime: 82.499803
+uptime: 348107
+binlog-oldest-index: 149
+binlog-current-index: 149
+binlog-records-migrated: 0
+binlog-records-written: 1457478
+binlog-max-size: 10240000
+id: fe6081983c4b33dd
+hostname: node-1.novalocal
+
diff --git a/beanstalk/beanstalk_test.go b/beanstalk/beanstalk_test.go
new file mode 100644
index 0000000..44a3061
--- /dev/null
+++ b/beanstalk/beanstalk_test.go
@@ -0,0 +1,200 @@
+// OpenIO netdata collectors
+// Copyright (C) 2020 OpenIO SAS
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 3.0 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public
+// License along with this program. If not, see .
+
+package beanstalk
+
+import (
+ "bufio"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "net"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+type testServer struct {
+}
+
+var expected = map[string]string{
+ "_default_cmd-delete": "0",
+ "_default_cmd-pause-tube": "0",
+ "_default_current-jobs-buried": "0",
+ "_default_current-jobs-delayed": "0",
+ "_default_current-jobs-ready": "0",
+ "_default_current-jobs-reserved": "0",
+ "_default_current-jobs-urgent": "0",
+ "_default_current-using": "3",
+ "_default_current-waiting": "12",
+ "_default_current-watching": "31",
+ "_default_name": "default",
+ "_default_pause": "0",
+ "_default_pause-time-left": "0",
+ "_default_total-jobs": "0",
+ "_oio-delete_cmd-delete": "90110",
+ "_oio-delete_cmd-pause-tube": "0",
+ "_oio-delete_current-jobs-buried": "0",
+ "_oio-delete_current-jobs-delayed": "0",
+ "_oio-delete_current-jobs-ready": "0",
+ "_oio-delete_current-jobs-reserved": "0",
+ "_oio-delete_current-jobs-urgent": "0",
+ "_oio-delete_current-using": "2",
+ "_oio-delete_current-waiting": "1",
+ "_oio-delete_current-watching": "1",
+ "_oio-delete_name": "oio-delete",
+ "_oio-delete_pause": "0",
+ "_oio-delete_pause-time-left": "0",
+ "_oio-delete_total-jobs": "90110",
+ "_oio-rebuild_cmd-delete": "0",
+ "_oio-rebuild_cmd-pause-tube": "0",
+ "_oio-rebuild_current-jobs-buried": "0",
+ "_oio-rebuild_current-jobs-delayed": "0",
+ "_oio-rebuild_current-jobs-ready": "0",
+ "_oio-rebuild_current-jobs-reserved": "0",
+ "_oio-rebuild_current-jobs-urgent": "0",
+ "_oio-rebuild_current-using": "2",
+ "_oio-rebuild_current-waiting": "1",
+ "_oio-rebuild_current-watching": "2",
+ "_oio-rebuild_name": "oio-rebuild",
+ "_oio-rebuild_pause": "0",
+ "_oio-rebuild_pause-time-left": "0",
+ "_oio-rebuild_total-jobs": "0",
+ "_oio_cmd-delete": "640670",
+ "_oio_cmd-pause-tube": "0",
+ "_oio_current-jobs-buried": "0",
+ "_oio_current-jobs-delayed": "0",
+ "_oio_current-jobs-ready": "0",
+ "_oio_current-jobs-reserved": "0",
+ "_oio_current-jobs-urgent": "0",
+ "_oio_current-using": "24",
+ "_oio_current-waiting": "10",
+ "_oio_current-watching": "10",
+ "_oio_name": "oio",
+ "_oio_pause": "0",
+ "_oio_pause-time-left": "0",
+ "_oio_total-jobs": "640670",
+ "binlog-current-index": "149",
+ "binlog-max-size": "10240000",
+ "binlog-oldest-index": "149",
+ "binlog-records-migrated": "0",
+ "binlog-records-written": "1457478",
+ "cmd-bury": "0",
+ "cmd-delete": "728737",
+ "cmd-ignore": "0",
+ "cmd-kick": "0",
+ "cmd-list-tube-used": "0",
+ "cmd-list-tubes": "32282",
+ "cmd-list-tubes-watched": "0",
+ "cmd-pause-tube": "0",
+ "cmd-peek": "0",
+ "cmd-peek-buried": "0",
+ "cmd-peek-delayed": "0",
+ "cmd-peek-ready": "0",
+ "cmd-put": "728737",
+ "cmd-release": "4",
+ "cmd-reserve": "728752",
+ "cmd-reserve-with-timeout": "173970",
+ "cmd-stats": "128258",
+ "cmd-stats-job": "0",
+ "cmd-stats-tube": "129094",
+ "cmd-touch": "0",
+ "cmd-use": "28",
+ "cmd-watch": "13",
+ "current-connections": "31",
+ "current-jobs-buried": "0",
+ "current-jobs-delayed": "0",
+ "current-jobs-ready": "0",
+ "current-jobs-reserved": "0",
+ "current-jobs-urgent": "0",
+ "current-producers": "10",
+ "current-tubes": "4",
+ "current-waiting": "12",
+ "current-workers": "12",
+ "hostname": "node-1.novalocal",
+ "id": "fe6081983c4b33dd",
+ "job-timeouts": "0",
+ "max-job-size": "65535",
+ "pid": "2892",
+ "rusage-stime": "82.499803",
+ "rusage-utime": "35.948646",
+ "total-connections": "122597",
+ "total-jobs": "728737",
+ "uptime": "348107",
+ "version": "1.10",
+}
+
+func newTestServer() *testServer {
+ return &testServer{}
+}
+
+func (s *testServer) Run(l net.Listener) {
+ for {
+ conn, err := l.Accept()
+ if err != nil {
+ log.Printf("WARN: %s", err)
+ return
+ }
+ go s.handleConn(conn)
+ }
+}
+
+func (s *testServer) handleConn(conn net.Conn) {
+ defer conn.Close()
+ out := bufio.NewWriter(conn)
+ scanner := bufio.NewScanner(conn)
+ for scanner.Scan() {
+ line := scanner.Text()
+ b, err := ioutil.ReadFile("./beanstalk.spec." + strings.Replace(line, " ", "_", -1) + ".txt")
+ if err != nil {
+ fmt.Print(err)
+ break
+ }
+ _, err = out.Write(b)
+ if err != nil {
+ fmt.Print(err)
+ break
+ }
+ out.Flush()
+ }
+}
+
+func TestBeanstalkCollector(t *testing.T) {
+ l, err := net.Listen("tcp", "127.0.0.1:0")
+
+ if err != nil {
+ t.Fatalf("listen error: %v", err)
+ }
+ beanstalk := newTestServer()
+ go beanstalk.Run(l)
+
+ var tubes = []string{"default", "oio", "oio-delete", "oio-rebuild", "prout"}
+ collector := NewCollector(l.Addr().String(), tubes)
+ data, err := collector.Collect()
+ if err != nil {
+ t.Fatalf("unexpected Collect error: %v", err)
+ }
+
+ if !reflect.DeepEqual(data, expected) {
+ t.Fatalf("unexpected result got\n%v\nexpected\n%v\n", data, expected)
+ }
+
+ l.Close()
+ _, err = collector.Collect()
+ if err == nil {
+ t.Fatalf("expected error")
+ }
+}
diff --git a/cmd/beanstalk/beanstalk.plugin.go b/cmd/beanstalk/beanstalk.plugin.go
new file mode 100644
index 0000000..c5e0a9e
--- /dev/null
+++ b/cmd/beanstalk/beanstalk.plugin.go
@@ -0,0 +1,131 @@
+// OpenIO netdata collectors
+// Copyright (C) 2020 OpenIO SAS
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 3.0 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public
+// License along with this program. If not, see .
+
+package main
+
+import (
+ "flag"
+ "log"
+ "oionetdata/beanstalk"
+ "oionetdata/collector"
+ "oionetdata/netdata"
+ "os"
+ "strings"
+ "time"
+)
+
+func main() {
+ if len(os.Args) < 2 {
+ log.Fatalf("argument required")
+ }
+ var targets string
+ fs := flag.NewFlagSet("", flag.ExitOnError)
+ fs.StringVar(&targets, "targets", "", "Comma separated list of Redis IP:PORT")
+ err := fs.Parse(os.Args[2:])
+ if err != nil {
+ log.Fatalln("ERROR: Beanstalk plugin: Could not parse args", err)
+ }
+ intervalSeconds := collector.ParseIntervalSeconds(os.Args[1])
+
+ if targets == "" {
+ log.Fatalln("ERROR: Beanstalk plugin: missing targets")
+ }
+
+ writer := netdata.NewDefaultWriter()
+ worker := netdata.NewWorker(time.Duration(intervalSeconds)*time.Second, writer)
+
+ for _, target := range strings.Split(targets, ",") {
+ res := strings.Split(target, ":")
+ if len(res) < 2 {
+ log.Fatalln("Invalid parameter", target, "must be IP:PORT[:tube1][:tube2]...")
+ }
+ addr := res[0] + ":" + res[1]
+ tubes := res[2:]
+ collector := beanstalk.NewCollector(addr, tubes)
+ worker.AddCollector(collector)
+ instance := "beanstalk." + addr + ":global"
+
+ c := netdata.NewChart(instance, "jobs", "", "", "", "general", "beanstalk.job")
+ c.AddDimension("current-jobs-urgent", "urgent", netdata.AbsoluteAlgorithm)
+ c.AddDimension("current-jobs-ready", "ready", netdata.AbsoluteAlgorithm)
+ c.AddDimension("current-jobs-reserved", "reserved", netdata.AbsoluteAlgorithm)
+ c.AddDimension("current-jobs-delayed", "delayed", netdata.AbsoluteAlgorithm)
+ c.AddDimension("current-jobs-buried", "buried", netdata.AbsoluteAlgorithm)
+ c.AddDimension("total-jobs", "total", netdata.IncrementalAlgorithm)
+ c.AddDimension("jobs-timeouts", "timeouts", netdata.IncrementalAlgorithm)
+ worker.AddChart(c, collector)
+
+ c = netdata.NewChart(instance, "commands", "", "", "", "general", "beanstalk.commands")
+ c.AddDimension("cmd-put", "put", netdata.IncrementalAlgorithm)
+ c.AddDimension("cmd-peek", "peek", netdata.IncrementalAlgorithm)
+ c.AddDimension("cmd-peek-ready", "peek-ready", netdata.IncrementalAlgorithm)
+ c.AddDimension("cmd-peek-delayed", "peek-delayed", netdata.IncrementalAlgorithm)
+ c.AddDimension("cmd-peek-buried", "peek-buried", netdata.IncrementalAlgorithm)
+ c.AddDimension("cmd-reserve", "reserve", netdata.IncrementalAlgorithm)
+ c.AddDimension("cmd-use", "use", netdata.IncrementalAlgorithm)
+ c.AddDimension("cmd-watch", "watch", netdata.IncrementalAlgorithm)
+ c.AddDimension("cmd-ignore", "ignore", netdata.IncrementalAlgorithm)
+ c.AddDimension("cmd-delete", "delete", netdata.IncrementalAlgorithm)
+ c.AddDimension("cmd-release", "release", netdata.IncrementalAlgorithm)
+ c.AddDimension("cmd-bury", "bury", netdata.IncrementalAlgorithm)
+ c.AddDimension("cmd-kick", "kick", netdata.IncrementalAlgorithm)
+ c.AddDimension("cmd-stats", "stats", netdata.IncrementalAlgorithm)
+ c.AddDimension("cmd-stats-job", "stats-job", netdata.IncrementalAlgorithm)
+ c.AddDimension("cmd-stats-tube", "stats-tube", netdata.IncrementalAlgorithm)
+ c.AddDimension("cmd-list-tubes", "list-tubes", netdata.IncrementalAlgorithm)
+ c.AddDimension("cmd-list-tubes-used", "list-tubes-used", netdata.IncrementalAlgorithm)
+ c.AddDimension("cmd-list-tubes-watched", "list-tubes-watched", netdata.IncrementalAlgorithm)
+ c.AddDimension("cmd-pause-tube", "pause-tube", netdata.IncrementalAlgorithm)
+ worker.AddChart(c, collector)
+
+ c = netdata.NewChart(instance, "tubes", "", "", "", "general", "beanstalk.tubes")
+ c.AddDimension("current-tubes", "current", netdata.AbsoluteAlgorithm)
+ worker.AddChart(c, collector)
+
+ c = netdata.NewChart(instance, "connections", "", "", "", "general", "beanstalk.connections")
+ c.AddDimension("current-connections", "open", netdata.AbsoluteAlgorithm)
+ c.AddDimension("current-producers", "producers", netdata.AbsoluteAlgorithm)
+ c.AddDimension("current-workers", "workers", netdata.AbsoluteAlgorithm)
+ c.AddDimension("current-waiting", "waiting", netdata.AbsoluteAlgorithm)
+ c.AddDimension("total-connections", "total", netdata.IncrementalAlgorithm)
+ worker.AddChart(c, collector)
+
+ c = netdata.NewChart(instance, "binlog", "", "", "", "general", "beanstalk.binlog")
+ c.AddDimension("binlog-records-written", "written", netdata.IncrementalAlgorithm)
+ c.AddDimension("binlog-records-migrated", "compaction", netdata.IncrementalAlgorithm)
+ worker.AddChart(c, collector)
+
+ for _, tube := range tubes {
+ instance = "beanstalk." + addr + ":" + tube
+ c = netdata.NewChart(instance, "jobs", "", "", "", tube, "beanstalk.job")
+ c.AddDimension("_"+tube+"_current-jobs-urgent", "urgent", netdata.AbsoluteAlgorithm)
+ c.AddDimension("_"+tube+"_current-jobs-ready", "ready", netdata.AbsoluteAlgorithm)
+ c.AddDimension("_"+tube+"_current-jobs-reserved", "reserved", netdata.AbsoluteAlgorithm)
+ c.AddDimension("_"+tube+"_current-jobs-delayed", "delayed", netdata.AbsoluteAlgorithm)
+ c.AddDimension("_"+tube+"_current-jobs-buried", "buried", netdata.AbsoluteAlgorithm)
+ c.AddDimension("_"+tube+"_total-jobs", "total", netdata.IncrementalAlgorithm)
+ worker.AddChart(c, collector)
+
+ c = netdata.NewChart(instance, "connections", "", "", "", tube, "beanstalk.connections")
+ c.AddDimension("_"+tube+"_current-using", "using", netdata.AbsoluteAlgorithm)
+ c.AddDimension("_"+tube+"_current-waiting", "waiting", netdata.AbsoluteAlgorithm)
+ c.AddDimension("_"+tube+"_current-watching", "watching", netdata.AbsoluteAlgorithm)
+ worker.AddChart(c, collector)
+ }
+ }
+
+ worker.Run()
+}