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