Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
230
plugins/inputs/nfsclient/README.md
Normal file
230
plugins/inputs/nfsclient/README.md
Normal 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
|
||||
```
|
514
plugins/inputs/nfsclient/nfsclient.go
Normal file
514
plugins/inputs/nfsclient/nfsclient.go
Normal 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{}
|
||||
})
|
||||
}
|
341
plugins/inputs/nfsclient/nfsclient_test.go
Normal file
341
plugins/inputs/nfsclient/nfsclient_test.go
Normal 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")
|
||||
}
|
27
plugins/inputs/nfsclient/sample.conf
Normal file
27
plugins/inputs/nfsclient/sample.conf
Normal 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 = []
|
231
plugins/inputs/nfsclient/testdata/mountstats
vendored
Normal file
231
plugins/inputs/nfsclient/testdata/mountstats
vendored
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue