pacemaker  2.1.7-0f7f88312f
Scalable High-Availability cluster resource manager
pcmk_sched_constraints.c
Go to the documentation of this file.
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>
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)
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 
55 void
57 {
58  xmlNode *xml_constraints = pcmk_find_cib_element(scheduler->input,
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)) {
89 
90  } else if (pcmk__str_eq(XML_CONS_TAG_RSC_DEPEND, tag, pcmk__str_none)) {
92 
93  } else if (pcmk__str_eq(XML_CONS_TAG_RSC_LOCATION, tag,
94  pcmk__str_none)) {
96 
97  } else if (pcmk__str_eq(XML_CONS_TAG_RSC_TICKET, tag, pcmk__str_none)) {
99 
100  } else {
101  pe_err("Unsupported constraint type: %s", tag);
102  }
103  }
104 }
105 
107 pcmk__find_constraint_resource(GList *rsc_list, const char *id)
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,
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 
141 static bool
142 find_constraint_tag(const pcmk_scheduler_t *scheduler, const char *id,
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 
184 bool
186  pcmk_resource_t **rsc, pcmk_tag_t **tag)
187 {
188  if (rsc != NULL) {
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 
216 xmlNode *
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)
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 
334 bool
335 pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr,
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 
415 void
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 }
pcmk_assignment_methods_t * cmds
Resource assignment methods.
Definition: resources.h:417
crm_time_t * crm_time_new_undefined(void)
Allocate memory for an uninitialized time object.
Definition: iso8601.c:126
GHashTable * tags
Configuration tags (ID -> pcmk_tag_t *)
Definition: scheduler.h:218
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:238
bool pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr, bool convert_rsc, const pcmk_scheduler_t *scheduler)
A dumping ground.
G_GNUC_INTERNAL void pcmk__unpack_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
bool crm_time_is_defined(const crm_time_t *t)
Check whether a time object has been initialized yet.
Definition: iso8601.c:142
pcmk_resource_t * uber_parent(pcmk_resource_t *rsc)
Definition: complex.c:936
struct crm_time_s crm_time_t
Definition: iso8601.h:32
#define pcmk__config_warn(fmt...)
#define XML_CIB_TAG_CONSTRAINTS
Definition: msg_xml.h:207
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2484
#define pcmk__config_err(fmt...)
void pcmk__unpack_constraints(pcmk_scheduler_t *scheduler)
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: nvpair.c:302
void pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value)
Definition: nvpair.c:872
#define XML_CONS_TAG_RSC_DEPEND
Definition: msg_xml.h:353
G_GNUC_INTERNAL void pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
#define XML_CONS_TAG_RSC_TICKET
Definition: msg_xml.h:356
Implementation of pcmk_scheduler_t.
Definition: scheduler.h:172
#define XML_CONS_TAG_RSC_SET
Definition: msg_xml.h:357
GList * resources
Resources in cluster.
Definition: scheduler.h:196
Also match clone instance ID from resource history.
Definition: resources.h:199
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:789
#define crm_warn(fmt, args...)
Definition: logging.h:382
bool pcmk__valid_resource_or_tag(const pcmk_scheduler_t *scheduler, const char *id, pcmk_resource_t **rsc, pcmk_tag_t **tag)
Implementation of pcmk_resource_t.
Definition: resources.h:399
#define crm_debug(fmt, args...)
Definition: logging.h:386
#define XML_ATTR_ID
Definition: msg_xml.h:156
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:447
xmlNode * pcmk_find_cib_element(xmlNode *cib, const char *element_name)
Find an element in the CIB.
Definition: cib.c:172
#define XML_CONS_TAG_RSC_LOCATION
Definition: msg_xml.h:355
G_GNUC_INTERNAL void pcmk__unpack_ordering(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
#define crm_trace(fmt, args...)
Definition: logging.h:387
Wrappers for and extensions to libxml2.
ISO_8601 Date handling.
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:638
#define XML_TAG_RESOURCE_REF
Definition: msg_xml.h:234
void free_xml(xmlNode *child)
Definition: xml.c:783
xmlNode * input
CIB XML.
Definition: scheduler.h:175
long long crm_time_get_seconds_since_epoch(const crm_time_t *dt)
Definition: iso8601.c:359
void pe__update_recheck_time(time_t recheck, pcmk_scheduler_t *scheduler, const char *reason)
Definition: utils.c:682
G_GNUC_INTERNAL void pcmk__unpack_colocation(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
const xmlChar * pcmkXmlStr
Definition: xml.h:50
GList * refs
XML IDs of objects that reference the tag.
Definition: tags.h:28
Cluster status and scheduling.
pcmk__action_result_t result
Definition: pcmk_fence.c:35
void(* internal_constraints)(pcmk_resource_t *rsc)
pcmk_scheduler_t * scheduler
Cluster Configuration.
xmlNode * pcmk__expand_tags_in_sets(xmlNode *xml_obj, const pcmk_scheduler_t *scheduler)
pcmk_resource_t * pcmk__find_constraint_resource(GList *rsc_list, const char *id)
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:1696
Configuration tag object.
Definition: tags.h:26
gboolean pe_evaluate_rules(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now, crm_time_t *next_change)
Evaluate any rules contained by given XML element.
Definition: rules.c:39
#define XML_CONS_TAG_RSC_ORDER
Definition: msg_xml.h:354
void pcmk__create_internal_constraints(pcmk_scheduler_t *scheduler)
#define ID(x)
Definition: msg_xml.h:474
#define pe_err(fmt...)
Definition: internal.h:39
const char * parent
Definition: cib.c:27
crm_time_t * now
Current time for evaluation purposes.
Definition: scheduler.h:176
#define crm_info(fmt, args...)
Definition: logging.h:384
GHashTable * template_rsc_sets
Mappings of template ID to resource ID.
Definition: scheduler.h:213
char * id
Resource ID in configuration.
Definition: resources.h:400
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2510
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:150