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. node_copy
  5. node_list_exclude
  6. node_hash_from_list
  7. node_list_dup
  8. sort_node_uname
  9. dump_node_scores_worker
  10. append_dump_text
  11. dump_node_capacity
  12. dump_rsc_utilization
  13. sort_rsc_index
  14. sort_rsc_priority
  15. custom_action
  16. unpack_operation_on_fail
  17. find_min_interval_mon
  18. unpack_start_delay
  19. unpack_interval_origin
  20. unpack_timeout
  21. pe_get_configured_timeout
  22. unpack_versioned_meta
  23. unpack_operation
  24. find_rsc_op_entry_helper
  25. find_rsc_op_entry
  26. print_node
  27. print_str_str
  28. print_resource
  29. pe_free_action
  30. find_recurring_actions
  31. get_complex_task
  32. find_first_action
  33. find_actions
  34. find_actions_exact
  35. resource_node_score
  36. resource_location
  37. sort_op_by_callid
  38. get_effective_time
  39. get_target_role
  40. order_actions
  41. get_pseudo_op
  42. destroy_ticket
  43. ticket_new
  44. filter_parameters
  45. append_versioned_params
  46. rsc_action_digest
  47. rsc_action_digest_cmp
  48. fencing_action_digest_cmp
  49. rsc_printable_id
  50. clear_bit_recursive
  51. set_bit_recursive
  52. find_unfencing_devices
  53. pe_fence_op
  54. trigger_unfencing
  55. add_tag_ref
  56. pe_action_set_flag_reason
  57. pe_action_set_reason

   1 /*
   2  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
   3  *
   4  * This library is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2.1 of the License, or (at your option) any later version.
   8  *
   9  * This library is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with this library; if not, write to the Free Software
  16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  17  */
  18 #include <crm_internal.h>
  19 #include <crm/crm.h>
  20 #include <crm/msg_xml.h>
  21 #include <crm/common/xml.h>
  22 #include <crm/common/util.h>
  23 
  24 #include <glib.h>
  25 
  26 #include <crm/pengine/rules.h>
  27 #include <crm/pengine/internal.h>
  28 
  29 #include <unpack.h>
  30 
  31 pe_working_set_t *pe_dataset = NULL;
  32 
  33 extern xmlNode *get_object_root(const char *object_type, xmlNode * the_root);
  34 void print_str_str(gpointer key, gpointer value, gpointer user_data);
  35 gboolean ghash_free_str_str(gpointer key, gpointer value, gpointer user_data);
  36 void unpack_operation(action_t * action, xmlNode * xml_obj, resource_t * container,
  37                       pe_working_set_t * data_set);
  38 static xmlNode *find_rsc_op_entry_helper(resource_t * rsc, const char *key,
  39                                          gboolean include_disabled);
  40 
  41 #if ENABLE_VERSIONED_ATTRS
  42 pe_rsc_action_details_t *
  43 pe_rsc_action_details(pe_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
  44 {
  45     pe_rsc_action_details_t *details;
  46 
  47     CRM_CHECK(action != NULL, return NULL);
  48 
  49     if (action->action_details == NULL) {
  50         action->action_details = calloc(1, sizeof(pe_rsc_action_details_t));
  51         CRM_CHECK(action->action_details != NULL, return NULL);
  52     }
  53 
  54     details = (pe_rsc_action_details_t *) action->action_details;
  55     if (details->versioned_parameters == NULL) {
  56         details->versioned_parameters = create_xml_node(NULL,
  57                                                         XML_TAG_OP_VER_ATTRS);
  58     }
  59     if (details->versioned_meta == NULL) {
  60         details->versioned_meta = create_xml_node(NULL, XML_TAG_OP_VER_META);
  61     }
  62     return details;
  63 }
  64 
  65 static void
  66 pe_free_rsc_action_details(pe_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
  67 {
  68     pe_rsc_action_details_t *details;
  69 
  70     if ((action == NULL) || (action->action_details == NULL)) {
  71         return;
  72     }
  73 
  74     details = (pe_rsc_action_details_t *) action->action_details;
  75 
  76     if (details->versioned_parameters) {
  77         free_xml(details->versioned_parameters);
  78     }
  79     if (details->versioned_meta) {
  80         free_xml(details->versioned_meta);
  81     }
  82 
  83     action->action_details = NULL;
  84 }
  85 #endif
  86 
  87 /*!
  88  * \internal
  89  * \brief Check whether we can fence a particular node
  90  *
  91  * \param[in] data_set  Working set for cluster
  92  * \param[in] node      Name of node to check
  93  *
  94  * \return TRUE if node can be fenced, FALSE otherwise
  95  *
  96  * \note This function should only be called for cluster nodes and baremetal
  97  *       remote nodes; guest nodes are fenced by stopping their container
  98  *       resource, so fence execution requirements do not apply to them.
  99  */
 100 bool pe_can_fence(pe_working_set_t * data_set, node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 101 {
 102     if(is_not_set(data_set->flags, pe_flag_stonith_enabled)) {
 103         return FALSE; /* Turned off */
 104 
 105     } else if (is_not_set(data_set->flags, pe_flag_have_stonith_resource)) {
 106         return FALSE; /* No devices */
 107 
 108     } else if (is_set(data_set->flags, pe_flag_have_quorum)) {
 109         return TRUE;
 110 
 111     } else if (data_set->no_quorum_policy == no_quorum_ignore) {
 112         return TRUE;
 113 
 114     } else if(node == NULL) {
 115         return FALSE;
 116 
 117     } else if(node->details->online) {
 118         crm_notice("We can fence %s without quorum because they're in our membership", node->details->uname);
 119         return TRUE;
 120     }
 121 
 122     crm_trace("Cannot fence %s", node->details->uname);
 123     return FALSE;
 124 }
 125 
 126 node_t *
 127 node_copy(const node_t *this_node)
     /* [previous][next][first][last][top][bottom][index][help] */
 128 {
 129     node_t *new_node = NULL;
 130 
 131     CRM_CHECK(this_node != NULL, return NULL);
 132 
 133     new_node = calloc(1, sizeof(node_t));
 134     CRM_ASSERT(new_node != NULL);
 135 
 136     crm_trace("Copying %p (%s) to %p", this_node, this_node->details->uname, new_node);
 137 
 138     new_node->rsc_discover_mode = this_node->rsc_discover_mode;
 139     new_node->weight = this_node->weight;
 140     new_node->fixed = this_node->fixed;
 141     new_node->details = this_node->details;
 142 
 143     return new_node;
 144 }
 145 
 146 /* any node in list1 or list2 and not in the other gets a score of -INFINITY */
 147 void
 148 node_list_exclude(GHashTable * hash, GListPtr list, gboolean merge_scores)
     /* [previous][next][first][last][top][bottom][index][help] */
 149 {
 150     GHashTable *result = hash;
 151     node_t *other_node = NULL;
 152     GListPtr gIter = list;
 153 
 154     GHashTableIter iter;
 155     node_t *node = NULL;
 156 
 157     g_hash_table_iter_init(&iter, hash);
 158     while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
 159 
 160         other_node = pe_find_node_id(list, node->details->id);
 161         if (other_node == NULL) {
 162             node->weight = -INFINITY;
 163         } else if (merge_scores) {
 164             node->weight = merge_weights(node->weight, other_node->weight);
 165         }
 166     }
 167 
 168     for (; gIter != NULL; gIter = gIter->next) {
 169         node_t *node = (node_t *) gIter->data;
 170 
 171         other_node = pe_hash_table_lookup(result, node->details->id);
 172 
 173         if (other_node == NULL) {
 174             node_t *new_node = node_copy(node);
 175 
 176             new_node->weight = -INFINITY;
 177             g_hash_table_insert(result, (gpointer) new_node->details->id, new_node);
 178         }
 179     }
 180 }
 181 
 182 GHashTable *
 183 node_hash_from_list(GListPtr list)
     /* [previous][next][first][last][top][bottom][index][help] */
 184 {
 185     GListPtr gIter = list;
 186     GHashTable *result = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, g_hash_destroy_str);
 187 
 188     for (; gIter != NULL; gIter = gIter->next) {
 189         node_t *node = (node_t *) gIter->data;
 190         node_t *n = node_copy(node);
 191 
 192         g_hash_table_insert(result, (gpointer) n->details->id, n);
 193     }
 194 
 195     return result;
 196 }
 197 
 198 GListPtr
 199 node_list_dup(GListPtr list1, gboolean reset, gboolean filter)
     /* [previous][next][first][last][top][bottom][index][help] */
 200 {
 201     GListPtr result = NULL;
 202     GListPtr gIter = list1;
 203 
 204     for (; gIter != NULL; gIter = gIter->next) {
 205         node_t *new_node = NULL;
 206         node_t *this_node = (node_t *) gIter->data;
 207 
 208         if (filter && this_node->weight < 0) {
 209             continue;
 210         }
 211 
 212         new_node = node_copy(this_node);
 213         if (reset) {
 214             new_node->weight = 0;
 215         }
 216         if (new_node != NULL) {
 217             result = g_list_prepend(result, new_node);
 218         }
 219     }
 220 
 221     return result;
 222 }
 223 
 224 gint
 225 sort_node_uname(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 226 {
 227     const node_t *node_a = a;
 228     const node_t *node_b = b;
 229 
 230     return strcmp(node_a->details->uname, node_b->details->uname);
 231 }
 232 
 233 void
 234 dump_node_scores_worker(int level, const char *file, const char *function, int line,
     /* [previous][next][first][last][top][bottom][index][help] */
 235                         resource_t * rsc, const char *comment, GHashTable * nodes)
 236 {
 237     GHashTable *hash = nodes;
 238     GHashTableIter iter;
 239     node_t *node = NULL;
 240 
 241     if (rsc) {
 242         hash = rsc->allowed_nodes;
 243     }
 244 
 245     if (rsc && is_set(rsc->flags, pe_rsc_orphan)) {
 246         /* Don't show the allocation scores for orphans */
 247         return;
 248     }
 249 
 250     if (level == 0) {
 251         char score[128];
 252         int len = sizeof(score);
 253         /* For now we want this in sorted order to keep the regression tests happy */
 254         GListPtr gIter = NULL;
 255         GListPtr list = g_hash_table_get_values(hash);
 256 
 257         list = g_list_sort(list, sort_node_uname);
 258 
 259         gIter = list;
 260         for (; gIter != NULL; gIter = gIter->next) {
 261             node_t *node = (node_t *) gIter->data;
 262             /* This function is called a whole lot, use stack allocated score */
 263             score2char_stack(node->weight, score, len);
 264 
 265             if (rsc) {
 266                 printf("%s: %s allocation score on %s: %s\n",
 267                        comment, rsc->id, node->details->uname, score);
 268             } else {
 269                 printf("%s: %s = %s\n", comment, node->details->uname, score);
 270             }
 271         }
 272 
 273         g_list_free(list);
 274 
 275     } else if (hash) {
 276         char score[128];
 277         int len = sizeof(score);
 278         g_hash_table_iter_init(&iter, hash);
 279         while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
 280             /* This function is called a whole lot, use stack allocated score */
 281             score2char_stack(node->weight, score, len);
 282 
 283             if (rsc) {
 284                 do_crm_log_alias(LOG_TRACE, file, function, line,
 285                                  "%s: %s allocation score on %s: %s", comment, rsc->id,
 286                                  node->details->uname, score);
 287             } else {
 288                 do_crm_log_alias(LOG_TRACE, file, function, line + 1, "%s: %s = %s", comment,
 289                                  node->details->uname, score);
 290             }
 291         }
 292     }
 293 
 294     if (rsc && rsc->children) {
 295         GListPtr gIter = NULL;
 296 
 297         gIter = rsc->children;
 298         for (; gIter != NULL; gIter = gIter->next) {
 299             resource_t *child = (resource_t *) gIter->data;
 300 
 301             dump_node_scores_worker(level, file, function, line, child, comment, nodes);
 302         }
 303     }
 304 }
 305 
 306 static void
 307 append_dump_text(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 308 {
 309     char **dump_text = user_data;
 310     int len = 0;
 311     char *new_text = NULL;
 312 
 313     len = strlen(*dump_text) + strlen(" ") + strlen(key) + strlen("=") + strlen(value) + 1;
 314     new_text = calloc(1, len);
 315     sprintf(new_text, "%s %s=%s", *dump_text, (char *)key, (char *)value);
 316 
 317     free(*dump_text);
 318     *dump_text = new_text;
 319 }
 320 
 321 void
 322 dump_node_capacity(int level, const char *comment, node_t * node)
     /* [previous][next][first][last][top][bottom][index][help] */
 323 {
 324     int len = 0;
 325     char *dump_text = NULL;
 326 
 327     len = strlen(comment) + strlen(": ") + strlen(node->details->uname) + strlen(" capacity:") + 1;
 328     dump_text = calloc(1, len);
 329     sprintf(dump_text, "%s: %s capacity:", comment, node->details->uname);
 330 
 331     g_hash_table_foreach(node->details->utilization, append_dump_text, &dump_text);
 332 
 333     if (level == 0) {
 334         fprintf(stdout, "%s\n", dump_text);
 335     } else {
 336         crm_trace("%s", dump_text);
 337     }
 338 
 339     free(dump_text);
 340 }
 341 
 342 void
 343 dump_rsc_utilization(int level, const char *comment, resource_t * rsc, node_t * node)
     /* [previous][next][first][last][top][bottom][index][help] */
 344 {
 345     int len = 0;
 346     char *dump_text = NULL;
 347 
 348     len = strlen(comment) + strlen(": ") + strlen(rsc->id) + strlen(" utilization on ")
 349         + strlen(node->details->uname) + strlen(":") + 1;
 350     dump_text = calloc(1, len);
 351     sprintf(dump_text, "%s: %s utilization on %s:", comment, rsc->id, node->details->uname);
 352 
 353     g_hash_table_foreach(rsc->utilization, append_dump_text, &dump_text);
 354 
 355     if (level == 0) {
 356         fprintf(stdout, "%s\n", dump_text);
 357     } else {
 358         crm_trace("%s", dump_text);
 359     }
 360 
 361     free(dump_text);
 362 }
 363 
 364 gint
 365 sort_rsc_index(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 366 {
 367     const resource_t *resource1 = (const resource_t *)a;
 368     const resource_t *resource2 = (const resource_t *)b;
 369 
 370     if (a == NULL && b == NULL) {
 371         return 0;
 372     }
 373     if (a == NULL) {
 374         return 1;
 375     }
 376     if (b == NULL) {
 377         return -1;
 378     }
 379 
 380     if (resource1->sort_index > resource2->sort_index) {
 381         return -1;
 382     }
 383 
 384     if (resource1->sort_index < resource2->sort_index) {
 385         return 1;
 386     }
 387 
 388     return 0;
 389 }
 390 
 391 gint
 392 sort_rsc_priority(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 393 {
 394     const resource_t *resource1 = (const resource_t *)a;
 395     const resource_t *resource2 = (const resource_t *)b;
 396 
 397     if (a == NULL && b == NULL) {
 398         return 0;
 399     }
 400     if (a == NULL) {
 401         return 1;
 402     }
 403     if (b == NULL) {
 404         return -1;
 405     }
 406 
 407     if (resource1->priority > resource2->priority) {
 408         return -1;
 409     }
 410 
 411     if (resource1->priority < resource2->priority) {
 412         return 1;
 413     }
 414 
 415     return 0;
 416 }
 417 
 418 action_t *
 419 custom_action(resource_t * rsc, char *key, const char *task,
     /* [previous][next][first][last][top][bottom][index][help] */
 420               node_t * on_node, gboolean optional, gboolean save_action,
 421               pe_working_set_t * data_set)
 422 {
 423     action_t *action = NULL;
 424     GListPtr possible_matches = NULL;
 425 
 426     CRM_CHECK(key != NULL, return NULL);
 427     CRM_CHECK(task != NULL, free(key); return NULL);
 428 
 429     if (save_action && rsc != NULL) {
 430         possible_matches = find_actions(rsc->actions, key, on_node);
 431     } else if(save_action) {
 432 #if 0
 433         action = g_hash_table_lookup(data_set->singletons, key);
 434 #else
 435         /* More expensive but takes 'node' into account */
 436         possible_matches = find_actions(data_set->actions, key, on_node);
 437 #endif
 438     }
 439 
 440     if(data_set->singletons == NULL) {
 441         data_set->singletons = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, NULL);
 442     }
 443 
 444     if (possible_matches != NULL) {
 445         if (g_list_length(possible_matches) > 1) {
 446             pe_warn("Action %s for %s on %s exists %d times",
 447                     task, rsc ? rsc->id : "<NULL>",
 448                     on_node ? on_node->details->uname : "<NULL>", g_list_length(possible_matches));
 449         }
 450 
 451         action = g_list_nth_data(possible_matches, 0);
 452         pe_rsc_trace(rsc, "Found existing action (%d) %s for %s on %s",
 453                      action->id, task, rsc ? rsc->id : "<NULL>",
 454                      on_node ? on_node->details->uname : "<NULL>");
 455         g_list_free(possible_matches);
 456     }
 457 
 458     if (action == NULL) {
 459         if (save_action) {
 460             pe_rsc_trace(rsc, "Creating%s action %d: %s for %s on %s %d",
 461                          optional ? "" : " mandatory", data_set->action_id, key,
 462                          rsc ? rsc->id : "<NULL>", on_node ? on_node->details->uname : "<NULL>", optional);
 463         }
 464 
 465         action = calloc(1, sizeof(action_t));
 466         if (save_action) {
 467             action->id = data_set->action_id++;
 468         } else {
 469             action->id = 0;
 470         }
 471         action->rsc = rsc;
 472         CRM_ASSERT(task != NULL);
 473         action->task = strdup(task);
 474         if (on_node) {
 475             action->node = node_copy(on_node);
 476         }
 477         action->uuid = strdup(key);
 478 
 479         pe_set_action_bit(action, pe_action_runnable);
 480         if (optional) {
 481             pe_rsc_trace(rsc, "Set optional on %s", action->uuid);
 482             pe_set_action_bit(action, pe_action_optional);
 483         } else {
 484             pe_clear_action_bit(action, pe_action_optional);
 485             pe_rsc_trace(rsc, "Unset optional on %s", action->uuid);
 486         }
 487 
 488 /*
 489   Implied by calloc()...
 490   action->actions_before   = NULL;
 491   action->actions_after    = NULL;
 492 
 493   action->pseudo     = FALSE;
 494   action->dumped     = FALSE;
 495   action->processed  = FALSE;
 496   action->seen_count = 0;
 497 */
 498 
 499         action->extra = crm_str_table_new();
 500         action->meta = crm_str_table_new();
 501 
 502         if (save_action) {
 503             data_set->actions = g_list_prepend(data_set->actions, action);
 504             if(rsc == NULL) {
 505                 g_hash_table_insert(data_set->singletons, action->uuid, action);
 506             }
 507         }
 508 
 509         if (rsc != NULL) {
 510             action->op_entry = find_rsc_op_entry_helper(rsc, key, TRUE);
 511 
 512             unpack_operation(action, action->op_entry, rsc->container, data_set);
 513 
 514             if (save_action) {
 515                 rsc->actions = g_list_prepend(rsc->actions, action);
 516             }
 517         }
 518 
 519         if (save_action) {
 520             pe_rsc_trace(rsc, "Action %d created", action->id);
 521         }
 522     }
 523 
 524     if (optional == FALSE) {
 525         pe_rsc_trace(rsc, "Unset optional on %s", action->uuid);
 526         pe_clear_action_bit(action, pe_action_optional);
 527     }
 528 
 529     if (rsc != NULL) {
 530         enum action_tasks a_task = text2task(action->task);
 531         int warn_level = LOG_TRACE;
 532 
 533         if (save_action) {
 534             warn_level = LOG_WARNING;
 535         }
 536 
 537         if (is_set(action->flags, pe_action_have_node_attrs) == FALSE
 538             && action->node != NULL && action->op_entry != NULL) {
 539             pe_set_action_bit(action, pe_action_have_node_attrs);
 540             unpack_instance_attributes(data_set->input, action->op_entry, XML_TAG_ATTR_SETS,
 541                                        action->node->details->attrs,
 542                                        action->extra, NULL, FALSE, data_set->now);
 543         }
 544 
 545         if (is_set(action->flags, pe_action_pseudo)) {
 546             /* leave untouched */
 547 
 548         } else if (action->node == NULL) {
 549             pe_rsc_trace(rsc, "Unset runnable on %s", action->uuid);
 550             pe_clear_action_bit(action, pe_action_runnable);
 551 
 552         } else if (is_not_set(rsc->flags, pe_rsc_managed)
 553                    && g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL) == NULL) {
 554             crm_debug("Action %s (unmanaged)", action->uuid);
 555             pe_rsc_trace(rsc, "Set optional on %s", action->uuid);
 556             pe_set_action_bit(action, pe_action_optional);
 557 /*                      action->runnable = FALSE; */
 558 
 559         } else if (action->node->details->online == FALSE
 560                    && (!is_container_remote_node(action->node) || action->node->details->remote_requires_reset)) {
 561             pe_clear_action_bit(action, pe_action_runnable);
 562             do_crm_log(warn_level, "Action %s on %s is unrunnable (offline)",
 563                        action->uuid, action->node->details->uname);
 564             if (is_set(action->rsc->flags, pe_rsc_managed)
 565                 && save_action && a_task == stop_rsc
 566                 && action->node->details->unclean == FALSE) {
 567                 pe_fence_node(data_set, action->node, "resource actions are unrunnable");
 568             }
 569 
 570         } else if (action->node->details->pending) {
 571             pe_clear_action_bit(action, pe_action_runnable);
 572             do_crm_log(warn_level, "Action %s on %s is unrunnable (pending)",
 573                        action->uuid, action->node->details->uname);
 574 
 575         } else if (action->needs == rsc_req_nothing) {
 576             pe_rsc_trace(rsc, "Action %s does not require anything", action->uuid);
 577             pe_action_set_reason(action, NULL, TRUE);
 578             pe_set_action_bit(action, pe_action_runnable);
 579 #if 0
 580             /*
 581              * No point checking this
 582              * - if we don't have quorum we can't stonith anyway
 583              */
 584         } else if (action->needs == rsc_req_stonith) {
 585             crm_trace("Action %s requires only stonith", action->uuid);
 586             action->runnable = TRUE;
 587 #endif
 588         } else if (is_set(data_set->flags, pe_flag_have_quorum) == FALSE
 589                    && data_set->no_quorum_policy == no_quorum_stop) {
 590             pe_action_set_flag_reason(__FUNCTION__, __LINE__, action, NULL, "no quorum", pe_action_runnable, TRUE);
 591             crm_debug("%s\t%s (cancelled : quorum)", action->node->details->uname, action->uuid);
 592 
 593         } else if (is_set(data_set->flags, pe_flag_have_quorum) == FALSE
 594                    && data_set->no_quorum_policy == no_quorum_freeze) {
 595             pe_rsc_trace(rsc, "Check resource is already active: %s %s %s %s", rsc->id, action->uuid, role2text(rsc->next_role), role2text(rsc->role));
 596             if (rsc->fns->active(rsc, TRUE) == FALSE || rsc->next_role > rsc->role) {
 597                 pe_action_set_flag_reason(__FUNCTION__, __LINE__, action, NULL, "quorum freeze", pe_action_runnable, TRUE);
 598                 pe_rsc_debug(rsc, "%s\t%s (cancelled : quorum freeze)",
 599                              action->node->details->uname, action->uuid);
 600             }
 601 
 602         } else if(is_not_set(action->flags, pe_action_runnable)) {
 603             pe_rsc_trace(rsc, "Action %s is runnable", action->uuid);
 604             //pe_action_set_reason(action, NULL, TRUE);
 605             pe_set_action_bit(action, pe_action_runnable);
 606         }
 607 
 608         if (save_action) {
 609             switch (a_task) {
 610                 case stop_rsc:
 611                     set_bit(rsc->flags, pe_rsc_stopping);
 612                     break;
 613                 case start_rsc:
 614                     clear_bit(rsc->flags, pe_rsc_starting);
 615                     if (is_set(action->flags, pe_action_runnable)) {
 616                         set_bit(rsc->flags, pe_rsc_starting);
 617                     }
 618                     break;
 619                 default:
 620                     break;
 621             }
 622         }
 623     }
 624 
 625     free(key);
 626     return action;
 627 }
 628 
 629 static const char *
 630 unpack_operation_on_fail(action_t * action)
     /* [previous][next][first][last][top][bottom][index][help] */
 631 {
 632 
 633     const char *value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ON_FAIL);
 634 
 635     if (safe_str_eq(action->task, CRMD_ACTION_STOP) && safe_str_eq(value, "standby")) {
 636         crm_config_err("on-fail=standby is not allowed for stop actions: %s", action->rsc->id);
 637         return NULL;
 638     } else if (safe_str_eq(action->task, CRMD_ACTION_DEMOTE) && !value) {
 639         /* demote on_fail defaults to master monitor value if present */
 640         xmlNode *operation = NULL;
 641         const char *name = NULL;
 642         const char *role = NULL;
 643         const char *on_fail = NULL;
 644         const char *interval = NULL;
 645         const char *enabled = NULL;
 646 
 647         CRM_CHECK(action->rsc != NULL, return NULL);
 648 
 649         for (operation = __xml_first_child(action->rsc->ops_xml);
 650              operation && !value; operation = __xml_next_element(operation)) {
 651 
 652             if (!crm_str_eq((const char *)operation->name, "op", TRUE)) {
 653                 continue;
 654             }
 655             name = crm_element_value(operation, "name");
 656             role = crm_element_value(operation, "role");
 657             on_fail = crm_element_value(operation, XML_OP_ATTR_ON_FAIL);
 658             enabled = crm_element_value(operation, "enabled");
 659             interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
 660             if (!on_fail) {
 661                 continue;
 662             } else if (enabled && !crm_is_true(enabled)) {
 663                 continue;
 664             } else if (safe_str_neq(name, "monitor") || safe_str_neq(role, "Master")) {
 665                 continue;
 666             } else if (crm_get_interval(interval) <= 0) {
 667                 continue;
 668             }
 669 
 670             value = on_fail;
 671         }
 672     }
 673 
 674     return value;
 675 }
 676 
 677 static xmlNode *
 678 find_min_interval_mon(resource_t * rsc, gboolean include_disabled)
     /* [previous][next][first][last][top][bottom][index][help] */
 679 {
 680     int number = 0;
 681     int min_interval = -1;
 682     const char *name = NULL;
 683     const char *value = NULL;
 684     const char *interval = NULL;
 685     xmlNode *op = NULL;
 686     xmlNode *operation = NULL;
 687 
 688     for (operation = __xml_first_child(rsc->ops_xml); operation != NULL;
 689          operation = __xml_next_element(operation)) {
 690 
 691         if (crm_str_eq((const char *)operation->name, "op", TRUE)) {
 692             name = crm_element_value(operation, "name");
 693             interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
 694             value = crm_element_value(operation, "enabled");
 695             if (!include_disabled && value && crm_is_true(value) == FALSE) {
 696                 continue;
 697             }
 698 
 699             if (safe_str_neq(name, RSC_STATUS)) {
 700                 continue;
 701             }
 702 
 703             number = crm_get_interval(interval);
 704             if (number < 0) {
 705                 continue;
 706             }
 707 
 708             if (min_interval < 0 || number < min_interval) {
 709                 min_interval = number;
 710                 op = operation;
 711             }
 712         }
 713     }
 714 
 715     return op;
 716 }
 717 
 718 static int
 719 unpack_start_delay(const char *value, GHashTable *meta)
     /* [previous][next][first][last][top][bottom][index][help] */
 720 {
 721     int start_delay = 0;
 722 
 723     if (value != NULL) {
 724         start_delay = crm_get_msec(value);
 725 
 726         if (start_delay < 0) {
 727             start_delay = 0;
 728         }
 729 
 730         if (meta) {
 731             g_hash_table_replace(meta, strdup(XML_OP_ATTR_START_DELAY), crm_itoa(start_delay));
 732         }
 733     }
 734 
 735     return start_delay;
 736 }
 737 
 738 static int
 739 unpack_interval_origin(const char *value, GHashTable *meta, xmlNode *xml_obj,
     /* [previous][next][first][last][top][bottom][index][help] */
 740                        unsigned long long interval, crm_time_t *now)
 741 {
 742     int start_delay = 0;
 743 
 744     if (interval > 0 && value) {
 745         crm_time_t *origin = crm_time_new(value);
 746 
 747         if (origin && now) {
 748             crm_time_t *delay = NULL;
 749             int rc = crm_time_compare(origin, now);
 750             long long delay_s = 0;
 751             int interval_s = (interval / 1000);
 752 
 753             crm_trace("Origin: %s, interval: %d", value, interval_s);
 754 
 755             /* If 'origin' is in the future, find the most recent "multiple" that occurred in the past */
 756             while(rc > 0) {
 757                 crm_time_add_seconds(origin, -interval_s);
 758                 rc = crm_time_compare(origin, now);
 759             }
 760 
 761             /* Now find the first "multiple" that occurs after 'now' */
 762             while (rc < 0) {
 763                 crm_time_add_seconds(origin, interval_s);
 764                 rc = crm_time_compare(origin, now);
 765             }
 766 
 767             delay = crm_time_calculate_duration(origin, now);
 768 
 769             crm_time_log(LOG_TRACE, "origin", origin,
 770                          crm_time_log_date | crm_time_log_timeofday |
 771                          crm_time_log_with_timezone);
 772             crm_time_log(LOG_TRACE, "now", now,
 773                          crm_time_log_date | crm_time_log_timeofday |
 774                          crm_time_log_with_timezone);
 775             crm_time_log(LOG_TRACE, "delay", delay, crm_time_log_duration);
 776 
 777             delay_s = crm_time_get_seconds(delay);
 778 
 779             CRM_CHECK(delay_s >= 0, delay_s = 0);
 780             start_delay = delay_s * 1000;
 781 
 782             if (xml_obj) {
 783                 crm_info("Calculated a start delay of %llds for %s", delay_s, ID(xml_obj));
 784             }
 785 
 786             if (meta) {
 787                 g_hash_table_replace(meta, strdup(XML_OP_ATTR_START_DELAY),
 788                                      crm_itoa(start_delay));
 789             }
 790 
 791             crm_time_free(origin);
 792             crm_time_free(delay);
 793         } else if (!origin && xml_obj) {
 794             crm_config_err("Operation %s contained an invalid " XML_OP_ATTR_ORIGIN ": %s",
 795                            ID(xml_obj), value);
 796         }
 797     }
 798 
 799     return start_delay;
 800 }
 801 
 802 static int
 803 unpack_timeout(const char *value, action_t *action, xmlNode *xml_obj,
     /* [previous][next][first][last][top][bottom][index][help] */
 804                unsigned long long interval, GHashTable *config_hash)
 805 {
 806     int timeout = 0;
 807 
 808     if (value == NULL && xml_obj == NULL && action &&
 809         safe_str_eq(action->task, RSC_STATUS) && interval == 0) {
 810 
 811         xmlNode *min_interval_mon = find_min_interval_mon(action->rsc, FALSE);
 812 
 813         if (min_interval_mon) {
 814             value = crm_element_value(min_interval_mon, XML_ATTR_TIMEOUT);
 815             pe_rsc_trace(action->rsc,
 816                          "\t%s uses the timeout value '%s' from the minimum interval monitor",
 817                          action->uuid, value);
 818         }
 819     }
 820 
 821     if (value == NULL && config_hash) {
 822         value = pe_pref(config_hash, "default-action-timeout");
 823         if (value) {
 824             pe_warn_once(pe_wo_default_timeo,
 825                          "Support for 'default-action-timeout' cluster property"
 826                          " is deprecated and will be removed in a future release"
 827                          " (use 'timeout' in op_defaults instead)");
 828 
 829         }
 830     }
 831 
 832     if (value == NULL) {
 833         value = CRM_DEFAULT_OP_TIMEOUT_S;
 834     }
 835 
 836     timeout = crm_get_msec(value);
 837     if (timeout < 0) {
 838         timeout = 0;
 839     }
 840 
 841     return timeout;
 842 }
 843 
 844 int
 845 pe_get_configured_timeout(resource_t *rsc, const char *action, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 846 {
 847     xmlNode *child = NULL;
 848     const char *timeout = NULL;
 849     int timeout_ms = 0;
 850 
 851     for (child = first_named_child(rsc->ops_xml, XML_ATTR_OP);
 852          child != NULL; child = crm_next_same_xml(child)) {
 853         if (safe_str_eq(action, crm_element_value(child, XML_NVPAIR_ATTR_NAME))) {
 854             timeout = crm_element_value(child, XML_ATTR_TIMEOUT);
 855             break;
 856         }
 857     }
 858 
 859     if (timeout == NULL && data_set->op_defaults) {
 860         GHashTable *action_meta = crm_str_table_new();
 861         unpack_instance_attributes(data_set->input, data_set->op_defaults, XML_TAG_META_SETS,
 862                                    NULL, action_meta, NULL, FALSE, data_set->now);
 863         timeout = g_hash_table_lookup(action_meta, XML_ATTR_TIMEOUT);
 864     }
 865 
 866     if (timeout == NULL && data_set->config_hash) {
 867         timeout = pe_pref(data_set->config_hash, "default-action-timeout");
 868     }
 869 
 870     if (timeout == NULL) {
 871         timeout = CRM_DEFAULT_OP_TIMEOUT_S;
 872     }
 873 
 874     timeout_ms = crm_get_msec(timeout);
 875     if (timeout_ms < 0) {
 876         timeout_ms = 0;
 877     }
 878 
 879     return timeout_ms;
 880 }
 881 
 882 #if ENABLE_VERSIONED_ATTRS
 883 static void
 884 unpack_versioned_meta(xmlNode *versioned_meta, xmlNode *xml_obj, unsigned long long interval, crm_time_t *now)
     /* [previous][next][first][last][top][bottom][index][help] */
 885 {
 886     xmlNode *attrs = NULL;
 887     xmlNode *attr = NULL;
 888 
 889     for (attrs = __xml_first_child(versioned_meta); attrs != NULL; attrs = __xml_next_element(attrs)) {
 890         for (attr = __xml_first_child(attrs); attr != NULL; attr = __xml_next_element(attr)) {
 891             const char *name = crm_element_value(attr, XML_NVPAIR_ATTR_NAME);
 892             const char *value = crm_element_value(attr, XML_NVPAIR_ATTR_VALUE);
 893 
 894             if (safe_str_eq(name, XML_OP_ATTR_START_DELAY)) {
 895                 int start_delay = unpack_start_delay(value, NULL);
 896 
 897                 crm_xml_add_int(attr, XML_NVPAIR_ATTR_VALUE, start_delay);
 898             } else if (safe_str_eq(name, XML_OP_ATTR_ORIGIN)) {
 899                 int start_delay = unpack_interval_origin(value, NULL, xml_obj, interval, now);
 900 
 901                 crm_xml_add(attr, XML_NVPAIR_ATTR_NAME, XML_OP_ATTR_START_DELAY);
 902                 crm_xml_add_int(attr, XML_NVPAIR_ATTR_VALUE, start_delay);
 903             } else if (safe_str_eq(name, XML_ATTR_TIMEOUT)) {
 904                 int timeout = unpack_timeout(value, NULL, NULL, 0, NULL);
 905 
 906                 crm_xml_add_int(attr, XML_NVPAIR_ATTR_VALUE, timeout);
 907             }
 908         }
 909     }
 910 }
 911 #endif
 912 
 913 void
 914 unpack_operation(action_t * action, xmlNode * xml_obj, resource_t * container,
     /* [previous][next][first][last][top][bottom][index][help] */
 915                  pe_working_set_t * data_set)
 916 {
 917     unsigned long long interval = 0;
 918     int timeout = 0;
 919     char *value_ms = NULL;
 920     const char *value = NULL;
 921     const char *field = NULL;
 922 #if ENABLE_VERSIONED_ATTRS
 923     pe_rsc_action_details_t *rsc_details = NULL;
 924 #endif
 925 
 926     CRM_CHECK(action->rsc != NULL, return);
 927 
 928     unpack_instance_attributes(data_set->input, data_set->op_defaults, XML_TAG_META_SETS, NULL,
 929                                action->meta, NULL, FALSE, data_set->now);
 930 
 931     if (xml_obj) {
 932         xmlAttrPtr xIter = NULL;
 933 
 934         for (xIter = xml_obj->properties; xIter; xIter = xIter->next) {
 935             const char *prop_name = (const char *)xIter->name;
 936             const char *prop_value = crm_element_value(xml_obj, prop_name);
 937 
 938             g_hash_table_replace(action->meta, strdup(prop_name), strdup(prop_value));
 939         }
 940     }
 941 
 942     unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_META_SETS,
 943                                NULL, action->meta, NULL, FALSE, data_set->now);
 944 
 945     unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_ATTR_SETS,
 946                                NULL, action->meta, NULL, FALSE, data_set->now);
 947 
 948 #if ENABLE_VERSIONED_ATTRS
 949     rsc_details = pe_rsc_action_details(action);
 950     pe_unpack_versioned_attributes(data_set->input, xml_obj, XML_TAG_ATTR_SETS, NULL,
 951                                    rsc_details->versioned_parameters, data_set->now);
 952     pe_unpack_versioned_attributes(data_set->input, xml_obj, XML_TAG_META_SETS, NULL,
 953                                    rsc_details->versioned_meta, data_set->now);
 954 #endif
 955 
 956     g_hash_table_remove(action->meta, "id");
 957 
 958     field = XML_LRM_ATTR_INTERVAL;
 959     value = g_hash_table_lookup(action->meta, field);
 960     if (value != NULL) {
 961         interval = crm_get_interval(value);
 962         if (interval > 0) {
 963             value_ms = crm_itoa(interval);
 964             g_hash_table_replace(action->meta, strdup(field), value_ms);
 965 
 966         } else {
 967             g_hash_table_remove(action->meta, field);
 968         }
 969     }
 970 
 971     /* @COMPAT data sets < 1.1.10 ("requires" on start action not resource) */
 972     value = g_hash_table_lookup(action->meta, "requires");
 973     if (value) {
 974         pe_warn_once(pe_wo_requires, "Support for 'requires' operation meta-attribute"
 975                                      " is deprecated and will be removed in a future version"
 976                                      " (use 'requires' resource meta-attribute instead)");
 977     }
 978 
 979     if (safe_str_neq(action->task, RSC_START)
 980         && safe_str_neq(action->task, RSC_PROMOTE)) {
 981         action->needs = rsc_req_nothing;
 982         value = "nothing (not start/promote)";
 983 
 984     } else if (safe_str_eq(value, "nothing")) {
 985         action->needs = rsc_req_nothing;
 986 
 987     } else if (safe_str_eq(value, "quorum")) {
 988         action->needs = rsc_req_quorum;
 989 
 990     } else if (safe_str_eq(value, "unfencing")) {
 991         action->needs = rsc_req_stonith;
 992         set_bit(action->rsc->flags, pe_rsc_needs_unfencing);
 993         if (is_not_set(data_set->flags, pe_flag_stonith_enabled)) {
 994             crm_notice("%s requires unfencing but fencing is disabled", action->rsc->id);
 995         }
 996 
 997     } else if (is_set(data_set->flags, pe_flag_stonith_enabled)
 998                && safe_str_eq(value, "fencing")) {
 999         action->needs = rsc_req_stonith;
1000         if (is_not_set(data_set->flags, pe_flag_stonith_enabled)) {
1001             crm_notice("%s requires fencing but fencing is disabled", action->rsc->id);
1002         }
1003         /* @COMPAT end compatibility code */
1004 
1005     } else if (is_set(action->rsc->flags, pe_rsc_needs_fencing)) {
1006         action->needs = rsc_req_stonith;
1007         value = "fencing (resource)";
1008 
1009     } else if (is_set(action->rsc->flags, pe_rsc_needs_quorum)) {
1010         action->needs = rsc_req_quorum;
1011         value = "quorum (resource)";
1012 
1013     } else {
1014         action->needs = rsc_req_nothing;
1015         value = "nothing (resource)";
1016     }
1017 
1018     pe_rsc_trace(action->rsc, "\tAction %s requires: %s", action->task, value);
1019 
1020     value = unpack_operation_on_fail(action);
1021 
1022     if (value == NULL) {
1023 
1024     } else if (safe_str_eq(value, "block")) {
1025         action->on_fail = action_fail_block;
1026         g_hash_table_insert(action->meta, strdup(XML_OP_ATTR_ON_FAIL), strdup("block"));
1027 
1028     } else if (safe_str_eq(value, "fence")) {
1029         action->on_fail = action_fail_fence;
1030         value = "node fencing";
1031 
1032         if (is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) {
1033             crm_config_err("Specifying on_fail=fence and" " stonith-enabled=false makes no sense");
1034             action->on_fail = action_fail_stop;
1035             action->fail_role = RSC_ROLE_STOPPED;
1036             value = "stop resource";
1037         }
1038 
1039     } else if (safe_str_eq(value, "standby")) {
1040         action->on_fail = action_fail_standby;
1041         value = "node standby";
1042 
1043     } else if (safe_str_eq(value, "ignore")
1044                || safe_str_eq(value, "nothing")) {
1045         action->on_fail = action_fail_ignore;
1046         value = "ignore";
1047 
1048     } else if (safe_str_eq(value, "migrate")) {
1049         action->on_fail = action_fail_migrate;
1050         value = "force migration";
1051 
1052     } else if (safe_str_eq(value, "stop")) {
1053         action->on_fail = action_fail_stop;
1054         action->fail_role = RSC_ROLE_STOPPED;
1055         value = "stop resource";
1056 
1057     } else if (safe_str_eq(value, "restart")) {
1058         action->on_fail = action_fail_recover;
1059         value = "restart (and possibly migrate)";
1060 
1061     } else if (safe_str_eq(value, "restart-container")) {
1062         if (container) {
1063             action->on_fail = action_fail_restart_container;
1064             value = "restart container (and possibly migrate)";
1065 
1066         } else {
1067             value = NULL;
1068         }
1069 
1070     } else {
1071         pe_err("Resource %s: Unknown failure type (%s)", action->rsc->id, value);
1072         value = NULL;
1073     }
1074 
1075     /* defaults */
1076     if (value == NULL && container) {
1077         action->on_fail = action_fail_restart_container;
1078         value = "restart container (and possibly migrate) (default)";
1079 
1080     /* for baremetal remote nodes, ensure that any failure that results in
1081      * dropping an active connection to a remote node results in fencing of
1082      * the remote node.
1083      *
1084      * There are only two action failures that don't result in fencing.
1085      * 1. probes - probe failures are expected.
1086      * 2. start - a start failure indicates that an active connection does not already
1087      * exist. The user can set op on-fail=fence if they really want to fence start
1088      * failures. */
1089     } else if (((value == NULL) || !is_set(action->rsc->flags, pe_rsc_managed)) &&
1090                 (is_rsc_baremetal_remote_node(action->rsc, data_set) &&
1091                !(safe_str_eq(action->task, CRMD_ACTION_STATUS) && interval == 0) &&
1092                 (safe_str_neq(action->task, CRMD_ACTION_START)))) {
1093 
1094         if (!is_set(action->rsc->flags, pe_rsc_managed)) {
1095             action->on_fail = action_fail_stop;
1096             action->fail_role = RSC_ROLE_STOPPED;
1097             value = "stop unmanaged baremetal remote node (enforcing default)";
1098 
1099         } else {
1100             if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
1101                 value = "fence baremetal remote node (default)";
1102             } else {
1103                 value = "recover baremetal remote node connection (default)";
1104             }
1105 
1106             if (action->rsc->remote_reconnect_interval) {
1107                 action->fail_role = RSC_ROLE_STOPPED;
1108             }
1109             action->on_fail = action_fail_reset_remote;
1110         }
1111 
1112     } else if (value == NULL && safe_str_eq(action->task, CRMD_ACTION_STOP)) {
1113         if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
1114             action->on_fail = action_fail_fence;
1115             value = "resource fence (default)";
1116 
1117         } else {
1118             action->on_fail = action_fail_block;
1119             value = "resource block (default)";
1120         }
1121 
1122     } else if (value == NULL) {
1123         action->on_fail = action_fail_recover;
1124         value = "restart (and possibly migrate) (default)";
1125     }
1126 
1127     pe_rsc_trace(action->rsc, "\t%s failure handling: %s", action->task, value);
1128 
1129     value = NULL;
1130     if (xml_obj != NULL) {
1131         value = g_hash_table_lookup(action->meta, "role_after_failure");
1132     }
1133     if (value != NULL && action->fail_role == RSC_ROLE_UNKNOWN) {
1134         action->fail_role = text2role(value);
1135     }
1136     /* defaults */
1137     if (action->fail_role == RSC_ROLE_UNKNOWN) {
1138         if (safe_str_eq(action->task, CRMD_ACTION_PROMOTE)) {
1139             action->fail_role = RSC_ROLE_SLAVE;
1140         } else {
1141             action->fail_role = RSC_ROLE_STARTED;
1142         }
1143     }
1144     pe_rsc_trace(action->rsc, "\t%s failure results in: %s", action->task,
1145                  role2text(action->fail_role));
1146 
1147     field = XML_OP_ATTR_START_DELAY;
1148     value = g_hash_table_lookup(action->meta, XML_OP_ATTR_START_DELAY);
1149     if (value) {
1150         unpack_start_delay(value, action->meta);
1151     } else {
1152         value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN);
1153         unpack_interval_origin(value, action->meta, xml_obj, interval, data_set->now);
1154     }
1155 
1156     field = XML_ATTR_TIMEOUT;
1157     value = g_hash_table_lookup(action->meta, field);
1158     timeout = unpack_timeout(value, action, xml_obj, interval, data_set->config_hash);
1159     g_hash_table_replace(action->meta, strdup(XML_ATTR_TIMEOUT), crm_itoa(timeout));
1160 
1161 #if ENABLE_VERSIONED_ATTRS
1162     unpack_versioned_meta(rsc_details->versioned_meta, xml_obj, interval,
1163                           data_set->now);
1164 #endif
1165 }
1166 
1167 static xmlNode *
1168 find_rsc_op_entry_helper(resource_t * rsc, const char *key, gboolean include_disabled)
     /* [previous][next][first][last][top][bottom][index][help] */
