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. append_dump_text
  12. dump_node_capacity
  13. dump_rsc_utilization
  14. sort_rsc_index
  15. sort_rsc_priority
  16. effective_quorum_policy
  17. custom_action
  18. valid_stop_on_fail
  19. unpack_operation_on_fail
  20. find_min_interval_mon
  21. unpack_start_delay
  22. unpack_interval_origin
  23. unpack_timeout
  24. pe_get_configured_timeout
  25. unpack_versioned_meta
  26. unpack_operation
  27. find_rsc_op_entry_helper
  28. find_rsc_op_entry
  29. print_node
  30. print_str_str
  31. pe_free_action
  32. find_recurring_actions
  33. get_complex_task
  34. find_first_action
  35. find_actions
  36. find_actions_exact
  37. pe__resource_actions
  38. resource_node_score
  39. resource_location
  40. sort_op_by_callid
  41. get_effective_time
  42. get_target_role
  43. order_actions
  44. get_pseudo_op
  45. destroy_ticket
  46. ticket_new
  47. filter_parameters
  48. append_versioned_params
  49. rsc_action_digest
  50. rsc_action_digest_cmp
  51. create_unfencing_summary
  52. unfencing_digest_matches
  53. fencing_action_digest_cmp
  54. rsc_printable_id
  55. pe__clear_resource_flags_recursive
  56. pe__set_resource_flags_recursive
  57. find_unfencing_devices
  58. node_priority_fencing_delay
  59. pe_fence_op
  60. trigger_unfencing
  61. add_tag_ref
  62. pe_action_set_flag_reason
  63. pe_action_set_reason
  64. pe__shutdown_requested
  65. pe__update_recheck_time
  66. pe__unpack_dataset_nvpairs
  67. pe__resource_is_disabled
  68. pe__clear_resource_history
  69. pe__rsc_running_on_any_node_in_list
  70. pcmk__rsc_filtered_by_node
  71. pe__filter_rsc_list

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

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