LCOV - code coverage report
Current view: top level - pengine - status.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 29 206 14.1 %
Date: 2024-05-07 11:09:47 Functions: 4 16 25.0 %

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

Generated by: LCOV version 1.14