root/lib/pengine/pe_actions.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. add_singleton
  2. lookup_singleton
  3. find_existing_action
  4. find_exact_action_config
  5. pcmk__find_action_config
  6. new_action
  7. pcmk__unpack_action_rsc_params
  8. update_action_optional
  9. effective_quorum_policy
  10. update_resource_action_runnable
  11. update_resource_flags_for_action
  12. valid_stop_on_fail
  13. validate_on_fail
  14. unpack_timeout
  15. unpack_interval_origin
  16. unpack_start_delay
  17. most_frequent_monitor
  18. pcmk__unpack_action_meta
  19. pcmk__action_requires
  20. pcmk__parse_on_fail
  21. pcmk__role_after_failure
  22. unpack_operation
  23. custom_action
  24. get_pseudo_op
  25. find_unfencing_devices
  26. node_priority_fencing_delay
  27. pe_fence_op
  28. pe_free_action
  29. pe_get_configured_timeout
  30. get_complex_task
  31. find_first_action
  32. find_actions
  33. find_actions_exact
  34. pe__resource_actions
  35. pe__action2reason
  36. pe_action_set_reason
  37. pe__clear_resource_history
  38. pe__is_newer_op
  39. sort_op_by_callid
  40. pe__new_rsc_pseudo_action
  41. pe__add_action_expected_result

   1 /*
   2  * Copyright 2004-2023 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <glib.h>
  13 #include <stdbool.h>
  14 
  15 #include <crm/crm.h>
  16 #include <crm/msg_xml.h>
  17 #include <crm/common/scheduler_internal.h>
  18 #include <crm/pengine/internal.h>
  19 #include <crm/common/xml_internal.h>
  20 #include "pe_status_private.h"
  21 
  22 static void unpack_operation(pcmk_action_t *action, const xmlNode *xml_obj,
  23                              guint interval_ms);
  24 
  25 static void
  26 add_singleton(pcmk_scheduler_t *scheduler, pcmk_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
  27 {
  28     if (scheduler->singletons == NULL) {
  29         scheduler->singletons = pcmk__strkey_table(NULL, NULL);
  30     }
  31     g_hash_table_insert(scheduler->singletons, action->uuid, action);
  32 }
  33 
  34 static pcmk_action_t *
  35 lookup_singleton(pcmk_scheduler_t *scheduler, const char *action_uuid)
     /* [previous][next][first][last][top][bottom][index][help] */
  36 {
  37     if (scheduler->singletons == NULL) {
  38         return NULL;
  39     }
  40     return g_hash_table_lookup(scheduler->singletons, action_uuid);
  41 }
  42 
  43 /*!
  44  * \internal
  45  * \brief Find an existing action that matches arguments
  46  *
  47  * \param[in] key        Action key to match
  48  * \param[in] rsc        Resource to match (if any)
  49  * \param[in] node       Node to match (if any)
  50  * \param[in] scheduler  Scheduler data
  51  *
  52  * \return Existing action that matches arguments (or NULL if none)
  53  */
  54 static pcmk_action_t *
  55 find_existing_action(const char *key, const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
  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 
  76 /*!
  77  * \internal
  78  * \brief Find the XML configuration corresponding to a specific action key
  79  *
  80  * \param[in] rsc               Resource to find action configuration for
  81  * \param[in] key               "RSC_ACTION_INTERVAL" of action to find
  82  * \param[in] include_disabled  If false, do not return disabled actions
  83  *
  84  * \return XML configuration of desired action if any, otherwise NULL
  85  */
  86 static xmlNode *
  87 find_exact_action_config(const pcmk_resource_t *rsc, const char *action_name,
     /* [previous][next][first][last][top][bottom][index][help] */
  88                          guint interval_ms, bool include_disabled)
  89 {
  90     for (xmlNode *operation = first_named_child(rsc->ops_xml, XML_ATTR_OP);
  91          operation != NULL; operation = crm_next_same_xml(operation)) {
  92 
  93         bool enabled = false;
  94         const char *config_name = NULL;
  95         const char *interval_spec = NULL;
  96 
  97         // @TODO This does not consider rules, defaults, etc.
  98         if (!include_disabled
  99             && (pcmk__xe_get_bool_attr(operation, "enabled",
 100                                        &enabled) == pcmk_rc_ok) && !enabled) {
 101             continue;
 102         }
 103 
 104         interval_spec = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
 105         if (crm_parse_interval_spec(interval_spec) != interval_ms) {
 106             continue;
 107         }
 108 
 109         config_name = crm_element_value(operation, "name");
 110         if (pcmk__str_eq(action_name, config_name, pcmk__str_none)) {
 111             return operation;
 112         }
 113     }
 114     return NULL;
 115 }
 116 
 117 /*!
 118  * \internal
 119  * \brief Find the XML configuration of a resource action
 120  *
 121  * \param[in] rsc               Resource to find action configuration for
 122  * \param[in] action_name       Action name to search for
 123  * \param[in] interval_ms       Action interval (in milliseconds) to search for
 124  * \param[in] include_disabled  If false, do not return disabled actions
 125  *
 126  * \return XML configuration of desired action if any, otherwise NULL
 127  */
 128 xmlNode *
 129 pcmk__find_action_config(const pcmk_resource_t *rsc, const char *action_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 130                          guint interval_ms, bool include_disabled)
 131 {
 132     xmlNode *action_config = NULL;
 133 
 134     // Try requested action first
 135     action_config = find_exact_action_config(rsc, action_name, interval_ms,
 136                                              include_disabled);
 137 
 138     // For migrate_to and migrate_from actions, retry with "migrate"
 139     // @TODO This should be either documented or deprecated
 140     if ((action_config == NULL)
 141         && pcmk__str_any_of(action_name, PCMK_ACTION_MIGRATE_TO,
 142                             PCMK_ACTION_MIGRATE_FROM, NULL)) {
 143         action_config = find_exact_action_config(rsc, "migrate", 0,
 144                                                  include_disabled);
 145     }
 146 
 147     return action_config;
 148 }
 149 
 150 /*!
 151  * \internal
 152  * \brief Create a new action object
 153  *
 154  * \param[in]     key        Action key
 155  * \param[in]     task       Action name
 156  * \param[in,out] rsc        Resource that action is for (if any)
 157  * \param[in]     node       Node that action is on (if any)
 158  * \param[in]     optional   Whether action should be considered optional
 159  * \param[in,out] scheduler  Scheduler data
 160  *
 161  * \return Newly allocated action
 162  * \note This function takes ownership of \p key. It is the caller's
 163  *       responsibility to free the return value with pe_free_action().
 164  */
 165 static pcmk_action_t *
 166 new_action(char *key, const char *task, pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 167            const pcmk_node_t *node, bool optional, pcmk_scheduler_t *scheduler)
 168 {
 169     pcmk_action_t *action = calloc(1, sizeof(pcmk_action_t));
 170 
 171     CRM_ASSERT(action != NULL);
 172 
 173     action->rsc = rsc;
 174     action->task = strdup(task); CRM_ASSERT(action->task != NULL);
 175     action->uuid = key;
 176 
 177     if (node) {
 178         action->node = pe__copy_node(node);
 179     }
 180 
 181     if (pcmk__str_eq(task, PCMK_ACTION_LRM_DELETE, pcmk__str_casei)) {
 182         // Resource history deletion for a node can be done on the DC
 183         pe__set_action_flags(action, pcmk_action_on_dc);
 184     }
 185 
 186     pe__set_action_flags(action, pcmk_action_runnable);
 187     if (optional) {
 188         pe__set_action_flags(action, pcmk_action_optional);
 189     } else {
 190         pe__clear_action_flags(action, pcmk_action_optional);
 191     }
 192 
 193     if (rsc == NULL) {
 194         action->meta = pcmk__strkey_table(free, free);
 195     } else {
 196         guint interval_ms = 0;
 197 
 198         parse_op_key(key, NULL, NULL, &interval_ms);
 199         action->op_entry = pcmk__find_action_config(rsc, task, interval_ms,
 200                                                     true);
 201 
 202         /* If the given key is for one of the many notification pseudo-actions
 203          * (pre_notify_promote, etc.), the actual action name is "notify"
 204          */
 205         if ((action->op_entry == NULL) && (strstr(key, "_notify_") != NULL)) {
 206             action->op_entry = find_exact_action_config(rsc, PCMK_ACTION_NOTIFY,
 207                                                         0, true);
 208         }
 209 
 210         unpack_operation(action, action->op_entry, interval_ms);
 211     }
 212 
 213     pe_rsc_trace(rsc, "Created %s action %d (%s): %s for %s on %s",
 214                  (optional? "optional" : "required"),
 215                  scheduler->action_id, key, task,
 216                  ((rsc == NULL)? "no resource" : rsc->id),
 217                  pe__node_name(node));
 218     action->id = scheduler->action_id++;
 219 
 220     scheduler->actions = g_list_prepend(scheduler->actions, action);
 221     if (rsc == NULL) {
 222         add_singleton(scheduler, action);
 223     } else {
 224         rsc->actions = g_list_prepend(rsc->actions, action);
 225     }
 226     return action;
 227 }
 228 
 229 /*!
 230  * \internal
 231  * \brief Unpack a resource's action-specific instance parameters
 232  *
 233  * \param[in]     action_xml  XML of action's configuration in CIB (if any)
 234  * \param[in,out] node_attrs  Table of node attributes (for rule evaluation)
 235  * \param[in,out] scheduler   Cluster working set (for rule evaluation)
 236  *
 237  * \return Newly allocated hash table of action-specific instance parameters
 238  */
 239 GHashTable *
 240 pcmk__unpack_action_rsc_params(const xmlNode *action_xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 241                                GHashTable *node_attrs,
 242                                pcmk_scheduler_t *scheduler)
 243 {
 244     GHashTable *params = pcmk__strkey_table(free, free);
 245 
 246     pe_rule_eval_data_t rule_data = {
 247         .node_hash = node_attrs,
 248         .role = pcmk_role_unknown,
 249         .now = scheduler->now,
 250         .match_data = NULL,
 251         .rsc_data = NULL,
 252         .op_data = NULL
 253     };
 254 
 255     pe__unpack_dataset_nvpairs(action_xml, XML_TAG_ATTR_SETS,
 256                                &rule_data, params, NULL,
 257                                FALSE, scheduler);
 258     return params;
 259 }
 260 
 261 /*!
 262  * \internal
 263  * \brief Update an action's optional flag
 264  *
 265  * \param[in,out] action    Action to update
 266  * \param[in]     optional  Requested optional status
 267  */
 268 static void
 269 update_action_optional(pcmk_action_t *action, gboolean optional)
     /* [previous][next][first][last][top][bottom][index][help] */
 270 {
 271     // Force a non-recurring action to be optional if its resource is unmanaged
 272     if ((action->rsc != NULL) && (action->node != NULL)
 273         && !pcmk_is_set(action->flags, pcmk_action_pseudo)
 274         && !pcmk_is_set(action->rsc->flags, pcmk_rsc_managed)
 275         && (g_hash_table_lookup(action->meta,
 276                                 XML_LRM_ATTR_INTERVAL_MS) == NULL)) {
 277             pe_rsc_debug(action->rsc, "%s on %s is optional (%s is unmanaged)",
 278                          action->uuid, pe__node_name(action->node),
 279                          action->rsc->id);
 280             pe__set_action_flags(action, pcmk_action_optional);
 281             // We shouldn't clear runnable here because ... something
 282 
 283     // Otherwise require the action if requested
 284     } else if (!optional) {
 285         pe__clear_action_flags(action, pcmk_action_optional);
 286     }
 287 }
 288 
 289 static enum pe_quorum_policy
 290 effective_quorum_policy(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 291 {
 292     enum pe_quorum_policy policy = scheduler->no_quorum_policy;
 293 
 294     if (pcmk_is_set(scheduler->flags, pcmk_sched_quorate)) {
 295         policy = pcmk_no_quorum_ignore;
 296 
 297     } else if (scheduler->no_quorum_policy == pcmk_no_quorum_demote) {
 298         switch (rsc->role) {
 299             case pcmk_role_promoted:
 300             case pcmk_role_unpromoted:
 301                 if (rsc->next_role > pcmk_role_unpromoted) {
 302                     pe__set_next_role(rsc, pcmk_role_unpromoted,
 303                                       "no-quorum-policy=demote");
 304                 }
 305                 policy = pcmk_no_quorum_ignore;
 306                 break;
 307             default:
 308                 policy = pcmk_no_quorum_stop;
 309                 break;
 310         }
 311     }
 312     return policy;
 313 }
 314 
 315 /*!
 316  * \internal
 317  * \brief Update a resource action's runnable flag
 318  *
 319  * \param[in,out] action     Action to update
 320  * \param[in,out] scheduler  Scheduler data
 321  *
 322  * \note This may also schedule fencing if a stop is unrunnable.
 323  */
 324 static void
 325 update_resource_action_runnable(pcmk_action_t *action,
     /* [previous][next][first][last][top][bottom][index][help] */
 326                                 pcmk_scheduler_t *scheduler)
 327 {
 328     if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
 329         return;
 330     }
 331 
 332     if (action->node == NULL) {
 333         pe_rsc_trace(action->rsc, "%s is unrunnable (unallocated)",
 334                      action->uuid);
 335         pe__clear_action_flags(action, pcmk_action_runnable);
 336 
 337     } else if (!pcmk_is_set(action->flags, pcmk_action_on_dc)
 338                && !(action->node->details->online)
 339                && (!pe__is_guest_node(action->node)
 340                    || action->node->details->remote_requires_reset)) {
 341         pe__clear_action_flags(action, pcmk_action_runnable);
 342         do_crm_log(LOG_WARNING, "%s on %s is unrunnable (node is offline)",
 343                    action->uuid, pe__node_name(action->node));
 344         if (pcmk_is_set(action->rsc->flags, pcmk_rsc_managed)
 345             && pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_casei)
 346             && !(action->node->details->unclean)) {
 347             pe_fence_node(scheduler, action->node, "stop is unrunnable", false);
 348         }
 349 
 350     } else if (!pcmk_is_set(action->flags, pcmk_action_on_dc)
 351                && action->node->details->pending) {
 352         pe__clear_action_flags(action, pcmk_action_runnable);
 353         do_crm_log(LOG_WARNING,
 354                    "Action %s on %s is unrunnable (node is pending)",
 355                    action->uuid, pe__node_name(action->node));
 356 
 357     } else if (action->needs == pcmk_requires_nothing) {
 358         pe_action_set_reason(action, NULL, TRUE);
 359         if (pe__is_guest_node(action->node)
 360             && !pe_can_fence(scheduler, action->node)) {
 361             /* An action that requires nothing usually does not require any
 362              * fencing in order to be runnable. However, there is an exception:
 363              * such an action cannot be completed if it is on a guest node whose
 364              * host is unclean and cannot be fenced.
 365              */
 366             pe_rsc_debug(action->rsc, "%s on %s is unrunnable "
 367                          "(node's host cannot be fenced)",
 368                          action->uuid, pe__node_name(action->node));
 369             pe__clear_action_flags(action, pcmk_action_runnable);
 370         } else {
 371             pe_rsc_trace(action->rsc,
 372                          "%s on %s does not require fencing or quorum",
 373                          action->uuid, pe__node_name(action->node));
 374             pe__set_action_flags(action, pcmk_action_runnable);
 375         }
 376 
 377     } else {
 378         switch (effective_quorum_policy(action->rsc, scheduler)) {
 379             case pcmk_no_quorum_stop:
 380                 pe_rsc_debug(action->rsc, "%s on %s is unrunnable (no quorum)",
 381                              action->uuid, pe__node_name(action->node));
 382                 pe__clear_action_flags(action, pcmk_action_runnable);
 383                 pe_action_set_reason(action, "no quorum", true);
 384                 break;
 385 
 386             case pcmk_no_quorum_freeze:
 387                 if (!action->rsc->fns->active(action->rsc, TRUE)
 388                     || (action->rsc->next_role > action->rsc->role)) {
 389                     pe_rsc_debug(action->rsc,
 390                                  "%s on %s is unrunnable (no quorum)",
 391                                  action->uuid, pe__node_name(action->node));
 392                     pe__clear_action_flags(action, pcmk_action_runnable);
 393                     pe_action_set_reason(action, "quorum freeze", true);
 394                 }
 395                 break;
 396 
 397             default:
 398                 //pe_action_set_reason(action, NULL, TRUE);
 399                 pe__set_action_flags(action, pcmk_action_runnable);
 400                 break;
 401         }
 402     }
 403 }
 404 
 405 /*!
 406  * \internal
 407  * \brief Update a resource object's flags for a new action on it
 408  *
 409  * \param[in,out] rsc     Resource that action is for (if any)
 410  * \param[in]     action  New action
 411  */
 412 static void
 413 update_resource_flags_for_action(pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 414                                  const pcmk_action_t *action)
 415 {
 416     /* @COMPAT pcmk_rsc_starting and pcmk_rsc_stopping are deprecated and unused
 417      * within Pacemaker, and will eventually be removed
 418      */
 419     if (pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_casei)) {
 420         pe__set_resource_flags(rsc, pcmk_rsc_stopping);
 421 
 422     } else if (pcmk__str_eq(action->task, PCMK_ACTION_START, pcmk__str_casei)) {
 423         if (pcmk_is_set(action->flags, pcmk_action_runnable)) {
 424             pe__set_resource_flags(rsc, pcmk_rsc_starting);
 425         } else {
 426             pe__clear_resource_flags(rsc, pcmk_rsc_starting);
 427         }
 428     }
 429 }
 430 
 431 static bool
 432 valid_stop_on_fail(const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 433 {
 434     return !pcmk__strcase_any_of(value, "standby", "demote", "stop", NULL);
 435 }
 436 
 437 /*!
 438  * \internal
 439  * \brief Validate (and possibly reset) resource action's on_fail meta-attribute
 440  *
 441  * \param[in]     rsc            Resource that action is for
 442  * \param[in]     action_name    Action name
 443  * \param[in]     action_config  Action configuration XML from CIB (if any)
 444  * \param[in,out] meta           Table of action meta-attributes
 445  */
 446 static void
 447 validate_on_fail(const pcmk_resource_t *rsc, const char *action_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 448                  const xmlNode *action_config, GHashTable *meta)
 449 {
 450     const char *name = NULL;
 451     const char *role = NULL;
 452     const char *interval_spec = NULL;
 453     const char *value = g_hash_table_lookup(meta, XML_OP_ATTR_ON_FAIL);
 454     char *key = NULL;
 455     char *new_value = NULL;
 456 
 457     // Stop actions can only use certain on-fail values
 458     if (pcmk__str_eq(action_name, PCMK_ACTION_STOP, pcmk__str_none)
 459         && !valid_stop_on_fail(value)) {
 460 
 461         pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for %s stop "
 462                          "action to default value because '%s' is not "
 463                          "allowed for stop", rsc->id, value);
 464         g_hash_table_remove(meta, XML_OP_ATTR_ON_FAIL);
 465         return;
 466     }
 467 
 468     /* Demote actions default on-fail to the on-fail value for the first
 469      * recurring monitor for the promoted role (if any).
 470      */
 471     if (pcmk__str_eq(action_name, PCMK_ACTION_DEMOTE, pcmk__str_none)
 472         && (value == NULL)) {
 473 
 474         /* @TODO This does not consider promote options set in a meta-attribute
 475          * block (which may have rules that need to be evaluated) rather than
 476          * XML properties.
 477          */
 478         for (xmlNode *operation = first_named_child(rsc->ops_xml, XML_ATTR_OP);
 479              operation != NULL; operation = crm_next_same_xml(operation)) {
 480             bool enabled = false;
 481             const char *promote_on_fail = NULL;
 482 
 483             /* We only care about explicit on-fail (if promote uses default, so
 484              * can demote)
 485              */
 486             promote_on_fail = crm_element_value(operation, XML_OP_ATTR_ON_FAIL);
 487             if (promote_on_fail == NULL) {
 488                 continue;
 489             }
 490 
 491             // We only care about recurring monitors for the promoted role
 492             name = crm_element_value(operation, "name");
 493             role = crm_element_value(operation, "role");
 494             if (!pcmk__str_eq(name, PCMK_ACTION_MONITOR, pcmk__str_none)
 495                 || !pcmk__strcase_any_of(role, PCMK__ROLE_PROMOTED,
 496                                          PCMK__ROLE_PROMOTED_LEGACY, NULL)) {
 497                 continue;
 498             }
 499             interval_spec = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
 500             if (crm_parse_interval_spec(interval_spec) == 0) {
 501                 continue;
 502             }
 503 
 504             // We only care about enabled monitors
 505             if ((pcmk__xe_get_bool_attr(operation, "enabled",
 506                                         &enabled) == pcmk_rc_ok) && !enabled) {
 507                 continue;
 508             }
 509 
 510             // Demote actions can't default to on-fail="demote"
 511             if (pcmk__str_eq(promote_on_fail, "demote", pcmk__str_casei)) {
 512                 continue;
 513             }
 514 
 515             // Use value from first applicable promote action found
 516             key = strdup(XML_OP_ATTR_ON_FAIL);
 517             new_value = strdup(promote_on_fail);
 518             CRM_ASSERT((key != NULL) && (new_value != NULL));
 519             g_hash_table_insert(meta, key, new_value);
 520         }
 521         return;
 522     }
 523 
 524     if (pcmk__str_eq(action_name, PCMK_ACTION_LRM_DELETE, pcmk__str_none)
 525         && !pcmk__str_eq(value, "ignore", pcmk__str_casei)) {
 526         key = strdup(XML_OP_ATTR_ON_FAIL);
 527         new_value = strdup("ignore");
 528         CRM_ASSERT((key != NULL) && (new_value != NULL));
 529         g_hash_table_insert(meta, key, new_value);
 530         return;
 531     }
 532 
 533     // on-fail="demote" is allowed only for certain actions
 534     if (pcmk__str_eq(value, "demote", pcmk__str_casei)) {
 535         name = crm_element_value(action_config, "name");
 536         role = crm_element_value(action_config, "role");
 537         interval_spec = crm_element_value(action_config,
 538                                           XML_LRM_ATTR_INTERVAL);
 539 
 540         if (!pcmk__str_eq(name, PCMK_ACTION_PROMOTE, pcmk__str_none)
 541             && (!pcmk__str_eq(name, PCMK_ACTION_MONITOR, pcmk__str_none)
 542                 || !pcmk__strcase_any_of(role, PCMK__ROLE_PROMOTED,
 543                                          PCMK__ROLE_PROMOTED_LEGACY, NULL)
 544                 || (crm_parse_interval_spec(interval_spec) == 0))) {
 545             pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for %s %s "
 546                              "action to default value because 'demote' is not "
 547                              "allowed for it", rsc->id, name);
 548             g_hash_table_remove(meta, XML_OP_ATTR_ON_FAIL);
 549             return;
 550         }
 551     }
 552 }
 553 
 554 static int
 555 unpack_timeout(const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 556 {
 557     int timeout_ms = crm_get_msec(value);
 558 
 559     if (timeout_ms < 0) {
 560         timeout_ms = PCMK_DEFAULT_ACTION_TIMEOUT_MS;
 561     }
 562     return timeout_ms;
 563 }
 564 
 565 // true if value contains valid, non-NULL interval origin for recurring op
 566 static bool
 567 unpack_interval_origin(const char *value, const xmlNode *xml_obj,
     /* [previous][next][first][last][top][bottom][index][help] */
 568                        guint interval_ms, const crm_time_t *now,
 569                        long long *start_delay)
 570 {
 571     long long result = 0;
 572     guint interval_sec = interval_ms / 1000;
 573     crm_time_t *origin = NULL;
 574 
 575     // Ignore unspecified values and non-recurring operations
 576     if ((value == NULL) || (interval_ms == 0) || (now == NULL)) {
 577         return false;
 578     }
 579 
 580     // Parse interval origin from text
 581     origin = crm_time_new(value);
 582     if (origin == NULL) {
 583         pcmk__config_err("Ignoring '" XML_OP_ATTR_ORIGIN "' for operation "
 584                          "'%s' because '%s' is not valid",
 585                          (ID(xml_obj)? ID(xml_obj) : "(missing ID)"), value);
 586         return false;
 587     }
 588 
 589     // Get seconds since origin (negative if origin is in the future)
 590     result = crm_time_get_seconds(now) - crm_time_get_seconds(origin);
 591     crm_time_free(origin);
 592 
 593     // Calculate seconds from closest interval to now
 594     result = result % interval_sec;
 595 
 596     // Calculate seconds remaining until next interval
 597     result = ((result <= 0)? 0 : interval_sec) - result;
 598     crm_info("Calculated a start delay of %llds for operation '%s'",
 599              result,
 600              (ID(xml_obj)? ID(xml_obj) : "(unspecified)"));
 601 
 602     if (start_delay != NULL) {
 603         *start_delay = result * 1000; // milliseconds
 604     }
 605     return true;
 606 }
 607 
 608 static int
 609 unpack_start_delay(const char *value, GHashTable *meta)
     /* [previous][next][first][last][top][bottom][index][help] */
 610 {
 611     int start_delay = 0;
 612 
 613     if (value != NULL) {
 614         start_delay = crm_get_msec(value);
 615 
 616         if (start_delay < 0) {
 617             start_delay = 0;
 618         }
 619 
 620         if (meta) {
 621             g_hash_table_replace(meta, strdup(XML_OP_ATTR_START_DELAY),
 622                                  pcmk__itoa(start_delay));
 623         }
 624     }
 625 
 626     return start_delay;
 627 }
 628 
 629 /*!
 630  * \internal
 631  * \brief Find a resource's most frequent recurring monitor
 632  *
 633  * \param[in] rsc  Resource to check
 634  *
 635  * \return Operation XML configured for most frequent recurring monitor for
 636  *         \p rsc (if any)
 637  */
 638 static xmlNode *
 639 most_frequent_monitor(const pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 640 {
 641     guint min_interval_ms = G_MAXUINT;
 642     xmlNode *op = NULL;
 643 
 644     for (xmlNode *operation = first_named_child(rsc->ops_xml, XML_ATTR_OP);
 645          operation != NULL; operation = crm_next_same_xml(operation)) {
 646         bool enabled = false;
 647         guint interval_ms = 0;
 648         const char *interval_spec = crm_element_value(operation,
 649                                                       XML_LRM_ATTR_INTERVAL);
 650 
 651         // We only care about enabled recurring monitors
 652         if (!pcmk__str_eq(crm_element_value(operation, "name"),
 653                           PCMK_ACTION_MONITOR, pcmk__str_none)) {
 654             continue;
 655         }
 656         interval_ms = crm_parse_interval_spec(interval_spec);
 657         if (interval_ms == 0) {
 658             continue;
 659         }
 660 
 661         // @TODO This does not account for rules, defaults, etc.
 662         if ((pcmk__xe_get_bool_attr(operation, "enabled",
 663                                     &enabled) == pcmk_rc_ok) && !enabled) {
 664             continue;
 665         }
 666 
 667         if (interval_ms < min_interval_ms) {
 668             min_interval_ms = interval_ms;
 669             op = operation;
 670         }
 671     }
 672     return op;
 673 }
 674 
 675 /*!
 676  * \internal
 677  * \brief Unpack action meta-attributes
 678  *
 679  * \param[in,out] rsc            Resource that action is for
 680  * \param[in]     node           Node that action is on
 681  * \param[in]     action_name    Action name
 682  * \param[in]     interval_ms    Action interval (in milliseconds)
 683  * \param[in]     action_config  Action XML configuration from CIB (if any)
 684  *
 685  * Unpack a resource action's meta-attributes (normalizing the interval,
 686  * timeout, and start delay values as integer milliseconds) from its CIB XML
 687  * configuration (including defaults).
 688  *
 689  * \return Newly allocated hash table with normalized action meta-attributes
 690  */
 691 GHashTable *
 692 pcmk__unpack_action_meta(pcmk_resource_t *rsc, const pcmk_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 693                          const char *action_name, guint interval_ms,
 694                          const xmlNode *action_config)
 695 {
 696     GHashTable *meta = NULL;
 697     char *name = NULL;
 698     char *value = NULL;
 699     const char *timeout_spec = NULL;
 700     const char *str = NULL;
 701 
 702     pe_rsc_eval_data_t rsc_rule_data = {
 703         .standard = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS),
 704         .provider = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER),
 705         .agent = crm_element_value(rsc->xml, XML_EXPR_ATTR_TYPE),
 706     };
 707 
 708     pe_op_eval_data_t op_rule_data = {
 709         .op_name = action_name,
 710         .interval = interval_ms,
 711     };
 712 
 713     pe_rule_eval_data_t rule_data = {
 714         .node_hash = (node == NULL)? NULL : node->details->attrs,
 715         .role = pcmk_role_unknown,
 716         .now = rsc->cluster->now,
 717         .match_data = NULL,
 718         .rsc_data = &rsc_rule_data,
 719         .op_data = &op_rule_data,
 720     };
 721 
 722     meta = pcmk__strkey_table(free, free);
 723 
 724     // Cluster-wide <op_defaults> <meta_attributes>
 725     pe__unpack_dataset_nvpairs(rsc->cluster->op_defaults, XML_TAG_META_SETS,
 726                                &rule_data, meta, NULL, FALSE, rsc->cluster);
 727 
 728     // Derive default timeout for probes from recurring monitor timeouts
 729     if (pcmk_is_probe(action_name, interval_ms)) {
 730         xmlNode *min_interval_mon = most_frequent_monitor(rsc);
 731 
 732         if (min_interval_mon != NULL) {
 733             /* @TODO This does not consider timeouts set in meta_attributes
 734              * blocks (which may also have rules that need to be evaluated).
 735              */
 736             timeout_spec = crm_element_value(min_interval_mon,
 737                                              XML_ATTR_TIMEOUT);
 738             if (timeout_spec != NULL) {
 739                 pe_rsc_trace(rsc,
 740                              "Setting default timeout for %s probe to "
 741                              "most frequent monitor's timeout '%s'",
 742                              rsc->id, timeout_spec);
 743                 name = strdup(XML_ATTR_TIMEOUT);
 744                 value = strdup(timeout_spec);
 745                 CRM_ASSERT((name != NULL) && (value != NULL));
 746                 g_hash_table_insert(meta, name, value);
 747             }
 748         }
 749     }
 750 
 751     if (action_config != NULL) {
 752         // <op> <meta_attributes> take precedence over defaults
 753         pe__unpack_dataset_nvpairs(action_config, XML_TAG_META_SETS, &rule_data,
 754                                    meta, NULL, TRUE, rsc->cluster);
 755 
 756         /* Anything set as an <op> XML property has highest precedence.
 757          * This ensures we use the name and interval from the <op> tag.
 758          * (See below for the only exception, fence device start/probe timeout.)
 759          */
 760         for (xmlAttrPtr attr = action_config->properties;
 761              attr != NULL; attr = attr->next) {
 762             name = strdup((const char *) attr->name);
 763             value = strdup(pcmk__xml_attr_value(attr));
 764 
 765             CRM_ASSERT((name != NULL) && (value != NULL));
 766             g_hash_table_insert(meta, name, value);
 767         }
 768     }
 769 
 770     g_hash_table_remove(meta, XML_ATTR_ID);
 771 
 772     // Normalize interval to milliseconds
 773     if (interval_ms > 0) {
 774         name = strdup(XML_LRM_ATTR_INTERVAL);
 775         CRM_ASSERT(name != NULL);
 776         value = crm_strdup_printf("%u", interval_ms);
 777         g_hash_table_insert(meta, name, value);
 778     } else {
 779         g_hash_table_remove(meta, XML_LRM_ATTR_INTERVAL);
 780     }
 781 
 782     /* Timeout order of precedence (highest to lowest):
 783      *   1. pcmk_monitor_timeout resource parameter (only for starts and probes
 784      *      when rsc has pcmk_ra_cap_fence_params; this gets used for recurring
 785      *      monitors via the executor instead)
 786      *   2. timeout configured in <op> (with <op timeout> taking precedence over
 787      *      <op> <meta_attributes>)
 788      *   3. timeout configured in <op_defaults> <meta_attributes>
 789      *   4. PCMK_DEFAULT_ACTION_TIMEOUT_MS
 790      */
 791 
 792     // Check for pcmk_monitor_timeout
 793     if (pcmk_is_set(pcmk_get_ra_caps(rsc_rule_data.standard),
 794                     pcmk_ra_cap_fence_params)
 795         && (pcmk__str_eq(action_name, PCMK_ACTION_START, pcmk__str_none)
 796             || pcmk_is_probe(action_name, interval_ms))) {
 797 
 798         GHashTable *params = pe_rsc_params(rsc, node, rsc->cluster);
 799 
 800         timeout_spec = g_hash_table_lookup(params, "pcmk_monitor_timeout");
 801         if (timeout_spec != NULL) {
 802             pe_rsc_trace(rsc,
 803                          "Setting timeout for %s %s to "
 804                          "pcmk_monitor_timeout (%s)",
 805                          rsc->id, action_name, timeout_spec);
 806             name = strdup(XML_ATTR_TIMEOUT);
 807             value = strdup(timeout_spec);
 808             CRM_ASSERT((name != NULL) && (value != NULL));
 809             g_hash_table_insert(meta, name, value);
 810         }
 811     }
 812 
 813     // Normalize timeout to positive milliseconds
 814     name = strdup(XML_ATTR_TIMEOUT);
 815     CRM_ASSERT(name != NULL);
 816     timeout_spec = g_hash_table_lookup(meta, XML_ATTR_TIMEOUT);
 817     g_hash_table_insert(meta, name, pcmk__itoa(unpack_timeout(timeout_spec)));
 818 
 819     // Ensure on-fail has a valid value
 820     validate_on_fail(rsc, action_name, action_config, meta);
 821 
 822     // Normalize start-delay
 823     str = g_hash_table_lookup(meta, XML_OP_ATTR_START_DELAY);
 824     if (str != NULL) {
 825         unpack_start_delay(str, meta);
 826     } else {
 827         long long start_delay = 0;
 828 
 829         str = g_hash_table_lookup(meta, XML_OP_ATTR_ORIGIN);
 830         if (unpack_interval_origin(str, action_config, interval_ms,
 831                                    rsc->cluster->now, &start_delay)) {
 832             name = strdup(XML_OP_ATTR_START_DELAY);
 833             CRM_ASSERT(name != NULL);
 834             g_hash_table_insert(meta, name,
 835                                 crm_strdup_printf("%lld", start_delay));
 836         }
 837     }
 838     return meta;
 839 }
 840 
 841 /*!
 842  * \internal
 843  * \brief Determine an action's quorum and fencing dependency
 844  *
 845  * \param[in] rsc          Resource that action is for
 846  * \param[in] action_name  Name of action being unpacked
 847  *
 848  * \return Quorum and fencing dependency appropriate to action
 849  */
 850 enum rsc_start_requirement
 851 pcmk__action_requires(const pcmk_resource_t *rsc, const char *action_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 852 {
 853     const char *value = NULL;
 854     enum rsc_start_requirement requires = pcmk_requires_nothing;
 855 
 856     CRM_CHECK((rsc != NULL) && (action_name != NULL), return requires);
 857 
 858     if (!pcmk__strcase_any_of(action_name, PCMK_ACTION_START,
 859                               PCMK_ACTION_PROMOTE, NULL)) {
 860         value = "nothing (not start or promote)";
 861 
 862     } else if (pcmk_is_set(rsc->flags, pcmk_rsc_needs_fencing)) {
 863         requires = pcmk_requires_fencing;
 864         value = "fencing";
 865 
 866     } else if (pcmk_is_set(rsc->flags, pcmk_rsc_needs_quorum)) {
 867         requires = pcmk_requires_quorum;
 868         value = "quorum";
 869 
 870     } else {
 871         value = "nothing";
 872     }
 873     pe_rsc_trace(rsc, "%s of %s requires %s", action_name, rsc->id, value);
 874     return requires;
 875 }
 876 
 877 /*!
 878  * \internal
 879  * \brief Parse action failure response from a user-provided string
 880  *
 881  * \param[in] rsc          Resource that action is for
 882  * \param[in] action_name  Name of action
 883  * \param[in] interval_ms  Action interval (in milliseconds)
 884  * \param[in] value        User-provided configuration value for on-fail
 885  *
 886  * \return Action failure response parsed from \p text
 887  */
 888 enum action_fail_response
 889 pcmk__parse_on_fail(const pcmk_resource_t *rsc, const char *action_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 890                     guint interval_ms, const char *value)
 891 {
 892     const char *desc = NULL;
 893     bool needs_remote_reset = false;
 894     enum action_fail_response on_fail = pcmk_on_fail_ignore;
 895 
 896     if (value == NULL) {
 897         // Use default
 898 
 899     } else if (pcmk__str_eq(value, "block", pcmk__str_casei)) {
 900         on_fail = pcmk_on_fail_block;
 901         desc = "block";
 902 
 903     } else if (pcmk__str_eq(value, "fence", pcmk__str_casei)) {
 904         if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_fencing_enabled)) {
 905             on_fail = pcmk_on_fail_fence_node;
 906             desc = "node fencing";
 907         } else {
 908             pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for "
 909                              "%s of %s to 'stop' because 'fence' is not "
 910                              "valid when fencing is disabled",
 911                              action_name, rsc->id);
 912             on_fail = pcmk_on_fail_stop;
 913             desc = "stop resource";
 914         }
 915 
 916     } else if (pcmk__str_eq(value, "standby", pcmk__str_casei)) {
 917         on_fail = pcmk_on_fail_standby_node;
 918         desc = "node standby";
 919 
 920     } else if (pcmk__strcase_any_of(value, "ignore", PCMK__VALUE_NOTHING,
 921                                     NULL)) {
 922         desc = "ignore";
 923 
 924     } else if (pcmk__str_eq(value, "migrate", pcmk__str_casei)) {
 925         on_fail = pcmk_on_fail_ban;
 926         desc = "force migration";
 927 
 928     } else if (pcmk__str_eq(value, "stop", pcmk__str_casei)) {
 929         on_fail = pcmk_on_fail_stop;
 930         desc = "stop resource";
 931 
 932     } else if (pcmk__str_eq(value, "restart", pcmk__str_casei)) {
 933         on_fail = pcmk_on_fail_restart;
 934         desc = "restart (and possibly migrate)";
 935 
 936     } else if (pcmk__str_eq(value, "restart-container", pcmk__str_casei)) {
 937         if (rsc->container == NULL) {
 938             pe_rsc_debug(rsc,
 939                          "Using default " XML_OP_ATTR_ON_FAIL
 940                          " for %s of %s because it does not have a container",
 941                          action_name, rsc->id);
 942         } else {
 943             on_fail = pcmk_on_fail_restart_container;
 944             desc = "restart container (and possibly migrate)";
 945         }
 946 
 947     } else if (pcmk__str_eq(value, "demote", pcmk__str_casei)) {
 948         on_fail = pcmk_on_fail_demote;
 949         desc = "demote instance";
 950 
 951     } else {
 952         pcmk__config_err("Using default '" XML_OP_ATTR_ON_FAIL "' for "
 953                          "%s of %s because '%s' is not valid",
 954                          action_name, rsc->id, value);
 955     }
 956 
 957     /* Remote node connections are handled specially. Failures that result
 958      * in dropping an active connection must result in fencing. The only
 959      * failures that don't are probes and starts. The user can explicitly set
 960      * on-fail="fence" to fence after start failures.
 961      */
 962     if (pe__resource_is_remote_conn(rsc)
 963         && !pcmk_is_probe(action_name, interval_ms)
 964         && !pcmk__str_eq(action_name, PCMK_ACTION_START, pcmk__str_none)) {
 965         needs_remote_reset = true;
 966         if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
 967             desc = NULL; // Force default for unmanaged connections
 968         }
 969     }
 970 
 971     if (desc != NULL) {
 972         // Explicit value used, default not needed
 973 
 974     } else if (rsc->container != NULL) {
 975         on_fail = pcmk_on_fail_restart_container;
 976         desc = "restart container (and possibly migrate) (default)";
 977 
 978     } else if (needs_remote_reset) {
 979         if (pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
 980             if (pcmk_is_set(rsc->cluster->flags,
 981                             pcmk_sched_fencing_enabled)) {
 982                 desc = "fence remote node (default)";
 983             } else {
 984                 desc = "recover remote node connection (default)";
 985             }
 986             on_fail = pcmk_on_fail_reset_remote;
 987         } else {
 988             on_fail = pcmk_on_fail_stop;
 989             desc = "stop unmanaged remote node (enforcing default)";
 990         }
 991 
 992     } else if (pcmk__str_eq(action_name, PCMK_ACTION_STOP, pcmk__str_none)) {
 993         if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_fencing_enabled)) {
 994             on_fail = pcmk_on_fail_fence_node;
 995             desc = "resource fence (default)";
 996         } else {
 997             on_fail = pcmk_on_fail_block;
 998             desc = "resource block (default)";
 999         }