1169 {
1170     unsigned long long number = 0;
1171     gboolean do_retry = TRUE;
1172     char *local_key = NULL;
1173     const char *name = NULL;
1174     const char *value = NULL;
1175     const char *interval = NULL;
1176     char *match_key = NULL;
1177     xmlNode *op = NULL;
1178     xmlNode *operation = NULL;
1179 
1180   retry:
1181     for (operation = __xml_first_child(rsc->ops_xml); operation != NULL;
1182          operation = __xml_next_element(operation)) {
1183         if (crm_str_eq((const char *)operation->name, "op", TRUE)) {
1184             name = crm_element_value(operation, "name");
1185             interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
1186             value = crm_element_value(operation, "enabled");
1187             if (!include_disabled && value && crm_is_true(value) == FALSE) {
1188                 continue;
1189             }
1190 
1191             number = crm_get_interval(interval);
1192             match_key = generate_op_key(rsc->id, name, number);
1193             if (safe_str_eq(key, match_key)) {
1194                 op = operation;
1195             }
1196             free(match_key);
1197 
1198             if (rsc->clone_name) {
1199                 match_key = generate_op_key(rsc->clone_name, name, number);
1200                 if (safe_str_eq(key, match_key)) {
1201                     op = operation;
1202                 }
1203                 free(match_key);
1204             }
1205 
1206             if (op != NULL) {
1207                 free(local_key);
1208                 return op;
1209             }
1210         }
1211     }
1212 
1213     free(local_key);
1214     if (do_retry == FALSE) {
1215         return NULL;
1216     }
1217 
1218     do_retry = FALSE;
1219     if (strstr(key, CRMD_ACTION_MIGRATE) || strstr(key, CRMD_ACTION_MIGRATED)) {
1220         local_key = generate_op_key(rsc->id, "migrate", 0);
1221         key = local_key;
1222         goto retry;
1223 
1224     } else if (strstr(key, "_notify_")) {
1225         local_key = generate_op_key(rsc->id, "notify", 0);
1226         key = local_key;
1227         goto retry;
1228     }
1229 
1230     return NULL;
1231 }
1232 
1233 xmlNode *
1234 find_rsc_op_entry(resource_t * rsc, const char *key)
     /* [previous][next][first][last][top][bottom][index][help] */
