pacemaker  2.1.9-49aab99839
Scalable High-Availability cluster resource manager
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>
18 #include <crm/pengine/internal.h>
20 #include "pe_status_private.h"
21 
22 static void unpack_operation(pcmk_action_t *action, const xmlNode *xml_obj,
23  guint interval_ms);
24 
25 static void
27 {
28  if (scheduler->singletons == NULL) {
30  }
31  g_hash_table_insert(scheduler->singletons, action->uuid, action);
32 }
33 
34 static pcmk_action_t *
35 lookup_singleton(pcmk_scheduler_t *scheduler, const char *action_uuid)
36 {
37  if (scheduler->singletons == NULL) {
38  return NULL;
39  }
40  return g_hash_table_lookup(scheduler->singletons, action_uuid);
41 }
42 
54 static pcmk_action_t *
55 find_existing_action(const char *key, const pcmk_resource_t *rsc,
56  const pcmk_node_t *node, const pcmk_scheduler_t *scheduler)
57 {
58  GList *matches = NULL;
59  pcmk_action_t *action = NULL;
60 
61  /* 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 
86 static xmlNode *
87 find_exact_action_config(const pcmk_resource_t *rsc, const char *action_name,
88  guint interval_ms, bool include_disabled)
89 {
90  for (xmlNode *operation = pcmk__xe_first_child(rsc->ops_xml, PCMK_XE_OP,
91  NULL, NULL);
92  operation != NULL; operation = pcmk__xe_next_same(operation)) {
93 
94  bool enabled = false;
95  const char *config_name = NULL;
96  const char *interval_spec = NULL;
97  guint tmp_ms = 0U;
98 
99  // @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 
131 xmlNode *
132 pcmk__find_action_config(const pcmk_resource_t *rsc, const char *action_name,
133  guint interval_ms, bool include_disabled)
134 {
135  xmlNode *action_config = NULL;
136 
137  // 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)
145  PCMK_ACTION_MIGRATE_FROM, NULL)) {
146  action_config = find_exact_action_config(rsc, "migrate", 0,
147  include_disabled);
148  }
149 
150  return action_config;
151 }
152 
168 static pcmk_action_t *
169 new_action(char *key, const char *task, pcmk_resource_t *rsc,
170  const pcmk_node_t *node, bool optional, pcmk_scheduler_t *scheduler)
171 {
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 
240 GHashTable *
241 pcmk__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 
268 static void
269 update_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)) {
276  pcmk__rsc_debug(action->rsc,
277  "%s on %s is optional (%s is unmanaged)",
278  action->uuid, pcmk__node_name(action->node),
279  action->rsc->id);
281  // We shouldn't clear runnable here because ... something
282 
283  // Otherwise require the action if requested
284  } else if (!optional) {
286  }
287 }
288 
289 static enum pe_quorum_policy
290 effective_quorum_policy(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
291 {
293 
295  policy = pcmk_no_quorum_ignore;
296 
298  switch (rsc->role) {
299  case pcmk_role_promoted:
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 
324 static void
325 update_resource_action_runnable(pcmk_action_t *action,
327 {
328  if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
329  return;
330  }
331 
332  if (action->node == NULL) {
333  pcmk__rsc_trace(action->rsc, "%s is unrunnable (unallocated)",
334  action->uuid);
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  */
366  pcmk__rsc_debug(action->rsc,
367  "%s on %s is unrunnable "
368  "(node's host cannot be fenced)",
369  action->uuid, pcmk__node_name(action->node));
371  } else {
372  pcmk__rsc_trace(action->rsc,
373  "%s on %s does not require fencing or quorum",
374  action->uuid, pcmk__node_name(action->node));
376  }
377 
378  } else {
379  switch (effective_quorum_policy(action->rsc, scheduler)) {
380  case pcmk_no_quorum_stop:
381  pcmk__rsc_debug(action->rsc,
382  "%s on %s is unrunnable (no quorum)",
383  action->uuid, pcmk__node_name(action->node));
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)) {
391  pcmk__rsc_debug(action->rsc,
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 
415 static void
416 update_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)) {
426  if (pcmk_is_set(action->flags, pcmk_action_runnable)) {
428  } else {
430  }
431  }
432 }
433 
434 static bool
435 valid_stop_on_fail(const char *value)
436 {
437  return !pcmk__strcase_any_of(value,
439  PCMK_VALUE_STOP, NULL);
440 }
441 
451 static void
452 validate_on_fail(const pcmk_resource_t *rsc, const char *action_name,
453  const xmlNode *action_config, GHashTable *meta)
454 {
455  const char *name = NULL;
456  const char *role = NULL;
457  const char *interval_spec = NULL;
458  const char *value = g_hash_table_lookup(meta, PCMK_META_ON_FAIL);
459  guint interval_ms = 0U;
460 
461  // 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,
521  pcmk__str_casei)) {
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)
549  PCMK__ROLE_PROMOTED_LEGACY, NULL))) {
550 
551  pcmk__config_err("Resetting '" PCMK_META_ON_FAIL "' for %s %s "
552  "action to default value because 'demote' is not "
553  "allowed for it", rsc->id, name);
554  g_hash_table_remove(meta, PCMK_META_ON_FAIL);
555  return;
556  }
557  }
558 }
559 
560 static int
561 unpack_timeout(const char *value)
562 {
563  long long timeout_ms = crm_get_msec(value);
564 
565  if (timeout_ms <= 0) {
566  timeout_ms = PCMK_DEFAULT_ACTION_TIMEOUT_MS;
567  }
568  return (int) QB_MIN(timeout_ms, INT_MAX);
569 }
570 
571 // true if value contains valid, non-NULL interval origin for recurring op
572 static bool
573 unpack_interval_origin(const char *value, const xmlNode *xml_obj,
574  guint interval_ms, const crm_time_t *now,
575  long long *start_delay)
576 {
577  long long result = 0;
578  guint interval_sec = interval_ms / 1000;
579  crm_time_t *origin = NULL;
580 
581  // 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 
613 static int
614 unpack_start_delay(const char *value, GHashTable *meta)
615 {
616  long long start_delay_ms = 0;
617 
618  if (value == NULL) {
619  return 0;
620  }
621 
622  start_delay_ms = crm_get_msec(value);
623  start_delay_ms = QB_MIN(start_delay_ms, INT_MAX);
624  if (start_delay_ms < 0) {
625  start_delay_ms = 0;
626  }
627 
628  if (meta != NULL) {
629  g_hash_table_replace(meta, strdup(PCMK_META_START_DELAY),
630  pcmk__itoa(start_delay_ms));
631  }
632 
633  return (int) start_delay_ms;
634 }
635 
645 static xmlNode *
646 most_frequent_monitor(const pcmk_resource_t *rsc)
647 {
648  guint min_interval_ms = G_MAXUINT;
649  xmlNode *op = NULL;
650 
651  for (xmlNode *operation = pcmk__xe_first_child(rsc->ops_xml, PCMK_XE_OP,
652  NULL, NULL);
653  operation != NULL; operation = pcmk__xe_next_same(operation)) {
654 
655  bool enabled = false;
656  guint interval_ms = 0U;
657  const char *interval_spec = crm_element_value(operation,
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 
701 GHashTable *
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 
852 pcmk__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 
890 pcmk__parse_on_fail(const pcmk_resource_t *rsc, const char *action_name,
891  guint interval_ms, const char *value)
892 {
893  const char *desc = NULL;
894  bool needs_remote_reset = false;
896 
897  // There's no enum value for unknown or invalid, so assert
898  pcmk__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)) {
921  on_fail = pcmk_on_fail_standby_node;
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,
942  pcmk__str_casei)) {
943  if (rsc->container == NULL) {
944  pcmk__rsc_debug(rsc,
945  "Using default " PCMK_META_ON_FAIL " for %s "
946  "of %s because it does not have a container",
947  action_name, rsc->id);
948  } else {
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) {
986  if (pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
987  if (pcmk_is_set(rsc->cluster->flags,
989  desc = "fence remote node (default)";
990  } else {
991  desc = "recover remote node connection (default)";
992  }
993  on_fail = pcmk_on_fail_reset_remote;
994  } else {
995  on_fail = pcmk_on_fail_stop;
996  desc = "stop unmanaged remote node (enforcing default)";
997  }
998 
999  } else if (pcmk__str_eq(action_name, PCMK_ACTION_STOP, pcmk__str_none)) {
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 
1030 enum rsc_role_e
1031 pcmk__role_after_failure(const pcmk_resource_t *rsc, const char *action_name,
1032  enum action_fail_response on_fail, GHashTable *meta)
1033 {
1034  const char *value = NULL;
1035  enum rsc_role_e role = pcmk_role_unknown;
1036 
1037  // 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 
1094 static void
1095 unpack_operation(pcmk_action_t *action, const xmlNode *xml_obj,
1096  guint interval_ms)
1097 {
1098  const char *value = NULL;
1099 
1100  action->meta = pcmk__unpack_action_meta(action->rsc, action->node,
1101  action->task, interval_ms, xml_obj);
1102  action->needs = pcmk__action_requires(action->rsc, action->task);
1103 
1104  value = g_hash_table_lookup(action->meta, PCMK_META_ON_FAIL);
1105  action->on_fail = pcmk__parse_on_fail(action->rsc, action->task,
1106  interval_ms, value);
1107 
1108  action->fail_role = pcmk__role_after_failure(action->rsc, action->task,
1109  action->on_fail, action->meta);
1110 }
1111 
1128 pcmk_action_t *
1129 custom_action(pcmk_resource_t *rsc, char *key, const char *task,
1130  const pcmk_node_t *on_node, gboolean optional,
1132 {
1133  pcmk_action_t *action = NULL;
1134 
1135  pcmk__assert((key != NULL) && (task != NULL) && (scheduler != NULL));
1136 
1137  action = find_existing_action(key, rsc, on_node, scheduler);
1138  if (action == NULL) {
1139  action = new_action(key, task, rsc, on_node, optional, scheduler);
1140  } else {
1141  free(key);
1142  }
1143 
1144  update_action_optional(action, optional);
1145 
1146  if (rsc != NULL) {
1147  /* 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 
1176 pcmk_action_t *
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 
1188 static GList *
1189 find_unfencing_devices(GList *candidates, GList *matches)
1190 {
1191  for (GList *gIter = candidates; gIter != NULL; gIter = gIter->next) {
1192  pcmk_resource_t *candidate = gIter->data;
1193 
1194  if (candidate->children != NULL) {
1195  matches = find_unfencing_devices(candidate->children, matches);
1196 
1197  } else if (!pcmk_is_set(candidate->flags, pcmk_rsc_fence_device)) {
1198  continue;
1199 
1200  } else if (pcmk_is_set(candidate->flags, pcmk_rsc_needs_unfencing)) {
1201  matches = g_list_prepend(matches, candidate);
1202 
1203  } else if (pcmk__str_eq(g_hash_table_lookup(candidate->meta,
1206  matches = g_list_prepend(matches, candidate);
1207  }
1208  }
1209  return matches;
1210 }
1211 
1212 static int
1213 node_priority_fencing_delay(const pcmk_node_t *node,
1214  const pcmk_scheduler_t *scheduler)
1215 {
1216  int member_count = 0;
1217  int online_count = 0;
1218  int top_priority = 0;
1219  int lowest_priority = 0;
1220  GList *gIter = NULL;
1221 
1222  // PCMK_OPT_PRIORITY_FENCING_DELAY is disabled
1223  if (scheduler->priority_fencing_delay <= 0) {
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 
1241  if (n->details->type != pcmk_node_variant_cluster) {
1242  continue;
1243  }
1244 
1245  member_count ++;
1246 
1247  if (n->details->online) {
1248  online_count++;
1249  }
1250 
1251  if (member_count == 1
1252  || n->details->priority > top_priority) {
1253  top_priority = n->details->priority;
1254  }
1255 
1256  if (member_count == 1
1257  || n->details->priority < lowest_priority) {
1258  lowest_priority = n->details->priority;
1259  }
1260  }
1261 
1262  // 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 
1280 pcmk_action_t *
1281 pe_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) {
1289  op = scheduler->stonith_action;
1290  }
1291 
1292  op_key = crm_strdup_printf("%s-%s-%s",
1293  PCMK_ACTION_STONITH, node->details->uname, op);
1294 
1295  stonith_op = lookup_singleton(scheduler, op_key);
1296  if(stonith_op == NULL) {
1297  stonith_op = custom_action(NULL, op_key, PCMK_ACTION_STONITH, node,
1298  TRUE, scheduler);
1299 
1300  pcmk__insert_meta(stonith_op, PCMK__META_ON_NODE, node->details->uname);
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) {
1326  pcmk__output_t *out = scheduler->priv;
1327 
1328  out->info(out,
1329  "notice: Unfencing node %s because the "
1330  "definition of %s changed",
1331  pcmk__node_name(node), match->id);
1332  }
1333  }
1334 
1335  pcmk__g_strcat(digests_all,
1336  match->id, ":", agent, ":",
1337  data->digest_all_calc, ",", NULL);
1338  pcmk__g_strcat(digests_secure,
1339  match->id, ":", agent, ":",
1340  data->digest_secure_calc, ",", NULL);
1341  }
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  g_list_free(matches);
1351  }
1352 
1353  } else {
1354  free(op_key);
1355  }
1356 
1358 
1359  /* It's a suitable case where PCMK_OPT_PRIORITY_FENCING_DELAY
1360  * applies. At least add PCMK_OPT_PRIORITY_FENCING_DELAY field as
1361  * an indicator.
1362  */
1363  && (priority_delay
1364 
1365  /* The priority delay needs to be recalculated if this function has
1366  * been called by schedule_fencing_and_shutdowns() after node
1367  * priority has already been calculated by native_add_running().
1368  */
1369  || g_hash_table_lookup(stonith_op->meta,
1370  PCMK_OPT_PRIORITY_FENCING_DELAY) != NULL)) {
1371 
1372  /* Add PCMK_OPT_PRIORITY_FENCING_DELAY to the fencing op even if
1373  * it's 0 for the targeting node. So that it takes precedence over
1374  * any possible `pcmk_delay_base/max`.
1375  */
1376  char *delay_s = pcmk__itoa(node_priority_fencing_delay(node,
1377  scheduler));
1378 
1379  g_hash_table_insert(stonith_op->meta,
1381  delay_s);
1382  }
1383 
1384  if(optional == FALSE && pe_can_fence(scheduler, node)) {
1386  pe_action_set_reason(stonith_op, reason, false);
1387 
1388  } else if(reason && stonith_op->reason == NULL) {
1389  stonith_op->reason = strdup(reason);
1390  }
1391 
1392  return stonith_op;
1393 }
1394 
1395 void
1397 {
1398  if (action == NULL) {
1399  return;
1400  }
1401  g_list_free_full(action->actions_before, free);
1402  g_list_free_full(action->actions_after, free);
1403  if (action->extra) {
1404  g_hash_table_destroy(action->extra);
1405  }
1406  if (action->meta) {
1407  g_hash_table_destroy(action->meta);
1408  }
1409  free(action->cancel_task);
1410  free(action->reason);
1411  free(action->task);
1412  free(action->uuid);
1413  free(action->node);
1414  free(action);
1415 }
1416 
1417 enum action_tasks
1418 get_complex_task(const pcmk_resource_t *rsc, const char *name)
1419 {
1420  enum action_tasks task = pcmk_parse_action(name);
1421 
1422  if (pcmk__is_primitive(rsc)) {
1423  switch (task) {
1424  case pcmk_action_stopped:
1425  case pcmk_action_started:
1426  case pcmk_action_demoted:
1427  case pcmk_action_promoted:
1428  crm_trace("Folding %s back into its atomic counterpart for %s",
1429  name, rsc->id);
1430  --task;
1431  break;
1432  default:
1433  break;
1434  }
1435  }
1436  return task;
1437 }
1438 
1450 pcmk_action_t *
1451 find_first_action(const GList *input, const char *uuid, const char *task,
1452  const pcmk_node_t *on_node)
1453 {
1454  CRM_CHECK(uuid || task, return NULL);
1455 
1456  for (const GList *gIter = input; gIter != NULL; gIter = gIter->next) {
1457  pcmk_action_t *action = (pcmk_action_t *) gIter->data;
1458 
1459  if (uuid != NULL && !pcmk__str_eq(uuid, action->uuid, pcmk__str_casei)) {
1460  continue;
1461 
1462  } else if (task != NULL && !pcmk__str_eq(task, action->task, pcmk__str_casei)) {
1463  continue;
1464 
1465  } else if (on_node == NULL) {
1466  return action;
1467 
1468  } else if (action->node == NULL) {
1469  continue;
1470 
1471  } else if (pcmk__same_node(on_node, action->node)) {
1472  return action;
1473  }
1474  }
1475 
1476  return NULL;
1477 }
1478 
1479 GList *
1480 find_actions(GList *input, const char *key, const pcmk_node_t *on_node)
1481 {
1482  GList *gIter = input;
1483  GList *result = NULL;
1484 
1485  CRM_CHECK(key != NULL, return NULL);
1486 
1487  for (; gIter != NULL; gIter = gIter->next) {
1488  pcmk_action_t *action = (pcmk_action_t *) gIter->data;
1489 
1490  if (!pcmk__str_eq(key, action->uuid, pcmk__str_casei)) {
1491  continue;
1492 
1493  } else if (on_node == NULL) {
1494  crm_trace("Action %s matches (ignoring node)", key);
1495  result = g_list_prepend(result, action);
1496 
1497  } else if (action->node == NULL) {
1498  crm_trace("Action %s matches (unallocated, assigning to %s)",
1499  key, pcmk__node_name(on_node));
1500 
1501  action->node = pe__copy_node(on_node);
1502  result = g_list_prepend(result, action);
1503 
1504  } else if (pcmk__same_node(on_node, action->node)) {
1505  crm_trace("Action %s on %s matches", key, pcmk__node_name(on_node));
1506  result = g_list_prepend(result, action);
1507  }
1508  }
1509 
1510  return result;
1511 }
1512 
1513 GList *
1514 find_actions_exact(GList *input, const char *key, const pcmk_node_t *on_node)
1515 {
1516  GList *result = NULL;
1517 
1518  CRM_CHECK(key != NULL, return NULL);
1519 
1520  if (on_node == NULL) {
1521  return NULL;
1522  }
1523 
1524  for (GList *gIter = input; gIter != NULL; gIter = gIter->next) {
1525  pcmk_action_t *action = (pcmk_action_t *) gIter->data;
1526 
1527  if ((action->node != NULL)
1528  && pcmk__str_eq(key, action->uuid, pcmk__str_casei)
1529  && pcmk__str_eq(on_node->details->id, action->node->details->id,
1530  pcmk__str_casei)) {
1531 
1532  crm_trace("Action %s on %s matches", key, pcmk__node_name(on_node));
1533  result = g_list_prepend(result, action);
1534  }
1535  }
1536 
1537  return result;
1538 }
1539 
1552 GList *
1554  const char *task, bool require_node)
1555 {
1556  GList *result = NULL;
1557  char *key = pcmk__op_key(rsc->id, task, 0);
1558 
1559  if (require_node) {
1560  result = find_actions_exact(rsc->actions, key, node);
1561  } else {
1562  result = find_actions(rsc->actions, key, node);
1563  }
1564  free(key);
1565  return result;
1566 }
1567 
1578 char *
1580 {
1581  const char *change = NULL;
1582 
1583  switch (flag) {
1584  case pcmk_action_runnable:
1585  change = "unrunnable";
1586  break;
1588  change = "unmigrateable";
1589  break;
1590  case pcmk_action_optional:
1591  change = "required";
1592  break;
1593  default:
1594  // Bug: caller passed unsupported flag
1595  CRM_CHECK(change != NULL, change = "");
1596  break;
1597  }
1598  return crm_strdup_printf("%s%s%s %s", change,
1599  (action->rsc == NULL)? "" : " ",
1600  (action->rsc == NULL)? "" : action->rsc->id,
1601  action->task);
1602 }
1603 
1604 void pe_action_set_reason(pcmk_action_t *action, const char *reason,
1605  bool overwrite)
1606 {
1607  if (action->reason != NULL && overwrite) {
1608  pcmk__rsc_trace(action->rsc, "Changing %s reason from '%s' to '%s'",
1609  action->uuid, action->reason,
1610  pcmk__s(reason, "(none)"));
1611  } else if (action->reason == NULL) {
1612  pcmk__rsc_trace(action->rsc, "Set %s reason to '%s'",
1613  action->uuid, pcmk__s(reason, "(none)"));
1614  } else {
1615  // crm_assert(action->reason != NULL && !overwrite);
1616  return;
1617  }
1618 
1619  pcmk__str_update(&action->reason, reason);
1620 }
1621 
1629 void
1631 {
1632  pcmk__assert((rsc != NULL) && (node != NULL));
1633 
1635  PCMK_ACTION_LRM_DELETE, node, FALSE, rsc->cluster);
1636 }
1637 
1638 #define sort_return(an_int, why) do { \
1639  free(a_uuid); \
1640  free(b_uuid); \
1641  crm_trace("%s (%d) %c %s (%d) : %s", \
1642  a_xml_id, a_call_id, an_int>0?'>':an_int<0?'<':'=', \
1643  b_xml_id, b_call_id, why); \
1644  return an_int; \
1645  } while(0)
1646 
1647 int
1648 pe__is_newer_op(const xmlNode *xml_a, const xmlNode *xml_b,
1649  bool same_node_default)
1650 {
1651  int a_call_id = -1;
1652  int b_call_id = -1;
1653 
1654  char *a_uuid = NULL;
1655  char *b_uuid = NULL;
1656 
1657  const char *a_xml_id = crm_element_value(xml_a, PCMK_XA_ID);
1658  const char *b_xml_id = crm_element_value(xml_b, PCMK_XA_ID);
1659 
1660  const char *a_node = crm_element_value(xml_a, PCMK__META_ON_NODE);
1661  const char *b_node = crm_element_value(xml_b, PCMK__META_ON_NODE);
1662  bool same_node = true;
1663 
1664  /* @COMPAT The on_node attribute was added to last_failure as of 1.1.13 (via
1665  * 8b3ca1c) and the other entries as of 1.1.12 (via 0b07b5c).
1666  *
1667  * In case that any of the PCMK__XE_LRM_RSC_OP entries doesn't have on_node
1668  * attribute, we need to explicitly tell whether the two operations are on
1669  * the same node.
1670  */
1671  if (a_node == NULL || b_node == NULL) {
1672  same_node = same_node_default;
1673 
1674  } else {
1675  same_node = pcmk__str_eq(a_node, b_node, pcmk__str_casei);
1676  }
1677 
1678  if (same_node && pcmk__str_eq(a_xml_id, b_xml_id, pcmk__str_none)) {
1679  /* We have duplicate PCMK__XE_LRM_RSC_OP entries in the status
1680  * section which is unlikely to be a good thing
1681  * - we can handle it easily enough, but we need to get
1682  * to the bottom of why it's happening.
1683  */
1684  pcmk__config_err("Duplicate " PCMK__XE_LRM_RSC_OP " entries named %s",
1685  a_xml_id);
1686  sort_return(0, "duplicate");
1687  }
1688 
1689  crm_element_value_int(xml_a, PCMK__XA_CALL_ID, &a_call_id);
1690  crm_element_value_int(xml_b, PCMK__XA_CALL_ID, &b_call_id);
1691 
1692  if (a_call_id == -1 && b_call_id == -1) {
1693  /* both are pending ops so it doesn't matter since
1694  * stops are never pending
1695  */
1696  sort_return(0, "pending");
1697 
1698  } else if (same_node && a_call_id >= 0 && a_call_id < b_call_id) {
1699  sort_return(-1, "call id");
1700 
1701  } else if (same_node && b_call_id >= 0 && a_call_id > b_call_id) {
1702  sort_return(1, "call id");
1703 
1704  } else if (a_call_id >= 0 && b_call_id >= 0
1705  && (!same_node || a_call_id == b_call_id)) {
1706  /* The op and last_failed_op are the same. Order on
1707  * PCMK_XA_LAST_RC_CHANGE.
1708  */
1709  time_t last_a = -1;
1710  time_t last_b = -1;
1711 
1714 
1715  crm_trace("rc-change: %lld vs %lld",
1716  (long long) last_a, (long long) last_b);
1717  if (last_a >= 0 && last_a < last_b) {
1718  sort_return(-1, "rc-change");
1719 
1720  } else if (last_b >= 0 && last_a > last_b) {
1721  sort_return(1, "rc-change");
1722  }
1723  sort_return(0, "rc-change");
1724 
1725  } else {
1726  /* One of the inputs is a pending operation.
1727  * Attempt to use PCMK__XA_TRANSITION_MAGIC to determine its age relative
1728  * to the other.
1729  */
1730 
1731  int a_id = -1;
1732  int b_id = -1;
1733 
1734  const char *a_magic = crm_element_value(xml_a,
1736  const char *b_magic = crm_element_value(xml_b,
1738 
1739  CRM_CHECK(a_magic != NULL && b_magic != NULL, sort_return(0, "No magic"));
1740  if (!decode_transition_magic(a_magic, &a_uuid, &a_id, NULL, NULL, NULL,
1741  NULL)) {
1742  sort_return(0, "bad magic a");
1743  }
1744  if (!decode_transition_magic(b_magic, &b_uuid, &b_id, NULL, NULL, NULL,
1745  NULL)) {
1746  sort_return(0, "bad magic b");
1747  }
1748  /* try to determine the relative age of the operation...
1749  * some pending operations (e.g. a start) may have been superseded
1750  * by a subsequent stop
1751  *
1752  * [a|b]_id == -1 means it's a shutdown operation and _always_ comes last
1753  */
1754  if (!pcmk__str_eq(a_uuid, b_uuid, pcmk__str_casei) || a_id == b_id) {
1755  /*
1756  * some of the logic in here may be redundant...
1757  *
1758  * if the UUID from the TE doesn't match then one better
1759  * be a pending operation.
1760  * pending operations don't survive between elections and joins
1761  * because we query the LRM directly
1762  */
1763 
1764  if (b_call_id == -1) {
1765  sort_return(-1, "transition + call");
1766 
1767  } else if (a_call_id == -1) {
1768  sort_return(1, "transition + call");
1769  }
1770 
1771  } else if ((a_id >= 0 && a_id < b_id) || b_id == -1) {
1772  sort_return(-1, "transition");
1773 
1774  } else if ((b_id >= 0 && a_id > b_id) || a_id == -1) {
1775  sort_return(1, "transition");
1776  }
1777  }
1778 
1779  /* we should never end up here */
1780  CRM_CHECK(FALSE, sort_return(0, "default"));
1781 }
1782 
1783 gint
1784 sort_op_by_callid(gconstpointer a, gconstpointer b)
1785 {
1786  const xmlNode *xml_a = a;
1787  const xmlNode *xml_b = b;
1788 
1789  return pe__is_newer_op(xml_a, xml_b, true);
1790 }
1791 
1803 pcmk_action_t *
1804 pe__new_rsc_pseudo_action(pcmk_resource_t *rsc, const char *task, bool optional,
1805  bool runnable)
1806 {
1807  pcmk_action_t *action = NULL;
1808 
1809  pcmk__assert((rsc != NULL) && (task != NULL));
1810 
1811  action = custom_action(rsc, pcmk__op_key(rsc->id, task, 0), task, NULL,
1812  optional, rsc->cluster);
1814  if (runnable) {
1816  }
1817  return action;
1818 }
1819 
1829 void
1831 {
1832  pcmk__assert((action != NULL) && (action->meta != NULL));
1833 
1834  g_hash_table_insert(action->meta, pcmk__str_copy(PCMK__META_OP_TARGET_RC),
1835  pcmk__itoa(expected_result));
1836 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:245
bool pe_can_fence(const pcmk_scheduler_t *scheduler, const pcmk_node_t *node)
Definition: utils.c:36
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_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.
Definition: pe_actions.c:1129
enum pe_quorum_policy no_quorum_policy
Definition: scheduler.h:217
A dumping ground.
pcmk_node_t * pe__copy_node(const pcmk_node_t *this_node)
Definition: utils.c:89
#define crm_notice(fmt, args...)
Definition: logging.h:397
pcmk_scheduler_t * cluster
Definition: resources.h:408
xmlNode * ops_xml
Definition: resources.h:406
GHashTable * attrs
Definition: nodes.h:143
#define PCMK_XA_NAME
Definition: xml_names.h:330
const char * pcmk_role_text(enum rsc_role_e role)
Get readable description of a resource role.
Definition: roles.c:23
char data[0]
Definition: cpg.c:58
#define PCMK__META_DIGESTS_SECURE
void pe__add_action_expected_result(pcmk_action_t *action, int expected_result)
Definition: pe_actions.c:1830
enum action_tasks pcmk_parse_action(const char *action_name)
Parse an action type from an action name.
Definition: actions.c:92
enum rsc_start_requirement pcmk__action_requires(const pcmk_resource_t *rsc, const char *action_name)
Definition: pe_actions.c:852
#define PCMK_META_INTERVAL_ORIGIN
Definition: options.h:93
GList * find_actions(GList *input, const char *key, const pcmk_node_t *on_node)
Definition: pe_actions.c:1480
Stopped.
Definition: roles.h:36
#define PCMK_STONITH_PROVIDES
Definition: agents.h:48
const char * name
Definition: cib.c:26
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1038
#define PCMK__XA_TRANSITION_MAGIC
struct crm_time_s crm_time_t
Definition: iso8601.h:32
enum rsc_role_e role
Definition: resources.h:464
GList * children
Definition: resources.h:471
#define pcmk__rsc_trace(rsc, fmt, args...)
int priority_fencing_delay
Definition: scheduler.h:261
xmlNode * op_defaults
Definition: scheduler.h:241
xmlNode * xml
Definition: resources.h:400
enum rsc_role_e next_role
Definition: resources.h:465
#define PCMK__META_OP_TARGET_RC
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__META_ROLE_AFTER_FAILURE
#define PCMK_VALUE_STANDBY
Definition: options.h:205
#define pcmk__insert_meta(obj, name, value)
#define pcmk__config_err(fmt...)
#define PCMK_ACTION_MONITOR
Definition: actions.h:60
long long crm_get_msec(const char *input)
Parse a time+units string and return milliseconds equivalent.
Definition: strings.c:361
GHashTable * meta
Definition: resources.h:467
#define PCMK_VALUE_RESTART
Definition: options.h:199
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)
Definition: pe_actions.c:1281
#define PCMK_ACTION_MIGRATE_TO
Definition: actions.h:59
#define PCMK_XA_PROVIDER
Definition: xml_names.h:364
Promoted.
Definition: roles.h:39
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
action_tasks
Definition: actions.h:83
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:362
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
#define PCMK__META_STONITH_ACTION
#define PCMK_VALUE_BLOCK
Definition: options.h:135
GList * actions
Definition: scheduler.h:239
action_fail_response
Definition: actions.h:130
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:228
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
#define PCMK__META_ON_NODE
char * pe__action2reason(const pcmk_action_t *action, enum pe_action_flags flag)
Definition: pe_actions.c:1579
pcmk_resource_t * container
Definition: resources.h:476
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition: nvpair.c:494
#define PCMK_XA_TYPE
Definition: xml_names.h:430
pcmk_action_t * get_pseudo_op(const char *name, pcmk_scheduler_t *scheduler)
Definition: pe_actions.c:1177
char * reason
Definition: actions.h:346
const char * action
Definition: pcmk_fence.c:30
enum rsc_role_e pcmk_parse_role(const char *role)
Parse a resource role from a string role specification.
Definition: roles.c:59
pe_quorum_policy
Possible responses to loss of quorum.
Definition: scheduler.h:40
GList * resources
Definition: scheduler.h:231
rsc_start_requirement
Definition: resources.h:52
#define pcmk__rsc_debug(rsc, fmt, args...)
enum action_tasks get_complex_task(const pcmk_resource_t *rsc, const char *name)
Definition: pe_actions.c:1418
#define PCMK_VALUE_NOTHING
Definition: options.h:181
char int pcmk_parse_interval_spec(const char *input, guint *result_ms)
Parse milliseconds from a Pacemaker interval specification.
Definition: strings.c:462
#define PCMK__ROLE_PROMOTED_LEGACY
#define PCMK_DEFAULT_ACTION_TIMEOUT_MS
Default timeout (in milliseconds) for non-metadata actions.
Definition: actions.h:38
#define PCMK_ACTION_DEMOTE
Definition: actions.h:49
guint remote_reconnect_ms
Definition: resources.h:423
void pe__set_next_role(pcmk_resource_t *rsc, enum rsc_role_e role, const char *why)
Definition: complex.c:1292
#define PCMK_VALUE_FENCE
Definition: options.h:153
void pe__clear_resource_history(pcmk_resource_t *rsc, const pcmk_node_t *node)
Definition: pe_actions.c:1630
#define pcmk__clear_action_flags(action, flags_to_clear)
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:458
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:481
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)
Definition: pe_actions.c:1031
#define PCMK_META_START_DELAY
Definition: options.h:112
#define crm_trace(fmt, args...)
Definition: logging.h:404
#define do_crm_log(level, fmt, args...)
Log a message.
Definition: logging.h:181
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1308
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
GHashTable * meta
Definition: actions.h:354
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:94
const char * stonith_action
Definition: scheduler.h:205
struct pe_node_shared_s * details
Definition: nodes.h:168
pcmk_action_t * find_first_action(const GList *input, const char *uuid, const char *task, const pcmk_node_t *on_node)
Definition: pe_actions.c:1451
#define PCMK_ACTION_START
Definition: actions.h:72
#define PCMK__META_ON_NODE_UUID
const char * op_name
Definition: common.h:41
int pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
Definition: nvpair.c:993
bool pcmk__is_daemon
Definition: logging.c:47
unsigned long long flags
Definition: resources.h:428
const char * uname
Definition: nodes.h:74
#define PCMK_VALUE_IGNORE
Definition: options.h:161
Unpromoted.
Definition: roles.h:38
#define PCMK_OPT_PRIORITY_FENCING_DELAY
Definition: options.h:58
void pcmk__str_update(char **str, const char *value)
Definition: strings.c:1289
Wrappers for and extensions to libxml2.
rsc_role_e
Definition: roles.h:34
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
#define PCMK_META_ENABLED
Definition: options.h:87
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:651
#define PCMK_ACTION_STOP
Definition: actions.h:75
GList * actions
Definition: resources.h:444
pcmk_action_t * pe__new_rsc_pseudo_action(pcmk_resource_t *rsc, const char *task, bool optional, bool runnable)
Definition: pe_actions.c:1804
#define PCMK_ACTION_STONITH
Definition: actions.h:74
#define PCMK__META_DIGESTS_ALL
#define PCMK_XA_ID
Definition: xml_names.h:301
pe_action_flags
Definition: actions.h:199
#define pcmk__set_rsc_flags(resource, 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
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1062
long long crm_time_get_seconds(const crm_time_t *dt)
Definition: iso8601.c:331
#define pcmk__str_copy(str)
#define pcmk__warn_once(wo_flag, fmt...)
void pe_action_set_reason(pcmk_action_t *action, const char *reason, bool overwrite)
Definition: pe_actions.c:1604
#define PCMK_META_TIMEOUT
Definition: options.h:114
const char * id
Definition: nodes.h:73
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
#define PCMK_XE_META_ATTRIBUTES
Definition: xml_names.h:130
#define pcmk__assert(expr)
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.
Definition: pe_actions.c:1553
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
#define PCMK_ACTION_LRM_DELETE
Definition: actions.h:53
#define PCMK_XE_OP
Definition: xml_names.h:146
#define PCMK_XA_CLASS
Definition: xml_names.h:246
gboolean is_remote_node
Definition: resources.h:431
#define PCMK_META_INTERVAL
Definition: options.h:91
#define PCMK_XA_LAST_RC_CHANGE
Definition: xml_names.h:316
const char * standard
Definition: common.h:35
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:695
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
#define PCMK_ROLE_PROMOTED
Definition: roles.h:28
pcmk_scheduler_t * scheduler
#define PCMK__XE_LRM_RSC_OP
GHashTable * node_hash
Definition: common.h:46
xmlNode * input
crm_time_t * crm_time_new(const char *string)
Definition: iso8601.c:112
#define PCMK_ACTION_MIGRATE_FROM
Definition: actions.h:58
#define PCMK_META_ON_FAIL
Definition: options.h:98
Started.
Definition: roles.h:37
GList * find_actions_exact(GList *input, const char *key, const pcmk_node_t *on_node)
Definition: pe_actions.c:1514
This structure contains everything that makes up a single output formatter.
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition: actions.c:249
#define PCMK_ACTION_PROMOTE
Definition: actions.h:66
#define PCMK_OPT_NO_QUORUM_POLICY
Definition: options.h:46
#define PCMK__XA_CALL_ID
#define pcmk__set_action_flags(action, flags_to_set)
GHashTable * pcmk__unpack_action_rsc_params(const xmlNode *action_xml, GHashTable *node_attrs, pcmk_scheduler_t *scheduler)
Definition: pe_actions.c:241
#define PCMK_VALUE_STOP
Definition: options.h:209
#define sort_return(an_int, why)
Definition: pe_actions.c:1638
const char * pcmk__readable_interval(guint interval_ms)
Definition: iso8601.c:2206
void pe_free_action(pcmk_action_t *action)
Definition: pe_actions.c:1396
uint32_t pcmk_get_ra_caps(const char *standard)
Get capabilities of a resource agent standard.
Definition: agents.c:31
#define PCMK_VALUE_UNFENCING
Definition: options.h:216
int pe__is_newer_op(const xmlNode *xml_a, const xmlNode *xml_b, bool same_node_default)
Definition: pe_actions.c:1648
Resource role is unknown.
Definition: roles.h:35
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
GHashTable * singletons
Definition: scheduler.h:225
unsigned long long flags
Definition: scheduler.h:211
gint sort_op_by_callid(gconstpointer a, gconstpointer b)
Definition: pe_actions.c:1784
#define pcmk__assert_alloc(nmemb, size)
Definition: internal.h:297
#define PCMK_XE_INSTANCE_ATTRIBUTES
Definition: xml_names.h:122
xmlNode * pcmk__xe_next_same(const xmlNode *node)
Definition: xml.c:2130
#define PCMK_XA_ROLE
Definition: xml_names.h:387
enum node_type type
Definition: nodes.h:75
crm_time_t * now
Definition: scheduler.h:198
#define crm_info(fmt, args...)
Definition: logging.h:399
#define pcmk__clear_rsc_flags(resource, flags_to_clear)
void pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
Definition: strings.c:713
#define PCMK_VALUE_DEMOTE
Definition: options.h:145
gboolean online
Definition: nodes.h:81
#define PCMK_VALUE_RESTART_CONTAINER
Definition: options.h:200
#define PCMK_ACTION_NOTIFY
Definition: actions.h:62
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:150