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_target_role
  12. order_actions
  13. destroy_ticket
  14. ticket_new
  15. rsc_printable_id
  16. pe__clear_resource_flags_recursive
  17. pe__clear_resource_flags_on_all
  18. pe__set_resource_flags_recursive
  19. trigger_unfencing
  20. pe__shutdown_requested
  21. pe__unpack_dataset_nvpairs
  22. pe__resource_is_disabled
  23. pe__rsc_running_on_only
  24. pe__rsc_running_on_any
  25. pcmk__rsc_filtered_by_node
  26. pe__filter_rsc_list
  27. pe__build_node_name_list
  28. pe__build_rsc_list
  29. pe__failed_probe_for_rsc

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

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