This source file includes following definitions.
- stonith_send_broadcast_history
- stonith_remove_history_entry
- stonith_fence_history_cleanup
- op_time_sort
- stonith_fence_history_trim
- stonith_xml_history_to_list
- stonith_local_history_diff_and_merge
- stonith_local_history
- stonith_fence_history
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <stdio.h>
13 #include <unistd.h>
14 #include <stdlib.h>
15
16 #include <crm/crm.h>
17 #include <crm/msg_xml.h>
18 #include <crm/common/ipc.h>
19 #include <crm/common/ipc_internal.h>
20 #include <crm/cluster/internal.h>
21
22 #include <crm/stonith-ng.h>
23 #include <crm/fencing/internal.h>
24 #include <crm/common/xml.h>
25 #include <crm/common/xml_internal.h>
26
27 #include <pacemaker-fenced.h>
28
29 #define MAX_STONITH_HISTORY 500
30
31
32
33
34
35
36
37
38
39
40 static void
41 stonith_send_broadcast_history(xmlNode *history,
42 int callopts,
43 const char *target)
44 {
45 xmlNode *bcast = create_xml_node(NULL, "stonith_command");
46 xmlNode *data = create_xml_node(NULL, __func__);
47
48 if (target) {
49 crm_xml_add(data, F_STONITH_TARGET, target);
50 }
51 crm_xml_add(bcast, F_TYPE, T_STONITH_NG);
52 crm_xml_add(bcast, F_SUBTYPE, "broadcast");
53 crm_xml_add(bcast, F_STONITH_OPERATION, STONITH_OP_FENCE_HISTORY);
54 crm_xml_add_int(bcast, F_STONITH_CALLOPTS, callopts);
55 if (history) {
56 add_node_copy(data, history);
57 }
58 add_message_xml(bcast, F_STONITH_CALLDATA, data);
59 send_cluster_message(NULL, crm_msg_stonith_ng, bcast, FALSE);
60
61 free_xml(data);
62 free_xml(bcast);
63 }
64
65 static gboolean
66 stonith_remove_history_entry (gpointer key,
67 gpointer value,
68 gpointer user_data)
69 {
70 remote_fencing_op_t *op = value;
71 const char *target = (const char *) user_data;
72
73 if ((op->state == st_failed) || (op->state == st_done)) {
74 if ((target) && (strcmp(op->target, target) != 0)) {
75 return FALSE;
76 }
77 return TRUE;
78 }
79
80 return FALSE;
81 }
82
83
84
85
86
87
88
89
90 static void
91 stonith_fence_history_cleanup(const char *target,
92 gboolean broadcast)
93 {
94 if (broadcast) {
95 stonith_send_broadcast_history(NULL,
96 st_opt_cleanup | st_opt_discard_reply,
97 target);
98
99 } else if (stonith_remote_op_list) {
100 g_hash_table_foreach_remove(stonith_remote_op_list,
101 stonith_remove_history_entry,
102 (gpointer) target);
103 do_stonith_notify(0, T_STONITH_NOTIFY_HISTORY, 0, NULL);
104 }
105 }
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137 static int
138 op_time_sort(const void *a_voidp, const void *b_voidp)
139 {
140 const remote_fencing_op_t **a = (const remote_fencing_op_t **) a_voidp;
141 const remote_fencing_op_t **b = (const remote_fencing_op_t **) b_voidp;
142 gboolean a_pending = ((*a)->state != st_failed) && ((*a)->state != st_done);
143 gboolean b_pending = ((*b)->state != st_failed) && ((*b)->state != st_done);
144
145 if (a_pending && b_pending) {
146 return 0;
147 } else if (a_pending) {
148 return -1;
149 } else if (b_pending) {
150 return 1;
151 } else if ((*b)->completed == (*a)->completed) {
152 return 0;
153 } else if ((*b)->completed > (*a)->completed) {
154 return 1;
155 }
156
157 return -1;
158 }
159
160
161
162
163
164
165
166 void
167 stonith_fence_history_trim(void)
168 {
169 guint num_ops;
170
171 if (!stonith_remote_op_list) {
172 return;
173 }
174 num_ops = g_hash_table_size(stonith_remote_op_list);
175 if (num_ops > MAX_STONITH_HISTORY) {
176 remote_fencing_op_t *ops[num_ops];
177 remote_fencing_op_t *op = NULL;
178 GHashTableIter iter;
179 int i;
180
181 crm_trace("Fencing History growing beyond limit of %d so purge "
182 "half of failed/successful attempts", MAX_STONITH_HISTORY);
183
184
185 i = 0;
186 g_hash_table_iter_init(&iter, stonith_remote_op_list);
187 while (g_hash_table_iter_next(&iter, NULL, (void **)&op)) {
188 ops[i++] = op;
189 }
190
191
192
193 qsort(ops, num_ops, sizeof(remote_fencing_op_t *), op_time_sort);
194
195 for (i = MAX_STONITH_HISTORY / 2; i < num_ops; i++) {
196
197
198
199 if ((ops[i]->state == st_failed) || (ops[i]->state == st_done)) {
200 g_hash_table_remove(stonith_remote_op_list, ops[i]->id);
201 }
202 }
203
204
205
206 }
207 }
208
209
210
211
212
213
214
215
216
217 static GHashTable *
218 stonith_xml_history_to_list(xmlNode *history)
219 {
220 xmlNode *xml_op = NULL;
221 GHashTable *rv = NULL;
222
223 init_stonith_remote_op_hash_table(&rv);
224
225 CRM_LOG_ASSERT(rv != NULL);
226
227 for (xml_op = pcmk__xml_first_child(history); xml_op != NULL;
228 xml_op = pcmk__xml_next(xml_op)) {
229 remote_fencing_op_t *op = NULL;
230 char *id = crm_element_value_copy(xml_op, F_STONITH_REMOTE_OP_ID);
231 int state;
232 long long completed;
233
234 if (!id) {
235 crm_warn("Malformed fencing history received from peer");
236 continue;
237 }
238
239 crm_trace("Attaching op %s to hashtable", id);
240
241 op = calloc(1, sizeof(remote_fencing_op_t));
242
243 op->id = id;
244 op->target = crm_element_value_copy(xml_op, F_STONITH_TARGET);
245 op->action = crm_element_value_copy(xml_op, F_STONITH_ACTION);
246 op->originator = crm_element_value_copy(xml_op, F_STONITH_ORIGIN);
247 op->delegate = crm_element_value_copy(xml_op, F_STONITH_DELEGATE);
248 op->client_name = crm_element_value_copy(xml_op, F_STONITH_CLIENTNAME);
249 crm_element_value_ll(xml_op, F_STONITH_DATE, &completed);
250 op->completed = (time_t) completed;
251 crm_element_value_int(xml_op, F_STONITH_STATE, &state);
252 op->state = (enum op_state) state;
253
254 g_hash_table_replace(rv, id, op);
255 CRM_LOG_ASSERT(g_hash_table_lookup(rv, id) != NULL);
256 }
257
258 return rv;
259 }
260
261
262
263
264
265
266
267
268
269
270
271
272
273 static xmlNode *
274 stonith_local_history_diff_and_merge(GHashTable *remote_history,
275 gboolean add_id,
276 const char *target)
277 {
278 xmlNode *history = NULL;
279 GHashTableIter iter;
280 remote_fencing_op_t *op = NULL;
281 gboolean updated = FALSE;
282 int cnt = 0;
283
284 if (stonith_remote_op_list) {
285 char *id = NULL;
286
287 history = create_xml_node(NULL, F_STONITH_HISTORY_LIST);
288
289 g_hash_table_iter_init(&iter, stonith_remote_op_list);
290 while (g_hash_table_iter_next(&iter, (void **)&id, (void **)&op)) {
291 xmlNode *entry = NULL;
292
293 if (remote_history) {
294 remote_fencing_op_t *remote_op =
295 g_hash_table_lookup(remote_history, op->id);
296
297 if (remote_op) {
298 if (stonith__op_state_pending(op->state)
299 && !stonith__op_state_pending(remote_op->state)) {
300
301 crm_debug("Updating outdated pending operation %.8s "
302 "(state=%s) according to the one (state=%s) from "
303 "remote peer history",
304 op->id, stonith_op_state_str(op->state),
305 stonith_op_state_str(remote_op->state));
306
307 g_hash_table_steal(remote_history, op->id);
308 op->id = remote_op->id;
309 remote_op->id = id;
310 g_hash_table_iter_replace(&iter, remote_op);
311
312 updated = TRUE;
313 continue;
314
315 } else if (!stonith__op_state_pending(op->state)
316 && stonith__op_state_pending(remote_op->state)) {
317
318 crm_debug("Broadcasting operation %.8s (state=%s) to "
319 "update the outdated pending one "
320 "(state=%s) in remote peer history",
321 op->id, stonith_op_state_str(op->state),
322 stonith_op_state_str(remote_op->state));
323
324 g_hash_table_remove(remote_history, op->id);
325
326 } else {
327 g_hash_table_remove(remote_history, op->id);
328 continue;
329 }
330 }
331 }
332
333 if (!pcmk__str_eq(target, op->target, pcmk__str_null_matches)) {
334 continue;
335 }
336
337 cnt++;
338 crm_trace("Attaching op %s", op->id);
339 entry = create_xml_node(history, STONITH_OP_EXEC);
340 if (add_id) {
341 crm_xml_add(entry, F_STONITH_REMOTE_OP_ID, op->id);
342 }
343 crm_xml_add(entry, F_STONITH_TARGET, op->target);
344 crm_xml_add(entry, F_STONITH_ACTION, op->action);
345 crm_xml_add(entry, F_STONITH_ORIGIN, op->originator);
346 crm_xml_add(entry, F_STONITH_DELEGATE, op->delegate);
347 crm_xml_add(entry, F_STONITH_CLIENTNAME, op->client_name);
348 crm_xml_add_ll(entry, F_STONITH_DATE, op->completed);
349 crm_xml_add_int(entry, F_STONITH_STATE, op->state);
350 }
351 }
352
353 if (remote_history) {
354 init_stonith_remote_op_hash_table(&stonith_remote_op_list);
355
356 updated |= g_hash_table_size(remote_history);
357
358 g_hash_table_iter_init(&iter, remote_history);
359 while (g_hash_table_iter_next(&iter, NULL, (void **)&op)) {
360
361 if (stonith__op_state_pending(op->state) &&
362 pcmk__str_eq(op->originator, stonith_our_uname, pcmk__str_casei)) {
363 crm_warn("Failing pending operation %.8s originated by us but "
364 "known only from peer history", op->id);
365 op->state = st_failed;
366 op->completed = time(NULL);
367
368
369
370
371 stonith_bcast_result_to_peers(op, -EHOSTUNREACH, FALSE);
372 }
373
374 g_hash_table_iter_steal(&iter);
375 g_hash_table_replace(stonith_remote_op_list, op->id, op);
376
377
378
379
380
381
382
383
384 }
385
386 g_hash_table_destroy(remote_history);
387 }
388
389 if (updated) {
390 stonith_fence_history_trim();
391 do_stonith_notify(0, T_STONITH_NOTIFY_HISTORY, 0, NULL);
392 }
393
394 if (cnt == 0) {
395 free_xml(history);
396 return NULL;
397 } else {
398 return history;
399 }
400 }
401
402
403
404
405
406
407
408
409
410
411
412 static xmlNode *
413 stonith_local_history(gboolean add_id, const char *target)
414 {
415 return stonith_local_history_diff_and_merge(NULL, add_id, target);
416 }
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431 int
432 stonith_fence_history(xmlNode *msg, xmlNode **output,
433 const char *remote_peer, int options)
434 {
435 int rc = 0;
436 const char *target = NULL;
437 xmlNode *dev = get_xpath_object("//@" F_STONITH_TARGET, msg, LOG_NEVER);
438 xmlNode *out_history = NULL;
439
440 if (dev) {
441 target = crm_element_value(dev, F_STONITH_TARGET);
442 if (target && (options & st_opt_cs_nodeid)) {
443 int nodeid;
444 crm_node_t *node;
445
446 pcmk__scan_min_int(target, &nodeid, 0);
447 node = pcmk__search_known_node_cache(nodeid, NULL, CRM_GET_PEER_ANY);
448 if (node) {
449 target = node->uname;
450 }
451 }
452 }
453
454 if (options & st_opt_cleanup) {
455 crm_trace("Cleaning up operations on %s in %p", target,
456 stonith_remote_op_list);
457
458 stonith_fence_history_cleanup(target,
459 crm_element_value(msg, F_STONITH_CALLID) != NULL);
460 } else if (options & st_opt_broadcast) {
461
462
463
464
465 do_stonith_notify(0, T_STONITH_NOTIFY_HISTORY_SYNCED, 0, NULL);
466 if (crm_element_value(msg, F_STONITH_CALLID)) {
467
468
469
470
471
472
473 out_history = stonith_local_history(TRUE, NULL);
474 crm_trace("Broadcasting history to peers");
475 stonith_send_broadcast_history(out_history,
476 st_opt_broadcast | st_opt_discard_reply,
477 NULL);
478 } else if (remote_peer &&
479 !pcmk__str_eq(remote_peer, stonith_our_uname, pcmk__str_casei)) {
480 xmlNode *history = get_xpath_object("//" F_STONITH_HISTORY_LIST,
481 msg, LOG_NEVER);
482 GHashTable *received_history =
483 history?stonith_xml_history_to_list(history):NULL;
484
485
486
487
488
489
490
491
492
493
494 if (!history ||
495 !crm_is_true(crm_element_value(history,
496 F_STONITH_DIFFERENTIAL))) {
497 out_history =
498 stonith_local_history_diff_and_merge(received_history, TRUE, NULL);
499 if (out_history) {
500 crm_trace("Broadcasting history-diff to peers");
501 crm_xml_add(out_history, F_STONITH_DIFFERENTIAL,
502 XML_BOOLEAN_TRUE);
503 stonith_send_broadcast_history(out_history,
504 st_opt_broadcast | st_opt_discard_reply,
505 NULL);
506 } else {
507 crm_trace("History-diff is empty - skip broadcast");
508 }
509 }
510 } else {
511 crm_trace("Skipping history-query-broadcast (%s%s)"
512 " we sent ourselves",
513 remote_peer?"remote-peer=":"local-ipc",
514 remote_peer?remote_peer:"");
515 }
516 } else {
517
518 crm_trace("Looking for operations on %s in %p", target,
519 stonith_remote_op_list);
520 *output = stonith_local_history(FALSE, target);
521 }
522 free_xml(out_history);
523 return rc;
524 }