Replaced all hardcoded heartbeat values (== 25 seconds) with `COLLECTD_HEARTBEAT'.
[collectd.git] / src / nfs.c
1 /**
2  * collectd - src/nfs.c
3  * Copyright (C) 2005  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  **/
23
24 #include "collectd.h"
25 #include "common.h"
26 #include "plugin.h"
27
28 #define MODULE_NAME "nfs"
29
30 #if defined(KERNEL_LINUX) || defined(HAVE_LIBKSTAT)
31 # define NFS_HAVE_READ 1
32 #else
33 # define NFS_HAVE_READ 0
34 #endif
35
36 static char *nfs2_procedures_file  = "nfs2_procedures-%s.rrd";
37 static char *nfs3_procedures_file  = "nfs3_procedures-%s.rrd";
38
39 /*
40 see /proc/net/rpc/nfs
41 see http://www.missioncriticallinux.com/orph/NFS-Statistics
42
43 net x x x x
44 rpc_stat.netcnt         Not used; always zero.
45 rpc_stat.netudpcnt      Not used; always zero.
46 rpc_stat.nettcpcnt      Not used; always zero.
47 rpc_stat.nettcpconn     Not used; always zero.
48
49 rpc x x x
50 rpc_stat.rpccnt             The number of RPC calls.
51 rpc_stat.rpcretrans         The number of retransmitted RPC calls.
52 rpc_stat.rpcauthrefresh     The number of credential refreshes.
53
54 proc2 x x x...
55 proc3 x x x...
56
57 Procedure   NFS Version NFS Version 3
58 Number      Procedures  Procedures
59
60 0           null        null
61 1           getattr     getattr
62 2           setattr     setattr
63 3           root        lookup
64 4           lookup      access
65 5           readlink    readlink
66 6           read        read
67 7           wrcache     write
68 8           write       create
69 9           create      mkdir
70 10          remove      symlink
71 11          rename      mknod
72 12          link        remove
73 13          symlink     rmdir
74 14          mkdir       rename
75 15          rmdir       link
76 16          readdir     readdir
77 17          fsstat      readdirplus
78 18                      fsstat
79 19                      fsinfo
80 20                      pathconf
81 21                      commit
82 */
83
84 static char *nfs2_procedures_ds_def[] =
85 {
86         "DS:null:COUNTER:"COLLECTD_HEARTBEAT":0:U",
87         "DS:getattr:COUNTER:"COLLECTD_HEARTBEAT":0:U",
88         "DS:setattr:COUNTER:"COLLECTD_HEARTBEAT":0:U",
89         "DS:root:COUNTER:"COLLECTD_HEARTBEAT":0:U",
90         "DS:lookup:COUNTER:"COLLECTD_HEARTBEAT":0:U",
91         "DS:readlink:COUNTER:"COLLECTD_HEARTBEAT":0:U",
92         "DS:read:COUNTER:"COLLECTD_HEARTBEAT":0:U",
93         "DS:wrcache:COUNTER:"COLLECTD_HEARTBEAT":0:U",
94         "DS:write:COUNTER:"COLLECTD_HEARTBEAT":0:U",
95         "DS:create:COUNTER:"COLLECTD_HEARTBEAT":0:U",
96         "DS:remove:COUNTER:"COLLECTD_HEARTBEAT":0:U",
97         "DS:rename:COUNTER:"COLLECTD_HEARTBEAT":0:U",
98         "DS:link:COUNTER:"COLLECTD_HEARTBEAT":0:U",
99         "DS:symlink:COUNTER:"COLLECTD_HEARTBEAT":0:U",
100         "DS:mkdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
101         "DS:rmdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
102         "DS:readdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
103         "DS:fsstat:COUNTER:"COLLECTD_HEARTBEAT":0:U",
104         NULL
105 };
106 static int nfs2_procedures_ds_num = 18;
107
108 static char *nfs3_procedures_ds_def[] =
109 {
110         "DS:null:COUNTER:"COLLECTD_HEARTBEAT":0:U",
111         "DS:getattr:COUNTER:"COLLECTD_HEARTBEAT":0:U",
112         "DS:setattr:COUNTER:"COLLECTD_HEARTBEAT":0:U",
113         "DS:lookup:COUNTER:"COLLECTD_HEARTBEAT":0:U",
114         "DS:access:COUNTER:"COLLECTD_HEARTBEAT":0:U",
115         "DS:readlink:COUNTER:"COLLECTD_HEARTBEAT":0:U",
116         "DS:read:COUNTER:"COLLECTD_HEARTBEAT":0:U",
117         "DS:write:COUNTER:"COLLECTD_HEARTBEAT":0:U",
118         "DS:create:COUNTER:"COLLECTD_HEARTBEAT":0:U",
119         "DS:mkdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
120         "DS:symlink:COUNTER:"COLLECTD_HEARTBEAT":0:U",
121         "DS:mknod:COUNTER:"COLLECTD_HEARTBEAT":0:U",
122         "DS:remove:COUNTER:"COLLECTD_HEARTBEAT":0:U",
123         "DS:rmdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
124         "DS:rename:COUNTER:"COLLECTD_HEARTBEAT":0:U",
125         "DS:link:COUNTER:"COLLECTD_HEARTBEAT":0:U",
126         "DS:readdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
127         "DS:readdirplus:COUNTER:"COLLECTD_HEARTBEAT":0:U",
128         "DS:fsstat:COUNTER:"COLLECTD_HEARTBEAT":0:U",
129         "DS:fsinfo:COUNTER:"COLLECTD_HEARTBEAT":0:U",
130         "DS:pathconf:COUNTER:"COLLECTD_HEARTBEAT":0:U",
131         "DS:commit:COUNTER:"COLLECTD_HEARTBEAT":0:U",
132         NULL
133 };
134 static int nfs3_procedures_ds_num = 22;
135
136 #ifdef HAVE_LIBKSTAT
137 extern kstat_ctl_t *kc;
138 static kstat_t *nfs2_ksp_client;
139 static kstat_t *nfs2_ksp_server;
140 static kstat_t *nfs3_ksp_client;
141 static kstat_t *nfs3_ksp_server;
142 static kstat_t *nfs4_ksp_client;
143 static kstat_t *nfs4_ksp_server;
144 #endif
145
146 /* Possibly TODO: NFSv4 statistics */
147
148 static void nfs_init (void)
149 {
150 #ifdef HAVE_LIBKSTAT
151         kstat_t *ksp_chain;
152
153         nfs2_ksp_client = NULL;
154         nfs2_ksp_server = NULL;
155         nfs3_ksp_client = NULL;
156         nfs3_ksp_server = NULL;
157         nfs4_ksp_client = NULL;
158         nfs4_ksp_server = NULL;
159         
160         if (kc == NULL)
161                 return;
162
163         for (ksp_chain = kc->kc_chain; ksp_chain != NULL;
164                         ksp_chain = ksp_chain->ks_next)
165         {
166                 if (strncmp (ksp_chain->ks_module, "nfs", 3) != 0)
167                         continue;
168                 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v2", 13) == 0)
169                         nfs2_ksp_server = ksp_chain;
170                 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v3", 13) == 0)
171                         nfs3_ksp_server = ksp_chain;
172                 else if (strncmp (ksp_chain->ks_name, "rfsproccnt_v4", 13) == 0)
173                         nfs4_ksp_server = ksp_chain;
174                 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v2", 12) == 0)
175                         nfs2_ksp_client = ksp_chain;
176                 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v3", 12) == 0)
177                         nfs3_ksp_client = ksp_chain;
178                 else if (strncmp (ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
179                         nfs4_ksp_client = ksp_chain;
180         }
181 #endif
182
183         return;
184 }
185
186 #define BUFSIZE 1024
187 static void nfs2_procedures_write (char *host, char *inst, char *val)
188 {
189         char filename[BUFSIZE];
190
191         if (snprintf (filename, BUFSIZE, nfs2_procedures_file, inst) > BUFSIZE)
192                 return;
193
194         rrd_update_file (host, filename, val, nfs2_procedures_ds_def,
195                         nfs2_procedures_ds_num);
196 }
197
198 static void nfs3_procedures_write (char *host, char *inst, char *val)
199 {
200         char filename[BUFSIZE];
201
202         if (snprintf (filename, BUFSIZE, nfs3_procedures_file, inst) > BUFSIZE)
203                 return;
204
205         rrd_update_file (host, filename, val, nfs3_procedures_ds_def,
206                         nfs3_procedures_ds_num);
207 }
208
209 #if NFS_HAVE_READ
210 static void nfs2_procedures_submit (unsigned long long *val, char *inst)
211 {
212         char buf[BUFSIZE];
213         int retval = 0;
214
215         retval = snprintf (buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:"
216                         "%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:"
217                         "%llu:%llu:%llu", /* 18x %llu */
218                         (unsigned int) curtime,
219                         val[0], val[1], val[2], val[3], val[4], val[5], val[6],
220                         val[7], val[8], val[9], val[10], val[11], val[12],
221                         val[13], val[14], val[15], val[16], val[17]);
222
223
224         if (retval >= BUFSIZE)
225                 return;
226         else if (retval < 0)
227         {
228                 syslog (LOG_ERR, "nfs: snprintf's format failed: %s", strerror (errno));
229                 return;
230         }
231
232         plugin_submit ("nfs2_procedures", inst, buf);
233 }
234
235 static void nfs3_procedures_submit (unsigned long long *val, char *inst)
236 {
237         char buf[BUFSIZE];
238         int retval = 0;
239
240         retval = snprintf(buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:"
241                         "%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:"
242                         "%llu:%llu:%llu:%llu:%llu:%llu:%llu", /* 22x %llu */
243                         (unsigned int) curtime,
244                         val[0], val[1], val[2], val[3], val[4], val[5], val[6],
245                         val[7], val[8], val[9], val[10], val[11], val[12],
246                         val[13], val[14], val[15], val[16], val[17], val[18],
247                         val[19], val[20], val[21]);
248
249         if (retval >= BUFSIZE)
250                 return;
251         else if (retval < 0)
252         {
253                 syslog (LOG_ERR, "nfs: snprintf's format failed: %s", strerror (errno));
254                 return;
255         }
256
257         plugin_submit("nfs3_procedures", inst, buf);
258 }
259 #endif /* NFS_HAVE_READ */
260
261 #if defined(KERNEL_LINUX)
262 static void nfs_read_stats_file (FILE *fh, char *inst)
263 {
264         char buffer[BUFSIZE];
265
266         char *fields[48];
267         int numfields = 0;
268
269         if (fh == NULL)
270                 return;
271
272         while (fgets (buffer, BUFSIZE, fh) != NULL)
273         {
274                 numfields = strsplit (buffer, fields, 48);
275
276                 if (numfields < 2)
277                         continue;
278
279                 if (strncmp (fields[0], "proc2", 5) == 0)
280                 {
281                         int i;
282                         unsigned long long *values;
283
284                         if (numfields - 2 != nfs2_procedures_ds_num)
285                         {
286                                 syslog (LOG_WARNING, "nfs: Wrong number of fields (= %i) for NFS2 statistics.", numfields - 2);
287                                 continue;
288                         }
289
290                         if ((values = (unsigned long long *) malloc (nfs2_procedures_ds_num * sizeof (unsigned long long))) == NULL)
291                         {
292                                 syslog (LOG_ERR, "nfs: malloc: %s", strerror (errno));
293                                 continue;
294                         }
295
296                         for (i = 0; i < nfs2_procedures_ds_num; i++)
297                                 values[i] = atoll (fields[i + 2]);
298
299                         nfs2_procedures_submit (values, inst);
300
301                         free (values);
302                 }
303                 else if (strncmp (fields[0], "proc3", 5) == 0)
304                 {
305                         int i;
306                         unsigned long long *values;
307
308                         if (numfields - 2 != nfs3_procedures_ds_num)
309                         {
310                                 syslog (LOG_WARNING, "nfs: Wrong number of fields (= %i) for NFS3 statistics.", numfields - 2);
311                                 continue;
312                         }
313
314                         if ((values = (unsigned long long *) malloc (nfs3_procedures_ds_num * sizeof (unsigned long long))) == NULL)
315                         {
316                                 syslog (LOG_ERR, "nfs: malloc: %s", strerror (errno));
317                                 continue;
318                         }
319
320                         for (i = 0; i < nfs3_procedures_ds_num; i++)
321                                 values[i] = atoll (fields[i + 2]);
322
323                         nfs3_procedures_submit (values, inst);
324
325                         free (values);
326                 }
327         }
328 }
329 #endif /* defined(KERNEL_LINUX) */
330 #undef BUFSIZE
331
332 #ifdef HAVE_LIBKSTAT
333 static void nfs2_read_kstat (kstat_t *ksp, char *inst)
334 {
335         unsigned long long values[18];
336
337         values[0] = get_kstat_value (ksp, "null");
338         values[1] = get_kstat_value (ksp, "getattr");
339         values[2] = get_kstat_value (ksp, "setattr");
340         values[3] = get_kstat_value (ksp, "root");
341         values[4] = get_kstat_value (ksp, "lookup");
342         values[5] = get_kstat_value (ksp, "readlink");
343         values[6] = get_kstat_value (ksp, "read");
344         values[7] = get_kstat_value (ksp, "wrcache");
345         values[8] = get_kstat_value (ksp, "write");
346         values[9] = get_kstat_value (ksp, "create");
347         values[10] = get_kstat_value (ksp, "remove");
348         values[11] = get_kstat_value (ksp, "rename");
349         values[12] = get_kstat_value (ksp, "link");
350         values[13] = get_kstat_value (ksp, "symlink");
351         values[14] = get_kstat_value (ksp, "mkdir");
352         values[15] = get_kstat_value (ksp, "rmdir");
353         values[16] = get_kstat_value (ksp, "readdir");
354         values[17] = get_kstat_value (ksp, "statfs");
355
356         nfs2_procedures_submit (values, inst);
357 }
358 #endif
359
360 #if NFS_HAVE_READ
361 static void nfs_read (void)
362 {
363 #if defined(KERNEL_LINUX)
364         FILE *fh;
365
366         if ((fh = fopen ("/proc/net/rpc/nfs", "r")) != NULL)
367         {
368                 nfs_read_stats_file (fh, "client");
369                 fclose (fh);
370         }
371
372         if ((fh = fopen ("/proc/net/rpc/nfsd", "r")) != NULL)
373         {
374                 nfs_read_stats_file (fh, "server");
375                 fclose (fh);
376         }
377
378 /* #endif defined(KERNEL_LINUX) */
379
380 #elif defined(HAVE_LIBKSTAT)
381         if (nfs2_ksp_client != NULL)
382                 nfs2_read_kstat (nfs2_ksp_client, "client");
383         if (nfs2_ksp_server != NULL)
384                 nfs2_read_kstat (nfs2_ksp_server, "server");
385 #endif /* defined(HAVE_LIBKSTAT) */
386 }
387 #else
388 # define nfs_read NULL
389 #endif /* NFS_HAVE_READ */
390
391 void module_register (void)
392 {
393         plugin_register (MODULE_NAME, nfs_init, nfs_read, NULL);
394         plugin_register ("nfs2_procedures", NULL, NULL, nfs2_procedures_write);
395         plugin_register ("nfs3_procedures", NULL, NULL, nfs3_procedures_write);
396 }
397
398 #undef MODULE_NAME