1235 {
1236     return find_rsc_op_entry_helper(rsc, key, FALSE);
1237 }
1238 
1239 void
1240 print_node(const char *pre_text, node_t * node, gboolean details)
     /* [previous][next][first][last][top][bottom][index][help] */
1241 {
1242     if (node == NULL) {
1243         crm_trace("%s%s: <NULL>", pre_text == NULL ? "" : pre_text, pre_text == NULL ? "" : ": ");
1244         return;
1245     }
1246 
1247     CRM_ASSERT(node->details);
1248     crm_trace("%s%s%sNode %s: (weight=%d, fixed=%s)",
1249               pre_text == NULL ? "" : pre_text,
1250               pre_text == NULL ? "" : ": ",
1251               node->details->online ? "" : "Unavailable/Unclean ",
1252               node->details->uname, node->weight, node->fixed ? "True" : "False");
1253 
1254     if (details) {
1255         char *pe_mutable = strdup("\t\t");
1256         GListPtr gIter = node->details->running_rsc;
1257 
1258         crm_trace("\t\t===Node Attributes");
1259         g_hash_table_foreach(node->details->attrs, print_str_str, pe_mutable);
1260         free(pe_mutable);
1261 
1262         crm_trace("\t\t=== Resources");
1263 
1264         for (; gIter != NULL; gIter = gIter->next) {
1265             resource_t *rsc = (resource_t *) gIter->data;
1266 
1267             print_resource(LOG_DEBUG_4, "\t\t", rsc, FALSE);
1268         }
1269     }
1270 }
1271 
1272 /*
1273  * Used by the HashTable for-loop
1274  */
1275 void
1276 print_str_str(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1277 {
1278     crm_trace("%s%s %s ==> %s",
1279               user_data == NULL ? "" : (char *)user_data,
1280               user_data == NULL ? "" : ": ", (char *)key, (char *)value);
1281 }
1282 
1283 void
1284 print_resource(int log_level, const char *pre_text, resource_t * rsc, gboolean details)
     /* [previous][next][first][last][top][bottom][index][help] */
1285 {
1286     long options = pe_print_log | pe_print_pending;
1287 
1288     if (rsc == NULL) {
1289         do_crm_log(log_level - 1, "%s%s: <NULL>",
1290                    pre_text == NULL ? "" : pre_text, pre_text == NULL ? "" : ": ");
1291         return;
1292     }
1293     if (details) {
1294         options |= pe_print_details;
1295     }
1296     rsc->fns->print(rsc, pre_text, options, &log_level);
1297 }
1298 
1299 void
1300 pe_free_action(action_t * action)
     /* [previous][next][first][last][top][bottom][index][help] */
1301 {
1302     if (action == NULL) {
1303         return;
1304     }
1305     g_list_free_full(action->actions_before, free);     /* action_wrapper_t* */
1306     g_list_free_full(action->actions_after, free);      /* action_wrapper_t* */
1307     if (action->extra) {
1308         g_hash_table_destroy(action->extra);
1309     }
1310     if (action->meta) {
1311         g_hash_table_destroy(action->meta);
1312     }
1313 #if ENABLE_VERSIONED_ATTRS
1314     if (action->rsc) {
1315         pe_free_rsc_action_details(action);
1316     }
1317 #endif
1318     free(action->cancel_task);
1319     free(action->reason);
1320     free(action->task);
1321     free(action->uuid);
1322     free(action->node);
1323     free(action);
1324 }
1325 
1326 GListPtr
1327 find_recurring_actions(GListPtr input, node_t * not_on_node)
     /* [previous][next][first][last][top][bottom][index][help] */
1328 {
1329     const char *value = NULL;
1330     GListPtr result = NULL;
1331     GListPtr gIter = input;
1332 
1333     CRM_CHECK(input != NULL, return NULL);
1334 
1335     for (; gIter != NULL; gIter = gIter->next) {
1336         action_t *action = (action_t *) gIter->data;
1337 
1338         value = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL);
1339         if (value == NULL) {
1340             /* skip */
1341         } else if (safe_str_eq(value, "0")) {
1342             /* skip */
1343         } else if (safe_str_eq(CRMD_ACTION_CANCEL, action->task)) {
1344             /* skip */
1345         } else if (not_on_node == NULL) {
1346             crm_trace("(null) Found: %s", action->uuid);
1347             result = g_list_prepend(result, action);
1348 
1349         } else if (action->node == NULL) {
1350             /* skip */
1351         } else if (action->node->details != not_on_node->details) {
1352             crm_trace("Found: %s", action->uuid);
1353             result = g_list_prepend(result, action);
1354         }
1355     }
1356 
1357     return result;
1358 }
1359 
1360 enum action_tasks
1361 get_complex_task(resource_t * rsc, const char *name, gboolean allow_non_atomic)
     /* [previous][next][first][last][top][bottom][index][help] */
