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     pcmk__set_scheduler_flags(scheduler,
 421                               pcmk_sched_symmetric_cluster
 422                               |pcmk_sched_stop_removed_resources
 423                               |pcmk_sched_cancel_removed_actions);
 424     if (!strcmp(PCMK__CONCURRENT_FENCING_DEFAULT, PCMK_VALUE_TRUE)) {
 425         pcmk__set_scheduler_flags(scheduler, pcmk_sched_concurrent_fencing);
 426     }
 427 }
 428 
 429 pcmk_resource_t *
 430 pe_find_resource(GList *rsc_list, const char *id)
     /* [previous][next][first][last][top][bottom][index][help] */
 431 {
 432     return pe_find_resource_with_flags(rsc_list, id, pcmk_rsc_match_history);
 433 }
 434 
 435 pcmk_resource_t *
 436 pe_find_resource_with_flags(GList *rsc_list, const char *id, enum pe_find flags)
     /* [previous][next][first][last][top][bottom][index][help] */
 437 {
 438     GList *rIter = NULL;
 439 
 440     for (rIter = rsc_list; id && rIter; rIter = rIter->next) {
 441         pcmk_resource_t *parent = rIter->data;
 442 
 443         pcmk_resource_t *match =
 444             parent->fns->find_rsc(parent, id, NULL, flags);
 445         if (match != NULL) {
 446             return match;
 447         }
 448     }
 449     crm_trace("No match for %s", id);
 450     return NULL;
 451 }
 452 
 453 /*!
 454  * \brief Find a node by name or ID in a list of nodes
 455  *
 456  * \param[in] nodes      List of nodes (as pcmk_node_t*)
 457  * \param[in] id         If not NULL, ID of node to find
 458  * \param[in] node_name  If not NULL, name of node to find
 459  *
 460  * \return Node from \p nodes that matches \p id if any,
 461  *         otherwise node from \p nodes that matches \p uname if any,
 462  *         otherwise NULL
 463  */
 464 pcmk_node_t *
 465 pe_find_node_any(const GList *nodes, const char *id, const char *uname)
     /* [previous][next][first][last][top][bottom][index][help] */
 466 {
 467     pcmk_node_t *match = NULL;
 468 
 469     if (id != NULL) {
 470         match = pe_find_node_id(nodes, id);
 471     }
 472     if ((match == NULL) && (uname != NULL)) {
 473         match = pcmk__find_node_in_list(nodes, uname);
 474     }
 475     return match;
 476 }
 477 
 478 /*!
 479  * \brief Find a node by ID in a list of nodes
 480  *
 481  * \param[in] nodes  List of nodes (as pcmk_node_t*)
 482  * \param[in] id     ID of node to find
 483  *
 484  * \return Node from \p nodes that matches \p id if any, otherwise NULL
 485  */
 486 pcmk_node_t *
 487 pe_find_node_id(const GList *nodes, const char *id)
     /* [previous][next][first][last][top][bottom][index][help] */
 488 {
 489     for (const GList *iter = nodes; iter != NULL; iter = iter->next) {
 490         pcmk_node_t *node = (pcmk_node_t *) iter->data;
 491 
 492         /* @TODO Whether node IDs should be considered case-sensitive should
 493          * probably depend on the node type, so functionizing the comparison
 494          * would be worthwhile
 495          */
 496         if (pcmk__str_eq(node->details->id, id, pcmk__str_casei)) {
 497             return node;
 498         }
 499     }
 500     return NULL;
 501 }
 502 
 503 // Deprecated functions kept only for backward API compatibility
 504 // LCOV_EXCL_START
 505 
 506 #include <crm/pengine/status_compat.h>
 507 
 508 /*!
 509  * \brief Find a node by name in a list of nodes
 510  *
 511  * \param[in] nodes      List of nodes (as pcmk_node_t*)
 512  * \param[in] node_name  Name of node to find
 513  *
 514  * \return Node from \p nodes that matches \p node_name if any, otherwise NULL
 515  */
 516 pcmk_node_t *
 517 pe_find_node(const GList *nodes, const char *node_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 518 {
 519     return pcmk__find_node_in_list(nodes, node_name);
 520 }
 521 
 522 // LCOV_EXCL_STOP
 523 // End deprecated API

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