Merge commit 'origin/master' into martins
[sahlberg/ctdb.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   called when a health monitoring event script finishes
38  */
39 static void ctdb_health_callback(struct ctdb_context *ctdb, int status, void *p)
40 {
41         struct ctdb_node *node = ctdb->nodes[ctdb->pnn];
42         TDB_DATA data;
43         struct ctdb_node_flag_change c;
44         uint32_t next_interval;
45
46         c.pnn = ctdb->pnn;
47         c.old_flags = node->flags;
48
49         if (status != 0 && !(node->flags & NODE_FLAGS_UNHEALTHY)) {
50                 DEBUG(DEBUG_NOTICE,("monitor event failed - disabling node\n"));
51                 node->flags |= NODE_FLAGS_UNHEALTHY;
52                 ctdb->monitor->next_interval = 1;
53                 if (ctdb->tunable.disable_when_unhealthy != 0) {
54                         DEBUG(DEBUG_INFO, ("DISABLING node since it became unhealthy\n"));
55                         node->flags |= NODE_FLAGS_DISABLED;
56                 }
57
58         } else if (status == 0 && (node->flags & NODE_FLAGS_UNHEALTHY)) {
59                 DEBUG(DEBUG_NOTICE,("monitor event OK - node re-enabled\n"));
60                 node->flags &= ~NODE_FLAGS_UNHEALTHY;
61                 ctdb->monitor->next_interval = 1;
62         }
63
64         next_interval = ctdb->monitor->next_interval;
65
66         ctdb->monitor->next_interval *= 2;
67         if (ctdb->monitor->next_interval > ctdb->tunable.monitor_interval) {
68                 ctdb->monitor->next_interval = ctdb->tunable.monitor_interval;
69         }
70
71         event_add_timed(ctdb->ev, ctdb->monitor->monitor_context, 
72                                 timeval_current_ofs(next_interval, 0), 
73                                 ctdb_check_health, ctdb);
74
75         if (c.old_flags == node->flags) {
76                 return;
77         }
78
79         c.new_flags = node->flags;
80
81         data.dptr = (uint8_t *)&c;
82         data.dsize = sizeof(c);
83
84         /* ask the recovery daemon to push these changes out to all nodes */
85         ctdb_daemon_send_message(ctdb, ctdb->pnn,
86                                  CTDB_SRVID_PUSH_NODE_FLAGS, data);
87
88 }
89
90
91 /*
92   called when the startup event script finishes
93  */
94 static void ctdb_startup_callback(struct ctdb_context *ctdb, int status, void *p)
95 {
96         if (status != 0) {
97                 DEBUG(DEBUG_ERR,("startup event failed\n"));
98         } else if (status == 0) {
99                 DEBUG(DEBUG_NOTICE,("startup event OK - enabling monitoring\n"));
100                 ctdb->done_startup = true;
101                 ctdb->monitor->next_interval = 1;
102         }
103
104         event_add_timed(ctdb->ev, ctdb->monitor->monitor_context, 
105                         timeval_current_ofs(ctdb->monitor->next_interval, 0),
106                         ctdb_check_health, ctdb);
107 }
108
109
110 /*
111   see if the event scripts think we are healthy
112  */
113 static void ctdb_check_health(struct event_context *ev, struct timed_event *te, 
114                               struct timeval t, void *private_data)
115 {
116         struct ctdb_context *ctdb = talloc_get_type(private_data, struct ctdb_context);
117         int ret;
118
119         if (ctdb->recovery_mode != CTDB_RECOVERY_NORMAL ||
120             (ctdb->monitor->monitoring_mode == CTDB_MONITORING_DISABLED && ctdb->done_startup)) {
121                 event_add_timed(ctdb->ev, ctdb->monitor->monitor_context,
122                                 timeval_current_ofs(ctdb->monitor->next_interval, 0), 
123                                 ctdb_check_health, ctdb);
124                 return;
125         }
126         
127         if (!ctdb->done_startup) {
128                 ret = ctdb_event_script_callback(ctdb, 
129                                                  timeval_current_ofs(ctdb->tunable.script_timeout, 0),
130                                                  ctdb->monitor->monitor_context, ctdb_startup_callback, 
131                                                  ctdb, "startup");
132         } else {
133                 ret = ctdb_event_script_callback(ctdb, 
134                                                  timeval_current_ofs(ctdb->tunable.script_timeout, 0),
135                                                  ctdb->monitor->monitor_context, ctdb_health_callback, 
136                                                  ctdb, "monitor");
137         }
138
139         if (ret != 0) {
140                 DEBUG(DEBUG_ERR,("Unable to launch monitor event script\n"));
141                 ctdb->monitor->next_interval = 1;
142                 event_add_timed(ctdb->ev, ctdb->monitor->monitor_context, 
143                                 timeval_current_ofs(1, 0), 
144                                 ctdb_check_health, ctdb);
145         }       
146 }
147
148 /* 
149   (Temporaily) Disabling monitoring will stop the monitor event scripts
150   from running   but node health checks will still occur
151 */
152 void ctdb_disable_monitoring(struct ctdb_context *ctdb)
153 {
154         ctdb->monitor->monitoring_mode = CTDB_MONITORING_DISABLED;
155         DEBUG(DEBUG_INFO,("Monitoring has been disabled\n"));
156 }
157
158 /* 
159    Re-enable running monitor events after they have been disabled
160  */
161 void ctdb_enable_monitoring(struct ctdb_context *ctdb)
162 {
163         ctdb->monitor->monitoring_mode  = CTDB_MONITORING_ACTIVE;
164         ctdb->monitor->next_interval = 2;
165         DEBUG(DEBUG_INFO,("Monitoring has been enabled\n"));
166 }
167
168 /* stop any monitoring 
169    this should only be done when shutting down the daemon
170 */
171 void ctdb_stop_monitoring(struct ctdb_context *ctdb)
172 {
173         talloc_free(ctdb->monitor->monitor_context);
174         ctdb->monitor->monitor_context = NULL;
175
176         ctdb->monitor->monitoring_mode  = CTDB_MONITORING_DISABLED;
177         ctdb->monitor->next_interval = 1;
178         DEBUG(DEBUG_NOTICE,("Monitoring has been stopped\n"));
179 }
180
181 /*
182   start watching for nodes that might be dead
183  */
184 void ctdb_start_monitoring(struct ctdb_context *ctdb)
185 {
186         struct timed_event *te;
187
188         if (ctdb->monitor != NULL) {
189                 return;
190         }
191
192         ctdb->monitor = talloc(ctdb, struct ctdb_monitor_state);
193         CTDB_NO_MEMORY_FATAL(ctdb, ctdb->monitor);
194
195         ctdb->monitor->next_interval = 1;
196
197         ctdb->monitor->monitor_context = talloc_new(ctdb->monitor);
198         CTDB_NO_MEMORY_FATAL(ctdb, ctdb->monitor->monitor_context);
199
200         te = event_add_timed(ctdb->ev, ctdb->monitor->monitor_context,
201                              timeval_current_ofs(1, 0), 
202                              ctdb_check_health, ctdb);
203         CTDB_NO_MEMORY_FATAL(ctdb, te);
204
205         ctdb->monitor->monitoring_mode  = CTDB_MONITORING_ACTIVE;
206         DEBUG(DEBUG_NOTICE,("Monitoring has been started\n"));
207 }
208
209
210 /*
211   modify flags on a node
212  */
213 int32_t ctdb_control_modflags(struct ctdb_context *ctdb, TDB_DATA indata)
214 {
215         struct ctdb_node_flag_change *c = (struct ctdb_node_flag_change *)indata.dptr;
216         struct ctdb_node *node;
217         
218         if (c->pnn >= ctdb->num_nodes) {
219                 DEBUG(DEBUG_ERR,(__location__ " Node %d is invalid, num_nodes :%d\n", c->pnn, ctdb->num_nodes));
220                 return -1;
221         }
222
223         node         = ctdb->nodes[c->pnn];
224         c->old_flags  = node->flags;
225         node->flags   = c->new_flags & ~NODE_FLAGS_DISCONNECTED;
226         node->flags  |= (c->old_flags & NODE_FLAGS_DISCONNECTED);
227
228         if (node->flags == c->old_flags) {
229                 DEBUG(DEBUG_INFO, ("Control modflags on node %u - Unchanged - flags 0x%x\n", c->pnn, node->flags));
230                 return 0;
231         }
232
233         DEBUG(DEBUG_INFO, ("Control modflags on node %u - flags now 0x%x\n", c->pnn, node->flags));
234
235
236         /* tell the recovery daemon something has changed */
237         ctdb_daemon_send_message(ctdb, ctdb->pnn,
238                                  CTDB_SRVID_SET_NODE_FLAGS, indata);
239
240         /* if we have become banned, we should go into recovery mode */
241         if ((node->flags & NODE_FLAGS_BANNED) && !(c->old_flags & NODE_FLAGS_BANNED)) {
242                 /* make sure we are frozen */
243                 DEBUG(DEBUG_NOTICE,("This node has been banned - forcing freeze and recovery\n"));
244                 /* Reset the generation id to 1 to make us ignore any
245                    REQ/REPLY CALL/DMASTER someone sends to us.
246                    We are now banned so we shouldnt service database calls
247                    anymore.
248                 */
249                 ctdb->vnn_map->generation = INVALID_GENERATION;
250
251                 ctdb_start_freeze(ctdb);
252                 ctdb_release_all_ips(ctdb);
253                 ctdb->recovery_mode = CTDB_RECOVERY_ACTIVE;
254         }
255         
256         return 0;
257 }
258
259 /*
260   return the monitoring mode
261  */
262 int32_t ctdb_monitoring_mode(struct ctdb_context *ctdb)
263 {
264         if (ctdb->monitor == NULL) {
265                 return CTDB_MONITORING_DISABLED;
266         }
267         return ctdb->monitor->monitoring_mode;
268 }
269