This source file includes following definitions.
- add_singleton
- lookup_singleton
- find_existing_action
- find_exact_action_config
- pcmk__find_action_config
- new_action
- pcmk__unpack_action_rsc_params
- update_action_optional
- effective_quorum_policy
- update_resource_action_runnable
- update_resource_flags_for_action
- valid_stop_on_fail
- validate_on_fail
- unpack_timeout
- unpack_interval_origin
- unpack_start_delay
- most_frequent_monitor
- pcmk__unpack_action_meta
- pcmk__action_requires
- pcmk__parse_on_fail
- pcmk__role_after_failure
- unpack_operation
- custom_action
- get_pseudo_op
- find_unfencing_devices
- node_priority_fencing_delay
- pe_fence_op
- pe_free_action
- get_complex_task
- find_first_action
- find_actions
- find_actions_exact
- pe__resource_actions
- pe__action2reason
- pe_action_set_reason
- pe__clear_resource_history
- pe__is_newer_op
- sort_op_by_callid
- pe__new_rsc_pseudo_action
- pe__add_action_expected_result
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <glib.h>
13 #include <stdbool.h>
14
15 #include <crm/crm.h>
16 #include <crm/common/xml.h>
17 #include <crm/common/scheduler_internal.h>
18 #include <crm/pengine/internal.h>
19 #include <crm/common/xml_internal.h>
20 #include "pe_status_private.h"
21
22 static void unpack_operation(pcmk_action_t *action, const xmlNode *xml_obj,
23 guint interval_ms);
24
25 static void
26 add_singleton(pcmk_scheduler_t *scheduler, pcmk_action_t *action)
27 {
28 if (scheduler->singletons == NULL) {
29 scheduler->singletons = pcmk__strkey_table(NULL, NULL);
30 }
31 g_hash_table_insert(scheduler->singletons, action->uuid, action);
32 }
33
34 static pcmk_action_t *
35 lookup_singleton(pcmk_scheduler_t *scheduler, const char *action_uuid)
36 {
37 if (scheduler->singletons == NULL) {
38 return NULL;
39 }
40 return g_hash_table_lookup(scheduler->singletons, action_uuid);
41 }
42
43
44
45
46
47
48
49
50
51
52
53
54 static pcmk_action_t *
55 find_existing_action(const char *key, const pcmk_resource_t *rsc,
56 const pcmk_node_t *node, const pcmk_scheduler_t *scheduler)
57 {
58 GList *matches = NULL;
59 pcmk_action_t *action = NULL;
60
61
62
63
64 matches = find_actions(((rsc == NULL)? scheduler->actions : rsc->actions),
65 key, node);
66 if (matches == NULL) {
67 return NULL;
68 }
69 CRM_LOG_ASSERT(!pcmk__list_of_multiple(matches));
70
71 action = matches->data;
72 g_list_free(matches);
73 return action;
74 }
75
76
77
78
79
80
81
82
83
84
85
86 static xmlNode *
87 find_exact_action_config(const pcmk_resource_t *rsc, const char *action_name,
88 guint interval_ms, bool include_disabled)
89 {
90 for (xmlNode *operation = pcmk__xe_first_child(rsc->ops_xml, PCMK_XE_OP,
91 NULL, NULL);
92 operation != NULL; operation = pcmk__xe_next_same(operation)) {
93
94 bool enabled = false;
95 const char *config_name = NULL;
96 const char *interval_spec = NULL;
97 guint tmp_ms = 0U;
98
99
100 if (!include_disabled
101 && (pcmk__xe_get_bool_attr(operation, PCMK_META_ENABLED,
102 &enabled) == pcmk_rc_ok) && !enabled) {
103 continue;
104 }
105
106 interval_spec = crm_element_value(operation, PCMK_META_INTERVAL);
107 pcmk_parse_interval_spec(interval_spec, &tmp_ms);
108 if (tmp_ms != interval_ms) {
109 continue;
110 }
111
112 config_name = crm_element_value(operation, PCMK_XA_NAME);
113 if (pcmk__str_eq(action_name, config_name, pcmk__str_none)) {
114 return operation;
115 }
116 }
117 return NULL;
118 }
119
120
121
122
123
124
125
126
127
128
129
130
131 xmlNode *
132 pcmk__find_action_config(const pcmk_resource_t *rsc, const char *action_name,
133 guint interval_ms, bool include_disabled)
134 {
135 xmlNode *action_config = NULL;
136
137
138 action_config = find_exact_action_config(rsc, action_name, interval_ms,
139 include_disabled);
140
141
142
143 if ((action_config == NULL)
144 && pcmk__str_any_of(action_name, PCMK_ACTION_MIGRATE_TO,
145 PCMK_ACTION_MIGRATE_FROM, NULL)) {
146 action_config = find_exact_action_config(rsc, "migrate", 0,
147 include_disabled);
148 }
149
150 return action_config;
151 }
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168 static pcmk_action_t *
169 new_action(char *key, const char *task, pcmk_resource_t *rsc,
170 const pcmk_node_t *node, bool optional, pcmk_scheduler_t *scheduler)
171 {
172 pcmk_action_t *action = pcmk__assert_alloc(1, sizeof(pcmk_action_t));
173
174 action->rsc = rsc;
175 action->task = pcmk__str_copy(task);
176 action->uuid = key;
177
178 if (node) {
179 action->node = pe__copy_node(node);
180 }
181
182 if (pcmk__str_eq(task, PCMK_ACTION_LRM_DELETE, pcmk__str_casei)) {
183
184 pcmk__set_action_flags(action, pcmk_action_on_dc);
185 }
186
187 pcmk__set_action_flags(action, pcmk_action_runnable);
188 if (optional) {
189 pcmk__set_action_flags(action, pcmk_action_optional);
190 } else {
191 pcmk__clear_action_flags(action, pcmk_action_optional);
192 }
193
194 if (rsc == NULL) {
195 action->meta = pcmk__strkey_table(free, free);
196 } else {
197 guint interval_ms = 0;
198
199 parse_op_key(key, NULL, NULL, &interval_ms);
200 action->op_entry = pcmk__find_action_config(rsc, task, interval_ms,
201 true);
202
203
204
205
206 if ((action->op_entry == NULL) && (strstr(key, "_notify_") != NULL)) {
207 action->op_entry = find_exact_action_config(rsc, PCMK_ACTION_NOTIFY,
208 0, true);
209 }
210
211 unpack_operation(action, action->op_entry, interval_ms);
212 }
213
214 pcmk__rsc_trace(rsc, "Created %s action %d (%s): %s for %s on %s",
215 (optional? "optional" : "required"),
216 scheduler->action_id, key, task,
217 ((rsc == NULL)? "no resource" : rsc->id),
218 pcmk__node_name(node));
219 action->id = scheduler->action_id++;
220
221 scheduler->actions = g_list_prepend(scheduler->actions, action);
222 if (rsc == NULL) {
223 add_singleton(scheduler, action);
224 } else {
225 rsc->actions = g_list_prepend(rsc->actions, action);
226 }
227 return action;
228 }
229
230
231
232
233
234
235
236
237
238
239
240 GHashTable *
241 pcmk__unpack_action_rsc_params(const xmlNode *action_xml,
242 GHashTable *node_attrs,
243 pcmk_scheduler_t *scheduler)
244 {
245 GHashTable *params = pcmk__strkey_table(free, free);
246
247 pe_rule_eval_data_t rule_data = {
248 .node_hash = node_attrs,
249 .now = scheduler->now,
250 .match_data = NULL,
251 .rsc_data = NULL,
252 .op_data = NULL
253 };
254
255 pe__unpack_dataset_nvpairs(action_xml, PCMK_XE_INSTANCE_ATTRIBUTES,
256 &rule_data, params, NULL,
257 FALSE, scheduler);
258 return params;
259 }
260
261
262
263
264
265
266
267
268 static void
269 update_action_optional(pcmk_action_t *action, gboolean optional)
270 {
271
272 if ((action->rsc != NULL) && (action->node != NULL)
273 && !pcmk_is_set(action->flags, pcmk_action_pseudo)
274 && !pcmk_is_set(action->rsc->flags, pcmk_rsc_managed)
275 && (g_hash_table_lookup(action->meta, PCMK_META_INTERVAL) == NULL)) {
276 pcmk__rsc_debug(action->rsc,
277 "%s on %s is optional (%s is unmanaged)",
278 action->uuid, pcmk__node_name(action->node),
279 action->rsc->id);
280 pcmk__set_action_flags(action, pcmk_action_optional);
281
282
283
284 } else if (!optional) {
285 pcmk__clear_action_flags(action, pcmk_action_optional);
286 }
287 }
288
289 static enum pe_quorum_policy
290 effective_quorum_policy(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
291 {
292 enum pe_quorum_policy policy = scheduler->no_quorum_policy;
293
294 if (pcmk_is_set(scheduler->flags, pcmk_sched_quorate)) {
295 policy = pcmk_no_quorum_ignore;
296
297 } else if (scheduler->no_quorum_policy == pcmk_no_quorum_demote) {
298 switch (rsc->role) {
299 case pcmk_role_promoted:
300 case pcmk_role_unpromoted:
301 if (rsc->next_role > pcmk_role_unpromoted) {
302 pe__set_next_role(rsc, pcmk_role_unpromoted,
303 PCMK_OPT_NO_QUORUM_POLICY "=demote");
304 }
305 policy = pcmk_no_quorum_ignore;
306 break;
307 default:
308 policy = pcmk_no_quorum_stop;
309 break;
310 }
311 }
312 return policy;
313 }
314
315
316
317
318
319
320
321
322
323
324 static void
325 update_resource_action_runnable(pcmk_action_t *action,
326 pcmk_scheduler_t *scheduler)
327 {
328 if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
329 return;
330 }
331
332 if (action->node == NULL) {
333 pcmk__rsc_trace(action->rsc, "%s is unrunnable (unallocated)",
334 action->uuid);
335 pcmk__clear_action_flags(action, pcmk_action_runnable);
336
337 } else if (!pcmk_is_set(action->flags, pcmk_action_on_dc)
338 && !(action->node->details->online)
339 && (!pcmk__is_guest_or_bundle_node(action->node)
340 || action->node->details->remote_requires_reset)) {
341 pcmk__clear_action_flags(action, pcmk_action_runnable);
342 do_crm_log(LOG_WARNING, "%s on %s is unrunnable (node is offline)",
343 action->uuid, pcmk__node_name(action->node));
344 if (pcmk_is_set(action->rsc->flags, pcmk_rsc_managed)
345 && pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_casei)
346 && !(action->node->details->unclean)) {
347 pe_fence_node(scheduler, action->node, "stop is unrunnable", false);
348 }
349
350 } else if (!pcmk_is_set(action->flags, pcmk_action_on_dc)
351 && action->node->details->pending) {
352 pcmk__clear_action_flags(action, pcmk_action_runnable);
353 do_crm_log(LOG_WARNING,
354 "Action %s on %s is unrunnable (node is pending)",
355 action->uuid, pcmk__node_name(action->node));
356
357 } else if (action->needs == pcmk_requires_nothing) {
358 pe_action_set_reason(action, NULL, TRUE);
359 if (pcmk__is_guest_or_bundle_node(action->node)
360 && !pe_can_fence(scheduler, action->node)) {
361
362
363
364
365
366 pcmk__rsc_debug(action->rsc,
367 "%s on %s is unrunnable "
368 "(node's host cannot be fenced)",
369 action->uuid, pcmk__node_name(action->node));
370 pcmk__clear_action_flags(action, pcmk_action_runnable);
371 } else {
372 pcmk__rsc_trace(action->rsc,
373 "%s on %s does not require fencing or quorum",
374 action->uuid, pcmk__node_name(action->node));
375 pcmk__set_action_flags(action, pcmk_action_runnable);
376 }
377
378 } else {
379 switch (effective_quorum_policy(action->rsc, scheduler)) {
380 case pcmk_no_quorum_stop:
381 pcmk__rsc_debug(action->rsc,
382 "%s on %s is unrunnable (no quorum)",
383 action->uuid, pcmk__node_name(action->node));
384 pcmk__clear_action_flags(action, pcmk_action_runnable);
385 pe_action_set_reason(action, "no quorum", true);
386 break;
387
388 case pcmk_no_quorum_freeze:
389 if (!action->rsc->fns->active(action->rsc, TRUE)
390 || (action->rsc->next_role > action->rsc->role)) {
391 pcmk__rsc_debug(action->rsc,
392 "%s on %s is unrunnable (no quorum)",
393 action->uuid,
394 pcmk__node_name(action->node));
395 pcmk__clear_action_flags(action, pcmk_action_runnable);
396 pe_action_set_reason(action, "quorum freeze", true);
397 }
398 break;
399
400 default:
401
402 pcmk__set_action_flags(action, pcmk_action_runnable);
403 break;
404 }
405 }
406 }
407
408
409
410
411
412
413
414
415 static void
416 update_resource_flags_for_action(pcmk_resource_t *rsc,
417 const pcmk_action_t *action)
418 {
419
420
421
422 if (pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_casei)) {
423 pcmk__set_rsc_flags(rsc, pcmk_rsc_stopping);
424
425 } else if (pcmk__str_eq(action->task, PCMK_ACTION_START, pcmk__str_casei)) {
426 if (pcmk_is_set(action->flags, pcmk_action_runnable)) {
427 pcmk__set_rsc_flags(rsc, pcmk_rsc_starting);
428 } else {
429 pcmk__clear_rsc_flags(rsc, pcmk_rsc_starting);
430 }
431 }
432 }
433
434 static bool
435 valid_stop_on_fail(const char *value)
436 {
437 return !pcmk__strcase_any_of(value,
438 PCMK_VALUE_STANDBY, PCMK_VALUE_DEMOTE,
439 PCMK_VALUE_STOP, NULL);
440 }
441
442
443
444
445
446
447
448
449
450
451 static void
452 validate_on_fail(const pcmk_resource_t *rsc, const char *action_name,
453 const xmlNode *action_config, GHashTable *meta)
454 {
455 const char *name = NULL;
456 const char *role = NULL;
457 const char *interval_spec = NULL;
458 const char *value = g_hash_table_lookup(meta, PCMK_META_ON_FAIL);
459 guint interval_ms = 0U;
460
461
462 if (pcmk__str_eq(action_name, PCMK_ACTION_STOP, pcmk__str_none)
463 && !valid_stop_on_fail(value)) {
464
465 pcmk__config_err("Resetting '" PCMK_META_ON_FAIL "' for %s stop "
466 "action to default value because '%s' is not "
467 "allowed for stop", rsc->id, value);
468 g_hash_table_remove(meta, PCMK_META_ON_FAIL);
469 return;
470 }
471
472
473
474
475 if (pcmk__str_eq(action_name, PCMK_ACTION_DEMOTE, pcmk__str_none)
476 && (value == NULL)) {
477
478
479
480
481
482 for (xmlNode *operation = pcmk__xe_first_child(rsc->ops_xml, PCMK_XE_OP,
483 NULL, NULL);
484 operation != NULL; operation = pcmk__xe_next_same(operation)) {
485
486 bool enabled = false;
487 const char *promote_on_fail = NULL;
488
489
490
491
492 promote_on_fail = crm_element_value(operation, PCMK_META_ON_FAIL);
493 if (promote_on_fail == NULL) {
494 continue;
495 }
496
497
498 name = crm_element_value(operation, PCMK_XA_NAME);
499 role = crm_element_value(operation, PCMK_XA_ROLE);
500 if (!pcmk__str_eq(name, PCMK_ACTION_MONITOR, pcmk__str_none)
501 || !pcmk__strcase_any_of(role, PCMK_ROLE_PROMOTED,
502 PCMK__ROLE_PROMOTED_LEGACY, NULL)) {
503 continue;
504 }
505 interval_spec = crm_element_value(operation, PCMK_META_INTERVAL);
506 pcmk_parse_interval_spec(interval_spec, &interval_ms);
507 if (interval_ms == 0U) {
508 continue;
509 }
510
511
512 if ((pcmk__xe_get_bool_attr(operation, PCMK_META_ENABLED,
513 &enabled) == pcmk_rc_ok) && !enabled) {
514 continue;
515 }
516
517
518
519
520 if (pcmk__str_eq(promote_on_fail, PCMK_VALUE_DEMOTE,
521 pcmk__str_casei)) {
522 continue;
523 }
524
525
526 pcmk__insert_dup(meta, PCMK_META_ON_FAIL, promote_on_fail);
527 }
528 return;
529 }
530
531 if (pcmk__str_eq(action_name, PCMK_ACTION_LRM_DELETE, pcmk__str_none)
532 && !pcmk__str_eq(value, PCMK_VALUE_IGNORE, pcmk__str_casei)) {
533
534 pcmk__insert_dup(meta, PCMK_META_ON_FAIL, PCMK_VALUE_IGNORE);
535 return;
536 }
537
538
539 if (pcmk__str_eq(value, PCMK_VALUE_DEMOTE, pcmk__str_casei)) {
540 name = crm_element_value(action_config, PCMK_XA_NAME);
541 role = crm_element_value(action_config, PCMK_XA_ROLE);
542 interval_spec = crm_element_value(action_config, PCMK_META_INTERVAL);
543 pcmk_parse_interval_spec(interval_spec, &interval_ms);
544
545 if (!pcmk__str_eq(name, PCMK_ACTION_PROMOTE, pcmk__str_none)
546 && ((interval_ms == 0U)
547 || !pcmk__str_eq(name, PCMK_ACTION_MONITOR, pcmk__str_none)
548 || !pcmk__strcase_any_of(role, PCMK_ROLE_PROMOTED,
549 PCMK__ROLE_PROMOTED_LEGACY, NULL))) {
550
551 pcmk__config_err("Resetting '" PCMK_META_ON_FAIL "' for %s %s "
552 "action to default value because 'demote' is not "
553 "allowed for it", rsc->id, name);
554 g_hash_table_remove(meta, PCMK_META_ON_FAIL);
555 return;
556 }
557 }
558 }
559
560 static int
561 unpack_timeout(const char *value)
562 {
563 long long timeout_ms = crm_get_msec(value);
564
565 if (timeout_ms <= 0) {
566 timeout_ms = PCMK_DEFAULT_ACTION_TIMEOUT_MS;
567 }
568 return (int) QB_MIN(timeout_ms, INT_MAX);
569 }
570
571
572 static bool
573 unpack_interval_origin(const char *value, const xmlNode *xml_obj,
574 guint interval_ms, const crm_time_t *now,
575 long long *start_delay)
576 {
577 long long result = 0;
578 guint interval_sec = interval_ms / 1000;
579 crm_time_t *origin = NULL;
580
581
582 if ((value == NULL) || (interval_ms == 0) || (now == NULL)) {
583 return false;
584 }
585
586
587 origin = crm_time_new(value);
588 if (origin == NULL) {
589 pcmk__config_err("Ignoring '" PCMK_META_INTERVAL_ORIGIN "' for "
590 "operation '%s' because '%s' is not valid",
591 pcmk__s(pcmk__xe_id(xml_obj), "(missing ID)"), value);
592 return false;
593 }
594
595
596 result = crm_time_get_seconds(now) - crm_time_get_seconds(origin);
597 crm_time_free(origin);
598
599
600 result = result % interval_sec;
601
602
603 result = ((result <= 0)? 0 : interval_sec) - result;
604 crm_info("Calculated a start delay of %llds for operation '%s'",
605 result, pcmk__s(pcmk__xe_id(xml_obj), "(unspecified)"));
606
607 if (start_delay != NULL) {
608 *start_delay = result * 1000;
609 }
610 return true;
611 }
612
613 static int
614 unpack_start_delay(const char *value, GHashTable *meta)
615 {
616 long long start_delay_ms = 0;
617
618 if (value == NULL) {
619 return 0;
620 }
621
622 start_delay_ms = crm_get_msec(value);
623 start_delay_ms = QB_MIN(start_delay_ms, INT_MAX);
624 if (start_delay_ms < 0) {
625 start_delay_ms = 0;
626 }
627
628 if (meta != NULL) {
629 g_hash_table_replace(meta, strdup(PCMK_META_START_DELAY),
630 pcmk__itoa(start_delay_ms));
631 }
632
633 return (int) start_delay_ms;
634 }
635
636
637
638
639
640
641
642
643
644
645 static xmlNode *
646 most_frequent_monitor(const pcmk_resource_t *rsc)
647 {
648 guint min_interval_ms = G_MAXUINT;
649 xmlNode *op = NULL;
650
651 for (xmlNode *operation = pcmk__xe_first_child(rsc->ops_xml, PCMK_XE_OP,
652 NULL, NULL);
653 operation != NULL; operation = pcmk__xe_next_same(operation)) {
654
655 bool enabled = false;
656 guint interval_ms = 0U;
657 const char *interval_spec = crm_element_value(operation,
658 PCMK_META_INTERVAL);
659
660
661 if (!pcmk__str_eq(crm_element_value(operation, PCMK_XA_NAME),
662 PCMK_ACTION_MONITOR, pcmk__str_none)) {
663 continue;
664 }
665
666 pcmk_parse_interval_spec(interval_spec, &interval_ms);
667 if (interval_ms == 0U) {
668 continue;
669 }
670
671
672 if ((pcmk__xe_get_bool_attr(operation, PCMK_META_ENABLED,
673 &enabled) == pcmk_rc_ok) && !enabled) {
674 continue;
675 }
676
677 if (interval_ms < min_interval_ms) {
678 min_interval_ms = interval_ms;
679 op = operation;
680 }
681 }
682 return op;
683 }
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701 GHashTable *
702 pcmk__unpack_action_meta(pcmk_resource_t *rsc, const pcmk_node_t *node,
703 const char *action_name, guint interval_ms,
704 const xmlNode *action_config)
705 {
706 GHashTable *meta = NULL;
707 const char *timeout_spec = NULL;
708 const char *str = NULL;
709
710 pe_rsc_eval_data_t rsc_rule_data = {
711 .standard = crm_element_value(rsc->xml, PCMK_XA_CLASS),
712 .provider = crm_element_value(rsc->xml, PCMK_XA_PROVIDER),
713 .agent = crm_element_value(rsc->xml, PCMK_XA_TYPE),
714 };
715
716 pe_op_eval_data_t op_rule_data = {
717 .op_name = action_name,
718 .interval = interval_ms,
719 };
720
721 pe_rule_eval_data_t rule_data = {
722
723
724
725
726
727 .node_hash = (node == NULL)? NULL : node->details->attrs,
728
729 .now = rsc->cluster->now,
730 .match_data = NULL,
731 .rsc_data = &rsc_rule_data,
732 .op_data = &op_rule_data,
733 };
734
735 meta = pcmk__strkey_table(free, free);
736
737
738 pe__unpack_dataset_nvpairs(rsc->cluster->op_defaults,
739 PCMK_XE_META_ATTRIBUTES, &rule_data, meta, NULL,
740 FALSE, rsc->cluster);
741
742
743 if (pcmk_is_probe(action_name, interval_ms)) {
744 xmlNode *min_interval_mon = most_frequent_monitor(rsc);
745
746 if (min_interval_mon != NULL) {
747
748
749
750
751 timeout_spec = crm_element_value(min_interval_mon,
752 PCMK_META_TIMEOUT);
753 if (timeout_spec != NULL) {
754 pcmk__rsc_trace(rsc,
755 "Setting default timeout for %s probe to "
756 "most frequent monitor's timeout '%s'",
757 rsc->id, timeout_spec);
758 pcmk__insert_dup(meta, PCMK_META_TIMEOUT, timeout_spec);
759 }
760 }
761 }
762
763 if (action_config != NULL) {
764
765 pe__unpack_dataset_nvpairs(action_config, PCMK_XE_META_ATTRIBUTES,
766 &rule_data, meta, NULL, TRUE, rsc->cluster);
767
768
769
770
771
772 for (xmlAttrPtr attr = action_config->properties;
773 attr != NULL; attr = attr->next) {
774 pcmk__insert_dup(meta, (const char *) attr->name,
775 pcmk__xml_attr_value(attr));
776 }
777 }
778
779 g_hash_table_remove(meta, PCMK_XA_ID);
780
781
782 if (interval_ms > 0) {
783 g_hash_table_insert(meta, pcmk__str_copy(PCMK_META_INTERVAL),
784 crm_strdup_printf("%u", interval_ms));
785 } else {
786 g_hash_table_remove(meta, PCMK_META_INTERVAL);
787 }
788
789
790
791
792
793
794
795
796
797
798
799
800 if (pcmk_is_set(pcmk_get_ra_caps(rsc_rule_data.standard),
801 pcmk_ra_cap_fence_params)
802 && (pcmk__str_eq(action_name, PCMK_ACTION_START, pcmk__str_none)
803 || pcmk_is_probe(action_name, interval_ms))) {
804
805 GHashTable *params = pe_rsc_params(rsc, node, rsc->cluster);
806
807 timeout_spec = g_hash_table_lookup(params, "pcmk_monitor_timeout");
808 if (timeout_spec != NULL) {
809 pcmk__rsc_trace(rsc,
810 "Setting timeout for %s %s to "
811 "pcmk_monitor_timeout (%s)",
812 rsc->id, action_name, timeout_spec);
813 pcmk__insert_dup(meta, PCMK_META_TIMEOUT, timeout_spec);
814 }
815 }
816
817
818 timeout_spec = g_hash_table_lookup(meta, PCMK_META_TIMEOUT);
819 g_hash_table_insert(meta, pcmk__str_copy(PCMK_META_TIMEOUT),
820 pcmk__itoa(unpack_timeout(timeout_spec)));
821
822
823 validate_on_fail(rsc, action_name, action_config, meta);
824
825
826 str = g_hash_table_lookup(meta, PCMK_META_START_DELAY);
827 if (str != NULL) {
828 unpack_start_delay(str, meta);
829 } else {
830 long long start_delay = 0;
831
832 str = g_hash_table_lookup(meta, PCMK_META_INTERVAL_ORIGIN);
833 if (unpack_interval_origin(str, action_config, interval_ms,
834 rsc->cluster->now, &start_delay)) {
835 g_hash_table_insert(meta, pcmk__str_copy(PCMK_META_START_DELAY),
836 crm_strdup_printf("%lld", start_delay));
837 }
838 }
839 return meta;
840 }
841
842
843
844
845
846
847
848
849
850
851 enum rsc_start_requirement
852 pcmk__action_requires(const pcmk_resource_t *rsc, const char *action_name)
853 {
854 const char *value = NULL;
855 enum rsc_start_requirement requires = pcmk_requires_nothing;
856
857 CRM_CHECK((rsc != NULL) && (action_name != NULL), return requires);
858
859 if (!pcmk__strcase_any_of(action_name, PCMK_ACTION_START,
860 PCMK_ACTION_PROMOTE, NULL)) {
861 value = "nothing (not start or promote)";
862
863 } else if (pcmk_is_set(rsc->flags, pcmk_rsc_needs_fencing)) {
864 requires = pcmk_requires_fencing;
865 value = "fencing";
866
867 } else if (pcmk_is_set(rsc->flags, pcmk_rsc_needs_quorum)) {
868 requires = pcmk_requires_quorum;
869 value = "quorum";
870
871 } else {
872 value = "nothing";
873 }
874 pcmk__rsc_trace(rsc, "%s of %s requires %s", action_name, rsc->id, value);
875 return requires;
876 }
877
878
879
880
881
882
883
884
885
886
887
888
889 enum action_fail_response
890 pcmk__parse_on_fail(const pcmk_resource_t *rsc, const char *action_name,
891 guint interval_ms, const char *value)
892 {
893 const char *desc = NULL;
894 bool needs_remote_reset = false;
895 enum action_fail_response on_fail = pcmk_on_fail_ignore;
896
897
898 pcmk__assert((rsc != NULL) && (action_name != NULL));
899
900 if (value == NULL) {
901
902
903 } else if (pcmk__str_eq(value, PCMK_VALUE_BLOCK, pcmk__str_casei)) {
904 on_fail = pcmk_on_fail_block;
905 desc = "block";
906
907 } else if (pcmk__str_eq(value, PCMK_VALUE_FENCE, pcmk__str_casei)) {
908 if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_fencing_enabled)) {
909 on_fail = pcmk_on_fail_fence_node;
910 desc = "node fencing";
911 } else {
912 pcmk__config_err("Resetting '" PCMK_META_ON_FAIL "' for "
913 "%s of %s to 'stop' because 'fence' is not "
914 "valid when fencing is disabled",
915 action_name, rsc->id);
916 on_fail = pcmk_on_fail_stop;
917 desc = "stop resource";
918 }
919
920 } else if (pcmk__str_eq(value, PCMK_VALUE_STANDBY, pcmk__str_casei)) {
921 on_fail = pcmk_on_fail_standby_node;
922 desc = "node standby";
923
924 } else if (pcmk__strcase_any_of(value,
925 PCMK_VALUE_IGNORE, PCMK_VALUE_NOTHING,
926 NULL)) {
927 desc = "ignore";
928
929 } else if (pcmk__str_eq(value, "migrate", pcmk__str_casei)) {
930 on_fail = pcmk_on_fail_ban;
931 desc = "force migration";
932
933 } else if (pcmk__str_eq(value, PCMK_VALUE_STOP, pcmk__str_casei)) {
934 on_fail = pcmk_on_fail_stop;
935 desc = "stop resource";
936
937 } else if (pcmk__str_eq(value, PCMK_VALUE_RESTART, pcmk__str_casei)) {
938 on_fail = pcmk_on_fail_restart;
939 desc = "restart (and possibly migrate)";
940
941 } else if (pcmk__str_eq(value, PCMK_VALUE_RESTART_CONTAINER,
942 pcmk__str_casei)) {
943 if (rsc->container == NULL) {
944 pcmk__rsc_debug(rsc,
945 "Using default " PCMK_META_ON_FAIL " for %s "
946 "of %s because it does not have a container",
947 action_name, rsc->id);
948 } else {
949 on_fail = pcmk_on_fail_restart_container;
950 desc = "restart container (and possibly migrate)";
951 }
952
953 } else if (pcmk__str_eq(value, PCMK_VALUE_DEMOTE, pcmk__str_casei)) {
954 on_fail = pcmk_on_fail_demote;
955 desc = "demote instance";
956
957 } else {
958 pcmk__config_err("Using default '" PCMK_META_ON_FAIL "' for "
959 "%s of %s because '%s' is not valid",
960 action_name, rsc->id, value);
961 }
962
963
964
965
966
967
968 if (rsc->is_remote_node
969 && pcmk__is_remote_node(pcmk_find_node(rsc->cluster, rsc->id))
970 && !pcmk_is_probe(action_name, interval_ms)
971 && !pcmk__str_eq(action_name, PCMK_ACTION_START, pcmk__str_none)) {
972 needs_remote_reset = true;
973 if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
974 desc = NULL;
975 }
976 }
977
978 if (desc != NULL) {
979
980
981 } else if (rsc->container != NULL) {
982 on_fail = pcmk_on_fail_restart_container;
983 desc = "restart container (and possibly migrate) (default)";
984
985 } else if (needs_remote_reset) {
986 if (pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
987 if (pcmk_is_set(rsc->cluster->flags,
988 pcmk_sched_fencing_enabled)) {
989 desc = "fence remote node (default)";
990 } else {
991 desc = "recover remote node connection (default)";
992 }
993 on_fail = pcmk_on_fail_reset_remote;
994 } else {
995 on_fail = pcmk_on_fail_stop;
996 desc = "stop unmanaged remote node (enforcing default)";
997 }
998
999 } else if (pcmk__str_eq(action_name, PCMK_ACTION_STOP, pcmk__str_none)) {
1000 if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_fencing_enabled)) {
1001 on_fail = pcmk_on_fail_fence_node;
1002 desc = "resource fence (default)";
1003 } else {
1004 on_fail = pcmk_on_fail_block;
1005 desc = "resource block (default)";
1006 }
1007
1008 } else {
1009 on_fail = pcmk_on_fail_restart;
1010 desc = "restart (and possibly migrate) (default)";
1011 }
1012
1013 pcmk__rsc_trace(rsc, "Failure handling for %s-interval %s of %s: %s",
1014 pcmk__readable_interval(interval_ms), action_name,
1015 rsc->id, desc);
1016 return on_fail;
1017 }
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030 enum rsc_role_e
1031 pcmk__role_after_failure(const pcmk_resource_t *rsc, const char *action_name,
1032 enum action_fail_response on_fail, GHashTable *meta)
1033 {
1034 const char *value = NULL;
1035 enum rsc_role_e role = pcmk_role_unknown;
1036
1037
1038 switch (on_fail) {
1039 case pcmk_on_fail_stop:
1040 role = pcmk_role_stopped;
1041 break;
1042
1043 case pcmk_on_fail_reset_remote:
1044 if (rsc->remote_reconnect_ms != 0) {
1045 role = pcmk_role_stopped;
1046 }
1047 break;
1048
1049 default:
1050 break;
1051 }
1052
1053
1054 value = g_hash_table_lookup(meta, PCMK__META_ROLE_AFTER_FAILURE);
1055 if (value != NULL) {
1056 pcmk__warn_once(pcmk__wo_role_after,
1057 "Support for " PCMK__META_ROLE_AFTER_FAILURE " is "
1058 "deprecated and will be removed in a future release");
1059 if (role == pcmk_role_unknown) {
1060 role = pcmk_parse_role(value);
1061 if (role == pcmk_role_unknown) {
1062 pcmk__config_err("Ignoring invalid value %s for "
1063 PCMK__META_ROLE_AFTER_FAILURE,
1064 value);
1065 }
1066 }
1067 }
1068
1069 if (role == pcmk_role_unknown) {
1070
1071 if (pcmk__str_eq(action_name, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
1072 role = pcmk_role_unpromoted;
1073 } else {
1074 role = pcmk_role_started;
1075 }
1076 }
1077 pcmk__rsc_trace(rsc, "Role after %s %s failure is: %s",
1078 rsc->id, action_name, pcmk_role_text(role));
1079 return role;
1080 }
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094 static void
1095 unpack_operation(pcmk_action_t *action, const xmlNode *xml_obj,
1096 guint interval_ms)
1097 {
1098 const char *value = NULL;
1099
1100 action->meta = pcmk__unpack_action_meta(action->rsc, action->node,
1101 action->task, interval_ms, xml_obj);
1102 action->needs = pcmk__action_requires(action->rsc, action->task);
1103
1104 value = g_hash_table_lookup(action->meta, PCMK_META_ON_FAIL);
1105 action->on_fail = pcmk__parse_on_fail(action->rsc, action->task,
1106 interval_ms, value);
1107
1108 action->fail_role = pcmk__role_after_failure(action->rsc, action->task,
1109 action->on_fail, action->meta);
1110 }
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128 pcmk_action_t *
1129 custom_action(pcmk_resource_t *rsc, char *key, const char *task,
1130 const pcmk_node_t *on_node, gboolean optional,
1131 pcmk_scheduler_t *scheduler)
1132 {
1133 pcmk_action_t *action = NULL;
1134
1135 pcmk__assert((key != NULL) && (task != NULL) && (scheduler != NULL));
1136
1137 action = find_existing_action(key, rsc, on_node, scheduler);
1138 if (action == NULL) {
1139 action = new_action(key, task, rsc, on_node, optional, scheduler);
1140 } else {
1141 free(key);
1142 }
1143
1144 update_action_optional(action, optional);
1145
1146 if (rsc != NULL) {
1147
1148
1149
1150
1151
1152 if ((action->node != NULL) && (action->op_entry != NULL)
1153 && !pcmk_is_set(action->flags, pcmk_action_attrs_evaluated)) {
1154
1155 GHashTable *attrs = action->node->details->attrs;
1156
1157 if (action->extra != NULL) {
1158 g_hash_table_destroy(action->extra);
1159 }
1160 action->extra = pcmk__unpack_action_rsc_params(action->op_entry,
1161 attrs, scheduler);
1162 pcmk__set_action_flags(action, pcmk_action_attrs_evaluated);
1163 }
1164
1165 update_resource_action_runnable(action, scheduler);
1166 update_resource_flags_for_action(rsc, action);
1167 }
1168
1169 if (action->extra == NULL) {
1170 action->extra = pcmk__strkey_table(free, free);
1171 }
1172
1173 return action;
1174 }
1175
1176 pcmk_action_t *
1177 get_pseudo_op(const char *name, pcmk_scheduler_t *scheduler)
1178 {
1179 pcmk_action_t *op = lookup_singleton(scheduler, name);
1180
1181 if (op == NULL) {
1182 op = custom_action(NULL, strdup(name), name, NULL, TRUE, scheduler);
1183 pcmk__set_action_flags(op, pcmk_action_pseudo|pcmk_action_runnable);
1184 }
1185 return op;
1186 }
1187
1188 static GList *
1189 find_unfencing_devices(GList *candidates, GList *matches)
1190 {
1191 for (GList *gIter = candidates; gIter != NULL; gIter = gIter->next) {
1192 pcmk_resource_t *candidate = gIter->data;
1193
1194 if (candidate->children != NULL) {
1195 matches = find_unfencing_devices(candidate->children, matches);
1196
1197 } else if (!pcmk_is_set(candidate->flags, pcmk_rsc_fence_device)) {
1198 continue;
1199
1200 } else if (pcmk_is_set(candidate->flags, pcmk_rsc_needs_unfencing)) {
1201 matches = g_list_prepend(matches, candidate);
1202
1203 } else if (pcmk__str_eq(g_hash_table_lookup(candidate->meta,
1204 PCMK_STONITH_PROVIDES),
1205 PCMK_VALUE_UNFENCING, pcmk__str_casei)) {
1206 matches = g_list_prepend(matches, candidate);
1207 }
1208 }
1209 return matches;
1210 }
1211
1212 static int
1213 node_priority_fencing_delay(const pcmk_node_t *node,
1214 const pcmk_scheduler_t *scheduler)
1215 {
1216 int member_count = 0;
1217 int online_count = 0;
1218 int top_priority = 0;
1219 int lowest_priority = 0;
1220 GList *gIter = NULL;
1221
1222
1223 if (scheduler->priority_fencing_delay <= 0) {
1224 return 0;
1225 }
1226
1227
1228
1229 if (node->details->type != pcmk_node_variant_cluster) {
1230 return 0;
1231 }
1232
1233
1234 if (node->details->online) {
1235 return 0;
1236 }
1237
1238 for (gIter = scheduler->nodes; gIter != NULL; gIter = gIter->next) {
1239 pcmk_node_t *n = gIter->data;
1240
1241 if (n->details->type != pcmk_node_variant_cluster) {
1242 continue;
1243 }
1244
1245 member_count ++;
1246
1247 if (n->details->online) {
1248 online_count++;
1249 }
1250
1251 if (member_count == 1
1252 || n->details->priority > top_priority) {
1253 top_priority = n->details->priority;
1254 }
1255
1256 if (member_count == 1
1257 || n->details->priority < lowest_priority) {
1258 lowest_priority = n->details->priority;
1259 }
1260 }
1261
1262
1263 if (online_count > member_count / 2) {
1264 return 0;
1265 }
1266
1267
1268
1269 if (lowest_priority == top_priority) {
1270 return 0;
1271 }
1272
1273 if (node->details->priority < top_priority) {
1274 return 0;
1275 }
1276
1277 return scheduler->priority_fencing_delay;
1278 }
1279
1280 pcmk_action_t *
1281 pe_fence_op(pcmk_node_t *node, const char *op, bool optional,
1282 const char *reason, bool priority_delay,
1283 pcmk_scheduler_t *scheduler)
1284 {
1285 char *op_key = NULL;
1286 pcmk_action_t *stonith_op = NULL;
1287
1288 if(op == NULL) {
1289 op = scheduler->stonith_action;
1290 }
1291
1292 op_key = crm_strdup_printf("%s-%s-%s",
1293 PCMK_ACTION_STONITH, node->details->uname, op);
1294
1295 stonith_op = lookup_singleton(scheduler, op_key);
1296 if(stonith_op == NULL) {
1297 stonith_op = custom_action(NULL, op_key, PCMK_ACTION_STONITH, node,
1298 TRUE, scheduler);
1299
1300 pcmk__insert_meta(stonith_op, PCMK__META_ON_NODE, node->details->uname);
1301 pcmk__insert_meta(stonith_op, PCMK__META_ON_NODE_UUID,
1302 node->details->id);
1303 pcmk__insert_meta(stonith_op, PCMK__META_STONITH_ACTION, op);
1304
1305 if (pcmk_is_set(scheduler->flags, pcmk_sched_enable_unfencing)) {
1306
1307
1308 GString *digests_all = g_string_sized_new(1024);
1309 GString *digests_secure = g_string_sized_new(1024);
1310
1311 GList *matches = find_unfencing_devices(scheduler->resources, NULL);
1312
1313 for (GList *gIter = matches; gIter != NULL; gIter = gIter->next) {
1314 pcmk_resource_t *match = gIter->data;
1315 const char *agent = g_hash_table_lookup(match->meta,
1316 PCMK_XA_TYPE);
1317 pcmk__op_digest_t *data = NULL;
1318
1319 data = pe__compare_fencing_digest(match, agent, node,
1320 scheduler);
1321 if (data->rc == pcmk__digest_mismatch) {
1322 optional = FALSE;
1323 crm_notice("Unfencing node %s because the definition of "
1324 "%s changed", pcmk__node_name(node), match->id);
1325 if (!pcmk__is_daemon && scheduler->priv != NULL) {
1326 pcmk__output_t *out = scheduler->priv;
1327
1328 out->info(out,
1329 "notice: Unfencing node %s because the "
1330 "definition of %s changed",
1331 pcmk__node_name(node), match->id);
1332 }
1333 }
1334
1335 pcmk__g_strcat(digests_all,
1336 match->id, ":", agent, ":",
1337 data->digest_all_calc, ",", NULL);
1338 pcmk__g_strcat(digests_secure,
1339 match->id, ":", agent, ":",
1340 data->digest_secure_calc, ",", NULL);
1341 }
1342 pcmk__insert_dup(stonith_op->meta, PCMK__META_DIGESTS_ALL,
1343 digests_all->str);
1344 g_string_free(digests_all, TRUE);
1345
1346 pcmk__insert_dup(stonith_op->meta, PCMK__META_DIGESTS_SECURE,
1347 digests_secure->str);
1348 g_string_free(digests_secure, TRUE);
1349
1350 g_list_free(matches);
1351 }
1352
1353 } else {
1354 free(op_key);
1355 }
1356
1357 if (scheduler->priority_fencing_delay > 0
1358
1359
1360
1361
1362
1363 && (priority_delay
1364
1365
1366
1367
1368
1369 || g_hash_table_lookup(stonith_op->meta,
1370 PCMK_OPT_PRIORITY_FENCING_DELAY) != NULL)) {
1371
1372
1373
1374
1375
1376 char *delay_s = pcmk__itoa(node_priority_fencing_delay(node,
1377 scheduler));
1378
1379 g_hash_table_insert(stonith_op->meta,
1380 strdup(PCMK_OPT_PRIORITY_FENCING_DELAY),
1381 delay_s);
1382 }
1383
1384 if(optional == FALSE && pe_can_fence(scheduler, node)) {
1385 pcmk__clear_action_flags(stonith_op, pcmk_action_optional);
1386 pe_action_set_reason(stonith_op, reason, false);
1387
1388 } else if(reason && stonith_op->reason == NULL) {
1389 stonith_op->reason = strdup(reason);
1390 }
1391
1392 return stonith_op;
1393 }
1394
1395 void
1396 pe_free_action(pcmk_action_t *action)
1397 {
1398 if (action == NULL) {
1399 return;
1400 }
1401 g_list_free_full(action->actions_before, free);
1402 g_list_free_full(action->actions_after, free);
1403 if (action->extra) {
1404 g_hash_table_destroy(action->extra);
1405 }
1406 if (action->meta) {
1407 g_hash_table_destroy(action->meta);
1408 }
1409 free(action->cancel_task);
1410 free(action->reason);
1411 free(action->task);
1412 free(action->uuid);
1413 free(action->node);
1414 free(action);
1415 }
1416
1417 enum action_tasks
1418 get_complex_task(const pcmk_resource_t *rsc, const char *name)
1419 {
1420 enum action_tasks task = pcmk_parse_action(name);
1421
1422 if (pcmk__is_primitive(rsc)) {
1423 switch (task) {
1424 case pcmk_action_stopped:
1425 case pcmk_action_started:
1426 case pcmk_action_demoted:
1427 case pcmk_action_promoted:
1428 crm_trace("Folding %s back into its atomic counterpart for %s",
1429 name, rsc->id);
1430 --task;
1431 break;
1432 default:
1433 break;
1434 }
1435 }
1436 return task;
1437 }
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450 pcmk_action_t *
1451 find_first_action(const GList *input, const char *uuid, const char *task,
1452 const pcmk_node_t *on_node)
1453 {
1454 CRM_CHECK(uuid || task, return NULL);
1455
1456 for (const GList *gIter = input; gIter != NULL; gIter = gIter->next) {
1457 pcmk_action_t *action = (pcmk_action_t *) gIter->data;
1458
1459 if (uuid != NULL && !pcmk__str_eq(uuid, action->uuid, pcmk__str_casei)) {
1460 continue;
1461
1462 } else if (task != NULL && !pcmk__str_eq(task, action->task, pcmk__str_casei)) {
1463 continue;
1464
1465 } else if (on_node == NULL) {
1466 return action;
1467
1468 } else if (action->node == NULL) {
1469 continue;
1470
1471 } else if (pcmk__same_node(on_node, action->node)) {
1472 return action;
1473 }
1474 }
1475
1476 return NULL;
1477 }
1478
1479 GList *
1480 find_actions(GList *input, const char *key, const pcmk_node_t *on_node)
1481 {
1482 GList *gIter = input;
1483 GList *result = NULL;
1484
1485 CRM_CHECK(key != NULL, return NULL);
1486
1487 for (; gIter != NULL; gIter = gIter->next) {
1488 pcmk_action_t *action = (pcmk_action_t *) gIter->data;
1489
1490 if (!pcmk__str_eq(key, action->uuid, pcmk__str_casei)) {
1491 continue;
1492
1493 } else if (on_node == NULL) {
1494 crm_trace("Action %s matches (ignoring node)", key);
1495 result = g_list_prepend(result, action);
1496
1497 } else if (action->node == NULL) {
1498 crm_trace("Action %s matches (unallocated, assigning to %s)",
1499 key, pcmk__node_name(on_node));
1500
1501 action->node = pe__copy_node(on_node);
1502 result = g_list_prepend(result, action);
1503
1504 } else if (pcmk__same_node(on_node, action->node)) {
1505 crm_trace("Action %s on %s matches", key, pcmk__node_name(on_node));
1506 result = g_list_prepend(result, action);
1507 }
1508 }
1509
1510 return result;
1511 }
1512
1513 GList *
1514 find_actions_exact(GList *input, const char *key, const pcmk_node_t *on_node)
1515 {
1516 GList *result = NULL;
1517
1518 CRM_CHECK(key != NULL, return NULL);
1519
1520 if (on_node == NULL) {
1521 return NULL;
1522 }
1523
1524 for (GList *gIter = input; gIter != NULL; gIter = gIter->next) {
1525 pcmk_action_t *action = (pcmk_action_t *) gIter->data;
1526
1527 if ((action->node != NULL)
1528 && pcmk__str_eq(key, action->uuid, pcmk__str_casei)
1529 && pcmk__str_eq(on_node->details->id, action->node->details->id,
1530 pcmk__str_casei)) {
1531
1532 crm_trace("Action %s on %s matches", key, pcmk__node_name(on_node));
1533 result = g_list_prepend(result, action);
1534 }
1535 }
1536
1537 return result;
1538 }
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552 GList *
1553 pe__resource_actions(const pcmk_resource_t *rsc, const pcmk_node_t *node,
1554 const char *task, bool require_node)
1555 {
1556 GList *result = NULL;
1557 char *key = pcmk__op_key(rsc->id, task, 0);
1558
1559 if (require_node) {
1560 result = find_actions_exact(rsc->actions, key, node);
1561 } else {
1562 result = find_actions(rsc->actions, key, node);
1563 }
1564 free(key);
1565 return result;
1566 }
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578 char *
1579 pe__action2reason(const pcmk_action_t *action, enum pe_action_flags flag)
1580 {
1581 const char *change = NULL;
1582
1583 switch (flag) {
1584 case pcmk_action_runnable:
1585 change = "unrunnable";
1586 break;
1587 case pcmk_action_migratable:
1588 change = "unmigrateable";
1589 break;
1590 case pcmk_action_optional:
1591 change = "required";
1592 break;
1593 default:
1594
1595 CRM_CHECK(change != NULL, change = "");
1596 break;
1597 }
1598 return crm_strdup_printf("%s%s%s %s", change,
1599 (action->rsc == NULL)? "" : " ",
1600 (action->rsc == NULL)? "" : action->rsc->id,
1601 action->task);
1602 }
1603
1604 void pe_action_set_reason(pcmk_action_t *action, const char *reason,
1605 bool overwrite)
1606 {
1607 if (action->reason != NULL && overwrite) {
1608 pcmk__rsc_trace(action->rsc, "Changing %s reason from '%s' to '%s'",
1609 action->uuid, action->reason,
1610 pcmk__s(reason, "(none)"));
1611 } else if (action->reason == NULL) {
1612 pcmk__rsc_trace(action->rsc, "Set %s reason to '%s'",
1613 action->uuid, pcmk__s(reason, "(none)"));
1614 } else {
1615
1616 return;
1617 }
1618
1619 pcmk__str_update(&action->reason, reason);
1620 }
1621
1622
1623
1624
1625
1626
1627
1628
1629 void
1630 pe__clear_resource_history(pcmk_resource_t *rsc, const pcmk_node_t *node)
1631 {
1632 pcmk__assert((rsc != NULL) && (node != NULL));
1633
1634 custom_action(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_LRM_DELETE, 0),
1635 PCMK_ACTION_LRM_DELETE, node, FALSE, rsc->cluster);
1636 }
1637
1638 #define sort_return(an_int, why) do { \
1639 free(a_uuid); \
1640 free(b_uuid); \
1641 crm_trace("%s (%d) %c %s (%d) : %s", \
1642 a_xml_id, a_call_id, an_int>0?'>':an_int<0?'<':'=', \
1643 b_xml_id, b_call_id, why); \
1644 return an_int; \
1645 } while(0)
1646
1647 int
1648 pe__is_newer_op(const xmlNode *xml_a, const xmlNode *xml_b,
1649 bool same_node_default)
1650 {
1651 int a_call_id = -1;
1652 int b_call_id = -1;
1653
1654 char *a_uuid = NULL;
1655 char *b_uuid = NULL;
1656
1657 const char *a_xml_id = crm_element_value(xml_a, PCMK_XA_ID);
1658 const char *b_xml_id = crm_element_value(xml_b, PCMK_XA_ID);
1659
1660 const char *a_node = crm_element_value(xml_a, PCMK__META_ON_NODE);
1661 const char *b_node = crm_element_value(xml_b, PCMK__META_ON_NODE);
1662 bool same_node = true;
1663
1664
1665
1666
1667
1668
1669
1670
1671 if (a_node == NULL || b_node == NULL) {
1672 same_node = same_node_default;
1673
1674 } else {
1675 same_node = pcmk__str_eq(a_node, b_node, pcmk__str_casei);
1676 }
1677
1678 if (same_node && pcmk__str_eq(a_xml_id, b_xml_id, pcmk__str_none)) {
1679
1680
1681
1682
1683
1684 pcmk__config_err("Duplicate " PCMK__XE_LRM_RSC_OP " entries named %s",
1685 a_xml_id);
1686 sort_return(0, "duplicate");
1687 }
1688
1689 crm_element_value_int(xml_a, PCMK__XA_CALL_ID, &a_call_id);
1690 crm_element_value_int(xml_b, PCMK__XA_CALL_ID, &b_call_id);
1691
1692 if (a_call_id == -1 && b_call_id == -1) {
1693
1694
1695
1696 sort_return(0, "pending");
1697
1698 } else if (same_node && a_call_id >= 0 && a_call_id < b_call_id) {
1699 sort_return(-1, "call id");
1700
1701 } else if (same_node && b_call_id >= 0 && a_call_id > b_call_id) {
1702 sort_return(1, "call id");
1703
1704 } else if (a_call_id >= 0 && b_call_id >= 0
1705 && (!same_node || a_call_id == b_call_id)) {
1706
1707
1708
1709 time_t last_a = -1;
1710 time_t last_b = -1;
1711
1712 crm_element_value_epoch(xml_a, PCMK_XA_LAST_RC_CHANGE, &last_a);
1713 crm_element_value_epoch(xml_b, PCMK_XA_LAST_RC_CHANGE, &last_b);
1714
1715 crm_trace("rc-change: %lld vs %lld",
1716 (long long) last_a, (long long) last_b);
1717 if (last_a >= 0 && last_a < last_b) {
1718 sort_return(-1, "rc-change");
1719
1720 } else if (last_b >= 0 && last_a > last_b) {
1721 sort_return(1, "rc-change");
1722 }
1723 sort_return(0, "rc-change");
1724
1725 } else {
1726
1727
1728
1729
1730
1731 int a_id = -1;
1732 int b_id = -1;
1733
1734 const char *a_magic = crm_element_value(xml_a,
1735 PCMK__XA_TRANSITION_MAGIC);
1736 const char *b_magic = crm_element_value(xml_b,
1737 PCMK__XA_TRANSITION_MAGIC);
1738
1739 CRM_CHECK(a_magic != NULL && b_magic != NULL, sort_return(0, "No magic"));
1740 if (!decode_transition_magic(a_magic, &a_uuid, &a_id, NULL, NULL, NULL,
1741 NULL)) {
1742 sort_return(0, "bad magic a");
1743 }
1744 if (!decode_transition_magic(b_magic, &b_uuid, &b_id, NULL, NULL, NULL,
1745 NULL)) {
1746 sort_return(0, "bad magic b");
1747 }
1748
1749
1750
1751
1752
1753
1754 if (!pcmk__str_eq(a_uuid, b_uuid, pcmk__str_casei) || a_id == b_id) {
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764 if (b_call_id == -1) {
1765 sort_return(-1, "transition + call");
1766
1767 } else if (a_call_id == -1) {
1768 sort_return(1, "transition + call");
1769 }
1770
1771 } else if ((a_id >= 0 && a_id < b_id) || b_id == -1) {
1772 sort_return(-1, "transition");
1773
1774 } else if ((b_id >= 0 && a_id > b_id) || a_id == -1) {
1775 sort_return(1, "transition");
1776 }
1777 }
1778
1779
1780 CRM_CHECK(FALSE, sort_return(0, "default"));
1781 }
1782
1783 gint
1784 sort_op_by_callid(gconstpointer a, gconstpointer b)
1785 {
1786 const xmlNode *xml_a = a;
1787 const xmlNode *xml_b = b;
1788
1789 return pe__is_newer_op(xml_a, xml_b, true);
1790 }
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803 pcmk_action_t *
1804 pe__new_rsc_pseudo_action(pcmk_resource_t *rsc, const char *task, bool optional,
1805 bool runnable)
1806 {
1807 pcmk_action_t *action = NULL;
1808
1809 pcmk__assert((rsc != NULL) && (task != NULL));
1810
1811 action = custom_action(rsc, pcmk__op_key(rsc->id, task, 0), task, NULL,
1812 optional, rsc->cluster);
1813 pcmk__set_action_flags(action, pcmk_action_pseudo);
1814 if (runnable) {
1815 pcmk__set_action_flags(action, pcmk_action_runnable);
1816 }
1817 return action;
1818 }
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829 void
1830 pe__add_action_expected_result(pcmk_action_t *action, int expected_result)
1831 {
1832 pcmk__assert((action != NULL) && (action->meta != NULL));
1833
1834 g_hash_table_insert(action->meta, pcmk__str_copy(PCMK__META_OP_TARGET_RC),
1835 pcmk__itoa(expected_result));
1836 }