Skip to content

Commit

Permalink
Merge pull request #3 from hawson/plugin_nfs
Browse files Browse the repository at this point in the history
Plugin nfs updates:  docs, include/exclude detailed operation counts.
  • Loading branch information
pmoranga authored Nov 24, 2020
2 parents f6c27f1 + f9a666d commit 3aaa066
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 39 deletions.
29 changes: 23 additions & 6 deletions plugins/inputs/nfsclient/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
# Telegraf plugin: NFSClient

#### Plugin arguments:
- **fullstat** bool: Collect per-operation type metrics
- **fullstat** bool: Collect per-operation type metrics. Defaults to false.
- **include_mounts** list(string): gather metrics for only these mounts. Default is to watch all mounts.
- **exclude_mounts** list(string): gather metrics for all mounts, except those listed in this option. Excludes take precedence over includes.
- **include_operations** list(string): List of specific NFS operations to track. See /proc/self/mountstats (the "per-op statistics" section) for complete lists of valid options for NFSv3 and NFSV4. The default is to gather all metrics, but this is almost certainly *not* what you want (there are 22 operations for NFSv3, and 59 for NFSv4). A 'minimal' list for basic usage: ['READ','WRITE','ACCESS','GETATTR','READDIR','LOOKUP','LOOKUP']
- **exclude_operations** list(string): Gather all metrics, except those listed. Excludes take precedence over includes.

#### Description

Expand Down Expand Up @@ -34,9 +38,6 @@ See references for more details.
- serverexport The full server export, for instance: "nfsserver.example.org:/export"

