root/lib/pengine/status.c

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

DEFINITIONS

This source file includes following definitions.
  1. check_for_deprecated_rules
  2. cluster_status
  3. pe_find_resource
  4. pe_find_resource_with_flags
  5. pe_find_node_any
  6. pe_find_node_id
  7. pe_new_working_set
  8. pe_reset_working_set
  9. cleanup_calculations
  10. set_working_set_defaults
  11. pe_free_working_set
  12. pe_find_node

   1 /*
   2  * Copyright 2004-2025 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <stdint.h>                     // uint32_t
  13 #include <sys/param.h>
  14 
  15 #include <glib.h>
  16 #include <libxml/tree.h>                // xmlNode
  17 
  18 #include <crm/crm.h>
  19 #include <crm/common/xml.h>
  20 #include <crm/common/cib_internal.h>
  21 
  22 #include <crm/pengine/internal.h>
  23 #include <pe_status_private.h>
  24 
  25 #define XPATH_DEPRECATED_RULES                          \
  26     "//" PCMK_XE_OP_DEFAULTS "//" PCMK_XE_EXPRESSION    \
  27     "|//" PCMK_XE_OP "//" PCMK_XE_EXPRESSION
  28 
  29 /*!
  30  * \internal
  31  * \brief Log a warning for deprecated rule syntax in operations
  32  *
  33  * \param[in] scheduler  Scheduler data
  34  */
  35 static void
  36 check_for_deprecated_rules(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
  37 {
  38     // @COMPAT Drop this function when support for the syntax is dropped
  39     xmlNode *deprecated = pcmk__xpath_find_one(scheduler->input->doc,
  40                                                XPATH_DEPRECATED_RULES,
  41                                                LOG_NEVER);
  42 
  43     if (deprecated != NULL) {
  44         pcmk__warn_once(pcmk__wo_op_attr_expr,
  45                         "Support for rules with node attribute expressions in "
  46                         PCMK_XE_OP " or " PCMK_XE_OP_DEFAULTS " is deprecated "
  47                         "and will be dropped in a future release");
  48     }
  49 }
  50 
  51 /*
  52  * Unpack everything
  53  * At the end you'll have:
  54  *  - A list of nodes
  55  *  - A list of resources (each with any dependencies on other resources)
  56  *  - A list of constraints between resources and nodes
  57  *  - A list of constraints between start/stop actions
  58  *  - A list of nodes that need to be stonith'd
  59  *  - A list of nodes that need to be shutdown
  60  *  - A list of the possible stop/start actions (without dependencies)
  61  *
  62  * @TODO Currently this function can modify scheduler->input by creating
  63  * primitive elements for guest nodes. Commit 5dbc819 aimed to make this
  64  * function idempotent. It seems to be idempotent now, except that if
  65  * scheduler->input is reused, it may gain duplicate primitive elements for
  66  * guest nodes each time.
  67  *
  68  * Investigate whether we can leave scheduler->input unmodified without a lot of
  69  * unnecessary XML copying. Otherwise, just document that scheduler->input may
  70  * be modified.
  71  */
  72 gboolean
  73 cluster_status(pcmk_scheduler_t * scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
  74 {
  75     // @TODO Deprecate, replacing with a safer public alternative if necessary
  76     const char *new_version = NULL;
  77     xmlNode *section = NULL;
  78 
  79     if ((scheduler == NULL) || (scheduler->input == NULL)) {
  80         return FALSE;
  81     }
  82 
  83     if (pcmk_is_set(scheduler->flags, pcmk__sched_have_status)) {
  84         /* cluster_status() has already been called since the last time the
  85          * scheduler was reset. Unpacking the input CIB again would cause
  86          * duplication within the scheduler object's data structures.
  87          *
  88          * The correct return code here is not obvious. Nothing internal checks
  89          * the code, however.
  90          */
  91         return TRUE;
  92     }
  93 
  94     new_version = crm_element_value(scheduler->input, PCMK_XA_CRM_FEATURE_SET);
  95 
  96     if (pcmk__check_feature_set(new_version) != pcmk_rc_ok) {
  97         pcmk__config_err("Can't process CIB with feature set '%s' greater than our own '%s'",
  98                          new_version, CRM_FEATURE_SET);
  99         return FALSE;
 100     }
 101 
 102     crm_trace("Beginning unpack");
 103 
 104     pcmk__xml_free(scheduler->priv->failed);
 105     scheduler->priv->failed = pcmk__xe_create(NULL, "failed-ops");
 106 
 107     if (scheduler->priv->now == NULL) {
 108         scheduler->priv->now = crm_time_new(NULL);
 109     }
 110 
 111     if (pcmk__xe_attr_is_true(scheduler->input, PCMK_XA_HAVE_QUORUM)) {
 112         pcmk__set_scheduler_flags(scheduler, pcmk__sched_quorate);
 113     } else {
 114         pcmk__clear_scheduler_flags(scheduler, pcmk__sched_quorate);
 115     }
 116 
 117     scheduler->priv->op_defaults =
 118         pcmk__xpath_find_one(scheduler->input->doc, "//" PCMK_XE_OP_DEFAULTS,
 119                              LOG_NEVER);
 120     check_for_deprecated_rules(scheduler);
 121 
 122     scheduler->priv->rsc_defaults =
 123         pcmk__xpath_find_one(scheduler->input->doc, "//" PCMK_XE_RSC_DEFAULTS,
 124                              LOG_NEVER);
 125 
 126     section = pcmk__xpath_find_one(scheduler->input->doc,
 127                                    "//" PCMK_XE_CRM_CONFIG, LOG_TRACE);
 128     unpack_config(section, scheduler);
 129 
 130    if (!pcmk_any_flags_set(scheduler->flags,
 131                            pcmk__sched_location_only|pcmk__sched_quorate)
 132        && (scheduler->no_quorum_policy != pcmk_no_quorum_ignore)) {
 133         pcmk__sched_warn(scheduler,
 134                          "Fencing and resource management disabled "
 135                          "due to lack of quorum");
 136     }
 137 
 138     section = pcmk__xpath_find_one(scheduler->input->doc, "//" PCMK_XE_NODES,
 139                                    LOG_TRACE);
 140     unpack_nodes(section, scheduler);
 141 
 142     section = pcmk__xpath_find_one(scheduler->input->doc,
 143                                    "//" PCMK_XE_RESOURCES, LOG_TRACE);
 144     if (!pcmk_is_set(scheduler->flags, pcmk__sched_location_only)) {
 145         unpack_remote_nodes(section, scheduler);
 146     }
 147     unpack_resources(section, scheduler);
 148 
 149     section = pcmk__xpath_find_one(scheduler->input->doc,
 150                                    "//" PCMK_XE_FENCING_TOPOLOGY, LOG_TRACE);
 151     pcmk__validate_fencing_topology(section);
 152 
 153     section = pcmk__xpath_find_one(scheduler->input->doc, "//" PCMK_XE_TAGS,
 154                                    LOG_NEVER);
 155     unpack_tags(section, scheduler);
 156 
 157     if (!pcmk_is_set(scheduler->flags, pcmk__sched_location_only)) {
 158         section = pcmk__xpath_find_one(scheduler->input->doc,
 159                                        "//" PCMK_XE_STATUS, LOG_TRACE);
 160         unpack_status(section, scheduler);
 161     }
 162 
 163     if (!pcmk_is_set(scheduler->flags, pcmk__sched_no_counts)) {
 164         for (GList *item = scheduler->priv->resources;
 165              item != NULL; item = item->next) {
 166 
 167             pcmk_resource_t *rsc = item->data;
 168 
 169             rsc->priv->fns->count(item->data);
 170         }
 171         crm_trace("Cluster resource count: %d (%d disabled, %d blocked)",
 172                   scheduler->priv->ninstances,
 173                   scheduler->priv->disabled_resources,
 174                   scheduler->priv->blocked_resources);
 175     }
 176 
 177     if ((scheduler->priv->local_node_name != NULL)
 178         && (pcmk_find_node(scheduler,
 179                            scheduler->priv->local_node_name) == NULL)) {
 180         crm_info("Creating a fake local node for %s",
 181                  scheduler->priv->local_node_name);
 182         pe_create_node(scheduler->priv->local_node_name,
 183                        scheduler->priv->local_node_name, NULL, 0, scheduler);
 184     }
 185 
 186     pcmk__set_scheduler_flags(scheduler, pcmk__sched_have_status);
 187     return TRUE;
 188 }
 189 
 190 pcmk_resource_t *
 191 pe_find_resource(GList *rsc_list, const char *id)
     /* [previous][next][first][last][top][bottom][index][help] */
 192 {
 193     return pe_find_resource_with_flags(rsc_list, id, pcmk_rsc_match_history);
 194 }
 195 
 196 pcmk_resource_t *
 197 pe_find_resource_with_flags(GList *rsc_list, const char *id, enum pe_find flags)
     /* [previous][next][first][last][top][bottom][index][help] */
 198 {
 199     GList *rIter = NULL;
 200 
 201     for (rIter = rsc_list; id && rIter; rIter = rIter->next) {
 202         pcmk_resource_t *parent = rIter->data;
 203         pcmk_resource_t *match = parent->priv->fns->find_rsc(parent, id, NULL,
 204                                                              (uint32_t) flags);
 205 
 206         if (match != NULL) {
 207             return match;
 208         }
 209     }
 210     crm_trace("No match for %s", id);
 211     return NULL;
 212 }
 213 
 214 /*!
 215  * \brief Find a node by name or ID in a list of nodes
 216  *
 217  * \param[in] nodes      List of nodes (as pcmk_node_t*)
 218  * \param[in] id         If not NULL, ID of node to find
 219  * \param[in] node_name  If not NULL, name of node to find
 220  *
 221  * \return Node from \p nodes that matches \p id if any,
 222  *         otherwise node from \p nodes that matches \p uname if any,
 223  *         otherwise NULL
 224  */
 225 pcmk_node_t *
 226 pe_find_node_any(const GList *nodes, const char *id, const char *uname)
     /* [previous][next][first][last][top][bottom][index][help] */
 227 {
 228     pcmk_node_t *match = NULL;
 229 
 230     if (id != NULL) {
 231         match = pe_find_node_id(nodes, id);
 232     }
 233     if ((match == NULL) && (uname != NULL)) {
 234         match = pcmk__find_node_in_list(nodes, uname);
 235     }
 236     return match;
 237 }
 238 
 239 /*!
 240  * \brief Find a node by ID in a list of nodes
 241  *
 242  * \param[in] nodes  List of nodes (as pcmk_node_t*)
 243  * \param[in] id     ID of node to find
 244  *
 245  * \return Node from \p nodes that matches \p id if any, otherwise NULL
 246  */
 247 pcmk_node_t *
 248 pe_find_node_id(const GList *nodes, const char *id)
     /* [previous][next][first][last][top][bottom][index][help] */
 249 {
 250     for (const GList *iter = nodes; iter != NULL; iter = iter->next) {
 251         pcmk_node_t *node = (pcmk_node_t *) iter->data;
 252 
 253         /* @TODO Whether node IDs should be considered case-sensitive should
 254          * probably depend on the node type, so functionizing the comparison
 255          * would be worthwhile
 256          */
 257         if (pcmk__str_eq(node->priv->id, id, pcmk__str_casei)) {
 258             return node;
 259         }
 260     }
 261     return NULL;
 262 }
 263 
 264 // Deprecated functions kept only for backward API compatibility
 265 // LCOV_EXCL_START
 266 
 267 #include <crm/pengine/status_compat.h>
 268 
 269 pcmk_scheduler_t *
 270 pe_new_working_set(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 271 {
 272     return pcmk_new_scheduler();
 273 }
 274 
 275 void
 276 pe_reset_working_set(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 277 {
 278     if (scheduler == NULL) {
 279         return;
 280     }
 281     pcmk_reset_scheduler(scheduler);
 282 }
 283 
 284 void
 285 cleanup_calculations(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 286 {
 287     if (scheduler == NULL) {
 288         return;
 289     }
 290 
 291     pcmk__clear_scheduler_flags(scheduler, pcmk__sched_have_status);
 292     if (scheduler->priv->options != NULL) {
 293         g_hash_table_destroy(scheduler->priv->options);
 294     }
 295 
 296     if (scheduler->priv->singletons != NULL) {
 297         g_hash_table_destroy(scheduler->priv->singletons);
 298     }
 299 
 300     if (scheduler->priv->ticket_constraints != NULL) {
 301         g_hash_table_destroy(scheduler->priv->ticket_constraints);
 302     }
 303 
 304     if (scheduler->priv->templates != NULL) {
 305         g_hash_table_destroy(scheduler->priv->templates);
 306     }
 307 
 308     if (scheduler->priv->tags != NULL) {
 309         g_hash_table_destroy(scheduler->priv->tags);
 310     }
 311 
 312     crm_trace("deleting resources");
 313     g_list_free_full(scheduler->priv->resources, pcmk__free_resource);
 314 
 315     crm_trace("deleting actions");
 316     g_list_free_full(scheduler->priv->actions, pcmk__free_action);
 317 
 318     crm_trace("deleting nodes");
 319     g_list_free_full(scheduler->nodes, pcmk__free_node);
 320     scheduler->nodes = NULL;
 321 
 322     pcmk__free_param_checks(scheduler);
 323     g_list_free(scheduler->priv->stop_needed);
 324     crm_time_free(scheduler->priv->now);
 325     pcmk__xml_free(scheduler->input);
 326     pcmk__xml_free(scheduler->priv->failed);
 327     pcmk__xml_free(scheduler->priv->graph);
 328 
 329     set_working_set_defaults(scheduler);
 330 
 331     CRM_LOG_ASSERT((scheduler->priv->location_constraints == NULL)
 332                    && (scheduler->priv->ordering_constraints == NULL));
 333 }
 334 
 335 void
 336 set_working_set_defaults(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 337 {
 338     // These members must be preserved
 339     pcmk__scheduler_private_t *priv = scheduler->priv;
 340     pcmk__output_t *out = priv->out;
 341     char *local_node_name = scheduler->priv->local_node_name;
 342 
 343     // Wipe the main structs (any other members must have previously been freed)
 344     memset(scheduler, 0, sizeof(pcmk_scheduler_t));
 345     memset(priv, 0, sizeof(pcmk__scheduler_private_t));
 346 
 347     // Restore the members to preserve
 348     scheduler->priv = priv;
 349     scheduler->priv->out = out;
 350     scheduler->priv->local_node_name = local_node_name;
 351 
 352     // Set defaults for everything else
 353     pcmk__set_scheduler_defaults(scheduler);
 354 }
 355 
 356 void
 357 pe_free_working_set(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 358 {
 359     pcmk_free_scheduler(scheduler);
 360 }
 361 
 362 pcmk_node_t *
 363 pe_find_node(const GList *nodes, const char *node_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 364 {
 365     return pcmk__find_node_in_list(nodes, node_name);
 366 }
 367 
 368 // LCOV_EXCL_STOP
 369 // End deprecated API

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