1
0
Fork 0

Adding upstream version 1.34.4.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-24 07:26:29 +02:00
parent e393c3af3f
commit 4978089aab
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
4963 changed files with 677545 additions and 0 deletions

View file

@ -0,0 +1,230 @@
# Network Filesystem Input Plugin
This plugin collects metrics about operations on [Network Filesystem][nfs]
mounts. By default, only a limited number of general system-level metrics are
collected, including basic read/write counts but more detailed metrics can be
enabled.
> [!NOTE]
> Many of the metrics, even if tagged with a mount point, are really
> _per-server_. E.g. if you mount two shares: `nfs01:/vol/foo/bar` and
> `nfs01:/vol/foo/baz`, there will be two near identical entries in
> `/proc/self/mountstats`. This is a limitation of the metrics exposed by the
> kernel, not by this plugin.
⭐ Telegraf v1.18.0
🏷️ network, system
💻 all
[nfs]: https://www.ietf.org/rfc/rfc1813.txt?number=1813
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
In addition to the plugin-specific configuration settings, plugins support
additional global and plugin configuration settings. These settings are used to
modify metrics, tags, and field or create aliases and configure ordering, etc.
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
## Configuration
```toml @sample.conf
# Read per-mount NFS client metrics from /proc/self/mountstats
[[inputs.nfsclient]]
## Read more low-level metrics (optional, defaults to false)
# fullstat = false
## List of mounts to explicitly include or exclude (optional)
## 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_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. Semantics 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 multiple [[input.nfsclient]] stanzas,
## with their own lists. See "include_mounts" above, and be careful of
## duplicate metrics.
# include_operations = []
# exclude_operations = []
```
### Configuration Options
- `fullstat`: Collect per-operation type metrics. Defaults to false.
- `include_mounts`: gather metrics for only these mounts. Default is to watch
all mounts.
- `exclude_mounts`: gather metrics for all mounts, except those listed in this
option. Excludes take precedence over includes.
- `include_operations`: List of specific NFS operations to track. See
`/proc/self/mountstats` (the "per-op statistics" section) for a 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 well over 50 for NFSv4). A suggested 'minimal'
list of operations to collect for basic usage:
`['READ','WRITE','ACCESS','GETATTR','READDIR','LOOKUP','LOOKUP']`
- `exclude_operations`: Gather all metrics, except those listed. Excludes take
precedence over includes.
> [!NOTE]
> The `include_mounts` and `exclude_mounts` arguments are both applied to the
> local mount location (e.g. /mnt/NFS), not the server export (e.g.
> nfsserver:/vol/NFS). Go regexp patterns can be used in either.
## Location of mountstats
If you have mounted the `/proc` file system in a container, to tell this plugin
where to find the new location, set the `MOUNT_PROC` environment variable. For
example, in a Docker compose file, if `/proc` is mounted to `/host/proc`, then
use:
```yaml
MOUNT_PROC: /host/proc/self/mountstats
```
## Metrics
Fields:
- nfsstat
- bytes (integer, bytes) - The total number of bytes exchanged doing this
operation. This is bytes sent _and_ received, including overhead _and_
payload (`bytes = OP_bytes_sent + OP_bytes_recv`). See `nfs_ops` below.
- ops (integer, count) - The number of operations of this type executed.
- retrans (integer, count) - The number of times an operation had to be
(`retried retrans = OP_trans - OP_ops`). See `nfs_ops` below.
- exe (integer, milliseconds) - The number of milliseconds it took to process
the operations.
- rtt (integer, milliseconds) - The total round-trip time for all operations.
- rtt_per_op (float, milliseconds) - The average round-trip time per operation.
In addition enabling `fullstat` will make many more metrics available.
Tags:
- All measurements have the following tags:
- mountpoint - The local mountpoint, for instance: "/var/www"
- serverexport - The full server export, for instance: "nfsserver.example.org:/export"
- Measurements nfsstat and nfs_ops will also include:
- operation - the NFS operation in question. `READ` or `WRITE` for nfsstat, but potentially one of ~20 or ~50, depending on NFS version. A complete list of operations supported is visible in `/proc/self/mountstats`.
### Additional metrics
When `fullstat` is true, additional measurements are collected. Tags are the
same as above.
NFS Operations:
Most descriptions come from [Reference][ref] and `nfs_iostat.h`. Field order
and names are the same as in `/proc/self/mountstats` and the Kernel source.
Please refer to `/proc/self/mountstats` for a list of supported NFS operations,
as it changes occasionally.
- nfs_bytes
- fields:
- normalreadbytes (int, bytes): Bytes read from the server via `read()`
- normalwritebytes (int, bytes): Bytes written to the server via `write()`
- directreadbytes (int, bytes): Bytes read with O_DIRECT set
- directwritebytes (int, bytes): Bytes written with O_DIRECT set
- serverreadbytes (int, bytes): Bytes read via NFS READ (via `mmap()`)
- serverwritebytes (int, bytes): Bytes written via NFS WRITE (via `mmap()`)
- readpages (int, count): Number of pages read
- writepages (int, count): Number of pages written
- nfs_events (Per-event metrics)
- fields:
- inoderevalidates (int, count): How many times cached inode attributes have to be re-validated from the server.
- dentryrevalidates (int, count): How many times cached dentry nodes have to be re-validated.
- datainvalidates (int, count): How many times an inode had its cached data thrown out.
- 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.
- 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()`?)
- 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.
- vfslock (int, count): Number of times a lock was attempted on a file (regardless of success or not).
- vfsrelease (int, count): Number of calls to `close()`.
- congestionwait (int, count): Believe unused by the Linux kernel, but it is part of the NFS spec.
- setattrtrunc (int, count): How many times files have had their size truncated.
- extendwrite (int, count): How many times a file has been grown because you're writing beyond the existing end of the file.
- sillyrenames (int, count): Number of times an in-use file was removed (thus creating a temporary ".nfsXXXXXX" file)
- shortreads (int, count): Number of times the NFS server returned less data than requested.
- shortwrites (int, count): Number of times NFS server reports it wrote less data than requested.
- delay (int, count): Occurrences of EJUKEBOX ("Jukebox Delay", probably unused)
- pnfsreads (int, count): Count of NFS v4.1+ pNFS reads.
- pnfswrites (int, count): Count of NFS v4.1+ pNFS writes.
- nfs_xprt_tcp
- fields:
- bind_count (int, count): Number of_completely new_ mounts to this server (sometimes 0?)
- connect_count (int, count): How many times the client has connected to the server in question
- connect_time (int, jiffies): How long the NFS client has spent waiting for its connection(s) to the server to be established.
- idle_time (int, seconds): How long (in seconds) since the NFS mount saw any RPC traffic.
- rpcsends (int, count): How many RPC requests this mount has sent to the server.
- rpcreceives (int, count): How many RPC replies this mount has received from the server.
- badxids (int, count): Count of XIDs sent by the server that the client doesn't know about.
- inflightsends (int, count): Number of outstanding requests; always >1. (See reference #4 for comment on this field)
- backlogutil (int, count): Cumulative backlog count
- nfs_xprt_udp
- fields:
- [same as nfs_xprt_tcp, except for connect_count, connect_time, and idle_time]
- nfs_ops
- fields (In all cases, the `operations` tag is set to the uppercase name of the NFS operation, _e.g._ "READ", "FSINFO", _etc_. See /proc/self/mountstats for a full list):
- ops (int, count): Total operations of this type.
- trans (int, count): Total transmissions of this type, including retransmissions: `OP_ops - OP_trans = total_retransmissions` (lower is better).
- timeouts (int, count): Number of major timeouts.
- bytes_sent (int, count): Bytes sent, including headers (should also be close to on-wire size).
- bytes_recv (int, count): Bytes received, including headers (should be close to on-wire size).
- queue_time (int, milliseconds): Cumulative time a request waited in the queue before sending this OP type.
- response_time (int, milliseconds): Cumulative time waiting for a response for this OP type.
- total_time (int, milliseconds): Cumulative time a request waited in the queue before sending.
- 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
[ref]: https://utcc.utoronto.ca/~cks/space/blog/linux/NFSMountstatsIndex
## Example Output
For basic metrics showing server-wise read and write data.
```text
nfsstat,mountpoint=/NFS,operation=READ,serverexport=1.2.3.4:/storage/NFS ops=600i,retrans=1i,bytes=1207i,rtt=606i,exe=607i 1612651512000000000
nfsstat,mountpoint=/NFS,operation=WRITE,serverexport=1.2.3.4:/storage/NFS bytes=1407i,rtt=706i,exe=707i,ops=700i,retrans=1i 1612651512000000000
```
For `fullstat=true` metrics, which includes additional measurements for
`nfs_bytes`, `nfs_events`, and `nfs_xprt_tcp` (and `nfs_xprt_udp` if present).
Additionally, per-OP metrics are collected, with examples for READ, LOOKUP, and
NULL shown. Please refer to `/proc/self/mountstats` for a list of supported NFS
operations, as it changes as it changes periodically.
```text
nfs_bytes,mountpoint=/home,serverexport=nfs01:/vol/home directreadbytes=0i,directwritebytes=0i,normalreadbytes=42648757667i,normalwritebytes=0i,readpages=10404603i,serverreadbytes=42617098139i,serverwritebytes=0i,writepages=0i 1608787697000000000
nfs_events,mountpoint=/home,serverexport=nfs01:/vol/home attrinvalidates=116i,congestionwait=0i,datainvalidates=65i,delay=0i,dentryrevalidates=5911243i,extendwrite=0i,inoderevalidates=200378i,pnfsreads=0i,pnfswrites=0i,setattrtrunc=0i,shortreads=0i,shortwrites=0i,sillyrenames=0i,vfsaccess=7203852i,vfsflush=117405i,vfsfsync=0i,vfsgetdents=3368i,vfslock=0i,vfslookup=740i,vfsopen=157281i,vfsreadpage=16i,vfsreadpages=86874i,vfsrelease=155526i,vfssetattr=0i,vfsupdatepage=0i,vfswritepage=0i,vfswritepages=215514i 1608787697000000000
nfs_xprt_tcp,mountpoint=/home,serverexport=nfs01:/vol/home backlogutil=0i,badxids=0i,bind_count=1i,connect_count=1i,connect_time=0i,idle_time=0i,inflightsends=15659826i,rpcreceives=2173896i,rpcsends=2173896i 1608787697000000000
nfs_ops,mountpoint=/NFS,operation=NULL,serverexport=1.2.3.4:/storage/NFS trans=0i,timeouts=0i,bytes_sent=0i,bytes_recv=0i,queue_time=0i,response_time=0i,total_time=0i,ops=0i 1612651512000000000
nfs_ops,mountpoint=/NFS,operation=READ,serverexport=1.2.3.4:/storage/NFS bytes=1207i,timeouts=602i,total_time=607i,exe=607i,trans=601i,bytes_sent=603i,bytes_recv=604i,queue_time=605i,ops=600i,retrans=1i,rtt=606i,response_time=606i 1612651512000000000
nfs_ops,mountpoint=/NFS,operation=WRITE,serverexport=1.2.3.4:/storage/NFS ops=700i,bytes=1407i,exe=707i,trans=701i,timeouts=702i,response_time=706i,total_time=707i,retrans=1i,rtt=706i,bytes_sent=703i,bytes_recv=704i,queue_time=705i 1612651512000000000
```

View file

@ -0,0 +1,514 @@
//go:generate ../../../tools/readme_config_includer/generator
package nfsclient
import (
"bufio"
_ "embed"
"errors"
"fmt"
"os"
"regexp"
"strconv"
"strings"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/internal/choice"
"github.com/influxdata/telegraf/plugins/inputs"
)
//go:embed sample.conf
var sampleConfig string
type NFSClient struct {
Fullstat bool `toml:"fullstat"`
IncludeMounts []string `toml:"include_mounts"`
ExcludeMounts []string `toml:"exclude_mounts"`
IncludeOperations []string `toml:"include_operations"`
ExcludeOperations []string `toml:"exclude_operations"`
Log telegraf.Logger `toml:"-"`
nfs3Ops map[string]bool
nfs4Ops map[string]bool
mountstatsPath string
// Add compiled regex patterns
includeMountRegex []*regexp.Regexp
excludeMountRegex []*regexp.Regexp
}
func (*NFSClient) SampleConfig() string {
return sampleConfig
}
func (n *NFSClient) Init() error {
var nfs3Fields = []string{
"NULL",
"GETATTR",
"SETATTR",
"LOOKUP",
"ACCESS",
"READLINK",
"READ",
"WRITE",
"CREATE",
"MKDIR",
"SYMLINK",
"MKNOD",
"REMOVE",
"RMDIR",
"RENAME",
"LINK",
"READDIR",
"READDIRPLUS",
"FSSTAT",
"FSINFO",
"PATHCONF",
"COMMIT",
}
var nfs4Fields = []string{
"NULL",
"READ",
"WRITE",
"COMMIT",
"OPEN",
"OPEN_CONFIRM",
"OPEN_NOATTR",
"OPEN_DOWNGRADE",
"CLOSE",
"SETATTR",
"FSINFO",
"RENEW",
"SETCLIENTID",
"SETCLIENTID_CONFIRM",
"LOCK",
"LOCKT",
"LOCKU",
"ACCESS",
"GETATTR",
"LOOKUP",
"LOOKUP_ROOT",
"REMOVE",
"RENAME",
"LINK",
"SYMLINK",
"CREATE",
"PATHCONF",
"STATFS",
"READLINK",
"READDIR",
"SERVER_CAPS",
"DELEGRETURN",
"GETACL",
"SETACL",
"FS_LOCATIONS",
"RELEASE_LOCKOWNER",
"SECINFO",
"FSID_PRESENT",
"EXCHANGE_ID",
"CREATE_SESSION",
"DESTROY_SESSION",
"SEQUENCE",
"GET_LEASE_TIME",
"RECLAIM_COMPLETE",
"LAYOUTGET",
"GETDEVICEINFO",
"LAYOUTCOMMIT",
"LAYOUTRETURN",
"SECINFO_NO_NAME",
"TEST_STATEID",
"FREE_STATEID",
"GETDEVICELIST",
"BIND_CONN_TO_SESSION",
"DESTROY_CLIENTID",
"SEEK",
"ALLOCATE",
"DEALLOCATE",
"LAYOUTSTATS",
"CLONE",
"COPY",
"OFFLOAD_CANCEL",
"LOOKUPP",
"LAYOUTERROR",
"COPY_NOTIFY",
"GETXATTR",
"SETXATTR",
"LISTXATTRS",
"REMOVEXATTR",
}
nfs3Ops := make(map[string]bool)
nfs4Ops := make(map[string]bool)
n.mountstatsPath = n.getMountStatsPath()
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)
}
}
}
n.nfs3Ops = nfs3Ops
n.nfs4Ops = nfs4Ops
if len(n.IncludeMounts) > 0 {
n.Log.Debugf("Including these mount patterns: %v", n.IncludeMounts)
} else {
n.Log.Debugf("Including all mounts.")
}
if len(n.ExcludeMounts) > 0 {
n.Log.Debugf("Excluding these mount patterns: %v", n.ExcludeMounts)
} else {
n.Log.Debugf("Not excluding any mounts.")
}
if len(n.IncludeOperations) > 0 {
n.Log.Debugf("Including these operations: %v", n.IncludeOperations)
} else {
n.Log.Debugf("Including all operations.")
}
if len(n.ExcludeOperations) > 0 {
n.Log.Debugf("Excluding these mount patterns: %v", n.ExcludeOperations)
} else {
n.Log.Debugf("Not excluding any operations.")
}
// Compile include mount patterns
if len(n.IncludeMounts) > 0 {
n.includeMountRegex = make([]*regexp.Regexp, 0, len(n.IncludeMounts))
for _, pattern := range n.IncludeMounts {
compiled, err := regexp.Compile(pattern)
if err != nil {
return fmt.Errorf("failed to compile include mount pattern %q: %w", pattern, err)
}
n.includeMountRegex = append(n.includeMountRegex, compiled)
}
}
// Compile exclude mount patterns
if len(n.ExcludeMounts) > 0 {
n.excludeMountRegex = make([]*regexp.Regexp, 0, len(n.ExcludeMounts))
for _, pattern := range n.ExcludeMounts {
compiled, err := regexp.Compile(pattern)
if err != nil {
return fmt.Errorf("failed to compile exclude mount pattern %q: %w", pattern, err)
}
n.excludeMountRegex = append(n.excludeMountRegex, compiled)
}
}
return nil
}
func (n *NFSClient) Gather(acc telegraf.Accumulator) error {
if _, err := os.Stat(n.mountstatsPath); os.IsNotExist(err) {
return err
}
// Attempt to read the file to see if we have permissions before opening
// which can lead to a panic
if _, err := os.ReadFile(n.mountstatsPath); err != nil {
return err
}
file, err := os.Open(n.mountstatsPath)
if err != nil {
n.Log.Errorf("Failed opening the %q file: %v ", file.Name(), err)
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
if err := n.processText(scanner, acc); err != nil {
return err
}
return scanner.Err()
}
func (n *NFSClient) parseStat(mountpoint, export, version string, line []string, acc telegraf.Accumulator) error {
tags := map[string]string{"mountpoint": mountpoint, "serverexport": export}
nline, err := convertToUint64(line)
if err != nil {
return err
}
if len(nline) == 0 {
n.Log.Warnf("Parsing Stat line with one field: %s\n", line)
return nil
}
first := strings.Replace(line[0], ":", "", 1)
var eventsFields = []string{
"inoderevalidates",
"dentryrevalidates",
"datainvalidates",
"attrinvalidates",
"vfsopen",
"vfslookup",
"vfsaccess",
"vfsupdatepage",
"vfsreadpage",
"vfsreadpages",
"vfswritepage",
"vfswritepages",
"vfsgetdents",
"vfssetattr",
"vfsflush",
"vfsfsync",
"vfslock",
"vfsrelease",
"congestionwait",
"setattrtrunc",
"extendwrite",
"sillyrenames",
"shortreads",
"shortwrites",
"delay",
"pnfsreads",
"pnfswrites",
}
var bytesFields = []string{
"normalreadbytes",
"normalwritebytes",
"directreadbytes",
"directwritebytes",
"serverreadbytes",
"serverwritebytes",
"readpages",
"writepages",
}
var xprtudpFields = []string{
"bind_count",
"rpcsends",
"rpcreceives",
"badxids",
"inflightsends",
"backlogutil",
}
var xprttcpFields = []string{
"bind_count",
"connect_count",
"connect_time",
"idle_time",
"rpcsends",
"rpcreceives",
"badxids",
"inflightsends",
"backlogutil",
}
var nfsopFields = []string{
"ops",
"trans",
"timeouts",
"bytes_sent",
"bytes_recv",
"queue_time",
"response_time",
"total_time",
"errors",
}
var fields = make(map[string]interface{})
switch first {
case "READ", "WRITE":
fields["ops"] = nline[0]
fields["retrans"] = nline[1] - nline[0]
fields["bytes"] = nline[3] + nline[4]
fields["rtt"] = nline[6]
fields["exe"] = nline[7]
fields["rtt_per_op"] = 0.0
if nline[0] > 0 {
fields["rtt_per_op"] = float64(nline[6]) / float64(nline[0])
}
tags["operation"] = first
acc.AddFields("nfsstat", fields, tags)
}
if n.Fullstat {
switch first {
case "events":
if len(nline) >= len(eventsFields) {
for i, t := range eventsFields {
fields[t] = nline[i]
}
acc.AddFields("nfs_events", fields, tags)
}
case "bytes":
if len(nline) >= len(bytesFields) {
for i, t := range bytesFields {
fields[t] = nline[i]
}
acc.AddFields("nfs_bytes", fields, tags)
}
case "xprt":
if len(line) > 1 {
switch line[1] {
case "tcp":
if len(nline)+2 >= len(xprttcpFields) {
for i, t := range xprttcpFields {
fields[t] = nline[i+2]
}
acc.AddFields("nfs_xprt_tcp", fields, tags)
}
case "udp":
if len(nline)+2 >= len(xprtudpFields) {
for i, t := range xprtudpFields {
fields[t] = nline[i+2]
}
acc.AddFields("nfs_xprt_udp", fields, tags)
}
}
}
}
if (version == "3" && n.nfs3Ops[first]) || (version == "4" && n.nfs4Ops[first]) {
tags["operation"] = first
if len(nline) <= len(nfsopFields) {
for i, t := range nline {
fields[nfsopFields[i]] = t
}
acc.AddFields("nfs_ops", fields, tags)
}
}
}
return nil
}
func (n *NFSClient) processText(scanner *bufio.Scanner, acc telegraf.Accumulator) error {
var mount string
var version string
var export string
var skip bool
for scanner.Scan() {
line := strings.Fields(scanner.Text())
lineLength := len(line)
if lineLength == 0 {
continue
}
skip = false
// This denotes a new mount has been found, so set
// mount and export, and stop skipping (for now)
if lineLength > 4 && choice.Contains("fstype", line) && (choice.Contains("nfs", line) || choice.Contains("nfs4", line)) {
mount = line[4]
export = line[1]
} else if lineLength > 5 && (choice.Contains("(nfs)", line) || choice.Contains("(nfs4)", line)) {
version = strings.Split(line[5], "/")[1]
}
if mount == "" {
continue
}
// Check include patterns using compiled regex
if len(n.includeMountRegex) > 0 {
skip = true
for _, regex := range n.includeMountRegex {
if regex.MatchString(mount) {
skip = false
break
}
}
}
// Check exclude patterns using compiled regex
if !skip && len(n.excludeMountRegex) > 0 {
for _, regex := range n.excludeMountRegex {
if regex.MatchString(mount) {
skip = true
break
}
}
}
if !skip {
err := n.parseStat(mount, export, version, line, acc)
if err != nil {
return fmt.Errorf("could not parseStat: %w", err)
}
}
}
return nil
}
func (n *NFSClient) getMountStatsPath() string {
path := "/proc/self/mountstats"
if os.Getenv("MOUNT_PROC") != "" {
path = os.Getenv("MOUNT_PROC")
}
n.Log.Debugf("using [%s] for mountstats", path)
return path
}
func convertToUint64(line []string) ([]uint64, error) {
/* A "line" of input data (a pre-split array of strings) is
processed one field at a time. Each field is converted to
an uint64 value, and appended to an array of return values.
On an error, check for ErrRange, and returns an error
if found. This situation indicates a pretty major issue in
the /proc/self/mountstats file, and returning faulty data
is worse than no data. Other errors are ignored, and append
whatever we got in the first place (probably 0).
Yes, this is ugly. */
if len(line) < 2 {
return nil, nil
}
nline := make([]uint64, 0, len(line[1:]))
// Skip the first field; it's handled specially as the "first" variable
for _, l := range line[1:] {
val, err := strconv.ParseUint(l, 10, 64)
if err != nil {
var numError *strconv.NumError
if errors.As(err, &numError) {
if errors.Is(numError.Err, strconv.ErrRange) {
return nil, fmt.Errorf("errrange: line:[%v] raw:[%v] -> parsed:[%v]", line, l, val)
}
}
}
nline = append(nline, val)
}
return nline, nil
}
func init() {
inputs.Add("nfsclient", func() telegraf.Input {
return &NFSClient{}
})
}

View file

@ -0,0 +1,341 @@
package nfsclient
import (
"bufio"
"os"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/influxdata/telegraf/testutil"
)
func getMountStatsPath() string {
path := "./testdata/mountstats"
if os.Getenv("MOUNT_PROC") != "" {
path = os.Getenv("MOUNT_PROC")
}
return path
}
func TestNFSClientParsev3(t *testing.T) {
var acc testutil.Accumulator
nfsclient := NFSClient{Fullstat: true}
nfsclient.nfs3Ops = map[string]bool{"READLINK": true, "GETATTR": false}
nfsclient.nfs4Ops = map[string]bool{"READLINK": true, "GETATTR": false}
data := strings.Fields(" READLINK: 500 501 502 503 504 505 506 507")
err := nfsclient.parseStat("1.2.3.4:/storage/NFS", "/A", "3", data, &acc)
require.NoError(t, err)
fieldsOps := map[string]interface{}{
"ops": uint64(500),
"trans": uint64(501),
"timeouts": uint64(502),
"bytes_sent": uint64(503),
"bytes_recv": uint64(504),
"queue_time": uint64(505),
"response_time": uint64(506),
"total_time": uint64(507),
}
acc.AssertContainsFields(t, "nfs_ops", fieldsOps)
}
func TestNFSClientParsev4(t *testing.T) {
var acc testutil.Accumulator
nfsclient := NFSClient{Fullstat: true}
nfsclient.nfs3Ops = map[string]bool{"DESTROY_SESSION": true, "GETATTR": false}
nfsclient.nfs4Ops = map[string]bool{"DESTROY_SESSION": true, "GETATTR": false}
data := strings.Fields(" DESTROY_SESSION: 500 501 502 503 504 505 506 507")
err := nfsclient.parseStat("2.2.2.2:/nfsdata/", "/B", "4", data, &acc)
require.NoError(t, err)
fieldsOps := map[string]interface{}{
"ops": uint64(500),
"trans": uint64(501),
"timeouts": uint64(502),
"bytes_sent": uint64(503),
"bytes_recv": uint64(504),
"queue_time": uint64(505),
"response_time": uint64(506),
"total_time": uint64(507),
}
acc.AssertContainsFields(t, "nfs_ops", fieldsOps)
}
func TestNFSClientParseLargeValue(t *testing.T) {
var acc testutil.Accumulator
nfsclient := NFSClient{Fullstat: true}
nfsclient.nfs3Ops = map[string]bool{"SETCLIENTID": true, "GETATTR": false}
nfsclient.nfs4Ops = map[string]bool{"SETCLIENTID": true, "GETATTR": false}
data := strings.Fields(" SETCLIENTID: 218 216 0 53568 12960 18446744073709531008 134 197")
err := nfsclient.parseStat("2.2.2.2:/nfsdata/", "/B", "4", data, &acc)
require.NoError(t, err)
fieldsOps := map[string]interface{}{
"ops": uint64(218),
"trans": uint64(216),
"timeouts": uint64(0),
"bytes_sent": uint64(53568),
"bytes_recv": uint64(12960),
"queue_time": uint64(18446744073709531008),
"response_time": uint64(134),
"total_time": uint64(197),
}
acc.AssertContainsFields(t, "nfs_ops", fieldsOps)
}
func TestNFSClientProcessStat(t *testing.T) {
var acc testutil.Accumulator
nfsclient := NFSClient{}
nfsclient.Fullstat = false
file, err := os.Open(getMountStatsPath())
require.NoError(t, err)
defer file.Close()
scanner := bufio.NewScanner(file)
err = nfsclient.processText(scanner, &acc)
require.NoError(t, err)
fieldsReadstat := map[string]interface{}{
"ops": uint64(600),
"retrans": uint64(1),
"bytes": uint64(1207),
"rtt": uint64(606),
"exe": uint64(607),
"rtt_per_op": float64(1.01),
}
readTags := map[string]string{
"serverexport": "1.2.3.4:/storage/NFS",
"mountpoint": "/A",
"operation": "READ",
}
acc.AssertContainsTaggedFields(t, "nfsstat", fieldsReadstat, readTags)
fieldsWritestat := map[string]interface{}{
"ops": uint64(700),
"retrans": uint64(1),
"bytes": uint64(1407),
"rtt": uint64(706),
"exe": uint64(707),
"rtt_per_op": float64(1.0085714285714287),
}
writeTags := map[string]string{
"serverexport": "1.2.3.4:/storage/NFS",
"mountpoint": "/A",
"operation": "WRITE",
}
acc.AssertContainsTaggedFields(t, "nfsstat", fieldsWritestat, writeTags)
}
func TestNFSClientProcessFull(t *testing.T) {
var acc testutil.Accumulator
nfsclient := NFSClient{}
nfsclient.Fullstat = true
file, err := os.Open(getMountStatsPath())
require.NoError(t, err)
defer file.Close()
scanner := bufio.NewScanner(file)
err = nfsclient.processText(scanner, &acc)
require.NoError(t, err)
fieldsEvents := map[string]interface{}{
"inoderevalidates": uint64(301736),
"dentryrevalidates": uint64(22838),
"datainvalidates": uint64(410979),
"attrinvalidates": uint64(26188427),
"vfsopen": uint64(27525),
"vfslookup": uint64(9140),
"vfsaccess": uint64(114420),
"vfsupdatepage": uint64(30785253),
"vfsreadpage": uint64(5308856),
"vfsreadpages": uint64(5364858),
"vfswritepage": uint64(30784819),
"vfswritepages": uint64(79832668),
"vfsgetdents": uint64(170),
"vfssetattr": uint64(64),
"vfsflush": uint64(18194),
"vfsfsync": uint64(29294718),
"vfslock": uint64(0),
"vfsrelease": uint64(18279),
"congestionwait": uint64(0),
"setattrtrunc": uint64(2),
"extendwrite": uint64(785551),
"sillyrenames": uint64(0),
"shortreads": uint64(0),
"shortwrites": uint64(0),
"delay": uint64(0),
"pnfsreads": uint64(0),
"pnfswrites": uint64(0),
}
fieldsBytes := map[string]interface{}{
"normalreadbytes": uint64(204440464584),
"normalwritebytes": uint64(110857586443),
"directreadbytes": uint64(783170354688),
"directwritebytes": uint64(296174954496),
"serverreadbytes": uint64(1134399088816),
"serverwritebytes": uint64(407107155723),
"readpages": uint64(85749323),
"writepages": uint64(30784819),
}
fieldsXprtTCP := map[string]interface{}{
"bind_count": uint64(1),
"connect_count": uint64(1),
"connect_time": uint64(0),
"idle_time": uint64(0),
"rpcsends": uint64(96172963),
"rpcreceives": uint64(96172963),
"badxids": uint64(0),
"inflightsends": uint64(620878754),
"backlogutil": uint64(0),
}
acc.AssertContainsFields(t, "nfs_events", fieldsEvents)
acc.AssertContainsFields(t, "nfs_bytes", fieldsBytes)
acc.AssertContainsFields(t, "nfs_xprt_tcp", fieldsXprtTCP)
}
func TestNFSClientFileDoesNotExist(t *testing.T) {
var acc testutil.Accumulator
nfsclient := NFSClient{Fullstat: true}
nfsclient.mountstatsPath = "/does_not_exist"
require.Error(t, nfsclient.Gather(&acc))
}
func TestNFSClientProcessTextWithIncludeExclude(t *testing.T) {
// Test cases for different include/exclude scenarios
testCases := []struct {
name string
includeMounts []string
excludeMounts []string
expectedMounts []string
unexpectedMounts []string
}{
{
name: "No filters",
includeMounts: nil,
excludeMounts: nil,
expectedMounts: []string{"/A", "/B"}, // All mounts should be included
},
{
name: "Exclude one mount",
includeMounts: nil,
excludeMounts: []string{"^/A$"},
expectedMounts: []string{"/B"},
unexpectedMounts: []string{"/A"},
},
{
name: "Include one mount",
includeMounts: []string{"^/A$"},
excludeMounts: nil,
expectedMounts: []string{"/A"},
unexpectedMounts: []string{"/B"},
},
{
name: "Include and exclude with regex",
includeMounts: []string{"^/"},
excludeMounts: []string{"^/A$"},
expectedMounts: []string{"/B"},
unexpectedMounts: []string{"/A"},
},
{
name: "Exclude with prefix pattern",
includeMounts: nil,
excludeMounts: []string{"^/A"},
expectedMounts: []string{"/B"},
unexpectedMounts: []string{"/A"},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Create NFS client with test configuration
nfsclient := NFSClient{
IncludeMounts: tc.includeMounts,
ExcludeMounts: tc.excludeMounts,
Fullstat: true,
Log: testutil.Logger{},
}
require.NoError(t, nfsclient.Init())
// Open the test data
file, err := os.Open(getMountStatsPath())
require.NoError(t, err)
defer file.Close()
scanner := bufio.NewScanner(file)
// Process the data
var acc testutil.Accumulator
require.NoError(t, nfsclient.processText(scanner, &acc))
// Verify expected mounts are present
for _, mount := range tc.expectedMounts {
found := false
for _, metric := range acc.Metrics {
if mountpoint, exists := metric.Tags["mountpoint"]; exists && mountpoint == mount {
found = true
break
}
}
require.True(t, found, "Expected mount %s not found", mount)
}
// Verify unexpected mounts are absent
for _, mount := range tc.unexpectedMounts {
found := false
for _, metric := range acc.Metrics {
if mountpoint, exists := metric.Tags["mountpoint"]; exists && mountpoint == mount {
found = true
break
}
}
require.False(t, found, "Unexpected mount %s found", mount)
}
})
}
}
func TestNFSClientInvalidIncludeRegex(t *testing.T) {
// Test that invalid include regex patterns are properly reported as errors during Init
nfsclient := &NFSClient{
IncludeMounts: []string{"[invalid"},
ExcludeMounts: nil,
Fullstat: true,
Log: testutil.Logger{},
}
// Init should fail with an error due to invalid regex
err := nfsclient.Init()
require.Error(t, err)
require.Contains(t, err.Error(), "failed to compile include mount pattern")
}
func TestNFSClientInvalidExcludeRegex(t *testing.T) {
// Test that invalid exclude regex patterns are properly reported as errors during Init
nfsclient := &NFSClient{
IncludeMounts: nil,
ExcludeMounts: []string{"[also-invalid"},
Fullstat: true,
Log: testutil.Logger{},
}
// Init should fail with an error due to invalid regex
err := nfsclient.Init()
require.Error(t, err)
require.Contains(t, err.Error(), "failed to compile exclude mount pattern")
}

View file

@ -0,0 +1,27 @@
# Read per-mount NFS client metrics from /proc/self/mountstats
[[inputs.nfsclient]]
## Read more low-level metrics (optional, defaults to false)
# fullstat = false
## List of mounts to explicitly include or exclude (optional)
## 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_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. Semantics 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 multiple [[input.nfsclient]] stanzas,
## with their own lists. See "include_mounts" above, and be careful of
## duplicate metrics.
# include_operations = []
# exclude_operations = []

View file

@ -0,0 +1,231 @@
device rootfs mounted on / with fstype rootfs
device proc mounted on /proc with fstype proc
device sysfs mounted on /sys with fstype sysfs
device devtmpfs mounted on /dev with fstype devtmpfs
device devpts mounted on /dev/pts with fstype devpts
device tmpfs mounted on /dev/shm with fstype tmpfs
device /dev/loop0 mounted on /dev/.initramfs/live with fstype iso9660
device /dev/loop6 mounted on / with fstype ext4
device /proc/bus/usb mounted on /proc/bus/usb with fstype usbfs
device none mounted on /proc/sys/fs/binfmt_misc with fstype binfmt_misc
device /tmp mounted on /tmp with fstype tmpfs
device /home mounted on /home with fstype tmpfs
device /var mounted on /var with fstype tmpfs
device /etc mounted on /etc with fstype tmpfs
device /dev/ram1 mounted on /root with fstype ext2
device cgroup mounted on /cgroup/cpuset with fstype cgroup
device cgroup mounted on /cgroup/cpu with fstype cgroup
device cgroup mounted on /cgroup/cpuacct with fstype cgroup
device cgroup mounted on /cgroup/memory with fstype cgroup
device cgroup mounted on /cgroup/devices with fstype cgroup
device cgroup mounted on /cgroup/freezer with fstype cgroup
device cgroup mounted on /cgroup/net_cls with fstype cgroup
device cgroup mounted on /cgroup/blkio with fstype cgroup
device sunrpc mounted on /var/lib/nfs/rpc_pipefs with fstype rpc_pipefs
device /etc/auto.misc mounted on /misc with fstype autofs
device -hosts mounted on /net with fstype autofs
device 1.2.3.4:/storage/NFS mounted on /A with fstype nfs statvers=1.1
opts: rw,vers=3,rsize=32768,wsize=32768,namlen=255,acregmin=60,acregmax=60,acdirmin=60,acdirmax=60,hard,nolock,noacl,nordirplus,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=1.2.3.4,mountvers=3,mountport=49193,mountproto=tcp,local_lock=all
age: 1136770
caps: caps=0x3fe6,wtmult=512,dtsize=8192,bsize=0,namlen=255
sec: flavor=1,pseudoflavor=1
events: 301736 22838 410979 26188427 27525 9140 114420 30785253 5308856 5364858 30784819 79832668 170 64 18194 29294718 0 18279 0 2 785551 0 0 0 0 0 0
bytes: 204440464584 110857586443 783170354688 296174954496 1134399088816 407107155723 85749323 30784819
RPC iostats version: 1.0 p/v: 100003/3 (nfs)
xprt: tcp 733 1 1 0 0 96172963 96172963 0 620878754 0 690 196347132 524706275
per-op statistics
NULL: 0 0 0 0 0 0 0 0
GETATTR: 100 101 102 103 104 105 106 107
SETATTR: 200 201 202 203 204 205 206 207
LOOKUP: 300 301 302 303 304 305 306 307
ACCESS: 400 401 402 403 404 405 406 407
READLINK: 500 501 502 503 504 505 506 507
READ: 600 601 602 603 604 605 606 607
WRITE: 700 701 702 703 704 705 706 707
CREATE: 800 801 802 803 804 805 806 807
MKDIR: 900 901 902 903 904 905 906 907
SYMLINK: 1000 1001 1002 1003 1004 1005 1006 1007
MKNOD: 1100 1101 1102 1103 1104 1105 1106 1107
REMOVE: 1200 1201 1202 1203 1204 1205 1206 1207
RMDIR: 1300 1301 1302 1303 1304 1305 1306 1307
RENAME: 1400 1401 1402 1403 1404 1405 1406 1407
LINK: 1500 1501 1502 1503 1504 1505 1506 1507
READDIR: 1600 1601 1602 1603 1604 1605 1606 1607
READDIRPLUS: 1700 1701 1702 1703 1704 1705 1706 1707
FSSTAT: 1800 1801 1802 1803 1804 1805 1806 1807
FSINFO: 1900 1901 1902 1903 1904 1905 1906 1907
PATHCONF: 2000 2001 2002 2003 2004 2005 2006 2007
COMMIT: 2100 2101 2102 2103 2104 2105 2106 2107
device 2.2.2.2:/nfsdata/ mounted on /B with fstype nfs4 statvers=1.1
opts: rw,vers=4,rsize=1048576,wsize=1048576,namlen=255,acregmin=3,acregmax=60, acdirmin=30,acdirmax=60,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys, clientaddr=3.3.3.3,minorversion=0,local_lock=none
age: 19
caps: caps=0xfff7,wtmult=512,dtsize=32768,bsize=0,namlen=255
nfsv4: bm0=0xfdffafff,bm1=0xf9be3e,acl=0x0
sec: flavor=1,pseudoflavor=1
events: 0 168232 0 0 0 10095 217808 0 2 9797 0 9739 0 0 19739 19739 0 19739 0 0 0 0 0 0 0 0 0
bytes: 1612840960 0 0 0 627536112 0 158076 0
RPC iostats version: 1.0 p/v: 100003/4 (nfs)
xprt: tcp 737 0 1 0 0 69698 69697 0 81817 0 2 1082 12119
per-op statistics
NULL: 0 0 0 0 0 0 0 0
READ: 9797 9797 0 1000 2000 71 7953 8200
WRITE: 0 0 0 0 0 0 0 0
COMMIT: 0 0 0 0 0 0 0 0
OPEN: 19740 19740 0 4737600 7343280 505 3449 4172
OPEN_CONFIRM: 10211 10211 0 1552072 694348 74 836 1008
OPEN_NOATTR: 0 0 0 0 0 0 0 0
OPEN_DOWNGRADE: 0 0 0 0 0 0 0 0
CLOSE: 19739 19739 0 3316152 2605548 334 3045 3620
SETATTR: 0 0 0 0 0 0 0 0
FSINFO: 1 1 0 132 108 0 0 0
RENEW: 0 0 0 0 0 0 0 0
SETCLIENTID: 0 0 0 0 0 0 0 0
SETCLIENTID_CONFIRM: 0 0 0 0 0 0 0 0
LOCK: 0 0 0 0 0 0 0 0
LOCKT: 0 0 0 0 0 0 0 0
LOCKU: 0 0 0 0 0 0 0 0
ACCESS: 96 96 0 14584 19584 0 8 10
GETATTR: 1 1 0 132 188 0 0 0
LOOKUP: 10095 10095 0 1655576 2382420 36 898 1072
LOOKUP_ROOT: 0 0 0 0 0 0 0 0
REMOVE: 0 0 0 0 0 0 0 0
RENAME: 0 0 0 0 0 0 0 0
LINK: 0 0 0 0 0 0 0 0
SYMLINK: 0 0 0 0 0 0 0 0
CREATE: 0 0 0 0 0 0 0 0
PATHCONF: 1 1 0 128 72 0 0 0
STATFS: 0 0 0 0 0 0 0 0
READLINK: 0 0 0 0 0 0 0 0
READDIR: 0 0 0 0 0 0 0 0
SERVER_CAPS: 2 2 0 256 176 0 0 0
DELEGRETURN: 0 0 0 0 0 0 0 0
GETACL: 0 0 0 0 0 0 0 0
SETACL: 0 0 0 0 0 0 0 0
FS_LOCATIONS: 0 0 0 0 0 0 0 0
RELEASE_LOCKOWNER: 0 0 0 0 0 0 0 0
SECINFO: 0 0 0 0 0 0 0 0
EXCHANGE_ID: 0 0 0 0 0 0 0 0
CREATE_SESSION: 0 0 0 0 0 0 0 0
DESTROY_SESSION: 500 501 502 503 504 505 506 507
SEQUENCE: 0 0 0 0 0 0 0 0
GET_LEASE_TIME: 0 0 0 0 0 0 0 0
RECLAIM_COMPLETE: 0 0 0 0 0 0 0 0
LAYOUTGET: 0 0 0 0 0 0 0 0
GETDEVICEINFO: 0 0 0 0 0 0 0 0
LAYOUTCOMMIT: 0 0 0 0 0 0 0 0
device nfsserver1:/vol/export1/bread_recipes mounted on /C with fstype nfs statvers=1.1
opts: rw,vers=3,rsize=65536,wsize=65536,namlen=255,acregmin=3,acregmax=60,acdirmin=30,acdirmax=60,hard,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=5.4.3.2,mountvers=3,mountport=635,mountproto=udp,local_lock=none
age: 1084700
caps: caps=0x3fc7,wtmult=512,dtsize=32768,bsize=0,namlen=255
sec: flavor=1,pseudoflavor=1
events: 145712 48345501 0 2476 804 1337 49359047 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
bytes: 0 0 0 0 0 0 0 0
RPC iostats version: 1.0 p/v: 100003/3 (nfs)
xprt: tcp 871 1 1 0 0 181124336 181124308 28 1971647851 0 1100 807885669 90279840
per-op statistics
NULL: 1 2 0 44 24 0 0 0
GETATTR: 145712 145712 0 22994472 16319744 532 107480 109969
SETATTR: 0 0 0 0 0 0 0 0
LOOKUP: 2553 2553 0 385932 476148 9 1695 1739
ACCESS: 596338 596338 0 79281020 71560560 2375 228286 237993
READLINK: 0 0 0 0 0 0 0 0
READ: 0 0 0 0 0 0 0 0
WRITE: 0 0 0 0 0 0 0 0
CREATE: 0 0 0 0 0 0 0 0
MKDIR: 0 0 0 0 0 0 0 0
SYMLINK: 0 0 0 0 0 0 0 0
MKNOD: 0 0 0 0 0 0 0 0
REMOVE: 0 0 0 0 0 0 0 0
RMDIR: 0 0 0 0 0 0 0 0
RENAME: 0 0 0 0 0 0 0 0
LINK: 0 0 0 0 0 0 0 0
READDIR: 0 0 0 0 0 0 0 0
READDIRPLUS: 0 0 0 0 0 0 0 0
FSSTAT: 1698 1698 0 250080 285264 6 929 951
FSINFO: 34 34 0 4352 5576 0 5 5
PATHCONF: 1 1 0 128 140 0 0 0
COMMIT: 0 0 0 0 0 0 0 0
device nfsserver2:/tank/os2warp mounted on /D with fstype nfs4 statvers=1.1
opts: rw,vers=4.2,rsize=1048576,wsize=1048576,namlen=255,acregmin=3,acregmax=60,acdirmin=30,acdirmax=60,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=10.66.88.239,local_lock=none
age: 2
impl_id: name='',domain='',date='0,0'
caps: caps=0xffbfff7,wtmult=512,dtsize=32768,bsize=0,namlen=255
nfsv4: bm0=0xfdffafff,bm1=0x40f9be3e,bm2=0x28803,acl=0x0,sessions,pnfs=not configured,lease_time=90,lease_expired=0
sec: flavor=1,pseudoflavor=1
events: 1 112 0 0 1 3 117 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0
bytes: 0 0 0 0 0 0 0 0
RPC iostats version: 1.1 p/v: 100003/4 (nfs)
xprt: tcp 763 0 2 0 2 39 39 0 42 0 2 0 3
per-op statistics
NULL: 1 1 0 44 24 0 0 1 0
READ: 0 0 0 0 0 0 0 0 0
WRITE: 0 0 0 0 0 0 0 0 0
COMMIT: 0 0 0 0 0 0 0 0 0
OPEN: 0 0 0 0 0 0 0 0 0
OPEN_CONFIRM: 0 0 0 0 0 0 0 0 0
OPEN_NOATTR: 0 0 0 0 0 0 0 0 0
OPEN_DOWNGRADE: 0 0 0 0 0 0 0 0 0
CLOSE: 0 0 0 0 0 0 0 0 0
SETATTR: 0 0 0 0 0 0 0 0 0
FSINFO: 1 1 0 168 164 0 0 0 0
RENEW: 0 0 0 0 0 0 0 0 0
SETCLIENTID: 0 0 0 0 0 0 0 0 0
SETCLIENTID_CONFIRM: 0 0 0 0 0 0 0 0 0
LOCK: 0 0 0 0 0 0 0 0 0
LOCKT: 0 0 0 0 0 0 0 0 0
LOCKU: 0 0 0 0 0 0 0 0 0
ACCESS: 3 3 0 600 504 0 1 1 0
GETATTR: 2 2 0 364 480 0 1 1 0
LOOKUP: 3 3 0 628 484 0 1 1 2
LOOKUP_ROOT: 0 0 0 0 0 0 0 0 0
REMOVE: 0 0 0 0 0 0 0 0 0
RENAME: 0 0 0 0 0 0 0 0 0
LINK: 0 0 0 0 0 0 0 0 0
SYMLINK: 0 0 0 0 0 0 0 0 0
CREATE: 0 0 0 0 0 0 0 0 0
PATHCONF: 1 1 0 160 116 0 0 0 0
STATFS: 1 1 0 164 160 0 0 0 0
READLINK: 0 0 0 0 0 0 0 0 0
READDIR: 1 1 0 224 11968 0 1 1 0
SERVER_CAPS: 2 2 0 336 328 0 1 1 0
DELEGRETURN: 0 0 0 0 0 0 0 0 0
GETACL: 0 0 0 0 0 0 0 0 0
SETACL: 0 0 0 0 0 0 0 0 0
FS_LOCATIONS: 0 0 0 0 0 0 0 0 0
RELEASE_LOCKOWNER: 0 0 0 0 0 0 0 0 0
SECINFO: 0 0 0 0 0 0 0 0 0
FSID_PRESENT: 0 0 0 0 0 0 0 0 0
EXCHANGE_ID: 2 2 0 480 200 0 2 2 0
CREATE_SESSION: 1 1 0 200 124 0 0 0 0
DESTROY_SESSION: 0 0 0 0 0 0 0 0 0
SEQUENCE: 0 0 0 0 0 0 0 0 0
GET_LEASE_TIME: 0 0 0 0 0 0 0 0 0
RECLAIM_COMPLETE: 1 1 0 128 88 0 107 107 0
LAYOUTGET: 0 0 0 0 0 0 0 0 0
GETDEVICEINFO: 0 0 0 0 0 0 0 0 0
LAYOUTCOMMIT: 0 0 0 0 0 0 0 0 0
LAYOUTRETURN: 0 0 0 0 0 0 0 0 0
SECINFO_NO_NAME: 0 0 0 0 0 0 0 0 0
TEST_STATEID: 0 0 0 0 0 0 0 0 0
FREE_STATEID: 0 0 0 0 0 0 0 0 0
GETDEVICELIST: 0 0 0 0 0 0 0 0 0
BIND_CONN_TO_SESSION: 0 0 0 0 0 0 0 0 0
DESTROY_CLIENTID: 0 0 0 0 0 0 0 0 0
SEEK: 0 0 0 0 0 0 0 0 0
ALLOCATE: 0 0 0 0 0 0 0 0 0
DEALLOCATE: 0 0 0 0 0 0 0 0 0
LAYOUTSTATS: 0 0 0 0 0 0 0 0 0
CLONE: 0 0 0 0 0 0 0 0 0
COPY: 0 0 0 0 0 0 0 0 0
OFFLOAD_CANCEL: 0 0 0 0 0 0 0 0 0
LOOKUPP: 0 0 0 0 0 0 0 0 0
LAYOUTERROR: 0 0 0 0 0 0 0 0 0
COPY_NOTIFY: 0 0 0 0 0 0 0 0 0
GETXATTR: 0 0 0 0 0 0 0 0 0
SETXATTR: 0 0 0 0 0 0 0 0 0
LISTXATTRS: 0 0 0 0 0 0 0 0 0
REMOVEXATTR: 0 0 0 0 0 0 0 0 0
LAYOUTRETURN: 0 0 0 0 0 0 0 0