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

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