root/lib/common/scheduler.c

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

DEFINITIONS

This source file includes following definitions.
  1. pcmk_new_scheduler
  2. pcmk__set_scheduler_defaults
  3. pcmk_reset_scheduler
  4. pcmk_free_scheduler
  5. pcmk_get_dc
  6. pcmk_get_no_quorum_policy
  7. pcmk_set_scheduler_cib
  8. pcmk_has_quorum
  9. pcmk_find_node
  10. pcmk__scheduler_epoch_time
  11. pcmk__update_recheck_time
  12. pcmk__add_param_check
  13. pcmk__foreach_param_check
  14. pcmk__free_param_checks

   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 <errno.h>              // EINVAL
  14 #include <glib.h>               // gboolean, FALSE, etc.
  15 #include <libxml/tree.h>        // xmlNode
  16 
  17 #include <crm/common/scheduler.h>
  18 
  19 uint32_t pcmk__warnings = 0;
  20 
  21 /*!
  22  * \brief Create a new object to hold scheduler data
  23  *
  24  * \return New, initialized scheduler data, or NULL on memory error
  25  * \note Only pcmk_scheduler_t objects created with this function (as opposed
  26  *       to statically declared or directly allocated) should be used with the
  27  *       functions in this library, to allow for future extensions to the
  28  *       data type. The caller is responsible for freeing the memory with
  29  *       pcmk_free_scheduler() when the instance is no longer needed.
  30  */
  31 pcmk_scheduler_t *
  32 pcmk_new_scheduler(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  33 {
  34     pcmk_scheduler_t *scheduler = calloc(1, sizeof(pcmk_scheduler_t));
  35 
  36     if (scheduler == NULL) {
  37         return NULL;
  38     }
  39     scheduler->priv = calloc(1, sizeof(pcmk__scheduler_private_t));
  40     if (scheduler->priv == NULL) {
  41         free(scheduler);
  42         return NULL;
  43     }
  44     pcmk__set_scheduler_defaults(scheduler);
  45     return scheduler;
  46 }
  47 
  48 /*!
  49  * \internal
  50  * \brief Set non-zero default values in scheduler data
  51  *
  52  * \param[in,out] scheduler  Scheduler data to modify
  53  *
  54  * \note Values that default to NULL or 0 will remain unchanged
  55  */
  56 void
  57 pcmk__set_scheduler_defaults(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
  58 {
  59     pcmk__assert(scheduler != NULL);
  60     scheduler->flags = 0U;
  61 #if PCMK__CONCURRENT_FENCING_DEFAULT_TRUE
  62     pcmk__set_scheduler_flags(scheduler,
  63                               pcmk__sched_symmetric_cluster
  64                               |pcmk__sched_concurrent_fencing
  65                               |pcmk__sched_stop_removed_resources
  66                               |pcmk__sched_cancel_removed_actions);
  67 #else
  68     pcmk__set_scheduler_flags(scheduler,
  69                               pcmk__sched_symmetric_cluster
  70                               |pcmk__sched_stop_removed_resources
  71                               |pcmk__sched_cancel_removed_actions);
  72 #endif
  73     scheduler->no_quorum_policy = pcmk_no_quorum_stop;
  74     scheduler->priv->next_action_id = 1;
  75     scheduler->priv->next_ordering_id = 1;
  76 }
  77 
  78 /*!
  79  * \brief Reset scheduler data to defaults
  80  *
  81  * Free scheduler data except the local node name and output object, and reset
  82  * all other values to defaults, so the data is suitable for rerunning status
  83  *
  84  * \param[in,out] scheduler  Scheduler data to reset
  85  */
  86 void
  87 pcmk_reset_scheduler(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
  88 {
  89     if (scheduler == NULL) {
  90         return;
  91     }
  92 
  93     /* Be careful about the order of freeing members. Many contain references to
  94      * other members that will become dangling if those members are freed first.
  95      * For example, the node name and ID of Pacemaker Remote nodes are pointers
  96      * into resource objects. Ensure that earlier-freed members are not needed
  97      * by any of the free functions for later-freed members.
  98      */
  99 
 100     scheduler->dc_node = NULL;
 101 
 102     g_list_free_full(scheduler->nodes, pcmk__free_node);
 103     scheduler->nodes = NULL;
 104 
 105     // Do not reset local_node_name or out
 106 
 107     crm_time_free(scheduler->priv->now);
 108     scheduler->priv->now = NULL;
 109 
 110     if (scheduler->priv->options != NULL) {
 111         g_hash_table_destroy(scheduler->priv->options);
 112         scheduler->priv->options = NULL;
 113     }
 114 
 115     scheduler->priv->fence_action = NULL;
 116     scheduler->priv->fence_timeout_ms = 0U;
 117     scheduler->priv->priority_fencing_ms = 0U;
 118     scheduler->priv->shutdown_lock_ms = 0U;
 119     scheduler->priv->node_pending_ms = 0U;
 120     scheduler->priv->placement_strategy = NULL;
 121     scheduler->priv->rsc_defaults = NULL;
 122     scheduler->priv->op_defaults = NULL;
 123 
 124     g_list_free_full(scheduler->priv->resources, pcmk__free_resource);
 125     scheduler->priv->resources = NULL;
 126 
 127     if (scheduler->priv->templates != NULL) {
 128         g_hash_table_destroy(scheduler->priv->templates);
 129         scheduler->priv->templates = NULL;
 130     }
 131     if (scheduler->priv->tags != NULL) {
 132         g_hash_table_destroy(scheduler->priv->tags);
 133         scheduler->priv->tags = NULL;
 134     }
 135 
 136     g_list_free_full(scheduler->priv->actions, pcmk__free_action);
 137     scheduler->priv->actions = NULL;
 138 
 139     if (scheduler->priv->singletons != NULL) {
 140         g_hash_table_destroy(scheduler->priv->singletons);
 141         scheduler->priv->singletons = NULL;
 142     }
 143 
 144     pcmk__xml_free(scheduler->priv->failed);
 145     scheduler->priv->failed = NULL;
 146 
 147     pcmk__free_param_checks(scheduler);
 148 
 149     g_list_free(scheduler->priv->stop_needed);
 150     scheduler->priv->stop_needed = NULL;
 151 
 152     g_list_free_full(scheduler->priv->location_constraints,
 153                      pcmk__free_location);
 154     scheduler->priv->location_constraints = NULL;
 155 
 156     g_list_free_full(scheduler->priv->colocation_constraints, free);
 157     scheduler->priv->colocation_constraints = NULL;
 158 
 159     g_list_free_full(scheduler->priv->ordering_constraints,
 160                      pcmk__free_action_relation);
 161     scheduler->priv->ordering_constraints = NULL;
 162 
 163     if (scheduler->priv->ticket_constraints != NULL) {
 164         g_hash_table_destroy(scheduler->priv->ticket_constraints);
 165         scheduler->priv->ticket_constraints = NULL;
 166     }
 167 
 168     scheduler->priv->ninstances = 0;
 169     scheduler->priv->blocked_resources = 0;
 170     scheduler->priv->disabled_resources = 0;
 171     scheduler->priv->recheck_by = 0;
 172 
 173     pcmk__xml_free(scheduler->priv->graph);
 174     scheduler->priv->graph = NULL;
 175 
 176     scheduler->priv->synapse_count = 0;
 177 
 178     pcmk__xml_free(scheduler->input);
 179     scheduler->input = NULL;
 180 
 181     pcmk__set_scheduler_defaults(scheduler);
 182 
 183     pcmk__config_has_error = false;
 184     pcmk__config_has_warning = false;
 185 }
 186 
 187 /*!
 188  * \brief Free scheduler data
 189  *
 190  * \param[in,out] scheduler  Scheduler data to free
 191  */
 192 void
 193 pcmk_free_scheduler(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 194 {
 195     if (scheduler != NULL) {
 196         pcmk_reset_scheduler(scheduler);
 197         free(scheduler->priv->local_node_name);
 198         free(scheduler->priv);
 199         free(scheduler);
 200     }
 201 }
 202 
 203 /*!
 204  * \internal
 205  * \brief Get the Designated Controller node from scheduler data
 206  *
 207  * \param[in] scheduler  Scheduler data
 208  *
 209  * \return Designated Controller node from scheduler data, or NULL if none
 210  */
 211 pcmk_node_t *
 212 pcmk_get_dc(const pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 213 {
 214     return (scheduler == NULL)? NULL : scheduler->dc_node;
 215 }
 216 
 217 /*!
 218  * \internal
 219  * \brief Get the no quorum policy from scheduler data
 220  *
 221  * \param[in] scheduler  Scheduler data
 222  *
 223  * \return No quorum policy from scheduler data
 224  */
 225 enum pe_quorum_policy
 226 pcmk_get_no_quorum_policy(const pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 227 {
 228     if (scheduler == NULL) {
 229         return pcmk_no_quorum_stop; // The default
 230     }
 231     return scheduler->no_quorum_policy;
 232 }
 233 
 234 /*!
 235  * \internal
 236  * \brief Set CIB XML as scheduler input in scheduler data
 237  *
 238  * \param[out] scheduler  Scheduler data
 239  * \param[in]  cib        CIB XML to set as scheduler input
 240  *
 241  * \return Standard Pacemaker return code (EINVAL if \p scheduler is NULL,
 242  *         otherwise pcmk_rc_ok)
 243  * \note This will not free any previously set scheduler CIB.
 244  */
 245 int
 246 pcmk_set_scheduler_cib(pcmk_scheduler_t *scheduler, xmlNode *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 247 {
 248     if (scheduler == NULL) {
 249         return EINVAL;
 250     }
 251     scheduler->input = cib;
 252     return pcmk_rc_ok;
 253 }
 254 
 255 /*!
 256  * \internal
 257  * \brief Check whether cluster has quorum
 258  *
 259  * \param[in] scheduler  Scheduler data
 260  *
 261  * \return true if cluster has quorum, otherwise false
 262  */
 263 bool
 264 pcmk_has_quorum(const pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 265 {
 266     if (scheduler == NULL) {
 267         return false;
 268     }
 269     return pcmk_is_set(scheduler->flags, pcmk__sched_quorate);
 270 }
 271 
 272 /*!
 273  * \brief Find a node by name in scheduler data
 274  *
 275  * \param[in] scheduler  Scheduler data
 276  * \param[in] node_name  Name of node to find
 277  *
 278  * \return Node from scheduler data that matches \p node_name if any,
 279  *         otherwise NULL
 280  */
 281 pcmk_node_t *
 282 pcmk_find_node(const pcmk_scheduler_t *scheduler, const char *node_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 283 {
 284     if ((scheduler == NULL) || (node_name == NULL)) {
 285         return NULL;
 286     }
 287     return pcmk__find_node_in_list(scheduler->nodes, node_name);
 288 }
 289 
 290 /*!
 291  * \internal
 292  * \brief Get scheduler data's "now" in epoch time
 293  *
 294  * \param[in,out] scheduler  Scheduler data
 295  *
 296  * \return Scheduler data's "now" as seconds since epoch (defaulting to current
 297  *         time)
 298  */
 299 time_t
 300 pcmk__scheduler_epoch_time(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 301 {
 302     if (scheduler == NULL) {
 303         return time(NULL);
 304     }
 305     if (scheduler->priv->now == NULL) {
 306         crm_trace("Scheduler 'now' set to current time");
 307         scheduler->priv->now = crm_time_new(NULL);
 308     }
 309     return crm_time_get_seconds_since_epoch(scheduler->priv->now);
 310 }
 311 
 312 /*!
 313  * \internal
 314  * \brief Update "recheck by" time in scheduler data
 315  *
 316  * \param[in]     recheck    Epoch time when recheck should happen
 317  * \param[in,out] scheduler  Scheduler data
 318  * \param[in]     reason     What time is being updated for (for logs)
 319  */
 320 void
 321 pcmk__update_recheck_time(time_t recheck, pcmk_scheduler_t *scheduler,
     /* [previous][next][first][last][top][bottom][index][help] */
 322                           const char *reason)
 323 {
 324     pcmk__assert(scheduler != NULL);
 325 
 326     if ((recheck > pcmk__scheduler_epoch_time(scheduler))
 327         && ((scheduler->priv->recheck_by == 0)
 328             || (scheduler->priv->recheck_by > recheck))) {
 329         scheduler->priv->recheck_by = recheck;
 330         crm_debug("Updated next scheduler recheck to %s for %s",
 331                   pcmk__trim(ctime(&recheck)),
 332                   pcmk__s(reason, "some reason"));
 333     }
 334 }
 335 
 336 /* Fail count clearing for parameter changes normally happens when unpacking
 337  * history, before resources are unpacked. However, for bundles using the
 338  * REMOTE_CONTAINER_HACK, we can't check the conditions until after unpacking
 339  * the bundle, so those parameter checks are deferred using the APIs below.
 340  */
 341 
 342 // History entry to be checked later for fail count clearing
 343 struct param_check {
 344     const xmlNode *rsc_history; // History entry XML
 345     pcmk_resource_t *rsc;       // Resource corresponding to history entry
 346     pcmk_node_t *node;          // Node corresponding to history entry
 347     enum pcmk__check_parameters check_type; // What needs checking
 348 };
 349 
 350 /*!
 351  * \internal
 352  * \brief Add a deferred parameter check
 353  *
 354  * \param[in]     rsc_history  Resource history XML to check later
 355  * \param[in,out] rsc          Resource that history is for
 356  * \param[in]     node         Node that history is for
 357  * \param[in]     flag         What needs to be checked later
 358  */
 359 void
 360 pcmk__add_param_check(const xmlNode *rsc_history, pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 361                       pcmk_node_t *node, enum pcmk__check_parameters flag)
 362 {
 363     struct param_check *param_check = NULL;
 364 
 365     CRM_CHECK((rsc_history != NULL) && (rsc != NULL) && (node != NULL), return);
 366 
 367     crm_trace("Deferring checks of %s until after assignment",
 368               pcmk__xe_id(rsc_history));
 369     param_check = pcmk__assert_alloc(1, sizeof(struct param_check));
 370     param_check->rsc_history = rsc_history;
 371     param_check->rsc = rsc;
 372     param_check->node = node;
 373     param_check->check_type = flag;
 374 
 375     rsc->priv->scheduler->priv->param_check =
 376         g_list_prepend(rsc->priv->scheduler->priv->param_check, param_check);
 377 }
 378 
 379 /*!
 380  * \internal
 381  * \brief Call a function for each deferred parameter check
 382  *
 383  * \param[in,out] scheduler  Scheduler data
 384  * \param[in]     cb         Function to be called
 385  */
 386 void
 387 pcmk__foreach_param_check(pcmk_scheduler_t *scheduler,
     /* [previous][next][first][last][top][bottom][index][help] */
 388                           void (*cb)(pcmk_resource_t*, pcmk_node_t*,
 389                                      const xmlNode*,
 390                                      enum pcmk__check_parameters))
 391 {
 392     CRM_CHECK((scheduler != NULL) && (cb != NULL), return);
 393 
 394     for (GList *item = scheduler->priv->param_check;
 395          item != NULL; item = item->next) {
 396         struct param_check *param_check = item->data;
 397 
 398         cb(param_check->rsc, param_check->node, param_check->rsc_history,
 399            param_check->check_type);
 400     }
 401 }
 402 
 403 /*!
 404  * \internal
 405  * \brief Free all deferred parameter checks
 406  *
 407  * \param[in,out] scheduler  Scheduler data
 408  */
 409 void
 410 pcmk__free_param_checks(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 411 {
 412     if ((scheduler != NULL) && (scheduler->priv->param_check != NULL)) {
 413         g_list_free_full(scheduler->priv->param_check, free);
 414         scheduler->priv->param_check = NULL;
 415     }
 416 }

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