1 /**
2 * collectd - src/nfs.c
3 * Copyright (C) 2005,2006 Jason Pepas
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * Authors:
20 * Jason Pepas <cell at ices.utexas.edu>
21 * Florian octo Forster <octo at verplant.org>
22 **/
24 #include "collectd.h"
25 #include "common.h"
26 #include "plugin.h"
28 #define MODULE_NAME "nfs"
30 /* #if defined(KERNEL_LINUX) || defined(HAVE_LIBKSTAT) */
31 #if KERNEL_LINUX
32 # define NFS_HAVE_READ 1
33 #else
34 # define NFS_HAVE_READ 0
35 #endif
37 static char *nfs2_procedures_file = "nfs2_procedures-%s.rrd";
38 static char *nfs3_procedures_file = "nfs3_procedures-%s.rrd";
40 /*
41 see /proc/net/rpc/nfs
42 see http://www.missioncriticallinux.com/orph/NFS-Statistics
44 net x x x x
45 rpc_stat.netcnt Not used; always zero.
46 rpc_stat.netudpcnt Not used; always zero.
47 rpc_stat.nettcpcnt Not used; always zero.
48 rpc_stat.nettcpconn Not used; always zero.
50 rpc x x x
51 rpc_stat.rpccnt The number of RPC calls.
52 rpc_stat.rpcretrans The number of retransmitted RPC calls.
53 rpc_stat.rpcauthrefresh The number of credential refreshes.
55 proc2 x x x...
56 proc3 x x x...
58 Procedure NFS Version NFS Version 3
59 Number Procedures Procedures
61 0 null null
62 1 getattr getattr
63 2 setattr setattr
64 3 root lookup
65 4 lookup access
66 5 readlink readlink
67 6 read read
68 7 wrcache write
69 8 write create
70 9 create mkdir
71 10 remove symlink
72 11 rename mknod
73 12 link remove
74 13 symlink rmdir
75 14 mkdir rename
76 15 rmdir link
77 16 readdir readdir
78 17 fsstat readdirplus
79 18 fsstat
80 19 fsinfo
81 20 pathconf
82 21 commit
83 */
85 static char *nfs2_procedures_ds_def[] =
86 {
87 "DS:null:COUNTER:"COLLECTD_HEARTBEAT":0:U",
88 "DS:getattr:COUNTER:"COLLECTD_HEARTBEAT":0:U",
89 "DS:setattr:COUNTER:"COLLECTD_HEARTBEAT":0:U",
90 "DS:root:COUNTER:"COLLECTD_HEARTBEAT":0:U",
91 "DS:lookup:COUNTER:"COLLECTD_HEARTBEAT":0:U",
92 "DS:readlink:COUNTER:"COLLECTD_HEARTBEAT":0:U",
93 "DS:read:COUNTER:"COLLECTD_HEARTBEAT":0:U",
94 "DS:wrcache:COUNTER:"COLLECTD_HEARTBEAT":0:U",
95 "DS:write:COUNTER:"COLLECTD_HEARTBEAT":0:U",
96 "DS:create:COUNTER:"COLLECTD_HEARTBEAT":0:U",
97 "DS:remove:COUNTER:"COLLECTD_HEARTBEAT":0:U",
98 "DS:rename:COUNTER:"COLLECTD_HEARTBEAT":0:U",
99 "DS:link:COUNTER:"COLLECTD_HEARTBEAT":0:U",
100 "DS:symlink:COUNTER:"COLLECTD_HEARTBEAT":0:U",
101 "DS:mkdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
102 "DS:rmdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
103 "DS:readdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
104 "DS:fsstat:COUNTER:"COLLECTD_HEARTBEAT":0:U",
105 NULL
106 };
107 static int nfs2_procedures_ds_num = 18;
109 static char *nfs3_procedures_ds_def[] =
110 {
111 "DS:null:COUNTER:"COLLECTD_HEARTBEAT":0:U",
112 "DS:getattr:COUNTER:"COLLECTD_HEARTBEAT":0:U",
113 "DS:setattr:COUNTER:"COLLECTD_HEARTBEAT":0:U",
114 "DS:lookup:COUNTER:"COLLECTD_HEARTBEAT":0:U",
115 "DS:access:COUNTER:"COLLECTD_HEARTBEAT":0:U",
116 "DS:readlink:COUNTER:"COLLECTD_HEARTBEAT":0:U",
117 "DS:read:COUNTER:"COLLECTD_HEARTBEAT":0:U",
118 "DS:write:COUNTER:"COLLECTD_HEARTBEAT":0:U",
119 "DS:create:COUNTER:"COLLECTD_HEARTBEAT":0:U",
120 "DS:mkdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
121 "DS:symlink:COUNTER:"COLLECTD_HEARTBEAT":0:U",
122 "DS:mknod:COUNTER:"COLLECTD_HEARTBEAT":0:U",
123 "DS:remove:COUNTER:"COLLECTD_HEARTBEAT":0:U",
124 "DS:rmdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
125 "DS:rename:COUNTER:"COLLECTD_HEARTBEAT":0:U",
126 "DS:link:COUNTER:"COLLECTD_HEARTBEAT":0:U",
127 "DS:readdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
128 "DS:readdirplus:COUNTER:"COLLECTD_HEARTBEAT":0:U",
129 "DS:fsstat:COUNTER:"COLLECTD_HEARTBEAT":0:U",
130 "DS:fsinfo:COUNTER:"COLLECTD_HEARTBEAT":0:U",
131 "DS:pathconf:COUNTER:"COLLECTD_HEARTBEAT":0:U",
132 "DS:commit:COUNTER:"COLLECTD_HEARTBEAT":0:U",
133 NULL
134 };
135 static int nfs3_procedures_ds_num = 22;
137 #if HAVE_LIBKSTAT && 0
138 extern kstat_ctl_t *kc;
139 static kstat_t *nfs2_ksp_client;
140 static kstat_t *nfs2_ksp_server;
141 static kstat_t *nfs3_ksp_client;
142 static kstat_t *nfs3_ksp_server;
143 static kstat_t *nfs4_ksp_client;
144 static kstat_t *nfs4_ksp_server;
145 #endif
147 /* Possibly TODO: NFSv4 statistics */
149 static void nfs_init (void)
150 {
151 #if HAVE_LIBKSTAT && 0
152 kstat_t *ksp_chain;
154 nfs2_ksp_client = NULL;
155 nfs2_ksp_server = NULL;
156 nfs3_ksp_client = NULL;
157 nfs3_ksp_server = NULL;
158 nfs4_ksp_client = NULL;
159 nfs4_ksp_server = NULL;
161 if (kc == NULL)
162 return;
164 for (ksp_chain = kc->kc_chain; ksp_chain != NULL;
165 ksp_chain = ksp_chain->ks_next)
166 {
167 if (strncmp (ksp_chain->ks_module, "nfs", 3) != 0)
168 continue;
169 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v2", 13) == 0)
170 nfs2_ksp_server = ksp_chain;
171 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v3", 13) == 0)
172 nfs3_ksp_server = ksp_chain;
173 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v4", 13) == 0)
174 nfs4_ksp_server = ksp_chain;
175 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v2", 12) == 0)
176 nfs2_ksp_client = ksp_chain;
177 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v3", 12) == 0)
178 nfs3_ksp_client = ksp_chain;
179 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
180 nfs4_ksp_client = ksp_chain;
181 }
182 #endif
184 return;
185 }
187 #define BUFSIZE 1024
188 static void nfs2_procedures_write (char *host, char *inst, char *val)
189 {
190 char filename[BUFSIZE];
192 if (snprintf (filename, BUFSIZE, nfs2_procedures_file, inst) > BUFSIZE)
193 return;
195 rrd_update_file (host, filename, val, nfs2_procedures_ds_def,
196 nfs2_procedures_ds_num);
197 }
199 static void nfs3_procedures_write (char *host, char *inst, char *val)
200 {
201 char filename[BUFSIZE];
203 if (snprintf (filename, BUFSIZE, nfs3_procedures_file, inst) > BUFSIZE)
204 return;
206 rrd_update_file (host, filename, val, nfs3_procedures_ds_def,
207 nfs3_procedures_ds_num);
208 }
210 #if NFS_HAVE_READ
211 static void nfs2_procedures_submit (unsigned long long *val, char *inst)
212 {
213 char buf[BUFSIZE];
214 int retval = 0;
216 retval = snprintf (buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:"
217 "%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:"
218 "%llu:%llu:%llu", /* 18x %llu */
219 (unsigned int) curtime,
220 val[0], val[1], val[2], val[3], val[4], val[5], val[6],
221 val[7], val[8], val[9], val[10], val[11], val[12],
222 val[13], val[14], val[15], val[16], val[17]);
225 if (retval >= BUFSIZE)
226 return;
227 else if (retval < 0)
228 {
229 syslog (LOG_ERR, "nfs: snprintf's format failed: %s", strerror (errno));
230 return;
231 }
233 plugin_submit ("nfs2_procedures", inst, buf);
234 }
236 static void nfs3_procedures_submit (unsigned long long *val, char *inst)
237 {
238 char buf[BUFSIZE];
239 int retval = 0;
241 retval = snprintf(buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:"
242 "%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:"
243 "%llu:%llu:%llu:%llu:%llu:%llu:%llu", /* 22x %llu */
244 (unsigned int) curtime,
245 val[0], val[1], val[2], val[3], val[4], val[5], val[6],
246 val[7], val[8], val[9], val[10], val[11], val[12],
247 val[13], val[14], val[15], val[16], val[17], val[18],
248 val[19], val[20], val[21]);
250 if (retval >= BUFSIZE)
251 return;
252 else if (retval < 0)
253 {
254 syslog (LOG_ERR, "nfs: snprintf's format failed: %s", strerror (errno));
255 return;
256 }
258 plugin_submit("nfs3_procedures", inst, buf);
259 }
260 #endif /* NFS_HAVE_READ */
262 #if KERNEL_LINUX
263 static void nfs_read_stats_file (FILE *fh, char *inst)
264 {
265 char buffer[BUFSIZE];
267 char *fields[48];
268 int numfields = 0;
270 if (fh == NULL)
271 return;
273 while (fgets (buffer, BUFSIZE, fh) != NULL)
274 {
275 numfields = strsplit (buffer, fields, 48);
277 if (numfields < 2)
278 continue;
280 if (strncmp (fields[0], "proc2", 5) == 0)
281 {
282 int i;
283 unsigned long long *values;
285 if (numfields - 2 != nfs2_procedures_ds_num)
286 {
287 syslog (LOG_WARNING, "nfs: Wrong number of fields (= %i) for NFS2 statistics.", numfields - 2);
288 continue;
289 }
291 if ((values = (unsigned long long *) malloc (nfs2_procedures_ds_num * sizeof (unsigned long long))) == NULL)
292 {
293 syslog (LOG_ERR, "nfs: malloc: %s", strerror (errno));
294 continue;
295 }
297 for (i = 0; i < nfs2_procedures_ds_num; i++)
298 values[i] = atoll (fields[i + 2]);
300 nfs2_procedures_submit (values, inst);
302 free (values);
303 }
304 else if (strncmp (fields[0], "proc3", 5) == 0)
305 {
306 int i;
307 unsigned long long *values;
309 if (numfields - 2 != nfs3_procedures_ds_num)
310 {
311 syslog (LOG_WARNING, "nfs: Wrong number of fields (= %i) for NFS3 statistics.", numfields - 2);
312 continue;
313 }
315 if ((values = (unsigned long long *) malloc (nfs3_procedures_ds_num * sizeof (unsigned long long))) == NULL)
316 {
317 syslog (LOG_ERR, "nfs: malloc: %s", strerror (errno));
318 continue;
319 }
321 for (i = 0; i < nfs3_procedures_ds_num; i++)
322 values[i] = atoll (fields[i + 2]);
324 nfs3_procedures_submit (values, inst);
326 free (values);
327 }
328 }
329 }
330 #endif /* defined(KERNEL_LINUX) */
331 #undef BUFSIZE
333 #if HAVE_LIBKSTAT && 0
334 static void nfs2_read_kstat (kstat_t *ksp, char *inst)
335 {
336 unsigned long long values[18];
338 values[0] = get_kstat_value (ksp, "null");
339 values[1] = get_kstat_value (ksp, "getattr");
340 values[2] = get_kstat_value (ksp, "setattr");
341 values[3] = get_kstat_value (ksp, "root");
342 values[4] = get_kstat_value (ksp, "lookup");
343 values[5] = get_kstat_value (ksp, "readlink");
344 values[6] = get_kstat_value (ksp, "read");
345 values[7] = get_kstat_value (ksp, "wrcache");
346 values[8] = get_kstat_value (ksp, "write");
347 values[9] = get_kstat_value (ksp, "create");
348 values[10] = get_kstat_value (ksp, "remove");
349 values[11] = get_kstat_value (ksp, "rename");
350 values[12] = get_kstat_value (ksp, "link");
351 values[13] = get_kstat_value (ksp, "symlink");
352 values[14] = get_kstat_value (ksp, "mkdir");
353 values[15] = get_kstat_value (ksp, "rmdir");
354 values[16] = get_kstat_value (ksp, "readdir");
355 values[17] = get_kstat_value (ksp, "statfs");
357 nfs2_procedures_submit (values, inst);
358 }
359 #endif
361 #if NFS_HAVE_READ
362 static void nfs_read (void)
363 {
364 #if KERNEL_LINUX
365 FILE *fh;
367 if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
368 {
369 nfs_read_stats_file (fh, "client");
370 fclose (fh);
371 }
373 if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
374 {
375 nfs_read_stats_file (fh, "server");
376 fclose (fh);
377 }
379 /* #endif defined(KERNEL_LINUX) */
381 #elif HAVE_LIBKSTAT && 0
382 if (nfs2_ksp_client != NULL)
383 nfs2_read_kstat (nfs2_ksp_client, "client");
384 if (nfs2_ksp_server != NULL)
385 nfs2_read_kstat (nfs2_ksp_server, "server");
386 #endif /* defined(HAVE_LIBKSTAT) */
387 }
388 #else
389 # define nfs_read NULL
390 #endif /* NFS_HAVE_READ */
392 void module_register (void)
393 {
394 plugin_register (MODULE_NAME, nfs_init, nfs_read, NULL);
395 plugin_register ("nfs2_procedures", NULL, NULL, nfs2_procedures_write);
396 plugin_register ("nfs3_procedures", NULL, NULL, nfs3_procedures_write);
397 }
399 #undef MODULE_NAME