root/lib/pengine/utils.c

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

DEFINITIONS

This source file includes following definitions.
  1. pe_rsc_action_details
  2. pe_free_rsc_action_details
  3. pe_can_fence
  4. pe__copy_node
  5. node_list_exclude
  6. pe__node_list2table
  7. sort_node_uname
  8. pe__output_node_weights
  9. pe__log_node_weights
  10. pe__show_node_weights_as
  11. sort_rsc_index
  12. sort_rsc_priority
  13. effective_quorum_policy
  14. add_singleton
  15. lookup_singleton
  16. find_existing_action
  17. new_action
  18. unpack_action_node_attributes
  19. update_action_optional
  20. update_resource_action_runnable
  21. update_resource_flags_for_action
  22. custom_action
  23. valid_stop_on_fail
  24. unpack_operation_on_fail
  25. find_min_interval_mon
  26. unpack_start_delay
  27. unpack_interval_origin
  28. unpack_timeout
  29. pe_get_configured_timeout
  30. unpack_versioned_meta
  31. unpack_operation
  32. find_rsc_op_entry_helper
  33. find_rsc_op_entry
  34. print_str_str
  35. pe_free_action
  36. find_recurring_actions
  37. get_complex_task
  38. find_first_action
  39. find_actions
  40. find_actions_exact
  41. pe__resource_actions
  42. resource_node_score
  43. resource_location
  44. sort_op_by_callid
  45. get_effective_time
  46. get_target_role
  47. order_actions
  48. get_pseudo_op
  49. destroy_ticket
  50. ticket_new
  51. rsc_printable_id
  52. pe__clear_resource_flags_recursive
  53. pe__clear_resource_flags_on_all
  54. pe__set_resource_flags_recursive
  55. find_unfencing_devices
  56. node_priority_fencing_delay
  57. pe_fence_op
  58. trigger_unfencing
  59. add_tag_ref
  60. pe__action2reason
  61. pe_action_set_reason
  62. pe__shutdown_requested
  63. pe__update_recheck_time
  64. pe__unpack_dataset_nvpairs
  65. pe__resource_is_disabled
  66. pe__clear_resource_history
  67. pe__rsc_running_on_any
  68. pcmk__rsc_filtered_by_node
  69. pe__filter_rsc_list
  70. pe__build_node_name_list
  71. pe__build_rsc_list

   1 /*
   2  * Copyright 2004-2021 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 #include <crm/crm.h>
  12 #include <crm/msg_xml.h>
  13 #include <crm/common/xml.h>
  14 #include <crm/common/xml_internal.h>
  15 #include <crm/common/util.h>
  16 
  17 #include <glib.h>
  18 #include <stdbool.h>
  19 
  20 #include <crm/pengine/rules.h>
  21 #include <crm/pengine/internal.h>
  22 #include "pe_status_private.h"
  23 
  24 extern bool pcmk__is_daemon;
  25 
  26 extern xmlNode *get_object_root(const char *object_type, xmlNode * the_root);
  27 void print_str_str(gpointer key, gpointer value, gpointer user_data);
  28 gboolean ghash_free_str_str(gpointer key, gpointer value, gpointer user_data);
  29 static void unpack_operation(pe_action_t * action, xmlNode * xml_obj, pe_resource_t * container,
  30                              pe_working_set_t * data_set, guint interval_ms);
  31 static xmlNode *find_rsc_op_entry_helper(pe_resource_t * rsc, const char *key,
  32                                          gboolean include_disabled);
  33 
  34 #if ENABLE_VERSIONED_ATTRS
  35 pe_rsc_action_details_t *
  36 pe_rsc_action_details(pe_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
  37 {
  38     pe_rsc_action_details_t *details;
  39 
  40     CRM_CHECK(action != NULL, return NULL);
  41 
  42     if (action->action_details == NULL) {
  43         action->action_details = calloc(1, sizeof(pe_rsc_action_details_t));
  44         CRM_CHECK(action->action_details != NULL, return NULL);
  45     }
  46 
  47     details = (pe_rsc_action_details_t *) action->action_details;
  48     if (details->versioned_parameters == NULL) {
  49         details->versioned_parameters = create_xml_node(NULL,
  50                                                         XML_TAG_OP_VER_ATTRS);
  51     }
  52     if (details->versioned_meta == NULL) {
  53         details->versioned_meta = create_xml_node(NULL, XML_TAG_OP_VER_META);
  54     }
  55     return details;
  56 }
  57 
  58 static void
  59 pe_free_rsc_action_details(pe_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
  60 {
  61     pe_rsc_action_details_t *details;
  62 
  63     if ((action == NULL) || (action->action_details == NULL)) {
  64         return;
  65     }
  66 
  67     details = (pe_rsc_action_details_t *) action->action_details;
  68 
  69     if (details->versioned_parameters) {
  70         free_xml(details->versioned_parameters);
  71     }
  72     if (details->versioned_meta) {
  73         free_xml(details->versioned_meta);
  74     }
  75 
  76     action->action_details = NULL;
  77 }
  78 #endif
  79 
  80 /*!
  81  * \internal
  82  * \brief Check whether we can fence a particular node
  83  *
  84  * \param[in] data_set  Working set for cluster
  85  * \param[in] node      Name of node to check
  86  *
  87  * \return true if node can be fenced, false otherwise
  88  */
  89 bool
  90 pe_can_fence(pe_working_set_t *data_set, pe_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
  91 {
  92     if (pe__is_guest_node(node)) {
  93         /* Guest nodes are fenced by stopping their container resource. We can
  94          * do that if the container's host is either online or fenceable.
  95          */
  96         pe_resource_t *rsc = node->details->remote_rsc->container;
  97 
  98         for (GList *n = rsc->running_on; n != NULL; n = n->next) {
  99             pe_node_t *container_node = n->data;
 100 
 101             if (!container_node->details->online
 102                 && !pe_can_fence(data_set, container_node)) {
 103                 return false;
 104             }
 105         }
 106         return true;
 107 
 108     } else if (!pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
 109         return false; /* Turned off */
 110 
 111     } else if (!pcmk_is_set(data_set->flags, pe_flag_have_stonith_resource)) {
 112         return false; /* No devices */
 113 
 114     } else if (pcmk_is_set(data_set->flags, pe_flag_have_quorum)) {
 115         return true;
 116 
 117     } else if (data_set->no_quorum_policy == no_quorum_ignore) {
 118         return true;
 119 
 120     } else if(node == NULL) {
 121         return false;
 122 
 123     } else if(node->details->online) {
 124         crm_notice("We can fence %s without quorum because they're in our membership", node->details->uname);
 125         return true;
 126     }
 127 
 128     crm_trace("Cannot fence %s", node->details->uname);
 129     return false;
 130 }
 131 
 132 /*!
 133  * \internal
 134  * \brief Copy a node object
 135  *
 136  * \param[in] this_node  Node object to copy
 137  *
 138  * \return Newly allocated shallow copy of this_node
 139  * \note This function asserts on errors and is guaranteed to return non-NULL.
 140  */
 141 pe_node_t *
 142 pe__copy_node(const pe_node_t *this_node)
     /* [previous][next][first][last][top][bottom][index][help] */
 143 {
 144     pe_node_t *new_node = NULL;
 145 
 146     CRM_ASSERT(this_node != NULL);
 147 
 148     new_node = calloc(1, sizeof(pe_node_t));
 149     CRM_ASSERT(new_node != NULL);
 150 
 151     new_node->rsc_discover_mode = this_node->rsc_discover_mode;
 152     new_node->weight = this_node->weight;
 153     new_node->fixed = this_node->fixed;
 154     new_node->details = this_node->details;
 155 
 156     return new_node;
 157 }
 158 
 159 /* any node in list1 or list2 and not in the other gets a score of -INFINITY */
 160 void
 161 node_list_exclude(GHashTable * hash, GList *list, gboolean merge_scores)
     /* [previous][next][first][last][top][bottom][index][help] */
 162 {
 163     GHashTable *result = hash;
 164     pe_node_t *other_node = NULL;
 165     GList *gIter = list;
 166 
 167     GHashTableIter iter;
 168     pe_node_t *node = NULL;
 169 
 170     g_hash_table_iter_init(&iter, hash);
 171     while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
 172 
 173         other_node = pe_find_node_id(list, node->details->id);
 174         if (other_node == NULL) {
 175             node->weight = -INFINITY;
 176         } else if (merge_scores) {
 177             node->weight = pe__add_scores(node->weight, other_node->weight);
 178         }
 179     }
 180 
 181     for (; gIter != NULL; gIter = gIter->next) {
 182         pe_node_t *node = (pe_node_t *) gIter->data;
 183 
 184         other_node = pe_hash_table_lookup(result, node->details->id);
 185 
 186         if (other_node == NULL) {
 187             pe_node_t *new_node = pe__copy_node(node);
 188 
 189             new_node->weight = -INFINITY;
 190             g_hash_table_insert(result, (gpointer) new_node->details->id, new_node);
 191         }
 192     }
 193 }
 194 
 195 /*!
 196  * \internal
 197  * \brief Create a node hash table from a node list
 198  *
 199  * \param[in] list  Node list
 200  *
 201  * \return Hash table equivalent of node list
 202  */
 203 GHashTable *
 204 pe__node_list2table(GList *list)
     /* [previous][next][first][last][top][bottom][index][help] */
 205 {
 206     GHashTable *result = NULL;
 207 
 208     result = pcmk__strkey_table(NULL, free);
 209     for (GList *gIter = list; gIter != NULL; gIter = gIter->next) {
 210         pe_node_t *new_node = pe__copy_node((pe_node_t *) gIter->data);
 211 
 212         g_hash_table_insert(result, (gpointer) new_node->details->id, new_node);
 213     }
 214     return result;
 215 }
 216 
 217 gint
 218 sort_node_uname(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 219 {
 220     return pcmk__numeric_strcasecmp(((const pe_node_t *) a)->details->uname,
 221                                     ((const pe_node_t *) b)->details->uname);
 222 }
 223 
 224 /*!
 225  * \internal
 226  * \brief Output node weights to stdout
 227  *
 228  * \param[in] rsc       Use allowed nodes for this resource
 229  * \param[in] comment   Text description to prefix lines with
 230  * \param[in] nodes     If rsc is not specified, use these nodes
 231  */
 232 static void
 233 pe__output_node_weights(pe_resource_t *rsc, const char *comment,
     /* [previous][next][first][last][top][bottom][index][help] */
 234                         GHashTable *nodes, pe_working_set_t *data_set)
 235 {
 236     pcmk__output_t *out = data_set->priv;
 237     char score[128]; // Stack-allocated since this is called frequently
 238 
 239     // Sort the nodes so the output is consistent for regression tests
 240     GList *list = g_list_sort(g_hash_table_get_values(nodes), sort_node_uname);
 241 
 242     for (GList *gIter = list; gIter != NULL; gIter = gIter->next) {
 243         pe_node_t *node = (pe_node_t *) gIter->data;
 244 
 245         score2char_stack(node->weight, score, sizeof(score));
 246         out->message(out, "node-weight", rsc, comment, node->details->uname, score);
 247     }
 248     g_list_free(list);
 249 }
 250 
 251 /*!
 252  * \internal
 253  * \brief Log node weights at trace level
 254  *
 255  * \param[in] file      Caller's filename
 256  * \param[in] function  Caller's function name
 257  * \param[in] line      Caller's line number
 258  * \param[in] rsc       Use allowed nodes for this resource
 259  * \param[in] comment   Text description to prefix lines with
 260  * \param[in] nodes     If rsc is not specified, use these nodes
 261  */
 262 static void
 263 pe__log_node_weights(const char *file, const char *function, int line,
     /* [previous][next][first][last][top][bottom][index][help] */
 264                      pe_resource_t *rsc, const char *comment, GHashTable *nodes)
 265 {
 266     GHashTableIter iter;
 267     pe_node_t *node = NULL;
 268     char score[128]; // Stack-allocated since this is called frequently
 269 
 270     // Don't waste time if we're not tracing at this point
 271     pcmk__log_else(LOG_TRACE, return);
 272 
 273     g_hash_table_iter_init(&iter, nodes);
 274     while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
 275         score2char_stack(node->weight, score, sizeof(score));
 276         if (rsc) {
 277             qb_log_from_external_source(function, file,
 278                                         "%s: %s allocation score on %s: %s",
 279                                         LOG_TRACE, line, 0,
 280                                         comment, rsc->id,
 281                                         node->details->uname, score);
 282         } else {
 283             qb_log_from_external_source(function, file, "%s: %s = %s",
 284                                         LOG_TRACE, line, 0,
 285                                         comment, node->details->uname,
 286                                         score);
 287         }
 288     }
 289 }
 290 
 291 /*!
 292  * \internal
 293  * \brief Log or output node weights
 294  *
 295  * \param[in] file      Caller's filename
 296  * \param[in] function  Caller's function name
 297  * \param[in] line      Caller's line number
 298  * \param[in] to_log    Log if true, otherwise output
 299  * \param[in] rsc       Use allowed nodes for this resource
 300  * \param[in] comment   Text description to prefix lines with
 301  * \param[in] nodes     Use these nodes
 302  */
 303 void
 304 pe__show_node_weights_as(const char *file, const char *function, int line,
     /* [previous][next][first][last][top][bottom][index][help] */
 305                          bool to_log, pe_resource_t *rsc, const char *comment,
 306                          GHashTable *nodes, pe_working_set_t *data_set)
 307 {
 308     if (rsc != NULL && pcmk_is_set(rsc->flags, pe_rsc_orphan)) {
 309         // Don't show allocation scores for orphans
 310         return;
 311     }
 312     if (nodes == NULL) {
 313         // Nothing to show
 314         return;
 315     }
 316 
 317     if (to_log) {
 318         pe__log_node_weights(file, function, line, rsc, comment, nodes);
 319     } else {
 320         pe__output_node_weights(rsc, comment, nodes, data_set);
 321     }
 322 
 323     // If this resource has children, repeat recursively for each
 324     if (rsc && rsc->children) {
 325         for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
 326             pe_resource_t *child = (pe_resource_t *) gIter->data;
 327 
 328             pe__show_node_weights_as(file, function, line, to_log, child,
 329                                      comment, child->allowed_nodes, data_set);
 330         }
 331     }
 332 }
 333 
 334 gint
 335 sort_rsc_index(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 336 {
 337     const pe_resource_t *resource1 = (const pe_resource_t *)a;
 338     const pe_resource_t *resource2 = (const pe_resource_t *)b;
 339 
 340     if (a == NULL && b == NULL) {
 341         return 0;
 342     }
 343     if (a == NULL) {
 344         return 1;
 345     }
 346     if (b == NULL) {
 347         return -1;
 348     }
 349 
 350     if (resource1->sort_index > resource2->sort_index) {
 351         return -1;
 352     }
 353 
 354     if (resource1->sort_index < resource2->sort_index) {
 355         return 1;
 356     }
 357 
 358     return 0;
 359 }
 360 
 361 gint
 362 sort_rsc_priority(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 363 {
 364     const pe_resource_t *resource1 = (const pe_resource_t *)a;
 365     const pe_resource_t *resource2 = (const pe_resource_t *)b;
 366 
 367     if (a == NULL && b == NULL) {
 368         return 0;
 369     }
 370     if (a == NULL) {
 371         return 1;
 372     }
 373     if (b == NULL) {
 374         return -1;
 375     }
 376 
 377     if (resource1->priority > resource2->priority) {
 378         return -1;
 379     }
 380 
 381     if (resource1->priority < resource2->priority) {
 382         return 1;
 383     }
 384 
 385     return 0;
 386 }
 387 
 388 static enum pe_quorum_policy
 389 effective_quorum_policy(pe_resource_t *rsc, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 390 {
 391     enum pe_quorum_policy policy = data_set->no_quorum_policy;
 392 
 393     if (pcmk_is_set(data_set->flags, pe_flag_have_quorum)) {
 394         policy = no_quorum_ignore;
 395 
 396     } else if (data_set->no_quorum_policy == no_quorum_demote) {
 397         switch (rsc->role) {
 398             case RSC_ROLE_PROMOTED:
 399             case RSC_ROLE_UNPROMOTED:
 400                 if (rsc->next_role > RSC_ROLE_UNPROMOTED) {
 401                     pe__set_next_role(rsc, RSC_ROLE_UNPROMOTED,
 402                                       "no-quorum-policy=demote");
 403                 }
 404                 policy = no_quorum_ignore;
 405                 break;
 406             default:
 407                 policy = no_quorum_stop;
 408                 break;
 409         }
 410     }
 411     return policy;
 412 }
 413 
 414 static void
 415 add_singleton(pe_working_set_t *data_set, pe_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 416 {
 417     if (data_set->singletons == NULL) {
 418         data_set->singletons = pcmk__strkey_table(NULL, NULL);
 419     }
 420     g_hash_table_insert(data_set->singletons, action->uuid, action);
 421 }
 422 
 423 static pe_action_t *
 424 lookup_singleton(pe_working_set_t *data_set, const char *action_uuid)
     /* [previous][next][first][last][top][bottom][index][help] */
 425 {
 426     if (data_set->singletons == NULL) {
 427         return NULL;
 428     }
 429     return g_hash_table_lookup(data_set->singletons, action_uuid);
 430 }
 431 
 432 /*!
 433  * \internal
 434  * \brief Find an existing action that matches arguments
 435  *
 436  * \param[in] key        Action key to match
 437  * \param[in] rsc        Resource to match (if any)
 438  * \param[in] node       Node to match (if any)
 439  * \param[in] data_set   Cluster working set
 440  *
 441  * \return Existing action that matches arguments (or NULL if none)
 442  */
 443 static pe_action_t *
 444 find_existing_action(const char *key, pe_resource_t *rsc, pe_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 445                      pe_working_set_t *data_set)
 446 {
 447     GList *matches = NULL;
 448     pe_action_t *action = NULL;
 449 
 450     /* When rsc is NULL, it would be quicker to check data_set->singletons,
 451      * but checking all data_set->actions takes the node into account.
 452      */
 453     matches = find_actions(((rsc == NULL)? data_set->actions : rsc->actions),
 454                            key, node);
 455     if (matches == NULL) {
 456         return NULL;
 457     }
 458     CRM_LOG_ASSERT(!pcmk__list_of_multiple(matches));
 459 
 460     action = matches->data;
 461     g_list_free(matches);
 462     return action;
 463 }
 464 
 465 /*!
 466  * \internal
 467  * \brief Create a new action object
 468  *
 469  * \param[in] key        Action key
 470  * \param[in] task       Action name
 471  * \param[in] rsc        Resource that action is for (if any)
 472  * \param[in] node       Node that action is on (if any)
 473  * \param[in] optional   Whether action should be considered optional
 474  * \param[in] for_graph  Whether action should be recorded in transition graph
 475  * \param[in] data_set   Cluster working set
 476  *
 477  * \return Newly allocated action
 478  * \note This function takes ownership of \p key. It is the caller's
 479  *       responsibility to free the return value with pe_free_action().
 480  */
 481 static pe_action_t *
 482 new_action(char *key, const char *task, pe_resource_t *rsc, pe_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 483            bool optional, bool for_graph, pe_working_set_t *data_set)
 484 {
 485     pe_action_t *action = calloc(1, sizeof(pe_action_t));
 486 
 487     CRM_ASSERT(action != NULL);
 488 
 489     action->rsc = rsc;
 490     action->task = strdup(task); CRM_ASSERT(action->task != NULL);
 491     action->uuid = key;
 492     action->extra = pcmk__strkey_table(free, free);
 493     action->meta = pcmk__strkey_table(free, free);
 494 
 495     if (node) {
 496         action->node = pe__copy_node(node);
 497     }
 498 
 499     if (pcmk__str_eq(task, CRM_OP_LRM_DELETE, pcmk__str_casei)) {
 500         // Resource history deletion for a node can be done on the DC
 501         pe__set_action_flags(action, pe_action_dc);
 502     }
 503 
 504     pe__set_action_flags(action, pe_action_runnable);
 505     if (optional) {
 506         pe__set_action_flags(action, pe_action_optional);
 507     } else {
 508         pe__clear_action_flags(action, pe_action_optional);
 509     }
 510 
 511     if (rsc != NULL) {
 512         guint interval_ms = 0;
 513 
 514         action->op_entry = find_rsc_op_entry_helper(rsc, key, TRUE);
 515         parse_op_key(key, NULL, NULL, &interval_ms);
 516         unpack_operation(action, action->op_entry, rsc->container, data_set,
 517                          interval_ms);
 518     }
 519 
 520     if (for_graph) {
 521         pe_rsc_trace(rsc, "Created %s action %d (%s): %s for %s on %s",
 522                      (optional? "optional" : "required"),
 523                      data_set->action_id, key, task,
 524                      ((rsc == NULL)? "no resource" : rsc->id),
 525                      ((node == NULL)? "no node" : node->details->uname));
 526         action->id = data_set->action_id++;
 527 
 528         data_set->actions = g_list_prepend(data_set->actions, action);
 529         if (rsc == NULL) {
 530             add_singleton(data_set, action);
 531         } else {
 532             rsc->actions = g_list_prepend(rsc->actions, action);
 533         }
 534     }
 535     return action;
 536 }
 537 
 538 /*!
 539  * \internal
 540  * \brief Evaluate node attribute values for an action
 541  *
 542  * \param[in] action    Action to unpack attributes for
 543  * \param[in] data_set  Cluster working set
 544  */
 545 static void
 546 unpack_action_node_attributes(pe_action_t *action, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 547 {
 548     if (!pcmk_is_set(action->flags, pe_action_have_node_attrs)
 549         && (action->op_entry != NULL)) {
 550 
 551         pe_rule_eval_data_t rule_data = {
 552             .node_hash = action->node->details->attrs,
 553             .role = RSC_ROLE_UNKNOWN,
 554             .now = data_set->now,
 555             .match_data = NULL,
 556             .rsc_data = NULL,
 557             .op_data = NULL
 558         };
 559 
 560         pe__set_action_flags(action, pe_action_have_node_attrs);
 561         pe__unpack_dataset_nvpairs(action->op_entry, XML_TAG_ATTR_SETS,
 562                                    &rule_data, action->extra, NULL,
 563                                    FALSE, data_set);
 564     }
 565 }
 566 
 567 /*!
 568  * \internal
 569  * \brief Update an action's optional flag
 570  *
 571  * \param[in] action    Action to update
 572  * \param[in] optional  Requested optional status
 573  */
 574 static void
 575 update_action_optional(pe_action_t *action, gboolean optional)
     /* [previous][next][first][last][top][bottom][index][help] */
 576 {
 577     // Force a non-recurring action to be optional if its resource is unmanaged
 578     if ((action->rsc != NULL) && (action->node != NULL)
 579         && !pcmk_is_set(action->flags, pe_action_pseudo)
 580         && !pcmk_is_set(action->rsc->flags, pe_rsc_managed)
 581         && (g_hash_table_lookup(action->meta,
 582                                 XML_LRM_ATTR_INTERVAL_MS) == NULL)) {
 583             pe_rsc_debug(action->rsc, "%s on %s is optional (%s is unmanaged)",
 584                          action->uuid, action->node->details->uname,
 585                          action->rsc->id);
 586             pe__set_action_flags(action, pe_action_optional);
 587             // We shouldn't clear runnable here because ... something
 588 
 589     // Otherwise require the action if requested
 590     } else if (!optional) {
 591         pe__clear_action_flags(action, pe_action_optional);
 592     }
 593 }
 594 
 595 /*!
 596  * \internal
 597  * \brief Update a resource action's runnable flag
 598  *
 599  * \param[in] action     Action to update
 600  * \param[in] for_graph  Whether action should be recorded in transition graph
 601  * \param[in] data_set   Cluster working set
 602  *
 603  * \note This may also schedule fencing if a stop is unrunnable.
 604  */
 605 static void
 606 update_resource_action_runnable(pe_action_t *action, bool for_graph,
     /* [previous][next][first][last][top][bottom][index][help] */
 607                                 pe_working_set_t *data_set)
 608 {
 609     if (pcmk_is_set(action->flags, pe_action_pseudo)) {
 610         return;
 611     }
 612 
 613     if (action->node == NULL) {
 614         pe_rsc_trace(action->rsc, "%s is unrunnable (unallocated)",
 615                      action->uuid);
 616         pe__clear_action_flags(action, pe_action_runnable);
 617 
 618     } else if (!pcmk_is_set(action->flags, pe_action_dc)
 619                && !(action->node->details->online)
 620                && (!pe__is_guest_node(action->node)
 621                    || action->node->details->remote_requires_reset)) {
 622         pe__clear_action_flags(action, pe_action_runnable);
 623         do_crm_log((for_graph? LOG_WARNING: LOG_TRACE),
 624                    "%s on %s is unrunnable (node is offline)",
 625                    action->uuid, action->node->details->uname);
 626         if (pcmk_is_set(action->rsc->flags, pe_rsc_managed)
 627             && for_graph
 628             && pcmk__str_eq(action->task, CRMD_ACTION_STOP, pcmk__str_casei)
 629             && !(action->node->details->unclean)) {
 630             pe_fence_node(data_set, action->node, "stop is unrunnable", false);
 631         }
 632 
 633     } else if (!pcmk_is_set(action->flags, pe_action_dc)
 634                && action->node->details->pending) {
 635         pe__clear_action_flags(action, pe_action_runnable);
 636         do_crm_log((for_graph? LOG_WARNING: LOG_TRACE),
 637                    "Action %s on %s is unrunnable (node is pending)",
 638                    action->uuid, action->node->details->uname);
 639 
 640     } else if (action->needs == rsc_req_nothing) {
 641         pe_action_set_reason(action, NULL, TRUE);
 642         if (pe__is_guest_node(action->node)
 643             && !pe_can_fence(data_set, action->node)) {
 644             /* An action that requires nothing usually does not require any
 645              * fencing in order to be runnable. However, there is an exception:
 646              * such an action cannot be completed if it is on a guest node whose
 647              * host is unclean and cannot be fenced.
 648              */
 649             pe_rsc_debug(action->rsc, "%s on %s is unrunnable "
 650                          "(node's host cannot be fenced)",
 651                          action->uuid, action->node->details->uname);
 652             pe__clear_action_flags(action, pe_action_runnable);
 653         } else {
 654             pe_rsc_trace(action->rsc,
 655                          "%s on %s does not require fencing or quorum",
 656                          action->uuid, action->node->details->uname);
 657             pe__set_action_flags(action, pe_action_runnable);
 658         }
 659 
 660     } else {
 661         switch (effective_quorum_policy(action->rsc, data_set)) {
 662             case no_quorum_stop:
 663                 pe_rsc_debug(action->rsc, "%s on %s is unrunnable (no quorum)",
 664                              action->uuid, action->node->details->uname);
 665                 pe__clear_action_flags(action, pe_action_runnable);
 666                 pe_action_set_reason(action, "no quorum", true);
 667                 break;
 668 
 669             case no_quorum_freeze:
 670                 if (!action->rsc->fns->active(action->rsc, TRUE)
 671                     || (action->rsc->next_role > action->rsc->role)) {
 672                     pe_rsc_debug(action->rsc,
 673                                  "%s on %s is unrunnable (no quorum)",
 674                                  action->uuid, action->node->details->uname);
 675                     pe__clear_action_flags(action, pe_action_runnable);
 676                     pe_action_set_reason(action, "quorum freeze", true);
 677                 }
 678                 break;
 679 
 680             default:
 681                 //pe_action_set_reason(action, NULL, TRUE);
 682                 pe__set_action_flags(action, pe_action_runnable);
 683                 break;
 684         }
 685     }
 686 }
 687 
 688 /*!
 689  * \internal
 690  * \brief Update a resource object's flags for a new action on it
 691  *
 692  * \param[in] rsc        Resource that action is for (if any)
 693  * \param[in] action     New action
 694  */
 695 static void
 696 update_resource_flags_for_action(pe_resource_t *rsc, pe_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 697 {
 698     /* @COMPAT pe_rsc_starting and pe_rsc_stopping are not actually used
 699      * within Pacemaker, and should be deprecated and eventually removed
 700      */
 701     if (pcmk__str_eq(action->task, CRMD_ACTION_STOP, pcmk__str_casei)) {
 702         pe__set_resource_flags(rsc, pe_rsc_stopping);
 703 
 704     } else if (pcmk__str_eq(action->task, CRMD_ACTION_START, pcmk__str_casei)) {
 705         if (pcmk_is_set(action->flags, pe_action_runnable)) {
 706             pe__set_resource_flags(rsc, pe_rsc_starting);
 707         } else {
 708             pe__clear_resource_flags(rsc, pe_rsc_starting);
 709         }
 710     }
 711 }
 712 
 713 /*!
 714  * \brief Create or update an action object
 715  *
 716  * \param[in] rsc          Resource that action is for (if any)
 717  * \param[in] key          Action key (must be non-NULL)
 718  * \param[in] task         Action name (must be non-NULL)
 719  * \param[in] on_node      Node that action is on (if any)
 720  * \param[in] optional     Whether action should be considered optional
 721  * \param[in] save_action  Whether action should be recorded in transition graph
 722  * \param[in] data_set     Cluster working set
 723  *
 724  * \return Action object corresponding to arguments
 725  * \note This function takes ownership of (and might free) \p key. If
 726  *       \p save_action is true, \p data_set will own the returned action,
 727  *       otherwise it is the caller's responsibility to free the return value
 728  *       with pe_free_action().
 729  */
 730 pe_action_t *
 731 custom_action(pe_resource_t *rsc, char *key, const char *task,
     /* [previous][next][first][last][top][bottom][index][help] */
 732               pe_node_t *on_node, gboolean optional, gboolean save_action,
 733               pe_working_set_t *data_set)
 734 {
 735     pe_action_t *action = NULL;
 736 
 737     CRM_ASSERT((key != NULL) && (task != NULL) && (data_set != NULL));
 738 
 739     if (save_action) {
 740         action = find_existing_action(key, rsc, on_node, data_set);
 741     }
 742 
 743     if (action == NULL) {
 744         action = new_action(key, task, rsc, on_node, optional, save_action,
 745                             data_set);
 746     } else {
 747         free(key);
 748     }
 749 
 750     update_action_optional(action, optional);
 751 
 752     if (rsc != NULL) {
 753         if (action->node != NULL) {
 754             unpack_action_node_attributes(action, data_set);
 755         }
 756 
 757         update_resource_action_runnable(action, save_action, data_set);
 758 
 759         if (save_action) {
 760             update_resource_flags_for_action(rsc, action);
 761         }
 762     }
 763 
 764     return action;
 765 }
 766 
 767 static bool
 768 valid_stop_on_fail(const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 769 {
 770     return !pcmk__strcase_any_of(value, "standby", "demote", "stop", NULL);
 771 }
 772 
 773 static const char *
 774 unpack_operation_on_fail(pe_action_t * action)
     /* [previous][next][first][last][top][bottom][index][help] */
 775 {
 776 
 777     const char *name = NULL;
 778     const char *role = NULL;
 779     const char *on_fail = NULL;
 780     const char *interval_spec = NULL;
 781     const char *enabled = NULL;
 782     const char *value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ON_FAIL);
 783 
 784     if (pcmk__str_eq(action->task, CRMD_ACTION_STOP, pcmk__str_casei)
 785         && !valid_stop_on_fail(value)) {
 786 
 787         pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for %s stop "
 788                          "action to default value because '%s' is not "
 789                          "allowed for stop", action->rsc->id, value);
 790         return NULL;
 791 
 792     } else if (pcmk__str_eq(action->task, CRMD_ACTION_DEMOTE, pcmk__str_casei) && !value) {
 793         // demote on_fail defaults to monitor value for promoted role if present
 794         xmlNode *operation = NULL;
 795 
 796         CRM_CHECK(action->rsc != NULL, return NULL);
 797 
 798         for (operation = pcmk__xe_first_child(action->rsc->ops_xml);
 799              (operation != NULL) && (value == NULL);
 800              operation = pcmk__xe_next(operation)) {
 801 
 802             if (!pcmk__str_eq((const char *)operation->name, "op", pcmk__str_none)) {
 803                 continue;
 804             }
 805             name = crm_element_value(operation, "name");
 806             role = crm_element_value(operation, "role");
 807             on_fail = crm_element_value(operation, XML_OP_ATTR_ON_FAIL);
 808             enabled = crm_element_value(operation, "enabled");
 809             interval_spec = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
 810             if (!on_fail) {
 811                 continue;
 812             } else if (enabled && !crm_is_true(enabled)) {
 813                 continue;
 814             } else if (!pcmk__str_eq(name, "monitor", pcmk__str_casei)
 815                        || !pcmk__strcase_any_of(role, RSC_ROLE_PROMOTED_S,
 816                                                 RSC_ROLE_PROMOTED_LEGACY_S,
 817                                                 NULL)) {
 818                 continue;
 819             } else if (crm_parse_interval_spec(interval_spec) == 0) {
 820                 continue;
 821             } else if (pcmk__str_eq(on_fail, "demote", pcmk__str_casei)) {
 822                 continue;
 823             }
 824 
 825             value = on_fail;
 826         }
 827     } else if (pcmk__str_eq(action->task, CRM_OP_LRM_DELETE, pcmk__str_casei)) {
 828         value = "ignore";
 829 
 830     } else if (pcmk__str_eq(value, "demote", pcmk__str_casei)) {
 831         name = crm_element_value(action->op_entry, "name");
 832         role = crm_element_value(action->op_entry, "role");
 833         interval_spec = crm_element_value(action->op_entry,
 834                                           XML_LRM_ATTR_INTERVAL);
 835 
 836         if (!pcmk__str_eq(name, CRMD_ACTION_PROMOTE, pcmk__str_casei)
 837             && (!pcmk__str_eq(name, CRMD_ACTION_STATUS, pcmk__str_casei)
 838                 || !pcmk__strcase_any_of(role, RSC_ROLE_PROMOTED_S,
 839                                          RSC_ROLE_PROMOTED_LEGACY_S, NULL)
 840                 || (crm_parse_interval_spec(interval_spec) == 0))) {
 841             pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for %s %s "
 842                              "action to default value because 'demote' is not "
 843                              "allowed for it", action->rsc->id, name);
 844             return NULL;
 845         }
 846     }
 847 
 848     return value;
 849 }
 850 
 851 static xmlNode *
 852 find_min_interval_mon(pe_resource_t * rsc, gboolean include_disabled)
     /* [previous][next][first][last][top][bottom][index][help] */
 853 {
 854     guint interval_ms = 0;
 855     guint min_interval_ms = G_MAXUINT;
 856     const char *name = NULL;
 857     const char *value = NULL;
 858     const char *interval_spec = NULL;
 859     xmlNode *op = NULL;
 860     xmlNode *operation = NULL;
 861 
 862     for (operation = pcmk__xe_first_child(rsc->ops_xml);
 863          operation != NULL;
 864          operation = pcmk__xe_next(operation)) {
 865 
 866         if (pcmk__str_eq((const char *)operation->name, "op", pcmk__str_none)) {
 867             name = crm_element_value(operation, "name");
 868             interval_spec = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
 869             value = crm_element_value(operation, "enabled");
 870             if (!include_disabled && value && crm_is_true(value) == FALSE) {
 871                 continue;
 872             }
 873 
 874             if (!pcmk__str_eq(name, RSC_STATUS, pcmk__str_casei)) {
 875                 continue;
 876             }
 877 
 878             interval_ms = crm_parse_interval_spec(interval_spec);
 879 
 880             if (interval_ms && (interval_ms < min_interval_ms)) {
 881                 min_interval_ms = interval_ms;
 882                 op = operation;
 883             }
 884         }
 885     }
 886 
 887     return op;
 888 }
 889 
 890 static int
 891 unpack_start_delay(const char *value, GHashTable *meta)
     /* [previous][next][first][last][top][bottom][index][help] */
 892 {
 893     int start_delay = 0;
 894 
 895     if (value != NULL) {
 896         start_delay = crm_get_msec(value);
 897 
 898         if (start_delay < 0) {
 899             start_delay = 0;
 900         }
 901 
 902         if (meta) {
 903             g_hash_table_replace(meta, strdup(XML_OP_ATTR_START_DELAY),
 904                                  pcmk__itoa(start_delay));
 905         }
 906     }
 907 
 908     return start_delay;
 909 }
 910 
 911 // true if value contains valid, non-NULL interval origin for recurring op
 912 static bool
 913 unpack_interval_origin(const char *value, xmlNode *xml_obj, guint interval_ms,
     /* [previous][next][first][last][top][bottom][index][help] */
 914                        crm_time_t *now, long long *start_delay)
 915 {
 916     long long result = 0;
 917     guint interval_sec = interval_ms / 1000;
 918     crm_time_t *origin = NULL;
 919 
 920     // Ignore unspecified values and non-recurring operations
 921     if ((value == NULL) || (interval_ms == 0) || (now == NULL)) {
 922         return false;
 923     }
 924 
 925     // Parse interval origin from text
 926     origin = crm_time_new(value);
 927     if (origin == NULL) {
 928         pcmk__config_err("Ignoring '" XML_OP_ATTR_ORIGIN "' for operation "
 929                          "'%s' because '%s' is not valid",
 930                          (ID(xml_obj)? ID(xml_obj) : "(missing ID)"), value);
 931         return false;
 932     }
 933 
 934     // Get seconds since origin (negative if origin is in the future)
 935     result = crm_time_get_seconds(now) - crm_time_get_seconds(origin);
 936     crm_time_free(origin);
 937 
 938     // Calculate seconds from closest interval to now
 939     result = result % interval_sec;
 940 
 941     // Calculate seconds remaining until next interval
 942     result = ((result <= 0)? 0 : interval_sec) - result;
 943     crm_info("Calculated a start delay of %llds for operation '%s'",
 944              result,
 945              (ID(xml_obj)? ID(xml_obj) : "(unspecified)"));
 946 
 947     if (start_delay != NULL) {
 948         *start_delay = result * 1000; // milliseconds
 949     }
 950     return true;
 951 }
 952 
 953 static int
 954 unpack_timeout(const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 955 {
 956     int timeout_ms = crm_get_msec(value);
 957 
 958     if (timeout_ms < 0) {
 959         timeout_ms = crm_get_msec(CRM_DEFAULT_OP_TIMEOUT_S);
 960     }
 961     return timeout_ms;
 962 }
 963 
 964 int
 965 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] */
 966 {
 967     xmlNode *child = NULL;
 968     GHashTable *action_meta = NULL;
 969     const char *timeout_spec = NULL;
 970     int timeout_ms = 0;
 971 
 972     pe_rule_eval_data_t rule_data = {
 973         .node_hash = NULL,
 974         .role = RSC_ROLE_UNKNOWN,
 975         .now = data_set->now,
 976         .match_data = NULL,
 977         .rsc_data = NULL,
 978         .op_data = NULL
 979     };
 980 
 981     for (child = first_named_child(rsc->ops_xml, XML_ATTR_OP);
 982          child != NULL; child = crm_next_same_xml(child)) {
 983         if (pcmk__str_eq(action, crm_element_value(child, XML_NVPAIR_ATTR_NAME),
 984                 pcmk__str_casei)) {
 985             timeout_spec = crm_element_value(child, XML_ATTR_TIMEOUT);
 986             break;
 987         }
 988     }
 989 
 990     if (timeout_spec == NULL && data_set->op_defaults) {
 991         action_meta = pcmk__strkey_table(free, free);
 992         pe__unpack_dataset_nvpairs(data_set->op_defaults, XML_TAG_META_SETS,
 993                                    &rule_data, action_meta, NULL, FALSE, data_set);
 994         timeout_spec = g_hash_table_lookup(action_meta, XML_ATTR_TIMEOUT);
 995     }
 996 
 997     // @TODO check meta-attributes (including versioned meta-attributes)
 998     // @TODO maybe use min-interval monitor timeout as default for monitors
 999 
1000     timeout_ms = crm_get_msec(timeout_spec);
1001     if (timeout_ms < 0) {
1002         timeout_ms = crm_get_msec(CRM_DEFAULT_OP_TIMEOUT_S);
1003     }
1004 
1005     if (action_meta != NULL) {
1006         g_hash_table_destroy(action_meta);
1007     }
1008     return timeout_ms;
1009 }
1010 
1011 #if ENABLE_VERSIONED_ATTRS
1012 static void
1013 unpack_versioned_meta(xmlNode *versioned_meta, xmlNode *xml_obj,
     /* [previous][next][first][last][top][bottom][index][help] */
1014                       guint interval_ms, crm_time_t *now)
1015 {
1016     xmlNode *attrs = NULL;
1017     xmlNode *attr = NULL;
1018 
1019     for (attrs = pcmk__xe_first_child(versioned_meta); attrs != NULL;
1020          attrs = pcmk__xe_next(attrs)) {
1021 
1022         for (attr = pcmk__xe_first_child(attrs); attr != NULL;
1023              attr = pcmk__xe_next(attr)) {
1024 
1025             const char *name = crm_element_value(attr, XML_NVPAIR_ATTR_NAME);
1026             const char *value = crm_element_value(attr, XML_NVPAIR_ATTR_VALUE);
1027 
1028             if (pcmk__str_eq(name, XML_OP_ATTR_START_DELAY, pcmk__str_casei)) {
1029                 int start_delay = unpack_start_delay(value, NULL);
1030 
1031                 crm_xml_add_int(attr, XML_NVPAIR_ATTR_VALUE, start_delay);
1032             } else if (pcmk__str_eq(name, XML_OP_ATTR_ORIGIN, pcmk__str_casei)) {
1033                 long long start_delay = 0;
1034 
1035                 if (unpack_interval_origin(value, xml_obj, interval_ms, now,
1036                                            &start_delay)) {
1037                     crm_xml_add(attr, XML_NVPAIR_ATTR_NAME,
1038                                 XML_OP_ATTR_START_DELAY);
1039                     crm_xml_add_ll(attr, XML_NVPAIR_ATTR_VALUE, start_delay);
1040                 }
1041             } else if (pcmk__str_eq(name, XML_ATTR_TIMEOUT, pcmk__str_casei)) {
1042                 int timeout_ms = unpack_timeout(value);
1043 
1044                 crm_xml_add_int(attr, XML_NVPAIR_ATTR_VALUE, timeout_ms);
1045             }
1046         }
1047     }
1048 }
1049 #endif
1050 
1051 /*!
1052  * \brief Unpack operation XML into an action structure
1053  *
1054  * Unpack an operation's meta-attributes (normalizing the interval, timeout,
1055  * and start delay values as integer milliseconds), requirements, and
1056  * failure policy.
1057  *
1058  * \param[in,out] action      Action to unpack into
1059  * \param[in]     xml_obj     Operation XML (or NULL if all defaults)
1060  * \param[in]     container   Resource that contains affected resource, if any
1061  * \param[in]     data_set    Cluster state
1062  * \param[in]     interval_ms How frequently to perform the operation
1063  */
1064 static void
1065 unpack_operation(pe_action_t * action, xmlNode * xml_obj, pe_resource_t * container,
     /* [previous][next][first][last][top][bottom][index][help] */
1066                  pe_working_set_t * data_set, guint interval_ms)
1067 {
1068     int timeout_ms = 0;
1069     const char *value = NULL;
1070     bool is_probe = pcmk__str_eq(action->task, RSC_STATUS, pcmk__str_casei)
1071                     && (interval_ms == 0);
1072 #if ENABLE_VERSIONED_ATTRS
1073     pe_rsc_action_details_t *rsc_details = NULL;
1074 #endif
1075 
1076     pe_rsc_eval_data_t rsc_rule_data = {
1077         .standard = crm_element_value(action->rsc->xml, XML_AGENT_ATTR_CLASS),
1078         .provider = crm_element_value(action->rsc->xml, XML_AGENT_ATTR_PROVIDER),
1079         .agent = crm_element_value(action->rsc->xml, XML_EXPR_ATTR_TYPE)
1080     };
1081 
1082     pe_op_eval_data_t op_rule_data = {
1083         .op_name = action->task,
1084         .interval = interval_ms
1085     };
1086 
1087     pe_rule_eval_data_t rule_data = {
1088         .node_hash = NULL,
1089         .role = RSC_ROLE_UNKNOWN,
1090         .now = data_set->now,
1091         .match_data = NULL,
1092         .rsc_data = &rsc_rule_data,
1093         .op_data = &op_rule_data
1094     };
1095 
1096     CRM_CHECK(action && action->rsc, return);
1097 
1098     // Cluster-wide <op_defaults> <meta_attributes>
1099     pe__unpack_dataset_nvpairs(data_set->op_defaults, XML_TAG_META_SETS, &rule_data,
1100                                action->meta, NULL, FALSE, data_set);
1101 
1102     // Determine probe default timeout differently
1103     if (is_probe) {
1104         xmlNode *min_interval_mon = find_min_interval_mon(action->rsc, FALSE);
1105 
1106         if (min_interval_mon) {
1107             value = crm_element_value(min_interval_mon, XML_ATTR_TIMEOUT);
1108             if (value) {
1109                 crm_trace("\t%s: Setting default timeout to minimum-interval "
1110                           "monitor's timeout '%s'", action->uuid, value);
1111                 g_hash_table_replace(action->meta, strdup(XML_ATTR_TIMEOUT),
1112                                      strdup(value));
1113             }
1114         }
1115     }
1116 
1117     if (xml_obj) {
1118         xmlAttrPtr xIter = NULL;
1119 
1120         // <op> <meta_attributes> take precedence over defaults
1121         pe__unpack_dataset_nvpairs(xml_obj, XML_TAG_META_SETS, &rule_data,
1122                                    action->meta, NULL, TRUE, data_set);
1123 
1124 #if ENABLE_VERSIONED_ATTRS
1125         rsc_details = pe_rsc_action_details(action);
1126 
1127         pe_eval_versioned_attributes(data_set->input, xml_obj,
1128                                      XML_TAG_ATTR_SETS, &rule_data,
1129                                      rsc_details->versioned_parameters,
1130                                      NULL);
1131         pe_eval_versioned_attributes(data_set->input, xml_obj,
1132                                      XML_TAG_META_SETS, &rule_data,
1133                                      rsc_details->versioned_meta,
1134                                      NULL);
1135 #endif
1136 
1137         /* Anything set as an <op> XML property has highest precedence.
1138          * This ensures we use the name and interval from the <op> tag.
1139          */
1140         for (xIter = xml_obj->properties; xIter; xIter = xIter->next) {
1141             const char *prop_name = (const char *)xIter->name;
1142             const char *prop_value = crm_element_value(xml_obj, prop_name);
1143 
1144             g_hash_table_replace(action->meta, strdup(prop_name), strdup(prop_value));
1145         }
1146     }
1147 
1148     g_hash_table_remove(action->meta, "id");
1149 
1150     // Normalize interval to milliseconds
1151     if (interval_ms > 0) {
1152         g_hash_table_replace(action->meta, strdup(XML_LRM_ATTR_INTERVAL),
1153                              crm_strdup_printf("%u", interval_ms));
1154     } else {
1155         g_hash_table_remove(action->meta, XML_LRM_ATTR_INTERVAL);
1156     }
1157 
1158     /*
1159      * Timeout order of precedence:
1160      *   1. pcmk_monitor_timeout (if rsc has pcmk_ra_cap_fence_params
1161      *      and task is start or a probe; pcmk_monitor_timeout works
1162      *      by default for a recurring monitor)
1163      *   2. explicit op timeout on the primitive
1164      *   3. default op timeout
1165      *      a. if probe, then min-interval monitor's timeout
1166      *      b. else, in XML_CIB_TAG_OPCONFIG
1167      *   4. CRM_DEFAULT_OP_TIMEOUT_S
1168      *
1169      * #1 overrides general rule of <op> XML property having highest
1170      * precedence.
1171      */
1172     if (pcmk_is_set(pcmk_get_ra_caps(rsc_rule_data.standard),
1173                     pcmk_ra_cap_fence_params)
1174         && (pcmk__str_eq(action->task, RSC_START, pcmk__str_casei)
1175             || is_probe)) {
1176 
1177         GHashTable *params = pe_rsc_params(action->rsc, action->node, data_set);
1178 
1179         value = g_hash_table_lookup(params, "pcmk_monitor_timeout");
1180 
1181         if (value) {
1182             crm_trace("\t%s: Setting timeout to pcmk_monitor_timeout '%s', "
1183                       "overriding default", action->uuid, value);
1184             g_hash_table_replace(action->meta, strdup(XML_ATTR_TIMEOUT),
1185                                  strdup(value));
1186         }
1187     }
1188 
1189     // Normalize timeout to positive milliseconds
1190     value = g_hash_table_lookup(action->meta, XML_ATTR_TIMEOUT);
1191     timeout_ms = unpack_timeout(value);
1192     g_hash_table_replace(action->meta, strdup(XML_ATTR_TIMEOUT),
1193                          pcmk__itoa(timeout_ms));
1194 
1195     if (!pcmk__strcase_any_of(action->task, RSC_START, RSC_PROMOTE, NULL)) {
1196         action->needs = rsc_req_nothing;
1197         value = "nothing (not start or promote)";
1198 
1199     } else if (pcmk_is_set(action->rsc->flags, pe_rsc_needs_fencing)) {
1200         action->needs = rsc_req_stonith;
1201         value = "fencing";
1202 
1203     } else if (pcmk_is_set(action->rsc->flags, pe_rsc_needs_quorum)) {
1204         action->needs = rsc_req_quorum;
1205         value = "quorum";
1206 
1207     } else {
1208         action->needs = rsc_req_nothing;
1209         value = "nothing";
1210     }
1211     pe_rsc_trace(action->rsc, "%s requires %s", action->uuid, value);
1212 
1213     value = unpack_operation_on_fail(action);
1214 
1215     if (value == NULL) {
1216 
1217     } else if (pcmk__str_eq(value, "block", pcmk__str_casei)) {
1218         action->on_fail = action_fail_block;
1219         g_hash_table_insert(action->meta, strdup(XML_OP_ATTR_ON_FAIL), strdup("block"));
1220         value = "block"; // The above could destroy the original string
1221 
1222     } else if (pcmk__str_eq(value, "fence", pcmk__str_casei)) {
1223         action->on_fail = action_fail_fence;
1224         value = "node fencing";
1225 
1226         if (!pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
1227             pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for "
1228                              "operation '%s' to 'stop' because 'fence' is not "
1229                              "valid when fencing is disabled", action->uuid);
1230             action->on_fail = action_fail_stop;
1231             action->fail_role = RSC_ROLE_STOPPED;
1232             value = "stop resource";
1233         }
1234 
1235     } else if (pcmk__str_eq(value, "standby", pcmk__str_casei)) {
1236         action->on_fail = action_fail_standby;
1237         value = "node standby";
1238 
1239     } else if (pcmk__strcase_any_of(value, "ignore", "nothing", NULL)) {
1240         action->on_fail = action_fail_ignore;
1241         value = "ignore";
1242 
1243     } else if (pcmk__str_eq(value, "migrate", pcmk__str_casei)) {
1244         action->on_fail = action_fail_migrate;
1245         value = "force migration";
1246 
1247     } else if (pcmk__str_eq(value, "stop", pcmk__str_casei)) {
1248         action->on_fail = action_fail_stop;
1249         action->fail_role = RSC_ROLE_STOPPED;
1250         value = "stop resource";
1251 
1252     } else if (pcmk__str_eq(value, "restart", pcmk__str_casei)) {
1253         action->on_fail = action_fail_recover;
1254         value = "restart (and possibly migrate)";
1255 
1256     } else if (pcmk__str_eq(value, "restart-container", pcmk__str_casei)) {
1257         if (container) {
1258             action->on_fail = action_fail_restart_container;
1259             value = "restart container (and possibly migrate)";
1260 
1261         } else {
1262             value = NULL;
1263         }
1264 
1265     } else if (pcmk__str_eq(value, "demote", pcmk__str_casei)) {
1266         action->on_fail = action_fail_demote;
1267         value = "demote instance";
1268 
1269     } else {
1270         pe_err("Resource %s: Unknown failure type (%s)", action->rsc->id, value);
1271         value = NULL;
1272     }
1273 
1274     /* defaults */
1275     if (value == NULL && container) {
1276         action->on_fail = action_fail_restart_container;
1277         value = "restart container (and possibly migrate) (default)";
1278 
1279     /* For remote nodes, ensure that any failure that results in dropping an
1280      * active connection to the node results in fencing of the node.
1281      *
1282      * There are only two action failures that don't result in fencing.
1283      * 1. probes - probe failures are expected.
1284      * 2. start - a start failure indicates that an active connection does not already
1285      * exist. The user can set op on-fail=fence if they really want to fence start
1286      * failures. */
1287     } else if (((value == NULL) || !pcmk_is_set(action->rsc->flags, pe_rsc_managed))
1288                && pe__resource_is_remote_conn(action->rsc, data_set)
1289                && !(pcmk__str_eq(action->task, CRMD_ACTION_STATUS, pcmk__str_casei)
1290                     && (interval_ms == 0))
1291                && !pcmk__str_eq(action->task, CRMD_ACTION_START, pcmk__str_casei)) {
1292 
1293         if (!pcmk_is_set(action->rsc->flags, pe_rsc_managed)) {
1294             action->on_fail = action_fail_stop;
1295             action->fail_role = RSC_ROLE_STOPPED;
1296             value = "stop unmanaged remote node (enforcing default)";
1297 
1298         } else {
1299             if (pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
1300                 value = "fence remote node (default)";
1301             } else {
1302                 value = "recover remote node connection (default)";
1303             }
1304 
1305             if (action->rsc->remote_reconnect_ms) {
1306                 action->fail_role = RSC_ROLE_STOPPED;
1307             }
1308             action->on_fail = action_fail_reset_remote;
1309         }
1310 
1311     } else if (value == NULL && pcmk__str_eq(action->task, CRMD_ACTION_STOP, pcmk__str_casei)) {
1312         if (pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
1313             action->on_fail = action_fail_fence;
1314             value = "resource fence (default)";
1315 
1316         } else {
1317             action->on_fail = action_fail_block;
1318             value = "resource block (default)";
1319         }
1320 
1321     } else if (value == NULL) {
1322         action->on_fail = action_fail_recover;
1323         value = "restart (and possibly migrate) (default)";
1324     }
1325 
1326     pe_rsc_trace(action->rsc, "%s failure handling: %s",
1327                  action->uuid, value);
1328 
1329     value = NULL;
1330     if (xml_obj != NULL) {
1331         value = g_hash_table_lookup(action->meta, "role_after_failure");
1332         if (value) {
1333             pe_warn_once(pe_wo_role_after,
1334                         "Support for role_after_failure is deprecated and will be removed in a future release");
1335         }
1336     }
1337     if (value != NULL && action->fail_role == RSC_ROLE_UNKNOWN) {
1338         action->fail_role = text2role(value);
1339     }
1340     /* defaults */
1341     if (action->fail_role == RSC_ROLE_UNKNOWN) {
1342         if (pcmk__str_eq(action->task, CRMD_ACTION_PROMOTE, pcmk__str_casei)) {
1343             action->fail_role = RSC_ROLE_UNPROMOTED;
1344         } else {
1345             action->fail_role = RSC_ROLE_STARTED;
1346         }
1347     }
1348     pe_rsc_trace(action->rsc, "%s failure results in: %s",
1349                  action->uuid, role2text(action->fail_role));
1350 
1351     value = g_hash_table_lookup(action->meta, XML_OP_ATTR_START_DELAY);
1352     if (value) {
1353         unpack_start_delay(value, action->meta);
1354     } else {
1355         long long start_delay = 0;
1356 
1357         value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN);
1358         if (unpack_interval_origin(value, xml_obj, interval_ms, data_set->now,
1359                                    &start_delay)) {
1360             g_hash_table_replace(action->meta, strdup(XML_OP_ATTR_START_DELAY),
1361                                  crm_strdup_printf("%lld", start_delay));
1362         }
1363     }
1364 
1365 #if ENABLE_VERSIONED_ATTRS
1366     unpack_versioned_meta(rsc_details->versioned_meta, xml_obj, interval_ms,
1367                           data_set->now);
1368 #endif
1369 }
1370 
1371 static xmlNode *
1372 find_rsc_op_entry_helper(pe_resource_t * rsc, const char *key, gboolean include_disabled)
     /* [previous][next][first][last][top][bottom][index][help] */
1373 {
1374     guint interval_ms = 0;
1375     gboolean do_retry = TRUE;
1376     char *local_key = NULL;
1377     const char *name = NULL;
1378     const char *value = NULL;
1379     const char *interval_spec = NULL;
1380     char *match_key = NULL;
1381     xmlNode *op = NULL;
1382     xmlNode *operation = NULL;
1383 
1384   retry:
1385     for (operation = pcmk__xe_first_child(rsc->ops_xml); operation != NULL;
1386          operation = pcmk__xe_next(operation)) {
1387 
1388         if (pcmk__str_eq((const char *)operation->name, "op", pcmk__str_none)) {
1389             name = crm_element_value(operation, "name");
1390             interval_spec = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
1391             value = crm_element_value(operation, "enabled");
1392             if (!include_disabled && value && crm_is_true(value) == FALSE) {
1393                 continue;
1394             }
1395 
1396             interval_ms = crm_parse_interval_spec(interval_spec);
1397             match_key = pcmk__op_key(rsc->id, name, interval_ms);
1398             if (pcmk__str_eq(key, match_key, pcmk__str_casei)) {
1399                 op = operation;
1400             }
1401             free(match_key);
1402 
1403             if (rsc->clone_name) {
1404                 match_key = pcmk__op_key(rsc->clone_name, name, interval_ms);
1405                 if (pcmk__str_eq(key, match_key, pcmk__str_casei)) {
1406                     op = operation;
1407                 }
1408                 free(match_key);
1409             }
1410 
1411             if (op != NULL) {
1412                 free(local_key);
1413                 return op;
1414             }
1415         }
1416     }
1417 
1418     free(local_key);
1419     if (do_retry == FALSE) {
1420         return NULL;
1421     }
1422 
1423     do_retry = FALSE;
1424     if (strstr(key, CRMD_ACTION_MIGRATE) || strstr(key, CRMD_ACTION_MIGRATED)) {
1425         local_key = pcmk__op_key(rsc->id, "migrate", 0);
1426         key = local_key;
1427         goto retry;
1428 
1429     } else if (strstr(key, "_notify_")) {
1430         local_key = pcmk__op_key(rsc->id, "notify", 0);
1431         key = local_key;
1432         goto retry;
1433     }
1434 
1435     return NULL;
1436 }
1437 
1438 xmlNode *
1439 find_rsc_op_entry(pe_resource_t * rsc, const char *key)
     /* [previous][next][first][last][top][bottom][index][help] */
1440 {
1441     return find_rsc_op_entry_helper(rsc, key, FALSE);
1442 }
1443 
1444 /*
1445  * Used by the HashTable for-loop
1446  */
1447 void
1448 print_str_str(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1449 {
1450     crm_trace("%s%s %s ==> %s",
1451               user_data == NULL ? "" : (char *)user_data,
1452               user_data == NULL ? "" : ": ", (char *)key, (char *)value);
1453 }
1454 
1455 void
1456 pe_free_action(pe_action_t * action)
     /* [previous][next][first][last][top][bottom][index][help] */
1457 {
1458     if (action == NULL) {
1459         return;
1460     }
1461     g_list_free_full(action->actions_before, free);     /* pe_action_wrapper_t* */
1462     g_list_free_full(action->actions_after, free);      /* pe_action_wrapper_t* */
1463     if (action->extra) {
1464         g_hash_table_destroy(action->extra);
1465     }
1466     if (action->meta) {
1467         g_hash_table_destroy(action->meta);
1468     }
1469 #if ENABLE_VERSIONED_ATTRS
1470     if (action->rsc) {
1471         pe_free_rsc_action_details(action);
1472     }
1473 #endif
1474     free(action->cancel_task);
1475     free(action->reason);
1476     free(action->task);
1477     free(action->uuid);
1478     free(action->node);
1479     free(action);
1480 }
1481 
1482 GList *
1483 find_recurring_actions(GList *input, pe_node_t * not_on_node)
     /* [previous][next][first][last][top][bottom][index][help] */
1484 {
1485     const char *value = NULL;
1486     GList *result = NULL;
1487     GList *gIter = input;
1488 
1489     CRM_CHECK(input != NULL, return NULL);
1490 
1491     for (; gIter != NULL; gIter = gIter->next) {
1492         pe_action_t *action = (pe_action_t *) gIter->data;
1493 
1494         value = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL_MS);
1495         if (value == NULL) {
1496             /* skip */
1497         } else if (pcmk__str_eq(value, "0", pcmk__str_casei)) {
1498             /* skip */
1499         } else if (pcmk__str_eq(CRMD_ACTION_CANCEL, action->task, pcmk__str_casei)) {
1500             /* skip */
1501         } else if (not_on_node == NULL) {
1502             crm_trace("(null) Found: %s", action->uuid);
1503             result = g_list_prepend(result, action);
1504 
1505         } else if (action->node == NULL) {
1506             /* skip */
1507         } else if (action->node->details != not_on_node->details) {
1508             crm_trace("Found: %s", action->uuid);
1509             result = g_list_prepend(result, action);
1510         }
1511     }
1512 
1513     return result;
1514 }
1515 
1516 enum action_tasks
1517 get_complex_task(pe_resource_t * rsc, const char *name, gboolean allow_non_atomic)
     /* [previous][next][first][last][top][bottom][index][help] */
1518 {
1519     enum action_tasks task = text2task(name);
1520 
1521     if (rsc == NULL) {
1522         return task;
1523 
1524     } else if (allow_non_atomic == FALSE || rsc->variant == pe_native) {
1525         switch (task) {
1526             case stopped_rsc:
1527             case started_rsc:
1528             case action_demoted:
1529             case action_promoted:
1530                 crm_trace("Folding %s back into its atomic counterpart for %s", name, rsc->id);
1531                 return task - 1;
1532             default:
1533                 break;
1534         }
1535     }
1536     return task;
1537 }
1538 
1539 pe_action_t *
1540 find_first_action(GList *input, const char *uuid, const char *task, pe_node_t * on_node)
     /* [previous][next][first][last][top][bottom][index][help] */
1541 {
1542     GList *gIter = NULL;
1543 
1544     CRM_CHECK(uuid || task, return NULL);
1545 
1546     for (gIter = input; gIter != NULL; gIter = gIter->next) {
1547         pe_action_t *action = (pe_action_t *) gIter->data;
1548 
1549         if (uuid != NULL && !pcmk__str_eq(uuid, action->uuid, pcmk__str_casei)) {
1550             continue;
1551 
1552         } else if (task != NULL && !pcmk__str_eq(task, action->task, pcmk__str_casei)) {
1553             continue;
1554 
1555         } else if (on_node == NULL) {
1556             return action;
1557 
1558         } else if (action->node == NULL) {
1559             continue;
1560 
1561         } else if (on_node->details == action->node->details) {
1562             return action;
1563         }
1564     }
1565 
1566     return NULL;
1567 }
1568 
1569 GList *
1570 find_actions(GList *input, const char *key, const pe_node_t *on_node)
     /* [previous][next][first][last][top][bottom][index][help] */
1571 {
1572     GList *gIter = input;
1573     GList *result = NULL;
1574 
1575     CRM_CHECK(key != NULL, return NULL);
1576 
1577     for (; gIter != NULL; gIter = gIter->next) {
1578         pe_action_t *action = (pe_action_t *) gIter->data;
1579 
1580         if (!pcmk__str_eq(key, action->uuid, pcmk__str_casei)) {
1581             continue;
1582 
1583         } else if (on_node == NULL) {
1584             crm_trace("Action %s matches (ignoring node)", key);
1585             result = g_list_prepend(result, action);
1586 
1587         } else if (action->node == NULL) {
1588             crm_trace("Action %s matches (unallocated, assigning to %s)",
1589                       key, on_node->details->uname);
1590 
1591             action->node = pe__copy_node(on_node);
1592             result = g_list_prepend(result, action);
1593 
1594         } else if (on_node->details == action->node->details) {
1595             crm_trace("Action %s on %s matches", key, on_node->details->uname);
1596             result = g_list_prepend(result, action);
1597         }
1598     }
1599 
1600     return result;
1601 }
1602 
1603 GList *
1604 find_actions_exact(GList *input, const char *key, const pe_node_t *on_node)
     /* [previous][next][first][last][top][bottom][index][help] */
1605 {
1606     GList *result = NULL;
1607 
1608     CRM_CHECK(key != NULL, return NULL);
1609 
1610     if (on_node == NULL) {
1611         return NULL;
1612     }
1613 
1614     for (GList *gIter = input; gIter != NULL; gIter = gIter->next) {
1615         pe_action_t *action = (pe_action_t *) gIter->data;
1616 
1617         if ((action->node != NULL)
1618             && pcmk__str_eq(key, action->uuid, pcmk__str_casei)
1619             && pcmk__str_eq(on_node->details->id, action->node->details->id,
1620                             pcmk__str_casei)) {
1621 
1622             crm_trace("Action %s on %s matches", key, on_node->details->uname);
1623             result = g_list_prepend(result, action);
1624         }
1625     }
1626 
1627     return result;
1628 }
1629 
1630 /*!
1631  * \brief Find all actions of given type for a resource
1632  *
1633  * \param[in] rsc           Resource to search
1634  * \param[in] node          Find only actions scheduled on this node
1635  * \param[in] task          Action name to search for
1636  * \param[in] require_node  If TRUE, NULL node or action node will not match
1637  *
1638  * \return List of actions found (or NULL if none)
1639  * \note If node is not NULL and require_node is FALSE, matching actions
1640  *       without a node will be assigned to node.
1641  */
1642 GList *
1643 pe__resource_actions(const pe_resource_t *rsc, const pe_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
1644                      const char *task, bool require_node)
1645 {
1646     GList *result = NULL;
1647     char *key = pcmk__op_key(rsc->id, task, 0);
1648 
1649     if (require_node) {
1650         result = find_actions_exact(rsc->actions, key, node);
1651     } else {
1652         result = find_actions(rsc->actions, key, node);
1653     }
1654     free(key);
1655     return result;
1656 }
1657 
1658 static void
1659 resource_node_score(pe_resource_t * rsc, pe_node_t * node, int score, const char *tag)
     /* [previous][next][first][last][top][bottom][index][help] */
1660 {
1661     pe_node_t *match = NULL;
1662 
1663     if ((rsc->exclusive_discover || (node->rsc_discover_mode == pe_discover_never))
1664         && pcmk__str_eq(tag, "symmetric_default", pcmk__str_casei)) {
1665         /* This string comparision may be fragile, but exclusive resources and
1666          * exclusive nodes should not have the symmetric_default constraint
1667          * applied to them.
1668          */
1669         return;
1670 
1671     } else if (rsc->children) {
1672         GList *gIter = rsc->children;
1673 
1674         for (; gIter != NULL; gIter = gIter->next) {
1675             pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
1676 
1677             resource_node_score(child_rsc, node, score, tag);
1678         }
1679     }
1680 
1681     pe_rsc_trace(rsc, "Setting %s for %s on %s: %d", tag, rsc->id, node->details->uname, score);
1682     match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id);
1683     if (match == NULL) {
1684         match = pe__copy_node(node);
1685         g_hash_table_insert(rsc->allowed_nodes, (gpointer) match->details->id, match);
1686     }
1687     match->weight = pe__add_scores(match->weight, score);
1688 }
1689 
1690 void
1691 resource_location(pe_resource_t * rsc, pe_node_t * node, int score, const char *tag,
     /* [previous][next][first][last][top][bottom][index][help] */
1692                   pe_working_set_t * data_set)
1693 {
1694     if (node != NULL) {
1695         resource_node_score(rsc, node, score, tag);
1696 
1697     } else if (data_set != NULL) {
1698         GList *gIter = data_set->nodes;
1699 
1700         for (; gIter != NULL; gIter = gIter->next) {
1701             pe_node_t *node_iter = (pe_node_t *) gIter->data;
1702 
1703             resource_node_score(rsc, node_iter, score, tag);
1704         }
1705 
1706     } else {
1707         GHashTableIter iter;
1708         pe_node_t *node_iter = NULL;
1709 
1710         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1711         while (g_hash_table_iter_next(&iter, NULL, (void **)&node_iter)) {
1712             resource_node_score(rsc, node_iter, score, tag);
1713         }
1714     }
1715 
1716     if (node == NULL && score == -INFINITY) {
1717         if (rsc->allocated_to) {
1718             crm_info("Deallocating %s from %s", rsc->id, rsc->allocated_to->details->uname);
1719             free(rsc->allocated_to);
1720             rsc->allocated_to = NULL;
1721         }
1722     }
1723 }
1724 
1725 #define sort_return(an_int, why) do {                                   \
1726         free(a_uuid);                                           \
1727         free(b_uuid);                                           \
1728         crm_trace("%s (%d) %c %s (%d) : %s",                            \
1729                   a_xml_id, a_call_id, an_int>0?'>':an_int<0?'<':'=',   \
1730                   b_xml_id, b_call_id, why);                            \
1731         return an_int;                                                  \
1732     } while(0)
1733 
1734 gint
1735 sort_op_by_callid(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
1736 {
1737     int a_call_id = -1;
1738     int b_call_id = -1;
1739 
1740     char *a_uuid = NULL;
1741     char *b_uuid = NULL;
1742 
1743     const xmlNode *xml_a = a;
1744     const xmlNode *xml_b = b;
1745 
1746     const char *a_xml_id = crm_element_value(xml_a, XML_ATTR_ID);
1747     const char *b_xml_id = crm_element_value(xml_b, XML_ATTR_ID);
1748 
1749     if (pcmk__str_eq(a_xml_id, b_xml_id, pcmk__str_casei)) {
1750         /* We have duplicate lrm_rsc_op entries in the status
1751          * section which is unlikely to be a good thing
1752          *    - we can handle it easily enough, but we need to get
1753          *    to the bottom of why it's happening.
1754          */
1755         pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id);
1756         sort_return(0, "duplicate");
1757     }
1758 
1759     crm_element_value_int(xml_a, XML_LRM_ATTR_CALLID, &a_call_id);
1760     crm_element_value_int(xml_b, XML_LRM_ATTR_CALLID, &b_call_id);
1761 
1762     if (a_call_id == -1 && b_call_id == -1) {
1763         /* both are pending ops so it doesn't matter since
1764          *   stops are never pending
1765          */
1766         sort_return(0, "pending");
1767 
1768     } else if (a_call_id >= 0 && a_call_id < b_call_id) {
1769         sort_return(-1, "call id");
1770 
1771     } else if (b_call_id >= 0 && a_call_id > b_call_id) {
1772         sort_return(1, "call id");
1773 
1774     } else if (b_call_id >= 0 && a_call_id == b_call_id) {
1775         /*
1776          * The op and last_failed_op are the same
1777          * Order on last-rc-change
1778          */
1779         time_t last_a = -1;
1780         time_t last_b = -1;
1781 
1782         crm_element_value_epoch(xml_a, XML_RSC_OP_LAST_CHANGE, &last_a);
1783         crm_element_value_epoch(xml_b, XML_RSC_OP_LAST_CHANGE, &last_b);
1784 
1785         crm_trace("rc-change: %lld vs %lld",
1786                   (long long) last_a, (long long) last_b);
1787         if (last_a >= 0 && last_a < last_b) {
1788             sort_return(-1, "rc-change");
1789 
1790         } else if (last_b >= 0 && last_a > last_b) {
1791             sort_return(1, "rc-change");
1792         }
1793         sort_return(0, "rc-change");
1794 
1795     } else {
1796         /* One of the inputs is a pending operation
1797          * Attempt to use XML_ATTR_TRANSITION_MAGIC to determine its age relative to the other
1798          */
1799 
1800         int a_id = -1;
1801         int b_id = -1;
1802 
1803         const char *a_magic = crm_element_value(xml_a, XML_ATTR_TRANSITION_MAGIC);
1804         const char *b_magic = crm_element_value(xml_b, XML_ATTR_TRANSITION_MAGIC);
1805 
1806         CRM_CHECK(a_magic != NULL && b_magic != NULL, sort_return(0, "No magic"));
1807         if (!decode_transition_magic(a_magic, &a_uuid, &a_id, NULL, NULL, NULL,
1808                                      NULL)) {
1809             sort_return(0, "bad magic a");
1810         }
1811         if (!decode_transition_magic(b_magic, &b_uuid, &b_id, NULL, NULL, NULL,
1812                                      NULL)) {
1813             sort_return(0, "bad magic b");
1814         }
1815         /* try to determine the relative age of the operation...
1816          * some pending operations (e.g. a start) may have been superseded
1817          *   by a subsequent stop
1818          *
1819          * [a|b]_id == -1 means it's a shutdown operation and _always_ comes last
1820          */
1821         if (!pcmk__str_eq(a_uuid, b_uuid, pcmk__str_casei) || a_id == b_id) {
1822             /*
1823              * some of the logic in here may be redundant...
1824              *
1825              * if the UUID from the TE doesn't match then one better
1826              *   be a pending operation.
1827              * pending operations don't survive between elections and joins
1828              *   because we query the LRM directly
1829              */
1830 
1831             if (b_call_id == -1) {
1832                 sort_return(-1, "transition + call");
1833 
1834             } else if (a_call_id == -1) {
1835                 sort_return(1, "transition + call");
1836             }
1837 
1838         } else if ((a_id >= 0 && a_id < b_id) || b_id == -1) {
1839             sort_return(-1, "transition");
1840 
1841         } else if ((b_id >= 0 && a_id > b_id) || a_id == -1) {
1842             sort_return(1, "transition");
1843         }
1844     }
1845 
1846     /* we should never end up here */
1847     CRM_CHECK(FALSE, sort_return(0, "default"));
1848 
1849 }
1850 
1851 time_t
1852 get_effective_time(pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
1853 {
1854     if(data_set) {
1855         if (data_set->now == NULL) {
1856             crm_trace("Recording a new 'now'");
1857             data_set->now = crm_time_new(NULL);
1858         }
1859         return crm_time_get_seconds_since_epoch(data_set->now);
1860     }
1861 
1862     crm_trace("Defaulting to 'now'");
1863     return time(NULL);
1864 }
1865 
1866 gboolean
1867 get_target_role(pe_resource_t * rsc, enum rsc_role_e * role)
     /* [previous][next][first][last][top][bottom][index][help] */
1868 {
1869     enum rsc_role_e local_role = RSC_ROLE_UNKNOWN;
1870     const char *value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
1871 
1872     CRM_CHECK(role != NULL, return FALSE);
1873 
1874     if (pcmk__str_eq(value, "started", pcmk__str_null_matches | pcmk__str_casei)
1875         || pcmk__str_eq("default", value, pcmk__str_casei)) {
1876         return FALSE;
1877     }
1878 
1879     local_role = text2role(value);
1880     if (local_role == RSC_ROLE_UNKNOWN) {
1881         pcmk__config_err("Ignoring '" XML_RSC_ATTR_TARGET_ROLE "' for %s "
1882                          "because '%s' is not valid", rsc->id, value);
1883         return FALSE;
1884 
1885     } else if (local_role > RSC_ROLE_STARTED) {
1886         if (pcmk_is_set(uber_parent(rsc)->flags, pe_rsc_promotable)) {
1887             if (local_role > RSC_ROLE_UNPROMOTED) {
1888                 /* This is what we'd do anyway, just leave the default to avoid messing up the placement algorithm */
1889                 return FALSE;
1890             }
1891 
1892         } else {
1893             pcmk__config_err("Ignoring '" XML_RSC_ATTR_TARGET_ROLE "' for %s "
1894                              "because '%s' only makes sense for promotable "
1895                              "clones", rsc->id, value);
1896             return FALSE;
1897         }
1898     }
1899 
1900     *role = local_role;
1901     return TRUE;
1902 }
1903 
1904 gboolean
1905 order_actions(pe_action_t * lh_action, pe_action_t * rh_action, enum pe_ordering order)
     /* [previous][next][first][last][top][bottom][index][help] */
1906 {
1907     GList *gIter = NULL;
1908     pe_action_wrapper_t *wrapper = NULL;
1909     GList *list = NULL;
1910 
1911     if (order == pe_order_none) {
1912         return FALSE;
1913     }
1914 
1915     if (lh_action == NULL || rh_action == NULL) {
1916         return FALSE;
1917     }
1918 
1919     crm_trace("Creating action wrappers for ordering: %s then %s",
1920               lh_action->uuid, rh_action->uuid);
1921 
1922     /* Ensure we never create a dependency on ourselves... it's happened */
1923     CRM_ASSERT(lh_action != rh_action);
1924 
1925     /* Filter dups, otherwise update_action_states() has too much work to do */
1926     gIter = lh_action->actions_after;
1927     for (; gIter != NULL; gIter = gIter->next) {
1928         pe_action_wrapper_t *after = (pe_action_wrapper_t *) gIter->data;
1929 
1930         if (after->action == rh_action && (after->type & order)) {
1931             return FALSE;
1932         }
1933     }
1934 
1935     wrapper = calloc(1, sizeof(pe_action_wrapper_t));
1936     wrapper->action = rh_action;
1937     wrapper->type = order;
1938     list = lh_action->actions_after;
1939     list = g_list_prepend(list, wrapper);
1940     lh_action->actions_after = list;
1941 
1942     wrapper = calloc(1, sizeof(pe_action_wrapper_t));
1943     wrapper->action = lh_action;
1944     wrapper->type = order;
1945     list = rh_action->actions_before;
1946     list = g_list_prepend(list, wrapper);
1947     rh_action->actions_before = list;
1948     return TRUE;
1949 }
1950 
1951 pe_action_t *
1952 get_pseudo_op(const char *name, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
1953 {
1954     pe_action_t *op = lookup_singleton(data_set, name);
1955 
1956     if (op == NULL) {
1957         op = custom_action(NULL, strdup(name), name, NULL, TRUE, TRUE, data_set);
1958         pe__set_action_flags(op, pe_action_pseudo|pe_action_runnable);
1959     }
1960     return op;
1961 }
1962 
1963 void
1964 destroy_ticket(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
1965 {
1966     pe_ticket_t *ticket = data;
1967 
1968     if (ticket->state) {
1969         g_hash_table_destroy(ticket->state);
1970     }
1971     free(ticket->id);
1972     free(ticket);
1973 }
1974 
1975 pe_ticket_t *
1976 ticket_new(const char *ticket_id, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
1977 {
1978     pe_ticket_t *ticket = NULL;
1979 
1980     if (pcmk__str_empty(ticket_id)) {
1981         return NULL;
1982     }
1983 
1984     if (data_set->tickets == NULL) {
1985         data_set->tickets = pcmk__strkey_table(free, destroy_ticket);
1986     }
1987 
1988     ticket = g_hash_table_lookup(data_set->tickets, ticket_id);
1989     if (ticket == NULL) {
1990 
1991         ticket = calloc(1, sizeof(pe_ticket_t));
1992         if (ticket == NULL) {
1993             crm_err("Cannot allocate ticket '%s'", ticket_id);
1994             return NULL;
1995         }
1996 
1997         crm_trace("Creaing ticket entry for %s", ticket_id);
1998 
1999         ticket->id = strdup(ticket_id);
2000         ticket->granted = FALSE;
2001         ticket->last_granted = -1;
2002         ticket->standby = FALSE;
2003         ticket->state = pcmk__strkey_table(free, free);
2004 
2005         g_hash_table_insert(data_set->tickets, strdup(ticket->id), ticket);
2006     }
2007 
2008     return ticket;
2009 }
2010 
2011 const char *rsc_printable_id(pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
2012 {
2013     if (!pcmk_is_set(rsc->flags, pe_rsc_unique)) {
2014         return ID(rsc->xml);
2015     }
2016     return rsc->id;
2017 }
2018 
2019 void
2020 pe__clear_resource_flags_recursive(pe_resource_t *rsc, uint64_t flags)
     /* [previous][next][first][last][top][bottom][index][help] */
2021 {
2022     pe__clear_resource_flags(rsc, flags);
2023     for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
2024         pe__clear_resource_flags_recursive((pe_resource_t *) gIter->data, flags);
2025     }
2026 }
2027 
2028 void
2029 pe__clear_resource_flags_on_all(pe_working_set_t *data_set, uint64_t flag)
     /* [previous][next][first][last][top][bottom][index][help] */
2030 {
2031     for (GList *lpc = data_set->resources; lpc != NULL; lpc = lpc->next) {
2032         pe_resource_t *r = (pe_resource_t *) lpc->data;
2033         pe__clear_resource_flags_recursive(r, flag);
2034     }
2035 }
2036 
2037 void
2038 pe__set_resource_flags_recursive(pe_resource_t *rsc, uint64_t flags)
     /* [previous][next][first][last][top][bottom][index][help] */
2039 {
2040     pe__set_resource_flags(rsc, flags);
2041     for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
2042         pe__set_resource_flags_recursive((pe_resource_t *) gIter->data, flags);
2043     }
2044 }
2045 
2046 static GList *
2047 find_unfencing_devices(GList *candidates, GList *matches) 
     /* [previous][next][first][last][top][bottom][index][help] */
2048 {
2049     for (GList *gIter = candidates; gIter != NULL; gIter = gIter->next) {
2050         pe_resource_t *candidate = gIter->data;
2051         const char *provides = g_hash_table_lookup(candidate->meta,
2052                                                    PCMK_STONITH_PROVIDES);
2053         const char *requires = g_hash_table_lookup(candidate->meta, XML_RSC_ATTR_REQUIRES);
2054 
2055         if(candidate->children) {
2056             matches = find_unfencing_devices(candidate->children, matches);
2057         } else if (!pcmk_is_set(candidate->flags, pe_rsc_fence_device)) {
2058             continue;
2059 
2060         } else if (pcmk__str_eq(provides, "unfencing", pcmk__str_casei) || pcmk__str_eq(requires, "unfencing", pcmk__str_casei)) {
2061             matches = g_list_prepend(matches, candidate);
2062         }
2063     }
2064     return matches;
2065 }
2066 
2067 static int
2068 node_priority_fencing_delay(pe_node_t * node, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
2069 {
2070     int member_count = 0;
2071     int online_count = 0;
2072     int top_priority = 0;
2073     int lowest_priority = 0;
2074     GList *gIter = NULL;
2075 
2076     // `priority-fencing-delay` is disabled
2077     if (data_set->priority_fencing_delay <= 0) {
2078         return 0;
2079     }
2080 
2081     /* No need to request a delay if the fencing target is not a normal cluster
2082      * member, for example if it's a remote node or a guest node. */
2083     if (node->details->type != node_member) {
2084         return 0;
2085     }
2086 
2087     // No need to request a delay if the fencing target is in our partition
2088     if (node->details->online) {
2089         return 0;
2090     }
2091 
2092     for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
2093         pe_node_t *n =  gIter->data;
2094 
2095         if (n->details->type != node_member) {
2096             continue;
2097         }
2098 
2099         member_count ++;
2100 
2101         if (n->details->online) {
2102             online_count++;
2103         }
2104 
2105         if (member_count == 1
2106             || n->details->priority > top_priority) {
2107             top_priority = n->details->priority;
2108         }
2109 
2110         if (member_count == 1
2111             || n->details->priority < lowest_priority) {
2112             lowest_priority = n->details->priority;
2113         }
2114     }
2115 
2116     // No need to delay if we have more than half of the cluster members
2117     if (online_count > member_count / 2) {
2118         return 0;
2119     }
2120 
2121     /* All the nodes have equal priority.
2122      * Any configured corresponding `pcmk_delay_base/max` will be applied. */
2123     if (lowest_priority == top_priority) {
2124         return 0;
2125     }
2126 
2127     if (node->details->priority < top_priority) {
2128         return 0;
2129     }
2130 
2131     return data_set->priority_fencing_delay;
2132 }
2133 
2134 pe_action_t *
2135 pe_fence_op(pe_node_t * node, const char *op, bool optional, const char *reason,
     /* [previous][next][first][last][top][bottom][index][help] */
2136             bool priority_delay, pe_working_set_t * data_set)
2137 {
2138     char *op_key = NULL;
2139     pe_action_t *stonith_op = NULL;
2140 
2141     if(op == NULL) {
2142         op = data_set->stonith_action;
2143     }
2144 
2145     op_key = crm_strdup_printf("%s-%s-%s", CRM_OP_FENCE, node->details->uname, op);
2146 
2147     stonith_op = lookup_singleton(data_set, op_key);
2148     if(stonith_op == NULL) {
2149         stonith_op = custom_action(NULL, op_key, CRM_OP_FENCE, node, TRUE, TRUE, data_set);
2150 
2151         add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET, node->details->uname);
2152         add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET_UUID, node->details->id);
2153         add_hash_param(stonith_op->meta, "stonith_action", op);
2154 
2155         if (pe__is_guest_or_remote_node(node)
2156             && pcmk_is_set(data_set->flags, pe_flag_enable_unfencing)) {
2157             /* Extra work to detect device changes on remotes
2158              *
2159              * We may do this for all nodes in the future, but for now
2160              * the check_action_definition() based stuff works fine.
2161              */
2162             long max = 1024;
2163             long digests_all_offset = 0;
2164             long digests_secure_offset = 0;
2165 
2166             char *digests_all = calloc(max, sizeof(char));
2167             char *digests_secure = calloc(max, sizeof(char));
2168             GList *matches = find_unfencing_devices(data_set->resources, NULL);
2169 
2170             for (GList *gIter = matches; gIter != NULL; gIter = gIter->next) {
2171                 pe_resource_t *match = gIter->data;
2172                 const char *agent = g_hash_table_lookup(match->meta,
2173                                                         XML_ATTR_TYPE);
2174                 op_digest_cache_t *data = NULL;
2175 
2176                 data = pe__compare_fencing_digest(match, agent, node, data_set);
2177                 if(data->rc == RSC_DIGEST_ALL) {
2178                     optional = FALSE;
2179                     crm_notice("Unfencing %s (remote): because the definition of %s changed", node->details->uname, match->id);
2180                     if (!pcmk__is_daemon && data_set->priv != NULL) {
2181                         pcmk__output_t *out = data_set->priv;
2182                         out->info(out, "notice: Unfencing %s (remote): because the definition of %s changed",
2183                                   node->details->uname, match->id);
2184                     }
2185                 }
2186 
2187                 digests_all_offset += snprintf(
2188                     digests_all+digests_all_offset, max-digests_all_offset,
2189                     "%s:%s:%s,", match->id, agent, data->digest_all_calc);
2190 
2191                 digests_secure_offset += snprintf(
2192                     digests_secure+digests_secure_offset, max-digests_secure_offset,
2193                     "%s:%s:%s,", match->id, agent, data->digest_secure_calc);
2194             }
2195             g_hash_table_insert(stonith_op->meta,
2196                                 strdup(XML_OP_ATTR_DIGESTS_ALL),
2197                                 digests_all);
2198             g_hash_table_insert(stonith_op->meta,
2199                                 strdup(XML_OP_ATTR_DIGESTS_SECURE),
2200                                 digests_secure);
2201         }
2202 
2203     } else {
2204         free(op_key);
2205     }
2206 
2207     if (data_set->priority_fencing_delay > 0
2208 
2209             /* It's a suitable case where `priority-fencing-delay` applies.
2210              * At least add `priority-fencing-delay` field as an indicator. */
2211         && (priority_delay
2212 
2213             /* Re-calculate priority delay for the suitable case when
2214              * pe_fence_op() is called again by stage6() after node priority has
2215              * been actually calculated with native_add_running() */
2216             || g_hash_table_lookup(stonith_op->meta,
2217                                    XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY) != NULL)) {
2218 
2219             /* Add `priority-fencing-delay` to the fencing op even if it's 0 for
2220              * the targeting node. So that it takes precedence over any possible
2221              * `pcmk_delay_base/max`.
2222              */
2223             char *delay_s = pcmk__itoa(node_priority_fencing_delay(node, data_set));
2224 
2225             g_hash_table_insert(stonith_op->meta,
2226                                 strdup(XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY),
2227                                 delay_s);
2228     }
2229 
2230     if(optional == FALSE && pe_can_fence(data_set, node)) {
2231         pe__clear_action_flags(stonith_op, pe_action_optional);
2232         pe_action_set_reason(stonith_op, reason, false);
2233 
2234     } else if(reason && stonith_op->reason == NULL) {
2235         stonith_op->reason = strdup(reason);
2236     }
2237 
2238     return stonith_op;
2239 }
2240 
2241 void
2242 trigger_unfencing(
     /* [previous][next][first][last][top][bottom][index][help] */
2243     pe_resource_t * rsc, pe_node_t *node, const char *reason, pe_action_t *dependency, pe_working_set_t * data_set) 
2244 {
2245     if (!pcmk_is_set(data_set->flags, pe_flag_enable_unfencing)) {
2246         /* No resources require it */
2247         return;
2248 
2249     } else if ((rsc != NULL)
2250                && !pcmk_is_set(rsc->flags, pe_rsc_fence_device)) {
2251         /* Wasn't a stonith device */
2252         return;
2253 
2254     } else if(node
2255               && node->details->online
2256               && node->details->unclean == FALSE
2257               && node->details->shutdown == FALSE) {
2258         pe_action_t *unfence = pe_fence_op(node, "on", FALSE, reason, FALSE, data_set);
2259 
2260         if(dependency) {
2261             order_actions(unfence, dependency, pe_order_optional);
2262         }
2263 
2264     } else if(rsc) {
2265         GHashTableIter iter;
2266 
2267         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
2268         while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
2269             if(node->details->online && node->details->unclean == FALSE && node->details->shutdown == FALSE) {
2270                 trigger_unfencing(rsc, node, reason, dependency, data_set);
2271             }
2272         }
2273     }
2274 }
2275 
2276 gboolean
2277 add_tag_ref(GHashTable * tags, const char * tag_name,  const char * obj_ref)
     /* [previous][next][first][last][top][bottom][index][help] */