1362 {
1363     enum action_tasks task = text2task(name);
1364 
1365     if (rsc == NULL) {
1366         return task;
1367 
1368     } else if (allow_non_atomic == FALSE || rsc->variant == pe_native) {
1369         switch (task) {
1370             case stopped_rsc:
1371             case started_rsc:
1372             case action_demoted:
1373             case action_promoted:
1374                 crm_trace("Folding %s back into its atomic counterpart for %s", name, rsc->id);
1375                 return task - 1;
1376                 break;
1377             default:
1378                 break;
1379         }
1380     }
1381     return task;
1382 }
1383 
1384 action_t *
1385 find_first_action(GListPtr input, const char *uuid, const char *task, node_t * on_node)
     /* [previous][next][first][last][top][bottom][index][help] */
1386 {
1387     GListPtr gIter = NULL;
1388 
1389     CRM_CHECK(uuid || task, return NULL);
1390 
1391     for (gIter = input; gIter != NULL; gIter = gIter->next) {
1392         action_t *action = (action_t *) gIter->data;
1393 
1394         if (uuid != NULL && safe_str_neq(uuid, action->uuid)) {
1395             continue;
1396 
1397         } else if (task != NULL && safe_str_neq(task, action->task)) {
1398             continue;
1399 
1400         } else if (on_node == NULL) {
1401             return action;
1402 
1403         } else if (action->node == NULL) {
1404             continue;
1405 
1406         } else if (on_node->details == action->node->details) {
1407             return action;
1408         }
1409     }
1410 
1411     return NULL;
1412 }
1413 
1414 GListPtr
1415 find_actions(GListPtr input, const char *key, const node_t *on_node)
     /* [previous][next][first][last][top][bottom][index][help] */
