Whamcloud - gitweb
b=15870
[fs/lustre-release.git] / snmp / lustre-snmp-trap.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  Copyright (c) 2005 Cluster File Systems, Inc.
5  *   Author: PJ Kirner <pjkirner@clusterfs.com>
6  *
7  *   This file is part of Lustre, http://www.lustre.org.
8  *
9  *   Lustre is free software; you can redistribute it and/or
10  *   modify it under the terms of version 2 of the GNU General Public
11  *   License as published by the Free Software Foundation.
12  *
13  *   Lustre is distributed in the hope that it will be useful,
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *   GNU General Public License for more details.
17  *
18  *   You should have received a copy of the GNU General Public License
19  *   along with Lustre; if not, write to the Free Software
20  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  */
22
23 /*
24  *   include important headers
25  */
26
27 #include <net-snmp/net-snmp-config.h>
28 #include <net-snmp/net-snmp-includes.h>
29 #include <net-snmp/agent/net-snmp-agent-includes.h>
30
31 /*
32  *  include our .h file
33  */ 
34
35 #include <sys/types.h>
36 #if defined (__linux__)
37 #include <sys/vfs.h>
38 #endif
39 #include <dirent.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 #include <stdio.h>
43 #include <stdarg.h>
44 #include "lustre-snmp-util.h"
45
46 /**************************************************************************
47  * Constants
48  *************************************************************************/
49
50 #define DEFAULT_POLL_INTERVAL_SECONDS   60
51 #define POLL_INTERVAL_ENV_VAR           "LSNMP_POLL_INTERVAL"
52 #define SNMP_HEALTH_CHECK_TEST_FILE     "LSNMP_HEALTH_CHECK_TEST_FILE"
53
54 /**************************************************************************
55  * Trap OIDS
56  *************************************************************************/
57
58 static oid objid_snmptrap[] =                       
59     { 1,3,6,1,6,3,1,1,4,1,0};
60 static oid lustre_portals_trap[] = 
61     { 1,3,6,1,4,1,13140,2,1,0,1};
62 static oid lustre_portals_trap_string[]= 
63     { 1,3,6,1,4,1,13140,2,1,0,2};
64 static oid lustre_unhealthy_trap[] = 
65     { 1,3,6,1,4,1,13140,2,1,0,3};
66 static oid lustre_unhealthy_trap_device_name_string[]= 
67     { 1,3,6,1,4,1,13140,2,1,0,4};
68 static oid lustre_unhealthy_trap_reason_string[]= 
69     { 1,3,6,1,4,1,13140,2,1,0,5};
70
71 /**************************************************************************
72  * Data structures
73  *************************************************************************/
74
75 typedef struct obd_unhealthy_entry_struct{
76
77     /*1-if seen as part of the the is_unhealthy scan, otherwise 0*/
78     int seen;                         
79
80     /*single linked list pointer*/
81     struct obd_unhealthy_entry_struct *next; 
82
83     /*obdname - variable size*/
84     char name[0];                     
85
86 }obd_unhealthy_entry;
87
88 /**************************************************************************
89  * Local functions
90  *************************************************************************/
91
92 int get_poll_interval_seconds();
93 void health_poll_worker(unsigned int registration_number, void *clientarg);
94 void send_portals_catastrophe_trap(char *reason_string);
95 void send_obd_unhealthy_trap(char *obd_name,char *reason_string);
96 int is_obd_newly_unhealthy(const char* obd_name);
97 void obd_unhealthy_scan(void);
98 void health_entry_parser(void);
99
100 /**************************************************************************
101  * Global variables
102  *************************************************************************/
103
104 static int g_sent_portals_catastrophe = 0;
105 static obd_unhealthy_entry* g_obd_unhealthy_list = NULL;
106 static int g_poll_interval_seconds;
107 static unsigned int g_registration_handle;
108 static char *g_health_check_test_file = 0;
109
110 /*****************************************************************************
111  * Function: initilize_trap_handler
112  *
113  * Description: Initlized the trap poll haalder.
114  *
115  * Input:   void
116  *
117  * Output:  Global g_poll_interval_seconds is set.
118  *
119  ****************************************************************************/
120  
121 void initilize_trap_handler(void)
122 {
123     g_poll_interval_seconds = get_poll_interval_seconds();
124
125     g_registration_handle = snmp_alarm_register(g_poll_interval_seconds, 0, health_poll_worker, NULL);
126     if (g_registration_handle == 0)
127         report("%s %s: line %d %s", __FILE__, __FUNCTION__, __LINE__,
128             "snmp_alarm_register failed");
129             
130     DEBUGMSGTL(("lsnmpd","lsnmp alarm registered poll interval = %d seconds\n",g_poll_interval_seconds));
131     
132     g_health_check_test_file = getenv(SNMP_HEALTH_CHECK_TEST_FILE);    
133     if(g_health_check_test_file != 0)
134         DEBUGMSGTL(("lsnmpd","lsnmp health check test file set to  \'%s\'\n",g_health_check_test_file));
135 }
136
137 /*****************************************************************************
138  * Function: terminate_trap_handler
139  *
140  * Description: Terminate the trap poll haalder.
141  *
142  * Input:   void
143  *
144  * Output:  Global g_poll_interval_seconds is set.
145  *
146  ****************************************************************************/
147
148 void terminate_trap_handler(void)
149 {
150     snmp_alarm_unregister(g_registration_handle);
151 }
152
153 /*****************************************************************************
154  * Function: get_poll_interval_seconds
155  *
156  * Description: This function used to get the poll period for timer, which 
157  *              is used to read throughput values periodically.
158  * Input:   void
159  * Output:  Alarm period, default value(if env var not set) otherwise.
160  ****************************************************************************/
161
162 int get_poll_interval_seconds()
163 {
164     char *alarm_period;
165     int ret_val = DEFAULT_POLL_INTERVAL_SECONDS;
166
167     /* Get Alarm period for reading the Lustre client table. */
168
169     alarm_period = getenv(POLL_INTERVAL_ENV_VAR);
170     if (alarm_period != NULL) {
171         char *ptr = alarm_period;
172         while(isdigit(*ptr)) ptr++;
173
174         /* if we have only digits then conver it*/
175         if (*ptr == '\0') {
176             int time = atoi(alarm_period);
177             if (time > 0)
178                 ret_val = time; /* Alarm period in seconds */
179         }
180     }
181     return ret_val;
182 }
183
184 /*****************************************************************************
185  * Function:  health_poll_worker
186  *
187  * Description: This is the routine registered to system timer for updating
188  *     the throughput values for all the clients and its respective osc(s).
189  *
190  * Input:  'registration_number` value obtained during the alarm registration
191  *         'clientarg' pointing to user defined data type.
192  * Output: void
193  *****************************************************************************/
194
195 void health_poll_worker(unsigned int registration_number, void *clientarg)
196 {
197     health_entry_parser();
198
199     /* Register the function again to call after lustre_alarm_period */
200     if (!snmp_alarm_register(g_poll_interval_seconds, 0, health_poll_worker, NULL)) {
201         report("%s %s:line %d %s", __FILE__, __FUNCTION__, __LINE__,
202                "snmp_alarm_register failed");
203     }
204 }
205
206 /*****************************************************************************
207  * Function:  health_entry_parser
208  *
209  * Description: This routine is called to parse the health_check entry
210  *              and send traps
211  * Input:  'None
212  * Output: void
213  *****************************************************************************/
214  
215  void health_entry_parser(void)
216 {
217     FILE    *fptr = NULL;
218     char string[MAX_LINE_SIZE];
219     int b_seen_portals_catastrophe = 0;
220     const char *filename =  g_health_check_test_file == 0 ? 
221             LUSTRE_PATH FILENAME_SYSHEALTHCHECK : 
222             g_health_check_test_file;
223     
224     /*DEBUGMSGTL(("lsnmpd","health_entry_parser(%s)\n",filename));*/
225
226     /* Open the file.  Use the test file env variable if
227        there is one */    
228     fptr = fopen(filename,"r");
229         
230     /* If the path is not found do nothing */
231     if( NULL == fptr)
232         return;
233        
234     while( NULL != fgets(string, sizeof(string), fptr)){
235         
236         /*DEBUGMSGTL(("lsnmpd","health_entry_parser() looking at = \'%s\'\n",string));*/
237        
238         /*
239          * First handle the portals catastrophe 
240          * Look for the string "LBUG"
241          */
242         if(0 == strncmp(string,"LBUG",4)){
243             /*
244              * If we haven't sent the catastrophe message yet
245              * send it now.  And keep track that we've sent it
246              */
247             if(!g_sent_portals_catastrophe){
248                 send_portals_catastrophe_trap("LBUG");
249                 g_sent_portals_catastrophe = 1;
250             }
251             b_seen_portals_catastrophe = 1;
252         }
253             
254         /*
255          * Now handle any of the OBD object failures
256          * look for "device <OBDNAME> reported unhealthy"
257          */
258         else if(0 == strncmp(string,"device ",7)){
259             char *obd_name = string+7;
260             char *space_after_obd_name;
261             
262             /*
263              * Now find the space after the obd name
264              * Again if there is no space we're in trouble
265              */
266             space_after_obd_name = strchr(obd_name,' ');
267             if(space_after_obd_name == 0)
268                 break;
269
270             /*
271              * Null terminate the obd_name
272              */
273             *space_after_obd_name = 0;
274             
275             DEBUGMSGTL(("lsnmpd","Looking at obd=%s\n",obd_name));
276
277             /*
278              * If we haven't sent a trap for this one
279              * then send it now
280              */
281             if(is_obd_newly_unhealthy(obd_name))
282                 send_obd_unhealthy_trap(obd_name,"unhealthy");
283         }
284     }        
285     
286     /* If we don't find it reset the catastrope flag*/            
287     if(!b_seen_portals_catastrophe && g_sent_portals_catastrophe)
288     {
289         DEBUGMSGTL(("lsnmpd","LBUG has been cleared\n"));
290         g_sent_portals_catastrophe = 0;
291     }
292                 
293     /*
294      *  Any <OBDNAMES> that weren't queried above are now unhealthy. 
295      * Scan through and cleanup the newly healthy obds
296      */
297     obd_unhealthy_scan();
298     
299     fclose(fptr);
300 }
301
302 /*****************************************************************************
303  * Function:  send_portals_catastrophe_trap
304  *
305  * Description: Send the SNMP V2 trap
306  *
307  * Input:  'reason_string' the reason for the catastrope.
308  
309  * Output: none
310  *****************************************************************************/
311  
312 void send_portals_catastrophe_trap(char *reason_string)
313 {
314     /*
315      * Setup the trap variables.  
316      * It's a linked list of netsnmp_variable_list items.
317      */
318     netsnmp_variable_list var_trap[2];
319
320     DEBUGMSGTL(("lsnmpd","Sending portals catastrophe trap reason=%s\n",reason_string));
321
322     /* 
323      * Setup the first variable in the trap data. 
324      * Have it chain to another variable.
325      */
326     var_trap[0].next_variable = &var_trap[1];
327
328     /*The "name" must be the standard snmp "trap" OID.*/
329     var_trap[0].name = objid_snmptrap;
330     var_trap[0].name_length = sizeof(objid_snmptrap) / sizeof(oid);
331
332     /*But the data contained in this variable, is an OID that is the trap OID.*/
333     var_trap[0].type = ASN_OBJECT_ID;
334     var_trap[0].val.objid = lustre_portals_trap;
335     var_trap[0].val_len = sizeof(lustre_portals_trap);
336
337     /* 
338      * Setup the second variable in the trap data. 
339      * It is the last in the chain so set next to NULL
340      */
341     var_trap[1].next_variable = NULL;
342
343     /*The "name" is the OID of the portals trap reason strong*/
344     var_trap[1].name = lustre_portals_trap_string;
345     var_trap[1].name_length = sizeof(lustre_portals_trap_string) / sizeof(oid);
346
347     /*And the data is a octet string, that contains the actually reason string*/
348     var_trap[1].type = ASN_OCTET_STR;
349     var_trap[1].val.string = reason_string;
350     var_trap[1].val_len = strlen(reason_string);
351
352     /*And now send off the trap*/
353     send_v2trap(var_trap);
354 }
355
356
357 /*****************************************************************************
358  * Function:  send_obd_unhealthy_trap
359  *
360  * Description: Send the SNMP V2 trap
361  *
362  * Input:  'obd_name' the name of the obd
363  *         'reason_string' the reason for the catastrope.
364  * Output: none
365  *****************************************************************************/
366  
367 void send_obd_unhealthy_trap(char *obd_name,char *reason_string)
368 {
369     /*
370      * Setup the trap variables.  
371      * It's a linked list of netsnmp_variable_list items.
372      */
373     netsnmp_variable_list var_trap[3];
374
375     DEBUGMSGTL(("lsnmpd","Sending OBD unhealthy trap obd=%s reason=%s\n",obd_name,reason_string));
376
377     /* 
378      * Setup the first variable in the trap data. 
379      * Have it chain to another variable.
380      */
381     var_trap[0].next_variable = &var_trap[1];
382
383     /*The "name" must be the standard snmp "trap" OID.*/
384     var_trap[0].name = objid_snmptrap;
385     var_trap[0].name_length = sizeof(objid_snmptrap) / sizeof(oid);
386
387     /*But the data contained in this variable, is an OID that is the trap OID.*/
388     var_trap[0].type = ASN_OBJECT_ID;
389     var_trap[0].val.objid = lustre_unhealthy_trap;
390     var_trap[0].val_len = sizeof(lustre_unhealthy_trap);
391
392     /* 
393      * Setup the second variable in the trap data. 
394      * Have it chain to another variable.
395      */
396     var_trap[1].next_variable = &var_trap[2];;
397
398     /*The "name" is the OID of the portals trap reason strong*/
399     var_trap[1].name = lustre_unhealthy_trap_device_name_string;
400     var_trap[1].name_length = sizeof(lustre_unhealthy_trap_device_name_string) / sizeof(oid);
401
402     /*And the data is a octet string, that contains the actually reason strong*/
403     var_trap[1].type = ASN_OCTET_STR;
404     var_trap[1].val.string = obd_name;
405     var_trap[1].val_len = strlen(obd_name);
406
407     /* 
408      * Setup the third variable in the trap data. 
409      * It is the last in the chain so set next to NULL
410      */
411     var_trap[2].next_variable = NULL;
412
413     /*The "name" is the OID of the portals trap reason strong*/
414     var_trap[2].name = lustre_unhealthy_trap_reason_string;
415     var_trap[2].name_length = sizeof(lustre_unhealthy_trap_reason_string) / sizeof(oid);
416
417     /*And the data is a octet string, that contains the actually reason strong*/
418     var_trap[2].type = ASN_OCTET_STR;
419     var_trap[2].val.string = reason_string;
420     var_trap[2].val_len = strlen(reason_string);
421
422     /*And now send off the trap*/
423     send_v2trap(var_trap);
424 }
425
426
427 /*****************************************************************************
428  * Function:  is_obd_newly_unhealthy
429  *
430  * Description: Deterime if the obd is going from health->unhealth
431  *              Also mark all unhealhy (new and old) as seen.
432  *
433  * Input:  'obd_name' the name of the obd
434  *
435  * Output: 1 if newly unhealthy 0 if previolsy unhealthy
436  *****************************************************************************/
437
438 int is_obd_newly_unhealthy(const char* obd_name)
439 {
440     /*for all elements in g_obd_unhealthy_list*/
441     obd_unhealthy_entry* walker;
442     obd_unhealthy_entry* entry;
443     int name_len;
444
445     for(walker = g_obd_unhealthy_list; walker != 0; walker = walker->next)
446     {
447         /*If the names match*/
448         if(0 == strcmp (walker->name,obd_name))
449         {
450             /* Commented out because it was just to noisy!
451              * DEBUGMSGTL(("lsnmpd","obd %s was already unhealthy\n",obd_name));
452              */
453             
454             /*Mark the entry as seen, and return that it was previously unhealthy*/
455             walker->seen =1;
456             return 0;
457         }
458     }
459
460     DEBUGMSGTL(("lsnmpd","obd %s is now unhealthy\n",obd_name));
461
462     /*We didn't find an entry so we need to create a new one. */
463     /*Calculate the obd_name length*/
464     name_len = strlen(obd_name)+1;
465
466     /*Allocate a new entry*/
467     entry = malloc(sizeof(*entry) + name_len);
468
469     /*Put this element at the front of the list*/
470     entry->next = g_obd_unhealthy_list;
471     g_obd_unhealthy_list = entry;
472
473     /*Mark it initially as seen*/
474     entry->seen = 1;
475
476     /*And copy the entry name*/
477     memcpy(entry->name,obd_name,name_len);
478
479     /*return this obd as newly unhealthy.*/
480     return 1;
481 }
482
483
484 /*****************************************************************************
485  * Function:  obd_unhealthy_scan
486  *
487  * Description: Deterime if any obd is going from unhealthy->healthy
488  *              Any of the obds that weren't "seen" by the 
489  *              is_obd_newly_unhealthy() pass are now health so 
490  *              remove them from the lists
491  *              Also clear all "seen" flags.
492  *
493  * Input:  None
494  * Output: None
495  *****************************************************************************/
496  
497 void obd_unhealthy_scan(void)
498 {
499     /*fore all elements in g_obd_unhealthy_list*/
500     obd_unhealthy_entry* walker = g_obd_unhealthy_list;
501     obd_unhealthy_entry* prev = 0;
502     while(walker != 0)
503     {
504         /*remove any that was not seen as unhealthy the last time*/
505         if(walker->seen == 0)
506         {
507             /*Remove element from the list, but first fix up the walker pointer*/
508             obd_unhealthy_entry* temp = walker;
509
510             DEBUGMSGTL(("lsnmpd","obd %s is now healthy\n",walker->name));
511
512             walker = walker->next;
513
514             /*Now adjust the pointers to effectively remove this entry*/
515             if(prev == 0)
516                 g_obd_unhealthy_list = walker;
517             else
518                 prev->next = walker;
519
520             /*And free the pointer. */
521             free(temp);
522             /*walker and prev are correctly setup so we can go around the loop again.*/
523         }
524
525         /*Mark all other entries as NOT seen for next pass through*/
526         else 
527         {
528             walker->seen = 0;
529             /*Go onto the next entry*/
530             prev = walker;
531             walker = walker->next;
532         }
533     }
534 }