root/lib/pengine/utils.c

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

DEFINITIONS

This source file includes following definitions.
  1. pe_can_fence
  2. pe__copy_node
  3. node_list_exclude
  4. pe__node_list2table
  5. pe__cmp_node_name
  6. pe__output_node_weights
  7. pe__log_node_weights
  8. pe__show_node_weights_as
  9. pe__cmp_rsc_priority
  10. resource_node_score
  11. resource_location
  12. get_effective_time
  13. get_target_role
  14. order_actions
  15. destroy_ticket
  16. ticket_new
  17. rsc_printable_id
  18. pe__clear_resource_flags_recursive
  19. pe__clear_resource_flags_on_all
  20. pe__set_resource_flags_recursive
  21. trigger_unfencing
  22. add_tag_ref
  23. pe__shutdown_requested
  24. pe__update_recheck_time
  25. pe__unpack_dataset_nvpairs
  26. pe__resource_is_disabled
  27. pe__rsc_running_on_any
  28. pcmk__rsc_filtered_by_node
  29. pe__filter_rsc_list
  30. pe__build_node_name_list
  31. pe__build_rsc_list
  32. pe__failed_probe_for_rsc

   1 /*
   2  * Copyright 2004-2022 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <glib.h>
  13 #include <stdbool.h>
  14 
  15 #include <crm/crm.h>
  16 #include <crm/msg_xml.h>
  17 #include <crm/pengine/rules.h>
  18 #include <crm/pengine/internal.h>
  19 
  20 #include "pe_status_private.h"
  21 
  22 extern bool pcmk__is_daemon;
  23 
  24 gboolean ghash_free_str_str(gpointer key, gpointer value, gpointer user_data);
  25 
  26 /*!
  27  * \internal
  28  * \brief Check whether we can fence a particular node
  29  *
  30  * \param[in] data_set  Working set for cluster
  31  * \param[in] node      Name of node to check
  32  *
  33  * \return true if node can be fenced, false otherwise
  34  */
  35 bool
  36 pe_can_fence(pe_working_set_t *data_set, pe_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
  37 {
  38     if (pe__is_guest_node(node)) {
  39         /* Guest nodes are fenced by stopping their container resource. We can
  40          * do that if the container's host is either online or fenceable.
  41          */
  42         pe_resource_t *rsc = node->details->remote_rsc->container;
  43 
  44         for (GList *n = rsc->running_on; n != NULL; n = n->next) {
  45             pe_node_t *container_node = n->data;
  46 
  47             if (!container_node->details->online
  48                 && !pe_can_fence(data_set, container_node)) {
  49                 return false;
  50             }
  51         }
  52         return true;
  53 
  54     } else if (!pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
  55         return false; /* Turned off */
  56 
  57     } else if (!pcmk_is_set(data_set->flags, pe_flag_have_stonith_resource)) {
  58         return false; /* No devices */
  59 
  60     } else if (pcmk_is_set(data_set->flags, pe_flag_have_quorum)) {
  61         return true;
  62 
  63     } else if (data_set->no_quorum_policy == no_quorum_ignore) {
  64         return true;
  65 
  66     } else if(node == NULL) {
  67         return false;
  68 
  69     } else if(node->details->online) {
  70         crm_notice("We can fence %s without quorum because they're in our membership",
  71                    pe__node_name(node));
  72         return true;
  73     }
  74 
  75     crm_trace("Cannot fence %s", pe__node_name(node));
  76     return false;
  77 }
  78 
  79 /*!
  80  * \internal
  81  * \brief Copy a node object
  82  *
  83  * \param[in] this_node  Node object to copy
  84  *
  85  * \return Newly allocated shallow copy of this_node
  86  * \note This function asserts on errors and is guaranteed to return non-NULL.
  87  */
  88 pe_node_t *
  89 pe__copy_node(const pe_node_t *this_node)
     /* [previous][next][first][last][top][bottom][index][help] */
  90 {
  91     pe_node_t *new_node = NULL;
  92 
  93     CRM_ASSERT(this_node != NULL);
  94 
  95     new_node = calloc(1, sizeof(pe_node_t));
  96     CRM_ASSERT(new_node != NULL);
  97 
  98     new_node->rsc_discover_mode = this_node->rsc_discover_mode;
  99     new_node->weight = this_node->weight;
 100     new_node->fixed = this_node->fixed;
 101     new_node->details = this_node->details;
 102 
 103     return new_node;
 104 }
 105 
 106 /* any node in list1 or list2 and not in the other gets a score of -INFINITY */
 107 void
 108 node_list_exclude(GHashTable * hash, GList *list, gboolean merge_scores)
     /* [previous][next][first][last][top][bottom][index][help] */
 109 {
 110     GHashTable *result = hash;
 111     pe_node_t *other_node = NULL;
 112     GList *gIter = list;
 113 
 114     GHashTableIter iter;
 115     pe_node_t *node = NULL;
 116 
 117     g_hash_table_iter_init(&iter, hash);
 118     while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
 119 
 120         other_node = pe_find_node_id(list, node->details->id);
 121         if (other_node == NULL) {
 122             node->weight = -INFINITY;
 123         } else if (merge_scores) {
 124             node->weight = pcmk__add_scores(node->weight, other_node->weight);
 125         }
 126     }
 127 
 128     for (; gIter != NULL; gIter = gIter->next) {
 129         pe_node_t *node = (pe_node_t *) gIter->data;
 130 
 131         other_node = pe_hash_table_lookup(result, node->details->id);
 132 
 133         if (other_node == NULL) {
 134             pe_node_t *new_node = pe__copy_node(node);
 135 
 136             new_node->weight = -INFINITY;
 137             g_hash_table_insert(result, (gpointer) new_node->details->id, new_node);
 138         }
 139     }
 140 }
 141 
 142 /*!
 143  * \internal
 144  * \brief Create a node hash table from a node list
 145  *
 146  * \param[in] list  Node list
 147  *
 148  * \return Hash table equivalent of node list
 149  */
 150 GHashTable *
 151 pe__node_list2table(GList *list)
     /* [previous][next][first][last][top][bottom][index][help] */
 152 {
 153     GHashTable *result = NULL;
 154 
 155     result = pcmk__strkey_table(NULL, free);
 156     for (GList *gIter = list; gIter != NULL; gIter = gIter->next) {
 157         pe_node_t *new_node = pe__copy_node((pe_node_t *) gIter->data);
 158 
 159         g_hash_table_insert(result, (gpointer) new_node->details->id, new_node);
 160     }
 161     return result;
 162 }
 163 
 164 /*!
 165  * \internal
 166  * \brief Compare two nodes by name, with numeric portions sorted numerically
 167  *
 168  * Sort two node names case-insensitively like strcasecmp(), but with any
 169  * numeric portions of the name sorted numerically. For example, "node10" will
 170  * sort higher than "node9" but lower than "remotenode9".
 171  *
 172  * \param[in] a  First node to compare (can be \c NULL)
 173  * \param[in] b  Second node to compare (can be \c NULL)
 174  *
 175  * \retval -1 \c a comes before \c b (or \c a is \c NULL and \c b is not)
 176  * \retval  0 \c a and \c b are equal (or both are \c NULL)
 177  * \retval  1 \c a comes after \c b (or \c b is \c NULL and \c a is not)
 178  */
 179 gint
 180 pe__cmp_node_name(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 181 {
 182     const pe_node_t *node1 = (const pe_node_t *) a;
 183     const pe_node_t *node2 = (const pe_node_t *) b;
 184 
 185     if ((node1 == NULL) && (node2 == NULL)) {
 186         return 0;
 187     }
 188 
 189     if (node1 == NULL) {
 190         return -1;
 191     }
 192 
 193     if (node2 == NULL) {
 194         return 1;
 195     }
 196 
 197     return pcmk__numeric_strcasecmp(node1->details->uname,
 198                                     node2->details->uname);
 199 }
 200 
 201 /*!
 202  * \internal
 203  * \brief Output node weights to stdout
 204  *
 205  * \param[in] rsc       Use allowed nodes for this resource
 206  * \param[in] comment   Text description to prefix lines with
 207  * \param[in] nodes     If rsc is not specified, use these nodes
 208  */
 209 static void
 210 pe__output_node_weights(pe_resource_t *rsc, const char *comment,
     /* [previous][next][first][last][top][bottom][index][help] */
 211                         GHashTable *nodes, pe_working_set_t *data_set)
 212 {
 213     pcmk__output_t *out = data_set->priv;
 214 
 215     // Sort the nodes so the output is consistent for regression tests
 216     GList *list = g_list_sort(g_hash_table_get_values(nodes),
 217                               pe__cmp_node_name);
 218 
 219     for (GList *gIter = list; gIter != NULL; gIter = gIter->next) {
 220         pe_node_t *node = (pe_node_t *) gIter->data;
 221 
 222         out->message(out, "node-weight", rsc, comment, node->details->uname,
 223                      pcmk_readable_score(node->weight));
 224     }
 225     g_list_free(list);
 226 }
 227 
 228 /*!
 229  * \internal
 230  * \brief Log node weights at trace level
 231  *
 232  * \param[in] file      Caller's filename
 233  * \param[in] function  Caller's function name
 234  * \param[in] line      Caller's line number
 235  * \param[in] rsc       Use allowed nodes for this resource
 236  * \param[in] comment   Text description to prefix lines with
 237  * \param[in] nodes     If rsc is not specified, use these nodes
 238  */
 239 static void
 240 pe__log_node_weights(const char *file, const char *function, int line,
     /* [previous][next][first][last][top][bottom][index][help] */
 241                      pe_resource_t *rsc, const char *comment, GHashTable *nodes)
 242 {
 243     GHashTableIter iter;
 244     pe_node_t *node = NULL;
 245 
 246     // Don't waste time if we're not tracing at this point
 247     pcmk__log_else(LOG_TRACE, return);
 248 
 249     g_hash_table_iter_init(&iter, nodes);
 250     while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
 251         if (rsc) {
 252             qb_log_from_external_source(function, file,
 253                                         "%s: %s allocation score on %s: %s",
 254                                         LOG_TRACE, line, 0,
 255                                         comment, rsc->id,
 256                                         pe__node_name(node),
 257                                         pcmk_readable_score(node->weight));
 258         } else {
 259             qb_log_from_external_source(function, file, "%s: %s = %s",
 260                                         LOG_TRACE, line, 0,
 261                                         comment, pe__node_name(node),
 262                                         pcmk_readable_score(node->weight));
 263         }
 264     }
 265 }
 266 
 267 /*!
 268  * \internal
 269  * \brief Log or output node weights
 270  *
 271  * \param[in] file      Caller's filename
 272  * \param[in] function  Caller's function name
 273  * \param[in] line      Caller's line number
 274  * \param[in] to_log    Log if true, otherwise output
 275  * \param[in] rsc       Use allowed nodes for this resource
 276  * \param[in] comment   Text description to prefix lines with
 277  * \param[in] nodes     Use these nodes
 278  */
 279 void
 280 pe__show_node_weights_as(const char *file, const char *function, int line,
     /* [previous][next][first][last][top][bottom][index][help] */
 281                          bool to_log, pe_resource_t *rsc, const char *comment,
 282                          GHashTable *nodes, pe_working_set_t *data_set)
 283 {
 284     if (rsc != NULL && pcmk_is_set(rsc->flags, pe_rsc_orphan)) {
 285         // Don't show allocation scores for orphans
 286         return;
 287     }
 288     if (nodes == NULL) {
 289         // Nothing to show
 290         return;
 291     }
 292 
 293     if (to_log) {
 294         pe__log_node_weights(file, function, line, rsc, comment, nodes);
 295     } else {
 296         pe__output_node_weights(rsc, comment, nodes, data_set);
 297     }
 298 
 299     // If this resource has children, repeat recursively for each
 300     if (rsc && rsc->children) {
 301         for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
 302             pe_resource_t *child = (pe_resource_t *) gIter->data;
 303 
 304             pe__show_node_weights_as(file, function, line, to_log, child,
 305                                      comment, child->allowed_nodes, data_set);
 306         }
 307     }
 308 }
 309 
 310 /*!
 311  * \internal
 312  * \brief Compare two resources by priority
 313  *
 314  * \param[in] a  First resource to compare (can be \c NULL)
 315  * \param[in] b  Second resource to compare (can be \c NULL)
 316  *
 317  * \retval -1 \c a->priority > \c b->priority (or \c b is \c NULL and \c a is
 318  *            not)
 319  * \retval  0 \c a->priority == \c b->priority (or both \c a and \c b are
 320  *            \c NULL)
 321  * \retval  1 \c a->priority < \c b->priority (or \c a is \c NULL and \c b is
 322  *            not)
 323  */
 324 gint
 325 pe__cmp_rsc_priority(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 326 {
 327     const pe_resource_t *resource1 = (const pe_resource_t *)a;
 328     const pe_resource_t *resource2 = (const pe_resource_t *)b;
 329 
 330     if (a == NULL && b == NULL) {
 331         return 0;
 332     }
 333     if (a == NULL) {
 334         return 1;
 335     }
 336     if (b == NULL) {
 337         return -1;
 338     }
 339 
 340     if (resource1->priority > resource2->priority) {
 341         return -1;
 342     }
 343 
 344     if (resource1->priority < resource2->priority) {
 345         return 1;
 346     }
 347 
 348     return 0;
 349 }
 350 
 351 static void
 352 resource_node_score(pe_resource_t * rsc, pe_node_t * node, int score, const char *tag)
     /* [previous][next][first][last][top][bottom][index][help] */
 353 {
 354     pe_node_t *match = NULL;
 355 
 356     if ((rsc->exclusive_discover || (node->rsc_discover_mode == pe_discover_never))
 357         && pcmk__str_eq(tag, "symmetric_default", pcmk__str_casei)) {
 358         /* This string comparision may be fragile, but exclusive resources and
 359          * exclusive nodes should not have the symmetric_default constraint
 360          * applied to them.
 361          */
 362         return;
 363 
 364     } else if (rsc->children) {
 365         GList *gIter = rsc->children;
 366 
 367         for (; gIter != NULL; gIter = gIter->next) {
 368             pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 369 
 370             resource_node_score(child_rsc, node, score, tag);
 371         }
 372     }
 373 
 374     pe_rsc_trace(rsc, "Setting %s for %s on %s: %d",
 375                  tag, rsc->id, pe__node_name(node), score);
 376     match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id);
 377     if (match == NULL) {
 378         match = pe__copy_node(node);
 379         g_hash_table_insert(rsc->allowed_nodes, (gpointer) match->details->id, match);
 380     }
 381     match->weight = pcmk__add_scores(match->weight, score);
 382 }
 383 
 384 void
 385 resource_location(pe_resource_t * rsc, pe_node_t * node, int score, const char *tag,
     /* [previous][next][first][last][top][bottom][index][help] */
 386                   pe_working_set_t * data_set)
 387 {
 388     if (node != NULL) {
 389         resource_node_score(rsc, node, score, tag);
 390 
 391     } else if (data_set != NULL) {
 392         GList *gIter = data_set->nodes;
 393 
 394         for (; gIter != NULL; gIter = gIter->next) {
 395             pe_node_t *node_iter = (pe_node_t *) gIter->data;
 396 
 397             resource_node_score(rsc, node_iter, score, tag);
 398         }
 399 
 400     } else {
 401         GHashTableIter iter;
 402         pe_node_t *node_iter = NULL;
 403 
 404         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
 405         while (g_hash_table_iter_next(&iter, NULL, (void **)&node_iter)) {
 406             resource_node_score(rsc, node_iter, score, tag);
 407         }
 408     }
 409 
 410     if (node == NULL && score == -INFINITY) {
 411         if (rsc->allocated_to) {
 412             crm_info("Deallocating %s from %s",
 413                      rsc->id, pe__node_name(rsc->allocated_to));
 414             free(rsc->allocated_to);
 415             rsc->allocated_to = NULL;
 416         }
 417     }
 418 }
 419 
 420 time_t
 421 get_effective_time(pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 422 {
 423     if(data_set) {
 424         if (data_set->now == NULL) {
 425             crm_trace("Recording a new 'now'");
 426             data_set->now = crm_time_new(NULL);
 427         }
 428         return crm_time_get_seconds_since_epoch(data_set->now);
 429     }
 430 
 431     crm_trace("Defaulting to 'now'");
 432     return time(NULL);
 433 }
 434 
 435 gboolean
 436 get_target_role(pe_resource_t * rsc, enum rsc_role_e * role)
     /* [previous][next][first][last][top][bottom][index][help] */
 437 {
 438     enum rsc_role_e local_role = RSC_ROLE_UNKNOWN;
 439     const char *value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
 440 
 441     CRM_CHECK(role != NULL, return FALSE);
 442 
 443     if (pcmk__str_eq(value, "started", pcmk__str_null_matches | pcmk__str_casei)
 444         || pcmk__str_eq("default", value, pcmk__str_casei)) {
 445         return FALSE;
 446     }
 447 
 448     local_role = text2role(value);
 449     if (local_role == RSC_ROLE_UNKNOWN) {
 450         pcmk__config_err("Ignoring '" XML_RSC_ATTR_TARGET_ROLE "' for %s "
 451                          "because '%s' is not valid", rsc->id, value);
 452         return FALSE;
 453 
 454     } else if (local_role > RSC_ROLE_STARTED) {
 455         if (pcmk_is_set(uber_parent(rsc)->flags, pe_rsc_promotable)) {
 456             if (local_role > RSC_ROLE_UNPROMOTED) {
 457                 /* This is what we'd do anyway, just leave the default to avoid messing up the placement algorithm */
 458                 return FALSE;
 459             }
 460 
 461         } else {
 462             pcmk__config_err("Ignoring '" XML_RSC_ATTR_TARGET_ROLE "' for %s "
 463                              "because '%s' only makes sense for promotable "
 464                              "clones", rsc->id, value);
 465             return FALSE;
 466         }
 467     }
 468 
 469     *role = local_role;
 470     return TRUE;
 471 }
 472 
 473 gboolean
 474 order_actions(pe_action_t * lh_action, pe_action_t * rh_action, enum pe_ordering order)
     /* [previous][next][first][last][top][bottom][index][help] */
 475 {
 476     GList *gIter = NULL;
 477     pe_action_wrapper_t *wrapper = NULL;
 478     GList *list = NULL;
 479 
 480     if (order == pe_order_none) {
 481         return FALSE;
 482     }
 483 
 484     if (lh_action == NULL || rh_action == NULL) {
 485         return FALSE;
 486     }
 487 
 488     crm_trace("Creating action wrappers for ordering: %s then %s",
 489               lh_action->uuid, rh_action->uuid);
 490 
 491     /* Ensure we never create a dependency on ourselves... it's happened */
 492     CRM_ASSERT(lh_action != rh_action);
 493 
 494     /* Filter dups, otherwise update_action_states() has too much work to do */
 495     gIter = lh_action->actions_after;
 496     for (; gIter != NULL; gIter = gIter->next) {
 497         pe_action_wrapper_t *after = (pe_action_wrapper_t *) gIter->data;
 498 
 499         if (after->action == rh_action && (after->type & order)) {
 500             return FALSE;
 501         }
 502     }
 503 
 504     wrapper = calloc(1, sizeof(pe_action_wrapper_t));
 505     wrapper->action = rh_action;
 506     wrapper->type = order;
 507     list = lh_action->actions_after;
 508     list = g_list_prepend(list, wrapper);
 509     lh_action->actions_after = list;
 510 
 511     wrapper = calloc(1, sizeof(pe_action_wrapper_t));
 512     wrapper->action = lh_action;
 513     wrapper->type = order;
 514     list = rh_action->actions_before;
 515     list = g_list_prepend(list, wrapper);
 516     rh_action->actions_before = list;
 517     return TRUE;
 518 }
 519 
 520 void
 521 destroy_ticket(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 522 {
 523     pe_ticket_t *ticket = data;
 524 
 525     if (ticket->state) {
 526         g_hash_table_destroy(ticket->state);
 527     }
 528     free(ticket->id);
 529     free(ticket);
 530 }
 531 
 532 pe_ticket_t *
 533 ticket_new(const char *ticket_id, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 534 {
 535     pe_ticket_t *ticket = NULL;
 536 
 537     if (pcmk__str_empty(ticket_id)) {
 538         return NULL;
 539     }
 540 
 541     if (data_set->tickets == NULL) {
 542         data_set->tickets = pcmk__strkey_table(free, destroy_ticket);
 543     }
 544 
 545     ticket = g_hash_table_lookup(data_set->tickets, ticket_id);
 546     if (ticket == NULL) {
 547 
 548         ticket = calloc(1, sizeof(pe_ticket_t));
 549         if (ticket == NULL) {
 550             crm_err("Cannot allocate ticket '%s'", ticket_id);
 551             return NULL;
 552         }
 553 
 554         crm_trace("Creaing ticket entry for %s", ticket_id);
 555 
 556         ticket->id = strdup(ticket_id);
 557         ticket->granted = FALSE;
 558         ticket->last_granted = -1;
 559         ticket->standby = FALSE;
 560         ticket->state = pcmk__strkey_table(free, free);
 561 
 562         g_hash_table_insert(data_set->tickets, strdup(ticket->id), ticket);
 563     }
 564 
 565     return ticket;
 566 }
 567 
 568 const char *rsc_printable_id(pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 569 {
 570     if (!pcmk_is_set(rsc->flags, pe_rsc_unique)) {
 571         return ID(rsc->xml);
 572     }
 573     return rsc->id;
 574 }
 575 
 576 void
 577 pe__clear_resource_flags_recursive(pe_resource_t *rsc, uint64_t flags)
     /* [previous][next][first][last][top][bottom][index][help] */
 578 {
 579     pe__clear_resource_flags(rsc, flags);
 580     for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
 581         pe__clear_resource_flags_recursive((pe_resource_t *) gIter->data, flags);
 582     }
 583 }
 584 
 585 void
 586 pe__clear_resource_flags_on_all(pe_working_set_t *data_set, uint64_t flag)
     /* [previous][next][first][last][top][bottom][index][help] */
 587 {
 588     for (GList *lpc = data_set->resources; lpc != NULL; lpc = lpc->next) {
 589         pe_resource_t *r = (pe_resource_t *) lpc->data;
 590         pe__clear_resource_flags_recursive(r, flag);
 591     }
 592 }
 593 
 594 void
 595 pe__set_resource_flags_recursive(pe_resource_t *rsc, uint64_t flags)
     /* [previous][next][first][last][top][bottom][index][help] */
 596 {
 597     pe__set_resource_flags(rsc, flags);
 598     for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
 599         pe__set_resource_flags_recursive((pe_resource_t *) gIter->data, flags);
 600     }
 601 }
 602 
 603 void
 604 trigger_unfencing(
     /* [previous][next][first][last][top][bottom][index][help] */
 605     pe_resource_t * rsc, pe_node_t *node, const char *reason, pe_action_t *dependency, pe_working_set_t * data_set) 
 606 {
 607     if (!pcmk_is_set(data_set->flags, pe_flag_enable_unfencing)) {
 608         /* No resources require it */
 609         return;
 610 
 611     } else if ((rsc != NULL)
 612                && !pcmk_is_set(rsc->flags, pe_rsc_fence_device)) {
 613         /* Wasn't a stonith device */
 614         return;
 615 
 616     } else if(node
 617               && node->details->online
 618               && node->details->unclean == FALSE
 619               && node->details->shutdown == FALSE) {
 620         pe_action_t *unfence = pe_fence_op(node, "on", FALSE, reason, FALSE, data_set);
 621 
 622         if(dependency) {
 623             order_actions(unfence, dependency, pe_order_optional);
 624         }
 625 
 626     } else if(rsc) {
 627         GHashTableIter iter;
 628 
 629         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
 630         while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
 631             if(node->details->online && node->details->unclean == FALSE && node->details->shutdown == FALSE) {
 632                 trigger_unfencing(rsc, node, reason, dependency, data_set);
 633             }
 634         }
 635     }
 636 }
 637 
 638 gboolean
 639 add_tag_ref(GHashTable * tags, const char * tag_name,  const char * obj_ref)
     /* [previous][next][first][last][top][bottom][index][help] */
 640 {
 641     pe_tag_t *tag = NULL;
 642     GList *gIter = NULL;
 643     gboolean is_existing = FALSE;
 644 
 645     CRM_CHECK(tags && tag_name && obj_ref, return FALSE);
 646 
 647     tag = g_hash_table_lookup(tags, tag_name);
 648     if (tag == NULL) {
 649         tag = calloc(1, sizeof(pe_tag_t));
 650         if (tag == NULL) {
 651             return FALSE;
 652         }
 653         tag->id = strdup(tag_name);
 654         tag->refs = NULL;
 655         g_hash_table_insert(tags, strdup(tag_name), tag);
 656     }
 657 
 658     for (gIter = tag->refs; gIter != NULL; gIter = gIter->next) {
 659         const char *existing_ref = (const char *) gIter->data;
 660 
 661         if (pcmk__str_eq(existing_ref, obj_ref, pcmk__str_none)){
 662             is_existing = TRUE;
 663             break;
 664         }
 665     }
 666 
 667     if (is_existing == FALSE) {
 668         tag->refs = g_list_append(tag->refs, strdup(obj_ref));
 669         crm_trace("Added: tag=%s ref=%s", tag->id, obj_ref);
 670     }
 671 
 672     return TRUE;
 673 }
 674 
 675 /*!
 676  * \internal
 677  * \brief Check whether shutdown has been requested for a node
 678  *
 679  * \param[in] node  Node to check
 680  *
 681  * \return TRUE if node has shutdown attribute set and nonzero, FALSE otherwise
 682  * \note This differs from simply using node->details->shutdown in that it can
 683  *       be used before that has been determined (and in fact to determine it),
 684  *       and it can also be used to distinguish requested shutdown from implicit
 685  *       shutdown of remote nodes by virtue of their connection stopping.
 686  */
 687 bool
 688 pe__shutdown_requested(pe_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 689 {
 690     const char *shutdown = pe_node_attribute_raw(node, XML_CIB_ATTR_SHUTDOWN);
 691 
 692     return !pcmk__str_eq(shutdown, "0", pcmk__str_null_matches);
 693 }
 694 
 695 /*!
 696  * \internal
 697  * \brief Update a data set's "recheck by" time
 698  *
 699  * \param[in]     recheck   Epoch time when recheck should happen
 700  * \param[in,out] data_set  Current working set
 701  */
 702 void
 703 pe__update_recheck_time(time_t recheck, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 704 {
 705     if ((recheck > get_effective_time(data_set))
 706         && ((data_set->recheck_by == 0)
 707             || (data_set->recheck_by > recheck))) {
 708         data_set->recheck_by = recheck;
 709     }
 710 }
 711 
 712 /*!
 713  * \internal
 714  * \brief Wrapper for pe_unpack_nvpairs() using a cluster working set
 715  */
 716 void
 717 pe__unpack_dataset_nvpairs(const xmlNode *xml_obj, const char *set_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 718                            pe_rule_eval_data_t *rule_data, GHashTable *hash,
 719                            const char *always_first, gboolean overwrite,
 720                            pe_working_set_t *data_set)
 721 {
 722     crm_time_t *next_change = crm_time_new_undefined();
 723 
 724     pe_eval_nvpairs(data_set->input, xml_obj, set_name, rule_data, hash,
 725                     always_first, overwrite, next_change);
 726     if (crm_time_is_defined(next_change)) {
 727         time_t recheck = (time_t) crm_time_get_seconds_since_epoch(next_change);
 728 
 729         pe__update_recheck_time(recheck, data_set);
 730     }
 731     crm_time_free(next_change);
 732 }
 733 
 734 bool
 735 pe__resource_is_disabled(pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 736 {
 737     const char *target_role = NULL;
 738 
 739     CRM_CHECK(rsc != NULL, return false);
 740     target_role = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
 741     if (target_role) {
 742         enum rsc_role_e target_role_e = text2role(target_role);
 743 
 744         if ((target_role_e == RSC_ROLE_STOPPED)
 745             || ((target_role_e == RSC_ROLE_UNPROMOTED)
 746                 && pcmk_is_set(uber_parent(rsc)->flags, pe_rsc_promotable))) {
 747             return true;
 748         }
 749     }
 750     return false;
 751 }
 752 
 753 bool
 754 pe__rsc_running_on_any(pe_resource_t *rsc, GList *node_list)
     /* [previous][next][first][last][top][bottom][index][help] */
 755 {
 756     for (GList *ele = rsc->running_on; ele; ele = ele->next) {
 757         pe_node_t *node = (pe_node_t *) ele->data;
 758         if (pcmk__str_in_list(node->details->uname, node_list,
 759                               pcmk__str_star_matches|pcmk__str_casei)) {
 760             return true;
 761         }
 762     }
 763 
 764     return false;
 765 }
 766 
 767 bool
 768 pcmk__rsc_filtered_by_node(pe_resource_t *rsc, GList *only_node)
     /* [previous][next][first][last][top][bottom][index][help] */
 769 {
 770     return (rsc->fns->active(rsc, FALSE) && !pe__rsc_running_on_any(rsc, only_node));
 771 }
 772 
 773 GList *
 774 pe__filter_rsc_list(GList *rscs, GList *filter)
     /* [previous][next][first][last][top][bottom][index][help] */
 775 {
 776     GList *retval = NULL;
 777 
 778     for (GList *gIter = rscs; gIter; gIter = gIter->next) {
 779         pe_resource_t *rsc = (pe_resource_t *) gIter->data;
 780 
 781         /* I think the second condition is safe here for all callers of this
 782          * function.  If not, it needs to move into pe__node_text.
 783          */
 784         if (pcmk__str_in_list(rsc_printable_id(rsc), filter, pcmk__str_star_matches) ||
 785             (rsc->parent && pcmk__str_in_list(rsc_printable_id(rsc->parent), filter, pcmk__str_star_matches))) {
 786             retval = g_list_prepend(retval, rsc);
 787         }
 788     }
 789 
 790     return retval;
 791 }
 792 
 793 GList *
 794 pe__build_node_name_list(pe_working_set_t *data_set, const char *s) {
     /* [previous][next][first][last][top][bottom][index][help] */
 795     GList *nodes = NULL;
 796 
 797     if (pcmk__str_eq(s, "*", pcmk__str_null_matches)) {
 798         /* Nothing was given so return a list of all node names.  Or, '*' was
 799          * given.  This would normally fall into the pe__unames_with_tag branch
 800          * where it will return an empty list.  Catch it here instead.
 801          */
 802         nodes = g_list_prepend(nodes, strdup("*"));
 803     } else {
 804         pe_node_t *node = pe_find_node(data_set->nodes, s);
 805 
 806         if (node) {
 807             /* The given string was a valid uname for a node.  Return a
 808              * singleton list containing just that uname.
 809              */
 810             nodes = g_list_prepend(nodes, strdup(s));
 811         } else {
 812             /* The given string was not a valid uname.  It's either a tag or
 813              * it's a typo or something.  In the first case, we'll return a
 814              * list of all the unames of the nodes with the given tag.  In the
 815              * second case, we'll return a NULL pointer and nothing will
 816              * get displayed.
 817              */
 818             nodes = pe__unames_with_tag(data_set, s);
 819         }
 820     }
 821 
 822     return nodes;
 823 }
 824 
 825 GList *
 826 pe__build_rsc_list(pe_working_set_t *data_set, const char *s) {
     /* [previous][next][first][last][top][bottom][index][help] */
 827     GList *resources = NULL;
 828 
 829     if (pcmk__str_eq(s, "*", pcmk__str_null_matches)) {
 830         resources = g_list_prepend(resources, strdup("*"));
 831     } else {
 832         pe_resource_t *rsc = pe_find_resource_with_flags(data_set->resources, s,
 833                                                          pe_find_renamed|pe_find_any);
 834 
 835         if (rsc) {
 836             /* A colon in the name we were given means we're being asked to filter
 837              * on a specific instance of a cloned resource.  Put that exact string
 838              * into the filter list.  Otherwise, use the printable ID of whatever
 839              * resource was found that matches what was asked for.
 840              */
 841             if (strstr(s, ":") != NULL) {
 842                 resources = g_list_prepend(resources, strdup(rsc->id));
 843             } else {
 844                 resources = g_list_prepend(resources, strdup(rsc_printable_id(rsc)));
 845             }
 846         } else {
 847             /* The given string was not a valid resource name. It's a tag or a
 848              * typo or something. See pe__build_node_name_list() for more
 849              * detail.
 850              */
 851             resources = pe__rscs_with_tag(data_set, s);
 852         }
 853     }
 854 
 855     return resources;
 856 }
 857 
 858 xmlNode *
 859 pe__failed_probe_for_rsc(pe_resource_t *rsc, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 860 {
 861     pe_resource_t *parent = uber_parent(rsc);
 862     const char *rsc_id = rsc->id;
 863 
 864     if (rsc->variant == pe_clone) {
 865         rsc_id = pe__clone_child_id(rsc);
 866     } else if (parent->variant == pe_clone) {
 867         rsc_id = pe__clone_child_id(parent);
 868     }
 869 
 870     for (xmlNode *xml_op = pcmk__xml_first_child(rsc->cluster->failed); xml_op != NULL;
 871          xml_op = pcmk__xml_next(xml_op)) {
 872         const char *value = NULL;
 873         char *op_id = NULL;
 874 
 875         /* This resource operation is not a failed probe. */
 876         if (!pcmk_xe_mask_probe_failure(xml_op)) {
 877             continue;
 878         }
 879 
 880         /* This resource operation was not run on the given node.  Note that if name is
 881          * NULL, this will always succeed.
 882          */
 883         value = crm_element_value(xml_op, XML_LRM_ATTR_TARGET);
 884         if (value == NULL || !pcmk__str_eq(value, name, pcmk__str_casei|pcmk__str_null_matches)) {
 885             continue;
 886         }
 887 
 888         /* This resource operation has no operation_key. */
 889         value = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
 890         if (!parse_op_key(value ? value : ID(xml_op), &op_id, NULL, NULL)) {
 891             continue;
 892         }
 893 
 894         /* This resource operation's ID does not match the rsc_id we are looking for. */
 895         if (!pcmk__str_eq(op_id, rsc_id, pcmk__str_none)) {
 896             free(op_id);
 897             continue;
 898         }
 899 
 900         free(op_id);
 901         return xml_op;
 902     }
 903 
 904     return NULL;
 905 }

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