Merge commit 'martins/master'
[metze/ctdb/wip.git] / server / ctdb_monitor.c
1 /* 
2    monitoring links to all other nodes to detect dead nodes
3
4
5    Copyright (C) Ronnie Sahlberg 2007
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "lib/events/events.h"
23 #include "system/filesys.h"
24 #include "system/wait.h"
25 #include "../include/ctdb_private.h"
26
27 struct ctdb_monitor_state {
28         uint32_t monitoring_mode;
29         TALLOC_CTX *monitor_context;
30         uint32_t next_interval;
31 };
32
33 static void ctdb_check_health(struct event_context *ev, struct timed_event *te, 
34                               struct timeval t, void *private_data);
35
36 /*
37   setup the notification script
38 */
39 int ctdb_set_notification_script(struct ctdb_context *ctdb, const char *script)
40 {
41         ctdb->notification_script = talloc_strdup(ctdb, script);
42         CTDB_NO_MEMORY(ctdb, ctdb->notification_script);
43         return 0;
44 }
45
46 static int ctdb_run_notification_script_child(struct ctdb_context *ctdb, const char *event)
47 {
48         struct stat st;
49         int ret;
50         char *cmd;
51
52         if (stat(ctdb->notification_script, &st) != 0) {
53                 DEBUG(DEBUG_ERR,("Could not stat notification script %s. Can not send notifications.\n", ctdb->notification_script));
54                 return -1;
55         }
56         if (!(st.st_mode & S_IXUSR)) {
57                 DEBUG(DEBUG_ERR,("Notification script %s is not executable.\n", ctdb->notification_script));
58                 return -1;
59         }
60
61         cmd = talloc_asprintf(ctdb, "%s %s\n", ctdb->notification_script, event);
62         CTDB_NO_MEMORY(ctdb, cmd);
63
64         ret = system(cmd);
65         /* if the system() call was successful, translate ret into the
66            return code from the command
67         */
68         if (ret != -1) {
69                 ret = WEXITSTATUS(ret);
70         }
71         if (ret != 0) {
72                 DEBUG(DEBUG_ERR,("Notification script \"%s\" failed with error %d\n", cmd, ret));
73         }
74
75         return ret;
76 }
77
78 static void ctdb_run_notification_script(struct ctdb_context *ctdb, const char *event)
79 {
80         pid_t child;
81
82         if (ctdb->notification_script == NULL) {
83                 return;
84         }
85
86         child = fork();
87         if (child == (pid_t)-1) {
88                 DEBUG(DEBUG_ERR,("Failed to fork() a notification child process\n"));
89                 return;
90         }
91         if (child == 0) {
92                 int ret;
93
94                 ret = ctdb_run_notification_script_child(ctdb, event);
95                 if (ret != 0) {
96                         DEBUG(DEBUG_ERR,(__location__ " Notification script failed\n"));
97                 }
98                 _exit(0);
99         }
100
101         return;
102 }
103
104 /*
105   called when a health monitoring event script finishes
106  */
107 static void ctdb_health_callback(struct ctdb_context *ctdb, int status, void *p)
108 {
109         struct ctdb_node *node = ctdb->nodes[ctdb->pnn];
110         TDB_DATA data;
111         struct ctdb_node_flag_change c;
112         uint32_t next_interval;
113
114         c.pnn = ctdb->pnn;
115         c.old_flags = node->flags;
116
117         if (status != 0 && !(node->flags & NODE_FLAGS_UNHEALTHY)) {
118                 DEBUG(DEBUG_NOTICE,("monitor event failed - disabling node\n"));
119                 node->flags |= NODE_FLAGS_UNHEALTHY;
120                 ctdb->monitor->next_interval = 1;
121                 if (ctdb->tunable.disable_when_unhealthy != 0) {
122                         DEBUG(DEBUG_INFO, ("DISABLING node since it became unhealthy\n"));
123                         node->flags |= NODE_FLAGS_DISABLED;
124                 }
125
126                 ctdb_run_notification_script(ctdb, "unhealthy");
127         } else if (status == 0 && (node->flags & NODE_FLAGS_UNHEALTHY)) {
128                 DEBUG(DEBUG_NOTICE,("monitor event OK - node re-enabled\n"));
129                 node->flags &= ~NODE_FLAGS_UNHEALTHY;
130                 ctdb->monitor->next_interval = 1;
131
132                 ctdb_run_notification_script(ctdb, "healthy");
133         }
134
135         next_interval = ctdb->monitor->next_interval;
136
137         ctdb->monitor->next_interval *= 2;
138         if (ctdb->monitor->next_interval > ctdb->tunable.monitor_interval) {
139                 ctdb->monitor->next_interval = ctdb->tunable.monitor_interval;
140         }
141
142         event_add_timed(ctdb->ev, ctdb->monitor->monitor_context, 
143                                 timeval_current_ofs(next_interval, 0), 
144                                 ctdb_check_health, ctdb);
145
146         if (c.old_flags == node->flags) {
147                 return;
148         }
149
150         c.new_flags = node->flags;
151
152         data.dptr = (uint8_t *)&c;
153         data.dsize = sizeof(c);
154
155         /* ask the recovery daemon to push these changes out to all nodes */
156         ctdb_daemon_send_message(ctdb, ctdb->pnn,
157                                  CTDB_SRVID_PUSH_NODE_FLAGS, data);
158
159 }
160
161
162 /*
163   called when the startup event script finishes
164  */
165 static void ctdb_startup_callback(struct ctdb_context *ctdb, int status, void *p)
166 {
167         if (status != 0) {
168                 DEBUG(DEBUG_ERR,("startup event failed\n"));
169         } else if (status == 0) {
170                 DEBUG(DEBUG_NOTICE,("startup event OK - enabling monitoring\n"));
171                 ctdb->done_startup = true;
172                 ctdb->monitor->next_interval = 1;
173         }
174
175         event_add_timed(ctdb->ev, ctdb->monitor->monitor_context, 
176                         timeval_current_ofs(ctdb->monitor->next_interval, 0),
177                         ctdb_check_health, ctdb);
178 }
179
180
181 /*
182   see if the event scripts think we are healthy
183  */
184 static void ctdb_check_health(struct event_context *ev, struct timed_event *te, 
185                               struct timeval t, void *private_data)
186 {
187         struct ctdb_context *ctdb = talloc_get_type(private_data, struct ctdb_context);
188         int ret;
189
190         if (ctdb->recovery_mode != CTDB_RECOVERY_NORMAL ||
191             (ctdb->monitor->monitoring_mode == CTDB_MONITORING_DISABLED && ctdb->done_startup)) {
192                 event_add_timed(ctdb->ev, ctdb->monitor->monitor_context,
193                                 timeval_current_ofs(ctdb->monitor->next_interval, 0), 
194                                 ctdb_check_health, ctdb);
195                 return;
196         }
197         
198         if (!ctdb->done_startup) {
199                 ret = ctdb_event_script_callback(ctdb, 
200                                                  timeval_current_ofs(ctdb->tunable.script_timeout, 0),
201                                                  ctdb->monitor->monitor_context, ctdb_startup_callback, 
202                                                  ctdb, "startup");
203         } else {
204                 ret = ctdb_event_script_callback(ctdb, 
205                                                  timeval_current_ofs(ctdb->tunable.script_timeout, 0),
206                                                  ctdb->monitor->monitor_context, ctdb_health_callback, 
207                                                  ctdb, "monitor");
208         }
209
210         if (ret != 0) {
211                 DEBUG(DEBUG_ERR,("Unable to launch monitor event script\n"));
212                 ctdb->monitor->next_interval = 1;
213                 event_add_timed(ctdb->ev, ctdb->monitor->monitor_context, 
214                                 timeval_current_ofs(1, 0), 
215                                 ctdb_check_health, ctdb);
216         }       
217 }
218
219 /* 
220   (Temporaily) Disabling monitoring will stop the monitor event scripts
221   from running   but node health checks will still occur
222 */
223 void ctdb_disable_monitoring(struct ctdb_context *ctdb)
224 {
225         ctdb->monitor->monitoring_mode = CTDB_MONITORING_DISABLED;
226         DEBUG(DEBUG_INFO,("Monitoring has been disabled\n"));
227 }
228
229 /* 
230    Re-enable running monitor events after they have been disabled
231  */
232 void ctdb_enable_monitoring(struct ctdb_context *ctdb)
233 {
234         ctdb->monitor->monitoring_mode  = CTDB_MONITORING_ACTIVE;
235         ctdb->monitor->next_interval = 2;
236         DEBUG(DEBUG_INFO,("Monitoring has been enabled\n"));
237 }
238
239 /* stop any monitoring 
240    this should only be done when shutting down the daemon
241 */
242 void ctdb_stop_monitoring(struct ctdb_context *ctdb)
243 {
244         talloc_free(ctdb->monitor->monitor_context);
245         ctdb->monitor->monitor_context = NULL;
246
247         ctdb->monitor->monitoring_mode  = CTDB_MONITORING_DISABLED;
248         ctdb->monitor->next_interval = 1;
249         DEBUG(DEBUG_NOTICE,("Monitoring has been stopped\n"));
250 }
251
252 /*
253   start watching for nodes that might be dead
254  */
255 void ctdb_start_monitoring(struct ctdb_context *ctdb)
256 {
257         struct timed_event *te;
258
259         if (ctdb->monitor != NULL) {
260                 return;
261         }
262
263         ctdb->monitor = talloc(ctdb, struct ctdb_monitor_state);
264         CTDB_NO_MEMORY_FATAL(ctdb, ctdb->monitor);
265
266         ctdb->monitor->next_interval = 1;
267
268         ctdb->monitor->monitor_context = talloc_new(ctdb->monitor);
269         CTDB_NO_MEMORY_FATAL(ctdb, ctdb->monitor->monitor_context);
270
271         te = event_add_timed(ctdb->ev, ctdb->monitor->monitor_context,
272                              timeval_current_ofs(1, 0), 
273                              ctdb_check_health, ctdb);
274         CTDB_NO_MEMORY_FATAL(ctdb, te);
275
276         ctdb->monitor->monitoring_mode  = CTDB_MONITORING_ACTIVE;
277         DEBUG(DEBUG_NOTICE,("Monitoring has been started\n"));
278 }
279
280
281 /*
282   modify flags on a node
283  */
284 int32_t ctdb_control_modflags(struct ctdb_context *ctdb, TDB_DATA indata)
285 {
286         struct ctdb_node_flag_change *c = (struct ctdb_node_flag_change *)indata.dptr;
287         struct ctdb_node *node;
288         uint32_t old_flags;
289         
290         if (c->pnn >= ctdb->num_nodes) {
291                 DEBUG(DEBUG_ERR,(__location__ " Node %d is invalid, num_nodes :%d\n", c->pnn, ctdb->num_nodes));
292                 return -1;
293         }
294
295         node         = ctdb->nodes[c->pnn];
296         old_flags    = node->flags;
297         c->old_flags  = node->flags;
298         node->flags   = c->new_flags & ~NODE_FLAGS_DISCONNECTED;
299         node->flags  |= (c->old_flags & NODE_FLAGS_DISCONNECTED);
300
301         /* we dont let other nodes modify our STOPPED status */
302         if (c->pnn == ctdb->pnn) {
303                 node->flags &= ~NODE_FLAGS_STOPPED;
304                 if (old_flags & NODE_FLAGS_STOPPED) {
305                         node->flags |= NODE_FLAGS_STOPPED;
306                 }
307         }
308
309         if (node->flags == c->old_flags) {
310                 DEBUG(DEBUG_INFO, ("Control modflags on node %u - Unchanged - flags 0x%x\n", c->pnn, node->flags));
311                 return 0;
312         }
313
314         DEBUG(DEBUG_INFO, ("Control modflags on node %u - flags now 0x%x\n", c->pnn, node->flags));
315
316
317         /* tell the recovery daemon something has changed */
318         ctdb_daemon_send_message(ctdb, ctdb->pnn,
319                                  CTDB_SRVID_SET_NODE_FLAGS, indata);
320
321         /* if we have become banned, we should go into recovery mode */
322         if ((node->flags & NODE_FLAGS_BANNED) && !(c->old_flags & NODE_FLAGS_BANNED) && (node->pnn == ctdb->pnn)) {
323                 /* make sure we are frozen */
324                 DEBUG(DEBUG_NOTICE,("This node has been banned - forcing freeze and recovery\n"));
325                 /* Reset the generation id to 1 to make us ignore any
326                    REQ/REPLY CALL/DMASTER someone sends to us.
327                    We are now banned so we shouldnt service database calls
328                    anymore.
329                 */
330                 ctdb->vnn_map->generation = INVALID_GENERATION;
331
332                 ctdb_start_freeze(ctdb);
333                 ctdb_release_all_ips(ctdb);
334                 ctdb->recovery_mode = CTDB_RECOVERY_ACTIVE;
335         }
336         
337         return 0;
338 }
339
340 /*
341   return the monitoring mode
342  */
343 int32_t ctdb_monitoring_mode(struct ctdb_context *ctdb)
344 {
345         if (ctdb->monitor == NULL) {
346                 return CTDB_MONITORING_DISABLED;
347         }
348         return ctdb->monitor->monitoring_mode;
349 }
350