Code

Merge pull request #2346 from trenkel/master
[collectd.git] / src / nfs.c
1 /**
2  * collectd - src/nfs.c
3  * Copyright (C) 2005,2006  Jason Pepas
4  * Copyright (C) 2012,2013  Florian Forster
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; only version 2 of the License is applicable.
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 collectd.org>
22  *   Cosmin Ioiart <cioiart at gmail.com>
23  **/
25 #include "collectd.h"
27 #include "common.h"
28 #include "plugin.h"
30 #if HAVE_KSTAT_H
31 #include <kstat.h>
32 #endif
34 static const char *config_keys[] = {"ReportV2", "ReportV3", "ReportV4"};
35 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
36 static _Bool report_v2 = 1;
37 static _Bool report_v3 = 1;
38 static _Bool report_v4 = 1;
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 const char *nfs2_procedures_names[] = {
86     "null", "getattr", "setattr", "root",   "lookup",  "readlink",
87     "read", "wrcache", "write",   "create", "remove",  "rename",
88     "link", "symlink", "mkdir",   "rmdir",  "readdir", "fsstat"};
89 static size_t nfs2_procedures_names_num =
90     STATIC_ARRAY_SIZE(nfs2_procedures_names);
92 static const char *nfs3_procedures_names[] = {
93     "null",   "getattr", "setattr",  "lookup", "access",  "readlink",
94     "read",   "write",   "create",   "mkdir",  "symlink", "mknod",
95     "remove", "rmdir",   "rename",   "link",   "readdir", "readdirplus",
96     "fsstat", "fsinfo",  "pathconf", "commit"};
97 static size_t nfs3_procedures_names_num =
98     STATIC_ARRAY_SIZE(nfs3_procedures_names);
100 #if HAVE_LIBKSTAT
101 static const char *nfs4_procedures_names[] = {"null",
102                                               "compound",
103                                               "reserved",
104                                               "access",
105                                               "close",
106                                               "commit",
107                                               "create",
108                                               "delegpurge",
109                                               "delegreturn",
110                                               "getattr",
111                                               "getfh",
112                                               "link",
113                                               "lock",
114                                               "lockt",
115                                               "locku",
116                                               "lookup",
117                                               "lookupp",
118                                               "nverify",
119                                               "open",
120                                               "openattr",
121                                               "open_confirm",
122                                               "open_downgrade",
123                                               "putfh",
124                                               "putpubfh",
125                                               "putrootfh",
126                                               "read",
127                                               "readdir",
128                                               "readlink",
129                                               "remove",
130                                               "rename",
131                                               "renew",
132                                               "restorefh",
133                                               "savefh",
134                                               "secinfo",
135                                               "setattr",
136                                               "setclientid",
137                                               "setclientid_confirm",
138                                               "verify",
139                                               "write"};
140 static size_t nfs4_procedures_names_num =
141     STATIC_ARRAY_SIZE(nfs4_procedures_names);
142 #endif
144 #if KERNEL_LINUX
145 static const char *nfs4_server40_procedures_names[] = {"null",
146                                                        "compound",
147                                                        "reserved",
148                                                        "access",
149                                                        "close",
150                                                        "commit",
151                                                        "create",
152                                                        "delegpurge",
153                                                        "delegreturn",
154                                                        "getattr",
155                                                        "getfh",
156                                                        "link",
157                                                        "lock",
158                                                        "lockt",
159                                                        "locku",
160                                                        "lookup",
161                                                        "lookupp",
162                                                        "nverify",
163                                                        "open",
164                                                        "openattr",
165                                                        "open_confirm",
166                                                        "open_downgrade",
167                                                        "putfh",
168                                                        "putpubfh",
169                                                        "putrootfh",
170                                                        "read",
171                                                        "readdir",
172                                                        "readlink",
173                                                        "remove",
174                                                        "rename",
175                                                        "renew",
176                                                        "restorefh",
177                                                        "savefh",
178                                                        "secinfo",
179                                                        "setattr",
180                                                        "setclientid",
181                                                        "setcltid_confirm",
182                                                        "verify",
183                                                        "write",
184                                                        "release_lockowner"};
186 static size_t nfs4_server40_procedures_names_num =
187     STATIC_ARRAY_SIZE(nfs4_server40_procedures_names);
189 static const char *nfs4_server41_procedures_names[] = {
190     "backchannel_ctl",
191     "bind_conn_to_session",
192     "exchange_id",
193     "create_session",
194     "destroy_session",
195     "free_stateid",
196     "get_dir_delegation",
197     "getdeviceinfo",
198     "getdevicelist",
199     "layoutcommit",
200     "layoutget",
201     "layoutreturn",
202     "secinfo_no_name",
203     "sequence",
204     "set_ssv",
205     "test_stateid",
206     "want_delegation",
207     "destroy_clientid",
208     "reclaim_complete",
209 };
211 static size_t nfs4_server41_procedures_names_num =
212     STATIC_ARRAY_SIZE(nfs4_server41_procedures_names);
214 #define NFS4_SERVER40_NUM_PROC                                                 \
215   (STATIC_ARRAY_SIZE(nfs4_server40_procedures_names))
217 #define NFS4_SERVER41_NUM_PROC                                                 \
218   (STATIC_ARRAY_SIZE(nfs4_server40_procedures_names) +                         \
219    STATIC_ARRAY_SIZE(nfs4_server41_procedures_names))
221 #define NFS4_SERVER_MAX_PROC (NFS4_SERVER41_NUM_PROC)
223 static const char *nfs4_client40_procedures_names[] = {
224     "null",
225     "read",
226     "write",
227     "commit",
228     "open",
229     "open_confirm",
230     "open_noattr",
231     "open_downgrade",
232     "close",
233     "setattr",
234     "fsinfo",
235     "renew",
236     "setclientid",
237     "setclientid_confirm",
238     "lock",
239     "lockt",
240     "locku",
241     "access",
242     "getattr",
243     "lookup",
244     "lookupp",
245     "remove",
246     "rename",
247     "link",
248     "symlink",
249     "create",
250     "pathconf",
251     "statfs",
252     "readlink",
253     "readdir",
254     "server_caps",
255     "delegreturn",
256     "getacl",
257     "setacl",
258     "fs_locations",      /* |35| 2.6.18 */
259     "release_lockowner", /* |42| 2.6.36 */
260     "secinfo",           /* |46| 2.6.39 */
261     "fsid_present"       /* |54| 3.13 */
262 };
264 static const char *nfs4_client41_procedures_names[] = {
265     "exchange_id",          /* |40| 2.6.30 */
266     "create_session",       /* |40| 2.6.30 */
267     "destroy_session",      /* |40| 2.6.30 */
268     "sequence",             /* |40| 2.6.30 */
269     "get_lease_time",       /* |40| 2.6.30 */
270     "reclaim_complete",     /* |41| 2.6.33 */
271     "layoutget",            /* |44| 2.6.37 */
272     "getdeviceinfo",        /* |44| 2.6.37 */
273     "layoutcommit",         /* |46| 2.6.39 */
274     "layoutreturn",         /* |47| 3.0 */
275     "secinfo_no_name",      /* |51| 3.1 */
276     "test_stateid",         /* |51| 3.1 */
277     "free_stateid",         /* |51| 3.1 */
278     "getdevicelist",        /* |51| 3.1 */
279     "bind_conn_to_session", /* |53| 3.5 */
280     "destroy_clientid"      /* |53| 3.5 */
281 };
283 #define NFS4_CLIENT40_NUM_PROC                                                 \
284   (STATIC_ARRAY_SIZE(nfs4_client40_procedures_names))
286 #define NFS4_CLIENT41_NUM_PROC                                                 \
287   (STATIC_ARRAY_SIZE(nfs4_client40_procedures_names) +                         \
288    STATIC_ARRAY_SIZE(nfs4_client41_procedures_names))
290 #define NFS4_CLIENT_MAX_PROC (NFS4_CLIENT41_NUM_PROC)
292 #endif
294 #if HAVE_LIBKSTAT
295 extern kstat_ctl_t *kc;
296 static kstat_t *nfs2_ksp_client;
297 static kstat_t *nfs2_ksp_server;
298 static kstat_t *nfs3_ksp_client;
299 static kstat_t *nfs3_ksp_server;
300 static kstat_t *nfs4_ksp_client;
301 static kstat_t *nfs4_ksp_server;
302 #endif
304 static int nfs_config(const char *key, const char *value) {
305   if (strcasecmp(key, "ReportV2") == 0)
306     report_v2 = IS_TRUE(value);
307   else if (strcasecmp(key, "ReportV3") == 0)
308     report_v3 = IS_TRUE(value);
309   else if (strcasecmp(key, "ReportV4") == 0)
310     report_v4 = IS_TRUE(value);
311   else
312     return -1;
314   return 0;
317 #if KERNEL_LINUX
318 static int nfs_init(void) { return 0; }
319 /* #endif KERNEL_LINUX */
321 #elif HAVE_LIBKSTAT
322 static int nfs_init(void) {
323   nfs2_ksp_client = NULL;
324   nfs2_ksp_server = NULL;
325   nfs3_ksp_client = NULL;
326   nfs3_ksp_server = NULL;
327   nfs4_ksp_client = NULL;
328   nfs4_ksp_server = NULL;
330   if (kc == NULL)
331     return -1;
333   for (kstat_t *ksp_chain = kc->kc_chain; ksp_chain != NULL;
334        ksp_chain = ksp_chain->ks_next) {
335     if (strncmp(ksp_chain->ks_module, "nfs", 3) != 0)
336       continue;
337     else if (strncmp(ksp_chain->ks_name, "rfsproccnt_v2", 13) == 0)
338       nfs2_ksp_server = ksp_chain;
339     else if (strncmp(ksp_chain->ks_name, "rfsproccnt_v3", 13) == 0)
340       nfs3_ksp_server = ksp_chain;
341     else if (strncmp(ksp_chain->ks_name, "rfsproccnt_v4", 13) == 0)
342       nfs4_ksp_server = ksp_chain;
343     else if (strncmp(ksp_chain->ks_name, "rfsreqcnt_v2", 12) == 0)
344       nfs2_ksp_client = ksp_chain;
345     else if (strncmp(ksp_chain->ks_name, "rfsreqcnt_v3", 12) == 0)
346       nfs3_ksp_client = ksp_chain;
347     else if (strncmp(ksp_chain->ks_name, "rfsreqcnt_v4", 12) == 0)
348       nfs4_ksp_client = ksp_chain;
349   }
351   return 0;
352 } /* int nfs_init */
353 #endif
355 static void nfs_procedures_submit(const char *plugin_instance,
356                                   const char **type_instances, value_t *values,
357                                   size_t values_num) {
358   value_list_t vl = VALUE_LIST_INIT;
360   vl.values_len = 1;
361   sstrncpy(vl.plugin, "nfs", sizeof(vl.plugin));
362   sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
363   sstrncpy(vl.type, "nfs_procedure", sizeof(vl.type));
365   for (size_t i = 0; i < values_num; i++) {
366     vl.values = values + i;
367     sstrncpy(vl.type_instance, type_instances[i], sizeof(vl.type_instance));
368     plugin_dispatch_values(&vl);
369   }
370 } /* void nfs_procedures_submit */
372 #if KERNEL_LINUX
373 static void nfs_submit_fields(int nfs_version, const char *instance,
374                               char **fields, size_t fields_num,
375                               const char **proc_names) {
376   char plugin_instance[DATA_MAX_NAME_LEN];
377   value_t values[fields_num];
379   snprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
380            instance);
382   for (size_t i = 0; i < fields_num; i++)
383     (void)parse_value(fields[i], &values[i], DS_TYPE_DERIVE);
385   nfs_procedures_submit(plugin_instance, proc_names, values, fields_num);
388 static int nfs_submit_fields_safe(int nfs_version, const char *instance,
389                                   char **fields, size_t fields_num,
390                                   const char **proc_names,
391                                   size_t proc_names_num) {
392   if (fields_num != proc_names_num) {
393     WARNING("nfs plugin: Wrong number of fields for "
394             "NFSv%i %s statistics. Expected %zu, got %zu.",
395             nfs_version, instance, proc_names_num, fields_num);
396     return EINVAL;
397   }
399   nfs_submit_fields(nfs_version, instance, fields, fields_num, proc_names);
401   return 0;
404 static int nfs_submit_nfs4_server(const char *instance, char **fields,
405                                   size_t fields_num) {
406   static int suppress_warning = 0;
408   if (fields_num != NFS4_SERVER40_NUM_PROC &&
409       fields_num != NFS4_SERVER41_NUM_PROC) {
410     if (!suppress_warning) {
411       WARNING("nfs plugin: Unexpected number of fields for "
412               "NFSv4 %s statistics: %zu. ",
413               instance, fields_num);
414     }
416     if (fields_num > NFS4_SERVER_MAX_PROC) {
417       fields_num = NFS4_SERVER_MAX_PROC;
418       suppress_warning = 1;
419     } else {
420       return EINVAL;
421     }
422   }
424   nfs_submit_fields(4, instance, fields, nfs4_server40_procedures_names_num,
425                     nfs4_server40_procedures_names);
427   if (fields_num >= NFS4_SERVER41_NUM_PROC) {
428     fields += nfs4_server40_procedures_names_num;
430     nfs_submit_fields(4, instance, fields, nfs4_server41_procedures_names_num,
431                       nfs4_server41_procedures_names);
432   }
434   return 0;
437 static int nfs_submit_nfs4_client(const char *instance, char **fields,
438                                   size_t fields_num) {
439   size_t proc40_names_num, proc41_names_num;
441   static int suppress_warning = 0;
443   switch (fields_num) {
444   case 34:
445   case 35:
446   case 36:
447   case 37:
448   case 38:
449     /* 4.0-only configuration */
450     proc40_names_num = fields_num;
451     break;
452   case 40:
453   case 41:
454     proc40_names_num = 35;
455     break;
456   case 42:
457   case 44:
458     proc40_names_num = 36;
459     break;
460   case 46:
461   case 47:
462   case 51:
463   case 53:
464     proc40_names_num = 37;
465     break;
466   case 54:
467     proc40_names_num = 38;
468     break;
469   default:
470     if (!suppress_warning) {
471       WARNING("nfs plugin: Unexpected number of "
472               "fields for NFSv4 %s "
473               "statistics: %zu. ",
474               instance, fields_num);
475     }
477     if (fields_num > 34) {
478       /* safe fallback to basic nfs40 procedures */
479       fields_num = 34;
480       proc40_names_num = 34;
482       suppress_warning = 1;
483     } else {
484       return EINVAL;
485     }
486   }
488   nfs_submit_fields(4, instance, fields, proc40_names_num,
489                     nfs4_client40_procedures_names);
491   if (fields_num > proc40_names_num) {
492     proc41_names_num = fields_num - proc40_names_num;
493     fields += proc40_names_num;
495     nfs_submit_fields(4, instance, fields, proc41_names_num,
496                       nfs4_client41_procedures_names);
497   }
499   return 0;
502 static void nfs_read_linux(FILE *fh, const char *inst) {
503   char buffer[1024];
505   char *fields[64];
506   int fields_num = 0;
508   if (fh == NULL)
509     return;
511   while (fgets(buffer, sizeof(buffer), fh) != NULL) {
512     fields_num = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
514     if (fields_num < 3)
515       continue;
517     if (strcmp(fields[0], "proc2") == 0 && report_v2) {
518       nfs_submit_fields_safe(/* version = */ 2, inst, fields + 2,
519                              (size_t)(fields_num - 2), nfs2_procedures_names,
520                              nfs2_procedures_names_num);
521     } else if (strncmp(fields[0], "proc3", 5) == 0 && report_v3) {
522       nfs_submit_fields_safe(/* version = */ 3, inst, fields + 2,
523                              (size_t)(fields_num - 2), nfs3_procedures_names,
524                              nfs3_procedures_names_num);
525     } else if (strcmp(fields[0], "proc4ops") == 0 && report_v4) {
526       if (inst[0] == 's')
527         nfs_submit_nfs4_server(inst, fields + 2, (size_t)(fields_num - 2));
528     } else if (strcmp(fields[0], "proc4") == 0 && report_v4) {
529       if (inst[0] == 'c')
530         nfs_submit_nfs4_client(inst, fields + 2, (size_t)(fields_num - 2));
531     }
532   } /* while (fgets) */
533 } /* void nfs_read_linux */
534 #endif /* KERNEL_LINUX */
536 #if HAVE_LIBKSTAT
537 static int nfs_read_kstat(kstat_t *ksp, int nfs_version, const char *inst,
538                           char const **proc_names, size_t proc_names_num) {
539   char plugin_instance[DATA_MAX_NAME_LEN];
540   value_t values[proc_names_num];
542   if (ksp == NULL)
543     return EINVAL;
545   snprintf(plugin_instance, sizeof(plugin_instance), "v%i%s", nfs_version,
546            inst);
548   kstat_read(kc, ksp, NULL);
549   for (size_t i = 0; i < proc_names_num; i++) {
550     /* The name passed to kstat_data_lookup() doesn't have the
551      * "const" modifier, so we need to copy the name here. */
552     char name[32];
553     sstrncpy(name, proc_names[i], sizeof(name));
555     values[i].counter = (derive_t)get_kstat_value(ksp, name);
556   }
558   nfs_procedures_submit(plugin_instance, proc_names, values, proc_names_num);
559   return 0;
561 #endif
563 #if KERNEL_LINUX
564 static int nfs_read(void) {
565   FILE *fh;
567   if ((fh = fopen("/proc/net/rpc/nfs", "r")) != NULL) {
568     nfs_read_linux(fh, "client");
569     fclose(fh);
570   }
572   if ((fh = fopen("/proc/net/rpc/nfsd", "r")) != NULL) {
573     nfs_read_linux(fh, "server");
574     fclose(fh);
575   }
577   return 0;
579 /* #endif KERNEL_LINUX */
581 #elif HAVE_LIBKSTAT
582 static int nfs_read(void) {
583   if (report_v2) {
584     nfs_read_kstat(nfs2_ksp_client, /* version = */ 2, "client",
585                    nfs2_procedures_names, nfs2_procedures_names_num);
586     nfs_read_kstat(nfs2_ksp_server, /* version = */ 2, "server",
587                    nfs2_procedures_names, nfs2_procedures_names_num);
588   }
589   if (report_v3) {
590     nfs_read_kstat(nfs3_ksp_client, /* version = */ 3, "client",
591                    nfs3_procedures_names, nfs3_procedures_names_num);
592     nfs_read_kstat(nfs3_ksp_server, /* version = */ 3, "server",
593                    nfs3_procedures_names, nfs3_procedures_names_num);
594   }
595   if (report_v4) {
596     nfs_read_kstat(nfs4_ksp_client, /* version = */ 4, "client",
597                    nfs4_procedures_names, nfs4_procedures_names_num);
598     nfs_read_kstat(nfs4_ksp_server, /* version = */ 4, "server",
599                    nfs4_procedures_names, nfs4_procedures_names_num);
600   }
602   return 0;
604 #endif /* HAVE_LIBKSTAT */
606 void module_register(void) {
607   plugin_register_config("nfs", nfs_config, config_keys, config_keys_num);
608   plugin_register_init("nfs", nfs_init);
609   plugin_register_read("nfs", nfs_read);
610 } /* void module_register */