2278 {
2279     pe_tag_t *tag = NULL;
2280     GList *gIter = NULL;
2281     gboolean is_existing = FALSE;
2282 
2283     CRM_CHECK(tags && tag_name && obj_ref, return FALSE);
2284 
2285     tag = g_hash_table_lookup(tags, tag_name);
2286     if (tag == NULL) {
2287         tag = calloc(1, sizeof(pe_tag_t));
2288         if (tag == NULL) {
2289             return FALSE;
2290         }
2291         tag->id = strdup(tag_name);
2292         tag->refs = NULL;
2293         g_hash_table_insert(tags, strdup(tag_name), tag);
2294     }
2295 
2296     for (gIter = tag->refs; gIter != NULL; gIter = gIter->next) {
2297         const char *existing_ref = (const char *) gIter->data;
2298 
2299         if (pcmk__str_eq(existing_ref, obj_ref, pcmk__str_none)){
2300             is_existing = TRUE;
2301             break;
2302         }
2303     }
2304 
2305     if (is_existing == FALSE) {
2306         tag->refs = g_list_append(tag->refs, strdup(obj_ref));
2307         crm_trace("Added: tag=%s ref=%s", tag->id, obj_ref);
2308     }
2309 
2310     return TRUE;
2311 }
2312 
2313 /*!
2314  * \internal
2315  * \brief Create an action reason string based on the action itself
2316  *
2317  * \param[in] action  Action to create reason string for
2318  * \param[in] flag    Action flag that was cleared
2319  *
2320  * \return Newly allocated string suitable for use as action reason
2321  * \note It is the caller's responsibility to free() the result.
2322  */
2323 char *
2324 pe__action2reason(pe_action_t *action, enum pe_action_flags flag)
     /* [previous][next][first][last][top][bottom][index][help] */
