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