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_rsc_op_entry_helper
  5. find_rsc_op_entry
  6. new_action
  7. unpack_action_node_attributes
  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. unpack_operation_on_fail
  14. unpack_timeout
  15. unpack_interval_origin
  16. unpack_start_delay
  17. find_min_interval_mon
  18. unpack_operation
  19. custom_action
  20. get_pseudo_op
  21. find_unfencing_devices
  22. node_priority_fencing_delay
  23. pe_fence_op
  24. pe_free_action
  25. pe_get_configured_timeout
  26. get_complex_task
  27. find_first_action
  28. find_actions
  29. find_actions_exact
  30. pe__resource_actions
  31. pe__action2reason
  32. pe_action_set_reason
  33. pe__clear_resource_history
  34. pe__is_newer_op
  35. sort_op_by_callid
  36. pe__new_rsc_pseudo_action
  37. pe__add_action_expected_result

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

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