LCOV - code coverage report
Current view: top level - pacemaker - pcmk_sched_constraints.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 153 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 8 0.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 General Public License version 2
       7             :  * or later (GPLv2+) WITHOUT ANY WARRANTY.
       8             :  */
       9             : 
      10             : #include <crm_internal.h>
      11             : 
      12             : #include <sys/param.h>
      13             : #include <sys/types.h>
      14             : #include <stdbool.h>
      15             : #include <regex.h>
      16             : #include <glib.h>
      17             : 
      18             : #include <crm/crm.h>
      19             : #include <crm/cib.h>
      20             : #include <crm/common/xml.h>
      21             : #include <crm/common/xml_internal.h>
      22             : #include <crm/common/iso8601.h>
      23             : #include <crm/pengine/status.h>
      24             : #include <crm/pengine/internal.h>
      25             : #include <crm/pengine/rules.h>
      26             : #include <pacemaker-internal.h>
      27             : #include "libpacemaker_private.h"
      28             : 
      29             : static bool
      30           0 : evaluate_lifetime(xmlNode *lifetime, pcmk_scheduler_t *scheduler)
      31             : {
      32           0 :     bool result = false;
      33           0 :     crm_time_t *next_change = crm_time_new_undefined();
      34           0 :     pcmk_rule_input_t rule_input = {
      35           0 :         .now = scheduler->now,
      36             :     };
      37             : 
      38           0 :     result = (pcmk__evaluate_rules(lifetime, &rule_input,
      39             :                                    next_change) == pcmk_rc_ok);
      40             : 
      41           0 :     if (crm_time_is_defined(next_change)) {
      42           0 :         time_t recheck = (time_t) crm_time_get_seconds_since_epoch(next_change);
      43             : 
      44           0 :         pe__update_recheck_time(recheck, scheduler, "constraint lifetime");
      45             :     }
      46           0 :     crm_time_free(next_change);
      47           0 :     return result;
      48             : }
      49             : 
      50             : /*!
      51             :  * \internal
      52             :  * \brief Unpack constraints from XML
      53             :  *
      54             :  * Given scheduler data, unpack all constraints from its input XML into
      55             :  * data structures.
      56             :  *
      57             :  * \param[in,out] scheduler  Scheduler data
      58             :  */
      59             : void
      60           0 : pcmk__unpack_constraints(pcmk_scheduler_t *scheduler)
      61             : {
      62           0 :     xmlNode *xml_constraints = pcmk_find_cib_element(scheduler->input,
      63             :                                                      PCMK_XE_CONSTRAINTS);
      64             : 
      65           0 :     for (xmlNode *xml_obj = pcmk__xe_first_child(xml_constraints, NULL, NULL,
      66             :                                                  NULL);
      67           0 :          xml_obj != NULL; xml_obj = pcmk__xe_next(xml_obj)) {
      68             : 
      69           0 :         xmlNode *lifetime = NULL;
      70           0 :         const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
      71           0 :         const char *tag = (const char *) xml_obj->name;
      72             : 
      73           0 :         if (id == NULL) {
      74           0 :             pcmk__config_err("Ignoring <%s> constraint without "
      75             :                              PCMK_XA_ID, tag);
      76           0 :             continue;
      77             :         }
      78             : 
      79           0 :         crm_trace("Unpacking %s constraint '%s'", tag, id);
      80             : 
      81           0 :         lifetime = pcmk__xe_first_child(xml_obj, PCMK__XE_LIFETIME, NULL, NULL);
      82           0 :         if (lifetime != NULL) {
      83           0 :             pcmk__config_warn("Support for '" PCMK__XE_LIFETIME "' element "
      84             :                               "(in %s) is deprecated and will be dropped "
      85             :                               "in a later release", id);
      86             :         }
      87             : 
      88           0 :         if ((lifetime != NULL) && !evaluate_lifetime(lifetime, scheduler)) {
      89           0 :             crm_info("Constraint %s %s is not active", tag, id);
      90             : 
      91           0 :         } else if (pcmk__str_eq(PCMK_XE_RSC_ORDER, tag, pcmk__str_none)) {
      92           0 :             pcmk__unpack_ordering(xml_obj, scheduler);
      93             : 
      94           0 :         } else if (pcmk__str_eq(PCMK_XE_RSC_COLOCATION, tag, pcmk__str_none)) {
      95           0 :             pcmk__unpack_colocation(xml_obj, scheduler);
      96             : 
      97           0 :         } else if (pcmk__str_eq(PCMK_XE_RSC_LOCATION, tag, pcmk__str_none)) {
      98           0 :             pcmk__unpack_location(xml_obj, scheduler);
      99             : 
     100           0 :         } else if (pcmk__str_eq(PCMK_XE_RSC_TICKET, tag, pcmk__str_none)) {
     101           0 :             pcmk__unpack_rsc_ticket(xml_obj, scheduler);
     102             : 
     103             :         } else {
     104           0 :             pcmk__config_err("Unsupported constraint type: %s", tag);
     105             :         }
     106             :     }
     107           0 : }
     108             : 
     109             : pcmk_resource_t *
     110           0 : pcmk__find_constraint_resource(GList *rsc_list, const char *id)
     111             : {
     112           0 :     if (id == NULL) {
     113           0 :         return NULL;
     114             :     }
     115           0 :     for (GList *iter = rsc_list; iter != NULL; iter = iter->next) {
     116           0 :         pcmk_resource_t *parent = iter->data;
     117           0 :         pcmk_resource_t *match = parent->fns->find_rsc(parent, id, NULL,
     118             :                                                        pcmk_rsc_match_history);
     119             : 
     120           0 :         if (match != NULL) {
     121           0 :             if (!pcmk__str_eq(match->id, id, pcmk__str_none)) {
     122             :                 /* We found an instance of a clone instead */
     123           0 :                 match = uber_parent(match);
     124           0 :                 crm_debug("Found %s for %s", match->id, id);
     125             :             }
     126           0 :             return match;
     127             :         }
     128             :     }
     129           0 :     crm_trace("No match for %s", id);
     130           0 :     return NULL;
     131             : }
     132             : 
     133             : /*!
     134             :  * \internal
     135             :  * \brief Check whether an ID references a resource tag
     136             :  *
     137             :  * \param[in]  scheduler  Scheduler data
     138             :  * \param[in]  id         Tag ID to search for
     139             :  * \param[out] tag        Where to store tag, if found
     140             :  *
     141             :  * \return true if ID refers to a tagged resource or resource set template,
     142             :  *         otherwise false
     143             :  */
     144             : static bool
     145           0 : find_constraint_tag(const pcmk_scheduler_t *scheduler, const char *id,
     146             :                     pcmk_tag_t **tag)
     147             : {
     148           0 :     *tag = NULL;
     149             : 
     150             :     // Check whether id refers to a resource set template
     151           0 :     if (g_hash_table_lookup_extended(scheduler->template_rsc_sets, id,
     152             :                                      NULL, (gpointer *) tag)) {
     153           0 :         if (*tag == NULL) {
     154           0 :             crm_notice("No resource is derived from template '%s'", id);
     155           0 :             return false;
     156             :         }
     157           0 :         return true;
     158             :     }
     159             : 
     160             :     // If not, check whether id refers to a tag
     161           0 :     if (g_hash_table_lookup_extended(scheduler->tags, id,
     162             :                                      NULL, (gpointer *) tag)) {
     163           0 :         if (*tag == NULL) {
     164           0 :             crm_notice("No resource is tagged with '%s'", id);
     165           0 :             return false;
     166             :         }
     167           0 :         return true;
     168             :     }
     169             : 
     170           0 :     pcmk__config_warn("No resource, template, or tag named '%s'", id);
     171           0 :     return false;
     172             : }
     173             : 
     174             : /*!
     175             :  * \brief
     176             :  * \internal Check whether an ID refers to a valid resource or tag
     177             :  *
     178             :  * \param[in]  scheduler  Scheduler data
     179             :  * \param[in]  id         ID to search for
     180             :  * \param[out] rsc        Where to store resource, if found
     181             :  *                        (or NULL to skip searching resources)
     182             :  * \param[out] tag        Where to store tag, if found
     183             :  *                        (or NULL to skip searching tags)
     184             :  *
     185             :  * \return true if id refers to a resource (possibly indirectly via a tag)
     186             :  */
     187             : bool
     188           0 : pcmk__valid_resource_or_tag(const pcmk_scheduler_t *scheduler, const char *id,
     189             :                             pcmk_resource_t **rsc, pcmk_tag_t **tag)
     190             : {
     191           0 :     if (rsc != NULL) {
     192           0 :         *rsc = pcmk__find_constraint_resource(scheduler->resources, id);
     193           0 :         if (*rsc != NULL) {
     194           0 :             return true;
     195             :         }
     196             :     }
     197             : 
     198           0 :     if ((tag != NULL) && find_constraint_tag(scheduler, id, tag)) {
     199           0 :         return true;
     200             :     }
     201             : 
     202           0 :     return false;
     203             : }
     204             : 
     205             : /*!
     206             :  * \internal
     207             :  * \brief Replace any resource tags with equivalent \C PCMK_XE_RESOURCE_REF
     208             :  *        entries
     209             :  *
     210             :  * If a given constraint has resource sets, check each set for
     211             :  * \c PCMK_XE_RESOURCE_REF entries that list tags rather than resource IDs, and
     212             :  * replace any found with \c PCMK_XE_RESOURCE_REF entries for the corresponding
     213             :  * resource IDs.
     214             :  *
     215             :  * \param[in,out] xml_obj    Constraint XML
     216             :  * \param[in]     scheduler  Scheduler data
     217             :  *
     218             :  * \return Equivalent XML with resource tags replaced (or NULL if none)
     219             :  * \note It is the caller's responsibility to free the result with free_xml().
     220             :  */
     221             : xmlNode *
     222           0 : pcmk__expand_tags_in_sets(xmlNode *xml_obj, const pcmk_scheduler_t *scheduler)
     223             : {
     224           0 :     xmlNode *new_xml = NULL;
     225           0 :     bool any_refs = false;
     226             : 
     227             :     // Short-circuit if there are no sets
     228           0 :     if (pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL,
     229             :                              NULL) == NULL) {
     230           0 :         return NULL;
     231             :     }
     232             : 
     233           0 :     new_xml = pcmk__xml_copy(NULL, xml_obj);
     234             : 
     235           0 :     for (xmlNode *set = pcmk__xe_first_child(new_xml, PCMK_XE_RESOURCE_SET,
     236             :                                              NULL, NULL);
     237           0 :          set != NULL; set = pcmk__xe_next_same(set)) {
     238             : 
     239           0 :         GList *tag_refs = NULL;
     240           0 :         GList *iter = NULL;
     241             : 
     242           0 :         for (xmlNode *xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF,
     243             :                                                      NULL, NULL);
     244           0 :              xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
     245             : 
     246           0 :             pcmk_resource_t *rsc = NULL;
     247           0 :             pcmk_tag_t *tag = NULL;
     248             : 
     249           0 :             if (!pcmk__valid_resource_or_tag(scheduler, pcmk__xe_id(xml_rsc),
     250             :                                              &rsc, &tag)) {
     251           0 :                 pcmk__config_err("Ignoring resource sets for constraint '%s' "
     252             :                                  "because '%s' is not a valid resource or tag",
     253             :                                  pcmk__xe_id(xml_obj), pcmk__xe_id(xml_rsc));
     254           0 :                 free_xml(new_xml);
     255           0 :                 return NULL;
     256             : 
     257           0 :             } else if (rsc) {
     258           0 :                 continue;
     259             : 
     260           0 :             } else if (tag) {
     261             :                 /* PCMK_XE_RESOURCE_REF under PCMK_XE_RESOURCE_SET references
     262             :                  * template or tag
     263             :                  */
     264           0 :                 xmlNode *last_ref = xml_rsc;
     265             : 
     266             :                 /* For example, given the original XML:
     267             :                  *
     268             :                  *   <resource_set id="tag1-colocation-0" sequential="true">
     269             :                  *     <resource_ref id="rsc1"/>
     270             :                  *     <resource_ref id="tag1"/>
     271             :                  *     <resource_ref id="rsc4"/>
     272             :                  *   </resource_set>
     273             :                  *
     274             :                  * If rsc2 and rsc3 are tagged with tag1, we add them after it:
     275             :                  *
     276             :                  *   <resource_set id="tag1-colocation-0" sequential="true">
     277             :                  *     <resource_ref id="rsc1"/>
     278             :                  *     <resource_ref id="tag1"/>
     279             :                  *     <resource_ref id="rsc2"/>
     280             :                  *     <resource_ref id="rsc3"/>
     281             :                  *     <resource_ref id="rsc4"/>
     282             :                  *   </resource_set>
     283             :                  */
     284             : 
     285           0 :                 for (iter = tag->refs; iter != NULL; iter = iter->next) {
     286           0 :                     const char *obj_ref = iter->data;
     287           0 :                     xmlNode *new_rsc_ref = NULL;
     288             : 
     289           0 :                     new_rsc_ref = xmlNewDocRawNode(set->doc, NULL,
     290             :                                                    (pcmkXmlStr)
     291             :                                                    PCMK_XE_RESOURCE_REF,
     292             :                                                    NULL);
     293           0 :                     crm_xml_add(new_rsc_ref, PCMK_XA_ID, obj_ref);
     294           0 :                     xmlAddNextSibling(last_ref, new_rsc_ref);
     295             : 
     296           0 :                     last_ref = new_rsc_ref;
     297             :                 }
     298             : 
     299           0 :                 any_refs = true;
     300             : 
     301             :                 /* Freeing the resource_ref now would break the XML child
     302             :                  * iteration, so just remember it for freeing later.
     303             :                  */
     304           0 :                 tag_refs = g_list_append(tag_refs, xml_rsc);
     305             :             }
     306             :         }
     307             : 
     308             :         /* Now free '<resource_ref id="tag1"/>', and finally get:
     309             : 
     310             :            <resource_set id="tag1-colocation-0" sequential="true">
     311             :              <resource_ref id="rsc1"/>
     312             :              <resource_ref id="rsc2"/>
     313             :              <resource_ref id="rsc3"/>
     314             :              <resource_ref id="rsc4"/>
     315             :            </resource_set>
     316             : 
     317             :          */
     318           0 :         for (iter = tag_refs; iter != NULL; iter = iter->next) {
     319           0 :             xmlNode *tag_ref = iter->data;
     320             : 
     321           0 :             free_xml(tag_ref);
     322             :         }
     323           0 :         g_list_free(tag_refs);
     324             :     }
     325             : 
     326           0 :     if (!any_refs) {
     327           0 :         free_xml(new_xml);
     328           0 :         new_xml = NULL;
     329             :     }
     330           0 :     return new_xml;
     331             : }
     332             : 
     333             : /*!
     334             :  * \internal
     335             :  * \brief Convert a tag into a resource set of tagged resources
     336             :  *
     337             :  * \param[in,out] xml_obj      Constraint XML
     338             :  * \param[out]    rsc_set      Where to store resource set XML
     339             :  * \param[in]     attr         Name of XML attribute with resource or tag ID
     340             :  * \param[in]     convert_rsc  If true, convert to set even if \p attr
     341             :  *                             references a resource
     342             :  * \param[in]     scheduler    Scheduler data
     343             :  */
     344             : bool
     345           0 : pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr,
     346             :                  bool convert_rsc, const pcmk_scheduler_t *scheduler)
     347             : {
     348           0 :     const char *cons_id = NULL;
     349           0 :     const char *id = NULL;
     350             : 
     351           0 :     pcmk_resource_t *rsc = NULL;
     352           0 :     pcmk_tag_t *tag = NULL;
     353             : 
     354           0 :     *rsc_set = NULL;
     355             : 
     356           0 :     CRM_CHECK((xml_obj != NULL) && (attr != NULL), return false);
     357             : 
     358           0 :     cons_id = pcmk__xe_id(xml_obj);
     359           0 :     if (cons_id == NULL) {
     360           0 :         pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
     361             :                          xml_obj->name);
     362           0 :         return false;
     363             :     }
     364             : 
     365           0 :     id = crm_element_value(xml_obj, attr);
     366           0 :     if (id == NULL) {
     367           0 :         return true;
     368             :     }
     369             : 
     370           0 :     if (!pcmk__valid_resource_or_tag(scheduler, id, &rsc, &tag)) {
     371           0 :         pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
     372             :                          "valid resource or tag", cons_id, id);
     373           0 :         return false;
     374             : 
     375           0 :     } else if (tag) {
     376             :         /* The "attr" attribute (for a resource in a constraint) specifies a
     377             :          * template or tag. Add the corresponding PCMK_XE_RESOURCE_SET
     378             :          * containing the resources derived from or tagged with it.
     379             :          */
     380           0 :         *rsc_set = pcmk__xe_create(xml_obj, PCMK_XE_RESOURCE_SET);
     381           0 :         crm_xml_add(*rsc_set, PCMK_XA_ID, id);
     382             : 
     383           0 :         for (GList *iter = tag->refs; iter != NULL; iter = iter->next) {
     384           0 :             const char *obj_ref = iter->data;
     385           0 :             xmlNode *rsc_ref = NULL;
     386             : 
     387           0 :             rsc_ref = pcmk__xe_create(*rsc_set, PCMK_XE_RESOURCE_REF);
     388           0 :             crm_xml_add(rsc_ref, PCMK_XA_ID, obj_ref);
     389             :         }
     390             : 
     391             :         // Set PCMK_XA_SEQUENTIAL=PCMK_VALUE_FALSE for the PCMK_XE_RESOURCE_SET
     392           0 :         pcmk__xe_set_bool_attr(*rsc_set, PCMK_XA_SEQUENTIAL, false);
     393             : 
     394           0 :     } else if ((rsc != NULL) && convert_rsc) {
     395             :         /* Even if a regular resource is referenced by "attr", convert it into a
     396             :          * PCMK_XE_RESOURCE_SET, because the other resource reference in the
     397             :          * constraint could be a template or tag.
     398             :          */
     399           0 :         xmlNode *rsc_ref = NULL;
     400             : 
     401           0 :         *rsc_set = pcmk__xe_create(xml_obj, PCMK_XE_RESOURCE_SET);
     402           0 :         crm_xml_add(*rsc_set, PCMK_XA_ID, id);
     403             : 
     404           0 :         rsc_ref = pcmk__xe_create(*rsc_set, PCMK_XE_RESOURCE_REF);
     405           0 :         crm_xml_add(rsc_ref, PCMK_XA_ID, id);
     406             : 
     407             :     } else {
     408           0 :         return true;
     409             :     }
     410             : 
     411             :     /* Remove the "attr" attribute referencing the template/tag */
     412           0 :     if (*rsc_set != NULL) {
     413           0 :         pcmk__xe_remove_attr(xml_obj, attr);
     414             :     }
     415             : 
     416           0 :     return true;
     417             : }
     418             : 
     419             : /*!
     420             :  * \internal
     421             :  * \brief Create constraints inherent to resource types
     422             :  *
     423             :  * \param[in,out] scheduler  Scheduler data
     424             :  */
     425             : void
     426           0 : pcmk__create_internal_constraints(pcmk_scheduler_t *scheduler)
     427             : {
     428           0 :     crm_trace("Create internal constraints");
     429           0 :     for (GList *iter = scheduler->resources; iter != NULL; iter = iter->next) {
     430           0 :         pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
     431             : 
     432           0 :         rsc->cmds->internal_constraints(rsc);
     433             :     }
     434           0 : }

Generated by: LCOV version 1.14