From 51c99d5b67f7a6d58d801e4dfcb041f3ade74fb6 Mon Sep 17 00:00:00 2001
From: Patrick Hemmer <phemmer@users.noreply.github.com>
Date: Fri, 31 Mar 2017 17:01:02 -0400
Subject: [PATCH] add support for linux sysctl fs metrics (#2609)

---
 CHANGELOG.md                                  |  1 +
 README.md                                     |  1 +
 .../inputs/system/LINUX_SYSCTL_FS_README.md   |  9 ++
 plugins/inputs/system/linux_sysctl_fs.go      | 88 +++++++++++++++++++
 plugins/inputs/system/linux_sysctl_fs_test.go | 41 +++++++++
 5 files changed, 140 insertions(+)
 create mode 100644 plugins/inputs/system/LINUX_SYSCTL_FS_README.md
 create mode 100644 plugins/inputs/system/linux_sysctl_fs.go
 create mode 100644 plugins/inputs/system/linux_sysctl_fs_test.go

diff --git a/CHANGELOG.md b/CHANGELOG.md
index cf7c31c4..fa4b820c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -62,6 +62,7 @@ be deprecated eventually.
 - [#1948](https://github.com/influxdata/telegraf/pull/1948): Support adding SNMP table indexes as tags.
 - [#2332](https://github.com/influxdata/telegraf/pull/2332): Add Elasticsearch 5.x output
 - [#2587](https://github.com/influxdata/telegraf/pull/2587): Add json timestamp units configurability
+- [#2597](https://github.com/influxdata/telegraf/issues/2597): Add support for Linux sysctl-fs metrics.
 
 ### Bugfixes
 
diff --git a/README.md b/README.md
index 90686271..55154e36 100644
--- a/README.md
+++ b/README.md
@@ -174,6 +174,7 @@ configuration options.
     * processes
     * kernel (/proc/stat)
     * kernel (/proc/vmstat)
+    * linux_sysctl_fs (/proc/sys/fs)
 
 Telegraf can also collect metrics via the following service plugins:
 
diff --git a/plugins/inputs/system/LINUX_SYSCTL_FS_README.md b/plugins/inputs/system/LINUX_SYSCTL_FS_README.md
new file mode 100644
index 00000000..e9341c32
--- /dev/null
+++ b/plugins/inputs/system/LINUX_SYSCTL_FS_README.md
@@ -0,0 +1,9 @@
+# Linux Sysctl FS Input
+
+The linux_sysctl_fs input provides Linux system level file metrics. The documentation on these fields can be found at https://www.kernel.org/doc/Documentation/sysctl/fs.txt.
+
+Example output:
+
+```
+> linux_sysctl_fs,host=foo dentry-want-pages=0i,file-max=44222i,aio-max-nr=65536i,inode-preshrink-nr=0i,dentry-nr=64340i,dentry-unused-nr=55274i,file-nr=1568i,aio-nr=0i,inode-nr=35952i,inode-free-nr=12957i,dentry-age-limit=45i 1490982022000000000
+```
diff --git a/plugins/inputs/system/linux_sysctl_fs.go b/plugins/inputs/system/linux_sysctl_fs.go
new file mode 100644
index 00000000..93e426e7
--- /dev/null
+++ b/plugins/inputs/system/linux_sysctl_fs.go
@@ -0,0 +1,88 @@
+package system
+
+import (
+	"bytes"
+	"io/ioutil"
+	"strconv"
+
+	"github.com/influxdata/telegraf"
+	"github.com/influxdata/telegraf/plugins/inputs"
+)
+
+// https://www.kernel.org/doc/Documentation/sysctl/fs.txt
+type SysctlFS struct {
+	path string
+}
+
+var sysctlFSDescription = `Provides Linux sysctl fs metrics`
+var sysctlFSSampleConfig = ``
+
+func (_ SysctlFS) Description() string {
+	return sysctlFSDescription
+}
+func (_ SysctlFS) SampleConfig() string {
+	return sysctlFSSampleConfig
+}
+
+func (sfs *SysctlFS) gatherList(file string, fields map[string]interface{}, fieldNames ...string) error {
+	bs, err := ioutil.ReadFile(sfs.path + "/" + file)
+	if err != nil {
+		return err
+	}
+
+	bsplit := bytes.Split(bytes.TrimRight(bs, "\n"), []byte{'\t'})
+	for i, name := range fieldNames {
+		if i >= len(bsplit) {
+			break
+		}
+		if name == "" {
+			continue
+		}
+
+		v, err := strconv.ParseUint(string(bsplit[i]), 10, 64)
+		if err != nil {
+			return err
+		}
+		fields[name] = v
+	}
+
+	return nil
+}
+
+func (sfs *SysctlFS) gatherOne(name string, fields map[string]interface{}) error {
+	bs, err := ioutil.ReadFile(sfs.path + "/" + name)
+	if err != nil {
+		return err
+	}
+
+	v, err := strconv.ParseUint(string(bytes.TrimRight(bs, "\n")), 10, 64)
+	if err != nil {
+		return err
+	}
+
+	fields[name] = v
+	return nil
+}
+
+func (sfs *SysctlFS) Gather(acc telegraf.Accumulator) error {
+	fields := map[string]interface{}{}
+
+	for _, n := range []string{"aio-nr", "aio-max-nr", "dquot-nr", "dquot-max", "super-nr", "super-max"} {
+		sfs.gatherOne(n, fields)
+	}
+
+	sfs.gatherList("inode-state", fields, "inode-nr", "inode-free-nr", "inode-preshrink-nr")
+	sfs.gatherList("dentry-state", fields, "dentry-nr", "dentry-unused-nr", "dentry-age-limit", "dentry-want-pages")
+	sfs.gatherList("file-nr", fields, "file-nr", "", "file-max")
+
+	acc.AddFields("linux_sysctl_fs", fields, nil)
+	return nil
+}
+
+func init() {
+	inputs.Add("linux_sysctl_fs", func() telegraf.Input {
+		return &SysctlFS{
+			path: "/proc/sys/fs",
+		}
+	})
+}
diff --git a/plugins/inputs/system/linux_sysctl_fs_test.go b/plugins/inputs/system/linux_sysctl_fs_test.go
new file mode 100644
index 00000000..6561465c
--- /dev/null
+++ b/plugins/inputs/system/linux_sysctl_fs_test.go
@@ -0,0 +1,41 @@
+package system
+
+import (
+	"io/ioutil"
+	"os"
+	"testing"
+
+	"github.com/influxdata/telegraf/testutil"
+	"github.com/stretchr/testify/require"
+)
+
+func TestSysctlFSGather(t *testing.T) {
+	td, err := ioutil.TempDir("", "")
+	require.NoError(t, err)
+	defer os.RemoveAll(td)
+
+	require.NoError(t, ioutil.WriteFile(td+"/aio-nr", []byte("100\n"), 0644))
+	require.NoError(t, ioutil.WriteFile(td+"/aio-max-nr", []byte("101\n"), 0644))
+	require.NoError(t, ioutil.WriteFile(td+"/super-nr", []byte("102\n"), 0644))
+	require.NoError(t, ioutil.WriteFile(td+"/super-max", []byte("103\n"), 0644))
+	require.NoError(t, ioutil.WriteFile(td+"/file-nr", []byte("104\t0\t106\n"), 0644))
+	require.NoError(t, ioutil.WriteFile(td+"/inode-state", []byte("107\t108\t109\t0\t0\t0\t0\n"), 0644))
+
+	sfs := &SysctlFS{
+		path: td,
+	}
+	var acc testutil.Accumulator
+	require.NoError(t, sfs.Gather(&acc))
+
+	acc.AssertContainsFields(t, "linux_sysctl_fs", map[string]interface{}{
+		"aio-nr":             uint64(100),
+		"aio-max-nr":         uint64(101),
+		"super-nr":           uint64(102),
+		"super-max":          uint64(103),
+		"file-nr":            uint64(104),
+		"file-max":           uint64(106),
+		"inode-nr":           uint64(107),
+		"inode-free-nr":      uint64(108),
+		"inode-preshrink-nr": uint64(109),
+	})
+}
-- 
GitLab