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

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