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