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