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