#### References
[nfsiostat](http://git.linux-nfs.org/?p=steved/nfs-utils.git;a=summary)
[net/sunrpc/stats.c - Linux source code](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/net/sunrpc/stats.c)
[What is in /proc/self/mountstats for NFS mounts: an introduction](https://utcc.utoronto.ca/~cks/space/blog/linux/NFSMountstatsIndex)
1. [nfsiostat](http://git.linux-nfs.org/?p=steved/nfs-utils.git;a=summary)
2. [net/sunrpc/stats.c - Linux source code](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/net/sunrpc/stats.c)
3. [What is in /proc/self/mountstats for NFS mounts: an introduction](https://utcc.utoronto.ca/~cks/space/blog/linux/NFSMountstatsIndex)
Expand Down Expand Up @@ -75,13 +76,14 @@ Most descriptions come from Reference [[3](https://utcc.utoronto.ca/~cks/space/b
- attrinvalidates - (int, count) - How many times an inode has had cached inode attributes invalidated.
- vfsopen - (int, count) - How many times files or directories have been `open()`'d.
- vfslookup - (int, count) - How many name lookups in directories there have been.
- vfspermission - (int, count) - Number of calls to `access()`.
- vfsaccess - (int, count) - Number of calls to `access()`. (formerly called "vfspermission")

- vfsupdatepage - (int, count) - Count of updates (and potential writes) to pages.
- vfsreadpage - (int, count) - Number of pages read.
- vfsreadpages - (int, count) - Count of how many times a _group_ of pages was read (possibly via `mmap()`?).
- vfswritepage - (int, count) - Number of pages written.
- vfswritepages - (int, count) - Count of how many times a _group_ of pages was written (possibly via `mmap()`?)
- vfsreaddir - (int, count) - Count of directory entry reads with getdents(). These reads can be served from cache and don't necessarily imply actual NFS requests.
- vfsgetdents - (int, count) - Count of directory entry reads with getdents(). These reads can be served from cache and don't necessarily imply actual NFS requests. (formerly called "vfsreaddir")
- vfssetattr - (int, count) - How many times we've set attributes on inodes.
- vfsflush - (int, count) - Count of times pending writes have been forcibly flushed to the server.
- vfsfsync - (int, count) - Count of calls to `fsync()` on directories and files.
Expand Down Expand Up @@ -117,3 +119,18 @@ Most descriptions come from Reference [[3](https://utcc.utoronto.ca/~cks/space/b
- [same as nfs_xprt_tcp]
- fields:
- [same as nfs_xprt_tcp, except for connect_count, connect_time, and idle_time]

- nfs_ops
- tags:
- mountpoint - The local mountpoint, for instance: "/var/www"
- serverexport - The full server export, for instance: "nfsserver.example.org:/export"
-fields - in all cases, "OP" is replaced with the uppercase name of the NFS operation, (_e.g._ "READ", "FSINFO", etc)
- OP_ops - (int, count) - Total operations of this type.
- OP_trans - (int, count) - Total transmissions of this type, including retransmissions: OP_ops - OP_trans = total_retransmissions (lower is better).
- OP_timeouts - (int, count) - Number of major timeouts.
- OP_bytes_sent - (int, count) - Bytes received, including headers (should also be close to on-wire size).
- OP_bytes_recv - (int, count) - Bytes sent, including headers (should be close to on-wire size).
- OP_queue_time - (int, milliseconds) - Cumulative time a request waited in the queue before sending this OP type.
- OP_response_time - (int, milliseconds) - Cumulative time waiting for a response for this OP type.
- OP_total_time - (int, milliseconds) - Cumulative time a request waited in the queue before sending.
- OP_errors - (int, count) - Total number operations that complete with tk_status < 0 (usually errors). This is a new field, present in kernel >=5.3, mountstats version 1.1
110 changes: 79 additions & 31 deletions plugins/inputs/nfsclient/nfsclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ import (
)

type NFSClient struct {
Fullstat bool
IncludeMounts []string
ExcludeMounts []string
Fullstat bool
IncludeMounts []string
ExcludeMounts []string
IncludeOperations []string
ExcludeOperations []string
}

var sampleConfig = `
Expand All @@ -27,9 +29,24 @@ var sampleConfig = `
# The pattern (Go regexp) is matched against the mount point (not the
# device being mounted). If include_mounts is set, all mounts are ignored
# unless present in the list. If a mount is listed in both include_mounts
# and exclude_monuts, it is excluded. Go regexp patterns can be used.
# and exclude_mounts, it is excluded. Go regexp patterns can be used.
include_mounts = []
exclude_mounts = []
# List of operations to include or exclude from collecting. This applies
# only when fullstat=true. Symantics are similar to {include,exclude}_mounts:
# the default is to collect everything; when include_operations is set, only
# those OPs are collected; when exclude_operations is set, all are collected
# except those listed. If include and exclude are set, the OP is excluded.
# See /proc/self/mountstats for a list of valid operations; note that
# NFSv3 and NFSv4 have different lists. While it is not possible to
# have different include/exclude lists for NFSv3/4, unused elements
# in the list should be okay. It is possible to have different lists
# for different mountpoints: use mulitple [[input.nfsclient]] stanzas,
# with their own lists. See "include_mounts" above, and be careful of
# duplicate metrics.
include_operations = []
exclude_operations = []
`

func (n *NFSClient) SampleConfig() string {
Expand All @@ -47,13 +64,13 @@ var eventsFields = []string{
"attrinvalidates",
"vfsopen",
"vfslookup",
"vfspermission",
"vfsaccess",
"vfsupdatepage",
"vfsreadpage",
"vfsreadpages",
"vfswritepage",
"vfswritepages",
"vfsreaddir",
"vfsgetdents",
"vfssetattr",
"vfsflush",
"vfsfsync",
Expand Down Expand Up @@ -198,6 +215,7 @@ var nfsopFields = []string{
"queue_time",
"response_time",
"total_time",
"errors",
}

func convert(line []string) []int64 {
Expand All @@ -218,7 +236,7 @@ func in(list []string, val string) bool {
return false
}

func (n *NFSClient) parseStat(mountpoint string, export string, version string, line []string, fullstat bool, acc telegraf.Accumulator) error {
func (n *NFSClient) parseStat(mountpoint string, export string, version string, line []string, fullstat bool, nfs3Ops map[string]bool, nfs4Ops map[string]bool, acc telegraf.Accumulator) error {
tags := map[string]string{"mountpoint": mountpoint, "serverexport": export}
nline := convert(line)
first := strings.Replace(line[0], ":", "", 1)
Expand Down Expand Up @@ -252,42 +270,41 @@ func (n *NFSClient) parseStat(mountpoint string, export string, version string,
acc.AddFields("nfs_xprt_udp", fields, tags)
}
}
} else if version == "3" || version == "4" {
if in(nfs3Fields, first) && len(nline) > 7 {
if first == "READ" {
fields["read_ops"] = nline[0]
fields["read_retrans"] = (nline[1] - nline[0])
fields["read_bytes"] = (nline[3] + nline[4])
fields["read_rtt"] = nline[6]
fields["read_exe"] = nline[7]
acc.AddFields("nfsstat_read", fields, tags)
} else if first == "WRITE" {
fields["write_ops"] = nline[0]
fields["write_retrans"] = (nline[1] - nline[0])
fields["write_bytes"] = (nline[3] + nline[4])
fields["write_rtt"] = nline[6]
fields["write_exe"] = nline[7]
acc.AddFields("nfsstat_write", fields, tags)
}
}
if fullstat && version == "3" {
if in(nfs3Fields, first) && len(nline) <= len(nfsopFields) {
} else if fullstat {
if version == "3" {
if nfs3Ops[first] && (len(nline) <= len(nfsopFields)) {
for i, t := range nline {
item := fmt.Sprintf("%s_%s", first, nfsopFields[i])
fields[item] = t
}
acc.AddFields("nfs_ops", fields, tags)
}
} else if fullstat && version == "4" {
if in(nfs4Fields, first) && len(nline) <= len(nfsopFields) {
} else if version == "4" {
if nfs4Ops[first] && (len(nline) <= len(nfsopFields)) {
for i, t := range nline {
item := fmt.Sprintf("%s_%s", first, nfsopFields[i])
fields[item] = t
}
acc.AddFields("nfs_ops", fields, tags)
}
}
}
} else {
if first == "READ" {
fields["read_ops"] = nline[0]
fields["read_retrans"] = (nline[1] - nline[0])
fields["read_bytes"] = (nline[3] + nline[4])
fields["read_rtt"] = nline[6]
fields["read_exe"] = nline[7]
acc.AddFields("nfsstat_read", fields, tags)
} else if first == "WRITE" {
fields["write_ops"] = nline[0]
fields["write_retrans"] = (nline[1] - nline[0])
fields["write_bytes"] = (nline[3] + nline[4])
fields["write_rtt"] = nline[6]
fields["write_exe"] = nline[7]
acc.AddFields("nfsstat_write", fields, tags)
}
}

return nil
}
Expand All @@ -297,6 +314,37 @@ func (n *NFSClient) processText(scanner *bufio.Scanner, acc telegraf.Accumulator
var version string
var export string
var skip bool
var nfs3Ops map[string]bool
var nfs4Ops map[string]bool
nfs3Ops = make(map[string]bool)
nfs4Ops = make(map[string]bool)

if len(n.IncludeOperations) == 0 {
for _, Op := range nfs3Fields {
nfs3Ops[Op] = true
}
for _, Op := range nfs4Fields {
nfs4Ops[Op] = true
}
} else {
for _, Op := range n.IncludeOperations {
nfs3Ops[Op] = true
}
for _, Op := range n.IncludeOperations {
nfs4Ops[Op] = true
}
}

if len(n.ExcludeOperations) > 0 {
for _, Op := range n.ExcludeOperations {
if nfs3Ops[Op] {
delete(nfs3Ops, Op)
}
if nfs4Ops[Op] {
delete(nfs4Ops, Op)
}
}
}

for scanner.Scan() {
line := strings.Fields(scanner.Text())
Expand Down Expand Up @@ -331,7 +379,7 @@ func (n *NFSClient) processText(scanner *bufio.Scanner, acc telegraf.Accumulator
}

if !skip && len(line) > 0 {
n.parseStat(device, export, version, line, n.Fullstat, acc)
n.parseStat(device, export, version, line, n.Fullstat, nfs3Ops, nfs4Ops, acc)
}
}
return nil
Expand Down
4 changes: 2 additions & 2 deletions plugins/inputs/nfsclient/nfsclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,13 +215,13 @@ func TestNFSClientProcessFull(t *testing.T) {
"attrinvalidates": int64(26188427),
"vfsopen": int64(27525),
"vfslookup": int64(9140),
"vfspermission": int64(114420),
"vfsaccess": int64(114420),
"vfsupdatepage": int64(30785253),
"vfsreadpage": int64(5308856),
"vfsreadpages": int64(5364858),
"vfswritepage": int64(30784819),
"vfswritepages": int64(79832668),
"vfsreaddir": int64(170),
"vfsgetdents": int64(170),
"vfssetattr": int64(64),
"vfsflush": int64(18194),
"vfsfsync": int64(29294718),
Expand Down

0 comments on commit 3aaa066

Please sign in to comment.