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, pe_working_set_t *data_set)
     /* [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, data_set->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, data_set);
  41     }
  42     crm_time_free(next_change);
  43     return result;
  44 }
  45 
  46 /*!
  47  * \internal
  48  * \brief Unpack constraints from XML
  49  *
  50  * Given a cluster working set, unpack all constraints from its input XML into
  51  * data structures.
  52  *
  53  * \param[in,out] data_set  Cluster working set
  54  */
  55 void
  56 pcmk__unpack_constraints(pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
  57 {
  58     xmlNode *xml_constraints = pcmk_find_cib_element(data_set->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 = crm_element_name(xml_obj);
  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, data_set)) {
  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_casei)) {
  88             pcmk__unpack_ordering(xml_obj, data_set);
  89 
  90         } else if (pcmk__str_eq(XML_CONS_TAG_RSC_DEPEND, tag, pcmk__str_casei)) {
  91             pcmk__unpack_colocation(xml_obj, data_set);
  92 
  93         } else if (pcmk__str_eq(XML_CONS_TAG_RSC_LOCATION, tag, pcmk__str_casei)) {
  94             pcmk__unpack_location(xml_obj, data_set);
  95 
  96         } else if (pcmk__str_eq(XML_CONS_TAG_RSC_TICKET, tag, pcmk__str_casei)) {
  97             pcmk__unpack_rsc_ticket(xml_obj, data_set);
  98 
  99         } else {
 100             pe_err("Unsupported constraint type: %s", tag);
 101         }
 102     }
 103 }
 104 
 105 pe_resource_t *
 106 pcmk__find_constraint_resource(GList *rsc_list, const char *id)
     /* [previous][next][first][last][top][bottom][index][help] */
 107 {
 108     GList *rIter = NULL;
 109 
 110     for (rIter = rsc_list; id && rIter; rIter = rIter->next) {
 111         pe_resource_t *parent = rIter->data;
 112         pe_resource_t *match = parent->fns->find_rsc(parent, id, NULL,
 113                                                      pe_find_renamed);
 114 
 115         if (match != NULL) {
 116             if(!pcmk__str_eq(match->id, id, pcmk__str_casei)) {
 117                 /* We found an instance of a clone instead */
 118                 match = uber_parent(match);
 119                 crm_debug("Found %s for %s", match->id, id);
 120             }
 121             return match;
 122         }
 123     }
 124     crm_trace("No match for %s", id);
 125     return NULL;
 126 }
 127 
 128 /*!
 129  * \internal
 130  * \brief Check whether an ID references a resource tag
 131  *
 132  * \param[in]  data_set  Cluster working set
 133  * \param[in]  id        Tag ID to search for
 134  * \param[out] tag       Where to store tag, if found
 135  *
 136  * \return true if ID refers to a tagged resource or resource set template,
 137  *         otherwise false
 138  */
 139 static bool
 140 find_constraint_tag(const pe_working_set_t *data_set, const char *id,
     /* [previous][next][first][last][top][bottom][index][help] */
 141                     pe_tag_t **tag)
 142 {
 143     *tag = NULL;
 144 
 145     // Check whether id refers to a resource set template
 146     if (g_hash_table_lookup_extended(data_set->template_rsc_sets, id,
 147                                      NULL, (gpointer *) tag)) {
 148         if (*tag == NULL) {
 149             crm_warn("No resource is derived from template '%s'", id);
 150             return false;
 151         }
 152         return true;
 153     }
 154 
 155     // If not, check whether id refers to a tag
 156     if (g_hash_table_lookup_extended(data_set->tags, id,
 157                                      NULL, (gpointer *) tag)) {
 158         if (*tag == NULL) {
 159             crm_warn("No resource is tagged with '%s'", id);
 160             return false;
 161         }
 162         return true;
 163     }
 164 
 165     crm_warn("No template or tag named '%s'", id);
 166     return false;
 167 }
 168 
 169 /*!
 170  * \brief
 171  * \internal Check whether an ID refers to a valid resource or tag
 172  *
 173  * \param[in]  data_set Cluster working set
 174  * \param[in]  id       ID to search for
 175  * \param[out] rsc      Where to store resource, if found (or NULL to skip
 176  *                      searching resources)
 177  * \param[out] tag      Where to store tag, if found (or NULL to skip searching
 178  *                      tags)
 179  *
 180  * \return true if id refers to a resource (possibly indirectly via a tag)
 181  */
 182 bool
 183 pcmk__valid_resource_or_tag(const pe_working_set_t *data_set, const char *id,
     /* [previous][next][first][last][top][bottom][index][help] */
 184                             pe_resource_t **rsc, pe_tag_t **tag)
 185 {
 186     if (rsc != NULL) {
 187         *rsc = pcmk__find_constraint_resource(data_set->resources, id);
 188         if (*rsc != NULL) {
 189             return true;
 190         }
 191     }
 192 
 193     if ((tag != NULL) && find_constraint_tag(data_set, id, tag)) {
 194         return true;
 195     }
 196 
 197     return false;
 198 }
 199 
 200 /*!
 201  * \internal
 202  * \brief Replace any resource tags with equivalent resource_ref entries
 203  *
 204  * If a given constraint has resource sets, check each set for resource_ref
 205  * entries that list tags rather than resource IDs, and replace any found with
 206  * resource_ref entries for the corresponding resource IDs.
 207  *
 208  * \param[in,out] xml_obj   Constraint XML
 209  * \param[in]     data_set  Cluster working set
 210  *
 211  * \return Equivalent XML with resource tags replaced (or NULL if none)
 212  * \note It is the caller's responsibility to free the result with free_xml().
 213  */
 214 xmlNode *
 215 pcmk__expand_tags_in_sets(xmlNode *xml_obj, const pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 216 {
 217     xmlNode *new_xml = NULL;
 218     bool any_refs = false;
 219 
 220     // Short-circuit if there are no sets
 221     if (first_named_child(xml_obj, XML_CONS_TAG_RSC_SET) == NULL) {
 222         return NULL;
 223     }
 224 
 225     new_xml = copy_xml(xml_obj);
 226 
 227     for (xmlNode *set = first_named_child(new_xml, XML_CONS_TAG_RSC_SET);
 228          set != NULL; set = crm_next_same_xml(set)) {
 229 
 230         GList *tag_refs = NULL;
 231         GList *gIter = NULL;
 232 
 233         for (xmlNode *xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
 234              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 235 
 236             pe_resource_t *rsc = NULL;
 237             pe_tag_t *tag = NULL;
 238 
 239             if (!pcmk__valid_resource_or_tag(data_set, ID(xml_rsc), &rsc,
 240                                              &tag)) {
 241                 pcmk__config_err("Ignoring resource sets for constraint '%s' "
 242                                  "because '%s' is not a valid resource or tag",
 243                                  ID(xml_obj), ID(xml_rsc));
 244                 free_xml(new_xml);
 245                 return NULL;
 246 
 247             } else if (rsc) {
 248                 continue;
 249 
 250             } else if (tag) {
 251                 /* The resource_ref under the resource_set references a template/tag */
 252                 xmlNode *last_ref = xml_rsc;
 253 
 254                 /* A sample:
 255 
 256                    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                    Now we are appending rsc2 and rsc3 which are tagged with tag1 right 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 
 276                 for (gIter = tag->refs; gIter != NULL; gIter = gIter->next) {
 277                     const char *obj_ref = (const char *) gIter->data;
 278                     xmlNode *new_rsc_ref = NULL;
 279 
 280                     new_rsc_ref = xmlNewDocRawNode(getDocPtr(set), NULL,
 281                                                    (pcmkXmlStr) XML_TAG_RESOURCE_REF, NULL);
 282                     crm_xml_add(new_rsc_ref, XML_ATTR_ID, obj_ref);
 283                     xmlAddNextSibling(last_ref, new_rsc_ref);
 284 
 285                     last_ref = new_rsc_ref;
 286                 }
 287 
 288                 any_refs = true;
 289 
 290                 /* Freeing the resource_ref now would break the XML child
 291                  * iteration, so just remember it for freeing later.
 292                  */
 293                 tag_refs = g_list_append(tag_refs, xml_rsc);
 294             }
 295         }
 296 
 297         /* Now free '<resource_ref id="tag1"/>', and finally get:
 298 
 299            <resource_set id="tag1-colocation-0" sequential="true">
 300              <resource_ref id="rsc1"/>
 301              <resource_ref id="rsc2"/>
 302              <resource_ref id="rsc3"/>
 303              <resource_ref id="rsc4"/>
 304            </resource_set>
 305 
 306          */
 307         for (gIter = tag_refs; gIter != NULL; gIter = gIter->next) {
 308             xmlNode *tag_ref = gIter->data;
 309 
 310             free_xml(tag_ref);
 311         }
 312         g_list_free(tag_refs);
 313     }
 314 
 315     if (!any_refs) {
 316         free_xml(new_xml);
 317         new_xml = NULL;
 318     }
 319     return new_xml;
 320 }
 321 
 322 /*!
 323  * \internal
 324  * \brief Convert a tag into a resource set of tagged resources
 325  *
 326  * \param[in,out] xml_obj      Constraint XML
 327  * \param[out]    rsc_set      Where to store resource set XML created based on tag
 328  * \param[in]     attr         Name of XML attribute containing resource or tag ID
 329  * \param[in]     convert_rsc  Convert to set even if \p attr references a resource
 330  * \param[in]     data_set     Cluster working set
 331  */
 332 bool
 333 pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr,
     /* [previous][next][first][last][top][bottom][index][help] */
 334                  bool convert_rsc, const pe_working_set_t *data_set)
 335 {
 336     const char *cons_id = NULL;
 337     const char *id = NULL;
 338 
 339     pe_resource_t *rsc = NULL;
 340     pe_tag_t *tag = NULL;
 341 
 342     *rsc_set = NULL;
 343 
 344     CRM_CHECK((xml_obj != NULL) && (attr != NULL), return false);
 345 
 346     cons_id = ID(xml_obj);
 347     if (cons_id == NULL) {
 348         pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
 349                          crm_element_name(xml_obj));
 350         return false;
 351     }
 352 
 353     id = crm_element_value(xml_obj, attr);
 354     if (id == NULL) {
 355         return true;
 356     }
 357 
 358     if (!pcmk__valid_resource_or_tag(data_set, id, &rsc, &tag)) {
 359         pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
 360                          "valid resource or tag", cons_id, id);
 361         return false;
 362 
 363     } else if (tag) {
 364         GList *gIter = NULL;
 365 
 366         /* A template/tag is referenced by the "attr" attribute (first, then, rsc or with-rsc).
 367            Add the template/tag's corresponding "resource_set" which contains the resources derived
 368            from it or tagged with it under the constraint. */
 369         *rsc_set = create_xml_node(xml_obj, XML_CONS_TAG_RSC_SET);
 370         crm_xml_add(*rsc_set, XML_ATTR_ID, id);
 371 
 372         for (gIter = tag->refs; gIter != NULL; gIter = gIter->next) {
 373             const char *obj_ref = (const char *) gIter->data;
 374             xmlNode *rsc_ref = NULL;
 375 
 376             rsc_ref = create_xml_node(*rsc_set, XML_TAG_RESOURCE_REF);
 377             crm_xml_add(rsc_ref, XML_ATTR_ID, obj_ref);
 378         }
 379 
 380         /* Set sequential="false" for the resource_set */
 381         pcmk__xe_set_bool_attr(*rsc_set, "sequential", false);
 382 
 383     } else if ((rsc != NULL) && convert_rsc) {
 384         /* Even a regular resource is referenced by "attr", convert it into a resource_set.
 385            Because the other side of the constraint could be a template/tag reference. */
 386         xmlNode *rsc_ref = NULL;
 387 
 388         *rsc_set = create_xml_node(xml_obj, XML_CONS_TAG_RSC_SET);
 389         crm_xml_add(*rsc_set, XML_ATTR_ID, id);
 390 
 391         rsc_ref = create_xml_node(*rsc_set, XML_TAG_RESOURCE_REF);
 392         crm_xml_add(rsc_ref, XML_ATTR_ID, id);
 393 
 394     } else {
 395         return true;
 396     }
 397 
 398     /* Remove the "attr" attribute referencing the template/tag */
 399     if (*rsc_set != NULL) {
 400         xml_remove_prop(xml_obj, attr);
 401     }
 402 
 403     return true;
 404 }
 405 
 406 /*!
 407  * \internal
 408  * \brief Create constraints inherent to resource types
 409  *
 410  * \param[in,out] data_set  Cluster working set
 411  */
 412 void
 413 pcmk__create_internal_constraints(pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 414 {
 415     crm_trace("Create internal constraints");
 416     for (GList *iter = data_set->resources; iter != NULL; iter = iter->next) {
 417         pe_resource_t *rsc = (pe_resource_t *) iter->data;
 418 
 419         rsc->cmds->internal_constraints(rsc);
 420     }
 421 }

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