Whamcloud - gitweb
LU-11304 misc: update all url links to whamcloud
[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.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Use is subject to license terms.
25  */
26 /*
27  * This file is part of Lustre, http://www.lustre.org/
28  * Lustre is a trademark of Sun Microsystems, Inc.
29  *
30  * snmp/lustre-snmp-trap.c
31  *
32  * Author: PJ Kirner <pjkirner@clusterfs.com>
33  */
34
35 /*
36  *   include important headers
37  */
38
39 #include <net-snmp/net-snmp-config.h>
40 #include <net-snmp/net-snmp-includes.h>
41 #include <net-snmp/agent/net-snmp-agent-includes.h>
42
43 /*
44  *  include our .h file
45  */ 
46
47 #include <sys/types.h>
48 #include <sys/vfs.h>
49 #include <dirent.h>
50 #include <sys/stat.h>
51 #include <unistd.h>
52 #include <stdio.h>
53 #include <stdarg.h>
54 #include <ctype.h>
55 #include "lustre-snmp-util.h"
56
57 /**************************************************************************
58  * Constants
59  *************************************************************************/
60
61 #define DEFAULT_POLL_INTERVAL_SECONDS   60
62 #define POLL_INTERVAL_ENV_VAR           "LSNMP_POLL_INTERVAL"
63 #define SNMP_HEALTH_CHECK_TEST_FILE     "LSNMP_HEALTH_CHECK_TEST_FILE"
64
65 /**************************************************************************
66  * Trap OIDS
67  *************************************************************************/
68
69 static oid objid_snmptrap[] =                       
70     { 1,3,6,1,6,3,1,1,4,1,0};
71 static oid lustre_portals_trap[] = 
72     { 1,3,6,1,4,1,13140,2,1,0,1};
73 static oid lustre_portals_trap_string[]= 
74     { 1,3,6,1,4,1,13140,2,1,0,2};
75 static oid lustre_unhealthy_trap[] = 
76     { 1,3,6,1,4,1,13140,2,1,0,3};
77 static oid lustre_unhealthy_trap_device_name_string[]= 
78     { 1,3,6,1,4,1,13140,2,1,0,4};
79 static oid lustre_unhealthy_trap_reason_string[]= 
80     { 1,3,6,1,4,1,13140,2,1,0,5};
81
82 /**************************************************************************
83  * Data structures
84  *************************************************************************/
85
86 typedef struct obd_unhealthy_entry_struct{
87
88     /*1-if seen as part of the the is_unhealthy scan, otherwise 0*/
89     int seen;                         
90
91     /*single linked list pointer*/
92     struct obd_unhealthy_entry_struct *next; 
93
94     /*obdname - variable size*/
95     char name[0];                     
96
97 }obd_unhealthy_entry;
98
99 /**************************************************************************
100  * Local functions
101  *************************************************************************/
102
103 int get_poll_interval_seconds();
104 void health_poll_worker(unsigned int registration_number, void *clientarg);
105 void send_portals_catastrophe_trap(char *reason_string);
106 void send_obd_unhealthy_trap(char *obd_name,char *reason_string);
107 int is_obd_newly_unhealthy(const char* obd_name);
108 void obd_unhealthy_scan(void);
109 void health_entry_parser(void);
110
111 /**************************************************************************
112  * Global variables
113  *************************************************************************/
114
115 static int g_sent_portals_catastrophe = 0;
116 static obd_unhealthy_entry* g_obd_unhealthy_list = NULL;
117 static int g_poll_interval_seconds;
118 static unsigned int g_registration_handle;
119 static char *g_health_check_test_file = 0;
120
121 /*****************************************************************************
122  * Function: initialize_trap_handler
123  *
124  * Description: Initlized the trap poll haalder.
125  *
126  * Input:   void
127  *
128  * Output:  Global g_poll_interval_seconds is set.
129  *
130  ****************************************************************************/
131  
132 void initialize_trap_handler(void)
133 {
134     g_poll_interval_seconds = get_poll_interval_seconds();
135
136     g_registration_handle = snmp_alarm_register(g_poll_interval_seconds, 0, health_poll_worker, NULL);
137     if (g_registration_handle == 0)
138         report("%s %s: line %d %s", __FILE__, __FUNCTION__, __LINE__,
139             "snmp_alarm_register failed");
140             
141     DEBUGMSGTL(("lsnmpd","lsnmp alarm registered poll interval = %d seconds\n",g_poll_interval_seconds));
142     
143     g_health_check_test_file = getenv(SNMP_HEALTH_CHECK_TEST_FILE);    
144     if(g_health_check_test_file != 0)
145         DEBUGMSGTL(("lsnmpd","lsnmp health check test file set to  \'%s\'\n",g_health_check_test_file));
146 }
147
148 /*****************************************************************************
149  * Function: terminate_trap_handler
150  *
151  * Description: Terminate the trap poll haalder.
152  *
153  * Input:   void
154  *
155  * Output:  Global g_poll_interval_seconds is set.
156  *
157  ****************************************************************************/
158
159 void terminate_trap_handler(void)
160 {
161     snmp_alarm_unregister(g_registration_handle);
162 }
163
164 /*****************************************************************************
165  * Function: get_poll_interval_seconds
166  *
167  * Description: This function used to get the poll period for timer, which 
168  *              is used to read throughput values periodically.
169  * Input:   void
170  * Output:  Alarm period, default value(if env var not set) otherwise.
171  ****************************************************************************/
172
173 int get_poll_interval_seconds()
174 {
175     char *alarm_period;
176     int ret_val = DEFAULT_POLL_INTERVAL_SECONDS;
177
178     /* Get Alarm period for reading the Lustre client table. */
179
180     alarm_period = getenv(POLL_INTERVAL_ENV_VAR);
181     if (alarm_period != NULL) {
182         char *ptr = alarm_period;
183         while(isdigit(*ptr)) ptr++;
184
185         /* if we have only digits then conver it*/
186         if (*ptr == '\0') {
187             int time = atoi(alarm_period);
188             if (time > 0)
189                 ret_val = time; /* Alarm period in seconds */
190         }
191     }
192     return ret_val;
193 }
194
195 /*****************************************************************************
196  * Function:  health_poll_worker
197  *
198  * Description: This is the routine registered to system timer for updating
199  *     the throughput values for all the clients and its respective osc(s).
200  *
201  * Input:  'registration_number` value obtained during the alarm registration
202  *         'clientarg' pointing to user defined data type.
203  * Output: void
204  *****************************************************************************/
205
206 void health_poll_worker(unsigned int registration_number, void *clientarg)
207 {
208     health_entry_parser();
209
210     /* Register the function again to call after lustre_alarm_period */
211     if (!snmp_alarm_register(g_poll_interval_seconds, 0, health_poll_worker, NULL)) {
212         report("%s %s:line %d %s", __FILE__, __FUNCTION__, __LINE__,
213                "snmp_alarm_register failed");
214     }
215 }
216
217 /*****************************************************************************
218  * Function:  health_entry_parser
219  *
220  * Description: This routine is called to parse the health_check entry
221  *              and send traps
222  * Input:  'None
223  * Output: void
224  *****************************************************************************/
225  
226  void health_entry_parser(void)
227 {
228     FILE    *fptr = NULL;
229     char string[MAX_LINE_SIZE];
230     int b_seen_portals_catastrophe = 0;
231     char *filename;
232     glob_t path;
233
234     if (cfs_get_param_paths(&path, "health_check") != 0)
235         return;
236
237     filename = g_health_check_test_file == 0 ? path.gl_pathv[0] : g_health_check_test_file;
238
239     /*DEBUGMSGTL(("lsnmpd","health_entry_parser(%s)\n",filename));*/
240
241     /* Open the file.  Use the test file env variable if
242        there is one */    
243     fptr = fopen(filename,"r");
244         
245     /* Free parameter's path string */
246     cfs_free_param_data(&path);
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 string */
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 an octet string, that contains the actually reason
366      * 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 string */
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 an octet string, that contains the actual reason
422      * string */
423     var_trap[1].type = ASN_OCTET_STR;
424     var_trap[1].val.string = (unsigned char *)obd_name;
425     var_trap[1].val_len = strlen(obd_name);
426
427     /*
428      * Setup the third variable in the trap data.
429      * It is the last in the chain so set next to NULL
430      */
431     var_trap[2].next_variable = NULL;
432
433     /* The "name" is the OID of the portals trap reason string */
434     var_trap[2].name = lustre_unhealthy_trap_reason_string;
435     var_trap[2].name_length = sizeof(lustre_unhealthy_trap_reason_string) / sizeof(oid);
436
437     /* And the data is an octet string, that contains the actual reason
438      * string */
439     var_trap[2].type = ASN_OCTET_STR;
440     var_trap[2].val.string = (unsigned char *)reason_string;
441     var_trap[2].val_len = strlen(reason_string);
442
443     /*And now send off the trap*/
444     send_v2trap(var_trap);
445 }
446
447
448 /*****************************************************************************
449  * Function:  is_obd_newly_unhealthy
450  *
451  * Description: Deterime if the obd is going from health->unhealth
452  *              Also mark all unhealhy (new and old) as seen.
453  *
454  * Input:  'obd_name' the name of the obd
455  *
456  * Output: 1 if newly unhealthy 0 if previolsy unhealthy
457  *****************************************************************************/
458
459 int is_obd_newly_unhealthy(const char* obd_name)
460 {
461     /*for all elements in g_obd_unhealthy_list*/
462     obd_unhealthy_entry* walker;
463     obd_unhealthy_entry* entry;
464     int name_len;
465
466     for(walker = g_obd_unhealthy_list; walker != 0; walker = walker->next)
467     {
468         /*If the names match*/
469         if(0 == strcmp (walker->name,obd_name))
470         {
471             /* Commented out because it was just to noisy!
472              * DEBUGMSGTL(("lsnmpd","obd %s was already unhealthy\n",obd_name));
473              */
474             
475             /*Mark the entry as seen, and return that it was previously unhealthy*/
476             walker->seen =1;
477             return 0;
478         }
479     }
480
481     DEBUGMSGTL(("lsnmpd","obd %s is now unhealthy\n",obd_name));
482
483     /*We didn't find an entry so we need to create a new one. */
484     /*Calculate the obd_name length*/
485     name_len = strlen(obd_name)+1;
486
487     /*Allocate a new entry*/
488     entry = malloc(sizeof(*entry) + name_len);
489
490     /*Put this element at the front of the list*/
491     entry->next = g_obd_unhealthy_list;
492     g_obd_unhealthy_list = entry;
493
494     /*Mark it initially as seen*/
495     entry->seen = 1;
496
497     /*And copy the entry name*/
498     memcpy(entry->name,obd_name,name_len);
499
500     /*return this obd as newly unhealthy.*/
501     return 1;
502 }
503
504
505 /*****************************************************************************
506  * Function:  obd_unhealthy_scan
507  *
508  * Description: Deterime if any obd is going from unhealthy->healthy
509  *              Any of the obds that weren't "seen" by the 
510  *              is_obd_newly_unhealthy() pass are now health so 
511  *              remove them from the lists
512  *              Also clear all "seen" flags.
513  *
514  * Input:  None
515  * Output: None
516  *****************************************************************************/
517  
518 void obd_unhealthy_scan(void)
519 {
520     /*fore all elements in g_obd_unhealthy_list*/
521     obd_unhealthy_entry* walker = g_obd_unhealthy_list;
522     obd_unhealthy_entry* prev = 0;
523     while(walker != 0)
524     {
525         /*remove any that was not seen as unhealthy the last time*/
526         if(walker->seen == 0)
527         {
528             /*Remove element from the list, but first fix up the walker pointer*/
529             obd_unhealthy_entry* temp = walker;
530
531             DEBUGMSGTL(("lsnmpd","obd %s is now healthy\n",walker->name));
532
533             walker = walker->next;
534
535             /*Now adjust the pointers to effectively remove this entry*/
536             if(prev == 0)
537                 g_obd_unhealthy_list = walker;
538             else
539                 prev->next = walker;
540
541             /*And free the pointer. */
542             free(temp);
543             /*walker and prev are correctly setup so we can go around the loop again.*/
544         }
545
546         /*Mark all other entries as NOT seen for next pass through*/
547         else 
548         {
549             walker->seen = 0;
550             /*Go onto the next entry*/
551             prev = walker;
552             walker = walker->next;
553         }
554     }
555 }