root/lib/pengine/status.c

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

DEFINITIONS

This source file includes following definitions.
  1. pe_new_working_set
  2. pe_free_working_set
  3. check_for_deprecated_rules
  4. cluster_status
  5. pe_free_resources
  6. pe_free_actions
  7. pe_free_nodes
  8. pe__free_ordering
  9. pe__free_location
  10. cleanup_calculations
  11. pe_reset_working_set
  12. set_working_set_defaults
  13. pe_find_resource
  14. pe_find_resource_with_flags
  15. pe_find_node_any
  16. pe_find_node_id
  17. pe_find_node

   1 /*
   2  * Copyright 2004-2024 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <sys/param.h>
  13 
  14 #include <crm/crm.h>
  15 #include <crm/common/xml.h>
  16 #include <crm/common/cib_internal.h>
  17 
  18 #include <glib.h>
  19 
  20 #include <crm/pengine/internal.h>
  21 #include <pe_status_private.h>
  22 
  23 /*!
  24  * \brief Create a new object to hold scheduler data
  25  *
  26  * \return New, initialized scheduler data on success, else NULL (and set errno)
  27  * \note Only pcmk_scheduler_t objects created with this function (as opposed
  28  *       to statically declared or directly allocated) should be used with the
  29  *       functions in this library, to allow for future extensions to the
  30  *       data type. The caller is responsible for freeing the memory with
  31  *       pe_free_working_set() when the instance is no longer needed.
  32  */
  33 pcmk_scheduler_t *
  34 pe_new_working_set(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  35 {
  36     pcmk_scheduler_t *scheduler = calloc(1, sizeof(pcmk_scheduler_t));
  37 
  38     if (scheduler != NULL) {
  39         set_working_set_defaults(scheduler);
  40     }
  41     return scheduler;
  42 }
  43 
  44 /*!
  45  * \brief Free scheduler data
  46  *
  47  * \param[in,out] scheduler  Scheduler data to free
  48  */
  49 void
  50 pe_free_working_set(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
  51 {
  52     if (scheduler != NULL) {
  53         pe_reset_working_set(scheduler);
  54         scheduler->priv = NULL;
  55         free(scheduler);
  56     }
  57 }
  58 
  59 #define XPATH_DEPRECATED_RULES                          \
  60     "//" PCMK_XE_OP_DEFAULTS "//" PCMK_XE_EXPRESSION    \
  61     "|//" PCMK_XE_OP "//" PCMK_XE_EXPRESSION
  62 
  63 /*!
  64  * \internal
  65  * \brief Log a warning for deprecated rule syntax in operations
  66  *
  67  * \param[in] scheduler  Scheduler data
  68  */
  69 static void
  70 check_for_deprecated_rules(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
  71 {
  72     // @COMPAT Drop this function when support for the syntax is dropped
  73     xmlNode *deprecated = get_xpath_object(XPATH_DEPRECATED_RULES,
  74                                            scheduler->input, LOG_NEVER);
  75 
  76     if (deprecated != NULL) {
  77         pcmk__warn_once(pcmk__wo_op_attr_expr,
  78                         "Support for rules with node attribute expressions in "
  79                         PCMK_XE_OP " or " PCMK_XE_OP_DEFAULTS " is deprecated "
  80                         "and will be dropped in a future release");
  81     }
  82 }
  83 
  84 /*
  85  * Unpack everything
  86  * At the end you'll have:
  87  *  - A list of nodes
  88  *  - A list of resources (each with any dependencies on other resources)
  89  *  - A list of constraints between resources and nodes
  90  *  - A list of constraints between start/stop actions
  91  *  - A list of nodes that need to be stonith'd
  92  *  - A list of nodes that need to be shutdown
  93  *  - A list of the possible stop/start actions (without dependencies)
  94  */
  95 gboolean
  96 cluster_status(pcmk_scheduler_t * scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
  97 {
  98     const char *new_version = NULL;
  99     xmlNode *section = NULL;
 100 
 101     if ((scheduler == NULL) || (scheduler->input == NULL)) {
 102         return FALSE;
 103     }
 104 
 105     new_version = crm_element_value(scheduler->input, PCMK_XA_CRM_FEATURE_SET);
 106 
 107     if (pcmk__check_feature_set(new_version) != pcmk_rc_ok) {
 108         pcmk__config_err("Can't process CIB with feature set '%s' greater than our own '%s'",
 109                          new_version, CRM_FEATURE_SET);
 110         return FALSE;
 111     }
 112 
 113     crm_trace("Beginning unpack");
 114 
 115     if (scheduler->failed != NULL) {
 116         free_xml(scheduler->failed);
 117     }
 118     scheduler->failed = pcmk__xe_create(NULL, "failed-ops");
 119 
 120     if (scheduler->now == NULL) {
 121         scheduler->now = crm_time_new(NULL);
 122     }
 123 
 124     if (scheduler->dc_uuid == NULL) {
 125         scheduler->dc_uuid = crm_element_value_copy(scheduler->input,
 126                                                     PCMK_XA_DC_UUID);
 127     }
 128 
 129     if (pcmk__xe_attr_is_true(scheduler->input, PCMK_XA_HAVE_QUORUM)) {
 130         pcmk__set_scheduler_flags(scheduler, pcmk_sched_quorate);
 131     } else {
 132         pcmk__clear_scheduler_flags(scheduler, pcmk_sched_quorate);
 133     }
 134 
 135     scheduler->op_defaults = get_xpath_object("//" PCMK_XE_OP_DEFAULTS,
 136                                               scheduler->input, LOG_NEVER);
 137     check_for_deprecated_rules(scheduler);
 138 
 139     scheduler->rsc_defaults = get_xpath_object("//" PCMK_XE_RSC_DEFAULTS,
 140                                                scheduler->input, LOG_NEVER);
 141 
 142     section = get_xpath_object("//" PCMK_XE_CRM_CONFIG, scheduler->input,
 143                                LOG_TRACE);
 144     unpack_config(section, scheduler);
 145 
 146    if (!pcmk_any_flags_set(scheduler->flags,
 147                            pcmk_sched_location_only|pcmk_sched_quorate)
 148        && (scheduler->no_quorum_policy != pcmk_no_quorum_ignore)) {
 149         pcmk__sched_warn("Fencing and resource management disabled "
 150                          "due to lack of quorum");
 151     }
 152 
 153     section = get_xpath_object("//" PCMK_XE_NODES, scheduler->input, LOG_TRACE);
 154     unpack_nodes(section, scheduler);
 155 
 156     section = get_xpath_object("//" PCMK_XE_RESOURCES, scheduler->input,
 157                                LOG_TRACE);
 158     if (!pcmk_is_set(scheduler->flags, pcmk_sched_location_only)) {
 159         unpack_remote_nodes(section, scheduler);
 160     }
 161     unpack_resources(section, scheduler);
 162 
 163     section = get_xpath_object("//" PCMK_XE_FENCING_TOPOLOGY, scheduler->input,
 164                                LOG_TRACE);
 165     pcmk__unpack_fencing_topology(section, scheduler);
 166 
 167     section = get_xpath_object("//" PCMK_XE_TAGS, scheduler->input, LOG_NEVER);
 168     unpack_tags(section, scheduler);
 169 
 170     if (!pcmk_is_set(scheduler->flags, pcmk_sched_location_only)) {
 171         section = get_xpath_object("//" PCMK_XE_STATUS, scheduler->input,
 172                                    LOG_TRACE);
 173         unpack_status(section, scheduler);
 174     }
 175 
 176     if (!pcmk_is_set(scheduler->flags, pcmk_sched_no_counts)) {
 177         for (GList *item = scheduler->resources; item != NULL;
 178              item = item->next) {
 179             ((pcmk_resource_t *) (item->data))->fns->count(item->data);
 180         }
 181         crm_trace("Cluster resource count: %d (%d disabled, %d blocked)",
 182                   scheduler->ninstances, scheduler->disabled_resources,
 183                   scheduler->blocked_resources);
 184     }
 185 
 186     pcmk__set_scheduler_flags(scheduler, pcmk_sched_have_status);
 187     return TRUE;
 188 }
 189 
 190 /*!
 191  * \internal
 192  * \brief Free a list of pcmk_resource_t
 193  *
 194  * \param[in,out] resources  List to free
 195  *
 196  * \note When the scheduler's resource list is freed, that includes the original
 197  *       storage for the uname and id of any Pacemaker Remote nodes in the
 198  *       scheduler's node list, so take care not to use those afterward.
 199  * \todo Refactor pcmk_node_t to strdup() the node name.
 200  */
 201 static void
 202 pe_free_resources(GList *resources)
     /* [previous][next][first][last][top][bottom][index][help] */
 203 {
 204     pcmk_resource_t *rsc = NULL;
 205     GList *iterator = resources;
 206 
 207     while (iterator != NULL) {
 208         rsc = (pcmk_resource_t *) iterator->data;
 209         iterator = iterator->next;
 210         rsc->fns->free(rsc);
 211     }
 212     if (resources != NULL) {
 213         g_list_free(resources);
 214     }
 215 }
 216 
 217 static void
 218 pe_free_actions(GList *actions)
     /* [previous][next][first][last][top][bottom][index][help] */
 219 {
 220     GList *iterator = actions;
 221 
 222     while (iterator != NULL) {
 223         pe_free_action(iterator->data);
 224         iterator = iterator->next;
 225     }
 226     if (actions != NULL) {
 227         g_list_free(actions);
 228     }
 229 }
 230 
 231 static void
 232 pe_free_nodes(GList *nodes)
     /* [previous][next][first][last][top][bottom][index][help] */
 233 {
 234     for (GList *iterator = nodes; iterator != NULL; iterator = iterator->next) {
 235         pcmk_node_t *node = (pcmk_node_t *) iterator->data;
 236 
 237         // Shouldn't be possible, but to be safe ...
 238         if (node == NULL) {
 239             continue;
 240         }
 241         if (node->details == NULL) {
 242             free(node);
 243             continue;
 244         }
 245 
 246         /* This is called after pe_free_resources(), which means that we can't
 247          * use node->details->uname for Pacemaker Remote nodes.
 248          */
 249         crm_trace("Freeing node %s", (pcmk__is_pacemaker_remote_node(node)?
 250                   "(guest or remote)" : pcmk__node_name(node)));
 251 
 252         if (node->details->attrs != NULL) {
 253             g_hash_table_destroy(node->details->attrs);
 254         }
 255         if (node->details->utilization != NULL) {
 256             g_hash_table_destroy(node->details->utilization);
 257         }
 258         if (node->details->digest_cache != NULL) {
 259             g_hash_table_destroy(node->details->digest_cache);
 260         }
 261         g_list_free(node->details->running_rsc);
 262         g_list_free(node->details->allocated_rsc);
 263         free(node->details);
 264         free(node);
 265     }
 266     if (nodes != NULL) {
 267         g_list_free(nodes);
 268     }
 269 }
 270 
 271 static void
 272 pe__free_ordering(GList *constraints)
     /* [previous][next][first][last][top][bottom][index][help] */
 273 {
 274     GList *iterator = constraints;
 275 
 276     while (iterator != NULL) {
 277         pcmk__action_relation_t *order = iterator->data;
 278 
 279         iterator = iterator->next;
 280 
 281         free(order->task1);
 282         free(order->task2);
 283         free(order);
 284     }
 285     if (constraints != NULL) {
 286         g_list_free(constraints);
 287     }
 288 }
 289 
 290 static void
 291 pe__free_location(GList *constraints)
     /* [previous][next][first][last][top][bottom][index][help] */
 292 {
 293     GList *iterator = constraints;
 294 
 295     while (iterator != NULL) {
 296         pcmk__location_t *cons = iterator->data;
 297 
 298         iterator = iterator->next;
 299 
 300         g_list_free_full(cons->nodes, free);
 301         free(cons->id);
 302         free(cons);
 303     }
 304     if (constraints != NULL) {
 305         g_list_free(constraints);
 306     }
 307 }
 308 
 309 /*!
 310  * \brief Reset scheduler data to defaults without freeing it or constraints
 311  *
 312  * \param[in,out] scheduler  Scheduler data to reset
 313  *
 314  * \deprecated This function is deprecated as part of the API;
 315  *             pe_reset_working_set() should be used instead.
 316  */
 317 void
 318 cleanup_calculations(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 319 {
 320     if (scheduler == NULL) {
 321         return;
 322     }
 323 
 324     pcmk__clear_scheduler_flags(scheduler, pcmk_sched_have_status);
 325     if (scheduler->config_hash != NULL) {
 326         g_hash_table_destroy(scheduler->config_hash);
 327     }
 328 
 329     if (scheduler->singletons != NULL) {
 330         g_hash_table_destroy(scheduler->singletons);
 331     }
 332 
 333     if (scheduler->tickets) {
 334         g_hash_table_destroy(scheduler->tickets);
 335     }
 336 
 337     if (scheduler->template_rsc_sets) {
 338         g_hash_table_destroy(scheduler->template_rsc_sets);
 339     }
 340 
 341     if (scheduler->tags) {
 342         g_hash_table_destroy(scheduler->tags);
 343     }
 344 
 345     free(scheduler->dc_uuid);
 346 
 347     crm_trace("deleting resources");
 348     pe_free_resources(scheduler->resources);
 349 
 350     crm_trace("deleting actions");
 351     pe_free_actions(scheduler->actions);
 352 
 353     crm_trace("deleting nodes");
 354     pe_free_nodes(scheduler->nodes);
 355 
 356     pe__free_param_checks(scheduler);
 357     g_list_free(scheduler->stop_needed);
 358     free_xml(scheduler->graph);
 359     crm_time_free(scheduler->now);
 360     free_xml(scheduler->input);
 361     free_xml(scheduler->failed);
 362 
 363     set_working_set_defaults(scheduler);
 364 
 365     CRM_CHECK(scheduler->ordering_constraints == NULL,;
 366         );
 367     CRM_CHECK(scheduler->placement_constraints == NULL,;
 368         );
 369 }
 370 
 371 /*!
 372  * \brief Reset scheduler data to default state without freeing it
 373  *
 374  * \param[in,out] scheduler  Scheduler data to reset
 375  */
 376 void
 377 pe_reset_working_set(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 378 {
 379     if (scheduler == NULL) {
 380         return;
 381     }
 382 
 383     crm_trace("Deleting %d ordering constraints",
 384               g_list_length(scheduler->ordering_constraints));
 385     pe__free_ordering(scheduler->ordering_constraints);
 386     scheduler->ordering_constraints = NULL;
 387 
 388     crm_trace("Deleting %d location constraints",
 389               g_list_length(scheduler->placement_constraints));
 390     pe__free_location(scheduler->placement_constraints);
 391     scheduler->placement_constraints = NULL;
 392 
 393     crm_trace("Deleting %d colocation constraints",
 394               g_list_length(scheduler->colocation_constraints));
 395     g_list_free_full(scheduler->colocation_constraints, free);
 396     scheduler->colocation_constraints = NULL;
 397 
 398     crm_trace("Deleting %d ticket constraints",
 399               g_list_length(scheduler->ticket_constraints));
 400     g_list_free_full(scheduler->ticket_constraints, free);
 401     scheduler->ticket_constraints = NULL;
 402 
 403     cleanup_calculations(scheduler);
 404 }
 405 
 406 void
 407 set_working_set_defaults(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 408 {
 409     void *priv = scheduler->priv;
 410 
 411     memset(scheduler, 0, sizeof(pcmk_scheduler_t));
 412 
 413     scheduler->priv = priv;
 414     scheduler->order_id = 1;
 415     scheduler->action_id = 1;
 416     scheduler->no_quorum_policy = pcmk_no_quorum_stop;
 417 
 418     scheduler->flags = 0x0ULL;
 419 
 420 #if PCMK__CONCURRENT_FENCING_DEFAULT_TRUE
 421     pcmk__set_scheduler_flags(scheduler,
 422                               pcmk_sched_symmetric_cluster
 423                               |pcmk_sched_concurrent_fencing
 424                               |pcmk_sched_stop_removed_resources
 425                               |pcmk_sched_cancel_removed_actions);
 426 #else
 427     pcmk__set_scheduler_flags(scheduler,
 428                               pcmk_sched_symmetric_cluster
 429                               |pcmk_sched_stop_removed_resources
 430                               |pcmk_sched_cancel_removed_actions);
 431 #endif
 432 }
 433 
 434 pcmk_resource_t *
 435 pe_find_resource(GList *rsc_list, const char *id)
     /* [previous][next][first][last][top][bottom][index][help] */
 436 {
 437     return pe_find_resource_with_flags(rsc_list, id, pcmk_rsc_match_history);
 438 }
 439 
 440 pcmk_resource_t *
 441 pe_find_resource_with_flags(GList *rsc_list, const char *id, enum pe_find flags)
     /* [previous][next][first][last][top][bottom][index][help] */
 442 {
 443     GList *rIter = NULL;
 444 
 445     for (rIter = rsc_list; id && rIter; rIter = rIter->next) {
 446         pcmk_resource_t *parent = rIter->data;
 447 
 448         pcmk_resource_t *match =
 449             parent->fns->find_rsc(parent, id, NULL, flags);
 450         if (match != NULL) {
 451             return match;
 452         }
 453     }
 454     crm_trace("No match for %s", id);
 455     return NULL;
 456 }
 457 
 458 /*!
 459  * \brief Find a node by name or ID in a list of nodes
 460  *
 461  * \param[in] nodes      List of nodes (as pcmk_node_t*)
 462  * \param[in] id         If not NULL, ID of node to find
 463  * \param[in] node_name  If not NULL, name of node to find
 464  *
 465  * \return Node from \p nodes that matches \p id if any,
 466  *         otherwise node from \p nodes that matches \p uname if any,
 467  *         otherwise NULL
 468  */
 469 pcmk_node_t *
 470 pe_find_node_any(const GList *nodes, const char *id, const char *uname)
     /* [previous][next][first][last][top][bottom][index][help] */
 471 {
 472     pcmk_node_t *match = NULL;
 473 
 474     if (id != NULL) {
 475         match = pe_find_node_id(nodes, id);
 476     }
 477     if ((match == NULL) && (uname != NULL)) {
 478         match = pcmk__find_node_in_list(nodes, uname);
 479     }
 480     return match;
 481 }
 482 
 483 /*!
 484  * \brief Find a node by ID in a list of nodes
 485  *
 486  * \param[in] nodes  List of nodes (as pcmk_node_t*)
 487  * \param[in] id     ID of node to find
 488  *
 489  * \return Node from \p nodes that matches \p id if any, otherwise NULL
 490  */
 491 pcmk_node_t *
 492 pe_find_node_id(const GList *nodes, const char *id)
     /* [previous][next][first][last][top][bottom][index][help] */
 493 {
 494     for (const GList *iter = nodes; iter != NULL; iter = iter->next) {
 495         pcmk_node_t *node = (pcmk_node_t *) iter->data;
 496 
 497         /* @TODO Whether node IDs should be considered case-sensitive should
 498          * probably depend on the node type, so functionizing the comparison
 499          * would be worthwhile
 500          */
 501         if (pcmk__str_eq(node->details->id, id, pcmk__str_casei)) {
 502             return node;
 503         }
 504     }
 505     return NULL;
 506 }
 507 
 508 // Deprecated functions kept only for backward API compatibility
 509 // LCOV_EXCL_START
 510 
 511 #include <crm/pengine/status_compat.h>
 512 
 513 /*!
 514  * \brief Find a node by name in a list of nodes
 515  *
 516  * \param[in] nodes      List of nodes (as pcmk_node_t*)
 517  * \param[in] node_name  Name of node to find
 518  *
 519  * \return Node from \p nodes that matches \p node_name if any, otherwise NULL
 520  */
 521 pcmk_node_t *
 522 pe_find_node(const GList *nodes, const char *node_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 523 {
 524     return pcmk__find_node_in_list(nodes, node_name);
 525 }
 526 
 527 // LCOV_EXCL_STOP
 528 // End deprecated API

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