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