root/lib/pengine/utils.c

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

DEFINITIONS

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

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