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. cluster_status
  4. pe_free_resources
  5. pe_free_actions
  6. pe_free_nodes
  7. pe__free_ordering
  8. pe__free_location
  9. cleanup_calculations
  10. pe_reset_working_set
  11. set_working_set_defaults
  12. pe_find_resource
  13. pe_find_resource_with_flags
  14. pe_find_node_any
  15. pe_find_node_id
  16. pe_find_node

   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 <sys/param.h>
  13 
  14 #include <crm/crm.h>
  15 #include <crm/msg_xml.h>
  16 #include <crm/common/xml.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 /*
  60  * Unpack everything
  61  * At the end you'll have:
  62  *  - A list of nodes
  63  *  - A list of resources (each with any dependencies on other resources)
  64  *  - A list of constraints between resources and nodes
  65  *  - A list of constraints between start/stop actions
  66  *  - A list of nodes that need to be stonith'd
  67  *  - A list of nodes that need to be shutdown
  68  *  - A list of the possible stop/start actions (without dependencies)
  69  */
  70 gboolean
  71 cluster_status(pcmk_scheduler_t * scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
  72 {
  73     xmlNode *section = NULL;
  74 
  75     if ((scheduler == NULL) || (scheduler->input == NULL)) {
  76         return FALSE;
  77     }
  78 
  79     crm_trace("Beginning unpack");
  80 
  81     if (scheduler->failed != NULL) {
  82         free_xml(scheduler->failed);
  83     }
  84     scheduler->failed = create_xml_node(NULL, "failed-ops");
  85 
  86     if (scheduler->now == NULL) {
  87         scheduler->now = crm_time_new(NULL);
  88     }
  89 
  90     if (scheduler->dc_uuid == NULL) {
  91         scheduler->dc_uuid = crm_element_value_copy(scheduler->input,
  92                                                     XML_ATTR_DC_UUID);
  93     }
  94 
  95     if (pcmk__xe_attr_is_true(scheduler->input, XML_ATTR_HAVE_QUORUM)) {
  96         pe__set_working_set_flags(scheduler, pcmk_sched_quorate);
  97     } else {
  98         pe__clear_working_set_flags(scheduler, pcmk_sched_quorate);
  99     }
 100 
 101     scheduler->op_defaults = get_xpath_object("//" XML_CIB_TAG_OPCONFIG,
 102                                               scheduler->input, LOG_NEVER);
 103     scheduler->rsc_defaults = get_xpath_object("//" XML_CIB_TAG_RSCCONFIG,
 104                                                scheduler->input, LOG_NEVER);
 105 
 106     section = get_xpath_object("//" XML_CIB_TAG_CRMCONFIG, scheduler->input,
 107                                LOG_TRACE);
 108     unpack_config(section, scheduler);
 109 
 110    if (!pcmk_any_flags_set(scheduler->flags,
 111                            pcmk_sched_location_only|pcmk_sched_quorate)
 112        && (scheduler->no_quorum_policy != pcmk_no_quorum_ignore)) {
 113         crm_warn("Fencing and resource management disabled due to lack of quorum");
 114     }
 115 
 116     section = get_xpath_object("//" XML_CIB_TAG_NODES, scheduler->input,
 117                                LOG_TRACE);
 118     unpack_nodes(section, scheduler);
 119 
 120     section = get_xpath_object("//" XML_CIB_TAG_RESOURCES, scheduler->input,
 121                                LOG_TRACE);
 122     if (!pcmk_is_set(scheduler->flags, pcmk_sched_location_only)) {
 123         unpack_remote_nodes(section, scheduler);
 124     }
 125     unpack_resources(section, scheduler);
 126 
 127     section = get_xpath_object("//" XML_CIB_TAG_TAGS, scheduler->input,
 128                                LOG_NEVER);
 129     unpack_tags(section, scheduler);
 130 
 131     if (!pcmk_is_set(scheduler->flags, pcmk_sched_location_only)) {
 132         section = get_xpath_object("//"XML_CIB_TAG_STATUS, scheduler->input,
 133                                    LOG_TRACE);
 134         unpack_status(section, scheduler);
 135     }
 136 
 137     if (!pcmk_is_set(scheduler->flags, pcmk_sched_no_counts)) {
 138         for (GList *item = scheduler->resources; item != NULL;
 139              item = item->next) {
 140             ((pcmk_resource_t *) (item->data))->fns->count(item->data);
 141         }
 142         crm_trace("Cluster resource count: %d (%d disabled, %d blocked)",
 143                   scheduler->ninstances, scheduler->disabled_resources,
 144                   scheduler->blocked_resources);
 145     }
 146 
 147     pe__set_working_set_flags(scheduler, pcmk_sched_have_status);
 148     return TRUE;
 149 }
 150 
 151 /*!
 152  * \internal
 153  * \brief Free a list of pcmk_resource_t
 154  *
 155  * \param[in,out] resources  List to free
 156  *
 157  * \note When the scheduler's resource list is freed, that includes the original
 158  *       storage for the uname and id of any Pacemaker Remote nodes in the
 159  *       scheduler's node list, so take care not to use those afterward.
 160  * \todo Refactor pcmk_node_t to strdup() the node name.
 161  */
 162 static void
 163 pe_free_resources(GList *resources)
     /* [previous][next][first][last][top][bottom][index][help] */
 164 {
 165     pcmk_resource_t *rsc = NULL;
 166     GList *iterator = resources;
 167 
 168     while (iterator != NULL) {
 169         rsc = (pcmk_resource_t *) iterator->data;
 170         iterator = iterator->next;
 171         rsc->fns->free(rsc);
 172     }
 173     if (resources != NULL) {
 174         g_list_free(resources);
 175     }
 176 }
 177 
 178 static void
 179 pe_free_actions(GList *actions)
     /* [previous][next][first][last][top][bottom][index][help] */
 180 {
 181     GList *iterator = actions;
 182 
 183     while (iterator != NULL) {
 184         pe_free_action(iterator->data);
 185         iterator = iterator->next;
 186     }
 187     if (actions != NULL) {
 188         g_list_free(actions);
 189     }
 190 }
 191 
 192 static void
 193 pe_free_nodes(GList *nodes)
     /* [previous][next][first][last][top][bottom][index][help] */
 194 {
 195     for (GList *iterator = nodes; iterator != NULL; iterator = iterator->next) {
 196         pcmk_node_t *node = (pcmk_node_t *) iterator->data;
 197 
 198         // Shouldn't be possible, but to be safe ...
 199         if (node == NULL) {
 200             continue;
 201         }
 202         if (node->details == NULL) {
 203             free(node);
 204             continue;
 205         }
 206 
 207         /* This is called after pe_free_resources(), which means that we can't
 208          * use node->details->uname for Pacemaker Remote nodes.
 209          */
 210         crm_trace("Freeing node %s", (pe__is_guest_or_remote_node(node)?
 211                   "(guest or remote)" : pe__node_name(node)));
 212 
 213         if (node->details->attrs != NULL) {
 214             g_hash_table_destroy(node->details->attrs);
 215         }
 216         if (node->details->utilization != NULL) {
 217             g_hash_table_destroy(node->details->utilization);
 218         }
 219         if (node->details->digest_cache != NULL) {
 220             g_hash_table_destroy(node->details->digest_cache);
 221         }
 222         g_list_free(node->details->running_rsc);
 223         g_list_free(node->details->allocated_rsc);
 224         free(node->details);
 225         free(node);
 226     }
 227     if (nodes != NULL) {
 228         g_list_free(nodes);
 229     }
 230 }
 231 
 232 static void
 233 pe__free_ordering(GList *constraints)
     /* [previous][next][first][last][top][bottom][index][help] */
 234 {
 235     GList *iterator = constraints;
 236 
 237     while (iterator != NULL) {
 238         pe__ordering_t *order = iterator->data;
 239 
 240         iterator = iterator->next;
 241 
 242         free(order->lh_action_task);
 243         free(order->rh_action_task);
 244         free(order);
 245     }
 246     if (constraints != NULL) {
 247         g_list_free(constraints);
 248     }
 249 }
 250 
 251 static void
 252 pe__free_location(GList *constraints)
     /* [previous][next][first][last][top][bottom][index][help] */
 253 {
 254     GList *iterator = constraints;
 255 
 256     while (iterator != NULL) {
 257         pe__location_t *cons = iterator->data;
 258 
 259         iterator = iterator->next;
 260 
 261         g_list_free_full(cons->node_list_rh, free);
 262         free(cons->id);
 263         free(cons);
 264     }
 265     if (constraints != NULL) {
 266         g_list_free(constraints);
 267     }
 268 }
 269 
 270 /*!
 271  * \brief Reset scheduler data to defaults without freeing it or constraints
 272  *
 273  * \param[in,out] scheduler  Scheduler data to reset
 274  *
 275  * \deprecated This function is deprecated as part of the API;
 276  *             pe_reset_working_set() should be used instead.
 277  */
 278 void
 279 cleanup_calculations(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 280 {
 281     if (scheduler == NULL) {
 282         return;
 283     }
 284 
 285     pe__clear_working_set_flags(scheduler, pcmk_sched_have_status);
 286     if (scheduler->config_hash != NULL) {
 287         g_hash_table_destroy(scheduler->config_hash);
 288     }
 289 
 290     if (scheduler->singletons != NULL) {
 291         g_hash_table_destroy(scheduler->singletons);
 292     }
 293 
 294     if (scheduler->tickets) {
 295         g_hash_table_destroy(scheduler->tickets);
 296     }
 297 
 298     if (scheduler->template_rsc_sets) {
 299         g_hash_table_destroy(scheduler->template_rsc_sets);
 300     }
 301 
 302     if (scheduler->tags) {
 303         g_hash_table_destroy(scheduler->tags);
 304     }
 305 
 306     free(scheduler->dc_uuid);
 307 
 308     crm_trace("deleting resources");
 309     pe_free_resources(scheduler->resources);
 310 
 311     crm_trace("deleting actions");
 312     pe_free_actions(scheduler->actions);
 313 
 314     crm_trace("deleting nodes");
 315     pe_free_nodes(scheduler->nodes);
 316 
 317     pe__free_param_checks(scheduler);
 318     g_list_free(scheduler->stop_needed);
 319     free_xml(scheduler->graph);
 320     crm_time_free(scheduler->now);
 321     free_xml(scheduler->input);
 322     free_xml(scheduler->failed);
 323 
 324     set_working_set_defaults(scheduler);
 325 
 326     CRM_CHECK(scheduler->ordering_constraints == NULL,;
 327         );
 328     CRM_CHECK(scheduler->placement_constraints == NULL,;
 329         );
 330 }
 331 
 332 /*!
 333  * \brief Reset scheduler data to default state without freeing it
 334  *
 335  * \param[in,out] scheduler  Scheduler data to reset
 336  */
 337 void
 338 pe_reset_working_set(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 339 {
 340     if (scheduler == NULL) {
 341         return;
 342     }
 343 
 344     crm_trace("Deleting %d ordering constraints",
 345               g_list_length(scheduler->ordering_constraints));
 346     pe__free_ordering(scheduler->ordering_constraints);
 347     scheduler->ordering_constraints = NULL;
 348 
 349     crm_trace("Deleting %d location constraints",
 350               g_list_length(scheduler->placement_constraints));
 351     pe__free_location(scheduler->placement_constraints);
 352     scheduler->placement_constraints = NULL;
 353 
 354     crm_trace("Deleting %d colocation constraints",
 355               g_list_length(scheduler->colocation_constraints));
 356     g_list_free_full(scheduler->colocation_constraints, free);
 357     scheduler->colocation_constraints = NULL;
 358 
 359     crm_trace("Deleting %d ticket constraints",
 360               g_list_length(scheduler->ticket_constraints));
 361     g_list_free_full(scheduler->ticket_constraints, free);
 362     scheduler->ticket_constraints = NULL;
 363 
 364     cleanup_calculations(scheduler);
 365 }
 366 
 367 void
 368 set_working_set_defaults(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 369 {
 370     void *priv = scheduler->priv;
 371 
 372     memset(scheduler, 0, sizeof(pcmk_scheduler_t));
 373 
 374     scheduler->priv = priv;
 375     scheduler->order_id = 1;
 376     scheduler->action_id = 1;
 377     scheduler->no_quorum_policy = pcmk_no_quorum_stop;
 378 
 379     scheduler->flags = 0x0ULL;
 380 
 381     pe__set_working_set_flags(scheduler,
 382                               pcmk_sched_symmetric_cluster
 383                               |pcmk_sched_stop_removed_resources
 384                               |pcmk_sched_cancel_removed_actions);
 385     if (!strcmp(PCMK__CONCURRENT_FENCING_DEFAULT, "true")) {
 386         pe__set_working_set_flags(scheduler, pcmk_sched_concurrent_fencing);
 387     }
 388 }
 389 
 390 pcmk_resource_t *
 391 pe_find_resource(GList *rsc_list, const char *id)
     /* [previous][next][first][last][top][bottom][index][help] */
 392 {
 393     return pe_find_resource_with_flags(rsc_list, id, pcmk_rsc_match_history);
 394 }
 395 
 396 pcmk_resource_t *
 397 pe_find_resource_with_flags(GList *rsc_list, const char *id, enum pe_find flags)
     /* [previous][next][first][last][top][bottom][index][help] */
 398 {
 399     GList *rIter = NULL;
 400 
 401     for (rIter = rsc_list; id && rIter; rIter = rIter->next) {
 402         pcmk_resource_t *parent = rIter->data;
 403 
 404         pcmk_resource_t *match =
 405             parent->fns->find_rsc(parent, id, NULL, flags);
 406         if (match != NULL) {
 407             return match;
 408         }
 409     }
 410     crm_trace("No match for %s", id);
 411     return NULL;
 412 }
 413 
 414 /*!
 415  * \brief Find a node by name or ID in a list of nodes
 416  *
 417  * \param[in] nodes      List of nodes (as pcmk_node_t*)
 418  * \param[in] id         If not NULL, ID of node to find
 419  * \param[in] node_name  If not NULL, name of node to find
 420  *
 421  * \return Node from \p nodes that matches \p id if any,
 422  *         otherwise node from \p nodes that matches \p uname if any,
 423  *         otherwise NULL
 424  */
 425 pcmk_node_t *
 426 pe_find_node_any(const GList *nodes, const char *id, const char *uname)
     /* [previous][next][first][last][top][bottom][index][help] */
 427 {
 428     pcmk_node_t *match = NULL;
 429 
 430     if (id != NULL) {
 431         match = pe_find_node_id(nodes, id);
 432     }
 433     if ((match == NULL) && (uname != NULL)) {
 434         match = pe_find_node(nodes, uname);
 435     }
 436     return match;
 437 }
 438 
 439 /*!
 440  * \brief Find a node by ID in a list of nodes
 441  *
 442  * \param[in] nodes  List of nodes (as pcmk_node_t*)
 443  * \param[in] id     ID of node to find
 444  *
 445  * \return Node from \p nodes that matches \p id if any, otherwise NULL
 446  */
 447 pcmk_node_t *
 448 pe_find_node_id(const GList *nodes, const char *id)
     /* [previous][next][first][last][top][bottom][index][help] */
 449 {
 450     for (const GList *iter = nodes; iter != NULL; iter = iter->next) {
 451         pcmk_node_t *node = (pcmk_node_t *) iter->data;
 452 
 453         /* @TODO Whether node IDs should be considered case-sensitive should
 454          * probably depend on the node type, so functionizing the comparison
 455          * would be worthwhile
 456          */
 457         if (pcmk__str_eq(node->details->id, id, pcmk__str_casei)) {
 458             return node;
 459         }
 460     }
 461     return NULL;
 462 }
 463 
 464 /*!
 465  * \brief Find a node by name in a list of nodes
 466  *
 467  * \param[in] nodes      List of nodes (as pcmk_node_t*)
 468  * \param[in] node_name  Name of node to find
 469  *
 470  * \return Node from \p nodes that matches \p node_name if any, otherwise NULL
 471  */
 472 pcmk_node_t *
 473 pe_find_node(const GList *nodes, const char *node_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 474 {
 475     for (const GList *iter = nodes; iter != NULL; iter = iter->next) {
 476         pcmk_node_t *node = (pcmk_node_t *) iter->data;
 477 
 478         if (pcmk__str_eq(node->details->uname, node_name, pcmk__str_casei)) {
 479             return node;
 480         }
 481     }
 482     return NULL;
 483 }

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