pacemaker  3.0.0-d8340737c4
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/scheduler.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 
39 void
41 {
42  xmlNode *xml_constraints = pcmk_find_cib_element(scheduler->input,
44 
45  for (xmlNode *xml_obj = pcmk__xe_first_child(xml_constraints, NULL, NULL,
46  NULL);
47  xml_obj != NULL; xml_obj = pcmk__xe_next(xml_obj, NULL)) {
48 
49  const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
50  const char *tag = (const char *) xml_obj->name;
51 
52  if (id == NULL) {
53  pcmk__config_err("Ignoring <%s> constraint without "
54  PCMK_XA_ID, tag);
55  continue;
56  }
57 
58  crm_trace("Unpacking %s constraint '%s'", tag, id);
59 
60  if (pcmk__str_eq(PCMK_XE_RSC_ORDER, tag, pcmk__str_none)) {
62 
63  } else if (pcmk__str_eq(PCMK_XE_RSC_COLOCATION, tag, pcmk__str_none)) {
65 
66  } else if (pcmk__str_eq(PCMK_XE_RSC_LOCATION, tag, pcmk__str_none)) {
68 
69  } else if (pcmk__str_eq(PCMK_XE_RSC_TICKET, tag, pcmk__str_none)) {
71 
72  } else {
73  pcmk__config_err("Unsupported constraint type: %s", tag);
74  }
75  }
76 }
77 
79 pcmk__find_constraint_resource(GList *rsc_list, const char *id)
80 {
81  if (id == NULL) {
82  return NULL;
83  }
84  for (GList *iter = rsc_list; iter != NULL; iter = iter->next) {
85  pcmk_resource_t *parent = iter->data;
86  pcmk_resource_t *match = NULL;
87 
88  match = parent->priv->fns->find_rsc(parent, id, NULL,
90  if (match != NULL) {
91  if (!pcmk__str_eq(match->id, id, pcmk__str_none)) {
92  /* We found an instance of a clone instead */
93  match = uber_parent(match);
94  crm_debug("Found %s for %s", match->id, id);
95  }
96  return match;
97  }
98  }
99  crm_trace("No match for %s", id);
100  return NULL;
101 }
102 
114 static bool
115 find_constraint_tag(const pcmk_scheduler_t *scheduler, const char *id,
116  pcmk__idref_t **tag)
117 {
118  *tag = NULL;
119 
120  // Check whether id refers to a resource set template
121  if (g_hash_table_lookup_extended(scheduler->priv->templates, id,
122  NULL, (gpointer *) tag)) {
123  if (*tag == NULL) {
124  crm_notice("No resource is derived from template '%s'", id);
125  return false;
126  }
127  return true;
128  }
129 
130  // If not, check whether id refers to a tag
131  if (g_hash_table_lookup_extended(scheduler->priv->tags, id,
132  NULL, (gpointer *) tag)) {
133  if (*tag == NULL) {
134  crm_notice("No resource is tagged with '%s'", id);
135  return false;
136  }
137  return true;
138  }
139 
140  pcmk__config_warn("No resource, template, or tag named '%s'", id);
141  return false;
142 }
143 
158 int
159 pcmk__parse_constraint_role(const char *id, const char *role_spec,
160  enum rsc_role_e *role)
161 {
162  *role = pcmk_parse_role(role_spec);
163  switch (*role) {
164  case pcmk_role_unknown:
165  if (role_spec != NULL) {
166  pcmk__config_err("Ignoring constraint %s: Invalid role '%s'",
167  id, role_spec);
168  return pcmk_rc_unpack_error;
169  }
170  break;
171 
172  case pcmk_role_started:
173  *role = pcmk_role_unknown;
174  break;
175 
176  default:
177  break;
178  }
179  return pcmk_rc_ok;
180 }
181 
195 bool
197  pcmk_resource_t **rsc, pcmk__idref_t **tag)
198 {
199  if (rsc != NULL) {
201  if (*rsc != NULL) {
202  return true;
203  }
204  }
205 
206  if ((tag != NULL) && find_constraint_tag(scheduler, id, tag)) {
207  return true;
208  }
209 
210  return false;
211 }
212 
230 xmlNode *
232 {
233  xmlNode *new_xml = NULL;
234  bool any_refs = false;
235 
236  // Short-circuit if there are no sets
237  if (pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL,
238  NULL) == NULL) {
239  return NULL;
240  }
241 
242  new_xml = pcmk__xml_copy(NULL, xml_obj);
243 
244  for (xmlNode *set = pcmk__xe_first_child(new_xml, PCMK_XE_RESOURCE_SET,
245  NULL, NULL);
246  set != NULL; set = pcmk__xe_next(set, PCMK_XE_RESOURCE_SET)) {
247 
248  GList *tag_refs = NULL;
249  GList *iter = NULL;
250 
251  for (xmlNode *xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF,
252  NULL, NULL);
253  xml_rsc != NULL;
254  xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
255 
256  pcmk_resource_t *rsc = NULL;
257  pcmk__idref_t *tag = NULL;
258 
259  if (!pcmk__valid_resource_or_tag(scheduler, pcmk__xe_id(xml_rsc),
260  &rsc, &tag)) {
261  pcmk__config_err("Ignoring resource sets for constraint '%s' "
262  "because '%s' is not a valid resource or tag",
263  pcmk__xe_id(xml_obj), pcmk__xe_id(xml_rsc));
264  pcmk__xml_free(new_xml);
265  return NULL;
266 
267  } else if (rsc) {
268  continue;
269 
270  } else if (tag) {
271  /* PCMK_XE_RESOURCE_REF under PCMK_XE_RESOURCE_SET references
272  * template or tag
273  */
274  xmlNode *last_ref = xml_rsc;
275 
276  /* For example, given the original XML:
277  *
278  * <resource_set id="tag1-colocation-0" sequential="true">
279  * <resource_ref id="rsc1"/>
280  * <resource_ref id="tag1"/>
281  * <resource_ref id="rsc4"/>
282  * </resource_set>
283  *
284  * If rsc2 and rsc3 are tagged with tag1, we add them after it:
285  *
286  * <resource_set id="tag1-colocation-0" sequential="true">
287  * <resource_ref id="rsc1"/>
288  * <resource_ref id="tag1"/>
289  * <resource_ref id="rsc2"/>
290  * <resource_ref id="rsc3"/>
291  * <resource_ref id="rsc4"/>
292  * </resource_set>
293  */
294 
295  for (iter = tag->refs; iter != NULL; iter = iter->next) {
296  const char *ref_id = iter->data;
297  xmlNode *new_ref = pcmk__xe_create(set,
299 
300  crm_xml_add(new_ref, PCMK_XA_ID, ref_id);
301  xmlAddNextSibling(last_ref, new_ref);
302 
303  last_ref = new_ref;
304  }
305 
306  any_refs = true;
307 
308  /* Freeing the resource_ref now would break the XML child
309  * iteration, so just remember it for freeing later.
310  */
311  tag_refs = g_list_append(tag_refs, xml_rsc);
312  }
313  }
314 
315  /* Now free '<resource_ref id="tag1"/>', and finally get:
316 
317  <resource_set id="tag1-colocation-0" sequential="true">
318  <resource_ref id="rsc1"/>
319  <resource_ref id="rsc2"/>
320  <resource_ref id="rsc3"/>
321  <resource_ref id="rsc4"/>
322  </resource_set>
323 
324  */
325  for (iter = tag_refs; iter != NULL; iter = iter->next) {
326  xmlNode *tag_ref = iter->data;
327 
328  pcmk__xml_free(tag_ref);
329  }
330  g_list_free(tag_refs);
331  }
332 
333  if (!any_refs) {
334  pcmk__xml_free(new_xml);
335  new_xml = NULL;
336  }
337  return new_xml;
338 }
339 
351 bool
352 pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr,
353  bool convert_rsc, const pcmk_scheduler_t *scheduler)
354 {
355  const char *cons_id = NULL;
356  const char *id = NULL;
357 
358  pcmk_resource_t *rsc = NULL;
359  pcmk__idref_t *tag = NULL;
360 
361  *rsc_set = NULL;
362 
363  CRM_CHECK((xml_obj != NULL) && (attr != NULL), return false);
364 
365  cons_id = pcmk__xe_id(xml_obj);
366  if (cons_id == NULL) {
367  pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
368  xml_obj->name);
369  return false;
370  }
371 
372  id = crm_element_value(xml_obj, attr);
373  if (id == NULL) {
374  return true;
375  }
376 
377  if (!pcmk__valid_resource_or_tag(scheduler, id, &rsc, &tag)) {
378  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
379  "valid resource or tag", cons_id, id);
380  return false;
381 
382  } else if (tag) {
383  /* The "attr" attribute (for a resource in a constraint) specifies a
384  * template or tag. Add the corresponding PCMK_XE_RESOURCE_SET
385  * containing the resources derived from or tagged with it.
386  */
387  *rsc_set = pcmk__xe_create(xml_obj, PCMK_XE_RESOURCE_SET);
388  crm_xml_add(*rsc_set, PCMK_XA_ID, id);
389 
390  for (GList *iter = tag->refs; iter != NULL; iter = iter->next) {
391  const char *obj_ref = iter->data;
392  xmlNode *rsc_ref = NULL;
393 
394  rsc_ref = pcmk__xe_create(*rsc_set, PCMK_XE_RESOURCE_REF);
395  crm_xml_add(rsc_ref, PCMK_XA_ID, obj_ref);
396  }
397 
398  // Set PCMK_XA_SEQUENTIAL=PCMK_VALUE_FALSE for the PCMK_XE_RESOURCE_SET
399  pcmk__xe_set_bool_attr(*rsc_set, PCMK_XA_SEQUENTIAL, false);
400 
401  } else if ((rsc != NULL) && convert_rsc) {
402  /* Even if a regular resource is referenced by "attr", convert it into a
403  * PCMK_XE_RESOURCE_SET, because the other resource reference in the
404  * constraint could be a template or tag.
405  */
406  xmlNode *rsc_ref = NULL;
407 
408  *rsc_set = pcmk__xe_create(xml_obj, PCMK_XE_RESOURCE_SET);
409  crm_xml_add(*rsc_set, PCMK_XA_ID, id);
410 
411  rsc_ref = pcmk__xe_create(*rsc_set, PCMK_XE_RESOURCE_REF);
412  crm_xml_add(rsc_ref, PCMK_XA_ID, id);
413 
414  } else {
415  return true;
416  }
417 
418  /* Remove the "attr" attribute referencing the template/tag */
419  if (*rsc_set != NULL) {
420  pcmk__xe_remove_attr(xml_obj, attr);
421  }
422 
423  return true;
424 }
425 
432 void
434 {
435  crm_trace("Create internal constraints");
436  for (GList *iter = scheduler->priv->resources;
437  iter != NULL; iter = iter->next) {
438 
439  pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
440 
441  rsc->priv->cmds->internal_constraints(rsc);
442  }
443 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:213
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:805
A dumping ground.
G_GNUC_INTERNAL void pcmk__unpack_location(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml_element.c:42
#define crm_notice(fmt, args...)
Definition: logging.h:365
#define PCMK_XE_RSC_ORDER
Definition: xml_names.h:189
void pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value)
Definition: xml_element.c:1480
pcmk_resource_t * uber_parent(pcmk_resource_t *rsc)
Definition: complex.c:1017
int pcmk__parse_constraint_role(const char *id, const char *role_spec, enum rsc_role_e *role)
#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)
pcmk__scheduler_private_t * priv
Definition: scheduler.h:99
G_GNUC_INTERNAL void pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
Definition: xml_element.c:339
void(* internal_constraints)(pcmk_resource_t *rsc)
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition: xml_element.c:407
enum rsc_role_e pcmk_parse_role(const char *role)
Parse a resource role from a string role specification.
Definition: roles.c:51
Scheduler API.
void pcmk__xml_free(xmlNode *xml)
Definition: xml.c:789
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: xml_element.c:1015
Also match clone instance ID from resource history.
Definition: resources.h:34
#define crm_debug(fmt, args...)
Definition: logging.h:370
#define PCMK_XE_RSC_LOCATION
Definition: xml_names.h:188
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)
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: xml_element.c:1168
#define crm_trace(fmt, args...)
Definition: logging.h:372
pcmk__resource_private_t * priv
Definition: resources.h:61
Wrappers for and extensions to libxml2.
rsc_role_e
Definition: roles.h:34
ISO_8601 Date handling.
bool pcmk__valid_resource_or_tag(const pcmk_scheduler_t *scheduler, const char *id, pcmk_resource_t **rsc, pcmk__idref_t **tag)
#define PCMK_XA_ID
Definition: xml_names.h:301
G_GNUC_INTERNAL void pcmk__unpack_colocation(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
xmlNode * pcmk__xe_next(const xmlNode *node, const char *element_name)
Definition: xml_element.c:106
Cluster status and scheduling.
#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
xmlNode * input
Definition: scheduler.h:81
pcmk_resource_t * pcmk__find_constraint_resource(GList *rsc_list, const char *id)
Started.
Definition: roles.h:37
void pcmk__create_internal_constraints(pcmk_scheduler_t *scheduler)
Resource role is unknown.
Definition: roles.h:35
const char * parent
Definition: cib.c:27
#define PCMK_XA_SEQUENTIAL
Definition: xml_names.h:398
const pcmk__assignment_methods_t * cmds