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(const 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, const char *target)
302 {
303 xmlNode *history = NULL;
304 GHashTableIter iter;
305 remote_fencing_op_t *op = NULL;
306 gboolean updated = FALSE;
307 int cnt = 0;
308
309 if (stonith_remote_op_list) {
310 char *id = NULL;
311
312 history = create_xml_node(NULL, F_STONITH_HISTORY_LIST);
313
314 g_hash_table_iter_init(&iter, stonith_remote_op_list);
315 while (g_hash_table_iter_next(&iter, (void **)&id, (void **)&op)) {
316 xmlNode *entry = NULL;
317
318 if (remote_history) {
319 remote_fencing_op_t *remote_op =
320 g_hash_table_lookup(remote_history, op->id);
321
322 if (remote_op) {
323 if (stonith__op_state_pending(op->state)
324 && !stonith__op_state_pending(remote_op->state)) {
325
326 crm_debug("Updating outdated pending operation %.8s "
327 "(state=%s) according to the one (state=%s) from "
328 "remote peer history",
329 op->id, stonith_op_state_str(op->state),
330 stonith_op_state_str(remote_op->state));
331
332 g_hash_table_steal(remote_history, op->id);
333 op->id = remote_op->id;
334 remote_op->id = id;
335 g_hash_table_iter_replace(&iter, remote_op);
336
337 updated = TRUE;
338 continue;
339
340 } else if (!stonith__op_state_pending(op->state)
341 && stonith__op_state_pending(remote_op->state)) {
342
343 crm_debug("Broadcasting operation %.8s (state=%s) to "
344 "update the outdated pending one "
345 "(state=%s) in remote peer history",
346 op->id, stonith_op_state_str(op->state),
347 stonith_op_state_str(remote_op->state));
348
349 g_hash_table_remove(remote_history, op->id);
350
351 } else {
352 g_hash_table_remove(remote_history, op->id);
353 continue;
354 }
355 }
356 }
357
358 if (!pcmk__str_eq(target, op->target, pcmk__str_null_matches)) {
359 continue;
360 }
361
362 cnt++;
363 crm_trace("Attaching op %s", op->id);
364 entry = create_xml_node(history, STONITH_OP_EXEC);
365 if (add_id) {
366 crm_xml_add(entry, F_STONITH_REMOTE_OP_ID, op->id);
367 }
368 crm_xml_add(entry, F_STONITH_TARGET, op->target);
369 crm_xml_add(entry, F_STONITH_ACTION, op->action);
370 crm_xml_add(entry, F_STONITH_ORIGIN, op->originator);
371 crm_xml_add(entry, F_STONITH_DELEGATE, op->delegate);
372 crm_xml_add(entry, F_STONITH_CLIENTNAME, op->client_name);
373 crm_xml_add_ll(entry, F_STONITH_DATE, op->completed);
374 crm_xml_add_ll(entry, F_STONITH_DATE_NSEC, op->completed_nsec);
375 crm_xml_add_int(entry, F_STONITH_STATE, op->state);
376 stonith__xe_set_result(entry, &op->result);
377 }
378 }
379
380 if (remote_history) {
381 init_stonith_remote_op_hash_table(&stonith_remote_op_list);
382
383 updated |= g_hash_table_size(remote_history);
384
385 g_hash_table_iter_init(&iter, remote_history);
386 while (g_hash_table_iter_next(&iter, NULL, (void **)&op)) {
387 if (stonith__op_state_pending(op->state) &&
388 pcmk__str_eq(op->originator, stonith_our_uname, pcmk__str_casei)) {
389
390 crm_warn("Failing pending operation %.8s originated by us but "
391 "known only from peer history", op->id);
392 op->state = st_failed;
393 set_fencing_completed(op);
394
395
396
397
398 pcmk__set_result(&op->result, CRM_EX_EXPIRED, PCMK_EXEC_INVALID,
399 "Initiated by earlier fencer "
400 "process and presumed failed");
401 fenced_broadcast_op_result(op, false);
402 }
403
404 g_hash_table_iter_steal(&iter);
405 g_hash_table_replace(stonith_remote_op_list, op->id, op);
406
407
408
409
410
411
412
413
414 }
415
416 g_hash_table_destroy(remote_history);
417 }
418
419 if (updated) {
420 stonith_fence_history_trim();
421 fenced_send_notification(T_STONITH_NOTIFY_HISTORY, NULL, NULL);
422 }
423
424 if (cnt == 0) {
425 free_xml(history);
426 return NULL;
427 } else {
428 return history;
429 }
430 }
431
432
433
434
435
436
437
438
439
440
441
442 static xmlNode *
443 stonith_local_history(gboolean add_id, const char *target)
444 {
445 return stonith_local_history_diff_and_merge(NULL, add_id, target);
446 }
447
448
449
450
451
452
453
454
455
456
457 void
458 stonith_fence_history(xmlNode *msg, xmlNode **output,
459 const char *remote_peer, int options)
460 {
461 const char *target = NULL;
462 xmlNode *dev = get_xpath_object("//@" F_STONITH_TARGET, msg, LOG_NEVER);
463 xmlNode *out_history = NULL;
464
465 if (dev) {
466 target = crm_element_value(dev, F_STONITH_TARGET);
467 if (target && (options & st_opt_cs_nodeid)) {
468 int nodeid;
469 crm_node_t *node;
470
471 pcmk__scan_min_int(target, &nodeid, 0);
472 node = pcmk__search_known_node_cache(nodeid, NULL, CRM_GET_PEER_ANY);
473 if (node) {
474 target = node->uname;
475 }
476 }
477 }
478
479 if (options & st_opt_cleanup) {
480 crm_trace("Cleaning up operations on %s in %p", target,
481 stonith_remote_op_list);
482
483 stonith_fence_history_cleanup(target,
484 crm_element_value(msg, F_STONITH_CALLID) != NULL);
485 } else if (options & st_opt_broadcast) {
486
487
488
489
490 fenced_send_notification(T_STONITH_NOTIFY_HISTORY_SYNCED, NULL, NULL);
491 if (crm_element_value(msg, F_STONITH_CALLID)) {
492
493
494
495
496
497
498 out_history = stonith_local_history(TRUE, NULL);
499 crm_trace("Broadcasting history to peers");
500 stonith_send_broadcast_history(out_history,
501 st_opt_broadcast | st_opt_discard_reply,
502 NULL);
503 } else if (remote_peer &&
504 !pcmk__str_eq(remote_peer, stonith_our_uname, pcmk__str_casei)) {
505 xmlNode *history = get_xpath_object("//" F_STONITH_HISTORY_LIST,
506 msg, LOG_NEVER);
507
508
509
510
511
512
513
514
515
516
517 if (!history || !pcmk__xe_attr_is_true(history, F_STONITH_DIFFERENTIAL)) {
518 GHashTable *received_history = NULL;
519
520 if (history != NULL) {
521 received_history = stonith_xml_history_to_list(history);
522 }
523 out_history =
524 stonith_local_history_diff_and_merge(received_history, TRUE, NULL);
525 if (out_history) {
526 crm_trace("Broadcasting history-diff to peers");
527 pcmk__xe_set_bool_attr(out_history, F_STONITH_DIFFERENTIAL, true);
528 stonith_send_broadcast_history(out_history,
529 st_opt_broadcast | st_opt_discard_reply,
530 NULL);
531 } else {
532 crm_trace("History-diff is empty - skip broadcast");
533 }
534 }
535 } else {
536 crm_trace("Skipping history-query-broadcast (%s%s)"
537 " we sent ourselves",
538 remote_peer?"remote-peer=":"local-ipc",
539 remote_peer?remote_peer:"");
540 }
541 } else {
542
543 crm_trace("Looking for operations on %s in %p", target,
544 stonith_remote_op_list);
545 *output = stonith_local_history(FALSE, target);
546 }
547 free_xml(out_history);
548 }