1416 {
1417     GListPtr gIter = input;
1418     GListPtr result = NULL;
1419 
1420     CRM_CHECK(key != NULL, return NULL);
1421 
1422     for (; gIter != NULL; gIter = gIter->next) {
1423         action_t *action = (action_t *) gIter->data;
1424 
1425         if (safe_str_neq(key, action->uuid)) {
1426             crm_trace("%s does not match action %s", key, action->uuid);
1427             continue;
1428 
1429         } else if (on_node == NULL) {
1430             crm_trace("Action %s matches (ignoring node)", key);
1431             result = g_list_prepend(result, action);
1432 
1433         } else if (action->node == NULL) {
1434             crm_trace("Action %s matches (unallocated, assigning to %s)",
1435                       key, on_node->details->uname);
1436 
1437             action->node = node_copy(on_node);
1438             result = g_list_prepend(result, action);
1439 
1440         } else if (on_node->details == action->node->details) {
1441             crm_trace("Action %s on %s matches", key, on_node->details->uname);
1442             result = g_list_prepend(result, action);
1443 
1444         } else {
1445             crm_trace("Action %s on node %s does not match requested node %s",
1446                       key, action->node->details->uname,
1447                       on_node->details->uname);
1448         }
1449     }
1450 
1451     return result;
1452 }
1453 
1454 GListPtr
1455 find_actions_exact(GListPtr input, const char *key, node_t * on_node)
     /* [previous][next][first][last][top][bottom][index][help] */
1456 {
1457     GListPtr gIter = input;
1458     GListPtr result = NULL;
1459 
1460     CRM_CHECK(key != NULL, return NULL);
1461 
1462     for (; gIter != NULL; gIter = gIter->next) {
1463         action_t *action = (action_t *) gIter->data;
1464 
1465         crm_trace("Matching %s against %s", key, action->uuid);
1466         if (safe_str_neq(key, action->uuid)) {
1467             crm_trace("Key mismatch: %s vs. %s", key, action->uuid);
1468             continue;
1469 
1470         } else if (on_node == NULL || action->node == NULL) {
1471             crm_trace("on_node=%p, action->node=%p", on_node, action->node);
1472             continue;
1473 
1474         } else if (safe_str_eq(on_node->details->id, action->node->details->id)) {
1475             result = g_list_prepend(result, action);
1476         }
1477         crm_trace("Node mismatch: %s vs. %s", on_node->details->id, action->node->details->id);
1478     }
1479 
1480     return result;
1481 }
1482 
1483 static void
1484 resource_node_score(resource_t * rsc, node_t * node, int score, const char *tag)
     /* [previous][next][first][last][top][bottom][index][help] */
