pacemaker 2.1.8-2.1.8
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
pe_actions.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2024 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
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>
20#include "pe_status_private.h"
21
22static void unpack_operation(pcmk_action_t *action, const xmlNode *xml_obj,
23 guint interval_ms);
24
25static void
27{
28 if (scheduler->singletons == NULL) {
30 }
31 g_hash_table_insert(scheduler->singletons, action->uuid, action);
32}
33
34static pcmk_action_t *
35lookup_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
54static pcmk_action_t *
55find_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 /* When rsc is NULL, it would be quicker to check scheduler->singletons,
62 * but checking all scheduler->actions takes the node into account.
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
86static xmlNode *
87find_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 // @TODO This does not consider meta-attributes, rules, defaults, etc.
100 if (!include_disabled
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
131xmlNode *
132pcmk__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 // Try requested action first
138 action_config = find_exact_action_config(rsc, action_name, interval_ms,
139 include_disabled);
140
141 // For migrate_to and migrate_from actions, retry with "migrate"
142 // @TODO This should be either documented or deprecated
143 if ((action_config == NULL)
146 action_config = find_exact_action_config(rsc, "migrate", 0,
147 include_disabled);
148 }
149
150 return action_config;
151}
152
168static pcmk_action_t *
169new_action(char *key, const char *task, pcmk_resource_t *rsc,
170 const pcmk_node_t *node, bool optional, pcmk_scheduler_t *scheduler)
171{
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 // Resource history deletion for a node can be done on the DC
185 }
186
188 if (optional) {
190 } else {
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 /* If the given key is for one of the many notification pseudo-actions
204 * (pre_notify_promote, etc.), the actual action name is "notify"
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
240GHashTable *
241pcmk__unpack_action_rsc_params(const xmlNode *action_xml,
242 GHashTable *node_attrs,
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
256 &rule_data, params, NULL,
257 FALSE, scheduler);
258 return params;
259}
260
268static void
269update_action_optional(pcmk_action_t *action, gboolean optional)
270{
271 // Force a non-recurring action to be optional if its resource is unmanaged
272 if ((action->rsc != NULL) && (action->node != NULL)
274 && !pcmk_is_set(action->rsc->flags, pcmk_rsc_managed)
275 && (g_hash_table_lookup(action->meta, PCMK_META_INTERVAL) == NULL)) {
277 "%s on %s is optional (%s is unmanaged)",
278 action->uuid, pcmk__node_name(action->node),
279 action->rsc->id);
281 // We shouldn't clear runnable here because ... something
282
283 // Otherwise require the action if requested
284 } else if (!optional) {
286 }
287}
288
289static enum pe_quorum_policy
290effective_quorum_policy(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
291{
293
295 policy = pcmk_no_quorum_ignore;
296
298 switch (rsc->role) {
301 if (rsc->next_role > 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
324static void
325update_resource_action_runnable(pcmk_action_t *action,
327{
329 return;
330 }
331
332 if (action->node == NULL) {
333 pcmk__rsc_trace(action->rsc, "%s is unrunnable (unallocated)",
334 action->uuid);
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)) {
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) {
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 /* An action that requires nothing usually does not require any
362 * fencing in order to be runnable. However, there is an exception:
363 * such an action cannot be completed if it is on a guest node whose
364 * host is unclean and cannot be fenced.
365 */
367 "%s on %s is unrunnable "
368 "(node's host cannot be fenced)",
369 action->uuid, pcmk__node_name(action->node));
371 } else {
373 "%s on %s does not require fencing or quorum",
374 action->uuid, pcmk__node_name(action->node));
376 }
377
378 } else {
379 switch (effective_quorum_policy(action->rsc, scheduler)) {
382 "%s on %s is unrunnable (no quorum)",
383 action->uuid, pcmk__node_name(action->node));
385 pe_action_set_reason(action, "no quorum", true);
386 break;
387
389 if (!action->rsc->fns->active(action->rsc, TRUE)
390 || (action->rsc->next_role > action->rsc->role)) {
392 "%s on %s is unrunnable (no quorum)",
393 action->uuid,
394 pcmk__node_name(action->node));
396 pe_action_set_reason(action, "quorum freeze", true);
397 }
398 break;
399
400 default:
401 //pe_action_set_reason(action, NULL, TRUE);
403 break;
404 }
405 }
406}
407
415static void
416update_resource_flags_for_action(pcmk_resource_t *rsc,
417 const pcmk_action_t *action)
418{
419 /* @COMPAT pcmk_rsc_starting and pcmk_rsc_stopping are deprecated and unused
420 * within Pacemaker, and will eventually be removed
421 */
422 if (pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_casei)) {
424
425 } else if (pcmk__str_eq(action->task, PCMK_ACTION_START, pcmk__str_casei)) {
428 } else {
430 }
431 }
432}
433
434static bool
435valid_stop_on_fail(const char *value)
436{
437 return !pcmk__strcase_any_of(value,
439 PCMK_VALUE_STOP, NULL);
440}
441
451static void
452validate_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 // Stop actions can only use certain on-fail values
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 /* Demote actions default on-fail to the on-fail value for the first
473 * recurring monitor for the promoted role (if any).
474 */
475 if (pcmk__str_eq(action_name, PCMK_ACTION_DEMOTE, pcmk__str_none)
476 && (value == NULL)) {
477
478 /* @TODO This does not consider promote options set in a meta-attribute
479 * block (which may have rules that need to be evaluated) rather than
480 * XML properties.
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 /* We only care about explicit on-fail (if promote uses default, so
490 * can demote)
491 */
492 promote_on_fail = crm_element_value(operation, PCMK_META_ON_FAIL);
493 if (promote_on_fail == NULL) {
494 continue;
495 }
496
497 // We only care about recurring monitors for the promoted role
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)
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 // We only care about enabled monitors
513 &enabled) == pcmk_rc_ok) && !enabled) {
514 continue;
515 }
516
517 /* Demote actions can't default to
518 * PCMK_META_ON_FAIL=PCMK_VALUE_DEMOTE
519 */
520 if (pcmk__str_eq(promote_on_fail, PCMK_VALUE_DEMOTE,
522 continue;
523 }
524
525 // Use value from first applicable promote action found
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
535 return;
536 }
537
538 // PCMK_META_ON_FAIL=PCMK_VALUE_DEMOTE is allowed only for certain actions
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)
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
560static int
561unpack_timeout(const char *value)
562{
563 long long timeout_ms = crm_get_msec(value);
564
565 if (timeout_ms <= 0) {
567 }
568 return (int) QB_MIN(timeout_ms, INT_MAX);
569}
570
571// true if value contains valid, non-NULL interval origin for recurring op
572static bool
573unpack_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 // Ignore unspecified values and non-recurring operations
582 if ((value == NULL) || (interval_ms == 0) || (now == NULL)) {
583 return false;
584 }
585
586 // Parse interval origin from text
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 // Get seconds since origin (negative if origin is in the future)
597 crm_time_free(origin);
598
599 // Calculate seconds from closest interval to now
600 result = result % interval_sec;
601
602 // Calculate seconds remaining until next interval
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; // milliseconds
609 }
610 return true;
611}
612
613static int
614unpack_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
645static xmlNode *
646most_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,
659
660 // We only care about enabled recurring monitors
661 if (!pcmk__str_eq(crm_element_value(operation, PCMK_XA_NAME),
663 continue;
664 }
665
666 pcmk_parse_interval_spec(interval_spec, &interval_ms);
667 if (interval_ms == 0U) {
668 continue;
669 }
670
671 // @TODO This does not consider meta-attributes, rules, defaults, etc.
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
701GHashTable *
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 = {
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 /* @COMPAT Support for node attribute expressions in operation
723 * meta-attributes (whether in the operation configuration or operation
724 * defaults) is deprecated. When we can break behavioral backward
725 * compatibility, drop this line.
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 // Cluster-wide <op_defaults> <meta_attributes>
739 PCMK_XE_META_ATTRIBUTES, &rule_data, meta, NULL,
740 FALSE, rsc->cluster);
741
742 // Derive default timeout for probes from recurring monitor timeouts
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 /* @TODO This does not consider timeouts set in
748 * PCMK_XE_META_ATTRIBUTES blocks (which may also have rules that
749 * need to be evaluated).
750 */
751 timeout_spec = crm_element_value(min_interval_mon,
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 // <op> <meta_attributes> take precedence over defaults
766 &rule_data, meta, NULL, TRUE, rsc->cluster);
767
768 /* Anything set as an <op> XML property has highest precedence.
769 * This ensures we use the name and interval from the <op> tag.
770 * (See below for the only exception, fence device start/probe timeout.)
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 // Normalize interval to milliseconds
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 /* Timeout order of precedence (highest to lowest):
790 * 1. pcmk_monitor_timeout resource parameter (only for starts and probes
791 * when rsc has pcmk_ra_cap_fence_params; this gets used for recurring
792 * monitors via the executor instead)
793 * 2. timeout configured in <op> (with <op timeout> taking precedence over
794 * <op> <meta_attributes>)
795 * 3. timeout configured in <op_defaults> <meta_attributes>
796 * 4. PCMK_DEFAULT_ACTION_TIMEOUT_MS
797 */
798
799 // Check for pcmk_monitor_timeout
800 if (pcmk_is_set(pcmk_get_ra_caps(rsc_rule_data.standard),
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 // Normalize timeout to positive milliseconds
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 // Ensure on-fail has a valid value
823 validate_on_fail(rsc, action_name, action_config, meta);
824
825 // Normalize PCMK_META_START_DELAY
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
852pcmk__action_requires(const pcmk_resource_t *rsc, const char *action_name)
853{
854 const char *value = NULL;
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
890pcmk__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;
896
897 // There's no enum value for unknown or invalid, so assert
898 CRM_ASSERT((rsc != NULL) && (action_name != NULL));
899
900 if (value == NULL) {
901 // Use default
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)) {
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)) {
922 desc = "node standby";
923
924 } else if (pcmk__strcase_any_of(value,
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,
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 {
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 /* Remote node connections are handled specially. Failures that result
964 * in dropping an active connection must result in fencing. The only
965 * failures that don't are probes and starts. The user can explicitly set
966 * PCMK_META_ON_FAIL=PCMK_VALUE_FENCE to fence after start failures.
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; // Force default for unmanaged connections
975 }
976 }
977
978 if (desc != NULL) {
979 // Explicit value used, default not needed
980
981 } else if (rsc->container != NULL) {
983 desc = "restart container (and possibly migrate) (default)";
984
985 } else if (needs_remote_reset) {
987 if (pcmk_is_set(rsc->cluster->flags,
989 desc = "fence remote node (default)";
990 } else {
991 desc = "recover remote node connection (default)";
992 }
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)) {
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
1030enum rsc_role_e
1031pcmk__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 // Set default for role after failure specially in certain circumstances
1038 switch (on_fail) {
1039 case pcmk_on_fail_stop:
1040 role = pcmk_role_stopped;
1041 break;
1042
1044 if (rsc->remote_reconnect_ms != 0) {
1045 role = pcmk_role_stopped;
1046 }
1047 break;
1048
1049 default:
1050 break;
1051 }
1052
1053 // @COMPAT Check for explicitly configured role (deprecated)
1054 value = g_hash_table_lookup(meta, PCMK__META_ROLE_AFTER_FAILURE);
1055 if (value != NULL) {
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 "
1064 value);
1065 }
1066 }
1067 }
1068
1069 if (role == pcmk_role_unknown) {
1070 // Use default
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
1094static void
1095unpack_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
1129custom_action(pcmk_resource_t *rsc, char *key, const char *task,
1130 const pcmk_node_t *on_node, gboolean optional,
1132{
1133 pcmk_action_t *action = NULL;
1134
1135 CRM_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 /* An action can be initially created with a NULL node, and later have
1148 * the node added via find_existing_action() (above) -> find_actions().
1149 * That is why the extra parameters are unpacked here rather than in
1150 * new_action().
1151 */
1152 if ((action->node != NULL) && (action->op_entry != NULL)
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);
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
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);
1184 }
1185 return op;
1186}
1187
1188static GList *
1189find_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,
1206 matches = g_list_prepend(matches, candidate);
1207 }
1208 }
1209 return matches;
1210}
1211
1212static int
1213node_priority_fencing_delay(const pcmk_node_t *node,
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 // PCMK_OPT_PRIORITY_FENCING_DELAY is disabled
1224 return 0;
1225 }
1226
1227 /* No need to request a delay if the fencing target is not a normal cluster
1228 * member, for example if it's a remote node or a guest node. */
1229 if (node->details->type != pcmk_node_variant_cluster) {
1230 return 0;
1231 }
1232
1233 // No need to request a delay if the fencing target is in our partition
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
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 // No need to delay if we have more than half of the cluster members
1263 if (online_count > member_count / 2) {
1264 return 0;
1265 }
1266
1267 /* All the nodes have equal priority.
1268 * Any configured corresponding `pcmk_delay_base/max` will be applied. */
1269 if (lowest_priority == top_priority) {
1270 return 0;
1271 }
1272
1273 if (node->details->priority < top_priority) {
1274 return 0;
1275 }
1276
1278}
1279
1281pe_fence_op(pcmk_node_t *node, const char *op, bool optional,
1282 const char *reason, bool priority_delay,
1284{
1285 char *op_key = NULL;
1286 pcmk_action_t *stonith_op = NULL;
1287
1288 if(op == NULL) {
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
1302 node->details->id);
1304
1306 /* Extra work to detect device changes
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) {
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 }
1343 digests_all->str);
1344 g_string_free(digests_all, TRUE);
1345
1347 digests_secure->str);
1348 g_string_free(digests_secure, TRUE);
1349 }
1350
1351 } else {
1352 free(op_key);
1353 }
1354
1356
1357 /* It's a suitable case where PCMK_OPT_PRIORITY_FENCING_DELAY
1358 * applies. At least add PCMK_OPT_PRIORITY_FENCING_DELAY field as
1359 * an indicator.
1360 */
1361 && (priority_delay
1362
1363 /* The priority delay needs to be recalculated if this function has
1364 * been called by schedule_fencing_and_shutdowns() after node
1365 * priority has already been calculated by native_add_running().
1366 */
1367 || g_hash_table_lookup(stonith_op->meta,
1369
1370 /* Add PCMK_OPT_PRIORITY_FENCING_DELAY to the fencing op even if
1371 * it's 0 for the targeting node. So that it takes precedence over
1372 * any possible `pcmk_delay_base/max`.
1373 */
1374 char *delay_s = pcmk__itoa(node_priority_fencing_delay(node,
1375 scheduler));
1376
1377 g_hash_table_insert(stonith_op->meta,
1379 delay_s);
1380 }
1381
1382 if(optional == FALSE && pe_can_fence(scheduler, node)) {
1384 pe_action_set_reason(stonith_op, reason, false);
1385
1386 } else if(reason && stonith_op->reason == NULL) {
1387 stonith_op->reason = strdup(reason);
1388 }
1389
1390 return stonith_op;
1391}
1392
1393void
1395{
1396 if (action == NULL) {
1397 return;
1398 }
1399 g_list_free_full(action->actions_before, free);
1400 g_list_free_full(action->actions_after, free);
1401 if (action->extra) {
1402 g_hash_table_destroy(action->extra);
1403 }
1404 if (action->meta) {
1405 g_hash_table_destroy(action->meta);
1406 }
1407 free(action->cancel_task);
1408 free(action->reason);
1409 free(action->task);
1410 free(action->uuid);
1411 free(action->node);
1412 free(action);
1413}
1414
1415enum action_tasks
1416get_complex_task(const pcmk_resource_t *rsc, const char *name)
1417{
1418 enum action_tasks task = pcmk_parse_action(name);
1419
1420 if (pcmk__is_primitive(rsc)) {
1421 switch (task) {
1426 crm_trace("Folding %s back into its atomic counterpart for %s",
1427 name, rsc->id);
1428 --task;
1429 break;
1430 default:
1431 break;
1432 }
1433 }
1434 return task;
1435}
1436
1449find_first_action(const GList *input, const char *uuid, const char *task,
1450 const pcmk_node_t *on_node)
1451{
1452 CRM_CHECK(uuid || task, return NULL);
1453
1454 for (const GList *gIter = input; gIter != NULL; gIter = gIter->next) {
1455 pcmk_action_t *action = (pcmk_action_t *) gIter->data;
1456
1457 if (uuid != NULL && !pcmk__str_eq(uuid, action->uuid, pcmk__str_casei)) {
1458 continue;
1459
1460 } else if (task != NULL && !pcmk__str_eq(task, action->task, pcmk__str_casei)) {
1461 continue;
1462
1463 } else if (on_node == NULL) {
1464 return action;
1465
1466 } else if (action->node == NULL) {
1467 continue;
1468
1469 } else if (pcmk__same_node(on_node, action->node)) {
1470 return action;
1471 }
1472 }
1473
1474 return NULL;
1475}
1476
1477GList *
1478find_actions(GList *input, const char *key, const pcmk_node_t *on_node)
1479{
1480 GList *gIter = input;
1481 GList *result = NULL;
1482
1483 CRM_CHECK(key != NULL, return NULL);
1484
1485 for (; gIter != NULL; gIter = gIter->next) {
1486 pcmk_action_t *action = (pcmk_action_t *) gIter->data;
1487
1488 if (!pcmk__str_eq(key, action->uuid, pcmk__str_casei)) {
1489 continue;
1490
1491 } else if (on_node == NULL) {
1492 crm_trace("Action %s matches (ignoring node)", key);
1493 result = g_list_prepend(result, action);
1494
1495 } else if (action->node == NULL) {
1496 crm_trace("Action %s matches (unallocated, assigning to %s)",
1497 key, pcmk__node_name(on_node));
1498
1499 action->node = pe__copy_node(on_node);
1500 result = g_list_prepend(result, action);
1501
1502 } else if (pcmk__same_node(on_node, action->node)) {
1503 crm_trace("Action %s on %s matches", key, pcmk__node_name(on_node));
1504 result = g_list_prepend(result, action);
1505 }
1506 }
1507
1508 return result;
1509}
1510
1511GList *
1512find_actions_exact(GList *input, const char *key, const pcmk_node_t *on_node)
1513{
1514 GList *result = NULL;
1515
1516 CRM_CHECK(key != NULL, return NULL);
1517
1518 if (on_node == NULL) {
1519 return NULL;
1520 }
1521
1522 for (GList *gIter = input; gIter != NULL; gIter = gIter->next) {
1523 pcmk_action_t *action = (pcmk_action_t *) gIter->data;
1524
1525 if ((action->node != NULL)
1526 && pcmk__str_eq(key, action->uuid, pcmk__str_casei)
1527 && pcmk__str_eq(on_node->details->id, action->node->details->id,
1528 pcmk__str_casei)) {
1529
1530 crm_trace("Action %s on %s matches", key, pcmk__node_name(on_node));
1531 result = g_list_prepend(result, action);
1532 }
1533 }
1534
1535 return result;
1536}
1537
1550GList *
1552 const char *task, bool require_node)
1553{
1554 GList *result = NULL;
1555 char *key = pcmk__op_key(rsc->id, task, 0);
1556
1557 if (require_node) {
1558 result = find_actions_exact(rsc->actions, key, node);
1559 } else {
1560 result = find_actions(rsc->actions, key, node);
1561 }
1562 free(key);
1563 return result;
1564}
1565
1576char *
1578{
1579 const char *change = NULL;
1580
1581 switch (flag) {
1583 change = "unrunnable";
1584 break;
1586 change = "unmigrateable";
1587 break;
1589 change = "required";
1590 break;
1591 default:
1592 // Bug: caller passed unsupported flag
1593 CRM_CHECK(change != NULL, change = "");
1594 break;
1595 }
1596 return crm_strdup_printf("%s%s%s %s", change,
1597 (action->rsc == NULL)? "" : " ",
1598 (action->rsc == NULL)? "" : action->rsc->id,
1599 action->task);
1600}
1601
1603 bool overwrite)
1604{
1605 if (action->reason != NULL && overwrite) {
1606 pcmk__rsc_trace(action->rsc, "Changing %s reason from '%s' to '%s'",
1607 action->uuid, action->reason,
1608 pcmk__s(reason, "(none)"));
1609 } else if (action->reason == NULL) {
1610 pcmk__rsc_trace(action->rsc, "Set %s reason to '%s'",
1611 action->uuid, pcmk__s(reason, "(none)"));
1612 } else {
1613 // crm_assert(action->reason != NULL && !overwrite);
1614 return;
1615 }
1616
1617 pcmk__str_update(&action->reason, reason);
1618}
1619
1627void
1629{
1630 CRM_ASSERT((rsc != NULL) && (node != NULL));
1631
1633 PCMK_ACTION_LRM_DELETE, node, FALSE, rsc->cluster);
1634}
1635
1636#define sort_return(an_int, why) do { \
1637 free(a_uuid); \
1638 free(b_uuid); \
1639 crm_trace("%s (%d) %c %s (%d) : %s", \
1640 a_xml_id, a_call_id, an_int>0?'>':an_int<0?'<':'=', \
1641 b_xml_id, b_call_id, why); \
1642 return an_int; \
1643 } while(0)
1644
1645int
1646pe__is_newer_op(const xmlNode *xml_a, const xmlNode *xml_b,
1647 bool same_node_default)
1648{
1649 int a_call_id = -1;
1650 int b_call_id = -1;
1651
1652 char *a_uuid = NULL;
1653 char *b_uuid = NULL;
1654
1655 const char *a_xml_id = crm_element_value(xml_a, PCMK_XA_ID);
1656 const char *b_xml_id = crm_element_value(xml_b, PCMK_XA_ID);
1657
1658 const char *a_node = crm_element_value(xml_a, PCMK__META_ON_NODE);
1659 const char *b_node = crm_element_value(xml_b, PCMK__META_ON_NODE);
1660 bool same_node = true;
1661
1662 /* @COMPAT The on_node attribute was added to last_failure as of 1.1.13 (via
1663 * 8b3ca1c) and the other entries as of 1.1.12 (via 0b07b5c).
1664 *
1665 * In case that any of the PCMK__XE_LRM_RSC_OP entries doesn't have on_node
1666 * attribute, we need to explicitly tell whether the two operations are on
1667 * the same node.
1668 */
1669 if (a_node == NULL || b_node == NULL) {
1670 same_node = same_node_default;
1671
1672 } else {
1673 same_node = pcmk__str_eq(a_node, b_node, pcmk__str_casei);
1674 }
1675
1676 if (same_node && pcmk__str_eq(a_xml_id, b_xml_id, pcmk__str_none)) {
1677 /* We have duplicate PCMK__XE_LRM_RSC_OP entries in the status
1678 * section which is unlikely to be a good thing
1679 * - we can handle it easily enough, but we need to get
1680 * to the bottom of why it's happening.
1681 */
1682 pcmk__config_err("Duplicate " PCMK__XE_LRM_RSC_OP " entries named %s",
1683 a_xml_id);
1684 sort_return(0, "duplicate");
1685 }
1686
1687 crm_element_value_int(xml_a, PCMK__XA_CALL_ID, &a_call_id);
1688 crm_element_value_int(xml_b, PCMK__XA_CALL_ID, &b_call_id);
1689
1690 if (a_call_id == -1 && b_call_id == -1) {
1691 /* both are pending ops so it doesn't matter since
1692 * stops are never pending
1693 */
1694 sort_return(0, "pending");
1695
1696 } else if (same_node && a_call_id >= 0 && a_call_id < b_call_id) {
1697 sort_return(-1, "call id");
1698
1699 } else if (same_node && b_call_id >= 0 && a_call_id > b_call_id) {
1700 sort_return(1, "call id");
1701
1702 } else if (a_call_id >= 0 && b_call_id >= 0
1703 && (!same_node || a_call_id == b_call_id)) {
1704 /* The op and last_failed_op are the same. Order on
1705 * PCMK_XA_LAST_RC_CHANGE.
1706 */
1707 time_t last_a = -1;
1708 time_t last_b = -1;
1709
1712
1713 crm_trace("rc-change: %lld vs %lld",
1714 (long long) last_a, (long long) last_b);
1715 if (last_a >= 0 && last_a < last_b) {
1716 sort_return(-1, "rc-change");
1717
1718 } else if (last_b >= 0 && last_a > last_b) {
1719 sort_return(1, "rc-change");
1720 }
1721 sort_return(0, "rc-change");
1722
1723 } else {
1724 /* One of the inputs is a pending operation.
1725 * Attempt to use PCMK__XA_TRANSITION_MAGIC to determine its age relative
1726 * to the other.
1727 */
1728
1729 int a_id = -1;
1730 int b_id = -1;
1731
1732 const char *a_magic = crm_element_value(xml_a,
1734 const char *b_magic = crm_element_value(xml_b,
1736
1737 CRM_CHECK(a_magic != NULL && b_magic != NULL, sort_return(0, "No magic"));
1738 if (!decode_transition_magic(a_magic, &a_uuid, &a_id, NULL, NULL, NULL,
1739 NULL)) {
1740 sort_return(0, "bad magic a");
1741 }
1742 if (!decode_transition_magic(b_magic, &b_uuid, &b_id, NULL, NULL, NULL,
1743 NULL)) {
1744 sort_return(0, "bad magic b");
1745 }
1746 /* try to determine the relative age of the operation...
1747 * some pending operations (e.g. a start) may have been superseded
1748 * by a subsequent stop
1749 *
1750 * [a|b]_id == -1 means it's a shutdown operation and _always_ comes last
1751 */
1752 if (!pcmk__str_eq(a_uuid, b_uuid, pcmk__str_casei) || a_id == b_id) {
1753 /*
1754 * some of the logic in here may be redundant...
1755 *
1756 * if the UUID from the TE doesn't match then one better
1757 * be a pending operation.
1758 * pending operations don't survive between elections and joins
1759 * because we query the LRM directly
1760 */
1761
1762 if (b_call_id == -1) {
1763 sort_return(-1, "transition + call");
1764
1765 } else if (a_call_id == -1) {
1766 sort_return(1, "transition + call");
1767 }
1768
1769 } else if ((a_id >= 0 && a_id < b_id) || b_id == -1) {
1770 sort_return(-1, "transition");
1771
1772 } else if ((b_id >= 0 && a_id > b_id) || a_id == -1) {
1773 sort_return(1, "transition");
1774 }
1775 }
1776
1777 /* we should never end up here */
1778 CRM_CHECK(FALSE, sort_return(0, "default"));
1779}
1780
1781gint
1782sort_op_by_callid(gconstpointer a, gconstpointer b)
1783{
1784 const xmlNode *xml_a = a;
1785 const xmlNode *xml_b = b;
1786
1787 return pe__is_newer_op(xml_a, xml_b, true);
1788}
1789
1802pe__new_rsc_pseudo_action(pcmk_resource_t *rsc, const char *task, bool optional,
1803 bool runnable)
1804{
1805 pcmk_action_t *action = NULL;
1806
1807 CRM_ASSERT((rsc != NULL) && (task != NULL));
1808
1809 action = custom_action(rsc, pcmk__op_key(rsc->id, task, 0), task, NULL,
1810 optional, rsc->cluster);
1812 if (runnable) {
1814 }
1815 return action;
1816}
1817
1827void
1829{
1830 CRM_ASSERT((action != NULL) && (action->meta != NULL));
1831
1832 g_hash_table_insert(action->meta, pcmk__str_copy(PCMK__META_OP_TARGET_RC),
1833 pcmk__itoa(expected_result));
1834}
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition actions.c:250
#define PCMK_ACTION_STOP
Definition actions.h:75
bool pcmk_is_probe(const char *task, guint interval)
Check whether an action name and interval represent a probe.
Definition probes.c:30
#define PCMK_ACTION_PROMOTE
Definition actions.h:66
#define PCMK_ACTION_LRM_DELETE
Definition actions.h:53
#define PCMK_ACTION_START
Definition actions.h:72
action_fail_response
Definition actions.h:130
@ pcmk_on_fail_ban
Definition actions.h:150
@ pcmk_on_fail_fence_node
Definition actions.h:162
@ pcmk_on_fail_ignore
Definition actions.h:144
@ pcmk_on_fail_restart_container
Definition actions.h:167
@ pcmk_on_fail_demote
Definition actions.h:178
@ pcmk_on_fail_standby_node
Definition actions.h:159
@ pcmk_on_fail_block
Definition actions.h:153
@ pcmk_on_fail_reset_remote
Definition actions.h:175
@ pcmk_on_fail_stop
Definition actions.h:156
@ pcmk_on_fail_restart
Definition actions.h:147
#define PCMK_ACTION_MIGRATE_FROM
Definition actions.h:58
pe_action_flags
Definition actions.h:199
@ pcmk_action_runnable
Definition actions.h:207
@ pcmk_action_migratable
Definition actions.h:219
@ pcmk_action_pseudo
Definition actions.h:204
@ pcmk_action_attrs_evaluated
Definition actions.h:216
@ pcmk_action_optional
Definition actions.h:210
@ pcmk_action_on_dc
Definition actions.h:245
#define PCMK_ACTION_MIGRATE_TO
Definition actions.h:59
#define PCMK_DEFAULT_ACTION_TIMEOUT_MS
Default timeout (in milliseconds) for non-metadata actions.
Definition actions.h:38
action_tasks
Definition actions.h:83
@ pcmk_action_stopped
Definition actions.h:90
@ pcmk_action_started
Definition actions.h:93
@ pcmk_action_demoted
Definition actions.h:102
@ pcmk_action_promoted
Definition actions.h:99
#define PCMK_ACTION_MONITOR
Definition actions.h:60
enum action_tasks pcmk_parse_action(const char *action_name)
Parse an action type from an action name.
Definition actions.c:92
#define PCMK_ACTION_STONITH
Definition actions.h:74
#define PCMK_ACTION_DEMOTE
Definition actions.h:49
gboolean decode_transition_magic(const char *magic, char **uuid, int *transition_id, int *action_id, int *op_status, int *op_rc, int *target_rc)
Parse a transition magic string into its constituent parts.
Definition actions.c:363
#define PCMK_ACTION_NOTIFY
Definition actions.h:62
#define pcmk__set_action_flags(action, flags_to_set)
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition actions.c:196
#define pcmk__clear_action_flags(action, flags_to_clear)
uint32_t pcmk_get_ra_caps(const char *standard)
Get capabilities of a resource agent standard.
Definition agents.c:31
@ pcmk_ra_cap_fence_params
Definition agents.h:65
#define PCMK_STONITH_PROVIDES
Definition agents.h:48
const char * name
Definition cib.c:26
int pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
Definition nvpair.c:909
bool pcmk__is_daemon
Definition logging.c:47
#define pcmk__assert_alloc(nmemb, size)
Definition internal.h:297
char int pcmk_parse_interval_spec(const char *input, guint *result_ms)
Parse milliseconds from a Pacemaker interval specification.
Definition strings.c:451
long long crm_get_msec(const char *input)
Parse a time+units string and return milliseconds equivalent.
Definition strings.c:356
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:98
GHashTable * pe_rsc_params(pcmk_resource_t *rsc, const pcmk_node_t *node, pcmk_scheduler_t *scheduler)
Get a table of resource parameters.
Definition complex.c:484
char data[0]
Definition cpg.c:10
uint32_t id
Definition cpg.c:0
A dumping ground.
@ pcmk__digest_mismatch
void crm_time_free(crm_time_t *dt)
Definition iso8601.c:150
long long crm_time_get_seconds(const crm_time_t *dt)
Definition iso8601.c:316
crm_time_t * crm_time_new(const char *string)
Definition iso8601.c:112
struct crm_time_s crm_time_t
Definition iso8601.h:32
const char * pcmk__readable_interval(guint interval_ms)
Definition iso8601.c:2134
#define crm_info(fmt, args...)
Definition logging.h:397
#define do_crm_log(level, fmt, args...)
Log a message.
Definition logging.h:181
#define CRM_LOG_ASSERT(expr)
Definition logging.h:228
#define crm_notice(fmt, args...)
Definition logging.h:395
#define CRM_CHECK(expr, failure_action)
Definition logging.h:245
#define crm_trace(fmt, args...)
Definition logging.h:402
#define pcmk__config_err(fmt...)
@ pcmk__wo_role_after
#define pcmk__warn_once(wo_flag, fmt...)
pcmk_scheduler_t * scheduler
xmlNode * input
@ pcmk_node_variant_cluster
Definition nodes.h:39
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:446
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition nvpair.c:482
int crm_element_value_epoch(const xmlNode *xml, const char *name, time_t *dest)
Retrieve the seconds-since-epoch value of an XML attribute.
Definition nvpair.c:567
#define pcmk__insert_meta(obj, name, value)
#define PCMK_VALUE_RESTART
Definition options.h:199
#define PCMK_META_INTERVAL
Definition options.h:91
#define PCMK_META_ON_FAIL
Definition options.h:98
#define PCMK_OPT_NO_QUORUM_POLICY
Definition options.h:46
#define PCMK_META_ENABLED
Definition options.h:87
#define PCMK_OPT_PRIORITY_FENCING_DELAY
Definition options.h:58
#define PCMK_VALUE_STANDBY
Definition options.h:205
#define PCMK_VALUE_DEMOTE
Definition options.h:145
#define PCMK_META_START_DELAY
Definition options.h:112
#define PCMK_META_INTERVAL_ORIGIN
Definition options.h:93
#define PCMK_VALUE_IGNORE
Definition options.h:161
#define PCMK_VALUE_FENCE
Definition options.h:153
#define PCMK_VALUE_BLOCK
Definition options.h:135
#define PCMK_META_TIMEOUT
Definition options.h:114
#define PCMK_VALUE_RESTART_CONTAINER
Definition options.h:200
#define PCMK_VALUE_UNFENCING
Definition options.h:216
#define PCMK_VALUE_NOTHING
Definition options.h:181
#define PCMK_VALUE_STOP
Definition options.h:209
#define PCMK__META_ROLE_AFTER_FAILURE
#define PCMK__META_ON_NODE_UUID
#define PCMK__META_ON_NODE
#define PCMK__META_OP_TARGET_RC
#define PCMK__META_DIGESTS_SECURE
#define PCMK__META_DIGESTS_ALL
#define PCMK__META_STONITH_ACTION
const char * action
Definition pcmk_fence.c:30
pcmk__action_result_t result
Definition pcmk_fence.c:35
GHashTable * pcmk__unpack_action_meta(pcmk_resource_t *rsc, const pcmk_node_t *node, const char *action_name, guint interval_ms, const xmlNode *action_config)
Definition pe_actions.c:702
pcmk_action_t * get_pseudo_op(const char *name, pcmk_scheduler_t *scheduler)
GList * pe__resource_actions(const pcmk_resource_t *rsc, const pcmk_node_t *node, const char *task, bool require_node)
Find all actions of given type for a resource.
int pe__is_newer_op(const xmlNode *xml_a, const xmlNode *xml_b, bool same_node_default)
GList * find_actions(GList *input, const char *key, const pcmk_node_t *on_node)
pcmk_action_t * pe__new_rsc_pseudo_action(pcmk_resource_t *rsc, const char *task, bool optional, bool runnable)
void pe_free_action(pcmk_action_t *action)
pcmk_action_t * find_first_action(const GList *input, const char *uuid, const char *task, const pcmk_node_t *on_node)
void pe_action_set_reason(pcmk_action_t *action, const char *reason, bool overwrite)
pcmk_action_t * pe_fence_op(pcmk_node_t *node, const char *op, bool optional, const char *reason, bool priority_delay, pcmk_scheduler_t *scheduler)
enum rsc_start_requirement pcmk__action_requires(const pcmk_resource_t *rsc, const char *action_name)
Definition pe_actions.c:852
void pe__add_action_expected_result(pcmk_action_t *action, int expected_result)
enum rsc_role_e pcmk__role_after_failure(const pcmk_resource_t *rsc, const char *action_name, enum action_fail_response on_fail, GHashTable *meta)
gint sort_op_by_callid(gconstpointer a, gconstpointer b)
char * pe__action2reason(const pcmk_action_t *action, enum pe_action_flags flag)
#define sort_return(an_int, why)
GHashTable * pcmk__unpack_action_rsc_params(const xmlNode *action_xml, GHashTable *node_attrs, pcmk_scheduler_t *scheduler)
Definition pe_actions.c:241
pcmk_action_t * custom_action(pcmk_resource_t *rsc, char *key, const char *task, const pcmk_node_t *on_node, gboolean optional, pcmk_scheduler_t *scheduler)
Create or update an action object.
void pe__clear_resource_history(pcmk_resource_t *rsc, const pcmk_node_t *node)
enum action_tasks get_complex_task(const pcmk_resource_t *rsc, const char *name)
xmlNode * pcmk__find_action_config(const pcmk_resource_t *rsc, const char *action_name, guint interval_ms, bool include_disabled)
Definition pe_actions.c:132
GList * find_actions_exact(GList *input, const char *key, const pcmk_node_t *on_node)
enum action_fail_response pcmk__parse_on_fail(const pcmk_resource_t *rsc, const char *action_name, guint interval_ms, const char *value)
Definition pe_actions.c:890
pcmk__op_digest_t * pe__compare_fencing_digest(pcmk_resource_t *rsc, const char *agent, pcmk_node_t *node, pcmk_scheduler_t *scheduler)
Definition pe_digest.c:551
bool pe_can_fence(const pcmk_scheduler_t *scheduler, const pcmk_node_t *node)
Definition utils.c:36
pcmk_node_t * pe__copy_node(const pcmk_node_t *this_node)
Definition utils.c:89
void pe__unpack_dataset_nvpairs(const xmlNode *xml_obj, const char *set_name, const pe_rule_eval_data_t *rule_data, GHashTable *hash, const char *always_first, gboolean overwrite, pcmk_scheduler_t *scheduler)
Definition utils.c:719
void pe_fence_node(pcmk_scheduler_t *scheduler, pcmk_node_t *node, const char *reason, bool priority_delay)
Schedule a fence action for a node.
Definition unpack.c:112
void pe__set_next_role(pcmk_resource_t *rsc, enum rsc_role_e role, const char *why)
Definition complex.c:1253
rsc_start_requirement
Definition resources.h:52
@ pcmk_requires_fencing
Definition resources.h:55
@ pcmk_requires_quorum
Definition resources.h:54
@ pcmk_requires_nothing
Definition resources.h:53
@ pcmk_rsc_needs_fencing
Definition resources.h:175
@ pcmk_rsc_needs_unfencing
Definition resources.h:178
@ pcmk_rsc_stopping
Definition resources.h:148
@ pcmk_rsc_fence_device
Definition resources.h:103
@ pcmk_rsc_needs_quorum
Definition resources.h:172
@ pcmk_rsc_managed
Definition resources.h:88
@ pcmk_rsc_starting
Definition resources.h:145
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk_rc_ok
Definition results.h:162
#define PCMK_ROLE_PROMOTED
Definition roles.h:28
const char * pcmk_role_text(enum rsc_role_e role)
Get readable description of a resource role.
Definition roles.c:23
enum rsc_role_e pcmk_parse_role(const char *role)
Parse a resource role from a string role specification.
Definition roles.c:59
rsc_role_e
Definition roles.h:34
@ pcmk_role_started
Started.
Definition roles.h:37
@ pcmk_role_unknown
Resource role is unknown.
Definition roles.h:35
@ pcmk_role_unpromoted
Unpromoted.
Definition roles.h:38
@ pcmk_role_promoted
Promoted.
Definition roles.h:39
@ pcmk_role_stopped
Stopped.
Definition roles.h:36
#define pcmk__set_rsc_flags(resource, flags_to_set)
#define PCMK__ROLE_PROMOTED_LEGACY
#define pcmk__clear_rsc_flags(resource, flags_to_clear)
pe_quorum_policy
Possible responses to loss of quorum.
Definition scheduler.h:40
@ pcmk_no_quorum_freeze
Definition scheduler.h:41
@ pcmk_no_quorum_stop
Definition scheduler.h:42
@ pcmk_no_quorum_ignore
Definition scheduler.h:43
@ pcmk_no_quorum_demote
Definition scheduler.h:45
pcmk_node_t * pcmk_find_node(const pcmk_scheduler_t *scheduler, const char *node_name)
Find a node by name in scheduler data.
Definition scheduler.c:103
@ pcmk_sched_fencing_enabled
Definition scheduler.h:89
@ pcmk_sched_quorate
Definition scheduler.h:80
@ pcmk_sched_enable_unfencing
Definition scheduler.h:99
#define pcmk__rsc_trace(rsc, fmt, args...)
#define pcmk__rsc_debug(rsc, fmt, args...)
void pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
Definition strings.c:701
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:683
void pcmk__str_update(char **str, const char *value)
Definition strings.c:1277
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1026
@ pcmk__str_none
@ pcmk__str_casei
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1050
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1296
#define pcmk__str_copy(str)
This structure contains everything that makes up a single output formatter.
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
char * reason
Definition actions.h:346
GHashTable * meta
Definition actions.h:354
struct pe_node_shared_s * details
Definition nodes.h:167
GHashTable * attrs
Definition nodes.h:142
const char * id
Definition nodes.h:72
gboolean online
Definition nodes.h:80
const char * uname
Definition nodes.h:73
enum node_type type
Definition nodes.h:74
const char * op_name
Definition common.h:41
GList * actions
Definition resources.h:444
GHashTable * meta
Definition resources.h:467
GList * children
Definition resources.h:471
pcmk_scheduler_t * cluster
Definition resources.h:408
pcmk_resource_t * container
Definition resources.h:476
gboolean is_remote_node
Definition resources.h:431
xmlNode * xml
Definition resources.h:400
unsigned long long flags
Definition resources.h:428
guint remote_reconnect_ms
Definition resources.h:423
enum rsc_role_e next_role
Definition resources.h:465
enum rsc_role_e role
Definition resources.h:464
xmlNode * ops_xml
Definition resources.h:406
const char * standard
Definition common.h:35
GHashTable * node_hash
Definition common.h:46
GHashTable * singletons
Definition scheduler.h:225
const char * stonith_action
Definition scheduler.h:205
GList * resources
Definition scheduler.h:231
unsigned long long flags
Definition scheduler.h:211
xmlNode * op_defaults
Definition scheduler.h:241
enum pe_quorum_policy no_quorum_policy
Definition scheduler.h:217
int priority_fencing_delay
Definition scheduler.h:261
crm_time_t * now
Definition scheduler.h:198
Wrappers for and extensions to libxml2.
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition xml.c:440
xmlNode * pcmk__xe_next_same(const xmlNode *node)
Definition xml.c:2108
#define PCMK_XA_CLASS
Definition xml_names.h:241
#define PCMK_XA_ID
Definition xml_names.h:296
#define PCMK_XA_ROLE
Definition xml_names.h:382
#define PCMK_XA_PROVIDER
Definition xml_names.h:359
#define PCMK_XE_INSTANCE_ATTRIBUTES
Definition xml_names.h:119
#define PCMK_XA_LAST_RC_CHANGE
Definition xml_names.h:311
#define PCMK_XE_META_ATTRIBUTES
Definition xml_names.h:127
#define PCMK_XA_TYPE
Definition xml_names.h:425
#define PCMK_XA_NAME
Definition xml_names.h:325
#define PCMK_XE_OP
Definition xml_names.h:143
#define PCMK__XE_LRM_RSC_OP
#define PCMK__XA_CALL_ID
#define PCMK__XA_TRANSITION_MAGIC