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-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 evaluate_lifetime(xmlNode *lifetime, pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
  31 {
  32     bool result = false;
  33     crm_time_t *next_change = crm_time_new_undefined();
  34     pcmk_rule_input_t rule_input = {
  35         .now = scheduler->now,
  36     };
  37 
  38     result = (pcmk__evaluate_rules(lifetime, &rule_input,
  39                                    next_change) == pcmk_rc_ok);
  40 
  41     if (crm_time_is_defined(next_change)) {
  42         time_t recheck = (time_t) crm_time_get_seconds_since_epoch(next_change);
  43 
  44         pe__update_recheck_time(recheck, scheduler, "constraint lifetime");
  45     }
  46     crm_time_free(next_change);
  47     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 pcmk__unpack_constraints(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
  61 {
  62     xmlNode *xml_constraints = pcmk_find_cib_element(scheduler->input,
  63                                                      PCMK_XE_CONSTRAINTS);
  64 
  65     for (xmlNode *xml_obj = pcmk__xe_first_child(xml_constraints, NULL, NULL,
  66                                                  NULL);
  67          xml_obj != NULL; xml_obj = pcmk__xe_next(xml_obj)) {
  68 
  69         xmlNode *lifetime = NULL;
  70         const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
  71         const char *tag = (const char *) xml_obj->name;
  72 
  73         if (id == NULL) {
  74             pcmk__config_err("Ignoring <%s> constraint without "
  75                              PCMK_XA_ID, tag);
  76             continue;
  77         }
  78 
  79         crm_trace("Unpacking %s constraint '%s'", tag, id);
  80 
  81         lifetime = pcmk__xe_first_child(xml_obj, PCMK__XE_LIFETIME, NULL, NULL);
  82         if (lifetime != NULL) {
  83             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         if ((lifetime != NULL) && !evaluate_lifetime(lifetime, scheduler)) {
  89             crm_info("Constraint %s %s is not active", tag, id);
  90 
  91         } else if (pcmk__str_eq(PCMK_XE_RSC_ORDER, tag, pcmk__str_none)) {
  92             pcmk__unpack_ordering(xml_obj, scheduler);
  93 
  94         } else if (pcmk__str_eq(PCMK_XE_RSC_COLOCATION, tag, pcmk__str_none)) {
  95             pcmk__unpack_colocation(xml_obj, scheduler);
  96 
  97         } else if (pcmk__str_eq(PCMK_XE_RSC_LOCATION, tag, pcmk__str_none)) {
  98             pcmk__unpack_location(xml_obj, scheduler);
  99 
 100         } else if (pcmk__str_eq(PCMK_XE_RSC_TICKET, tag, pcmk__str_none)) {
 101             pcmk__unpack_rsc_ticket(xml_obj, scheduler);
 102 
 103         } else {
 104             pcmk__config_err("Unsupported constraint type: %s", tag);
 105         }
 106     }
 107 }
 108 
 109 pcmk_resource_t *
 110 pcmk__find_constraint_resource(GList *rsc_list, const char *id)
     /* [previous][next][first][last][top][bottom][index][help] */
 111 {
 112     if (id == NULL) {
 113         return NULL;
 114     }
 115     for (GList *iter = rsc_list; iter != NULL; iter = iter->next) {
 116         pcmk_resource_t *parent = iter->data;
 117         pcmk_resource_t *match = parent->fns->find_rsc(parent, id, NULL,
 118                                                        pcmk_rsc_match_history);
 119 
 120         if (match != NULL) {
 121             if (!pcmk__str_eq(match->id, id, pcmk__str_none)) {
 122                 /* We found an instance of a clone instead */
 123                 match = uber_parent(match);
 124                 crm_debug("Found %s for %s", match->id, id);
 125             }
 126             return match;
 127         }
 128     }
 129     crm_trace("No match for %s", id);
 130     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 find_constraint_tag(const pcmk_scheduler_t *scheduler, const char *id,
     /* [previous][next][first][last][top][bottom][index][help] */
 146                     pcmk_tag_t **tag)
 147 {
 148     *tag = NULL;
 149 
 150     // Check whether id refers to a resource set template
 151     if (g_hash_table_lookup_extended(scheduler->template_rsc_sets, id,
 152                                      NULL, (gpointer *) tag)) {
 153         if (*tag == NULL) {
 154             crm_notice("No resource is derived from template '%s'", id);
 155             return false;
 156         }
 157         return true;
 158     }
 159 
 160     // If not, check whether id refers to a tag
 161     if (g_hash_table_lookup_extended(scheduler->tags, id,
 162                                      NULL, (gpointer *) tag)) {
 163         if (*tag == NULL) {
 164             crm_notice("No resource is tagged with '%s'", id);
 165             return false;
 166         }
 167         return true;
 168     }
 169 
 170     pcmk__config_warn("No resource, template, or tag named '%s'", id);
 171     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 pcmk__valid_resource_or_tag(const pcmk_scheduler_t *scheduler, const char *id,
     /* [previous][next][first][last][top][bottom][index][help] */
 189                             pcmk_resource_t **rsc, pcmk_tag_t **tag)
 190 {
 191     if (rsc != NULL) {
 192         *rsc = pcmk__find_constraint_resource(scheduler->resources, id);
 193         if (*rsc != NULL) {
 194             return true;
 195         }
 196     }
 197 
 198     if ((tag != NULL) && find_constraint_tag(scheduler, id, tag)) {
 199         return true;
 200     }
 201 
 202     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 pcmk__expand_tags_in_sets(xmlNode *xml_obj, const pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 223 {
 224     xmlNode *new_xml = NULL;
 225     bool any_refs = false;
 226 
 227     // Short-circuit if there are no sets
 228     if (pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL,
 229                              NULL) == NULL) {
 230         return NULL;
 231     }
 232 
 233     new_xml = pcmk__xml_copy(NULL, xml_obj);
 234 
 235     for (xmlNode *set = pcmk__xe_first_child(new_xml, PCMK_XE_RESOURCE_SET,
 236                                              NULL, NULL);
 237          set != NULL; set = pcmk__xe_next_same(set)) {
 238 
 239         GList *tag_refs = NULL;
 240         GList *iter = NULL;
 241 
 242         for (xmlNode *xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF,
 243                                                      NULL, NULL);
 244              xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
 245 
 246             pcmk_resource_t *rsc = NULL;
 247             pcmk_tag_t *tag = NULL;
 248 
 249             if (!pcmk__valid_resource_or_tag(scheduler, pcmk__xe_id(xml_rsc),
 250                                              &rsc, &tag)) {
 251                 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                 free_xml(new_xml);
 255                 return NULL;
 256 
 257             } else if (rsc) {
 258                 continue;
 259 
 260             } else if (tag) {
 261                 /* PCMK_XE_RESOURCE_REF under PCMK_XE_RESOURCE_SET references
 262                  * template or tag
 263                  */
 264                 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                 for (iter = tag->refs; iter != NULL; iter = iter->next) {
 286                     const char *ref_id = iter->data;
 287                     xmlNode *new_ref = pcmk__xe_create(set,
 288                                                        PCMK_XE_RESOURCE_REF);
 289 
 290                     crm_xml_add(new_ref, PCMK_XA_ID, ref_id);
 291                     xmlAddNextSibling(last_ref, new_ref);
 292 
 293                     last_ref = new_ref;
 294                 }
 295 
 296                 any_refs = true;
 297 
 298                 /* Freeing the resource_ref now would break the XML child
 299                  * iteration, so just remember it for freeing later.
 300                  */
 301                 tag_refs = g_list_append(tag_refs, xml_rsc);
 302             }
 303         }
 304 
 305         /* Now free '<resource_ref id="tag1"/>', and finally get:
 306 
 307            <resource_set id="tag1-colocation-0" sequential="true">
 308              <resource_ref id="rsc1"/>
 309              <resource_ref id="rsc2"/>
 310              <resource_ref id="rsc3"/>
 311              <resource_ref id="rsc4"/>
 312            </resource_set>
 313 
 314          */
 315         for (iter = tag_refs; iter != NULL; iter = iter->next) {
 316             xmlNode *tag_ref = iter->data;
 317 
 318             free_xml(tag_ref);
 319         }
 320         g_list_free(tag_refs);
 321     }
 322 
 323     if (!any_refs) {
 324         free_xml(new_xml);
 325         new_xml = NULL;
 326     }
 327     return new_xml;
 328 }
 329 
 330 /*!
 331  * \internal
 332  * \brief Convert a tag into a resource set of tagged resources
 333  *
 334  * \param[in,out] xml_obj      Constraint XML
 335  * \param[out]    rsc_set      Where to store resource set XML
 336  * \param[in]     attr         Name of XML attribute with resource or tag ID
 337  * \param[in]     convert_rsc  If true, convert to set even if \p attr
 338  *                             references a resource
 339  * \param[in]     scheduler    Scheduler data
 340  */
 341 bool
 342 pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr,
     /* [previous][next][first][last][top][bottom][index][help] */
 343                  bool convert_rsc, const pcmk_scheduler_t *scheduler)
 344 {
 345     const char *cons_id = NULL;
 346     const char *id = NULL;
 347 
 348     pcmk_resource_t *rsc = NULL;
 349     pcmk_tag_t *tag = NULL;
 350 
 351     *rsc_set = NULL;
 352 
 353     CRM_CHECK((xml_obj != NULL) && (attr != NULL), return false);
 354 
 355     cons_id = pcmk__xe_id(xml_obj);
 356     if (cons_id == NULL) {
 357         pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
 358                          xml_obj->name);
 359         return false;
 360     }
 361 
 362     id = crm_element_value(xml_obj, attr);
 363     if (id == NULL) {
 364         return true;
 365     }
 366 
 367     if (!pcmk__valid_resource_or_tag(scheduler, id, &rsc, &tag)) {
 368         pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
 369                          "valid resource or tag", cons_id, id);
 370         return false;
 371 
 372     } else if (tag) {
 373         /* The "attr" attribute (for a resource in a constraint) specifies a
 374          * template or tag. Add the corresponding PCMK_XE_RESOURCE_SET
 375          * containing the resources derived from or tagged with it.
 376          */
 377         *rsc_set = pcmk__xe_create(xml_obj, PCMK_XE_RESOURCE_SET);
 378         crm_xml_add(*rsc_set, PCMK_XA_ID, id);
 379 
 380         for (GList *iter = tag->refs; iter != NULL; iter = iter->next) {
 381             const char *obj_ref = iter->data;
 382             xmlNode *rsc_ref = NULL;
 383 
 384             rsc_ref = pcmk__xe_create(*rsc_set, PCMK_XE_RESOURCE_REF);
 385             crm_xml_add(rsc_ref, PCMK_XA_ID, obj_ref);
 386         }
 387 
 388         // Set PCMK_XA_SEQUENTIAL=PCMK_VALUE_FALSE for the PCMK_XE_RESOURCE_SET
 389         pcmk__xe_set_bool_attr(*rsc_set, PCMK_XA_SEQUENTIAL, false);
 390 
 391     } else if ((rsc != NULL) && convert_rsc) {
 392         /* Even if a regular resource is referenced by "attr", convert it into a
 393          * PCMK_XE_RESOURCE_SET, because the other resource reference in the
 394          * constraint could be a template or tag.
 395          */
 396         xmlNode *rsc_ref = NULL;
 397 
 398         *rsc_set = pcmk__xe_create(xml_obj, PCMK_XE_RESOURCE_SET);
 399         crm_xml_add(*rsc_set, PCMK_XA_ID, id);
 400 
 401         rsc_ref = pcmk__xe_create(*rsc_set, PCMK_XE_RESOURCE_REF);
 402         crm_xml_add(rsc_ref, PCMK_XA_ID, id);
 403 
 404     } else {
 405         return true;
 406     }
 407 
 408     /* Remove the "attr" attribute referencing the template/tag */
 409     if (*rsc_set != NULL) {
 410         pcmk__xe_remove_attr(xml_obj, attr);
 411     }
 412 
 413     return true;
 414 }
 415 
 416 /*!
 417  * \internal
 418  * \brief Create constraints inherent to resource types
 419  *
 420  * \param[in,out] scheduler  Scheduler data
 421  */
 422 void
 423 pcmk__create_internal_constraints(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 424 {
 425     crm_trace("Create internal constraints");
 426     for (GList *iter = scheduler->resources; iter != NULL; iter = iter->next) {
 427         pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
 428 
 429         rsc->cmds->internal_constraints(rsc);
 430     }
 431 }

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