1485 {
1486     node_t *match = NULL;
1487 
1488     if ((rsc->exclusive_discover || (node->rsc_discover_mode == pe_discover_never))
1489         && safe_str_eq(tag, "symmetric_default")) {
1490         /* This string comparision may be fragile, but exclusive resources and
1491          * exclusive nodes should not have the symmetric_default constraint
1492          * applied to them.
1493          */
1494         return;
1495 
1496     } else if (rsc->children) {
1497         GListPtr gIter = rsc->children;
1498 
1499         for (; gIter != NULL; gIter = gIter->next) {
1500             resource_t *child_rsc = (resource_t *) gIter->data;
1501 
1502             resource_node_score(child_rsc, node, score, tag);
1503         }
1504     }
1505 
1506     pe_rsc_trace(rsc, "Setting %s for %s on %s: %d", tag, rsc->id, node->details->uname, score);
1507     match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id);
1508     if (match == NULL) {
1509         match = node_copy(node);
1510         g_hash_table_insert(rsc->allowed_nodes, (gpointer) match->details->id, match);
1511     }
1512     match->weight = merge_weights(match->weight, score);
1513 }
1514 
1515 void
1516 resource_location(resource_t * rsc, node_t * node, int score, const char *tag,
     /* [previous][next][first][last][top][bottom][index][help] */
1517                   pe_working_set_t * data_set)
1518 {
1519     if (node != NULL) {
1520         resource_node_score(rsc, node, score, tag);
1521 
1522     } else if (data_set != NULL) {
1523         GListPtr gIter = data_set->nodes;
1524 
1525         for (; gIter != NULL; gIter = gIter->next) {
1526             node_t *node_iter = (node_t *) gIter->data;
1527 
1528             resource_node_score(rsc, node_iter, score, tag);
1529         }
1530 
1531     } else {
1532         GHashTableIter iter;
1533         node_t *node_iter = NULL;
1534 
1535         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1536         while (g_hash_table_iter_next(&iter, NULL, (void **)&node_iter)) {
1537             resource_node_score(rsc, node_iter, score, tag);
1538         }
1539     }
1540 
1541     if (node == NULL && score == -INFINITY) {
1542         if (rsc->allocated_to) {
1543             crm_info("Deallocating %s from %s", rsc->id, rsc->allocated_to->details->uname);
1544             free(rsc->allocated_to);
1545             rsc->allocated_to = NULL;
1546         }
1547     }
1548 }
1549 
1550 #define sort_return(an_int, why) do {                                   \
1551         free(a_uuid);                                           \
1552         free(b_uuid);                                           \
1553         crm_trace("%s (%d) %c %s (%d) : %s",                            \
1554                   a_xml_id, a_call_id, an_int>0?'>':an_int<0?'<':'=',   \
1555                   b_xml_id, b_call_id, why);                            \
1556         return an_int;                                                  \
1557     } while(0)
1558 
1559 gint
1560 sort_op_by_callid(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
1561 {
1562     int a_call_id = -1;
1563     int b_call_id = -1;
1564 
1565     char *a_uuid = NULL;
1566     char *b_uuid = NULL;
1567 
1568     const xmlNode *xml_a = a;
1569     const xmlNode *xml_b = b;
1570 
1571     const char *a_xml_id = crm_element_value_const(xml_a, XML_ATTR_ID);
1572     const char *b_xml_id = crm_element_value_const(xml_b, XML_ATTR_ID);
1573 
1574     if (safe_str_eq(a_xml_id, b_xml_id)) {
1575         /* We have duplicate lrm_rsc_op entries in the status
1576          *    section which is unliklely to be a good thing
1577          *    - we can handle it easily enough, but we need to get
1578          *    to the bottom of why it's happening.
1579          */
1580         pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id);
1581         sort_return(0, "duplicate");
1582     }
1583 
1584     crm_element_value_const_int(xml_a, XML_LRM_ATTR_CALLID, &a_call_id);
1585     crm_element_value_const_int(xml_b, XML_LRM_ATTR_CALLID, &b_call_id);
1586 
1587     if (a_call_id == -1 && b_call_id == -1) {
1588         /* both are pending ops so it doesn't matter since
1589          *   stops are never pending
1590          */
1591         sort_return(0, "pending");
1592 
1593     } else if (a_call_id >= 0 && a_call_id < b_call_id) {
1594         sort_return(-1, "call id");
1595 
1596     } else if (b_call_id >= 0 && a_call_id > b_call_id) {
1597         sort_return(1, "call id");
1598 
1599     } else if (b_call_id >= 0 && a_call_id == b_call_id) {
1600         /*
1601          * The op and last_failed_op are the same
1602          * Order on last-rc-change
1603          */
1604         int last_a = -1;
1605         int last_b = -1;
1606 
1607         crm_element_value_const_int(xml_a, XML_RSC_OP_LAST_CHANGE, &last_a);
1608         crm_element_value_const_int(xml_b, XML_RSC_OP_LAST_CHANGE, &last_b);
1609 
1610         crm_trace("rc-change: %d vs %d", last_a, last_b);
1611         if (last_a >= 0 && last_a < last_b) {
1612             sort_return(-1, "rc-change");
1613 
1614         } else if (last_b >= 0 && last_a > last_b) {
1615             sort_return(1, "rc-change");
1616         }
1617         sort_return(0, "rc-change");
1618 
1619     } else {
1620         /* One of the inputs is a pending operation
1621          * Attempt to use XML_ATTR_TRANSITION_MAGIC to determine its age relative to the other
1622          */
1623 
1624         int a_id = -1;
1625         int b_id = -1;
1626         int dummy = -1;
1627 
1628         const char *a_magic = crm_element_value_const(xml_a, XML_ATTR_TRANSITION_MAGIC);
1629         const char *b_magic = crm_element_value_const(xml_b, XML_ATTR_TRANSITION_MAGIC);
1630 
1631         CRM_CHECK(a_magic != NULL && b_magic != NULL, sort_return(0, "No magic"));
1632         if(!decode_transition_magic(a_magic, &a_uuid, &a_id, &dummy, &dummy, &dummy, &dummy)) {
1633             sort_return(0, "bad magic a");
1634         }
1635         if(!decode_transition_magic(b_magic, &b_uuid, &b_id, &dummy, &dummy, &dummy, &dummy)) {
1636             sort_return(0, "bad magic b");
1637         }
1638         /* try to determine the relative age of the operation...
1639          * some pending operations (e.g. a start) may have been superseded
1640          *   by a subsequent stop
1641          *
1642          * [a|b]_id == -1 means it's a shutdown operation and _always_ comes last
1643          */
1644         if (safe_str_neq(a_uuid, b_uuid) || a_id == b_id) {
1645             /*
1646              * some of the logic in here may be redundant...
1647              *
1648              * if the UUID from the TE doesn't match then one better
1649              *   be a pending operation.
1650              * pending operations don't survive between elections and joins
1651              *   because we query the LRM directly
1652              */
1653 
1654             if (b_call_id == -1) {
1655                 sort_return(-1, "transition + call");
1656 
1657             } else if (a_call_id == -1) {
1658                 sort_return(1, "transition + call");
1659             }
1660 
1661         } else if ((a_id >= 0 && a_id < b_id) || b_id == -1) {
1662             sort_return(-1, "transition");
1663 
1664         } else if ((b_id >= 0 && a_id > b_id) || a_id == -1) {
1665             sort_return(1, "transition");
1666         }
1667     }
1668 
1669     /* we should never end up here */
1670     CRM_CHECK(FALSE, sort_return(0, "default"));
1671 
1672 }
1673 
1674 time_t
1675 get_effective_time(pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
1676 {
1677     if(data_set) {
1678         if (data_set->now == NULL) {
1679             crm_trace("Recording a new 'now'");
1680             data_set->now = crm_time_new(NULL);
1681         }
1682         return crm_time_get_seconds_since_epoch(data_set->now);
1683     }
1684 
1685     crm_trace("Defaulting to 'now'");
1686     return time(NULL);
1687 }
1688 
1689 gboolean
1690 get_target_role(resource_t * rsc, enum rsc_role_e * role)
     /* [previous][next][first][last][top][bottom][index][help] */
1691 {
1692     enum rsc_role_e local_role = RSC_ROLE_UNKNOWN;
1693     const char *value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
1694 
1695     CRM_CHECK(role != NULL, return FALSE);
1696 
1697     if (value == NULL || safe_str_eq("started", value)
1698         || safe_str_eq("default", value)) {
1699         return FALSE;
1700     }
1701 
1702     local_role = text2role(value);
1703     if (local_role == RSC_ROLE_UNKNOWN) {
1704         crm_config_err("%s: Unknown value for %s: %s", rsc->id, XML_RSC_ATTR_TARGET_ROLE, value);
1705         return FALSE;
1706 
1707     } else if (local_role > RSC_ROLE_STARTED) {
1708         if (uber_parent(rsc)->variant == pe_master) {
1709             if (local_role > RSC_ROLE_SLAVE) {
1710                 /* This is what we'd do anyway, just leave the default to avoid messing up the placement algorithm */
1711                 return FALSE;
1712             }
1713 
1714         } else {
1715             crm_config_err("%s is not part of a master/slave resource, a %s of '%s' makes no sense",
1716                            rsc->id, XML_RSC_ATTR_TARGET_ROLE, value);
1717             return FALSE;
1718         }
1719     }
1720 
1721     *role = local_role;
1722     return TRUE;
1723 }
1724 
1725 gboolean
1726 order_actions(action_t * lh_action, action_t * rh_action, enum pe_ordering order)
     /* [previous][next][first][last][top][bottom][index][help] */
1727 {
1728     GListPtr gIter = NULL;
1729     action_wrapper_t *wrapper = NULL;
1730     GListPtr list = NULL;
1731 
1732     if (order == pe_order_none) {
1733         return FALSE;
1734     }
1735 
1736     if (lh_action == NULL || rh_action == NULL) {
1737         return FALSE;
1738     }
1739 
1740     crm_trace("Ordering Action %s before %s", lh_action->uuid, rh_action->uuid);
1741 
1742     /* Ensure we never create a dependency on ourselves... it's happened */
1743     CRM_ASSERT(lh_action != rh_action);
1744 
1745     /* Filter dups, otherwise update_action_states() has too much work to do */
1746     gIter = lh_action->actions_after;
1747     for (; gIter != NULL; gIter = gIter->next) {
1748         action_wrapper_t *after = (action_wrapper_t *) gIter->data;
1749 
1750         if (after->action == rh_action && (after->type & order)) {
1751             return FALSE;
1752         }
1753     }
1754 
1755     wrapper = calloc(1, sizeof(action_wrapper_t));
1756     wrapper->action = rh_action;
1757     wrapper->type = order;
1758 
1759     list = lh_action->actions_after;
1760     list = g_list_prepend(list, wrapper);
1761     lh_action->actions_after = list;
1762 
1763     wrapper = NULL;
1764 
1765 /*      order |= pe_order_implies_then; */
1766 /*      order ^= pe_order_implies_then; */
1767 
1768     wrapper = calloc(1, sizeof(action_wrapper_t));
1769     wrapper->action = lh_action;
1770     wrapper->type = order;
1771     list = rh_action->actions_before;
1772     list = g_list_prepend(list, wrapper);
1773     rh_action->actions_before = list;
1774     return TRUE;
1775 }
1776 
1777 action_t *
1778 get_pseudo_op(const char *name, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
1779 {
1780     action_t *op = NULL;
1781 
1782     if(data_set->singletons) {
1783         op = g_hash_table_lookup(data_set->singletons, name);
1784     }
1785     if (op == NULL) {
1786         op = custom_action(NULL, strdup(name), name, NULL, TRUE, TRUE, data_set);
1787         set_bit(op->flags, pe_action_pseudo);
1788         set_bit(op->flags, pe_action_runnable);
1789     }
1790 
1791     return op;
1792 }
1793 
1794 void
1795 destroy_ticket(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
1796 {
1797     ticket_t *ticket = data;
1798 
1799     if (ticket->state) {
1800         g_hash_table_destroy(ticket->state);
1801     }
1802     free(ticket->id);
1803     free(ticket);
1804 }
1805 
1806 ticket_t *
1807 ticket_new(const char *ticket_id, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
1808 {
1809     ticket_t *ticket = NULL;
1810 
1811     if (ticket_id == NULL || strlen(ticket_id) == 0) {
1812         return NULL;
1813     }
1814 
1815     if (data_set->tickets == NULL) {
1816         data_set->tickets =
1817             g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, destroy_ticket);
1818     }
1819 
1820     ticket = g_hash_table_lookup(data_set->tickets, ticket_id);
1821     if (ticket == NULL) {
1822 
1823         ticket = calloc(1, sizeof(ticket_t));
1824         if (ticket == NULL) {
1825             crm_err("Cannot allocate ticket '%s'", ticket_id);
1826             return NULL;
1827         }
1828 
1829         crm_trace("Creaing ticket entry for %s", ticket_id);
1830 
1831         ticket->id = strdup(ticket_id);
1832         ticket->granted = FALSE;
1833         ticket->last_granted = -1;
1834         ticket->standby = FALSE;
1835         ticket->state = crm_str_table_new();
1836 
1837         g_hash_table_insert(data_set->tickets, strdup(ticket->id), ticket);
1838     }
1839 
1840     return ticket;
1841 }
1842 
1843 static void
1844 filter_parameters(xmlNode * param_set, const char *param_string, bool need_present)
     /* [previous][next][first][last][top][bottom][index][help] */
1845 {
1846     if (param_set && param_string) {
1847         xmlAttrPtr xIter = param_set->properties;
1848 
1849         while (xIter) {
1850             const char *prop_name = (const char *)xIter->name;
1851             char *name = crm_strdup_printf(" %s ", prop_name);
1852             char *match = strstr(param_string, name);
1853 
1854             free(name);
1855 
1856             //  Do now, because current entry might get removed below
1857             xIter = xIter->next;
1858 
1859             if (need_present && match == NULL) {
1860                 crm_trace("%s not found in %s", prop_name, param_string);
1861                 xml_remove_prop(param_set, prop_name);
1862 
1863             } else if (need_present == FALSE && match) {
1864                 crm_trace("%s found in %s", prop_name, param_string);
1865                 xml_remove_prop(param_set, prop_name);
1866             }
1867         }
1868     }
1869 }
1870 
1871 #if ENABLE_VERSIONED_ATTRS
1872 static void
1873 append_versioned_params(xmlNode *versioned_params, const char *ra_version, xmlNode *params)
     /* [previous][next][first][last][top][bottom][index][help] */
1874 {
1875     GHashTable *hash = pe_unpack_versioned_parameters(versioned_params, ra_version);
1876     char *key = NULL;
1877     char *value = NULL;
1878     GHashTableIter iter;
1879 
1880     g_hash_table_iter_init(&iter, hash);
1881     while (g_hash_table_iter_next(&iter, (gpointer *) &key, (gpointer *) &value)) {
1882         crm_xml_add(params, key, value);
1883     }
1884     g_hash_table_destroy(hash);
1885 }
1886 #endif
1887 
1888 static op_digest_cache_t *
1889 rsc_action_digest(resource_t * rsc, const char *task, const char *key,
     /* [previous][next][first][last][top][bottom][index][help] */
1890                   node_t * node, xmlNode * xml_op, pe_working_set_t * data_set) 
1891 {
1892     op_digest_cache_t *data = NULL;
1893 
1894     data = g_hash_table_lookup(node->details->digest_cache, key);
1895     if (data == NULL) {
1896         GHashTable *local_rsc_params = crm_str_table_new();
1897         action_t *action = custom_action(rsc, strdup(key), task, node, TRUE, FALSE, data_set);
1898 #if ENABLE_VERSIONED_ATTRS
1899         xmlNode *local_versioned_params = create_xml_node(NULL, XML_TAG_RSC_VER_ATTRS);
1900         const char *ra_version = NULL;
1901 #endif
1902 
1903         const char *op_version;
1904         const char *restart_list = NULL;
1905         const char *secure_list = " passwd password ";
1906 
1907         data = calloc(1, sizeof(op_digest_cache_t));
1908         CRM_ASSERT(data != NULL);
1909 
1910         get_rsc_attributes(local_rsc_params, rsc, node, data_set);
1911 #if ENABLE_VERSIONED_ATTRS
1912         pe_get_versioned_attributes(local_versioned_params, rsc, node, data_set);
1913 #endif
1914 
1915         data->params_all = create_xml_node(NULL, XML_TAG_PARAMS);
1916 
1917         // REMOTE_CONTAINER_HACK: Allow remote nodes that start containers with pacemaker remote inside
1918         if (container_fix_remote_addr_in(rsc, data->params_all, "addr")) {
1919             crm_trace("Fixed addr for %s on %s", rsc->id, node->details->uname);
1920         }
1921 
1922         g_hash_table_foreach(local_rsc_params, hash2field, data->params_all);
1923         g_hash_table_foreach(action->extra, hash2field, data->params_all);
1924         g_hash_table_foreach(rsc->parameters, hash2field, data->params_all);
1925         g_hash_table_foreach(action->meta, hash2metafield, data->params_all);
1926 
1927         if(xml_op) {
1928             secure_list = crm_element_value(xml_op, XML_LRM_ATTR_OP_SECURE);
1929             restart_list = crm_element_value(xml_op, XML_LRM_ATTR_OP_RESTART);
1930 
1931             op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION);
1932 #if ENABLE_VERSIONED_ATTRS
1933             ra_version = crm_element_value(xml_op, XML_ATTR_RA_VERSION);
1934 #endif
1935 
1936         } else {
1937             op_version = CRM_FEATURE_SET;
1938         }
1939 
1940 #if ENABLE_VERSIONED_ATTRS
1941         append_versioned_params(local_versioned_params, ra_version, data->params_all);
1942         append_versioned_params(rsc->versioned_parameters, ra_version, data->params_all);
1943 
1944         {
1945             pe_rsc_action_details_t *details = pe_rsc_action_details(action);
1946             append_versioned_params(details->versioned_parameters, ra_version, data->params_all);
1947         }
1948 #endif
1949 
1950         filter_action_parameters(data->params_all, op_version);
1951 
1952         g_hash_table_destroy(local_rsc_params);
1953         pe_free_action(action);
1954 
1955         data->digest_all_calc = calculate_operation_digest(data->params_all, op_version);
1956 
1957         if (is_set(data_set->flags, pe_flag_sanitized)) {
1958             data->params_secure = copy_xml(data->params_all);
1959             if(secure_list) {
1960                 filter_parameters(data->params_secure, secure_list, FALSE);
1961             }
1962             data->digest_secure_calc = calculate_operation_digest(data->params_secure, op_version);
1963         }
1964 
1965         if(xml_op && crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST) != NULL) {
1966             data->params_restart = copy_xml(data->params_all);
1967             if (restart_list) {
1968                 filter_parameters(data->params_restart, restart_list, TRUE);
1969             }
1970             data->digest_restart_calc = calculate_operation_digest(data->params_restart, op_version);
1971         }
1972 
1973         g_hash_table_insert(node->details->digest_cache, strdup(key), data);
1974     }
1975 
1976     return data;
1977 }
1978 
1979 op_digest_cache_t *
1980 rsc_action_digest_cmp(resource_t * rsc, xmlNode * xml_op, node_t * node,
     /* [previous][next][first][last][top][bottom][index][help] */
1981                       pe_working_set_t * data_set)
1982 {
1983     op_digest_cache_t *data = NULL;
1984 
1985     char *key = NULL;
1986     int interval = 0;
1987 
1988     const char *op_version;
1989     const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
1990     const char *interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL);
1991 
1992     const char *digest_all;
1993     const char *digest_restart;
1994 
1995     CRM_ASSERT(node != NULL);
1996 
1997     op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION);
1998     digest_all = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST);
1999     digest_restart = crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST);
2000 
2001     interval = crm_parse_int(interval_s, "0");
2002     key = generate_op_key(rsc->id, task, interval);
2003     data = rsc_action_digest(rsc, task, key, node, xml_op, data_set);
2004 
2005     data->rc = RSC_DIGEST_MATCH;
2006     if (digest_restart && data->digest_restart_calc && strcmp(data->digest_restart_calc, digest_restart) != 0) {
2007         pe_rsc_info(rsc, "Parameters to %s on %s changed: was %s vs. now %s (restart:%s) %s",
2008                  key, node->details->uname,
2009                  crm_str(digest_restart), data->digest_restart_calc,
2010                  op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC));
2011         data->rc = RSC_DIGEST_RESTART;
2012 
2013     } else if (digest_all == NULL) {
2014         /* it is unknown what the previous op digest was */
2015         data->rc = RSC_DIGEST_UNKNOWN;
2016 
2017     } else if (strcmp(digest_all, data->digest_all_calc) != 0) {
2018         pe_rsc_info(rsc, "Parameters to %s on %s changed: was %s vs. now %s (%s:%s) %s",
2019                  key, node->details->uname,
2020                  crm_str(digest_all), data->digest_all_calc,
2021                  (interval > 0)? "reschedule" : "reload",
2022                  op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC));
2023         data->rc = RSC_DIGEST_ALL;
2024     }
2025 
2026     free(key);
2027     return data;
2028 }
2029 
2030 #define STONITH_DIGEST_TASK "stonith-on"
2031 
2032 static op_digest_cache_t *
2033 fencing_action_digest_cmp(resource_t * rsc, node_t * node, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
2034 {
2035     char *key = generate_op_key(rsc->id, STONITH_DIGEST_TASK, 0);
2036     op_digest_cache_t *data = rsc_action_digest(rsc, STONITH_DIGEST_TASK, key, node, NULL, data_set);
2037 
2038     const char *digest_all = pe_node_attribute_raw(node, CRM_ATTR_DIGESTS_ALL);
2039     const char *digest_secure = pe_node_attribute_raw(node, CRM_ATTR_DIGESTS_SECURE);
2040 
2041     /* No 'reloads' for fencing device changes
2042      *
2043      * We use the resource id + agent + digest so that we can detect
2044      * changes to the agent and/or the parameters used
2045      */
2046     char *search_all = crm_strdup_printf("%s:%s:%s", rsc->id, (const char*)g_hash_table_lookup(rsc->meta, XML_ATTR_TYPE), data->digest_all_calc);
2047     char *search_secure = crm_strdup_printf("%s:%s:%s", rsc->id, (const char*)g_hash_table_lookup(rsc->meta, XML_ATTR_TYPE), data->digest_secure_calc);
2048 
2049     data->rc = RSC_DIGEST_ALL;
2050     if (digest_all == NULL) {
2051         /* it is unknown what the previous op digest was */
2052         data->rc = RSC_DIGEST_UNKNOWN;
2053 
2054     } else if (strstr(digest_all, search_all)) {
2055         data->rc = RSC_DIGEST_MATCH;
2056 
2057     } else if(digest_secure && data->digest_secure_calc) {
2058         if(strstr(digest_secure, search_secure)) {
2059             if (is_set(data_set->flags, pe_flag_sanitized)) {
2060                 printf("Only 'private' parameters to %s for unfencing %s changed\n",
2061                        rsc->id, node->details->uname);
2062             }
2063             data->rc = RSC_DIGEST_MATCH;
2064         }
2065     }
2066 
2067     if (data->rc == RSC_DIGEST_ALL && is_set(data_set->flags, pe_flag_sanitized) && data->digest_secure_calc) {
2068         if (is_set(data_set->flags, pe_flag_sanitized)) {
2069             printf("Parameters to %s for unfencing %s changed, try '%s:%s:%s'\n",
2070                    rsc->id, node->details->uname, rsc->id,
2071                    (const char *) g_hash_table_lookup(rsc->meta, XML_ATTR_TYPE),
2072                    data->digest_secure_calc);
2073         }
2074     }
2075 
2076     free(key);
2077     free(search_all);
2078     free(search_secure);
2079 
2080     return data;
2081 }
2082 
2083 const char *rsc_printable_id(resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
2084 {
2085     if (is_not_set(rsc->flags, pe_rsc_unique)) {
2086         return ID(rsc->xml);
2087     }
2088     return rsc->id;
2089 }
2090 
2091 void
2092 clear_bit_recursive(resource_t * rsc, unsigned long long flag)
     /* [previous][next][first][last][top][bottom][index][help] */
2093 {
2094     GListPtr gIter = rsc->children;
2095 
2096     clear_bit(rsc->flags, flag);
2097     for (; gIter != NULL; gIter = gIter->next) {
2098         resource_t *child_rsc = (resource_t *) gIter->data;
2099 
2100         clear_bit_recursive(child_rsc, flag);
2101     }
2102 }
2103 
2104 void
2105 set_bit_recursive(resource_t * rsc, unsigned long long flag)
     /* [previous][next][first][last][top][bottom][index][help] */
2106 {
2107     GListPtr gIter = rsc->children;
2108 
2109     set_bit(rsc->flags, flag);
2110     for (; gIter != NULL; gIter = gIter->next) {
2111         resource_t *child_rsc = (resource_t *) gIter->data;
2112 
2113         set_bit_recursive(child_rsc, flag);
2114     }
2115 }
2116 
2117 static GListPtr
2118 find_unfencing_devices(GListPtr candidates, GListPtr matches) 
     /* [previous][next][first][last][top][bottom][index][help] */
2119 {
2120     for (GListPtr gIter = candidates; gIter != NULL; gIter = gIter->next) {
2121         resource_t *candidate = gIter->data;
2122         const char *provides = g_hash_table_lookup(candidate->meta, XML_RSC_ATTR_PROVIDES);
2123         const char *requires = g_hash_table_lookup(candidate->meta, XML_RSC_ATTR_REQUIRES);
2124 
2125         if(candidate->children) {
2126             matches = find_unfencing_devices(candidate->children, matches);
2127         } else if (is_not_set(candidate->flags, pe_rsc_fence_device)) {
2128             continue;
2129 
2130         } else if (crm_str_eq(provides, "unfencing", FALSE) || crm_str_eq(requires, "unfencing", FALSE)) {
2131             matches = g_list_prepend(matches, candidate);
2132         }
2133     }
2134     return matches;
2135 }
2136 
2137 
2138 action_t *
2139 pe_fence_op(node_t * node, const char *op, bool optional, const char *reason, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
2140 {
2141     char *op_key = NULL;
2142     action_t *stonith_op = NULL;
2143 
2144     if(op == NULL) {
2145         op = data_set->stonith_action;
2146     }
2147 
2148     op_key = crm_strdup_printf("%s-%s-%s", CRM_OP_FENCE, node->details->uname, op);
2149 
2150     if(data_set->singletons) {
2151         stonith_op = g_hash_table_lookup(data_set->singletons, op_key);
2152     }
2153 
2154     if(stonith_op == NULL) {
2155         stonith_op = custom_action(NULL, op_key, CRM_OP_FENCE, node, TRUE, TRUE, data_set);
2156 
2157         add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET, node->details->uname);
2158         add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET_UUID, node->details->id);
2159         add_hash_param(stonith_op->meta, "stonith_action", op);
2160 
2161         if(is_remote_node(node) && is_set(data_set->flags, pe_flag_enable_unfencing)) {
2162             /* Extra work to detect device changes on remotes
2163              *
2164              * We may do this for all nodes in the future, but for now
2165              * the check_action_definition() based stuff works fine.
2166              *
2167              * Use "stonith-on" to avoid creating cache entries for
2168              * operations check_action_definition() would look for.
2169              */
2170             long max = 1024;
2171             long digests_all_offset = 0;
2172             long digests_secure_offset = 0;
2173 
2174             char *digests_all = malloc(max);
2175             char *digests_secure = malloc(max);
2176             GListPtr matches = find_unfencing_devices(data_set->resources, NULL);
2177 
2178             for (GListPtr gIter = matches; gIter != NULL; gIter = gIter->next) {
2179                 resource_t *match = gIter->data;
2180                 op_digest_cache_t *data = fencing_action_digest_cmp(match, node, data_set);
2181 
2182                 if(data->rc == RSC_DIGEST_ALL) {
2183                     optional = FALSE;
2184                     crm_notice("Unfencing %s (remote): because the definition of %s changed", node->details->uname, match->id);
2185                     if (is_set(data_set->flags, pe_flag_sanitized)) {
2186                         /* Extra detail for those running from the commandline */
2187                         fprintf(stdout, "  notice: Unfencing %s (remote): because the definition of %s changed\n", node->details->uname, match->id);
2188                     }
2189                 }
2190 
2191                 digests_all_offset += snprintf(
2192                     digests_all+digests_all_offset, max-digests_all_offset,
2193                     "%s:%s:%s,", match->id, (const char*)g_hash_table_lookup(match->meta, XML_ATTR_TYPE), data->digest_all_calc);
2194 
2195                 digests_secure_offset += snprintf(
2196                     digests_secure+digests_secure_offset, max-digests_secure_offset,
2197                     "%s:%s:%s,", match->id, (const char*)g_hash_table_lookup(match->meta, XML_ATTR_TYPE), data->digest_secure_calc);
2198             }
2199             g_hash_table_insert(stonith_op->meta,
2200                                 strdup(XML_OP_ATTR_DIGESTS_ALL),
2201                                 digests_all);
2202             g_hash_table_insert(stonith_op->meta,
2203                                 strdup(XML_OP_ATTR_DIGESTS_SECURE),
2204                                 digests_secure);
2205         }
2206 
2207     } else {
2208         free(op_key);
2209     }
2210 
2211     if(optional == FALSE && pe_can_fence(data_set, node)) {
2212         pe_action_required(stonith_op, NULL, reason);
2213     } else if(reason && stonith_op->reason == NULL) {
2214         stonith_op->reason = strdup(reason);
2215     }
2216 
2217     return stonith_op;
2218 }
2219 
2220 void
2221 trigger_unfencing(
     /* [previous][next][first][last][top][bottom][index][help] */
2222     resource_t * rsc, node_t *node, const char *reason, action_t *dependency, pe_working_set_t * data_set) 
2223 {
2224     if(is_not_set(data_set->flags, pe_flag_enable_unfencing)) {
2225         /* No resources require it */
2226         return;
2227 
2228     } else if (rsc != NULL && is_not_set(rsc->flags, pe_rsc_fence_device)) {
2229         /* Wasn't a stonith device */
2230         return;
2231 
2232     } else if(node
2233               && node->details->online
2234               && node->details->unclean == FALSE
2235               && node->details->shutdown == FALSE) {
2236         action_t *unfence = pe_fence_op(node, "on", FALSE, reason, data_set);
2237 
2238         if(dependency) {
2239             order_actions(unfence, dependency, pe_order_optional);
2240         }
2241 
2242     } else if(rsc) {
2243         GHashTableIter iter;
2244 
2245         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
2246         while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
2247             if(node->details->online && node->details->unclean == FALSE && node->details->shutdown == FALSE) {
2248                 trigger_unfencing(rsc, node, reason, dependency, data_set);
2249             }
2250         }
2251     }
2252 }
2253 
2254 gboolean
2255 add_tag_ref(GHashTable * tags, const char * tag_name,  const char * obj_ref)
     /* [previous][next][first][last][top][bottom][index][help] */
