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 fenced_send_notification(T_STONITH_NOTIFY_HISTORY, NULL, 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 if ((*b)->completed_nsec > (*a)->completed_nsec) {
153 return 1;
154 } else if ((*b)->completed_nsec == (*a)->completed_nsec) {
155 return 0;
156 }
157 } else if ((*b)->completed > (*a)->completed) {
158 return 1;
159 }
160
161 return -1;
162 }
163
164
165
166
167
168
169
170 void
171 stonith_fence_history_trim(void)
172 {
173 guint num_ops;
174
175 if (!stonith_remote_op_list) {
176 return;
177 }
178 num_ops = g_hash_table_size(stonith_remote_op_list);
179 if (num_ops > MAX_STONITH_HISTORY) {
180 remote_fencing_op_t *ops[num_ops];
181 remote_fencing_op_t *op = NULL;
182 GHashTableIter iter;
183 int i;
184
185 crm_trace("Fencing History growing beyond limit of %d so purge "
186 "half of failed/successful attempts", MAX_STONITH_HISTORY);
187
188
189 i = 0;
190 g_hash_table_iter_init(&iter, stonith_remote_op_list);
191 while (g_hash_table_iter_next(&iter, NULL, (void **)&op)) {
192 ops[i++] = op;
193 }
194
195
196
197 qsort(ops, num_ops, sizeof(remote_fencing_op_t *), op_time_sort);
198
199 for (i = MAX_STONITH_HISTORY / 2; i < num_ops; i++) {
200
201
202
203 if ((ops[i]->state == st_failed) || (ops[i]->state == st_done)) {
204 g_hash_table_remove(stonith_remote_op_list, ops[i]->id);
205 }
206 }
207
208
209
210 }
211 }
212
213
214
215
216
217
218
219
220
221 static GHashTable *
222 stonith_xml_history_to_list(xmlNode *history)
223 {
224 xmlNode *xml_op = NULL;
225 GHashTable *rv = NULL;
226
227 init_stonith_remote_op_hash_table(&rv);
228
229 CRM_LOG_ASSERT(rv != NULL);
230
231 for (xml_op = pcmk__xml_first_child(history); xml_op != NULL;
232 xml_op = pcmk__xml_next(xml_op)) {
233 remote_fencing_op_t *op = NULL;
234 char *id = crm_element_value_copy(xml_op, F_STONITH_REMOTE_OP_ID);
235 int state;
236 int exit_status = CRM_EX_OK;
237 int execution_status = PCMK_EXEC_DONE;
238 long long completed;
239 long long completed_nsec = 0L;
240
241 if (!id) {
242 crm_warn("Malformed fencing history received from peer");
243 continue;
244 }
245
246 crm_trace("Attaching op %s to hashtable", id);
247
248 op = calloc(1, sizeof(remote_fencing_op_t));
249
250 op->id = id;
251 op->target = crm_element_value_copy(xml_op, F_STONITH_TARGET);
252 op->action = crm_element_value_copy(xml_op, F_STONITH_ACTION);
253 op->originator = crm_element_value_copy(xml_op, F_STONITH_ORIGIN);
254 op->delegate = crm_element_value_copy(xml_op, F_STONITH_DELEGATE);
255 op->client_name = crm_element_value_copy(xml_op, F_STONITH_CLIENTNAME);
256 crm_element_value_ll(xml_op, F_STONITH_DATE, &completed);
257 op->completed = (time_t) completed;
258 crm_element_value_ll(xml_op, F_STONITH_DATE_NSEC, &completed_nsec);
259 op->completed_nsec = completed_nsec;
260 crm_element_value_int(xml_op, F_STONITH_STATE, &state);
261 op->state = (enum op_state) state;
262
263
264
265
266
267 if ((crm_element_value_int(xml_op, XML_LRM_ATTR_RC, &exit_status) < 0)
268 || (crm_element_value_int(xml_op, XML_LRM_ATTR_OPSTATUS,
269 &execution_status) < 0)) {
270 exit_status = CRM_EX_INDETERMINATE;
271 execution_status = PCMK_EXEC_UNKNOWN;
272 }
273 pcmk__set_result(&op->result, exit_status, execution_status,
274 crm_element_value(xml_op, XML_LRM_ATTR_EXIT_REASON));
275 pcmk__set_result_output(&op->result,
276 crm_element_value_copy(xml_op, F_STONITH_OUTPUT),
277 NULL);
278
279
280 g_hash_table_replace(rv, id, op);
281 CRM_LOG_ASSERT(g_hash_table_lookup(rv, id) != NULL);
282 }
283
284 return rv;
285 }
286
287
288
289
290
291
292
293
294
295
296
297
298
299 static xmlNode *
300 stonith_local_history_diff_and_merge(GHashTable *remote_history,
301 gboolean add_id,
302 const char *target)
303 {
304 xmlNode *history = NULL;
305 GHashTableIter iter;
306 remote_fencing_op_t *op = NULL;
307 gboolean updated = FALSE;
308 int cnt = 0;
309
310 if (stonith_remote_op_list) {
311 char *id = NULL;
312
313 history = create_xml_node(NULL, F_STONITH_HISTORY_LIST);
314
315 g_hash_table_iter_init(&iter, stonith_remote_op_list);
316 while (g_hash_table_iter_next(&iter, (void **)&id, (void **)&op)) {
317 xmlNode *entry = NULL;
318
319 if (remote_history) {
320 remote_fencing_op_t *remote_op =
321 g_hash_table_lookup(remote_history, op->id);
322
323 if (remote_op) {
324 if (stonith__op_state_pending(op->state)
325 && !stonith__op_state_pending(remote_op->state)) {
326
327 crm_debug("Updating outdated pending operation %.8s "
328 "(state=%s) according to the one (state=%s) from "
329 "remote peer history",
330 op->id, stonith_op_state_str(op->state),
331 stonith_op_state_str(remote_op->state));
332
333 g_hash_table_steal(remote_history, op->id);
334 op->id = remote_op->id;
335 remote_op->id = id;
336 g_hash_table_iter_replace(&iter, remote_op);
337
338 updated = TRUE;
339 continue;
340
341 } else if (!stonith__op_state_pending(op->state)
342 && stonith__op_state_pending(remote_op->state)) {
343
344 crm_debug("Broadcasting operation %.8s (state=%s) to "
345 "update the outdated pending one "
346 "(state=%s) in remote peer history",
347 op->id, stonith_op_state_str(op->state),
348 stonith_op_state_str(remote_op->state));
349
350 g_hash_table_remove(remote_history, op->id);
351
352 } else {
353 g_hash_table_remove(remote_history, op->id);
354 continue;
355 }
356 }
357 }
358
359 if (!pcmk__str_eq(target, op->target, pcmk__str_null_matches)) {
360 continue;
361 }
362
363 cnt++;
364 crm_trace("Attaching op %s", op->id);
365 entry = create_xml_node(history, STONITH_OP_EXEC);
366 if (add_id) {
367 crm_xml_add(entry, F_STONITH_REMOTE_OP_ID, op->id);
368 }
369 crm_xml_add(entry, F_STONITH_TARGET, op->target);
370 crm_xml_add(entry, F_STONITH_ACTION, op->action);
371 crm_xml_add(entry, F_STONITH_ORIGIN, op->originator);
372 crm_xml_add(entry, F_STONITH_DELEGATE, op->delegate);
373 crm_xml_add(entry, F_STONITH_CLIENTNAME, op->client_name);
374 crm_xml_add_ll(entry, F_STONITH_DATE, op->completed);
375 crm_xml_add_ll(entry, F_STONITH_DATE_NSEC, op->completed_nsec);
376 crm_xml_add_int(entry, F_STONITH_STATE, op->state);
377 stonith__xe_set_result(entry, &op->result);
378 }
379 }
380
381 if (remote_history) {
382 init_stonith_remote_op_hash_table(&stonith_remote_op_list);
383
384 updated |= g_hash_table_size(remote_history);
385
386 g_hash_table_iter_init(&iter, remote_history);
387 while (g_hash_table_iter_next(&iter, NULL, (void **)&op)) {
388 if (stonith__op_state_pending(op->state) &&
389 pcmk__str_eq(op->originator, stonith_our_uname, pcmk__str_casei)) {
390
391 crm_warn("Failing pending operation %.8s originated by us but "
392 "known only from peer history", op->id);
393 op->state = st_failed;
394 set_fencing_completed(op);
395
396
397
398
399 pcmk__set_result(&op->result, CRM_EX_EXPIRED, PCMK_EXEC_INVALID,
400 "Initiated by earlier fencer "
401 "process and presumed failed");
402 fenced_broadcast_op_result(op, false);
403 }
404
405 g_hash_table_iter_steal(&iter);
406 g_hash_table_replace(stonith_remote_op_list, op->id, op);
407
408
409
410
411
412
413
414
415 }
416
417 g_hash_table_destroy(remote_history);
418 }
419
420 if (updated) {
421 stonith_fence_history_trim();
422 fenced_send_notification(T_STONITH_NOTIFY_HISTORY, NULL, NULL);
423 }
424
425 if (cnt == 0) {
426 free_xml(history);
427 return NULL;
428 } else {
429 return history;
430 }
431 }
432
433
434
435
436
437
438
439
440
441
442
443 static xmlNode *
444 stonith_local_history(gboolean add_id, const char *target)
445 {
446 return stonith_local_history_diff_and_merge(NULL, add_id, target);
447 }
448
449
450
451
452
453
454
455
456
457
458
459
460 void
461 stonith_fence_history(xmlNode *msg, xmlNode **output,
462 const char *remote_peer, int options)
463 {
464 const char *target = NULL;
465 xmlNode *dev = get_xpath_object("//@" F_STONITH_TARGET, msg, LOG_NEVER);
466 xmlNode *out_history = NULL;
467
468 if (dev) {
469 target = crm_element_value(dev, F_STONITH_TARGET);
470 if (target && (options & st_opt_cs_nodeid)) {
471 int nodeid;
472 crm_node_t *node;
473
474 pcmk__scan_min_int(target, &nodeid, 0);
475 node = pcmk__search_known_node_cache(nodeid, NULL, CRM_GET_PEER_ANY);
476 if (node) {
477 target = node->uname;
478 }
479 }
480 }
481
482 if (options & st_opt_cleanup) {
483 crm_trace("Cleaning up operations on %s in %p", target,
484 stonith_remote_op_list);
485
486 stonith_fence_history_cleanup(target,
487 crm_element_value(msg, F_STONITH_CALLID) != NULL);
488 } else if (options & st_opt_broadcast) {
489
490
491
492
493 fenced_send_notification(T_STONITH_NOTIFY_HISTORY_SYNCED, NULL, NULL);
494 if (crm_element_value(msg, F_STONITH_CALLID)) {
495
496
497
498
499
500
501 out_history = stonith_local_history(TRUE, NULL);
502 crm_trace("Broadcasting history to peers");
503 stonith_send_broadcast_history(out_history,
504 st_opt_broadcast | st_opt_discard_reply,
505 NULL);
506 } else if (remote_peer &&
507 !pcmk__str_eq(remote_peer, stonith_our_uname, pcmk__str_casei)) {
508 xmlNode *history = get_xpath_object("//" F_STONITH_HISTORY_LIST,
509 msg, LOG_NEVER);
510
511
512
513
514
515
516
517
518
519
520 if (!history || !pcmk__xe_attr_is_true(history, F_STONITH_DIFFERENTIAL)) {
521 GHashTable *received_history = NULL;
522
523 if (history != NULL) {
524 received_history = stonith_xml_history_to_list(history);
525 }
526 out_history =
527 stonith_local_history_diff_and_merge(received_history, TRUE, NULL);
528 if (out_history) {
529 crm_trace("Broadcasting history-diff to peers");
530 pcmk__xe_set_bool_attr(out_history, F_STONITH_DIFFERENTIAL, true);
531 stonith_send_broadcast_history(out_history,
532 st_opt_broadcast | st_opt_discard_reply,
533 NULL);
534 } else {
535 crm_trace("History-diff is empty - skip broadcast");
536 }
537 }
538 } else {
539 crm_trace("Skipping history-query-broadcast (%s%s)"
540 " we sent ourselves",
541 remote_peer?"remote-peer=":"local-ipc",
542 remote_peer?remote_peer:"");
543 }
544 } else {
545
546 crm_trace("Looking for operations on %s in %p", target,
547 stonith_remote_op_list);
548 *output = stonith_local_history(FALSE, target);
549 }
550 free_xml(out_history);
551 }