root/lib/pacemaker/pcmk_sched_constraints.c

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

DEFINITIONS

This source file includes following definitions.
  1. evaluate_lifetime
  2. pcmk__unpack_constraints
  3. pcmk__find_constraint_resource
  4. find_constraint_tag
  5. pcmk__valid_resource_or_tag
  6. pcmk__expand_tags_in_sets
  7. pcmk__tag_to_set
  8. pcmk__create_internal_constraints

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

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