pacemaker  2.1.3-ea053b43a
Scalable High-Availability cluster resource manager
pcmk_sched_constraints.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2021 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, pe_working_set_t *data_set)
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 
41  }
42  crm_time_free(next_change);
43  return result;
44 }
45 
55 void
57 {
58  xmlNode *xml_constraints = pcmk_find_cib_element(data_set->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 = 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 descendents 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)) {
89 
90  } else if (pcmk__str_eq(XML_CONS_TAG_RSC_DEPEND, tag, pcmk__str_casei)) {
92 
93  } else if (pcmk__str_eq(XML_CONS_TAG_RSC_LOCATION, tag, pcmk__str_casei)) {
95 
96  } else if (pcmk__str_eq(XML_CONS_TAG_RSC_TICKET, tag, pcmk__str_casei)) {
98 
99  } else {
100  pe_err("Unsupported constraint type: %s", tag);
101  }
102  }
103 }
104 
106 pcmk__find_constraint_resource(GList *rsc_list, const char *id)
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,
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 
139 static bool
140 find_constraint_tag(pe_working_set_t *data_set, const char *id, pe_tag_t **tag)
141 {
142  *tag = NULL;
143 
144  // Check whether id refers to a resource set template
145  if (g_hash_table_lookup_extended(data_set->template_rsc_sets, id,
146  NULL, (gpointer *) tag)) {
147  if (*tag == NULL) {
148  crm_warn("No resource is derived from template '%s'", id);
149  return false;
150  }
151  return true;
152  }
153 
154  // If not, check whether id refers to a tag
155  if (g_hash_table_lookup_extended(data_set->tags, id,
156  NULL, (gpointer *) tag)) {
157  if (*tag == NULL) {
158  crm_warn("No resource is tagged with '%s'", id);
159  return false;
160  }
161  return true;
162  }
163 
164  crm_warn("No template or tag named '%s'", id);
165  return false;
166 }
167 
181 bool
183  pe_resource_t **rsc, pe_tag_t **tag)
184 {
185  if (rsc != NULL) {
187  if (*rsc != NULL) {
188  return true;
189  }
190  }
191 
192  if ((tag != NULL) && find_constraint_tag(data_set, id, tag)) {
193  return true;
194  }
195 
196  return false;
197 }
198 
213 xmlNode *
215 {
216  xmlNode *new_xml = NULL;
217  bool any_refs = false;
218 
219  // Short-circuit if there are no sets
220  if (first_named_child(xml_obj, XML_CONS_TAG_RSC_SET) == NULL) {
221  return NULL;
222  }
223 
224  new_xml = copy_xml(xml_obj);
225 
226  for (xmlNode *set = first_named_child(new_xml, XML_CONS_TAG_RSC_SET);
227  set != NULL; set = crm_next_same_xml(set)) {
228 
229  GList *tag_refs = NULL;
230  GList *gIter = NULL;
231 
232  for (xmlNode *xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
233  xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
234 
235  pe_resource_t *rsc = NULL;
236  pe_tag_t *tag = NULL;
237 
238  if (!pcmk__valid_resource_or_tag(data_set, ID(xml_rsc), &rsc,
239  &tag)) {
240  pcmk__config_err("Ignoring resource sets for constraint '%s' "
241  "because '%s' is not a valid resource or tag",
242  ID(xml_obj), ID(xml_rsc));
243  free_xml(new_xml);
244  return NULL;
245 
246  } else if (rsc) {
247  continue;
248 
249  } else if (tag) {
250  /* The resource_ref under the resource_set references a template/tag */
251  xmlNode *last_ref = xml_rsc;
252 
253  /* A sample:
254 
255  Original XML:
256 
257  <resource_set id="tag1-colocation-0" sequential="true">
258  <resource_ref id="rsc1"/>
259  <resource_ref id="tag1"/>
260  <resource_ref id="rsc4"/>
261  </resource_set>
262 
263  Now we are appending rsc2 and rsc3 which are tagged with tag1 right after it:
264 
265  <resource_set id="tag1-colocation-0" sequential="true">
266  <resource_ref id="rsc1"/>
267  <resource_ref id="tag1"/>
268  <resource_ref id="rsc2"/>
269  <resource_ref id="rsc3"/>
270  <resource_ref id="rsc4"/>
271  </resource_set>
272 
273  */
274 
275  for (gIter = tag->refs; gIter != NULL; gIter = gIter->next) {
276  const char *obj_ref = (const char *) gIter->data;
277  xmlNode *new_rsc_ref = NULL;
278 
279  new_rsc_ref = xmlNewDocRawNode(getDocPtr(set), NULL,
281  crm_xml_add(new_rsc_ref, XML_ATTR_ID, obj_ref);
282  xmlAddNextSibling(last_ref, new_rsc_ref);
283 
284  last_ref = new_rsc_ref;
285  }
286 
287  any_refs = true;
288 
289  /* Freeing the resource_ref now would break the XML child
290  * iteration, so just remember it for freeing later.
291  */
292  tag_refs = g_list_append(tag_refs, xml_rsc);
293  }
294  }
295 
296  /* Now free '<resource_ref id="tag1"/>', and finally get:
297 
298  <resource_set id="tag1-colocation-0" sequential="true">
299  <resource_ref id="rsc1"/>
300  <resource_ref id="rsc2"/>
301  <resource_ref id="rsc3"/>
302  <resource_ref id="rsc4"/>
303  </resource_set>
304 
305  */
306  for (gIter = tag_refs; gIter != NULL; gIter = gIter->next) {
307  xmlNode *tag_ref = gIter->data;
308 
309  free_xml(tag_ref);
310  }
311  g_list_free(tag_refs);
312  }
313 
314  if (!any_refs) {
315  free_xml(new_xml);
316  new_xml = NULL;
317  }
318  return new_xml;
319 }
320 
331 bool
332 pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr,
333  bool convert_rsc, pe_working_set_t *data_set)
334 {
335  const char *cons_id = NULL;
336  const char *id = NULL;
337 
338  pe_resource_t *rsc = NULL;
339  pe_tag_t *tag = NULL;
340 
341  *rsc_set = NULL;
342 
343  CRM_CHECK((xml_obj != NULL) && (attr != NULL), return false);
344 
345  cons_id = ID(xml_obj);
346  if (cons_id == NULL) {
347  pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
348  crm_element_name(xml_obj));
349  return false;
350  }
351 
352  id = crm_element_value(xml_obj, attr);
353  if (id == NULL) {
354  return true;
355  }
356 
357  if (!pcmk__valid_resource_or_tag(data_set, id, &rsc, &tag)) {
358  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
359  "valid resource or tag", cons_id, id);
360  return false;
361 
362  } else if (tag) {
363  GList *gIter = NULL;
364 
365  /* A template/tag is referenced by the "attr" attribute (first, then, rsc or with-rsc).
366  Add the template/tag's corresponding "resource_set" which contains the resources derived
367  from it or tagged with it under the constraint. */
368  *rsc_set = create_xml_node(xml_obj, XML_CONS_TAG_RSC_SET);
369  crm_xml_add(*rsc_set, XML_ATTR_ID, id);
370 
371  for (gIter = tag->refs; gIter != NULL; gIter = gIter->next) {
372  const char *obj_ref = (const char *) gIter->data;
373  xmlNode *rsc_ref = NULL;
374 
375  rsc_ref = create_xml_node(*rsc_set, XML_TAG_RESOURCE_REF);
376  crm_xml_add(rsc_ref, XML_ATTR_ID, obj_ref);
377  }
378 
379  /* Set sequential="false" for the resource_set */
380  pcmk__xe_set_bool_attr(*rsc_set, "sequential", false);
381 
382  } else if ((rsc != NULL) && convert_rsc) {
383  /* Even a regular resource is referenced by "attr", convert it into a resource_set.
384  Because the other side of the constraint could be a template/tag reference. */
385  xmlNode *rsc_ref = NULL;
386 
387  *rsc_set = create_xml_node(xml_obj, XML_CONS_TAG_RSC_SET);
388  crm_xml_add(*rsc_set, XML_ATTR_ID, id);
389 
390  rsc_ref = create_xml_node(*rsc_set, XML_TAG_RESOURCE_REF);
391  crm_xml_add(rsc_ref, XML_ATTR_ID, id);
392 
393  } else {
394  return true;
395  }
396 
397  /* Remove the "attr" attribute referencing the template/tag */
398  if (*rsc_set != NULL) {
399  xml_remove_prop(xml_obj, attr);
400  }
401 
402  return true;
403 }
404 
411 void
413 {
414  crm_trace("Create internal constraints");
415  for (GList *iter = data_set->resources; iter != NULL; iter = iter->next) {
416  pe_resource_t *rsc = (pe_resource_t *) iter->data;
417 
418  rsc->cmds->internal_constraints(rsc, data_set);
419  }
420 }
crm_time_t * crm_time_new_undefined(void)
Allocate memory for an uninitialized time object.
Definition: iso8601.c:116
GHashTable * tags
Definition: pe_types.h:187
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:226
A dumping ground.
bool crm_time_is_defined(const crm_time_t *t)
Check whether a time object has been initialized yet.
Definition: iso8601.c:132
struct crm_time_s crm_time_t
Definition: iso8601.h:32
void pe__update_recheck_time(time_t recheck, pe_working_set_t *data_set)
Definition: utils.c:2393
#define pcmk__config_warn(fmt...)
#define XML_CIB_TAG_CONSTRAINTS
Definition: msg_xml.h:189
resource_alloc_functions_t * cmds
Definition: pe_types.h:348
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2794
#define pcmk__config_err(fmt...)
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:323
void pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value)
Definition: nvpair.c:954
#define XML_CONS_TAG_RSC_DEPEND
Definition: msg_xml.h:351
void(* internal_constraints)(pe_resource_t *, pe_working_set_t *)
#define XML_CONS_TAG_RSC_TICKET
Definition: msg_xml.h:354
#define XML_CONS_TAG_RSC_SET
Definition: msg_xml.h:355
void pcmk__create_internal_constraints(pe_working_set_t *data_set)
xmlDoc * getDocPtr(xmlNode *node)
Definition: xml.c:658
GList * resources
Definition: pe_types.h:165
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:830
#define crm_warn(fmt, args...)
Definition: logging.h:359
#define crm_debug(fmt, args...)
Definition: logging.h:363
pe_resource_t * uber_parent(pe_resource_t *rsc)
Definition: complex.c:913
#define XML_ATTR_ID
Definition: msg_xml.h:135
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:529
xmlNode * pcmk_find_cib_element(xmlNode *cib, const char *element_name)
Find an element in the CIB.
Definition: cib.c:153
#define XML_CONS_TAG_RSC_LOCATION
Definition: msg_xml.h:353
#define crm_trace(fmt, args...)
Definition: logging.h:364
pe_working_set_t * data_set
Wrappers for and extensions to libxml2.
ISO_8601 Date handling.
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:696
long long int crm_time_get_seconds_since_epoch(crm_time_t *dt)
Definition: iso8601.c:350
#define XML_TAG_RESOURCE_REF
Definition: msg_xml.h:219
void pcmk__unpack_constraints(pe_working_set_t *data_set)
void free_xml(xmlNode *child)
Definition: xml.c:824
bool pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr, bool convert_rsc, pe_working_set_t *data_set)
xmlNode * input
Definition: pe_types.h:144
const xmlChar * pcmkXmlStr
Definition: xml.h:52
xmlNode * pcmk__expand_tags_in_sets(xmlNode *xml_obj, pe_working_set_t *data_set)
GList * refs
Definition: pe_types.h:480
match resource ID or LRM history ID
Definition: pe_types.h:85
Cluster status and scheduling.
pcmk__action_result_t result
Definition: pcmk_fence.c:34
pe_resource_t * pcmk__find_constraint_resource(GList *rsc_list, const char *id)
Cluster Configuration.
G_GNUC_INTERNAL void pcmk__unpack_ordering(xmlNode *xml_obj, pe_working_set_t *data_set)
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:2037
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
bool pcmk__valid_resource_or_tag(pe_working_set_t *data_set, const char *id, pe_resource_t **rsc, pe_tag_t **tag)
#define XML_CONS_TAG_RSC_ORDER
Definition: msg_xml.h:352
G_GNUC_INTERNAL void pcmk__unpack_colocation(xmlNode *xml_obj, pe_working_set_t *data_set)
G_GNUC_INTERNAL void pcmk__unpack_location(xmlNode *xml_obj, pe_working_set_t *data_set)
G_GNUC_INTERNAL void pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pe_working_set_t *data_set)
#define ID(x)
Definition: msg_xml.h:460
#define pe_err(fmt...)
Definition: internal.h:24
const char * parent
Definition: cib.c:25
crm_time_t * now
Definition: pe_types.h:145
#define crm_info(fmt, args...)
Definition: logging.h:361
GHashTable * template_rsc_sets
Definition: pe_types.h:185
char * id
Definition: pe_types.h:336
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2820
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:140