2325 {
2326     const char *change = NULL;
2327 
2328     switch (flag) {
2329         case pe_action_runnable:
2330         case pe_action_migrate_runnable:
2331             change = "unrunnable";
2332             break;
2333         case pe_action_optional:
2334             change = "required";
2335             break;
2336         default:
2337             // Bug: caller passed unsupported flag
2338             CRM_CHECK(change != NULL, change = "");
2339             break;
2340     }
2341     return crm_strdup_printf("%s%s%s %s", change,
2342                              (action->rsc == NULL)? "" : " ",
2343                              (action->rsc == NULL)? "" : action->rsc->id,
2344                              action->task);
2345 }
2346 
2347 void pe_action_set_reason(pe_action_t *action, const char *reason, bool overwrite) 
     /* [previous][next][first][last][top][bottom][index][help] */
2348 {
2349     if (action->reason != NULL && overwrite) {
2350         pe_rsc_trace(action->rsc, "Changing %s reason from '%s' to '%s'",
2351                      action->uuid, action->reason, crm_str(reason));
2352         free(action->reason);
2353     } else if (action->reason == NULL) {
2354         pe_rsc_trace(action->rsc, "Set %s reason to '%s'",
2355                      action->uuid, crm_str(reason));
2356     } else {
2357         // crm_assert(action->reason != NULL && !overwrite);
2358         return;
2359     }
2360 
2361     if (reason != NULL) {
2362         action->reason = strdup(reason);
2363     } else {
2364         action->reason = NULL;
2365     }
2366 }
2367 
2368 /*!
2369  * \internal
2370  * \brief Check whether shutdown has been requested for a node
2371  *
2372  * \param[in] node  Node to check
2373  *
2374  * \return TRUE if node has shutdown attribute set and nonzero, FALSE otherwise
2375  * \note This differs from simply using node->details->shutdown in that it can
2376  *       be used before that has been determined (and in fact to determine it),
2377  *       and it can also be used to distinguish requested shutdown from implicit
2378  *       shutdown of remote nodes by virtue of their connection stopping.
2379  */
2380 bool
2381 pe__shutdown_requested(pe_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
2382 {
2383     const char *shutdown = pe_node_attribute_raw(node, XML_CIB_ATTR_SHUTDOWN);
2384 
2385     return !pcmk__str_eq(shutdown, "0", pcmk__str_null_matches);
2386 }
2387 
2388 /*!
2389  * \internal
2390  * \brief Update a data set's "recheck by" time
2391  *
2392  * \param[in]     recheck   Epoch time when recheck should happen
2393  * \param[in,out] data_set  Current working set
2394  */
2395 void
2396 pe__update_recheck_time(time_t recheck, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
2397 {
2398     if ((recheck > get_effective_time(data_set))
2399         && ((data_set->recheck_by == 0)
2400             || (data_set->recheck_by > recheck))) {
2401         data_set->recheck_by = recheck;
2402     }
2403 }
2404 
2405 /*!
2406  * \internal
2407  * \brief Wrapper for pe_unpack_nvpairs() using a cluster working set
2408  */
2409 void
2410 pe__unpack_dataset_nvpairs(xmlNode *xml_obj, const char *set_name,
     /* [previous][next][first][last][top][bottom][index][help] */
2411                            pe_rule_eval_data_t *rule_data, GHashTable *hash,
2412                            const char *always_first, gboolean overwrite,
2413                            pe_working_set_t *data_set)
2414 {
2415     crm_time_t *next_change = crm_time_new_undefined();
2416 
2417     pe_eval_nvpairs(data_set->input, xml_obj, set_name, rule_data, hash,
2418                     always_first, overwrite, next_change);
2419     if (crm_time_is_defined(next_change)) {
2420         time_t recheck = (time_t) crm_time_get_seconds_since_epoch(next_change);
2421 
2422         pe__update_recheck_time(recheck, data_set);
2423     }
2424     crm_time_free(next_change);
2425 }
2426 
2427 bool
2428 pe__resource_is_disabled(pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
2429 {
2430     const char *target_role = NULL;
2431 
2432     CRM_CHECK(rsc != NULL, return false);
2433     target_role = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
2434     if (target_role) {
2435         enum rsc_role_e target_role_e = text2role(target_role);
2436 
2437         if ((target_role_e == RSC_ROLE_STOPPED)
2438             || ((target_role_e == RSC_ROLE_UNPROMOTED)
2439                 && pcmk_is_set(uber_parent(rsc)->flags, pe_rsc_promotable))) {
2440             return true;
2441         }
2442     }
2443     return false;
2444 }
2445 
2446 /*!
2447  * \internal
2448  * \brief Create an action to clear a resource's history from CIB
2449  *
2450  * \param[in] rsc   Resource to clear
2451  * \param[in] node  Node to clear history on
2452  *
2453  * \return New action to clear resource history
2454  */
2455 pe_action_t *
2456 pe__clear_resource_history(pe_resource_t *rsc, pe_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
2457                            pe_working_set_t *data_set)
2458 {
2459     char *key = NULL;
2460 
2461     CRM_ASSERT(rsc && node);
2462     key = pcmk__op_key(rsc->id, CRM_OP_LRM_DELETE, 0);
2463     return custom_action(rsc, key, CRM_OP_LRM_DELETE, node, FALSE, TRUE,
2464                          data_set);
2465 }
2466 
2467 bool
2468 pe__rsc_running_on_any(pe_resource_t *rsc, GList *node_list)
     /* [previous][next][first][last][top][bottom][index][help] */
2469 {
2470     for (GList *ele = rsc->running_on; ele; ele = ele->next) {
2471         pe_node_t *node = (pe_node_t *) ele->data;
2472         if (pcmk__str_in_list(node->details->uname, node_list,
2473                               pcmk__str_star_matches|pcmk__str_casei)) {
2474             return true;
2475         }
2476     }
2477 
2478     return false;
2479 }
2480 
2481 bool
2482 pcmk__rsc_filtered_by_node(pe_resource_t *rsc, GList *only_node)
     /* [previous][next][first][last][top][bottom][index][help] */
2483 {
2484     return (rsc->fns->active(rsc, FALSE) && !pe__rsc_running_on_any(rsc, only_node));
2485 }
2486 
2487 GList *
2488 pe__filter_rsc_list(GList *rscs, GList *filter)
     /* [previous][next][first][last][top][bottom][index][help] */
2489 {
2490     GList *retval = NULL;
2491 
2492     for (GList *gIter = rscs; gIter; gIter = gIter->next) {
2493         pe_resource_t *rsc = (pe_resource_t *) gIter->data;
2494 
2495         /* I think the second condition is safe here for all callers of this
2496          * function.  If not, it needs to move into pe__node_text.
2497          */
2498         if (pcmk__str_in_list(rsc_printable_id(rsc), filter, pcmk__str_star_matches) ||
2499             (rsc->parent && pcmk__str_in_list(rsc_printable_id(rsc->parent), filter, pcmk__str_star_matches))) {
2500             retval = g_list_prepend(retval, rsc);
2501         }
2502     }
2503 
2504     return retval;
2505 }
2506 
2507 GList *
2508 pe__build_node_name_list(pe_working_set_t *data_set, const char *s) {
     /* [previous][next][first][last][top][bottom][index][help] */
2509     GList *nodes = NULL;
2510 
2511     if (pcmk__str_eq(s, "*", pcmk__str_null_matches)) {
2512         /* Nothing was given so return a list of all node names.  Or, '*' was
2513          * given.  This would normally fall into the pe__unames_with_tag branch
2514          * where it will return an empty list.  Catch it here instead.
2515          */
2516         nodes = g_list_prepend(nodes, strdup("*"));
2517     } else {
2518         pe_node_t *node = pe_find_node(data_set->nodes, s);
2519 
2520         if (node) {
2521             /* The given string was a valid uname for a node.  Return a
2522              * singleton list containing just that uname.
2523              */
2524             nodes = g_list_prepend(nodes, strdup(s));
2525         } else {
2526             /* The given string was not a valid uname.  It's either a tag or
2527              * it's a typo or something.  In the first case, we'll return a
2528              * list of all the unames of the nodes with the given tag.  In the
2529              * second case, we'll return a NULL pointer and nothing will
2530              * get displayed.
2531              */
2532             nodes = pe__unames_with_tag(data_set, s);
2533         }
2534     }
2535 
2536     return nodes;
2537 }
2538 
2539 GList *
2540 pe__build_rsc_list(pe_working_set_t *data_set, const char *s) {
     /* [previous][next][first][last][top][bottom][index][help] */
2541     GList *resources = NULL;
2542 
2543     if (pcmk__str_eq(s, "*", pcmk__str_null_matches)) {
2544         resources = g_list_prepend(resources, strdup("*"));
2545     } else {
2546         pe_resource_t *rsc = pe_find_resource_with_flags(data_set->resources, s,
2547                                                          pe_find_renamed|pe_find_any);
2548 
2549         if (rsc) {
2550             /* A colon in the name we were given means we're being asked to filter
2551              * on a specific instance of a cloned resource.  Put that exact string
2552              * into the filter list.  Otherwise, use the printable ID of whatever
2553              * resource was found that matches what was asked for.
2554              */
2555             if (strstr(s, ":") != NULL) {
2556                 resources = g_list_prepend(resources, strdup(rsc->id));
2557             } else {
2558                 resources = g_list_prepend(resources, strdup(rsc_printable_id(rsc)));
2559             }
2560         } else {
2561             /* The given string was not a valid resource name.  It's either
2562              * a tag or it's a typo or something.  See build_uname_list for
2563              * more detail.
2564              */
2565             resources = pe__rscs_with_tag(data_set, s);
2566         }
2567     }
2568 
2569     return resources;
2570 }

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