pacemaker  3.0.0-d8340737c4
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->priv->singletons == NULL) {
30  }
31  g_hash_table_insert(scheduler->priv->singletons, action->uuid, action);
32 }
33 
34 static pcmk_action_t *
35 lookup_singleton(pcmk_scheduler_t *scheduler, const char *action_uuid)
36 {
37  /* @TODO This is the only use of the pcmk_scheduler_t:singletons hash table.
38  * Compare the performance of this approach to keeping the
39  * pcmk_scheduler_t:actions list sorted by action key and just searching
40  * that instead.
41  */
42  if (scheduler->priv->singletons == NULL) {
43  return NULL;
44  }
45  return g_hash_table_lookup(scheduler->priv->singletons, action_uuid);
46 }
47 
59 static pcmk_action_t *
60 find_existing_action(const char *key, const pcmk_resource_t *rsc,
61  const pcmk_node_t *node, const pcmk_scheduler_t *scheduler)
62 {
63  /* When rsc is NULL, it would be quicker to check
64  * scheduler->priv->singletons, but checking all scheduler->priv->actions
65  * takes the node into account.
66  */
67  GList *actions = (rsc == NULL)? scheduler->priv->actions : rsc->priv->actions;
68  GList *matches = find_actions(actions, key, node);
69  pcmk_action_t *action = NULL;
70 
71  if (matches == NULL) {
72  return NULL;
73  }
74  CRM_LOG_ASSERT(!pcmk__list_of_multiple(matches));
75 
76  action = matches->data;
77  g_list_free(matches);
78  return action;
79 }
80 
91 static xmlNode *
92 find_exact_action_config(const pcmk_resource_t *rsc, const char *action_name,
93  guint interval_ms, bool include_disabled)
94 {
95  for (xmlNode *operation = pcmk__xe_first_child(rsc->priv->ops_xml,
96  PCMK_XE_OP, NULL, NULL);
97  operation != NULL; operation = pcmk__xe_next(operation, PCMK_XE_OP)) {
98 
99  bool enabled = false;
100  const char *config_name = NULL;
101  const char *interval_spec = NULL;
102  guint tmp_ms = 0U;
103 
104  // @TODO This does not consider meta-attributes, rules, defaults, etc.
105  if (!include_disabled
107  &enabled) == pcmk_rc_ok) && !enabled) {
108  continue;
109  }
110 
111  interval_spec = crm_element_value(operation, PCMK_META_INTERVAL);
112  pcmk_parse_interval_spec(interval_spec, &tmp_ms);
113  if (tmp_ms != interval_ms) {
114  continue;
115  }
116 
117  config_name = crm_element_value(operation, PCMK_XA_NAME);
118  if (pcmk__str_eq(action_name, config_name, pcmk__str_none)) {
119  return operation;
120  }
121  }
122  return NULL;
123 }
124 
136 xmlNode *
137 pcmk__find_action_config(const pcmk_resource_t *rsc, const char *action_name,
138  guint interval_ms, bool include_disabled)
139 {
140  xmlNode *action_config = NULL;
141 
142  // Try requested action first
143  action_config = find_exact_action_config(rsc, action_name, interval_ms,
144  include_disabled);
145 
146  // For migrate_to and migrate_from actions, retry with "migrate"
147  // @TODO This should be either documented or deprecated
148  if ((action_config == NULL)
150  PCMK_ACTION_MIGRATE_FROM, NULL)) {
151  action_config = find_exact_action_config(rsc, "migrate", 0,
152  include_disabled);
153  }
154 
155  return action_config;
156 }
157 
173 static pcmk_action_t *
174 new_action(char *key, const char *task, pcmk_resource_t *rsc,
175  const pcmk_node_t *node, bool optional, pcmk_scheduler_t *scheduler)
176 {
178 
179  action->rsc = rsc;
180  action->task = pcmk__str_copy(task);
181  action->uuid = key;
182  action->scheduler = scheduler;
183 
184  if (node) {
185  action->node = pe__copy_node(node);
186  }
187 
188  if (pcmk__str_eq(task, PCMK_ACTION_LRM_DELETE, pcmk__str_casei)) {
189  // Resource history deletion for a node can be done on the DC
191  }
192 
194  if (optional) {
196  } else {
198  }
199 
200  if (rsc == NULL) {
201  action->meta = pcmk__strkey_table(free, free);
202  } else {
203  guint interval_ms = 0;
204 
205  parse_op_key(key, NULL, NULL, &interval_ms);
206  action->op_entry = pcmk__find_action_config(rsc, task, interval_ms,
207  true);
208 
209  /* If the given key is for one of the many notification pseudo-actions
210  * (pre_notify_promote, etc.), the actual action name is "notify"
211  */
212  if ((action->op_entry == NULL) && (strstr(key, "_notify_") != NULL)) {
213  action->op_entry = find_exact_action_config(rsc, PCMK_ACTION_NOTIFY,
214  0, true);
215  }
216 
217  unpack_operation(action, action->op_entry, interval_ms);
218  }
219 
220  pcmk__rsc_trace(rsc, "Created %s action %d (%s): %s for %s on %s",
221  (optional? "optional" : "required"),
222  scheduler->priv->next_action_id, key, task,
223  ((rsc == NULL)? "no resource" : rsc->id),
224  pcmk__node_name(node));
226 
227  scheduler->priv->actions = g_list_prepend(scheduler->priv->actions, action);
228  if (rsc == NULL) {
229  add_singleton(scheduler, action);
230  } else {
231  rsc->priv->actions = g_list_prepend(rsc->priv->actions, action);
232  }
233  return action;
234 }
235 
246 GHashTable *
247 pcmk__unpack_action_rsc_params(const xmlNode *action_xml,
248  GHashTable *node_attrs,
250 {
251  GHashTable *params = pcmk__strkey_table(free, free);
252 
253  pe_rule_eval_data_t rule_data = {
254  .node_hash = node_attrs,
255  .now = scheduler->priv->now,
256  .match_data = NULL,
257  .rsc_data = NULL,
258  .op_data = NULL
259  };
260 
262  &rule_data, params, NULL, scheduler);
263  return params;
264 }
265 
273 static void
274 update_action_optional(pcmk_action_t *action, gboolean optional)
275 {
276  // Force a non-recurring action to be optional if its resource is unmanaged
277  if ((action->rsc != NULL) && (action->node != NULL)
279  && !pcmk_is_set(action->rsc->flags, pcmk__rsc_managed)
280  && (g_hash_table_lookup(action->meta, PCMK_META_INTERVAL) == NULL)) {
281  pcmk__rsc_debug(action->rsc,
282  "%s on %s is optional (%s is unmanaged)",
283  action->uuid, pcmk__node_name(action->node),
284  action->rsc->id);
286  // We shouldn't clear runnable here because ... something
287 
288  // Otherwise require the action if requested
289  } else if (!optional) {
291  }
292 }
293 
294 static enum pe_quorum_policy
295 effective_quorum_policy(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
296 {
298 
300  policy = pcmk_no_quorum_ignore;
301 
303  switch (rsc->priv->orig_role) {
304  case pcmk_role_promoted:
306  if (rsc->priv->next_role > pcmk_role_unpromoted) {
308  PCMK_OPT_NO_QUORUM_POLICY "=demote");
309  }
310  policy = pcmk_no_quorum_ignore;
311  break;
312  default:
313  policy = pcmk_no_quorum_stop;
314  break;
315  }
316  }
317  return policy;
318 }
319 
329 static void
330 update_resource_action_runnable(pcmk_action_t *action,
332 {
333  pcmk_resource_t *rsc = action->rsc;
334 
335  if (pcmk_is_set(action->flags, pcmk__action_pseudo)) {
336  return;
337  }
338 
339  if (action->node == NULL) {
340  pcmk__rsc_trace(rsc, "%s is unrunnable (unallocated)", action->uuid);
342 
343  } else if (!pcmk_is_set(action->flags, pcmk__action_on_dc)
344  && !(action->node->details->online)
345  && (!pcmk__is_guest_or_bundle_node(action->node)
346  || pcmk_is_set(action->node->priv->flags,
349  do_crm_log(LOG_WARNING, "%s on %s is unrunnable (node is offline)",
350  action->uuid, pcmk__node_name(action->node));
352  && pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_casei)
353  && !(action->node->details->unclean)) {
354  pe_fence_node(scheduler, action->node, "stop is unrunnable", false);
355  }
356 
357  } else if (!pcmk_is_set(action->flags, pcmk__action_on_dc)
358  && action->node->details->pending) {
360  do_crm_log(LOG_WARNING,
361  "Action %s on %s is unrunnable (node is pending)",
362  action->uuid, pcmk__node_name(action->node));
363 
364  } else if (action->needs == pcmk__requires_nothing) {
365  pe_action_set_reason(action, NULL, TRUE);
366  if (pcmk__is_guest_or_bundle_node(action->node)
367  && !pe_can_fence(scheduler, action->node)) {
368  /* An action that requires nothing usually does not require any
369  * fencing in order to be runnable. However, there is an exception:
370  * such an action cannot be completed if it is on a guest node whose
371  * host is unclean and cannot be fenced.
372  */
373  pcmk__rsc_debug(rsc,
374  "%s on %s is unrunnable "
375  "(node's host cannot be fenced)",
376  action->uuid, pcmk__node_name(action->node));
378  } else {
379  pcmk__rsc_trace(rsc,
380  "%s on %s does not require fencing or quorum",
381  action->uuid, pcmk__node_name(action->node));
383  }
384 
385  } else {
386  switch (effective_quorum_policy(rsc, scheduler)) {
387  case pcmk_no_quorum_stop:
388  pcmk__rsc_debug(rsc, "%s on %s is unrunnable (no quorum)",
389  action->uuid, pcmk__node_name(action->node));
391  pe_action_set_reason(action, "no quorum", true);
392  break;
393 
395  if (!rsc->priv->fns->active(rsc, TRUE)
396  || (rsc->priv->next_role > rsc->priv->orig_role)) {
397  pcmk__rsc_debug(rsc, "%s on %s is unrunnable (no quorum)",
398  action->uuid,
399  pcmk__node_name(action->node));
401  pe_action_set_reason(action, "quorum freeze", true);
402  }
403  break;
404 
405  default:
406  //pe_action_set_reason(action, NULL, TRUE);
408  break;
409  }
410  }
411 }
412 
413 static bool
414 valid_stop_on_fail(const char *value)
415 {
416  return !pcmk__strcase_any_of(value,
418  PCMK_VALUE_STOP, NULL);
419 }
420 
430 static void
431 validate_on_fail(const pcmk_resource_t *rsc, const char *action_name,
432  const xmlNode *action_config, GHashTable *meta)
433 {
434  const char *name = NULL;
435  const char *role = NULL;
436  const char *interval_spec = NULL;
437  const char *value = g_hash_table_lookup(meta, PCMK_META_ON_FAIL);
438  guint interval_ms = 0U;
439 
440  // Stop actions can only use certain on-fail values
441  if (pcmk__str_eq(action_name, PCMK_ACTION_STOP, pcmk__str_none)
442  && !valid_stop_on_fail(value)) {
443 
444  pcmk__config_err("Resetting '" PCMK_META_ON_FAIL "' for %s stop "
445  "action to default value because '%s' is not "
446  "allowed for stop", rsc->id, value);
447  g_hash_table_remove(meta, PCMK_META_ON_FAIL);
448  return;
449  }
450 
451  /* Demote actions default on-fail to the on-fail value for the first
452  * recurring monitor for the promoted role (if any).
453  */
454  if (pcmk__str_eq(action_name, PCMK_ACTION_DEMOTE, pcmk__str_none)
455  && (value == NULL)) {
456 
457  /* @TODO This does not consider promote options set in a meta-attribute
458  * block (which may have rules that need to be evaluated) rather than
459  * XML properties.
460  */
461  for (xmlNode *operation = pcmk__xe_first_child(rsc->priv->ops_xml,
462  PCMK_XE_OP, NULL, NULL);
463  operation != NULL;
464  operation = pcmk__xe_next(operation, PCMK_XE_OP)) {
465 
466  bool enabled = false;
467  const char *promote_on_fail = NULL;
468 
469  /* We only care about explicit on-fail (if promote uses default, so
470  * can demote)
471  */
472  promote_on_fail = crm_element_value(operation, PCMK_META_ON_FAIL);
473  if (promote_on_fail == NULL) {
474  continue;
475  }
476 
477  // We only care about recurring monitors for the promoted role
478  name = crm_element_value(operation, PCMK_XA_NAME);
479  role = crm_element_value(operation, PCMK_XA_ROLE);
480  if (!pcmk__str_eq(name, PCMK_ACTION_MONITOR, pcmk__str_none)
483  continue;
484  }
485  interval_spec = crm_element_value(operation, PCMK_META_INTERVAL);
486  pcmk_parse_interval_spec(interval_spec, &interval_ms);
487  if (interval_ms == 0U) {
488  continue;
489  }
490 
491  // We only care about enabled monitors
493  &enabled) == pcmk_rc_ok) && !enabled) {
494  continue;
495  }
496 
497  /* Demote actions can't default to
498  * PCMK_META_ON_FAIL=PCMK_VALUE_DEMOTE
499  */
500  if (pcmk__str_eq(promote_on_fail, PCMK_VALUE_DEMOTE,
501  pcmk__str_casei)) {
502  continue;
503  }
504 
505  // Use value from first applicable promote action found
506  pcmk__insert_dup(meta, PCMK_META_ON_FAIL, promote_on_fail);
507  }
508  return;
509  }
510 
511  if (pcmk__str_eq(action_name, PCMK_ACTION_LRM_DELETE, pcmk__str_none)
512  && !pcmk__str_eq(value, PCMK_VALUE_IGNORE, pcmk__str_casei)) {
513 
515  return;
516  }
517 
518  // PCMK_META_ON_FAIL=PCMK_VALUE_DEMOTE is allowed only for certain actions
519  if (pcmk__str_eq(value, PCMK_VALUE_DEMOTE, pcmk__str_casei)) {
520  name = crm_element_value(action_config, PCMK_XA_NAME);
521  role = crm_element_value(action_config, PCMK_XA_ROLE);
522  interval_spec = crm_element_value(action_config, PCMK_META_INTERVAL);
523  pcmk_parse_interval_spec(interval_spec, &interval_ms);
524 
525  if (!pcmk__str_eq(name, PCMK_ACTION_PROMOTE, pcmk__str_none)
526  && ((interval_ms == 0U)
527  || !pcmk__str_eq(name, PCMK_ACTION_MONITOR, pcmk__str_none)
529  PCMK__ROLE_PROMOTED_LEGACY, NULL))) {
530 
531  pcmk__config_err("Resetting '" PCMK_META_ON_FAIL "' for %s %s "
532  "action to default value because 'demote' is not "
533  "allowed for it", rsc->id, name);
534  g_hash_table_remove(meta, PCMK_META_ON_FAIL);
535  return;
536  }
537  }
538 }
539 
540 static int
541 unpack_timeout(const char *value)
542 {
543  long long timeout_ms = crm_get_msec(value);
544 
545  if (timeout_ms <= 0) {
546  timeout_ms = PCMK_DEFAULT_ACTION_TIMEOUT_MS;
547  }
548  return (int) QB_MIN(timeout_ms, INT_MAX);
549 }
550 
551 // true if value contains valid, non-NULL interval origin for recurring op
552 static bool
553 unpack_interval_origin(const char *value, const xmlNode *xml_obj,
554  guint interval_ms, const crm_time_t *now,
555  long long *start_delay)
556 {
557  long long result = 0;
558  guint interval_sec = pcmk__timeout_ms2s(interval_ms);
559  crm_time_t *origin = NULL;
560 
561  // Ignore unspecified values and non-recurring operations
562  if ((value == NULL) || (interval_ms == 0) || (now == NULL)) {
563  return false;
564  }
565 
566  // Parse interval origin from text
567  origin = crm_time_new(value);
568  if (origin == NULL) {
569  pcmk__config_err("Ignoring '" PCMK_META_INTERVAL_ORIGIN "' for "
570  "operation '%s' because '%s' is not valid",
571  pcmk__s(pcmk__xe_id(xml_obj), "(missing ID)"), value);
572  return false;
573  }
574 
575  // Get seconds since origin (negative if origin is in the future)
577  crm_time_free(origin);
578 
579  // Calculate seconds from closest interval to now
580  result = result % interval_sec;
581 
582  // Calculate seconds remaining until next interval
583  result = ((result <= 0)? 0 : interval_sec) - result;
584  crm_info("Calculated a start delay of %llds for operation '%s'",
585  result, pcmk__s(pcmk__xe_id(xml_obj), "(unspecified)"));
586 
587  if (start_delay != NULL) {
588  *start_delay = result * 1000; // milliseconds
589  }
590  return true;
591 }
592 
593 static int
594 unpack_start_delay(const char *value, GHashTable *meta)
595 {
596  long long start_delay_ms = 0;
597 
598  if (value == NULL) {
599  return 0;
600  }
601 
602  start_delay_ms = crm_get_msec(value);
603  start_delay_ms = QB_MIN(start_delay_ms, INT_MAX);
604  if (start_delay_ms < 0) {
605  start_delay_ms = 0;
606  }
607 
608  if (meta != NULL) {
609  g_hash_table_replace(meta, strdup(PCMK_META_START_DELAY),
610  pcmk__itoa(start_delay_ms));
611  }
612 
613  return (int) start_delay_ms;
614 }
615 
625 static xmlNode *
626 most_frequent_monitor(const pcmk_resource_t *rsc)
627 {
628  guint min_interval_ms = G_MAXUINT;
629  xmlNode *op = NULL;
630 
631  for (xmlNode *operation = pcmk__xe_first_child(rsc->priv->ops_xml,
632  PCMK_XE_OP, NULL, NULL);
633  operation != NULL; operation = pcmk__xe_next(operation, PCMK_XE_OP)) {
634 
635  bool enabled = false;
636  guint interval_ms = 0U;
637  const char *interval_spec = crm_element_value(operation,
639 
640  // We only care about enabled recurring monitors
641  if (!pcmk__str_eq(crm_element_value(operation, PCMK_XA_NAME),
643  continue;
644  }
645 
646  pcmk_parse_interval_spec(interval_spec, &interval_ms);
647  if (interval_ms == 0U) {
648  continue;
649  }
650 
651  // @TODO This does not consider meta-attributes, rules, defaults, etc.
653  &enabled) == pcmk_rc_ok) && !enabled) {
654  continue;
655  }
656 
657  if (interval_ms < min_interval_ms) {
658  min_interval_ms = interval_ms;
659  op = operation;
660  }
661  }
662  return op;
663 }
664 
681 GHashTable *
683  const char *action_name, guint interval_ms,
684  const xmlNode *action_config)
685 {
686  GHashTable *meta = NULL;
687  const char *timeout_spec = NULL;
688  const char *str = NULL;
689 
690  pe_rsc_eval_data_t rsc_rule_data = {
692  .provider = crm_element_value(rsc->priv->xml, PCMK_XA_PROVIDER),
693  .agent = crm_element_value(rsc->priv->xml, PCMK_XA_TYPE),
694  };
695 
696  pe_op_eval_data_t op_rule_data = {
697  .op_name = action_name,
698  .interval = interval_ms,
699  };
700 
701  pe_rule_eval_data_t rule_data = {
702  /* Node attributes are not set because node expressions are not allowed
703  * for meta-attributes
704  */
705  .now = rsc->priv->scheduler->priv->now,
706  .match_data = NULL,
707  .rsc_data = &rsc_rule_data,
708  .op_data = &op_rule_data,
709  };
710 
711  meta = pcmk__strkey_table(free, free);
712 
713  if (action_config != NULL) {
714  // <op> <meta_attributes> take precedence over defaults
716  &rule_data, meta, NULL,
717  rsc->priv->scheduler);
718 
719  /* Anything set as an <op> XML property has highest precedence.
720  * This ensures we use the name and interval from the <op> tag.
721  * (See below for the only exception, fence device start/probe timeout.)
722  */
723  for (xmlAttrPtr attr = action_config->properties;
724  attr != NULL; attr = attr->next) {
725  pcmk__insert_dup(meta, (const char *) attr->name,
726  pcmk__xml_attr_value(attr));
727  }
728  }
729 
730  // Derive default timeout for probes from recurring monitor timeouts
731  if (pcmk_is_probe(action_name, interval_ms)
732  && (g_hash_table_lookup(meta, PCMK_META_TIMEOUT) == NULL)) {
733 
734  xmlNode *min_interval_mon = most_frequent_monitor(rsc);
735 
736  if (min_interval_mon != NULL) {
737  /* @TODO This does not consider timeouts set in
738  * PCMK_XE_META_ATTRIBUTES blocks (which may also have rules that
739  * need to be evaluated).
740  */
741  timeout_spec = crm_element_value(min_interval_mon,
743  if (timeout_spec != NULL) {
744  pcmk__rsc_trace(rsc,
745  "Setting default timeout for %s probe to "
746  "most frequent monitor's timeout '%s'",
747  rsc->id, timeout_spec);
748  pcmk__insert_dup(meta, PCMK_META_TIMEOUT, timeout_spec);
749  }
750  }
751  }
752 
753  // Cluster-wide <op_defaults> <meta_attributes>
755  PCMK_XE_META_ATTRIBUTES, &rule_data, meta, NULL,
756  rsc->priv->scheduler);
757 
758  g_hash_table_remove(meta, PCMK_XA_ID);
759 
760  // Normalize interval to milliseconds
761  if (interval_ms > 0) {
762  g_hash_table_insert(meta, pcmk__str_copy(PCMK_META_INTERVAL),
763  crm_strdup_printf("%u", interval_ms));
764  } else {
765  g_hash_table_remove(meta, PCMK_META_INTERVAL);
766  }
767 
768  /* Timeout order of precedence (highest to lowest):
769  * 1. pcmk_monitor_timeout resource parameter (only for starts and probes
770  * when rsc has pcmk_ra_cap_fence_params; this gets used for recurring
771  * monitors via the executor instead)
772  * 2. timeout configured in <op> (with <op timeout> taking precedence over
773  * <op> <meta_attributes>)
774  * 3. timeout configured in <op_defaults> <meta_attributes>
775  * 4. PCMK_DEFAULT_ACTION_TIMEOUT_MS
776  */
777 
778  // Check for pcmk_monitor_timeout
779  if (pcmk_is_set(pcmk_get_ra_caps(rsc_rule_data.standard),
781  && (pcmk__str_eq(action_name, PCMK_ACTION_START, pcmk__str_none)
782  || pcmk_is_probe(action_name, interval_ms))) {
783 
784  GHashTable *params = pe_rsc_params(rsc, node, rsc->priv->scheduler);
785 
786  timeout_spec = g_hash_table_lookup(params, "pcmk_monitor_timeout");
787  if (timeout_spec != NULL) {
788  pcmk__rsc_trace(rsc,
789  "Setting timeout for %s %s to "
790  "pcmk_monitor_timeout (%s)",
791  rsc->id, action_name, timeout_spec);
792  pcmk__insert_dup(meta, PCMK_META_TIMEOUT, timeout_spec);
793  }
794  }
795 
796  // Normalize timeout to positive milliseconds
797  timeout_spec = g_hash_table_lookup(meta, PCMK_META_TIMEOUT);
798  g_hash_table_insert(meta, pcmk__str_copy(PCMK_META_TIMEOUT),
799  pcmk__itoa(unpack_timeout(timeout_spec)));
800 
801  // Ensure on-fail has a valid value
802  validate_on_fail(rsc, action_name, action_config, meta);
803 
804  // Normalize PCMK_META_START_DELAY
805  str = g_hash_table_lookup(meta, PCMK_META_START_DELAY);
806  if (str != NULL) {
807  unpack_start_delay(str, meta);
808  } else {
809  long long start_delay = 0;
810 
811  str = g_hash_table_lookup(meta, PCMK_META_INTERVAL_ORIGIN);
812  if (unpack_interval_origin(str, action_config, interval_ms,
813  rsc->priv->scheduler->priv->now,
814  &start_delay)) {
815  g_hash_table_insert(meta, pcmk__str_copy(PCMK_META_START_DELAY),
816  crm_strdup_printf("%lld", start_delay));
817  }
818  }
819  return meta;
820 }
821 
831 enum pcmk__requires
832 pcmk__action_requires(const pcmk_resource_t *rsc, const char *action_name)
833 {
834  const char *value = NULL;
835  enum pcmk__requires requires = pcmk__requires_nothing;
836 
837  CRM_CHECK((rsc != NULL) && (action_name != NULL), return requires);
838 
839  if (!pcmk__strcase_any_of(action_name, PCMK_ACTION_START,
840  PCMK_ACTION_PROMOTE, NULL)) {
841  value = "nothing (not start or promote)";
842 
843  } else if (pcmk_is_set(rsc->flags, pcmk__rsc_needs_fencing)) {
844  requires = pcmk__requires_fencing;
845  value = "fencing";
846 
847  } else if (pcmk_is_set(rsc->flags, pcmk__rsc_needs_quorum)) {
848  requires = pcmk__requires_quorum;
849  value = "quorum";
850 
851  } else {
852  value = "nothing";
853  }
854  pcmk__rsc_trace(rsc, "%s of %s requires %s", action_name, rsc->id, value);
855  return requires;
856 }
857 
869 enum pcmk__on_fail
870 pcmk__parse_on_fail(const pcmk_resource_t *rsc, const char *action_name,
871  guint interval_ms, const char *value)
872 {
873  const char *desc = NULL;
874  bool needs_remote_reset = false;
875  enum pcmk__on_fail on_fail = pcmk__on_fail_ignore;
876  const pcmk_scheduler_t *scheduler = NULL;
877 
878  // There's no enum value for unknown or invalid, so assert
879  pcmk__assert((rsc != NULL) && (action_name != NULL));
880  scheduler = rsc->priv->scheduler;
881 
882  if (value == NULL) {
883  // Use default
884 
885  } else if (pcmk__str_eq(value, PCMK_VALUE_BLOCK, pcmk__str_casei)) {
886  on_fail = pcmk__on_fail_block;
887  desc = "block";
888 
889  } else if (pcmk__str_eq(value, PCMK_VALUE_FENCE, pcmk__str_casei)) {
891  on_fail = pcmk__on_fail_fence_node;
892  desc = "node fencing";
893  } else {
894  pcmk__config_err("Resetting '" PCMK_META_ON_FAIL "' for "
895  "%s of %s to 'stop' because 'fence' is not "
896  "valid when fencing is disabled",
897  action_name, rsc->id);
898  on_fail = pcmk__on_fail_stop;
899  desc = "stop resource";
900  }
901 
902  } else if (pcmk__str_eq(value, PCMK_VALUE_STANDBY, pcmk__str_casei)) {
903  on_fail = pcmk__on_fail_standby_node;
904  desc = "node standby";
905 
906  } else if (pcmk__strcase_any_of(value,
908  NULL)) {
909  desc = "ignore";
910 
911  } else if (pcmk__str_eq(value, "migrate", pcmk__str_casei)) {
912  on_fail = pcmk__on_fail_ban;
913  desc = "force migration";
914 
915  } else if (pcmk__str_eq(value, PCMK_VALUE_STOP, pcmk__str_casei)) {
916  on_fail = pcmk__on_fail_stop;
917  desc = "stop resource";
918 
919  } else if (pcmk__str_eq(value, PCMK_VALUE_RESTART, pcmk__str_casei)) {
920  on_fail = pcmk__on_fail_restart;
921  desc = "restart (and possibly migrate)";
922 
923  } else if (pcmk__str_eq(value, PCMK_VALUE_RESTART_CONTAINER,
924  pcmk__str_casei)) {
925  if (rsc->priv->launcher == NULL) {
926  pcmk__rsc_debug(rsc,
927  "Using default " PCMK_META_ON_FAIL " for %s "
928  "of %s because it does not have a launcher",
929  action_name, rsc->id);
930  } else {
932  desc = "restart container (and possibly migrate)";
933  }
934 
935  } else if (pcmk__str_eq(value, PCMK_VALUE_DEMOTE, pcmk__str_casei)) {
936  on_fail = pcmk__on_fail_demote;
937  desc = "demote instance";
938 
939  } else {
940  pcmk__config_err("Using default '" PCMK_META_ON_FAIL "' for "
941  "%s of %s because '%s' is not valid",
942  action_name, rsc->id, value);
943  }
944 
945  /* Remote node connections are handled specially. Failures that result
946  * in dropping an active connection must result in fencing. The only
947  * failures that don't are probes and starts. The user can explicitly set
948  * PCMK_META_ON_FAIL=PCMK_VALUE_FENCE to fence after start failures.
949  */
951  && pcmk__is_remote_node(pcmk_find_node(scheduler, rsc->id))
952  && !pcmk_is_probe(action_name, interval_ms)
953  && !pcmk__str_eq(action_name, PCMK_ACTION_START, pcmk__str_none)) {
954  needs_remote_reset = true;
955  if (!pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
956  desc = NULL; // Force default for unmanaged connections
957  }
958  }
959 
960  if (desc != NULL) {
961  // Explicit value used, default not needed
962 
963  } else if (rsc->priv->launcher != NULL) {
965  desc = "restart container (and possibly migrate) (default)";
966 
967  } else if (needs_remote_reset) {
968  if (pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
970  desc = "fence remote node (default)";
971  } else {
972  desc = "recover remote node connection (default)";
973  }
974  on_fail = pcmk__on_fail_reset_remote;
975  } else {
976  on_fail = pcmk__on_fail_stop;
977  desc = "stop unmanaged remote node (enforcing default)";
978  }
979 
980  } else if (pcmk__str_eq(action_name, PCMK_ACTION_STOP, pcmk__str_none)) {
982  on_fail = pcmk__on_fail_fence_node;
983  desc = "resource fence (default)";
984  } else {
985  on_fail = pcmk__on_fail_block;
986  desc = "resource block (default)";
987  }
988 
989  } else {
990  on_fail = pcmk__on_fail_restart;
991  desc = "restart (and possibly migrate) (default)";
992  }
993 
994  pcmk__rsc_trace(rsc, "Failure handling for %s-interval %s of %s: %s",
995  pcmk__readable_interval(interval_ms), action_name,
996  rsc->id, desc);
997  return on_fail;
998 }
999 
1011 enum rsc_role_e
1012 pcmk__role_after_failure(const pcmk_resource_t *rsc, const char *action_name,
1013  enum pcmk__on_fail on_fail, GHashTable *meta)
1014 {
1015  enum rsc_role_e role = pcmk_role_unknown;
1016 
1017  // Set default for role after failure specially in certain circumstances
1018  switch (on_fail) {
1019  case pcmk__on_fail_stop:
1020  role = pcmk_role_stopped;
1021  break;
1022 
1024  if (rsc->priv->remote_reconnect_ms != 0U) {
1025  role = pcmk_role_stopped;
1026  }
1027  break;
1028 
1029  default:
1030  break;
1031  }
1032 
1033  if (role == pcmk_role_unknown) {
1034  // Use default
1035  if (pcmk__str_eq(action_name, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
1036  role = pcmk_role_unpromoted;
1037  } else {
1038  role = pcmk_role_started;
1039  }
1040  }
1041  pcmk__rsc_trace(rsc, "Role after %s %s failure is: %s",
1042  rsc->id, action_name, pcmk_role_text(role));
1043  return role;
1044 }
1045 
1058 static void
1059 unpack_operation(pcmk_action_t *action, const xmlNode *xml_obj,
1060  guint interval_ms)
1061 {
1062  const char *value = NULL;
1063 
1064  action->meta = pcmk__unpack_action_meta(action->rsc, action->node,
1065  action->task, interval_ms, xml_obj);
1066  action->needs = pcmk__action_requires(action->rsc, action->task);
1067 
1068  value = g_hash_table_lookup(action->meta, PCMK_META_ON_FAIL);
1069  action->on_fail = pcmk__parse_on_fail(action->rsc, action->task,
1070  interval_ms, value);
1071 
1072  action->fail_role = pcmk__role_after_failure(action->rsc, action->task,
1073  action->on_fail, action->meta);
1074 }
1075 
1092 pcmk_action_t *
1093 custom_action(pcmk_resource_t *rsc, char *key, const char *task,
1094  const pcmk_node_t *on_node, gboolean optional,
1096 {
1097  pcmk_action_t *action = NULL;
1098 
1099  pcmk__assert((key != NULL) && (task != NULL) && (scheduler != NULL));
1100 
1101  action = find_existing_action(key, rsc, on_node, scheduler);
1102  if (action == NULL) {
1103  action = new_action(key, task, rsc, on_node, optional, scheduler);
1104  } else {
1105  free(key);
1106  }
1107 
1108  update_action_optional(action, optional);
1109 
1110  if (rsc != NULL) {
1111  /* An action can be initially created with a NULL node, and later have
1112  * the node added via find_existing_action() (above) -> find_actions().
1113  * That is why the extra parameters are unpacked here rather than in
1114  * new_action().
1115  */
1116  if ((action->node != NULL) && (action->op_entry != NULL)
1118 
1119  GHashTable *attrs = action->node->priv->attrs;
1120 
1121  if (action->extra != NULL) {
1122  g_hash_table_destroy(action->extra);
1123  }
1124  action->extra = pcmk__unpack_action_rsc_params(action->op_entry,
1125  attrs, scheduler);
1127  }
1128 
1129  update_resource_action_runnable(action, scheduler);
1130  }
1131 
1132  if (action->extra == NULL) {
1133  action->extra = pcmk__strkey_table(free, free);
1134  }
1135 
1136  return action;
1137 }
1138 
1139 pcmk_action_t *
1141 {
1142  pcmk_action_t *op = lookup_singleton(scheduler, name);
1143 
1144  if (op == NULL) {
1145  op = custom_action(NULL, strdup(name), name, NULL, TRUE, scheduler);
1147  }
1148  return op;
1149 }
1150 
1151 static GList *
1152 find_unfencing_devices(GList *candidates, GList *matches)
1153 {
1154  for (GList *gIter = candidates; gIter != NULL; gIter = gIter->next) {
1155  pcmk_resource_t *candidate = gIter->data;
1156 
1157  if (candidate->priv->children != NULL) {
1158  matches = find_unfencing_devices(candidate->priv->children,
1159  matches);
1160 
1161  } else if (!pcmk_is_set(candidate->flags, pcmk__rsc_fence_device)) {
1162  continue;
1163 
1164  } else if (pcmk_is_set(candidate->flags, pcmk__rsc_needs_unfencing)) {
1165  matches = g_list_prepend(matches, candidate);
1166 
1167  } else if (pcmk__str_eq(g_hash_table_lookup(candidate->priv->meta,
1170  matches = g_list_prepend(matches, candidate);
1171  }
1172  }
1173  return matches;
1174 }
1175 
1176 static int
1177 node_priority_fencing_delay(const pcmk_node_t *node,
1178  const pcmk_scheduler_t *scheduler)
1179 {
1180  int member_count = 0;
1181  int online_count = 0;
1182  int top_priority = 0;
1183  int lowest_priority = 0;
1184  GList *gIter = NULL;
1185 
1186  // PCMK_OPT_PRIORITY_FENCING_DELAY is disabled
1187  if (scheduler->priv->priority_fencing_ms == 0U) {
1188  return 0;
1189  }
1190 
1191  /* No need to request a delay if the fencing target is not a normal cluster
1192  * member, for example if it's a remote node or a guest node. */
1193  if (node->priv->variant != pcmk__node_variant_cluster) {
1194  return 0;
1195  }
1196 
1197  // No need to request a delay if the fencing target is in our partition
1198  if (node->details->online) {
1199  return 0;
1200  }
1201 
1202  for (gIter = scheduler->nodes; gIter != NULL; gIter = gIter->next) {
1203  pcmk_node_t *n = gIter->data;
1204 
1206  continue;
1207  }
1208 
1209  member_count ++;
1210 
1211  if (n->details->online) {
1212  online_count++;
1213  }
1214 
1215  if (member_count == 1
1216  || n->priv->priority > top_priority) {
1217  top_priority = n->priv->priority;
1218  }
1219 
1220  if (member_count == 1
1221  || n->priv->priority < lowest_priority) {
1222  lowest_priority = n->priv->priority;
1223  }
1224  }
1225 
1226  // No need to delay if we have more than half of the cluster members
1227  if (online_count > member_count / 2) {
1228  return 0;
1229  }
1230 
1231  /* All the nodes have equal priority.
1232  * Any configured corresponding `pcmk_delay_base/max` will be applied. */
1233  if (lowest_priority == top_priority) {
1234  return 0;
1235  }
1236 
1237  if (node->priv->priority < top_priority) {
1238  return 0;
1239  }
1240 
1242 }
1243 
1244 pcmk_action_t *
1245 pe_fence_op(pcmk_node_t *node, const char *op, bool optional,
1246  const char *reason, bool priority_delay,
1248 {
1249  char *op_key = NULL;
1250  pcmk_action_t *stonith_op = NULL;
1251 
1252  if(op == NULL) {
1253  op = scheduler->priv->fence_action;
1254  }
1255 
1256  op_key = crm_strdup_printf("%s-%s-%s",
1257  PCMK_ACTION_STONITH, node->priv->name, op);
1258 
1259  stonith_op = lookup_singleton(scheduler, op_key);
1260  if(stonith_op == NULL) {
1261  stonith_op = custom_action(NULL, op_key, PCMK_ACTION_STONITH, node,
1262  TRUE, scheduler);
1263 
1264  pcmk__insert_meta(stonith_op, PCMK__META_ON_NODE, node->priv->name);
1266  node->priv->id);
1268 
1270  /* Extra work to detect device changes
1271  */
1272  GString *digests_all = g_string_sized_new(1024);
1273  GString *digests_secure = g_string_sized_new(1024);
1274 
1275  GList *matches = find_unfencing_devices(scheduler->priv->resources,
1276  NULL);
1277 
1278  for (GList *gIter = matches; gIter != NULL; gIter = gIter->next) {
1279  pcmk_resource_t *match = gIter->data;
1280  const char *agent = g_hash_table_lookup(match->priv->meta,
1281  PCMK_XA_TYPE);
1282  pcmk__op_digest_t *data = NULL;
1283 
1284  data = pe__compare_fencing_digest(match, agent, node,
1285  scheduler);
1286  if (data->rc == pcmk__digest_mismatch) {
1287  optional = FALSE;
1288  crm_notice("Unfencing node %s because the definition of "
1289  "%s changed", pcmk__node_name(node), match->id);
1290  if (!pcmk__is_daemon && (scheduler->priv->out != NULL)) {
1291  pcmk__output_t *out = scheduler->priv->out;
1292 
1293  out->info(out,
1294  "notice: Unfencing node %s because the "
1295  "definition of %s changed",
1296  pcmk__node_name(node), match->id);
1297  }
1298  }
1299 
1300  pcmk__g_strcat(digests_all,
1301  match->id, ":", agent, ":",
1302  data->digest_all_calc, ",", NULL);
1303  pcmk__g_strcat(digests_secure,
1304  match->id, ":", agent, ":",
1305  data->digest_secure_calc, ",", NULL);
1306  }
1308  digests_all->str);
1309  g_string_free(digests_all, TRUE);
1310 
1312  digests_secure->str);
1313  g_string_free(digests_secure, TRUE);
1314 
1315  g_list_free(matches);
1316  }
1317 
1318  } else {
1319  free(op_key);
1320  }
1321 
1322  if ((scheduler->priv->priority_fencing_ms > 0U)
1323 
1324  /* It's a suitable case where PCMK_OPT_PRIORITY_FENCING_DELAY
1325  * applies. At least add PCMK_OPT_PRIORITY_FENCING_DELAY field as
1326  * an indicator.
1327  */
1328  && (priority_delay
1329 
1330  /* The priority delay needs to be recalculated if this function has
1331  * been called by schedule_fencing_and_shutdowns() after node
1332  * priority has already been calculated by native_add_running().
1333  */
1334  || g_hash_table_lookup(stonith_op->meta,
1335  PCMK_OPT_PRIORITY_FENCING_DELAY) != NULL)) {
1336 
1337  /* Add PCMK_OPT_PRIORITY_FENCING_DELAY to the fencing op even if
1338  * it's 0 for the targeting node. So that it takes precedence over
1339  * any possible `pcmk_delay_base/max`.
1340  */
1341  char *delay_s = pcmk__itoa(node_priority_fencing_delay(node,
1342  scheduler));
1343 
1344  g_hash_table_insert(stonith_op->meta,
1346  delay_s);
1347  }
1348 
1349  if(optional == FALSE && pe_can_fence(scheduler, node)) {
1351  pe_action_set_reason(stonith_op, reason, false);
1352 
1353  } else if(reason && stonith_op->reason == NULL) {
1354  stonith_op->reason = strdup(reason);
1355  }
1356 
1357  return stonith_op;
1358 }
1359 
1360 void
1362 {
1363  if (action == NULL) {
1364  return;
1365  }
1366  g_list_free_full(action->actions_before, free);
1367  g_list_free_full(action->actions_after, free);
1368  if (action->extra) {
1369  g_hash_table_destroy(action->extra);
1370  }
1371  if (action->meta) {
1372  g_hash_table_destroy(action->meta);
1373  }
1375  free(action->cancel_task);
1376  free(action->reason);
1377  free(action->task);
1378  free(action->uuid);
1379  free(action);
1380 }
1381 
1382 enum pcmk__action_type
1383 get_complex_task(const pcmk_resource_t *rsc, const char *name)
1384 {
1386 
1387  if (pcmk__is_primitive(rsc)) {
1388  switch (task) {
1389  case pcmk__action_stopped:
1390  case pcmk__action_started:
1391  case pcmk__action_demoted:
1392  case pcmk__action_promoted:
1393  crm_trace("Folding %s back into its atomic counterpart for %s",
1394  name, rsc->id);
1395  --task;
1396  break;
1397  default:
1398  break;
1399  }
1400  }
1401  return task;
1402 }
1403 
1415 pcmk_action_t *
1416 find_first_action(const GList *input, const char *uuid, const char *task,
1417  const pcmk_node_t *on_node)
1418 {
1419  CRM_CHECK(uuid || task, return NULL);
1420 
1421  for (const GList *gIter = input; gIter != NULL; gIter = gIter->next) {
1422  pcmk_action_t *action = (pcmk_action_t *) gIter->data;
1423 
1424  if (uuid != NULL && !pcmk__str_eq(uuid, action->uuid, pcmk__str_casei)) {
1425  continue;
1426 
1427  } else if (task != NULL && !pcmk__str_eq(task, action->task, pcmk__str_casei)) {
1428  continue;
1429 
1430  } else if (on_node == NULL) {
1431  return action;
1432 
1433  } else if (action->node == NULL) {
1434  continue;
1435 
1436  } else if (pcmk__same_node(on_node, action->node)) {
1437  return action;
1438  }
1439  }
1440 
1441  return NULL;
1442 }
1443 
1444 GList *
1445 find_actions(GList *input, const char *key, const pcmk_node_t *on_node)
1446 {
1447  GList *gIter = input;
1448  GList *result = NULL;
1449 
1450  CRM_CHECK(key != NULL, return NULL);
1451 
1452  for (; gIter != NULL; gIter = gIter->next) {
1453  pcmk_action_t *action = (pcmk_action_t *) gIter->data;
1454 
1455  if (!pcmk__str_eq(key, action->uuid, pcmk__str_casei)) {
1456  continue;
1457 
1458  } else if (on_node == NULL) {
1459  crm_trace("Action %s matches (ignoring node)", key);
1460  result = g_list_prepend(result, action);
1461 
1462  } else if (action->node == NULL) {
1463  crm_trace("Action %s matches (unallocated, assigning to %s)",
1464  key, pcmk__node_name(on_node));
1465 
1466  action->node = pe__copy_node(on_node);
1467  result = g_list_prepend(result, action);
1468 
1469  } else if (pcmk__same_node(on_node, action->node)) {
1470  crm_trace("Action %s on %s matches", key, pcmk__node_name(on_node));
1471  result = g_list_prepend(result, action);
1472  }
1473  }
1474 
1475  return result;
1476 }
1477 
1478 GList *
1479 find_actions_exact(GList *input, const char *key, const pcmk_node_t *on_node)
1480 {
1481  GList *result = NULL;
1482 
1483  CRM_CHECK(key != NULL, return NULL);
1484 
1485  if (on_node == NULL) {
1486  return NULL;
1487  }
1488 
1489  for (GList *gIter = input; gIter != NULL; gIter = gIter->next) {
1490  pcmk_action_t *action = (pcmk_action_t *) gIter->data;
1491 
1492  if ((action->node != NULL)
1493  && pcmk__str_eq(key, action->uuid, pcmk__str_casei)
1494  && pcmk__same_node(on_node, action->node)) {
1495 
1496  crm_trace("Action %s on %s matches", key, pcmk__node_name(on_node));
1497  result = g_list_prepend(result, action);
1498  }
1499  }
1500 
1501  return result;
1502 }
1503 
1516 GList *
1518  const char *task, bool require_node)
1519 {
1520  GList *result = NULL;
1521  char *key = pcmk__op_key(rsc->id, task, 0);
1522 
1523  if (require_node) {
1524  result = find_actions_exact(rsc->priv->actions, key, node);
1525  } else {
1526  result = find_actions(rsc->priv->actions, key, node);
1527  }
1528  free(key);
1529  return result;
1530 }
1531 
1542 char *
1544 {
1545  const char *change = NULL;
1546 
1547  switch (flag) {
1548  case pcmk__action_runnable:
1549  change = "unrunnable";
1550  break;
1552  change = "unmigrateable";
1553  break;
1554  case pcmk__action_optional:
1555  change = "required";
1556  break;
1557  default:
1558  // Bug: caller passed unsupported flag
1559  CRM_CHECK(change != NULL, change = "");
1560  break;
1561  }
1562  return crm_strdup_printf("%s%s%s %s", change,
1563  (action->rsc == NULL)? "" : " ",
1564  (action->rsc == NULL)? "" : action->rsc->id,
1565  action->task);
1566 }
1567 
1568 void pe_action_set_reason(pcmk_action_t *action, const char *reason,
1569  bool overwrite)
1570 {
1571  if (action->reason != NULL && overwrite) {
1572  pcmk__rsc_trace(action->rsc, "Changing %s reason from '%s' to '%s'",
1573  action->uuid, action->reason,
1574  pcmk__s(reason, "(none)"));
1575  } else if (action->reason == NULL) {
1576  pcmk__rsc_trace(action->rsc, "Set %s reason to '%s'",
1577  action->uuid, pcmk__s(reason, "(none)"));
1578  } else {
1579  // crm_assert(action->reason != NULL && !overwrite);
1580  return;
1581  }
1582 
1583  pcmk__str_update(&action->reason, reason);
1584 }
1585 
1593 void
1595 {
1596  pcmk__assert((rsc != NULL) && (node != NULL));
1597 
1599  PCMK_ACTION_LRM_DELETE, node, FALSE, rsc->priv->scheduler);
1600 }
1601 
1602 #define sort_return(an_int, why) do { \
1603  free(a_uuid); \
1604  free(b_uuid); \
1605  crm_trace("%s (%d) %c %s (%d) : %s", \
1606  a_xml_id, a_call_id, an_int>0?'>':an_int<0?'<':'=', \
1607  b_xml_id, b_call_id, why); \
1608  return an_int; \
1609  } while(0)
1610 
1611 int
1612 pe__is_newer_op(const xmlNode *xml_a, const xmlNode *xml_b)
1613 {
1614  int a_call_id = -1;
1615  int b_call_id = -1;
1616 
1617  char *a_uuid = NULL;
1618  char *b_uuid = NULL;
1619 
1620  const char *a_xml_id = crm_element_value(xml_a, PCMK_XA_ID);
1621  const char *b_xml_id = crm_element_value(xml_b, PCMK_XA_ID);
1622 
1623  const char *a_node = crm_element_value(xml_a, PCMK__META_ON_NODE);
1624  const char *b_node = crm_element_value(xml_b, PCMK__META_ON_NODE);
1625  bool same_node = pcmk__str_eq(a_node, b_node, pcmk__str_casei);
1626 
1627  if (same_node && pcmk__str_eq(a_xml_id, b_xml_id, pcmk__str_none)) {
1628  /* We have duplicate PCMK__XE_LRM_RSC_OP entries in the status
1629  * section which is unlikely to be a good thing
1630  * - we can handle it easily enough, but we need to get
1631  * to the bottom of why it's happening.
1632  */
1633  pcmk__config_err("Duplicate " PCMK__XE_LRM_RSC_OP " entries named %s",
1634  a_xml_id);
1635  sort_return(0, "duplicate");
1636  }
1637 
1638  crm_element_value_int(xml_a, PCMK__XA_CALL_ID, &a_call_id);
1639  crm_element_value_int(xml_b, PCMK__XA_CALL_ID, &b_call_id);
1640 
1641  if (a_call_id == -1 && b_call_id == -1) {
1642  /* both are pending ops so it doesn't matter since
1643  * stops are never pending
1644  */
1645  sort_return(0, "pending");
1646 
1647  } else if (same_node && a_call_id >= 0 && a_call_id < b_call_id) {
1648  sort_return(-1, "call id");
1649 
1650  } else if (same_node && b_call_id >= 0 && a_call_id > b_call_id) {
1651  sort_return(1, "call id");
1652 
1653  } else if (a_call_id >= 0 && b_call_id >= 0
1654  && (!same_node || a_call_id == b_call_id)) {
1655  /* The op and last_failed_op are the same. Order on
1656  * PCMK_XA_LAST_RC_CHANGE.
1657  */
1658  time_t last_a = -1;
1659  time_t last_b = -1;
1660 
1663 
1664  crm_trace("rc-change: %lld vs %lld",
1665  (long long) last_a, (long long) last_b);
1666  if (last_a >= 0 && last_a < last_b) {
1667  sort_return(-1, "rc-change");
1668 
1669  } else if (last_b >= 0 && last_a > last_b) {
1670  sort_return(1, "rc-change");
1671  }
1672  sort_return(0, "rc-change");
1673 
1674  } else {
1675  /* One of the inputs is a pending operation.
1676  * Attempt to use PCMK__XA_TRANSITION_MAGIC to determine its age relative
1677  * to the other.
1678  */
1679 
1680  int a_id = -1;
1681  int b_id = -1;
1682 
1683  const char *a_magic = crm_element_value(xml_a,
1685  const char *b_magic = crm_element_value(xml_b,
1687 
1688  CRM_CHECK(a_magic != NULL && b_magic != NULL, sort_return(0, "No magic"));
1689  if (!decode_transition_magic(a_magic, &a_uuid, &a_id, NULL, NULL, NULL,
1690  NULL)) {
1691  sort_return(0, "bad magic a");
1692  }
1693  if (!decode_transition_magic(b_magic, &b_uuid, &b_id, NULL, NULL, NULL,
1694  NULL)) {
1695  sort_return(0, "bad magic b");
1696  }
1697  /* try to determine the relative age of the operation...
1698  * some pending operations (e.g. a start) may have been superseded
1699  * by a subsequent stop
1700  *
1701  * [a|b]_id == -1 means it's a shutdown operation and _always_ comes last
1702  */
1703  if (!pcmk__str_eq(a_uuid, b_uuid, pcmk__str_casei) || a_id == b_id) {
1704  /*
1705  * some of the logic in here may be redundant...
1706  *
1707  * if the UUID from the TE doesn't match then one better
1708  * be a pending operation.
1709  * pending operations don't survive between elections and joins
1710  * because we query the LRM directly
1711  */
1712 
1713  if (b_call_id == -1) {
1714  sort_return(-1, "transition + call");
1715 
1716  } else if (a_call_id == -1) {
1717  sort_return(1, "transition + call");
1718  }
1719 
1720  } else if ((a_id >= 0 && a_id < b_id) || b_id == -1) {
1721  sort_return(-1, "transition");
1722 
1723  } else if ((b_id >= 0 && a_id > b_id) || a_id == -1) {
1724  sort_return(1, "transition");
1725  }
1726  }
1727 
1728  /* we should never end up here */
1729  CRM_CHECK(FALSE, sort_return(0, "default"));
1730 }
1731 
1732 gint
1733 sort_op_by_callid(gconstpointer a, gconstpointer b)
1734 {
1735  return pe__is_newer_op((const xmlNode *) a, (const xmlNode *) b);
1736 }
1737 
1749 pcmk_action_t *
1750 pe__new_rsc_pseudo_action(pcmk_resource_t *rsc, const char *task, bool optional,
1751  bool runnable)
1752 {
1753  pcmk_action_t *action = NULL;
1754 
1755  pcmk__assert((rsc != NULL) && (task != NULL));
1756 
1757  action = custom_action(rsc, pcmk__op_key(rsc->id, task, 0), task, NULL,
1758  optional, rsc->priv->scheduler);
1760  if (runnable) {
1762  }
1763  return action;
1764 }
1765 
1775 void
1777 {
1778  pcmk__assert((action != NULL) && (action->meta != NULL));
1779 
1780  g_hash_table_insert(action->meta, pcmk__str_copy(PCMK__META_OP_TARGET_RC),
1781  pcmk__itoa(expected_result));
1782 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:213
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:100
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:1093
pcmk__action_type
A dumping ground.
pcmk_node_t * pe__copy_node(const pcmk_node_t *this_node)
Definition: utils.c:91
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml_element.c:42
#define crm_notice(fmt, args...)
Definition: logging.h:365
#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
Do not recover resources from outside partition.
Definition: scheduler.h:39
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:1776
#define PCMK_META_INTERVAL_ORIGIN
Definition: options.h:93
gboolean(* active)(pcmk_resource_t *rsc, gboolean all)
GList * find_actions(GList *input, const char *key, const pcmk_node_t *on_node)
Definition: pe_actions.c:1445
Stopped.
Definition: roles.h:36
#define PCMK_STONITH_PROVIDES
Definition: agents.h:44
const char * name
Definition: cib.c:26
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1027
#define PCMK__XA_TRANSITION_MAGIC
enum pcmk__action_type get_complex_task(const pcmk_resource_t *rsc, const char *name)
Definition: pe_actions.c:1383
struct crm_time_s crm_time_t
Definition: iso8601.h:32
#define pcmk__rsc_trace(rsc, fmt, args...)
enum rsc_role_e pcmk__role_after_failure(const pcmk_resource_t *rsc, const char *action_name, enum pcmk__on_fail on_fail, GHashTable *meta)
Definition: pe_actions.c:1012
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: xml_element.c:1359
#define PCMK__META_OP_TARGET_RC
#define PCMK_VALUE_STANDBY
Definition: options.h:208
enum pcmk__requires pcmk__action_requires(const pcmk_resource_t *rsc, const char *action_name)
Definition: pe_actions.c:832
#define pcmk__insert_meta(obj, name, value)
#define pcmk__config_err(fmt...)
#define PCMK_ACTION_MONITOR
Definition: actions.h:51
#define PCMK_VALUE_RESTART
Definition: options.h:202
pcmk__scheduler_private_t * priv
Definition: scheduler.h:99
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:1245
#define PCMK_ACTION_MIGRATE_TO
Definition: actions.h:50
#define PCMK_XA_PROVIDER
Definition: xml_names.h:364
Promoted.
Definition: roles.h:39
uint64_t flags
Definition: scheduler.h:89
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:361
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:114
#define PCMK__META_STONITH_ACTION
Stop all resources in partition.
Definition: scheduler.h:40
#define PCMK_VALUE_BLOCK
Definition: options.h:135
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:196
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
#define PCMK__META_ON_NODE
const pcmk__rsc_methods_t * fns
void pcmk__free_node_copy(void *data)
Definition: nodes.c:22
#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:1140
const char * action
Definition: pcmk_fence.c:32
enum pe_quorum_policy no_quorum_policy
Definition: scheduler.h:93
pe_quorum_policy
Possible responses to loss of quorum.
Definition: scheduler.h:38
pcmk__on_fail
#define pcmk__rsc_debug(rsc, fmt, args...)
#define PCMK_VALUE_NOTHING
Definition: options.h:182
#define PCMK__ROLE_PROMOTED_LEGACY
pcmk__node_private_t * priv
Definition: nodes.h:85
int pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
Definition: xml_element.c:1501
#define PCMK_DEFAULT_ACTION_TIMEOUT_MS
Default timeout (in milliseconds) for non-metadata actions.
Definition: actions.h:33
#define PCMK_ACTION_DEMOTE
Definition: actions.h:40
void pe__set_next_role(pcmk_resource_t *rsc, enum rsc_role_e role, const char *why)
Definition: complex.c:1265
#define PCMK_VALUE_FENCE
Definition: options.h:154
pcmk_scheduler_t * scheduler
void pe__clear_resource_history(pcmk_resource_t *rsc, const pcmk_node_t *node)
Definition: pe_actions.c:1594
#define pcmk__clear_action_flags(action, flags_to_clear)
int pcmk_parse_interval_spec(const char *input, guint *result_ms)
Parse milliseconds from a Pacemaker interval specification.
Definition: strings.c:452
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: xml_element.c:1168
pcmk__action_flags
#define PCMK_META_START_DELAY
Definition: options.h:112
#define crm_trace(fmt, args...)
Definition: logging.h:372
#define do_crm_log(level, fmt, args...)
Log a message.
Definition: logging.h:149
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1297
char * pe__action2reason(const pcmk_action_t *action, enum pcmk__action_flags flag)
Definition: pe_actions.c:1543
Demote promotable resources and stop all others.
Definition: scheduler.h:43
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:80
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:1416
#define PCMK_ACTION_START
Definition: actions.h:63
pcmk__resource_private_t * priv
Definition: resources.h:61
#define PCMK__META_ON_NODE_UUID
const char * op_name
Definition: common.h:41
bool pcmk__is_daemon
Definition: logging.c:47
#define PCMK_VALUE_IGNORE
Definition: options.h:162
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:1278
Wrappers for and extensions to libxml2.
rsc_role_e
Definition: roles.h:34
#define PCMK_META_ENABLED
Definition: options.h:87
#define PCMK_ACTION_STOP
Definition: actions.h:66
pcmk_action_t * pe__new_rsc_pseudo_action(pcmk_resource_t *rsc, const char *task, bool optional, bool runnable)
Definition: pe_actions.c:1750
#define PCMK_ACTION_STONITH
Definition: actions.h:65
#define PCMK__META_DIGESTS_ALL
Act as if partition still holds quorum.
Definition: scheduler.h:41
#define PCMK_XA_ID
Definition: xml_names.h:301
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:195
bool pcmk_is_probe(const char *task, guint interval)
Check whether an action name and interval represent a probe.
Definition: probes.c:31
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1051
long long crm_time_get_seconds(const crm_time_t *dt)
Definition: iso8601.c:331
#define pcmk__str_copy(str)
void pe_action_set_reason(pcmk_action_t *action, const char *reason, bool overwrite)
Definition: pe_actions.c:1568
#define PCMK_META_TIMEOUT
Definition: options.h:114
xmlNode * pcmk__find_action_config(const pcmk_resource_t *rsc, const char *action_name, guint interval_ms, bool include_disabled)
Definition: pe_actions.c:137
long long crm_get_msec(const char *input)
Parse a time+units string and return milliseconds equivalent.
Definition: strings.c:351
#define PCMK_XE_META_ATTRIBUTES
Definition: xml_names.h:130
xmlNode * pcmk__xe_next(const xmlNode *node, const char *element_name)
Definition: xml_element.c:106
#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:1517
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:549
#define PCMK_ACTION_LRM_DELETE
Definition: actions.h:44
#define PCMK_XE_OP
Definition: xml_names.h:146
#define PCMK_XA_CLASS
Definition: xml_names.h:246
#define PCMK_META_INTERVAL
Definition: options.h:91
#define PCMK_XA_LAST_RC_CHANGE
Definition: xml_names.h:316
GList * nodes
Definition: scheduler.h:97
const char * standard
Definition: common.h:35
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:685
pcmk__action_result_t result
Definition: pcmk_fence.c:37
crm_time_t * now
Definition: common.h:48
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:682
#define PCMK_ROLE_PROMOTED
Definition: roles.h:28
pcmk_scheduler_t * scheduler
#define PCMK__XE_LRM_RSC_OP
enum pcmk__node_variant variant
GHashTable * node_hash
Definition: common.h:46
xmlNode * input
enum pcmk__action_type pcmk__parse_action(const char *action_name)
Definition: actions.c:90
crm_time_t * crm_time_new(const char *string)
Definition: iso8601.c:112
pcmk_resource_t * launcher
#define PCMK_ACTION_MIGRATE_FROM
Definition: actions.h:49
#define PCMK_META_ON_FAIL
Definition: options.h:98
Started.
Definition: roles.h:37
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition: xml_element.c:1201
GList * find_actions_exact(GList *input, const char *key, const pcmk_node_t *on_node)
Definition: pe_actions.c:1479
guint pcmk__timeout_ms2s(guint timeout_ms)
Definition: utils.c:425
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:248
#define PCMK_ACTION_PROMOTE
Definition: actions.h:57
#define PCMK_OPT_NO_QUORUM_POLICY
Definition: options.h:46
#define PCMK__XA_CALL_ID
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, pcmk_scheduler_t *scheduler)
Definition: utils.c:701
#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:247
#define PCMK_VALUE_STOP
Definition: options.h:212
unsigned long long flags
Definition: resources.h:69
#define sort_return(an_int, why)
Definition: pe_actions.c:1602
const char * pcmk__readable_interval(guint interval_ms)
Definition: iso8601.c:2206
void pe_free_action(pcmk_action_t *action)
Definition: pe_actions.c:1361
gboolean online
Definition: nodes.h:50
uint32_t pcmk_get_ra_caps(const char *standard)
Get capabilities of a resource agent standard.
Definition: agents.c:27
#define PCMK_VALUE_UNFENCING
Definition: options.h:219
GHashTable * meta
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:476
struct pcmk__node_details * details
Definition: nodes.h:82
gint sort_op_by_callid(gconstpointer a, gconstpointer b)
Definition: pe_actions.c:1733
#define pcmk__assert_alloc(nmemb, size)
Definition: internal.h:257
#define PCMK_XE_INSTANCE_ATTRIBUTES
Definition: xml_names.h:122
int pe__is_newer_op(const xmlNode *xml_a, const xmlNode *xml_b)
Definition: pe_actions.c:1612
#define PCMK_XA_ROLE
Definition: xml_names.h:387
#define crm_info(fmt, args...)
Definition: logging.h:367
void pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
Definition: strings.c:703
#define PCMK_VALUE_DEMOTE
Definition: options.h:146
pcmk__requires
#define PCMK_VALUE_RESTART_CONTAINER
Definition: options.h:203
enum pcmk__on_fail pcmk__parse_on_fail(const pcmk_resource_t *rsc, const char *action_name, guint interval_ms, const char *value)
Definition: pe_actions.c:870
#define PCMK_ACTION_NOTIFY
Definition: actions.h:53
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:150