This source file includes following definitions.
- stonith_send_broadcast_history
- stonith_remove_history_entry
- stonith_fence_history_cleanup
- cmp_op_by_completion
- remove_completed_remote_op
- 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 <libxml/tree.h>
17
18 #include <crm/crm.h>
19 #include <crm/common/ipc.h>
20 #include <crm/common/ipc_internal.h>
21 #include <crm/cluster/internal.h>
22
23 #include <crm/stonith-ng.h>
24 #include <crm/fencing/internal.h>
25 #include <crm/common/xml.h>
26 #include <crm/common/xml_internal.h>
27
28 #include <pacemaker-fenced.h>
29
30 #define MAX_STONITH_HISTORY 500
31
32
33
34
35
36
37
38
39
40
41 static void
42 stonith_send_broadcast_history(xmlNode *history,
43 int callopts,
44 const char *target)
45 {
46 xmlNode *bcast = pcmk__xe_create(NULL, PCMK__XE_STONITH_COMMAND);
47 xmlNode *wrapper = pcmk__xe_create(bcast, PCMK__XE_ST_CALLDATA);
48 xmlNode *call_data = pcmk__xe_create(wrapper, __func__);
49
50 crm_xml_add(bcast, PCMK__XA_T, PCMK__VALUE_STONITH_NG);
51 crm_xml_add(bcast, PCMK__XA_SUBT, PCMK__VALUE_BROADCAST);
52 crm_xml_add(bcast, PCMK__XA_ST_OP, STONITH_OP_FENCE_HISTORY);
53 crm_xml_add_int(bcast, PCMK__XA_ST_CALLOPT, callopts);
54
55 pcmk__xml_copy(call_data, history);
56 if (target != NULL) {
57 crm_xml_add(call_data, PCMK__XA_ST_TARGET, target);
58 }
59
60 pcmk__cluster_send_message(NULL, pcmk_ipc_fenced, bcast);
61
62 pcmk__xml_free(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(PCMK__VALUE_ST_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
138
139
140
141
142
143
144
145
146
147
148
149
150 static gint
151 cmp_op_by_completion(gconstpointer a, gconstpointer b)
152 {
153 const remote_fencing_op_t *op1 = a;
154 const remote_fencing_op_t *op2 = b;
155 bool op1_pending = stonith__op_state_pending(op1->state);
156 bool op2_pending = stonith__op_state_pending(op2->state);
157
158 if (op1_pending && op2_pending) {
159 return 0;
160 }
161 if (op1_pending) {
162 return -1;
163 }
164 if (op2_pending) {
165 return 1;
166 }
167 if (op1->completed > op2->completed) {
168 return -1;
169 }
170 if (op1->completed < op2->completed) {
171 return 1;
172 }
173 if (op1->completed_nsec > op2->completed_nsec) {
174 return -1;
175 }
176 if (op1->completed_nsec < op2->completed_nsec) {
177 return 1;
178 }
179 return 0;
180 }
181
182
183
184
185
186
187
188
189 static void
190 remove_completed_remote_op(gpointer data, gpointer user_data)
191 {
192 const remote_fencing_op_t *op = data;
193
194 if (!stonith__op_state_pending(op->state)) {
195 g_hash_table_remove(stonith_remote_op_list, op->id);
196 }
197 }
198
199
200
201
202
203
204 void
205 stonith_fence_history_trim(void)
206 {
207 if (stonith_remote_op_list == NULL) {
208 return;
209 }
210
211 if (g_hash_table_size(stonith_remote_op_list) > MAX_STONITH_HISTORY) {
212 GList *ops = g_hash_table_get_values(stonith_remote_op_list);
213
214 crm_trace("More than %d entries in fencing history, purging oldest "
215 "completed operations", MAX_STONITH_HISTORY);
216
217 ops = g_list_sort(ops, cmp_op_by_completion);
218
219
220 g_list_foreach(g_list_nth(ops, MAX_STONITH_HISTORY / 2),
221 remove_completed_remote_op, NULL);
222
223
224 g_list_free(ops);
225 }
226 }
227
228
229
230
231
232
233
234
235
236 static GHashTable *
237 stonith_xml_history_to_list(const xmlNode *history)
238 {
239 xmlNode *xml_op = NULL;
240 GHashTable *rv = NULL;
241
242 init_stonith_remote_op_hash_table(&rv);
243
244 CRM_LOG_ASSERT(rv != NULL);
245
246 for (xml_op = pcmk__xe_first_child(history, NULL, NULL, NULL);
247 xml_op != NULL; xml_op = pcmk__xe_next(xml_op, NULL)) {
248
249 remote_fencing_op_t *op = NULL;
250 char *id = crm_element_value_copy(xml_op, PCMK__XA_ST_REMOTE_OP);
251 int state;
252 int exit_status = CRM_EX_OK;
253 int execution_status = PCMK_EXEC_DONE;
254 long long completed;
255 long long completed_nsec = 0L;
256
257 if (!id) {
258 crm_warn("Malformed fencing history received from peer");
259 continue;
260 }
261
262 crm_trace("Attaching op %s to hashtable", id);
263
264 op = pcmk__assert_alloc(1, sizeof(remote_fencing_op_t));
265
266 op->id = id;
267 op->target = crm_element_value_copy(xml_op, PCMK__XA_ST_TARGET);
268 op->action = crm_element_value_copy(xml_op, PCMK__XA_ST_DEVICE_ACTION);
269 op->originator = crm_element_value_copy(xml_op, PCMK__XA_ST_ORIGIN);
270 op->delegate = crm_element_value_copy(xml_op, PCMK__XA_ST_DELEGATE);
271 op->client_name = crm_element_value_copy(xml_op,
272 PCMK__XA_ST_CLIENTNAME);
273 crm_element_value_ll(xml_op, PCMK__XA_ST_DATE, &completed);
274 op->completed = (time_t) completed;
275 crm_element_value_ll(xml_op, PCMK__XA_ST_DATE_NSEC, &completed_nsec);
276 op->completed_nsec = completed_nsec;
277 crm_element_value_int(xml_op, PCMK__XA_ST_STATE, &state);
278 op->state = (enum op_state) state;
279
280
281
282
283
284 if ((crm_element_value_int(xml_op, PCMK__XA_RC_CODE, &exit_status) < 0)
285 || (crm_element_value_int(xml_op, PCMK__XA_OP_STATUS,
286 &execution_status) < 0)) {
287 exit_status = CRM_EX_INDETERMINATE;
288 execution_status = PCMK_EXEC_UNKNOWN;
289 }
290 pcmk__set_result(&op->result, exit_status, execution_status,
291 crm_element_value(xml_op, PCMK_XA_EXIT_REASON));
292 pcmk__set_result_output(&op->result,
293 crm_element_value_copy(xml_op,
294 PCMK__XA_ST_OUTPUT),
295 NULL);
296
297
298 g_hash_table_replace(rv, id, op);
299 CRM_LOG_ASSERT(g_hash_table_lookup(rv, id) != NULL);
300 }
301
302 return rv;
303 }
304
305
306
307
308
309
310
311
312
313
314
315
316
317 static xmlNode *
318 stonith_local_history_diff_and_merge(GHashTable *remote_history,
319 gboolean add_id, const char *target)
320 {
321 xmlNode *history = NULL;
322 GHashTableIter iter;
323 remote_fencing_op_t *op = NULL;
324 gboolean updated = FALSE;
325 int cnt = 0;
326
327 if (stonith_remote_op_list) {
328 char *id = NULL;
329
330 history = pcmk__xe_create(NULL, PCMK__XE_ST_HISTORY);
331
332 g_hash_table_iter_init(&iter, stonith_remote_op_list);
333 while (g_hash_table_iter_next(&iter, (void **)&id, (void **)&op)) {
334 xmlNode *entry = NULL;
335
336 if (remote_history) {
337 remote_fencing_op_t *remote_op =
338 g_hash_table_lookup(remote_history, op->id);
339
340 if (remote_op) {
341 if (stonith__op_state_pending(op->state)
342 && !stonith__op_state_pending(remote_op->state)) {
343
344 crm_debug("Updating outdated pending operation %.8s "
345 "(state=%s) according to the one (state=%s) from "
346 "remote peer history",
347 op->id, stonith__op_state_text(op->state),
348 stonith__op_state_text(remote_op->state));
349
350 g_hash_table_steal(remote_history, op->id);
351 op->id = remote_op->id;
352 remote_op->id = id;
353 g_hash_table_iter_replace(&iter, remote_op);
354
355 updated = TRUE;
356 continue;
357
358 } else if (!stonith__op_state_pending(op->state)
359 && stonith__op_state_pending(remote_op->state)) {
360
361 crm_debug("Broadcasting operation %.8s (state=%s) to "
362 "update the outdated pending one "
363 "(state=%s) in remote peer history",
364 op->id, stonith__op_state_text(op->state),
365 stonith__op_state_text(remote_op->state));
366
367 g_hash_table_remove(remote_history, op->id);
368
369 } else {
370 g_hash_table_remove(remote_history, op->id);
371 continue;
372 }
373 }
374 }
375
376 if (!pcmk__str_eq(target, op->target, pcmk__str_null_matches)) {
377 continue;
378 }
379
380 cnt++;
381 crm_trace("Attaching op %s", op->id);
382 entry = pcmk__xe_create(history, STONITH_OP_EXEC);
383 if (add_id) {
384 crm_xml_add(entry, PCMK__XA_ST_REMOTE_OP, op->id);
385 }
386 crm_xml_add(entry, PCMK__XA_ST_TARGET, op->target);
387 crm_xml_add(entry, PCMK__XA_ST_DEVICE_ACTION, op->action);
388 crm_xml_add(entry, PCMK__XA_ST_ORIGIN, op->originator);
389 crm_xml_add(entry, PCMK__XA_ST_DELEGATE, op->delegate);
390 crm_xml_add(entry, PCMK__XA_ST_CLIENTNAME, op->client_name);
391 crm_xml_add_ll(entry, PCMK__XA_ST_DATE, op->completed);
392 crm_xml_add_ll(entry, PCMK__XA_ST_DATE_NSEC,
393 op->completed_nsec);
394 crm_xml_add_int(entry, PCMK__XA_ST_STATE, op->state);
395 stonith__xe_set_result(entry, &op->result);
396 }
397 }
398
399 if (remote_history) {
400 init_stonith_remote_op_hash_table(&stonith_remote_op_list);
401
402 updated |= g_hash_table_size(remote_history);
403
404 g_hash_table_iter_init(&iter, remote_history);
405 while (g_hash_table_iter_next(&iter, NULL, (void **)&op)) {
406 if (stonith__op_state_pending(op->state) &&
407 pcmk__str_eq(op->originator, fenced_get_local_node(),
408 pcmk__str_casei)) {
409
410 crm_warn("Failing pending operation %.8s originated by us but "
411 "known only from peer history", op->id);
412 op->state = st_failed;
413 set_fencing_completed(op);
414
415
416
417
418 pcmk__set_result(&op->result, CRM_EX_EXPIRED, PCMK_EXEC_INVALID,
419 "Initiated by earlier fencer "
420 "process and presumed failed");
421 fenced_broadcast_op_result(op, false);
422 }
423
424 g_hash_table_iter_steal(&iter);
425 g_hash_table_replace(stonith_remote_op_list, op->id, op);
426
427
428
429
430
431
432
433
434 }
435
436 g_hash_table_destroy(remote_history);
437 }
438
439 if (updated) {
440 stonith_fence_history_trim();
441 fenced_send_notification(PCMK__VALUE_ST_NOTIFY_HISTORY, NULL, NULL);
442 }
443
444 if (cnt == 0) {
445 pcmk__xml_free(history);
446 return NULL;
447 } else {
448 return history;
449 }
450 }
451
452
453
454
455
456
457
458
459
460
461
462 static xmlNode *
463 stonith_local_history(gboolean add_id, const char *target)
464 {
465 return stonith_local_history_diff_and_merge(NULL, add_id, target);
466 }
467
468
469
470
471
472
473
474
475
476
477 void
478 stonith_fence_history(xmlNode *msg, xmlNode **output,
479 const char *remote_peer, int options)
480 {
481 const char *target = NULL;
482 xmlNode *dev = pcmk__xpath_find_one(msg->doc,
483 "//*[@" PCMK__XA_ST_TARGET "]",
484 LOG_NEVER);
485 xmlNode *out_history = NULL;
486
487 if (dev) {
488 target = crm_element_value(dev, PCMK__XA_ST_TARGET);
489 if (target && (options & st_opt_cs_nodeid)) {
490 int nodeid;
491 pcmk__node_status_t *node = NULL;
492
493 pcmk__scan_min_int(target, &nodeid, 0);
494 node = pcmk__search_node_caches(nodeid, NULL, NULL,
495 pcmk__node_search_any
496 |pcmk__node_search_cluster_cib);
497 if (node != NULL) {
498 target = node->name;
499 }
500 }
501 }
502
503 if (options & st_opt_cleanup) {
504 const char *call_id = crm_element_value(msg, PCMK__XA_ST_CALLID);
505
506 crm_trace("Cleaning up operations on %s in %p", target,
507 stonith_remote_op_list);
508 stonith_fence_history_cleanup(target, (call_id != NULL));
509
510 } else if (options & st_opt_broadcast) {
511
512
513
514
515 fenced_send_notification(PCMK__VALUE_ST_NOTIFY_HISTORY_SYNCED, NULL,
516 NULL);
517 if (crm_element_value(msg, PCMK__XA_ST_CALLID) != NULL) {
518
519
520
521
522
523
524 out_history = stonith_local_history(TRUE, NULL);
525 crm_trace("Broadcasting history to peers");
526 stonith_send_broadcast_history(out_history,
527 st_opt_broadcast | st_opt_discard_reply,
528 NULL);
529 } else if (remote_peer &&
530 !pcmk__str_eq(remote_peer, fenced_get_local_node(),
531 pcmk__str_casei)) {
532 xmlNode *history = pcmk__xpath_find_one(msg->doc,
533 "//" PCMK__XE_ST_HISTORY,
534 LOG_NEVER);
535
536
537
538
539
540
541
542
543
544
545 if (!history
546 || !pcmk__xe_attr_is_true(history, PCMK__XA_ST_DIFFERENTIAL)) {
547
548 GHashTable *received_history = NULL;
549
550 if (history != NULL) {
551 received_history = stonith_xml_history_to_list(history);
552 }
553 out_history =
554 stonith_local_history_diff_and_merge(received_history, TRUE, NULL);
555 if (out_history) {
556 crm_trace("Broadcasting history-diff to peers");
557 pcmk__xe_set_bool_attr(out_history,
558 PCMK__XA_ST_DIFFERENTIAL, true);
559 stonith_send_broadcast_history(out_history,
560 st_opt_broadcast | st_opt_discard_reply,
561 NULL);
562 } else {
563 crm_trace("History-diff is empty - skip broadcast");
564 }
565 }
566 } else {
567 crm_trace("Skipping history-query-broadcast (%s%s)"
568 " we sent ourselves",
569 remote_peer?"remote-peer=":"local-ipc",
570 remote_peer?remote_peer:"");
571 }
572 } else {
573
574 crm_trace("Looking for operations on %s in %p", target,
575 stonith_remote_op_list);
576 *output = stonith_local_history(FALSE, target);
577 }
578 pcmk__xml_free(out_history);
579 }