pacemaker 2.1.7-2.1.7
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
pe_actions.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2023 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/msg_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 = first_named_child(rsc->ops_xml, XML_ATTR_OP);
91 operation != NULL; operation = crm_next_same_xml(operation)) {
92
93 bool enabled = false;
94 const char *config_name = NULL;
95 const char *interval_spec = NULL;
96
97 // @TODO This does not consider rules, defaults, etc.
98 if (!include_disabled
99 && (pcmk__xe_get_bool_attr(operation, "enabled",
100 &enabled) == pcmk_rc_ok) && !enabled) {
101 continue;
102 }
103
104 interval_spec = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
105 if (crm_parse_interval_spec(interval_spec) != interval_ms) {
106 continue;
107 }
108
109 config_name = crm_element_value(operation, "name");
110 if (pcmk__str_eq(action_name, config_name, pcmk__str_none)) {
111 return operation;
112 }
113 }
114 return NULL;
115}
116
128xmlNode *
129pcmk__find_action_config(const pcmk_resource_t *rsc, const char *action_name,
130 guint interval_ms, bool include_disabled)
131{
132 xmlNode *action_config = NULL;
133
134 // Try requested action first
135 action_config = find_exact_action_config(rsc, action_name, interval_ms,
136 include_disabled);
137
138 // For migrate_to and migrate_from actions, retry with "migrate"
139 // @TODO This should be either documented or deprecated
140 if ((action_config == NULL)
143 action_config = find_exact_action_config(rsc, "migrate", 0,
144 include_disabled);
145 }
146
147 return action_config;
148}
149
165static pcmk_action_t *
166new_action(char *key, const char *task, pcmk_resource_t *rsc,
167 const pcmk_node_t *node, bool optional, pcmk_scheduler_t *scheduler)
168{
169 pcmk_action_t *action = calloc(1, sizeof(pcmk_action_t));
170
171 CRM_ASSERT(action != NULL);
172
173 action->rsc = rsc;
174 action->task = strdup(task); CRM_ASSERT(action->task != NULL);
175 action->uuid = key;
176
177 if (node) {
178 action->node = pe__copy_node(node);
179 }
180
181 if (pcmk__str_eq(task, PCMK_ACTION_LRM_DELETE, pcmk__str_casei)) {
182 // Resource history deletion for a node can be done on the DC
184 }
185
187 if (optional) {
189 } else {
191 }
192
193 if (rsc == NULL) {
194 action->meta = pcmk__strkey_table(free, free);
195 } else {
196 guint interval_ms = 0;
197
198 parse_op_key(key, NULL, NULL, &interval_ms);
199 action->op_entry = pcmk__find_action_config(rsc, task, interval_ms,
200 true);
201
202 /* If the given key is for one of the many notification pseudo-actions
203 * (pre_notify_promote, etc.), the actual action name is "notify"
204 */
205 if ((action->op_entry == NULL) && (strstr(key, "_notify_") != NULL)) {
206 action->op_entry = find_exact_action_config(rsc, PCMK_ACTION_NOTIFY,
207 0, true);
208 }
209
210 unpack_operation(action, action->op_entry, interval_ms);
211 }
212
213 pe_rsc_trace(rsc, "Created %s action %d (%s): %s for %s on %s",
214 (optional? "optional" : "required"),
215 scheduler->action_id, key, task,
216 ((rsc == NULL)? "no resource" : rsc->id),
217 pe__node_name(node));
218 action->id = scheduler->action_id++;
219
220 scheduler->actions = g_list_prepend(scheduler->actions, action);
221 if (rsc == NULL) {
222 add_singleton(scheduler, action);
223 } else {
224 rsc->actions = g_list_prepend(rsc->actions, action);
225 }
226 return action;
227}
228
239GHashTable *
240pcmk__unpack_action_rsc_params(const xmlNode *action_xml,
241 GHashTable *node_attrs,
243{
244 GHashTable *params = pcmk__strkey_table(free, free);
245
246 pe_rule_eval_data_t rule_data = {
247 .node_hash = node_attrs,
248 .role = pcmk_role_unknown,
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,
276 XML_LRM_ATTR_INTERVAL_MS) == NULL)) {
277 pe_rsc_debug(action->rsc, "%s on %s is optional (%s is unmanaged)",
278 action->uuid, pe__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 "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 pe_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 && (!pe__is_guest_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, pe__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, pe__node_name(action->node));
356
357 } else if (action->needs == pcmk_requires_nothing) {
358 pe_action_set_reason(action, NULL, TRUE);
359 if (pe__is_guest_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 */
366 pe_rsc_debug(action->rsc, "%s on %s is unrunnable "
367 "(node's host cannot be fenced)",
368 action->uuid, pe__node_name(action->node));
370 } else {
371 pe_rsc_trace(action->rsc,
372 "%s on %s does not require fencing or quorum",
373 action->uuid, pe__node_name(action->node));
375 }
376
377 } else {
378 switch (effective_quorum_policy(action->rsc, scheduler)) {
380 pe_rsc_debug(action->rsc, "%s on %s is unrunnable (no quorum)",
381 action->uuid, pe__node_name(action->node));
383 pe_action_set_reason(action, "no quorum", true);
384 break;
385
387 if (!action->rsc->fns->active(action->rsc, TRUE)
388 || (action->rsc->next_role > action->rsc->role)) {
389 pe_rsc_debug(action->rsc,
390 "%s on %s is unrunnable (no quorum)",
391 action->uuid, pe__node_name(action->node));
393 pe_action_set_reason(action, "quorum freeze", true);
394 }
395 break;
396
397 default:
398 //pe_action_set_reason(action, NULL, TRUE);
400 break;
401 }
402 }
403}
404
412static void
413update_resource_flags_for_action(pcmk_resource_t *rsc,
414 const pcmk_action_t *action)
415{
416 /* @COMPAT pcmk_rsc_starting and pcmk_rsc_stopping are deprecated and unused
417 * within Pacemaker, and will eventually be removed
418 */
419 if (pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_casei)) {
421
422 } else if (pcmk__str_eq(action->task, PCMK_ACTION_START, pcmk__str_casei)) {
425 } else {
427 }
428 }
429}
430
431static bool
432valid_stop_on_fail(const char *value)
433{
434 return !pcmk__strcase_any_of(value, "standby", "demote", "stop", NULL);
435}
436
446static void
447validate_on_fail(const pcmk_resource_t *rsc, const char *action_name,
448 const xmlNode *action_config, GHashTable *meta)
449{
450 const char *name = NULL;
451 const char *role = NULL;
452 const char *interval_spec = NULL;
453 const char *value = g_hash_table_lookup(meta, XML_OP_ATTR_ON_FAIL);
454 char *key = NULL;
455 char *new_value = NULL;
456
457 // Stop actions can only use certain on-fail values
458 if (pcmk__str_eq(action_name, PCMK_ACTION_STOP, pcmk__str_none)
459 && !valid_stop_on_fail(value)) {
460
461 pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for %s stop "
462 "action to default value because '%s' is not "
463 "allowed for stop", rsc->id, value);
464 g_hash_table_remove(meta, XML_OP_ATTR_ON_FAIL);
465 return;
466 }
467
468 /* Demote actions default on-fail to the on-fail value for the first
469 * recurring monitor for the promoted role (if any).
470 */
471 if (pcmk__str_eq(action_name, PCMK_ACTION_DEMOTE, pcmk__str_none)
472 && (value == NULL)) {
473
474 /* @TODO This does not consider promote options set in a meta-attribute
475 * block (which may have rules that need to be evaluated) rather than
476 * XML properties.
477 */
478 for (xmlNode *operation = first_named_child(rsc->ops_xml, XML_ATTR_OP);
479 operation != NULL; operation = crm_next_same_xml(operation)) {
480 bool enabled = false;
481 const char *promote_on_fail = NULL;
482
483 /* We only care about explicit on-fail (if promote uses default, so
484 * can demote)
485 */
486 promote_on_fail = crm_element_value(operation, XML_OP_ATTR_ON_FAIL);
487 if (promote_on_fail == NULL) {
488 continue;
489 }
490
491 // We only care about recurring monitors for the promoted role
492 name = crm_element_value(operation, "name");
493 role = crm_element_value(operation, "role");
494 if (!pcmk__str_eq(name, PCMK_ACTION_MONITOR, pcmk__str_none)
497 continue;
498 }
499 interval_spec = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
500 if (crm_parse_interval_spec(interval_spec) == 0) {
501 continue;
502 }
503
504 // We only care about enabled monitors
505 if ((pcmk__xe_get_bool_attr(operation, "enabled",
506 &enabled) == pcmk_rc_ok) && !enabled) {
507 continue;
508 }
509
510 // Demote actions can't default to on-fail="demote"
511 if (pcmk__str_eq(promote_on_fail, "demote", pcmk__str_casei)) {
512 continue;
513 }
514
515 // Use value from first applicable promote action found
516 key = strdup(XML_OP_ATTR_ON_FAIL);
517 new_value = strdup(promote_on_fail);
518 CRM_ASSERT((key != NULL) && (new_value != NULL));
519 g_hash_table_insert(meta, key, new_value);
520 }
521 return;
522 }
523
524 if (pcmk__str_eq(action_name, PCMK_ACTION_LRM_DELETE, pcmk__str_none)
525 && !pcmk__str_eq(value, "ignore", pcmk__str_casei)) {
526 key = strdup(XML_OP_ATTR_ON_FAIL);
527 new_value = strdup("ignore");
528 CRM_ASSERT((key != NULL) && (new_value != NULL));
529 g_hash_table_insert(meta, key, new_value);
530 return;
531 }
532
533 // on-fail="demote" is allowed only for certain actions
534 if (pcmk__str_eq(value, "demote", pcmk__str_casei)) {
535 name = crm_element_value(action_config, "name");
536 role = crm_element_value(action_config, "role");
537 interval_spec = crm_element_value(action_config,
539
540 if (!pcmk__str_eq(name, PCMK_ACTION_PROMOTE, pcmk__str_none)
541 && (!pcmk__str_eq(name, PCMK_ACTION_MONITOR, pcmk__str_none)
544 || (crm_parse_interval_spec(interval_spec) == 0))) {
545 pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for %s %s "
546 "action to default value because 'demote' is not "
547 "allowed for it", rsc->id, name);
548 g_hash_table_remove(meta, XML_OP_ATTR_ON_FAIL);
549 return;
550 }
551 }
552}
553
554static int
555unpack_timeout(const char *value)
556{
557 int timeout_ms = crm_get_msec(value);
558
559 if (timeout_ms < 0) {
561 }
562 return timeout_ms;
563}
564
565// true if value contains valid, non-NULL interval origin for recurring op
566static bool
567unpack_interval_origin(const char *value, const xmlNode *xml_obj,
568 guint interval_ms, const crm_time_t *now,
569 long long *start_delay)
570{
571 long long result = 0;
572 guint interval_sec = interval_ms / 1000;
573 crm_time_t *origin = NULL;
574
575 // Ignore unspecified values and non-recurring operations
576 if ((value == NULL) || (interval_ms == 0) || (now == NULL)) {
577 return false;
578 }
579
580 // Parse interval origin from text
581 origin = crm_time_new(value);
582 if (origin == NULL) {
583 pcmk__config_err("Ignoring '" XML_OP_ATTR_ORIGIN "' for operation "
584 "'%s' because '%s' is not valid",
585 (ID(xml_obj)? ID(xml_obj) : "(missing ID)"), value);
586 return false;
587 }
588
589 // Get seconds since origin (negative if origin is in the future)
591 crm_time_free(origin);
592
593 // Calculate seconds from closest interval to now
594 result = result % interval_sec;
595
596 // Calculate seconds remaining until next interval
597 result = ((result <= 0)? 0 : interval_sec) - result;
598 crm_info("Calculated a start delay of %llds for operation '%s'",
599 result,
600 (ID(xml_obj)? ID(xml_obj) : "(unspecified)"));
601
602 if (start_delay != NULL) {
603 *start_delay = result * 1000; // milliseconds
604 }
605 return true;
606}
607
608static int
609unpack_start_delay(const char *value, GHashTable *meta)
610{
611 int start_delay = 0;
612
613 if (value != NULL) {
614 start_delay = crm_get_msec(value);
615
616 if (start_delay < 0) {
617 start_delay = 0;
618 }
619
620 if (meta) {
621 g_hash_table_replace(meta, strdup(XML_OP_ATTR_START_DELAY),
622 pcmk__itoa(start_delay));
623 }
624 }
625
626 return start_delay;
627}
628
638static xmlNode *
639most_frequent_monitor(const pcmk_resource_t *rsc)
640{
641 guint min_interval_ms = G_MAXUINT;
642 xmlNode *op = NULL;
643
644 for (xmlNode *operation = first_named_child(rsc->ops_xml, XML_ATTR_OP);
645 operation != NULL; operation = crm_next_same_xml(operation)) {
646 bool enabled = false;
647 guint interval_ms = 0;
648 const char *interval_spec = crm_element_value(operation,
650
651 // We only care about enabled recurring monitors
652 if (!pcmk__str_eq(crm_element_value(operation, "name"),
654 continue;
655 }
656 interval_ms = crm_parse_interval_spec(interval_spec);
657 if (interval_ms == 0) {
658 continue;
659 }
660
661 // @TODO This does not account for rules, defaults, etc.
662 if ((pcmk__xe_get_bool_attr(operation, "enabled",
663 &enabled) == pcmk_rc_ok) && !enabled) {
664 continue;
665 }
666
667 if (interval_ms < min_interval_ms) {
668 min_interval_ms = interval_ms;
669 op = operation;
670 }
671 }
672 return op;
673}
674
691GHashTable *
693 const char *action_name, guint interval_ms,
694 const xmlNode *action_config)
695{
696 GHashTable *meta = NULL;
697 char *name = NULL;
698 char *value = NULL;
699 const char *timeout_spec = NULL;
700 const char *str = NULL;
701
702 pe_rsc_eval_data_t rsc_rule_data = {
706 };
707
708 pe_op_eval_data_t op_rule_data = {
709 .op_name = action_name,
710 .interval = interval_ms,
711 };
712
713 pe_rule_eval_data_t rule_data = {
714 .node_hash = (node == NULL)? NULL : node->details->attrs,
715 .role = pcmk_role_unknown,
716 .now = rsc->cluster->now,
717 .match_data = NULL,
718 .rsc_data = &rsc_rule_data,
719 .op_data = &op_rule_data,
720 };
721
722 meta = pcmk__strkey_table(free, free);
723
724 // Cluster-wide <op_defaults> <meta_attributes>
726 &rule_data, meta, NULL, FALSE, rsc->cluster);
727
728 // Derive default timeout for probes from recurring monitor timeouts
729 if (pcmk_is_probe(action_name, interval_ms)) {
730 xmlNode *min_interval_mon = most_frequent_monitor(rsc);
731
732 if (min_interval_mon != NULL) {
733 /* @TODO This does not consider timeouts set in meta_attributes
734 * blocks (which may also have rules that need to be evaluated).
735 */
736 timeout_spec = crm_element_value(min_interval_mon,
738 if (timeout_spec != NULL) {
739 pe_rsc_trace(rsc,
740 "Setting default timeout for %s probe to "
741 "most frequent monitor's timeout '%s'",
742 rsc->id, timeout_spec);
743 name = strdup(XML_ATTR_TIMEOUT);
744 value = strdup(timeout_spec);
745 CRM_ASSERT((name != NULL) && (value != NULL));
746 g_hash_table_insert(meta, name, value);
747 }
748 }
749 }
750
751 if (action_config != NULL) {
752 // <op> <meta_attributes> take precedence over defaults
753 pe__unpack_dataset_nvpairs(action_config, XML_TAG_META_SETS, &rule_data,
754 meta, NULL, TRUE, rsc->cluster);
755
756 /* Anything set as an <op> XML property has highest precedence.
757 * This ensures we use the name and interval from the <op> tag.
758 * (See below for the only exception, fence device start/probe timeout.)
759 */
760 for (xmlAttrPtr attr = action_config->properties;
761 attr != NULL; attr = attr->next) {
762 name = strdup((const char *) attr->name);
763 value = strdup(pcmk__xml_attr_value(attr));
764
765 CRM_ASSERT((name != NULL) && (value != NULL));
766 g_hash_table_insert(meta, name, value);
767 }
768 }
769
770 g_hash_table_remove(meta, XML_ATTR_ID);
771
772 // Normalize interval to milliseconds
773 if (interval_ms > 0) {
774 name = strdup(XML_LRM_ATTR_INTERVAL);
775 CRM_ASSERT(name != NULL);
776 value = crm_strdup_printf("%u", interval_ms);
777 g_hash_table_insert(meta, name, value);
778 } else {
779 g_hash_table_remove(meta, XML_LRM_ATTR_INTERVAL);
780 }
781
782 /* Timeout order of precedence (highest to lowest):
783 * 1. pcmk_monitor_timeout resource parameter (only for starts and probes
784 * when rsc has pcmk_ra_cap_fence_params; this gets used for recurring
785 * monitors via the executor instead)
786 * 2. timeout configured in <op> (with <op timeout> taking precedence over
787 * <op> <meta_attributes>)
788 * 3. timeout configured in <op_defaults> <meta_attributes>
789 * 4. PCMK_DEFAULT_ACTION_TIMEOUT_MS
790 */
791
792 // Check for pcmk_monitor_timeout
793 if (pcmk_is_set(pcmk_get_ra_caps(rsc_rule_data.standard),
795 && (pcmk__str_eq(action_name, PCMK_ACTION_START, pcmk__str_none)
796 || pcmk_is_probe(action_name, interval_ms))) {
797
798 GHashTable *params = pe_rsc_params(rsc, node, rsc->cluster);
799
800 timeout_spec = g_hash_table_lookup(params, "pcmk_monitor_timeout");
801 if (timeout_spec != NULL) {
802 pe_rsc_trace(rsc,
803 "Setting timeout for %s %s to "
804 "pcmk_monitor_timeout (%s)",
805 rsc->id, action_name, timeout_spec);
806 name = strdup(XML_ATTR_TIMEOUT);
807 value = strdup(timeout_spec);
808 CRM_ASSERT((name != NULL) && (value != NULL));
809 g_hash_table_insert(meta, name, value);
810 }
811 }
812
813 // Normalize timeout to positive milliseconds
814 name = strdup(XML_ATTR_TIMEOUT);
815 CRM_ASSERT(name != NULL);
816 timeout_spec = g_hash_table_lookup(meta, XML_ATTR_TIMEOUT);
817 g_hash_table_insert(meta, name, pcmk__itoa(unpack_timeout(timeout_spec)));
818
819 // Ensure on-fail has a valid value
820 validate_on_fail(rsc, action_name, action_config, meta);
821
822 // Normalize start-delay
823 str = g_hash_table_lookup(meta, XML_OP_ATTR_START_DELAY);
824 if (str != NULL) {
825 unpack_start_delay(str, meta);
826 } else {
827 long long start_delay = 0;
828
829 str = g_hash_table_lookup(meta, XML_OP_ATTR_ORIGIN);
830 if (unpack_interval_origin(str, action_config, interval_ms,
831 rsc->cluster->now, &start_delay)) {
833 CRM_ASSERT(name != NULL);
834 g_hash_table_insert(meta, name,
835 crm_strdup_printf("%lld", start_delay));
836 }
837 }
838 return meta;
839}
840
851pcmk__action_requires(const pcmk_resource_t *rsc, const char *action_name)
852{
853 const char *value = NULL;
855
856 CRM_CHECK((rsc != NULL) && (action_name != NULL), return requires);
857
858 if (!pcmk__strcase_any_of(action_name, PCMK_ACTION_START,
859 PCMK_ACTION_PROMOTE, NULL)) {
860 value = "nothing (not start or promote)";
861
862 } else if (pcmk_is_set(rsc->flags, pcmk_rsc_needs_fencing)) {
863 requires = pcmk_requires_fencing;
864 value = "fencing";
865
866 } else if (pcmk_is_set(rsc->flags, pcmk_rsc_needs_quorum)) {
867 requires = pcmk_requires_quorum;
868 value = "quorum";
869
870 } else {
871 value = "nothing";
872 }
873 pe_rsc_trace(rsc, "%s of %s requires %s", action_name, rsc->id, value);
874 return requires;
875}
876
889pcmk__parse_on_fail(const pcmk_resource_t *rsc, const char *action_name,
890 guint interval_ms, const char *value)
891{
892 const char *desc = NULL;
893 bool needs_remote_reset = false;
895
896 if (value == NULL) {
897 // Use default
898
899 } else if (pcmk__str_eq(value, "block", pcmk__str_casei)) {
900 on_fail = pcmk_on_fail_block;
901 desc = "block";
902
903 } else if (pcmk__str_eq(value, "fence", pcmk__str_casei)) {
905 on_fail = pcmk_on_fail_fence_node;
906 desc = "node fencing";
907 } else {
908 pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for "
909 "%s of %s to 'stop' because 'fence' is not "
910 "valid when fencing is disabled",
911 action_name, rsc->id);
912 on_fail = pcmk_on_fail_stop;
913 desc = "stop resource";
914 }
915
916 } else if (pcmk__str_eq(value, "standby", pcmk__str_casei)) {
918 desc = "node standby";
919
920 } else if (pcmk__strcase_any_of(value, "ignore", PCMK__VALUE_NOTHING,
921 NULL)) {
922 desc = "ignore";
923
924 } else if (pcmk__str_eq(value, "migrate", pcmk__str_casei)) {
925 on_fail = pcmk_on_fail_ban;
926 desc = "force migration";
927
928 } else if (pcmk__str_eq(value, "stop", pcmk__str_casei)) {
929 on_fail = pcmk_on_fail_stop;
930 desc = "stop resource";
931
932 } else if (pcmk__str_eq(value, "restart", pcmk__str_casei)) {
933 on_fail = pcmk_on_fail_restart;
934 desc = "restart (and possibly migrate)";
935
936 } else if (pcmk__str_eq(value, "restart-container", pcmk__str_casei)) {
937 if (rsc->container == NULL) {
938 pe_rsc_debug(rsc,
939 "Using default " XML_OP_ATTR_ON_FAIL
940 " for %s of %s because it does not have a container",
941 action_name, rsc->id);
942 } else {
944 desc = "restart container (and possibly migrate)";
945 }
946
947 } else if (pcmk__str_eq(value, "demote", pcmk__str_casei)) {
948 on_fail = pcmk_on_fail_demote;
949 desc = "demote instance";
950
951 } else {
952 pcmk__config_err("Using default '" XML_OP_ATTR_ON_FAIL "' for "
953 "%s of %s because '%s' is not valid",
954 action_name, rsc->id, value);
955 }
956
957 /* Remote node connections are handled specially. Failures that result
958 * in dropping an active connection must result in fencing. The only
959 * failures that don't are probes and starts. The user can explicitly set
960 * on-fail="fence" to fence after start failures.
961 */
963 && !pcmk_is_probe(action_name, interval_ms)
964 && !pcmk__str_eq(action_name, PCMK_ACTION_START, pcmk__str_none)) {
965 needs_remote_reset = true;
966 if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
967 desc = NULL; // Force default for unmanaged connections
968 }
969 }
970
971 if (desc != NULL) {
972 // Explicit value used, default not needed
973
974 } else if (rsc->container != NULL) {
976 desc = "restart container (and possibly migrate) (default)";
977
978 } else if (needs_remote_reset) {
980 if (pcmk_is_set(rsc->cluster->flags,
982 desc = "fence remote node (default)";
983 } else {
984 desc = "recover remote node connection (default)";
985 }
987 } else {
988 on_fail = pcmk_on_fail_stop;
989 desc = "stop unmanaged remote node (enforcing default)";
990 }
991
992 } else if (pcmk__str_eq(action_name, PCMK_ACTION_STOP, pcmk__str_none)) {
994 on_fail = pcmk_on_fail_fence_node;
995 desc = "resource fence (default)";
996 } else {
997 on_fail = pcmk_on_fail_block;
998 desc = "resource block (default)";
999 }
1000
1001 } else {
1002 on_fail = pcmk_on_fail_restart;
1003 desc = "restart (and possibly migrate) (default)";
1004 }
1005
1006 pe_rsc_trace(rsc, "Failure handling for %s-interval %s of %s: %s",
1007 pcmk__readable_interval(interval_ms), action_name,
1008 rsc->id, desc);
1009 return on_fail;
1010}
1011
1023enum rsc_role_e
1024pcmk__role_after_failure(const pcmk_resource_t *rsc, const char *action_name,
1025 enum action_fail_response on_fail, GHashTable *meta)
1026{
1027 const char *value = NULL;
1028 enum rsc_role_e role = pcmk_role_unknown;
1029
1030 // Set default for role after failure specially in certain circumstances
1031 switch (on_fail) {
1032 case pcmk_on_fail_stop:
1033 role = pcmk_role_stopped;
1034 break;
1035
1037 if (rsc->remote_reconnect_ms != 0) {
1038 role = pcmk_role_stopped;
1039 }
1040 break;
1041
1042 default:
1043 break;
1044 }
1045
1046 // @COMPAT Check for explicitly configured role (deprecated)
1047 value = g_hash_table_lookup(meta, "role_after_failure");
1048 if (value != NULL) {
1050 "Support for role_after_failure is deprecated "
1051 "and will be removed in a future release");
1052 if (role == pcmk_role_unknown) {
1053 role = text2role(value);
1054 }
1055 }
1056
1057 if (role == pcmk_role_unknown) {
1058 // Use default
1059 if (pcmk__str_eq(action_name, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
1060 role = pcmk_role_unpromoted;
1061 } else {
1062 role = pcmk_role_started;
1063 }
1064 }
1065 pe_rsc_trace(rsc, "Role after %s %s failure is: %s",
1066 rsc->id, action_name, role2text(role));
1067 return role;
1068}
1069
1082static void
1083unpack_operation(pcmk_action_t *action, const xmlNode *xml_obj,
1084 guint interval_ms)
1085{
1086 const char *value = NULL;
1087
1088 action->meta = pcmk__unpack_action_meta(action->rsc, action->node,
1089 action->task, interval_ms, xml_obj);
1090 action->needs = pcmk__action_requires(action->rsc, action->task);
1091
1092 value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ON_FAIL);
1093 action->on_fail = pcmk__parse_on_fail(action->rsc, action->task,
1094 interval_ms, value);
1095
1096 action->fail_role = pcmk__role_after_failure(action->rsc, action->task,
1097 action->on_fail, action->meta);
1098}
1099
1117custom_action(pcmk_resource_t *rsc, char *key, const char *task,
1118 const pcmk_node_t *on_node, gboolean optional,
1120{
1121 pcmk_action_t *action = NULL;
1122
1123 CRM_ASSERT((key != NULL) && (task != NULL) && (scheduler != NULL));
1124
1125 action = find_existing_action(key, rsc, on_node, scheduler);
1126 if (action == NULL) {
1127 action = new_action(key, task, rsc, on_node, optional, scheduler);
1128 } else {
1129 free(key);
1130 }
1131
1132 update_action_optional(action, optional);
1133
1134 if (rsc != NULL) {
1135 if ((action->node != NULL) && (action->op_entry != NULL)
1137
1138 GHashTable *attrs = action->node->details->attrs;
1139
1140 if (action->extra != NULL) {
1141 g_hash_table_destroy(action->extra);
1142 }
1143 action->extra = pcmk__unpack_action_rsc_params(action->op_entry,
1144 attrs, scheduler);
1146 }
1147
1148 update_resource_action_runnable(action, scheduler);
1149 update_resource_flags_for_action(rsc, action);
1150 }
1151
1152 if (action->extra == NULL) {
1153 action->extra = pcmk__strkey_table(free, free);
1154 }
1155
1156 return action;
1157}
1158
1161{
1162 pcmk_action_t *op = lookup_singleton(scheduler, name);
1163
1164 if (op == NULL) {
1165 op = custom_action(NULL, strdup(name), name, NULL, TRUE, scheduler);
1167 }
1168 return op;
1169}
1170
1171static GList *
1172find_unfencing_devices(GList *candidates, GList *matches)
1173{
1174 for (GList *gIter = candidates; gIter != NULL; gIter = gIter->next) {
1175 pcmk_resource_t *candidate = gIter->data;
1176
1177 if (candidate->children != NULL) {
1178 matches = find_unfencing_devices(candidate->children, matches);
1179
1180 } else if (!pcmk_is_set(candidate->flags, pcmk_rsc_fence_device)) {
1181 continue;
1182
1183 } else if (pcmk_is_set(candidate->flags, pcmk_rsc_needs_unfencing)) {
1184 matches = g_list_prepend(matches, candidate);
1185
1186 } else if (pcmk__str_eq(g_hash_table_lookup(candidate->meta,
1189 pcmk__str_casei)) {
1190 matches = g_list_prepend(matches, candidate);
1191 }
1192 }
1193 return matches;
1194}
1195
1196static int
1197node_priority_fencing_delay(const pcmk_node_t *node,
1199{
1200 int member_count = 0;
1201 int online_count = 0;
1202 int top_priority = 0;
1203 int lowest_priority = 0;
1204 GList *gIter = NULL;
1205
1206 // `priority-fencing-delay` is disabled
1208 return 0;
1209 }
1210
1211 /* No need to request a delay if the fencing target is not a normal cluster
1212 * member, for example if it's a remote node or a guest node. */
1213 if (node->details->type != pcmk_node_variant_cluster) {
1214 return 0;
1215 }
1216
1217 // No need to request a delay if the fencing target is in our partition
1218 if (node->details->online) {
1219 return 0;
1220 }
1221
1222 for (gIter = scheduler->nodes; gIter != NULL; gIter = gIter->next) {
1223 pcmk_node_t *n = gIter->data;
1224
1226 continue;
1227 }
1228
1229 member_count ++;
1230
1231 if (n->details->online) {
1232 online_count++;
1233 }
1234
1235 if (member_count == 1
1236 || n->details->priority > top_priority) {
1237 top_priority = n->details->priority;
1238 }
1239
1240 if (member_count == 1
1241 || n->details->priority < lowest_priority) {
1242 lowest_priority = n->details->priority;
1243 }
1244 }
1245
1246 // No need to delay if we have more than half of the cluster members
1247 if (online_count > member_count / 2) {
1248 return 0;
1249 }
1250
1251 /* All the nodes have equal priority.
1252 * Any configured corresponding `pcmk_delay_base/max` will be applied. */
1253 if (lowest_priority == top_priority) {
1254 return 0;
1255 }
1256
1257 if (node->details->priority < top_priority) {
1258 return 0;
1259 }
1260
1262}
1263
1265pe_fence_op(pcmk_node_t *node, const char *op, bool optional,
1266 const char *reason, bool priority_delay,
1268{
1269 char *op_key = NULL;
1270 pcmk_action_t *stonith_op = NULL;
1271
1272 if(op == NULL) {
1274 }
1275
1276 op_key = crm_strdup_printf("%s-%s-%s",
1277 PCMK_ACTION_STONITH, node->details->uname, op);
1278
1279 stonith_op = lookup_singleton(scheduler, op_key);
1280 if(stonith_op == NULL) {
1281 stonith_op = custom_action(NULL, op_key, PCMK_ACTION_STONITH, node,
1282 TRUE, scheduler);
1283
1284 add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET, node->details->uname);
1286 add_hash_param(stonith_op->meta, "stonith_action", op);
1287
1289 /* Extra work to detect device changes
1290 */
1291 GString *digests_all = g_string_sized_new(1024);
1292 GString *digests_secure = g_string_sized_new(1024);
1293
1294 GList *matches = find_unfencing_devices(scheduler->resources, NULL);
1295
1296 char *key = NULL;
1297 char *value = NULL;
1298
1299 for (GList *gIter = matches; gIter != NULL; gIter = gIter->next) {
1300 pcmk_resource_t *match = gIter->data;
1301 const char *agent = g_hash_table_lookup(match->meta,
1303 op_digest_cache_t *data = NULL;
1304
1305 data = pe__compare_fencing_digest(match, agent, node,
1306 scheduler);
1307 if (data->rc == pcmk__digest_mismatch) {
1308 optional = FALSE;
1309 crm_notice("Unfencing node %s because the definition of "
1310 "%s changed", pe__node_name(node), match->id);
1311 if (!pcmk__is_daemon && scheduler->priv != NULL) {
1313
1314 out->info(out,
1315 "notice: Unfencing node %s because the "
1316 "definition of %s changed",
1317 pe__node_name(node), match->id);
1318 }
1319 }
1320
1321 pcmk__g_strcat(digests_all,
1322 match->id, ":", agent, ":",
1323 data->digest_all_calc, ",", NULL);
1324 pcmk__g_strcat(digests_secure,
1325 match->id, ":", agent, ":",
1326 data->digest_secure_calc, ",", NULL);
1327 }
1328 key = strdup(XML_OP_ATTR_DIGESTS_ALL);
1329 value = strdup((const char *) digests_all->str);
1330 CRM_ASSERT((key != NULL) && (value != NULL));
1331 g_hash_table_insert(stonith_op->meta, key, value);
1332 g_string_free(digests_all, TRUE);
1333
1334 key = strdup(XML_OP_ATTR_DIGESTS_SECURE);
1335 value = strdup((const char *) digests_secure->str);
1336 CRM_ASSERT((key != NULL) && (value != NULL));
1337 g_hash_table_insert(stonith_op->meta, key, value);
1338 g_string_free(digests_secure, TRUE);
1339 }
1340
1341 } else {
1342 free(op_key);
1343 }
1344
1346
1347 /* It's a suitable case where `priority-fencing-delay` applies.
1348 * At least add `priority-fencing-delay` field as an indicator. */
1349 && (priority_delay
1350
1351 /* The priority delay needs to be recalculated if this function has
1352 * been called by schedule_fencing_and_shutdowns() after node
1353 * priority has already been calculated by native_add_running().
1354 */
1355 || g_hash_table_lookup(stonith_op->meta,
1357
1358 /* Add `priority-fencing-delay` to the fencing op even if it's 0 for
1359 * the targeting node. So that it takes precedence over any possible
1360 * `pcmk_delay_base/max`.
1361 */
1362 char *delay_s = pcmk__itoa(node_priority_fencing_delay(node,
1363 scheduler));
1364
1365 g_hash_table_insert(stonith_op->meta,
1367 delay_s);
1368 }
1369
1370 if(optional == FALSE && pe_can_fence(scheduler, node)) {
1372 pe_action_set_reason(stonith_op, reason, false);
1373
1374 } else if(reason && stonith_op->reason == NULL) {
1375 stonith_op->reason = strdup(reason);
1376 }
1377
1378 return stonith_op;
1379}
1380
1381void
1383{
1384 if (action == NULL) {
1385 return;
1386 }
1387 g_list_free_full(action->actions_before, free);
1388 g_list_free_full(action->actions_after, free);
1389 if (action->extra) {
1390 g_hash_table_destroy(action->extra);
1391 }
1392 if (action->meta) {
1393 g_hash_table_destroy(action->meta);
1394 }
1395 free(action->cancel_task);
1396 free(action->reason);
1397 free(action->task);
1398 free(action->uuid);
1399 free(action->node);
1400 free(action);
1401}
1402
1403int
1406{
1407 xmlNode *child = NULL;
1408 GHashTable *action_meta = NULL;
1409 const char *timeout_spec = NULL;
1410 int timeout_ms = 0;
1411
1412 pe_rule_eval_data_t rule_data = {
1413 .node_hash = NULL,
1414 .role = pcmk_role_unknown,
1415 .now = scheduler->now,
1416 .match_data = NULL,
1417 .rsc_data = NULL,
1418 .op_data = NULL
1419 };
1420
1421 for (child = first_named_child(rsc->ops_xml, XML_ATTR_OP);
1422 child != NULL; child = crm_next_same_xml(child)) {
1423 if (pcmk__str_eq(action, crm_element_value(child, XML_NVPAIR_ATTR_NAME),
1424 pcmk__str_casei)) {
1425 timeout_spec = crm_element_value(child, XML_ATTR_TIMEOUT);
1426 break;
1427 }
1428 }
1429
1430 if (timeout_spec == NULL && scheduler->op_defaults) {
1431 action_meta = pcmk__strkey_table(free, free);
1433 &rule_data, action_meta, NULL, FALSE,
1434 scheduler);
1435 timeout_spec = g_hash_table_lookup(action_meta, XML_ATTR_TIMEOUT);
1436 }
1437
1438 // @TODO check meta-attributes
1439 // @TODO maybe use min-interval monitor timeout as default for monitors
1440
1441 timeout_ms = crm_get_msec(timeout_spec);
1442 if (timeout_ms < 0) {
1443 timeout_ms = PCMK_DEFAULT_ACTION_TIMEOUT_MS;
1444 }
1445
1446 if (action_meta != NULL) {
1447 g_hash_table_destroy(action_meta);
1448 }
1449 return timeout_ms;
1450}
1451
1452enum action_tasks
1453get_complex_task(const pcmk_resource_t *rsc, const char *name)
1454{
1455 enum action_tasks task = text2task(name);
1456
1457 if ((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_primitive)) {
1458 switch (task) {
1463 crm_trace("Folding %s back into its atomic counterpart for %s",
1464 name, rsc->id);
1465 --task;
1466 break;
1467 default:
1468 break;
1469 }
1470 }
1471 return task;
1472}
1473
1486find_first_action(const GList *input, const char *uuid, const char *task,
1487 const pcmk_node_t *on_node)
1488{
1489 CRM_CHECK(uuid || task, return NULL);
1490
1491 for (const GList *gIter = input; gIter != NULL; gIter = gIter->next) {
1492 pcmk_action_t *action = (pcmk_action_t *) gIter->data;
1493
1494 if (uuid != NULL && !pcmk__str_eq(uuid, action->uuid, pcmk__str_casei)) {
1495 continue;
1496
1497 } else if (task != NULL && !pcmk__str_eq(task, action->task, pcmk__str_casei)) {
1498 continue;
1499
1500 } else if (on_node == NULL) {
1501 return action;
1502
1503 } else if (action->node == NULL) {
1504 continue;
1505
1506 } else if (on_node->details == action->node->details) {
1507 return action;
1508 }
1509 }
1510
1511 return NULL;
1512}
1513
1514GList *
1515find_actions(GList *input, const char *key, const pcmk_node_t *on_node)
1516{
1517 GList *gIter = input;
1518 GList *result = NULL;
1519
1520 CRM_CHECK(key != NULL, return NULL);
1521
1522 for (; gIter != NULL; gIter = gIter->next) {
1523 pcmk_action_t *action = (pcmk_action_t *) gIter->data;
1524
1525 if (!pcmk__str_eq(key, action->uuid, pcmk__str_casei)) {
1526 continue;
1527
1528 } else if (on_node == NULL) {
1529 crm_trace("Action %s matches (ignoring node)", key);
1530 result = g_list_prepend(result, action);
1531
1532 } else if (action->node == NULL) {
1533 crm_trace("Action %s matches (unallocated, assigning to %s)",
1534 key, pe__node_name(on_node));
1535
1536 action->node = pe__copy_node(on_node);
1537 result = g_list_prepend(result, action);
1538
1539 } else if (on_node->details == action->node->details) {
1540 crm_trace("Action %s on %s matches", key, pe__node_name(on_node));
1541 result = g_list_prepend(result, action);
1542 }
1543 }
1544
1545 return result;
1546}
1547
1548GList *
1549find_actions_exact(GList *input, const char *key, const pcmk_node_t *on_node)
1550{
1551 GList *result = NULL;
1552
1553 CRM_CHECK(key != NULL, return NULL);
1554
1555 if (on_node == NULL) {
1556 return NULL;
1557 }
1558
1559 for (GList *gIter = input; gIter != NULL; gIter = gIter->next) {
1560 pcmk_action_t *action = (pcmk_action_t *) gIter->data;
1561
1562 if ((action->node != NULL)
1563 && pcmk__str_eq(key, action->uuid, pcmk__str_casei)
1564 && pcmk__str_eq(on_node->details->id, action->node->details->id,
1565 pcmk__str_casei)) {
1566
1567 crm_trace("Action %s on %s matches", key, pe__node_name(on_node));
1568 result = g_list_prepend(result, action);
1569 }
1570 }
1571
1572 return result;
1573}
1574
1587GList *
1589 const char *task, bool require_node)
1590{
1591 GList *result = NULL;
1592 char *key = pcmk__op_key(rsc->id, task, 0);
1593
1594 if (require_node) {
1595 result = find_actions_exact(rsc->actions, key, node);
1596 } else {
1597 result = find_actions(rsc->actions, key, node);
1598 }
1599 free(key);
1600 return result;
1601}
1602
1613char *
1615{
1616 const char *change = NULL;
1617
1618 switch (flag) {
1620 change = "unrunnable";
1621 break;
1623 change = "unmigrateable";
1624 break;
1626 change = "required";
1627 break;
1628 default:
1629 // Bug: caller passed unsupported flag
1630 CRM_CHECK(change != NULL, change = "");
1631 break;
1632 }
1633 return crm_strdup_printf("%s%s%s %s", change,
1634 (action->rsc == NULL)? "" : " ",
1635 (action->rsc == NULL)? "" : action->rsc->id,
1636 action->task);
1637}
1638
1640 bool overwrite)
1641{
1642 if (action->reason != NULL && overwrite) {
1643 pe_rsc_trace(action->rsc, "Changing %s reason from '%s' to '%s'",
1644 action->uuid, action->reason, pcmk__s(reason, "(none)"));
1645 } else if (action->reason == NULL) {
1646 pe_rsc_trace(action->rsc, "Set %s reason to '%s'",
1647 action->uuid, pcmk__s(reason, "(none)"));
1648 } else {
1649 // crm_assert(action->reason != NULL && !overwrite);
1650 return;
1651 }
1652
1653 pcmk__str_update(&action->reason, reason);
1654}
1655
1663void
1665{
1666 CRM_ASSERT((rsc != NULL) && (node != NULL));
1667
1669 PCMK_ACTION_LRM_DELETE, node, FALSE, rsc->cluster);
1670}
1671
1672#define sort_return(an_int, why) do { \
1673 free(a_uuid); \
1674 free(b_uuid); \
1675 crm_trace("%s (%d) %c %s (%d) : %s", \
1676 a_xml_id, a_call_id, an_int>0?'>':an_int<0?'<':'=', \
1677 b_xml_id, b_call_id, why); \
1678 return an_int; \
1679 } while(0)
1680
1681int
1682pe__is_newer_op(const xmlNode *xml_a, const xmlNode *xml_b,
1683 bool same_node_default)
1684{
1685 int a_call_id = -1;
1686 int b_call_id = -1;
1687
1688 char *a_uuid = NULL;
1689 char *b_uuid = NULL;
1690
1691 const char *a_xml_id = crm_element_value(xml_a, XML_ATTR_ID);
1692 const char *b_xml_id = crm_element_value(xml_b, XML_ATTR_ID);
1693
1694 const char *a_node = crm_element_value(xml_a, XML_LRM_ATTR_TARGET);
1695 const char *b_node = crm_element_value(xml_b, XML_LRM_ATTR_TARGET);
1696 bool same_node = true;
1697
1698 /* @COMPAT The on_node attribute was added to last_failure as of 1.1.13 (via
1699 * 8b3ca1c) and the other entries as of 1.1.12 (via 0b07b5c).
1700 *
1701 * In case that any of the lrm_rsc_op entries doesn't have on_node
1702 * attribute, we need to explicitly tell whether the two operations are on
1703 * the same node.
1704 */
1705 if (a_node == NULL || b_node == NULL) {
1706 same_node = same_node_default;
1707
1708 } else {
1709 same_node = pcmk__str_eq(a_node, b_node, pcmk__str_casei);
1710 }
1711
1712 if (same_node && pcmk__str_eq(a_xml_id, b_xml_id, pcmk__str_none)) {
1713 /* We have duplicate lrm_rsc_op entries in the status
1714 * section which is unlikely to be a good thing
1715 * - we can handle it easily enough, but we need to get
1716 * to the bottom of why it's happening.
1717 */
1718 pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id);
1719 sort_return(0, "duplicate");
1720 }
1721
1722 crm_element_value_int(xml_a, XML_LRM_ATTR_CALLID, &a_call_id);
1723 crm_element_value_int(xml_b, XML_LRM_ATTR_CALLID, &b_call_id);
1724
1725 if (a_call_id == -1 && b_call_id == -1) {
1726 /* both are pending ops so it doesn't matter since
1727 * stops are never pending
1728 */
1729 sort_return(0, "pending");
1730
1731 } else if (same_node && a_call_id >= 0 && a_call_id < b_call_id) {
1732 sort_return(-1, "call id");
1733
1734 } else if (same_node && b_call_id >= 0 && a_call_id > b_call_id) {
1735 sort_return(1, "call id");
1736
1737 } else if (a_call_id >= 0 && b_call_id >= 0
1738 && (!same_node || a_call_id == b_call_id)) {
1739 /*
1740 * The op and last_failed_op are the same
1741 * Order on last-rc-change
1742 */
1743 time_t last_a = -1;
1744 time_t last_b = -1;
1745
1748
1749 crm_trace("rc-change: %lld vs %lld",
1750 (long long) last_a, (long long) last_b);
1751 if (last_a >= 0 && last_a < last_b) {
1752 sort_return(-1, "rc-change");
1753
1754 } else if (last_b >= 0 && last_a > last_b) {
1755 sort_return(1, "rc-change");
1756 }
1757 sort_return(0, "rc-change");
1758
1759 } else {
1760 /* One of the inputs is a pending operation
1761 * Attempt to use XML_ATTR_TRANSITION_MAGIC to determine its age relative to the other
1762 */
1763
1764 int a_id = -1;
1765 int b_id = -1;
1766
1767 const char *a_magic = crm_element_value(xml_a, XML_ATTR_TRANSITION_MAGIC);
1768 const char *b_magic = crm_element_value(xml_b, XML_ATTR_TRANSITION_MAGIC);
1769
1770 CRM_CHECK(a_magic != NULL && b_magic != NULL, sort_return(0, "No magic"));
1771 if (!decode_transition_magic(a_magic, &a_uuid, &a_id, NULL, NULL, NULL,
1772 NULL)) {
1773 sort_return(0, "bad magic a");
1774 }
1775 if (!decode_transition_magic(b_magic, &b_uuid, &b_id, NULL, NULL, NULL,
1776 NULL)) {
1777 sort_return(0, "bad magic b");
1778 }
1779 /* try to determine the relative age of the operation...
1780 * some pending operations (e.g. a start) may have been superseded
1781 * by a subsequent stop
1782 *
1783 * [a|b]_id == -1 means it's a shutdown operation and _always_ comes last
1784 */
1785 if (!pcmk__str_eq(a_uuid, b_uuid, pcmk__str_casei) || a_id == b_id) {
1786 /*
1787 * some of the logic in here may be redundant...
1788 *
1789 * if the UUID from the TE doesn't match then one better
1790 * be a pending operation.
1791 * pending operations don't survive between elections and joins
1792 * because we query the LRM directly
1793 */
1794
1795 if (b_call_id == -1) {
1796 sort_return(-1, "transition + call");
1797
1798 } else if (a_call_id == -1) {
1799 sort_return(1, "transition + call");
1800 }
1801
1802 } else if ((a_id >= 0 && a_id < b_id) || b_id == -1) {
1803 sort_return(-1, "transition");
1804
1805 } else if ((b_id >= 0 && a_id > b_id) || a_id == -1) {
1806 sort_return(1, "transition");
1807 }
1808 }
1809
1810 /* we should never end up here */
1811 CRM_CHECK(FALSE, sort_return(0, "default"));
1812}
1813
1814gint
1815sort_op_by_callid(gconstpointer a, gconstpointer b)
1816{
1817 const xmlNode *xml_a = a;
1818 const xmlNode *xml_b = b;
1819
1820 return pe__is_newer_op(xml_a, xml_b, true);
1821}
1822
1835pe__new_rsc_pseudo_action(pcmk_resource_t *rsc, const char *task, bool optional,
1836 bool runnable)
1837{
1838 pcmk_action_t *action = NULL;
1839
1840 CRM_ASSERT((rsc != NULL) && (task != NULL));
1841
1842 action = custom_action(rsc, pcmk__op_key(rsc->id, task, 0), task, NULL,
1843 optional, rsc->cluster);
1845 if (runnable) {
1847 }
1848 return action;
1849}
1850
1860void
1862{
1863 char *name = NULL;
1864
1865 CRM_ASSERT((action != NULL) && (action->meta != NULL));
1866
1867 name = strdup(XML_ATTR_TE_TARGET_RC);
1868 CRM_ASSERT (name != NULL);
1869
1870 g_hash_table_insert(action->meta, name, pcmk__itoa(expected_result));
1871}
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition actions.c:96
#define PCMK_ACTION_STOP
Definition actions.h:74
bool pcmk_is_probe(const char *task, guint interval)
Definition actions.c:496
#define PCMK_ACTION_PROMOTE
Definition actions.h:65
#define PCMK_ACTION_LRM_DELETE
Definition actions.h:53
#define PCMK_ACTION_START
Definition actions.h:71
action_fail_response
Possible responses to a resource action failure.
Definition actions.h:149
@ pcmk_on_fail_ban
Ban resource from current node.
Definition actions.h:169
@ pcmk_on_fail_fence_node
Fence resource's node.
Definition actions.h:181
@ pcmk_on_fail_ignore
Act as if failure didn't happen.
Definition actions.h:163
@ pcmk_on_fail_restart_container
Restart resource's container.
Definition actions.h:186
@ pcmk_on_fail_demote
Demote if promotable, else stop.
Definition actions.h:197
@ pcmk_on_fail_standby_node
Put resource's node in standby.
Definition actions.h:178
@ pcmk_on_fail_block
Treat resource as unmanaged.
Definition actions.h:172
@ pcmk_on_fail_reset_remote
Definition actions.h:194
@ pcmk_on_fail_stop
Stop resource and leave stopped.
Definition actions.h:175
@ pcmk_on_fail_restart
Restart resource.
Definition actions.h:166
#define PCMK_ACTION_MIGRATE_FROM
Definition actions.h:57
pe_action_flags
Action scheduling flags.
Definition actions.h:233
@ pcmk_action_runnable
Whether action is runnable.
Definition actions.h:241
@ pcmk_action_migratable
Whether action is allowed to be part of a live migration.
Definition actions.h:253
@ pcmk_action_pseudo
Whether action does not require invoking an agent.
Definition actions.h:238
@ pcmk_action_attrs_evaluated
Whether operation-specific instance attributes have been unpacked yet.
Definition actions.h:250
@ pcmk_action_optional
Whether action should not be executed.
Definition actions.h:244
@ pcmk_action_on_dc
Whether action can be executed on DC rather than own node.
Definition actions.h:278
#define PCMK_ACTION_MIGRATE_TO
Definition actions.h:58
#define PCMK_DEFAULT_ACTION_TIMEOUT_MS
Default timeout (in milliseconds) for non-metadata actions.
Definition actions.h:38
action_tasks
Possible actions (including some pseudo-actions)
Definition actions.h:79
@ pcmk_action_stopped
Stop completed.
Definition actions.h:86
@ pcmk_action_started
Start completed.
Definition actions.h:89
@ pcmk_action_demoted
Demoted.
Definition actions.h:98
@ pcmk_action_promoted
Promoted.
Definition actions.h:95
#define PCMK_ACTION_MONITOR
Definition actions.h:59
#define PCMK_ACTION_STONITH
Definition actions.h:73
#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:209
#define PCMK_ACTION_NOTIFY
Definition actions.h:61
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:42
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:878
bool pcmk__is_daemon
Definition logging.c:47
char guint crm_parse_interval_spec(const char *input)
Parse milliseconds from a Pacemaker interval specification.
Definition utils.c:271
long long crm_get_msec(const char *input)
Parse a time+units string and return milliseconds equivalent.
Definition strings.c:364
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:99
enum action_tasks text2task(const char *task)
Definition common.c:360
const char * role2text(enum rsc_role_e role)
Definition common.c:458
enum rsc_role_e text2role(const char *role)
Definition common.c:487
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:446
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:109
struct crm_time_s crm_time_t
Definition iso8601.h:32
const char * pcmk__readable_interval(guint interval_ms)
Definition iso8601.c:1926
#define crm_info(fmt, args...)
Definition logging.h:382
#define do_crm_log(level, fmt, args...)
Log a message.
Definition logging.h:175
#define CRM_LOG_ASSERT(expr)
Definition logging.h:222
#define crm_notice(fmt, args...)
Definition logging.h:381
#define CRM_CHECK(expr, failure_action)
Definition logging.h:238
#define crm_trace(fmt, args...)
Definition logging.h:385
#define pcmk__config_err(fmt...)
#define ID(x)
Definition msg_xml.h:474
#define XML_EXPR_ATTR_TYPE
Definition msg_xml.h:350
#define XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY
Definition msg_xml.h:405
#define XML_LRM_ATTR_TARGET_UUID
Definition msg_xml.h:309
#define XML_TAG_ATTR_SETS
Definition msg_xml.h:227
#define XML_ATTR_ID
Definition msg_xml.h:156
#define XML_LRM_ATTR_INTERVAL
Definition msg_xml.h:300
#define XML_ATTR_TRANSITION_MAGIC
Definition msg_xml.h:415
#define XML_AGENT_ATTR_PROVIDER
Definition msg_xml.h:281
#define XML_AGENT_ATTR_CLASS
Definition msg_xml.h:280
#define XML_TAG_META_SETS
Definition msg_xml.h:228
#define XML_OP_ATTR_DIGESTS_ALL
Definition msg_xml.h:273
#define XML_ATTR_TYPE
Definition msg_xml.h:160
#define XML_NVPAIR_ATTR_NAME
Definition msg_xml.h:393
#define XML_LRM_ATTR_TARGET
Definition msg_xml.h:308
#define XML_ATTR_TIMEOUT
Definition msg_xml.h:150
#define XML_OP_ATTR_ON_FAIL
Definition msg_xml.h:268
#define XML_RSC_OP_LAST_CHANGE
Definition msg_xml.h:326
#define XML_OP_ATTR_DIGESTS_SECURE
Definition msg_xml.h:274
#define XML_OP_ATTR_ORIGIN
Definition msg_xml.h:271
#define XML_LRM_ATTR_CALLID
Definition msg_xml.h:318
#define XML_ATTR_OP
Definition msg_xml.h:161
#define XML_OP_ATTR_START_DELAY
Definition msg_xml.h:269
#define XML_ATTR_TE_TARGET_RC
Definition msg_xml.h:419
#define XML_LRM_ATTR_INTERVAL_MS
Definition msg_xml.h:304
pcmk_scheduler_t * scheduler
xmlNode * input
@ pcmk_node_variant_cluster
Cluster layer node.
Definition nodes.h:34
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:447
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition nvpair.c:483
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:568
#define PCMK__VALUE_UNFENCING
#define PCMK__VALUE_NOTHING
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:692
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_get_configured_timeout(pcmk_resource_t *rsc, const char *action, pcmk_scheduler_t *scheduler)
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:851
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:240
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:129
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:889
op_digest_cache_t * pe__compare_fencing_digest(pcmk_resource_t *rsc, const char *agent, pcmk_node_t *node, pcmk_scheduler_t *scheduler)
Definition pe_digest.c:541
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
#define pe_warn_once(pe_wo_bit, fmt...)
Definition internal.h:142
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:707
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:110
#define pe__clear_resource_flags(resource, flags_to_clear)
Definition internal.h:70
#define pe_rsc_debug(rsc, fmt, args...)
Definition internal.h:36
#define pe_rsc_trace(rsc, fmt, args...)
Definition internal.h:37
#define pe__set_resource_flags(resource, flags_to_set)
Definition internal.h:64
void pe__set_next_role(pcmk_resource_t *rsc, enum rsc_role_e role, const char *why)
Definition complex.c:1184
#define pe__clear_action_flags(action, flags_to_clear)
Definition internal.h:85
#define pe_err(fmt...)
Definition internal.h:39
#define pe__set_action_flags(action, flags_to_set)
Definition internal.h:76
void add_hash_param(GHashTable *hash, const char *name, const char *value)
Definition common.c:508
bool pe__resource_is_remote_conn(const pcmk_resource_t *rsc)
Definition remote.c:18
bool pe__is_guest_node(const pcmk_node_t *node)
Definition remote.c:33
rsc_start_requirement
What resource needs before it can be recovered from a failed node.
Definition resources.h:58
@ pcmk_requires_fencing
Resource can be recovered after fencing.
Definition resources.h:61
@ pcmk_requires_quorum
Resource can be recovered if quorate.
Definition resources.h:60
@ pcmk_requires_nothing
Resource can be recovered immediately.
Definition resources.h:59
@ pcmk_rsc_variant_primitive
Primitive resource.
Definition resources.h:34
@ pcmk_rsc_needs_fencing
Whether resource requires fencing before recovery if on unclean node.
Definition resources.h:190
@ pcmk_rsc_needs_unfencing
Whether resource can be started or promoted only on unfenced nodes.
Definition resources.h:193
@ pcmk_rsc_stopping
Definition resources.h:166
@ pcmk_rsc_fence_device
Whether resource's class is "stonith".
Definition resources.h:121
@ pcmk_rsc_needs_quorum
Whether resource can be started or promoted only on quorate nodes.
Definition resources.h:187
@ pcmk_rsc_managed
Whether resource is managed.
Definition resources.h:106
@ pcmk_rsc_starting
Definition resources.h:163
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk_rc_ok
Definition results.h:154
rsc_role_e
Definition roles.h:27
@ pcmk_role_started
Started.
Definition roles.h:30
@ pcmk_role_unknown
Resource role is unknown.
Definition roles.h:28
@ pcmk_role_unpromoted
Unpromoted.
Definition roles.h:31
@ pcmk_role_promoted
Promoted.
Definition roles.h:32
@ pcmk_role_stopped
Stopped.
Definition roles.h:29
#define PCMK__ROLE_PROMOTED
#define PCMK__ROLE_PROMOTED_LEGACY
pe_quorum_policy
Possible responses to loss of quorum.
Definition scheduler.h:38
@ pcmk_no_quorum_freeze
Definition scheduler.h:39
@ pcmk_no_quorum_stop
Definition scheduler.h:40
@ pcmk_no_quorum_ignore
Definition scheduler.h:41
@ pcmk_no_quorum_demote
Definition scheduler.h:43
@ pcmk_sched_fencing_enabled
Whether fencing is enabled (via stonith-enabled property)
Definition scheduler.h:80
@ pcmk_sched_quorate
Whether partition has quorum (via have-quorum property)
Definition scheduler.h:71
@ pcmk_sched_enable_unfencing
Whether any resource provides or requires unfencing (via CIB resources)
Definition scheduler.h:86
@ pcmk__wo_role_after
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:608
void pcmk__str_update(char **str, const char *value)
Definition strings.c:1193
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:933
@ pcmk__str_none
@ pcmk__str_casei
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:957
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1217
This structure contains everything that makes up a single output formatter.
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
Implementation of pcmk_action_t.
Definition actions.h:390
char * reason
Readable description of why action is needed.
Definition actions.h:406
GHashTable * meta
Meta-attributes relevant to action.
Definition actions.h:414
Implementation of pcmk_node_t.
Definition nodes.h:130
struct pe_node_shared_s * details
Basic node information.
Definition nodes.h:134
GHashTable * attrs
Node attributes.
Definition nodes.h:115
const char * id
Node ID at the cluster layer.
Definition nodes.h:67
gboolean online
Whether online.
Definition nodes.h:72
const char * uname
Node name in cluster.
Definition nodes.h:68
enum node_type type
Node variant.
Definition nodes.h:69
const char * op_name
Definition common.h:75
Implementation of pcmk_resource_t.
Definition resources.h:399
GList * actions
Definition resources.h:447
enum pe_obj_types variant
Resource variant.
Definition resources.h:414
GHashTable * meta
Resource's meta-attributes.
Definition resources.h:471
GList * children
Resource's child resources, if any.
Definition resources.h:475
pcmk_scheduler_t * cluster
Cluster that resource is part of.
Definition resources.h:412
pcmk_resource_t * container
Resource containing this one, if any.
Definition resources.h:480
char * id
Resource ID in configuration.
Definition resources.h:400
xmlNode * xml
Resource configuration (possibly expanded from template)
Definition resources.h:404
unsigned long long flags
Group of enum pcmk_rsc_flags.
Definition resources.h:429
guint remote_reconnect_ms
Retry interval for remote connections.
Definition resources.h:427
enum rsc_role_e next_role
Resource's scheduled next role.
Definition resources.h:469
enum rsc_role_e role
Resource's current role.
Definition resources.h:468
xmlNode * ops_xml
Configuration of resource operations (possibly expanded from template)
Definition resources.h:410
const char * standard
Definition common.h:69
GHashTable * node_hash
Definition common.h:80
Implementation of pcmk_scheduler_t.
Definition scheduler.h:172
GHashTable * singletons
Actions for which there can be only one (such as "fence node X")
Definition scheduler.h:193
const char * stonith_action
Default fencing action.
Definition scheduler.h:179
int action_id
ID to use for next created action.
Definition scheduler.h:211
GList * actions
Scheduled actions.
Definition scheduler.h:204
GList * resources
Resources in cluster.
Definition scheduler.h:196
unsigned long long flags
Group of enum pcmk_scheduler_flags.
Definition scheduler.h:183
void * priv
For Pacemaker use only.
Definition scheduler.h:229
xmlNode * op_defaults
Configured operation defaults.
Definition scheduler.h:206
enum pe_quorum_policy no_quorum_policy
Response to loss of quorum.
Definition scheduler.h:186
GList * nodes
Nodes in cluster.
Definition scheduler.h:195
int priority_fencing_delay
Priority fencing delay.
Definition scheduler.h:226
crm_time_t * now
Current time for evaluation purposes.
Definition scheduler.h:176
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition xml.c:2484
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition xml.c:2510