2256 {
2257     tag_t *tag = NULL;
2258     GListPtr gIter = NULL;
2259     gboolean is_existing = FALSE;
2260 
2261     CRM_CHECK(tags && tag_name && obj_ref, return FALSE);
2262 
2263     tag = g_hash_table_lookup(tags, tag_name);
2264     if (tag == NULL) {
2265         tag = calloc(1, sizeof(tag_t));
2266         if (tag == NULL) {
2267             return FALSE;
2268         }
2269         tag->id = strdup(tag_name);
2270         tag->refs = NULL;
2271         g_hash_table_insert(tags, strdup(tag_name), tag);
2272     }
2273 
2274     for (gIter = tag->refs; gIter != NULL; gIter = gIter->next) {
2275         const char *existing_ref = (const char *) gIter->data;
2276 
2277         if (crm_str_eq(existing_ref, obj_ref, TRUE)){
2278             is_existing = TRUE;
2279             break;
2280         }
2281     }
2282 
2283     if (is_existing == FALSE) {
2284         tag->refs = g_list_append(tag->refs, strdup(obj_ref));
2285         crm_trace("Added: tag=%s ref=%s", tag->id, obj_ref);
2286     }
2287 
2288     return TRUE;
2289 }
2290 
2291 void pe_action_set_flag_reason(const char *function, long line,
     /* [previous][next][first][last][top][bottom][index][help] */
2292                                pe_action_t *action, pe_action_t *reason, const char *text,
2293                                enum pe_action_flags flags, bool overwrite)
2294 {
2295     bool unset = FALSE;
2296     bool update = FALSE;
2297     const char *change = NULL;
2298 
2299     if(is_set(flags, pe_action_runnable)) {
2300         unset = TRUE;
2301         change = "unrunnable";
2302     } else if(is_set(flags, pe_action_optional)) {
2303         unset = TRUE;
2304         change = "required";
2305     } else if(is_set(flags, pe_action_failure_is_fatal)) {
2306         change = "fatally failed";
2307     } else if(is_set(flags, pe_action_migrate_runnable)) {
2308         unset = TRUE;
2309         overwrite = TRUE;
2310         change = "unrunnable";
2311     } else if(is_set(flags, pe_action_dangle)) {
2312         change = "dangling";
2313     } else if(is_set(flags, pe_action_requires_any)) {
2314         change = "required";
2315     } else {
2316         crm_err("Unknown flag change to %s by %s: 0x%.16x",
2317                 flags, action->uuid, (reason? reason->uuid : 0));
2318     }
2319 
2320     if(unset) {
2321         if(is_set(action->flags, flags)) {
2322             action->flags = crm_clear_bit(function, line, action->uuid, action->flags, flags);
2323             update = TRUE;
2324         }
2325 
2326     } else {
2327         if(is_not_set(action->flags, flags)) {
2328             action->flags = crm_set_bit(function, line, action->uuid, action->flags, flags);
2329             update = TRUE;
2330         }
2331     }
2332 
2333     if((change && update) || text) {
2334         char *reason_text = NULL;
2335         if(reason == NULL) {
2336             pe_action_set_reason(action, text, overwrite);
2337 
2338         } else if(reason->rsc == NULL) {
2339             reason_text = crm_strdup_printf("%s %s%c %s", change, reason->task, text?':':0, text?text:"");
2340         } else {
2341             reason_text = crm_strdup_printf("%s %s %s%c %s", change, reason->rsc->id, reason->task, text?':':0, text?text:"NA");
2342         }
2343 
2344         if(reason_text && action->rsc != reason->rsc) {
2345             pe_action_set_reason(action, reason_text, overwrite);
2346         }
2347         free(reason_text);
2348     }
2349  }
2350 
2351 void pe_action_set_reason(pe_action_t *action, const char *reason, bool overwrite) 
     /* [previous][next][first][last][top][bottom][index][help] */
2352 {
2353     if(action->reason && overwrite) {
2354         pe_rsc_trace(action->rsc, "Changing %s reason from '%s' to '%s'", action->uuid, action->reason, reason);
2355         free(action->reason);
2356         action->reason = NULL;
2357     }
2358     if(action->reason == NULL) {
2359         if(reason) {
2360             pe_rsc_trace(action->rsc, "Set %s reason to '%s'", action->uuid, reason);
2361             action->reason = strdup(reason);
2362         } else {
2363             action->reason = NULL;
2364         }
2365     }
2366 }

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