1000 
1001     } else {
1002         on_fail = pcmk_on_fail_restart;
1003         desc = "restart (and possibly migrate) (default)";
1004     }
1005 
1006     pe_rsc_trace(rsc, "Failure handling for %s-interval %s of %s: %s",
1007                  pcmk__readable_interval(interval_ms), action_name,
1008                  rsc->id, desc);
1009     return on_fail;
1010 }
1011 
1012 /*!
1013  * \internal
1014  * \brief Determine a resource's role after failure of an action
1015  *
1016  * \param[in] rsc          Resource that action is for
1017  * \param[in] action_name  Action name
1018  * \param[in] on_fail      Failure handling for action
1019  * \param[in] meta         Unpacked action meta-attributes
1020  *
1021  * \return Resource role that results from failure of action
1022  */
1023 enum rsc_role_e
1024 pcmk__role_after_failure(const pcmk_resource_t *rsc, const char *action_name,
     /* [previous][next][first][last][top][bottom][index][help] */
1025                          enum action_fail_response on_fail, GHashTable *meta)
1026 {
1027     const char *value = NULL;
1028     enum rsc_role_e role = pcmk_role_unknown;
1029 
1030     // Set default for role after failure specially in certain circumstances
1031     switch (on_fail) {
1032         case pcmk_on_fail_stop:
1033             role = pcmk_role_stopped;
1034             break;
1035 
1036         case pcmk_on_fail_reset_remote:
1037             if (rsc->remote_reconnect_ms != 0) {
1038                 role = pcmk_role_stopped;
1039             }
1040             break;
1041 
1042         default:
1043             break;
1044     }
1045 
1046     // @COMPAT Check for explicitly configured role (deprecated)
1047     value = g_hash_table_lookup(meta, "role_after_failure");
1048     if (value != NULL) {
1049         pe_warn_once(pcmk__wo_role_after,
1050                     "Support for role_after_failure is deprecated "
1051                     "and will be removed in a future release");
1052         if (role == pcmk_role_unknown) {
1053             role = text2role(value);
1054         }
1055     }
1056 
1057     if (role == pcmk_role_unknown) {
1058         // Use default
1059         if (pcmk__str_eq(action_name, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
1060             role = pcmk_role_unpromoted;
1061         } else {
1062             role = pcmk_role_started;
1063         }
1064     }
1065     pe_rsc_trace(rsc, "Role after %s %s failure is: %s",
1066                  rsc->id, action_name, role2text(role));
1067     return role;
1068 }
1069 
1070 /*!
1071  * \internal
1072  * \brief Unpack action configuration
1073  *
1074  * Unpack a resource action's meta-attributes (normalizing the interval,
1075  * timeout, and start delay values as integer milliseconds), requirements, and
1076  * failure policy from its CIB XML configuration (including defaults).
1077  *
1078  * \param[in,out] action       Resource action to unpack into
1079  * \param[in]     xml_obj      Action configuration XML (NULL for defaults only)
1080  * \param[in]     interval_ms  How frequently to perform the operation
1081  */
1082 static void
1083 unpack_operation(pcmk_action_t *action, const xmlNode *xml_obj,
     /* [previous][next][first][last][top][bottom][index][help] */
1084                  guint interval_ms)
1085 {
1086     const char *value = NULL;
1087 
1088     action->meta = pcmk__unpack_action_meta(action->rsc, action->node,
1089                                             action->task, interval_ms, xml_obj);
1090     action->needs = pcmk__action_requires(action->rsc, action->task);
1091 
1092     value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ON_FAIL);
1093     action->on_fail = pcmk__parse_on_fail(action->rsc, action->task,
1094                                           interval_ms, value);
1095 
1096     action->fail_role = pcmk__role_after_failure(action->rsc, action->task,
1097                                                  action->on_fail, action->meta);
1098 }
1099 
1100 /*!
1101  * \brief Create or update an action object
1102  *
1103  * \param[in,out] rsc          Resource that action is for (if any)
1104  * \param[in,out] key          Action key (must be non-NULL)
1105  * \param[in]     task         Action name (must be non-NULL)
1106  * \param[in]     on_node      Node that action is on (if any)
1107  * \param[in]     optional     Whether action should be considered optional
1108  * \param[in,out] scheduler    Scheduler data
1109  *
1110  * \return Action object corresponding to arguments (guaranteed not to be
1111  *         \c NULL)
1112  * \note This function takes ownership of (and might free) \p key, and
1113  *       \p scheduler takes ownership of the returned action (the caller should
1114  *       not free it).
1115  */
1116 pcmk_action_t *
1117 custom_action(pcmk_resource_t *rsc, char *key, const char *task,
     /* [previous][next][first][last][top][bottom][index][help] */
1118               const pcmk_node_t *on_node, gboolean optional,
1119               pcmk_scheduler_t *scheduler)
1120 {
1121     pcmk_action_t *action = NULL;
1122 
1123     CRM_ASSERT((key != NULL) && (task != NULL) && (scheduler != NULL));
1124 
1125     action = find_existing_action(key, rsc, on_node, scheduler);
1126     if (action == NULL) {
1127         action = new_action(key, task, rsc, on_node, optional, scheduler);
1128     } else {
1129         free(key);
1130     }
1131 
1132     update_action_optional(action, optional);
1133 
1134     if (rsc != NULL) {
1135         if ((action->node != NULL) && (action->op_entry != NULL)
1136             && !pcmk_is_set(action->flags, pcmk_action_attrs_evaluated)) {
1137 
1138             GHashTable *attrs = action->node->details->attrs;
1139 
1140             if (action->extra != NULL) {
1141                 g_hash_table_destroy(action->extra);
1142             }
1143             action->extra = pcmk__unpack_action_rsc_params(action->op_entry,
1144                                                            attrs, scheduler);
1145             pe__set_action_flags(action, pcmk_action_attrs_evaluated);
1146         }
1147 
1148         update_resource_action_runnable(action, scheduler);
1149         update_resource_flags_for_action(rsc, action);
1150     }
1151 
1152     if (action->extra == NULL) {
1153         action->extra = pcmk__strkey_table(free, free);
1154     }
1155 
1156     return action;
1157 }
1158 
1159 pcmk_action_t *
1160 get_pseudo_op(const char *name, pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
1161 {
1162     pcmk_action_t *op = lookup_singleton(scheduler, name);
1163 
1164     if (op == NULL) {
1165         op = custom_action(NULL, strdup(name), name, NULL, TRUE, scheduler);
1166         pe__set_action_flags(op, pcmk_action_pseudo|pcmk_action_runnable);
1167     }
1168     return op;
1169 }
1170 
1171 static GList *
1172 find_unfencing_devices(GList *candidates, GList *matches) 
     /* [previous][next][first][last][top][bottom][index][help] */
1173 {
1174     for (GList *gIter = candidates; gIter != NULL; gIter = gIter->next) {
1175         pcmk_resource_t *candidate = gIter->data;
1176 
1177         if (candidate->children != NULL) {
1178             matches = find_unfencing_devices(candidate->children, matches);
1179 
1180         } else if (!pcmk_is_set(candidate->flags, pcmk_rsc_fence_device)) {
1181             continue;
1182 
1183         } else if (pcmk_is_set(candidate->flags, pcmk_rsc_needs_unfencing)) {
1184             matches = g_list_prepend(matches, candidate);
1185 
1186         } else if (pcmk__str_eq(g_hash_table_lookup(candidate->meta,
1187                                                     PCMK_STONITH_PROVIDES),
1188                                 PCMK__VALUE_UNFENCING,
1189                                 pcmk__str_casei)) {
1190             matches = g_list_prepend(matches, candidate);
1191         }
1192     }
1193     return matches;
1194 }
1195 
1196 static int
1197 node_priority_fencing_delay(const pcmk_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
1198                             const pcmk_scheduler_t *scheduler)
1199 {
1200     int member_count = 0;
1201     int online_count = 0;
1202     int top_priority = 0;
1203     int lowest_priority = 0;
1204     GList *gIter = NULL;
1205 
1206     // `priority-fencing-delay` is disabled
1207     if (scheduler->priority_fencing_delay <= 0) {
1208         return 0;
1209     }
1210 
1211     /* No need to request a delay if the fencing target is not a normal cluster
1212      * member, for example if it's a remote node or a guest node. */
1213     if (node->details->type != pcmk_node_variant_cluster) {
1214         return 0;
1215     }
1216 
1217     // No need to request a delay if the fencing target is in our partition
1218     if (node->details->online) {
1219         return 0;
1220     }
1221 
1222     for (gIter = scheduler->nodes; gIter != NULL; gIter = gIter->next) {
1223         pcmk_node_t *n = gIter->data;
1224 
1225         if (n->details->type != pcmk_node_variant_cluster) {
1226             continue;
1227         }
1228 
1229         member_count ++;
1230 
1231         if (n->details->online) {
1232             online_count++;
1233         }
1234 
1235         if (member_count == 1
1236             || n->details->priority > top_priority) {
1237             top_priority = n->details->priority;
1238         }
1239 
1240         if (member_count == 1
1241             || n->details->priority < lowest_priority) {
1242             lowest_priority = n->details->priority;
1243         }
1244     }
1245 
1246     // No need to delay if we have more than half of the cluster members
1247     if (online_count > member_count / 2) {
1248         return 0;
1249     }
1250 
1251     /* All the nodes have equal priority.
1252      * Any configured corresponding `pcmk_delay_base/max` will be applied. */
1253     if (lowest_priority == top_priority) {
1254         return 0;
1255     }
1256 
1257     if (node->details->priority < top_priority) {
1258         return 0;
1259     }
1260 
1261     return scheduler->priority_fencing_delay;
1262 }
1263 
1264 pcmk_action_t *
1265 pe_fence_op(pcmk_node_t *node, const char *op, bool optional,
     /* [previous][next][first][last][top][bottom][index][help] */
1266             const char *reason, bool priority_delay,
1267             pcmk_scheduler_t *scheduler)
1268 {
1269     char *op_key = NULL;
1270     pcmk_action_t *stonith_op = NULL;
1271 
1272     if(op == NULL) {
1273         op = scheduler->stonith_action;
1274     }
1275 
1276     op_key = crm_strdup_printf("%s-%s-%s",
1277                                PCMK_ACTION_STONITH, node->details->uname, op);
1278 
1279     stonith_op = lookup_singleton(scheduler, op_key);
1280     if(stonith_op == NULL) {
1281         stonith_op = custom_action(NULL, op_key, PCMK_ACTION_STONITH, node,
1282                                    TRUE, scheduler);
1283 
1284         add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET, node->details->uname);
1285         add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET_UUID, node->details->id);
1286         add_hash_param(stonith_op->meta, "stonith_action", op);
1287 
1288         if (pcmk_is_set(scheduler->flags, pcmk_sched_enable_unfencing)) {
1289             /* Extra work to detect device changes
1290              */
1291             GString *digests_all = g_string_sized_new(1024);
1292             GString *digests_secure = g_string_sized_new(1024);
1293 
1294             GList *matches = find_unfencing_devices(scheduler->resources, NULL);
1295 
1296             char *key = NULL;
1297             char *value = NULL;
1298 
1299             for (GList *gIter = matches; gIter != NULL; gIter = gIter->next) {
1300                 pcmk_resource_t *match = gIter->data;
1301                 const char *agent = g_hash_table_lookup(match->meta,
1302                                                         XML_ATTR_TYPE);
1303                 op_digest_cache_t *data = NULL;
1304 
1305                 data = pe__compare_fencing_digest(match, agent, node,
1306                                                   scheduler);
1307                 if (data->rc == pcmk__digest_mismatch) {
1308                     optional = FALSE;
1309                     crm_notice("Unfencing node %s because the definition of "
1310                                "%s changed", pe__node_name(node), match->id);
1311                     if (!pcmk__is_daemon && scheduler->priv != NULL) {
1312                         pcmk__output_t *out = scheduler->priv;
1313 
1314                         out->info(out,
1315                                   "notice: Unfencing node %s because the "
1316                                   "definition of %s changed",
1317                                   pe__node_name(node), match->id);
1318                     }
1319                 }
1320 
1321                 pcmk__g_strcat(digests_all,
1322                                match->id, ":", agent, ":",
1323                                data->digest_all_calc, ",", NULL);
1324                 pcmk__g_strcat(digests_secure,
1325                                match->id, ":", agent, ":",
1326                                data->digest_secure_calc, ",", NULL);
1327             }
1328             key = strdup(XML_OP_ATTR_DIGESTS_ALL);
1329             value = strdup((const char *) digests_all->str);
1330             CRM_ASSERT((key != NULL) && (value != NULL));
1331             g_hash_table_insert(stonith_op->meta, key, value);
1332             g_string_free(digests_all, TRUE);
1333 
1334             key = strdup(XML_OP_ATTR_DIGESTS_SECURE);
1335             value = strdup((const char *) digests_secure->str);
1336             CRM_ASSERT((key != NULL) && (value != NULL));
1337             g_hash_table_insert(stonith_op->meta, key, value);
1338             g_string_free(digests_secure, TRUE);
1339         }
1340 
1341     } else {
1342         free(op_key);
1343     }
1344 
1345     if (scheduler->priority_fencing_delay > 0
1346 
1347             /* It's a suitable case where `priority-fencing-delay` applies.
1348              * At least add `priority-fencing-delay` field as an indicator. */
1349         && (priority_delay
1350 
1351             /* The priority delay needs to be recalculated if this function has
1352              * been called by schedule_fencing_and_shutdowns() after node
1353              * priority has already been calculated by native_add_running().
1354              */
1355             || g_hash_table_lookup(stonith_op->meta,
1356                                    XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY) != NULL)) {
1357 
1358             /* Add `priority-fencing-delay` to the fencing op even if it's 0 for
1359              * the targeting node. So that it takes precedence over any possible
1360              * `pcmk_delay_base/max`.
1361              */
1362             char *delay_s = pcmk__itoa(node_priority_fencing_delay(node,
1363                                                                    scheduler));
1364 
1365             g_hash_table_insert(stonith_op->meta,
1366                                 strdup(XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY),
1367                                 delay_s);
1368     }
1369 
1370     if(optional == FALSE && pe_can_fence(scheduler, node)) {
1371         pe__clear_action_flags(stonith_op, pcmk_action_optional);
1372         pe_action_set_reason(stonith_op, reason, false);
1373 
1374     } else if(reason && stonith_op->reason == NULL) {
1375         stonith_op->reason = strdup(reason);
1376     }
1377 
1378     return stonith_op;
1379 }
1380 
1381 void
1382 pe_free_action(pcmk_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
1383 {
1384     if (action == NULL) {
1385         return;
1386     }
1387     g_list_free_full(action->actions_before, free);
1388     g_list_free_full(action->actions_after, free);
1389     if (action->extra) {
1390         g_hash_table_destroy(action->extra);
1391     }
1392     if (action->meta) {
1393         g_hash_table_destroy(action->meta);
1394     }
1395     free(action->cancel_task);
1396     free(action->reason);
1397     free(action->task);
1398     free(action->uuid);
1399     free(action->node);
1400     free(action);
1401 }
1402 
1403 int
1404 pe_get_configured_timeout(pcmk_resource_t *rsc, const char *action,
     /* [previous][next][first][last][top][bottom][index][help] */
1405                           pcmk_scheduler_t *scheduler)
1406 {
1407     xmlNode *child = NULL;
1408     GHashTable *action_meta = NULL;
1409     const char *timeout_spec = NULL;
1410     int timeout_ms = 0;
1411 
1412     pe_rule_eval_data_t rule_data = {
1413         .node_hash = NULL,
1414         .role = pcmk_role_unknown,
1415         .now = scheduler->now,
1416         .match_data = NULL,
1417         .rsc_data = NULL,
1418         .op_data = NULL
1419     };
1420 
1421     for (child = first_named_child(rsc->ops_xml, XML_ATTR_OP);
1422          child != NULL; child = crm_next_same_xml(child)) {
1423         if (pcmk__str_eq(action, crm_element_value(child, XML_NVPAIR_ATTR_NAME),
1424                 pcmk__str_casei)) {
1425             timeout_spec = crm_element_value(child, XML_ATTR_TIMEOUT);
1426             break;
1427         }
1428     }
1429 
1430     if (timeout_spec == NULL && scheduler->op_defaults) {
1431         action_meta = pcmk__strkey_table(free, free);
1432         pe__unpack_dataset_nvpairs(scheduler->op_defaults, XML_TAG_META_SETS,
1433                                    &rule_data, action_meta, NULL, FALSE,
1434                                    scheduler);
1435         timeout_spec = g_hash_table_lookup(action_meta, XML_ATTR_TIMEOUT);
1436     }
1437 
1438     // @TODO check meta-attributes
1439     // @TODO maybe use min-interval monitor timeout as default for monitors
1440 
1441     timeout_ms = crm_get_msec(timeout_spec);
1442     if (timeout_ms < 0) {
1443         timeout_ms = PCMK_DEFAULT_ACTION_TIMEOUT_MS;
1444     }
1445 
1446     if (action_meta != NULL) {
1447         g_hash_table_destroy(action_meta);
1448     }
1449     return timeout_ms;
1450 }
1451 
1452 enum action_tasks
1453 get_complex_task(const pcmk_resource_t *rsc, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
1454 {
1455     enum action_tasks task = text2task(name);
1456 
1457     if ((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_primitive)) {
1458         switch (task) {
1459             case pcmk_action_stopped:
1460             case pcmk_action_started:
1461             case pcmk_action_demoted:
1462             case pcmk_action_promoted:
1463                 crm_trace("Folding %s back into its atomic counterpart for %s",
1464                           name, rsc->id);
1465                 --task;
1466                 break;
1467             default:
1468                 break;
1469         }
1470     }
1471     return task;
1472 }
1473 
1474 /*!
1475  * \internal
1476  * \brief Find first matching action in a list
1477  *
1478  * \param[in] input    List of actions to search
1479  * \param[in] uuid     If not NULL, action must have this UUID
1480  * \param[in] task     If not NULL, action must have this action name
1481  * \param[in] on_node  If not NULL, action must be on this node
1482  *
1483  * \return First action in list that matches criteria, or NULL if none
1484  */
1485 pcmk_action_t *
1486 find_first_action(const GList *input, const char *uuid, const char *task,
     /* [previous][next][first][last][top][bottom][index][help] */
1487                   const pcmk_node_t *on_node)
1488 {
1489     CRM_CHECK(uuid || task, return NULL);
1490 
1491     for (const GList *gIter = input; gIter != NULL; gIter = gIter->next) {
1492         pcmk_action_t *action = (pcmk_action_t *) gIter->data;
1493 
1494         if (uuid != NULL && !pcmk__str_eq(uuid, action->uuid, pcmk__str_casei)) {
1495             continue;
1496 
1497         } else if (task != NULL && !pcmk__str_eq(task, action->task, pcmk__str_casei)) {
1498             continue;
1499 
1500         } else if (on_node == NULL) {
1501             return action;
1502 
1503         } else if (action->node == NULL) {
1504             continue;
1505 
1506         } else if (on_node->details == action->node->details) {
1507             return action;
1508         }
1509     }
1510 
1511     return NULL;
1512 }
1513 
1514 GList *
1515 find_actions(GList *input, const char *key, const pcmk_node_t *on_node)
     /* [previous][next][first][last][top][bottom][index][help] */
1516 {
1517     GList *gIter = input;
1518     GList *result = NULL;
1519 
1520     CRM_CHECK(key != NULL, return NULL);
1521 
1522     for (; gIter != NULL; gIter = gIter->next) {
1523         pcmk_action_t *action = (pcmk_action_t *) gIter->data;
1524 
1525         if (!pcmk__str_eq(key, action->uuid, pcmk__str_casei)) {
1526             continue;
1527 
1528         } else if (on_node == NULL) {
1529             crm_trace("Action %s matches (ignoring node)", key);
1530             result = g_list_prepend(result, action);
1531 
1532         } else if (action->node == NULL) {
1533             crm_trace("Action %s matches (unallocated, assigning to %s)",
1534                       key, pe__node_name(on_node));
1535 
1536             action->node = pe__copy_node(on_node);
1537             result = g_list_prepend(result, action);
1538 
1539         } else if (on_node->details == action->node->details) {
1540             crm_trace("Action %s on %s matches", key, pe__node_name(on_node));
1541             result = g_list_prepend(result, action);
1542         }
1543     }
1544 
1545     return result;
1546 }
1547 
1548 GList *
1549 find_actions_exact(GList *input, const char *key, const pcmk_node_t *on_node)
     /* [previous][next][first][last][top][bottom][index][help] */
1550 {
1551     GList *result = NULL;
1552 
1553     CRM_CHECK(key != NULL, return NULL);
1554 
1555     if (on_node == NULL) {
1556         return NULL;
1557     }
1558 
1559     for (GList *gIter = input; gIter != NULL; gIter = gIter->next) {
1560         pcmk_action_t *action = (pcmk_action_t *) gIter->data;
1561 
1562         if ((action->node != NULL)
1563             && pcmk__str_eq(key, action->uuid, pcmk__str_casei)
1564             && pcmk__str_eq(on_node->details->id, action->node->details->id,
1565                             pcmk__str_casei)) {
1566 
1567             crm_trace("Action %s on %s matches", key, pe__node_name(on_node));
1568             result = g_list_prepend(result, action);
1569         }
1570     }
1571 
1572     return result;
1573 }
1574 
1575 /*!
1576  * \brief Find all actions of given type for a resource
1577  *
1578  * \param[in] rsc           Resource to search
1579  * \param[in] node          Find only actions scheduled on this node
1580  * \param[in] task          Action name to search for
1581  * \param[in] require_node  If TRUE, NULL node or action node will not match
1582  *
1583  * \return List of actions found (or NULL if none)
1584  * \note If node is not NULL and require_node is FALSE, matching actions
1585  *       without a node will be assigned to node.
1586  */
1587 GList *
1588 pe__resource_actions(const pcmk_resource_t *rsc, const pcmk_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
1589                      const char *task, bool require_node)
1590 {
1591     GList *result = NULL;
1592     char *key = pcmk__op_key(rsc->id, task, 0);
1593 
1594     if (require_node) {
1595         result = find_actions_exact(rsc->actions, key, node);
1596     } else {
1597         result = find_actions(rsc->actions, key, node);
1598     }
1599     free(key);
1600     return result;
1601 }
1602 
1603 /*!
1604  * \internal
1605  * \brief Create an action reason string based on the action itself
1606  *
1607  * \param[in] action  Action to create reason string for
1608  * \param[in] flag    Action flag that was cleared
1609  *
1610  * \return Newly allocated string suitable for use as action reason
1611  * \note It is the caller's responsibility to free() the result.
1612  */
1613 char *
1614 pe__action2reason(const pcmk_action_t *action, enum pe_action_flags flag)
     /* [previous][next][first][last][top][bottom][index][help] */
1615 {
1616     const char *change = NULL;
1617 
1618     switch (flag) {
1619         case pcmk_action_runnable:
1620             change = "unrunnable";
1621             break;
1622         case pcmk_action_migratable:
1623             change = "unmigrateable";
1624             break;
1625         case pcmk_action_optional:
1626             change = "required";
1627             break;
1628         default:
1629             // Bug: caller passed unsupported flag
1630             CRM_CHECK(change != NULL, change = "");
1631             break;
1632     }
1633     return crm_strdup_printf("%s%s%s %s", change,
1634                              (action->rsc == NULL)? "" : " ",
1635                              (action->rsc == NULL)? "" : action->rsc->id,
1636                              action->task);
1637 }
1638 
1639 void pe_action_set_reason(pcmk_action_t *action, const char *reason,
     /* [previous][next][first][last][top][bottom][index][help] */
1640                           bool overwrite)
1641 {
1642     if (action->reason != NULL && overwrite) {
1643         pe_rsc_trace(action->rsc, "Changing %s reason from '%s' to '%s'",
1644                      action->uuid, action->reason, pcmk__s(reason, "(none)"));
1645     } else if (action->reason == NULL) {
1646         pe_rsc_trace(action->rsc, "Set %s reason to '%s'",
1647                      action->uuid, pcmk__s(reason, "(none)"));
1648     } else {
1649         // crm_assert(action->reason != NULL && !overwrite);
1650         return;
1651     }
1652 
1653     pcmk__str_update(&action->reason, reason);
1654 }
1655 
1656 /*!
1657  * \internal
1658  * \brief Create an action to clear a resource's history from CIB
1659  *
1660  * \param[in,out] rsc       Resource to clear
1661  * \param[in]     node      Node to clear history on
1662  */
1663 void
1664 pe__clear_resource_history(pcmk_resource_t *rsc, const pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
1665 {
1666     CRM_ASSERT((rsc != NULL) && (node != NULL));
1667 
1668     custom_action(rsc, pcmk__op_key(rsc->id, PCMK_ACTION_LRM_DELETE, 0),
1669                   PCMK_ACTION_LRM_DELETE, node, FALSE, rsc->cluster);
1670 }
1671 
1672 #define sort_return(an_int, why) do {                                   \
1673         free(a_uuid);                                           \
1674         free(b_uuid);                                           \
1675         crm_trace("%s (%d) %c %s (%d) : %s",                            \
1676                   a_xml_id, a_call_id, an_int>0?'>':an_int<0?'<':'=',   \
1677                   b_xml_id, b_call_id, why);                            \
1678         return an_int;                                                  \
1679     } while(0)
1680 
1681 int
1682 pe__is_newer_op(const xmlNode *xml_a, const xmlNode *xml_b,
     /* [previous][next][first][last][top][bottom][index][help] */
1683                 bool same_node_default)
1684 {
1685     int a_call_id = -1;
1686     int b_call_id = -1;
1687 
1688     char *a_uuid = NULL;
1689     char *b_uuid = NULL;
1690 
1691     const char *a_xml_id = crm_element_value(xml_a, XML_ATTR_ID);
1692     const char *b_xml_id = crm_element_value(xml_b, XML_ATTR_ID);
1693 
1694     const char *a_node = crm_element_value(xml_a, XML_LRM_ATTR_TARGET);
1695     const char *b_node = crm_element_value(xml_b, XML_LRM_ATTR_TARGET);
1696     bool same_node = true;
1697 
1698     /* @COMPAT The on_node attribute was added to last_failure as of 1.1.13 (via
1699      * 8b3ca1c) and the other entries as of 1.1.12 (via 0b07b5c).
1700      *
1701      * In case that any of the lrm_rsc_op entries doesn't have on_node
1702      * attribute, we need to explicitly tell whether the two operations are on
1703      * the same node.
1704      */
1705     if (a_node == NULL || b_node == NULL) {
1706         same_node = same_node_default;
1707 
1708     } else {
1709         same_node = pcmk__str_eq(a_node, b_node, pcmk__str_casei);
1710     }
1711 
1712     if (same_node && pcmk__str_eq(a_xml_id, b_xml_id, pcmk__str_none)) {
1713         /* We have duplicate lrm_rsc_op entries in the status
1714          * section which is unlikely to be a good thing
1715          *    - we can handle it easily enough, but we need to get
1716          *    to the bottom of why it's happening.
1717          */
1718         pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id);
1719         sort_return(0, "duplicate");
1720     }
1721 
1722     crm_element_value_int(xml_a, XML_LRM_ATTR_CALLID, &a_call_id);
1723     crm_element_value_int(xml_b, XML_LRM_ATTR_CALLID, &b_call_id);
1724 
1725     if (a_call_id == -1 && b_call_id == -1) {
1726         /* both are pending ops so it doesn't matter since
1727          *   stops are never pending
1728          */
1729         sort_return(0, "pending");
1730 
1731     } else if (same_node && a_call_id >= 0 && a_call_id < b_call_id) {
1732         sort_return(-1, "call id");
1733 
1734     } else if (same_node && b_call_id >= 0 && a_call_id > b_call_id) {
1735         sort_return(1, "call id");
1736 
1737     } else if (a_call_id >= 0 && b_call_id >= 0
1738                && (!same_node || a_call_id == b_call_id)) {
1739         /*
1740          * The op and last_failed_op are the same
1741          * Order on last-rc-change
1742          */
1743         time_t last_a = -1;
1744         time_t last_b = -1;
1745 
1746         crm_element_value_epoch(xml_a, XML_RSC_OP_LAST_CHANGE, &last_a);
1747         crm_element_value_epoch(xml_b, XML_RSC_OP_LAST_CHANGE, &last_b);
1748 
1749         crm_trace("rc-change: %lld vs %lld",
1750                   (long long) last_a, (long long) last_b);
1751         if (last_a >= 0 && last_a < last_b) {
1752             sort_return(-1, "rc-change");
1753 
1754         } else if (last_b >= 0 && last_a > last_b) {
1755             sort_return(1, "rc-change");
1756         }
1757         sort_return(0, "rc-change");
1758 
1759     } else {
1760         /* One of the inputs is a pending operation
1761          * Attempt to use XML_ATTR_TRANSITION_MAGIC to determine its age relative to the other
1762          */
1763 
1764         int a_id = -1;
1765         int b_id = -1;
1766 
1767         const char *a_magic = crm_element_value(xml_a, XML_ATTR_TRANSITION_MAGIC);
1768         const char *b_magic = crm_element_value(xml_b, XML_ATTR_TRANSITION_MAGIC);
1769 
1770         CRM_CHECK(a_magic != NULL && b_magic != NULL, sort_return(0, "No magic"));
1771         if (!decode_transition_magic(a_magic, &a_uuid, &a_id, NULL, NULL, NULL,
1772                                      NULL)) {
1773             sort_return(0, "bad magic a");
1774         }
1775         if (!decode_transition_magic(b_magic, &b_uuid, &b_id, NULL, NULL, NULL,
1776                                      NULL)) {
1777             sort_return(0, "bad magic b");
1778         }
1779         /* try to determine the relative age of the operation...
1780          * some pending operations (e.g. a start) may have been superseded
1781          *   by a subsequent stop
1782          *
1783          * [a|b]_id == -1 means it's a shutdown operation and _always_ comes last
1784          */
1785         if (!pcmk__str_eq(a_uuid, b_uuid, pcmk__str_casei) || a_id == b_id) {
1786             /*
1787              * some of the logic in here may be redundant...
1788              *
1789              * if the UUID from the TE doesn't match then one better
1790              *   be a pending operation.
1791              * pending operations don't survive between elections and joins
1792              *   because we query the LRM directly
1793              */
1794 
1795             if (b_call_id == -1) {
1796                 sort_return(-1, "transition + call");
1797 
1798             } else if (a_call_id == -1) {
1799                 sort_return(1, "transition + call");
1800             }
1801 
1802         } else if ((a_id >= 0 && a_id < b_id) || b_id == -1) {
1803             sort_return(-1, "transition");
1804 
1805         } else if ((b_id >= 0 && a_id > b_id) || a_id == -1) {
1806             sort_return(1, "transition");
1807         }
1808     }
1809 
1810     /* we should never end up here */
1811     CRM_CHECK(FALSE, sort_return(0, "default"));
1812 }
1813 
1814 gint
1815 sort_op_by_callid(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
1816 {
1817     const xmlNode *xml_a = a;
1818     const xmlNode *xml_b = b;
1819 
1820     return pe__is_newer_op(xml_a, xml_b, true);
1821 }
1822 
1823 /*!
1824  * \internal
1825  * \brief Create a new pseudo-action for a resource
1826  *
1827  * \param[in,out] rsc       Resource to create action for
1828  * \param[in]     task      Action name
1829  * \param[in]     optional  Whether action should be considered optional
1830  * \param[in]     runnable  Whethe action should be considered runnable
1831  *
1832  * \return New action object corresponding to arguments
1833  */
1834 pcmk_action_t *
1835 pe__new_rsc_pseudo_action(pcmk_resource_t *rsc, const char *task, bool optional,
     /* [previous][next][first][last][top][bottom][index][help] */
1836                           bool runnable)
1837 {
1838     pcmk_action_t *action = NULL;
1839 
1840     CRM_ASSERT((rsc != NULL) && (task != NULL));
1841 
1842     action = custom_action(rsc, pcmk__op_key(rsc->id, task, 0), task, NULL,
1843                            optional, rsc->cluster);
1844     pe__set_action_flags(action, pcmk_action_pseudo);
1845     if (runnable) {
1846         pe__set_action_flags(action, pcmk_action_runnable);
1847     }
1848     return action;
1849 }
1850 
1851 /*!
1852  * \internal
1853  * \brief Add the expected result to an action
1854  *
1855  * \param[in,out] action           Action to add expected result to
1856  * \param[in]     expected_result  Expected result to add
1857  *
1858  * \note This is more efficient than calling add_hash_param().
1859  */
1860 void
1861 pe__add_action_expected_result(pcmk_action_t *action, int expected_result)
     /* [previous][next][first][last][top][bottom][index][help] */
1862 {
1863     char *name = NULL;
1864 
1865     CRM_ASSERT((action != NULL) && (action->meta != NULL));
1866 
1867     name = strdup(XML_ATTR_TE_TARGET_RC);
1868     CRM_ASSERT (name != NULL);
1869 
1870     g_hash_table_insert(action->meta, name, pcmk__itoa(expected_result));
1871 }

/* [previous][next][first][last][top][bottom][index][help] */