aboutsummaryrefslogtreecommitdiff
path: root/collector/nfs_linux.go
diff options
context:
space:
mode:
authorEd Schouten <ed@kumina.nl>2016-11-29 14:32:52 +0100
committerEd Schouten <ed@nuxi.nl>2016-12-09 19:58:08 +0100
commita696830c38f8dcb171d36651d0b10fe58cf28d4c (patch)
tree3dc888745af0d67a237305c2cbf9157af2fc2980 /collector/nfs_linux.go
parent006d1c7922b765f458fe9b92ce646641bded0f52 (diff)
downloadprometheus_node_collector-a696830c38f8dcb171d36651d0b10fe58cf28d4c.tar.bz2
prometheus_node_collector-a696830c38f8dcb171d36651d0b10fe58cf28d4c.tar.xz
prometheus_node_collector-a696830c38f8dcb171d36651d0b10fe58cf28d4c.zip
Add a collector for NFS client statistics.
This change adds a new collector called "nfs" that parses the contents of /proc/net/rpc/nfs and turns it into metrics. It can be used to inspect the number of operations per type, but also to keep an eye on an extraneous number of retransmissions, which may indicate connectivity issues. I've picked the name "nfs", as most operating systems use "nfs" for the client component and "nfsd" as the server component. If we want to add stats for the NFS server as well, we'd better call such a collector "nfsd".
Diffstat (limited to 'collector/nfs_linux.go')
-rw-r--r--collector/nfs_linux.go176
1 files changed, 176 insertions, 0 deletions
diff --git a/collector/nfs_linux.go b/collector/nfs_linux.go
new file mode 100644
index 0000000..70083d3
--- /dev/null
+++ b/collector/nfs_linux.go
@@ -0,0 +1,176 @@
1// Copyright 2016 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14package collector
15
16import (
17 "errors"
18 "io/ioutil"
19 "os"
20 "regexp"
21 "strconv"
22 "strings"
23
24 "github.com/prometheus/client_golang/prometheus"
25 "github.com/prometheus/common/log"
26)
27
28var (
29 netLineRE = regexp.MustCompile(`^net \d+ (\d+) (\d+) (\d+)$`)
30 rpcLineRE = regexp.MustCompile(`^rpc (\d+) (\d+) (\d+)$`)
31 procLineRE = regexp.MustCompile(`^proc(\d+) \d+ (\d+( \d+)*)$`)
32
33 nfsProcedures = map[string][]string{
34 "2": []string{
35 "null", "getattr", "setattr", "root", "lookup",
36 "readlink", "read", "writecache", "write", "create",
37 "remove", "rename", "link", "symlink", "mkdir",
38 "rmdir", "readdir", "statfs",
39 },
40 "3": []string{
41 "null", "getattr", "setattr", "lookup", "access",
42 "readlink", "read", "write", "create", "mkdir",
43 "symlink", "mknod", "remove", "rmdir", "rename",
44 "link", "readdir", "readdirplus", "fsstat", "fsinfo",
45 "pathconf", "commit",
46 },
47 "4": []string{
48 "null", "read", "write", "commit", "open",
49 "open_confirm", "open_noattr", "open_downgrade",
50 "close", "setattr", "fsinfo", "renew", "setclientid",
51 "setclientid_confirm", "lock", "lockt", "locku",
52 "access", "getattr", "lookup", "lookup_root", "remove",
53 "rename", "link", "symlink", "create", "pathconf",
54 "statfs", "readlink", "readdir", "server_caps",
55 "delegreturn", "getacl", "setacl", "fs_locations",
56 "release_lockowner", "secinfo", "fsid_present",
57 "exchange_id", "create_session", "destroy_session",
58 "sequence", "get_lease_time", "reclaim_complete",
59 "layoutget", "getdeviceinfo", "layoutcommit",
60 "layoutreturn", "secinfo_no_name", "test_stateid",
61 "free_stateid", "getdevicelist",
62 "bind_conn_to_session", "destroy_clientid", "seek",
63 "allocate", "deallocate", "layoutstats", "clone",
64 "copy",
65 },
66 }
67
68 nfsNetReadsDesc = prometheus.NewDesc(
69 prometheus.BuildFQName(Namespace, "nfs", "net_reads"),
70 "Number of reads at the network layer.",
71 []string{"protocol"},
72 nil,
73 )
74 nfsNetConnectionsDesc = prometheus.NewDesc(
75 prometheus.BuildFQName(Namespace, "nfs", "net_connections"),
76 "Number of connections at the network layer.",
77 []string{"protocol"},
78 nil,
79 )
80
81 nfsRpcOperationsDesc = prometheus.NewDesc(
82 prometheus.BuildFQName(Namespace, "nfs", "rpc_operations"),
83 "Number of RPCs performed.",
84 nil,
85 nil,
86 )
87 nfsRpcRetransmissionsDesc = prometheus.NewDesc(
88 prometheus.BuildFQName(Namespace, "nfs", "rpc_retransmissions"),
89 "Number of RPC transmissions performed.",
90 nil,
91 nil,
92 )
93 nfsRpcAuthenticationRefreshesDesc = prometheus.NewDesc(
94 prometheus.BuildFQName(Namespace, "nfs", "rpc_authentication_refreshes"),
95 "Number of RPC authentication refreshes performed.",
96 nil,
97 nil,
98 )
99
100 nfsProceduresDesc = prometheus.NewDesc(
101 prometheus.BuildFQName(Namespace, "nfs", "procedures"),
102 "Number of NFS procedures invoked.",
103 []string{"version", "procedure"},
104 nil,
105 )
106)
107
108type nfsCollector struct{}
109
110func init() {
111 Factories["nfs"] = NewNfsCollector
112}
113
114func NewNfsCollector() (Collector, error) {
115 return &nfsCollector{}, nil
116}
117
118func (c *nfsCollector) Update(ch chan<- prometheus.Metric) (err error) {
119 statsFile := procFilePath("net/rpc/nfs")
120 content, err := ioutil.ReadFile(statsFile)
121 if err != nil {
122 if os.IsNotExist(err) {
123 log.Debugf("Not collecting NFS statistics, as %s does not exist: %s", statsFile)
124 return nil
125 }
126 return err
127 }
128
129 for _, line := range strings.Split(string(content), "\n") {
130 if fields := netLineRE.FindStringSubmatch(line); fields != nil {
131 value, _ := strconv.ParseFloat(fields[1], 64)
132 ch <- prometheus.MustNewConstMetric(
133 nfsNetReadsDesc, prometheus.CounterValue,
134 value, "udp")
135
136 value, _ = strconv.ParseFloat(fields[2], 64)
137 ch <- prometheus.MustNewConstMetric(
138 nfsNetReadsDesc, prometheus.CounterValue,
139 value, "tcp")
140
141 value, _ = strconv.ParseFloat(fields[3], 64)
142 ch <- prometheus.MustNewConstMetric(
143 nfsNetConnectionsDesc, prometheus.CounterValue,
144 value, "tcp")
145 } else if fields := rpcLineRE.FindStringSubmatch(line); fields != nil {
146 value, _ := strconv.ParseFloat(fields[1], 64)
147 ch <- prometheus.MustNewConstMetric(
148 nfsRpcOperationsDesc,
149 prometheus.CounterValue, value)
150
151 value, _ = strconv.ParseFloat(fields[2], 64)
152 ch <- prometheus.MustNewConstMetric(
153 nfsRpcRetransmissionsDesc,
154 prometheus.CounterValue, value)
155
156 value, _ = strconv.ParseFloat(fields[3], 64)
157 ch <- prometheus.MustNewConstMetric(
158 nfsRpcAuthenticationRefreshesDesc,
159 prometheus.CounterValue, value)
160 } else if fields := procLineRE.FindStringSubmatch(line); fields != nil {
161 version := fields[1]
162 for procedure, count := range strings.Split(fields[2], " ") {
163 value, _ := strconv.ParseFloat(count, 64)
164 ch <- prometheus.MustNewConstMetric(
165 nfsProceduresDesc,
166 prometheus.CounterValue,
167 value,
168 version,
169 nfsProcedures[version][procedure])
170 }
171 } else if line != "" {
172 return errors.New("Failed to parse line: " + line)
173 }
174 }
175 return nil
176}