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