pacemaker  2.1.9-49aab99839
Scalable High-Availability cluster resource manager
pcmk_sched_constraints.c
Go to the documentation of this file.
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>
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)
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 
59 void
61 {
62  xmlNode *xml_constraints = pcmk_find_cib_element(scheduler->input,
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)) {
93 
94  } else if (pcmk__str_eq(PCMK_XE_RSC_COLOCATION, tag, pcmk__str_none)) {
96 
97  } else if (pcmk__str_eq(PCMK_XE_RSC_LOCATION, tag, pcmk__str_none)) {
99 
100  } else if (pcmk__str_eq(PCMK_XE_RSC_TICKET, tag, pcmk__str_none)) {
102 
103  } else {
104  pcmk__config_err("Unsupported constraint type: %s", tag);
105  }
106  }
107 }
108 
110 pcmk__find_constraint_resource(GList *rsc_list, const char *id)
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,
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 
144 static bool
145 find_constraint_tag(const pcmk_scheduler_t *scheduler, const char *id,
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 
187 bool
189  pcmk_resource_t **rsc, pcmk_tag_t **tag)
190 {
191  if (rsc != NULL) {
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 
221 xmlNode *
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,
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 
341 bool
342 pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr,
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 
422 void
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 }
pcmk_assignment_methods_t * cmds
Definition: resources.h:413
crm_time_t * crm_time_new_undefined(void)
Allocate memory for an uninitialized time object.
Definition: iso8601.c:129
GHashTable * tags
Definition: scheduler.h:253
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:245
int pcmk__evaluate_rules(xmlNode *xml, const pcmk_rule_input_t *rule_input, crm_time_t *next_change)
Definition: rules.c:1487
bool pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr, bool convert_rsc, const pcmk_scheduler_t *scheduler)
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition: xml.c:974
A dumping ground.
G_GNUC_INTERNAL void pcmk__unpack_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
#define crm_notice(fmt, args...)
Definition: logging.h:397
#define PCMK_XE_RSC_ORDER
Definition: xml_names.h:189
const crm_time_t * now
Current time for rule evaluation purposes.
Definition: rules.h:59
Data used to evaluate a rule (any NULL items are ignored)
Definition: rules.h:57
#define PCMK__XE_LIFETIME
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:1046
struct crm_time_s crm_time_t
Definition: iso8601.h:32
#define pcmk__config_warn(fmt...)
#define PCMK_XE_CONSTRAINTS
Definition: xml_names.h:89
#define PCMK_XE_RSC_COLOCATION
Definition: xml_names.h:185
#define PCMK_XE_RESOURCE_REF
Definition: xml_names.h:177
#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:313
void pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value)
Definition: nvpair.c:987
G_GNUC_INTERNAL void pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
GList * resources
Definition: scheduler.h:231
Also match clone instance ID from resource history.
Definition: resources.h:185
bool pcmk__valid_resource_or_tag(const pcmk_scheduler_t *scheduler, const char *id, pcmk_resource_t **rsc, pcmk_tag_t **tag)
#define crm_debug(fmt, args...)
Definition: logging.h:402
#define PCMK_XE_RSC_LOCATION
Definition: xml_names.h:188
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:458
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:481
xmlNode * pcmk_find_cib_element(xmlNode *cib, const char *element_name)
Find an element in the CIB.
Definition: cib.c:172
G_GNUC_INTERNAL void pcmk__unpack_ordering(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
#define crm_trace(fmt, args...)
Definition: logging.h:404
Wrappers for and extensions to libxml2.
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
Definition: xml.c:702
ISO_8601 Date handling.
#define PCMK_XA_ID
Definition: xml_names.h:301
void free_xml(xmlNode *child)
Definition: xml.c:958
xmlNode * input
Definition: scheduler.h:196
long long crm_time_get_seconds_since_epoch(const crm_time_t *dt)
Definition: iso8601.c:374
void pe__update_recheck_time(time_t recheck, pcmk_scheduler_t *scheduler, const char *reason)
Definition: utils.c:694
G_GNUC_INTERNAL void pcmk__unpack_colocation(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
GList * refs
Definition: tags.h:31
Cluster status and scheduling.
pcmk__action_result_t result
Definition: pcmk_fence.c:35
void(* internal_constraints)(pcmk_resource_t *rsc)
#define PCMK_XE_RSC_TICKET
Definition: xml_names.h:190
pcmk_scheduler_t * scheduler
Cluster Configuration.
xmlNode * pcmk__expand_tags_in_sets(xmlNode *xml_obj, const pcmk_scheduler_t *scheduler)
#define PCMK_XE_RESOURCE_SET
Definition: xml_names.h:178
pcmk_resource_t * pcmk__find_constraint_resource(GList *rsc_list, const char *id)
Definition: tags.h:29
void pcmk__create_internal_constraints(pcmk_scheduler_t *scheduler)
const char * parent
Definition: cib.c:27
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition: xml.c:770
xmlNode * pcmk__xe_next_same(const xmlNode *node)
Definition: xml.c:2130
#define PCMK_XA_SEQUENTIAL
Definition: xml_names.h:398
crm_time_t * now
Definition: scheduler.h:198
#define crm_info(fmt, args...)
Definition: logging.h:399
GHashTable * template_rsc_sets
Definition: scheduler.h:248
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:150