pacemaker  2.1.2-ada5c3b36
Scalable High-Availability cluster resource manager
pcmk_sched_tickets.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 <stdbool.h>
13 #include <glib.h>
14 
15 #include <crm/crm.h>
16 #include <crm/pengine/status.h>
17 #include <pacemaker-internal.h>
18 
19 #include "libpacemaker_private.h"
20 
30 static bool
31 ticket_role_matches(pe_resource_t *rsc_lh, rsc_ticket_t *rsc_ticket)
32 {
33  if ((rsc_ticket->role_lh == RSC_ROLE_UNKNOWN)
34  || (rsc_ticket->role_lh == rsc_lh->role)) {
35  return true;
36  }
37  pe_rsc_trace(rsc_lh, "LH: Skipping constraint: \"%s\" state filter",
38  role2text(rsc_ticket->role_lh));
39  return false;
40 }
41 
49 static void
50 constraints_for_ticket(pe_resource_t *rsc_lh, rsc_ticket_t *rsc_ticket,
51  pe_working_set_t *data_set)
52 {
53  GList *gIter = NULL;
54 
55  CRM_CHECK((rsc_lh != NULL) && (rsc_ticket != NULL), return);
56 
57  if (rsc_ticket->ticket->granted && !rsc_ticket->ticket->standby) {
58  return;
59  }
60 
61  if (rsc_lh->children) {
62  pe_rsc_trace(rsc_lh, "Processing ticket dependencies from %s", rsc_lh->id);
63  for (gIter = rsc_lh->children; gIter != NULL; gIter = gIter->next) {
64  constraints_for_ticket((pe_resource_t *) gIter->data, rsc_ticket,
65  data_set);
66  }
67  return;
68  }
69 
70  pe_rsc_trace(rsc_lh, "%s: Processing ticket dependency on %s (%s, %s)",
71  rsc_lh->id, rsc_ticket->ticket->id, rsc_ticket->id,
72  role2text(rsc_ticket->role_lh));
73 
74  if (!rsc_ticket->ticket->granted && (rsc_lh->running_on != NULL)) {
75 
76  switch (rsc_ticket->loss_policy) {
77  case loss_ticket_stop:
78  resource_location(rsc_lh, NULL, -INFINITY, "__loss_of_ticket__",
79  data_set);
80  break;
81 
82  case loss_ticket_demote:
83  // Promotion score will be set to -INFINITY in promotion_order()
84  if (rsc_ticket->role_lh != RSC_ROLE_PROMOTED) {
85  resource_location(rsc_lh, NULL, -INFINITY,
86  "__loss_of_ticket__", data_set);
87  }
88  break;
89 
90  case loss_ticket_fence:
91  if (!ticket_role_matches(rsc_lh, rsc_ticket)) {
92  return;
93  }
94 
95  resource_location(rsc_lh, NULL, -INFINITY, "__loss_of_ticket__",
96  data_set);
97 
98  for (gIter = rsc_lh->running_on; gIter != NULL;
99  gIter = gIter->next) {
100  pe_fence_node(data_set, (pe_node_t *) gIter->data,
101  "deadman ticket was lost", FALSE);
102  }
103  break;
104 
105  case loss_ticket_freeze:
106  if (!ticket_role_matches(rsc_lh, rsc_ticket)) {
107  return;
108  }
109  if (rsc_lh->running_on != NULL) {
112  }
113  break;
114  }
115 
116  } else if (!rsc_ticket->ticket->granted) {
117 
118  if ((rsc_ticket->role_lh != RSC_ROLE_PROMOTED)
119  || (rsc_ticket->loss_policy == loss_ticket_stop)) {
120  resource_location(rsc_lh, NULL, -INFINITY, "__no_ticket__",
121  data_set);
122  }
123 
124  } else if (rsc_ticket->ticket->standby) {
125 
126  if ((rsc_ticket->role_lh != RSC_ROLE_PROMOTED)
127  || (rsc_ticket->loss_policy == loss_ticket_stop)) {
128  resource_location(rsc_lh, NULL, -INFINITY, "__ticket_standby__",
129  data_set);
130  }
131  }
132 }
133 
134 static void
135 rsc_ticket_new(const char *id, pe_resource_t *rsc_lh, pe_ticket_t *ticket,
136  const char *state_lh, const char *loss_policy,
137  pe_working_set_t *data_set)
138 {
139  rsc_ticket_t *new_rsc_ticket = NULL;
140 
141  if (rsc_lh == NULL) {
142  pcmk__config_err("Ignoring ticket '%s' because resource "
143  "does not exist", id);
144  return;
145  }
146 
147  new_rsc_ticket = calloc(1, sizeof(rsc_ticket_t));
148  if (new_rsc_ticket == NULL) {
149  return;
150  }
151 
152  if (pcmk__str_eq(state_lh, RSC_ROLE_STARTED_S,
154  state_lh = RSC_ROLE_UNKNOWN_S;
155  }
156 
157  new_rsc_ticket->id = id;
158  new_rsc_ticket->ticket = ticket;
159  new_rsc_ticket->rsc_lh = rsc_lh;
160  new_rsc_ticket->role_lh = text2role(state_lh);
161 
162  if (pcmk__str_eq(loss_policy, "fence", pcmk__str_casei)) {
163  if (pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
164  new_rsc_ticket->loss_policy = loss_ticket_fence;
165  } else {
167  "' for ticket '%s' to 'stop' "
168  "because fencing is not configured", ticket->id);
169  loss_policy = "stop";
170  }
171  }
172 
173  if (new_rsc_ticket->loss_policy == loss_ticket_fence) {
174  crm_debug("On loss of ticket '%s': Fence the nodes running %s (%s)",
175  new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
176  role2text(new_rsc_ticket->role_lh));
177 
178  } else if (pcmk__str_eq(loss_policy, "freeze", pcmk__str_casei)) {
179  crm_debug("On loss of ticket '%s': Freeze %s (%s)",
180  new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
181  role2text(new_rsc_ticket->role_lh));
182  new_rsc_ticket->loss_policy = loss_ticket_freeze;
183 
184  } else if (pcmk__str_eq(loss_policy, "demote", pcmk__str_casei)) {
185  crm_debug("On loss of ticket '%s': Demote %s (%s)",
186  new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
187  role2text(new_rsc_ticket->role_lh));
188  new_rsc_ticket->loss_policy = loss_ticket_demote;
189 
190  } else if (pcmk__str_eq(loss_policy, "stop", pcmk__str_casei)) {
191  crm_debug("On loss of ticket '%s': Stop %s (%s)",
192  new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
193  role2text(new_rsc_ticket->role_lh));
194  new_rsc_ticket->loss_policy = loss_ticket_stop;
195 
196  } else {
197  if (new_rsc_ticket->role_lh == RSC_ROLE_PROMOTED) {
198  crm_debug("On loss of ticket '%s': Default to demote %s (%s)",
199  new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
200  role2text(new_rsc_ticket->role_lh));
201  new_rsc_ticket->loss_policy = loss_ticket_demote;
202 
203  } else {
204  crm_debug("On loss of ticket '%s': Default to stop %s (%s)",
205  new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
206  role2text(new_rsc_ticket->role_lh));
207  new_rsc_ticket->loss_policy = loss_ticket_stop;
208  }
209  }
210 
211  pe_rsc_trace(rsc_lh, "%s (%s) ==> %s",
212  rsc_lh->id, role2text(new_rsc_ticket->role_lh), ticket->id);
213 
214  rsc_lh->rsc_tickets = g_list_append(rsc_lh->rsc_tickets, new_rsc_ticket);
215 
216  data_set->ticket_constraints = g_list_append(data_set->ticket_constraints,
217  new_rsc_ticket);
218 
219  if (!(new_rsc_ticket->ticket->granted) || new_rsc_ticket->ticket->standby) {
220  constraints_for_ticket(rsc_lh, new_rsc_ticket, data_set);
221  }
222 }
223 
224 // \return Standard Pacemaker return code
225 static int
226 unpack_rsc_ticket_set(xmlNode *set, pe_ticket_t *ticket,
227  const char *loss_policy, pe_working_set_t *data_set)
228 {
229  const char *set_id = NULL;
230  const char *role = NULL;
231 
232  CRM_CHECK(set != NULL, return EINVAL);
233  CRM_CHECK(ticket != NULL, return EINVAL);
234 
235  set_id = ID(set);
236  if (set_id == NULL) {
237  pcmk__config_err("Ignoring <" XML_CONS_TAG_RSC_SET "> without "
238  XML_ATTR_ID);
240  }
241 
242  role = crm_element_value(set, "role");
243 
244  for (xmlNode *xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
245  xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
246 
247  pe_resource_t *resource = NULL;
248 
249  resource = pcmk__find_constraint_resource(data_set->resources,
250  ID(xml_rsc));
251  if (resource == NULL) {
252  pcmk__config_err("%s: No resource found for %s",
253  set_id, ID(xml_rsc));
255  }
256  pe_rsc_trace(resource, "Resource '%s' depends on ticket '%s'",
257  resource->id, ticket->id);
258  rsc_ticket_new(set_id, resource, ticket, role, loss_policy, data_set);
259  }
260 
261  return pcmk_rc_ok;
262 }
263 
264 static void
265 unpack_simple_rsc_ticket(xmlNode *xml_obj, pe_working_set_t *data_set)
266 {
267  const char *id = NULL;
268  const char *ticket_str = crm_element_value(xml_obj, XML_TICKET_ATTR_TICKET);
269  const char *loss_policy = crm_element_value(xml_obj,
271 
272  pe_ticket_t *ticket = NULL;
273 
274  const char *id_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
275  const char *state_lh = crm_element_value(xml_obj,
277 
278  // experimental syntax from pacemaker-next (unlikely to be adopted as-is)
279  const char *instance_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_INSTANCE);
280 
281  pe_resource_t *rsc_lh = NULL;
282 
283  CRM_CHECK(xml_obj != NULL, return);
284 
285  id = ID(xml_obj);
286  if (id == NULL) {
287  pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
288  crm_element_name(xml_obj));
289  return;
290  }
291 
292  if (ticket_str == NULL) {
293  pcmk__config_err("Ignoring constraint '%s' without ticket specified",
294  id);
295  return;
296  } else {
297  ticket = g_hash_table_lookup(data_set->tickets, ticket_str);
298  }
299 
300  if (ticket == NULL) {
301  pcmk__config_err("Ignoring constraint '%s' because ticket '%s' "
302  "does not exist", id, ticket_str);
303  return;
304  }
305 
306  if (id_lh == NULL) {
307  pcmk__config_err("Ignoring constraint '%s' without resource", id);
308  return;
309  } else {
310  rsc_lh = pcmk__find_constraint_resource(data_set->resources, id_lh);
311  }
312 
313  if (rsc_lh == NULL) {
314  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
315  "does not exist", id, id_lh);
316  return;
317 
318  } else if ((instance_lh != NULL) && !pe_rsc_is_clone(rsc_lh)) {
319  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
320  "is not a clone but instance '%s' was requested",
321  id, id_lh, instance_lh);
322  return;
323  }
324 
325  if (instance_lh != NULL) {
326  rsc_lh = find_clone_instance(rsc_lh, instance_lh, data_set);
327  if (rsc_lh == NULL) {
328  pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
329  "does not have an instance '%s'",
330  "'%s'", id, id_lh, instance_lh);
331  return;
332  }
333  }
334 
335  rsc_ticket_new(id, rsc_lh, ticket, state_lh, loss_policy, data_set);
336 }
337 
338 // \return Standard Pacemaker return code
339 static int
340 unpack_rsc_ticket_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
341  pe_working_set_t *data_set)
342 {
343  const char *id = NULL;
344  const char *id_lh = NULL;
345  const char *state_lh = NULL;
346 
347  pe_resource_t *rsc_lh = NULL;
348  pe_tag_t *tag_lh = NULL;
349 
350  xmlNode *rsc_set_lh = NULL;
351 
352  *expanded_xml = NULL;
353 
354  CRM_CHECK(xml_obj != NULL, return EINVAL);
355 
356  id = ID(xml_obj);
357  if (id == NULL) {
358  pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
359  crm_element_name(xml_obj));
361  }
362 
363  // Check whether there are any resource sets with template or tag references
364  *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, data_set);
365  if (*expanded_xml != NULL) {
366  crm_log_xml_trace(*expanded_xml, "Expanded rsc_ticket");
367  return pcmk_rc_ok;
368  }
369 
370  id_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
371  if (id_lh == NULL) {
372  return pcmk_rc_ok;
373  }
374 
375  if (!pcmk__valid_resource_or_tag(data_set, id_lh, &rsc_lh, &tag_lh)) {
376  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
377  "valid resource or tag", id, id_lh);
379 
380  } else if (rsc_lh) {
381  // No template or tag is referenced
382  return pcmk_rc_ok;
383  }
384 
385  state_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
386 
387  *expanded_xml = copy_xml(xml_obj);
388 
389  // Convert template/tag reference in "rsc" into resource_set under rsc_ticket
390  if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_lh, XML_COLOC_ATTR_SOURCE,
391  false, data_set)) {
392  free_xml(*expanded_xml);
393  *expanded_xml = NULL;
395  }
396 
397  if (rsc_set_lh != NULL) {
398  if (state_lh != NULL) {
399  // Move "rsc-role" into converted resource_set as a "role" attribute
400  crm_xml_add(rsc_set_lh, "role", state_lh);
402  }
403 
404  } else {
405  free_xml(*expanded_xml);
406  *expanded_xml = NULL;
407  }
408 
409  return pcmk_rc_ok;
410 }
411 
412 void
413 pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pe_working_set_t *data_set)
414 {
415  xmlNode *set = NULL;
416  bool any_sets = false;
417 
418  const char *id = NULL;
419  const char *ticket_str = crm_element_value(xml_obj, XML_TICKET_ATTR_TICKET);
420  const char *loss_policy = crm_element_value(xml_obj, XML_TICKET_ATTR_LOSS_POLICY);
421 
422  pe_ticket_t *ticket = NULL;
423 
424  xmlNode *orig_xml = NULL;
425  xmlNode *expanded_xml = NULL;
426 
427  CRM_CHECK(xml_obj != NULL, return);
428 
429  id = ID(xml_obj);
430  if (id == NULL) {
431  pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
432  crm_element_name(xml_obj));
433  return;
434  }
435 
436  if (data_set->tickets == NULL) {
437  data_set->tickets = pcmk__strkey_table(free, destroy_ticket);
438  }
439 
440  if (ticket_str == NULL) {
441  pcmk__config_err("Ignoring constraint '%s' without ticket", id);
442  return;
443  } else {
444  ticket = g_hash_table_lookup(data_set->tickets, ticket_str);
445  }
446 
447  if (ticket == NULL) {
448  ticket = ticket_new(ticket_str, data_set);
449  if (ticket == NULL) {
450  return;
451  }
452  }
453 
454  if (unpack_rsc_ticket_tags(xml_obj, &expanded_xml,
455  data_set) != pcmk_rc_ok) {
456  return;
457  }
458  if (expanded_xml != NULL) {
459  orig_xml = xml_obj;
460  xml_obj = expanded_xml;
461  }
462 
463  for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET); set != NULL;
464  set = crm_next_same_xml(set)) {
465 
466  any_sets = true;
467  set = expand_idref(set, data_set->input);
468  if ((set == NULL) // Configuration error, message already logged
469  || (unpack_rsc_ticket_set(set, ticket, loss_policy,
470  data_set) != pcmk_rc_ok)) {
471  if (expanded_xml != NULL) {
472  free_xml(expanded_xml);
473  }
474  return;
475  }
476  }
477 
478  if (expanded_xml) {
479  free_xml(expanded_xml);
480  xml_obj = orig_xml;
481  }
482 
483  if (!any_sets) {
484  unpack_simple_rsc_ticket(xml_obj, data_set);
485  }
486 }
pe_resource_t * rsc_lh
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:225
A dumping ground.
GList * rsc_tickets
Definition: pe_types.h:360
#define INFINITY
Definition: crm.h:99
enum rsc_role_e role
Definition: pe_types.h:370
#define pcmk__config_warn(fmt...)
#define RSC_ROLE_STARTED_S
Definition: common.h:112
GList * children
Definition: pe_types.h:377
gboolean standby
Definition: pe_types.h:460
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2789
#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 resource_location(pe_resource_t *rsc, pe_node_t *node, int score, const char *tag, pe_working_set_t *data_set)
Definition: utils.c:1691
pe_ticket_t * ticket
GHashTable * tickets
Definition: pe_types.h:152
#define XML_CONS_TAG_RSC_SET
Definition: msg_xml.h:349
#define pe__set_resource_flags(resource, flags_to_set)
Definition: internal.h:47
GList * resources
Definition: pe_types.h:158
G_GNUC_INTERNAL xmlNode * pcmk__expand_tags_in_sets(xmlNode *xml_obj, pe_working_set_t *data_set)
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:830
const char * role2text(enum rsc_role_e role)
Definition: common.c:459
GList * ticket_constraints
Definition: pe_types.h:162
#define crm_debug(fmt, args...)
Definition: logging.h:362
#define XML_ATTR_ID
Definition: msg_xml.h:129
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:529
void pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pe_working_set_t *data_set)
pe_ticket_t * ticket_new(const char *ticket_id, pe_working_set_t *data_set)
Definition: utils.c:1976
void pe_fence_node(pe_working_set_t *data_set, pe_node_t *node, const char *reason, bool priority_delay)
Schedule a fence action for a node.
Definition: unpack.c:96
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:114
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:2862
pe_resource_t * find_clone_instance(pe_resource_t *rsc, const char *sub_id, pe_working_set_t *data_set)
Definition: clone.c:84
enum loss_ticket_policy_e loss_policy
#define pe_flag_stonith_enabled
Definition: pe_types.h:98
#define XML_COLOC_ATTR_SOURCE_INSTANCE
Definition: msg_xml.h:359
#define XML_TAG_RESOURCE_REF
Definition: msg_xml.h:213
void free_xml(xmlNode *child)
Definition: xml.c:824
enum rsc_role_e text2role(const char *role)
Definition: common.c:488
xmlNode * input
Definition: pe_types.h:137
gboolean granted
Definition: pe_types.h:458
G_GNUC_INTERNAL pe_resource_t * pcmk__find_constraint_resource(GList *rsc_list, const char *id)
uint32_t id
Definition: cpg.c:45
#define XML_COLOC_ATTR_SOURCE_ROLE
Definition: msg_xml.h:355
char * id
Definition: pe_types.h:457
Cluster status and scheduling.
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:611
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:2032
#define pe__clear_resource_flags(resource, flags_to_clear)
Definition: internal.h:53
GList * running_on
Definition: pe_types.h:366
#define pe_rsc_block
Definition: pe_types.h:250
void destroy_ticket(gpointer data)
Definition: utils.c:1964
#define RSC_ROLE_UNKNOWN_S
Definition: common.h:110
#define crm_log_xml_trace(xml, text)
Definition: logging.h:371
const char * id
#define XML_TICKET_ATTR_TICKET
Definition: msg_xml.h:374
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:20
#define ID(x)
Definition: msg_xml.h:456
unsigned long long flags
Definition: pe_types.h:146
#define pe_rsc_managed
Definition: pe_types.h:249
#define XML_COLOC_ATTR_SOURCE
Definition: msg_xml.h:354
G_GNUC_INTERNAL bool pcmk__valid_resource_or_tag(pe_working_set_t *data_set, const char *id, pe_resource_t **rsc, pe_tag_t **tag)
G_GNUC_INTERNAL bool pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr, bool convert_rsc, pe_working_set_t *data_set)
char * id
Definition: pe_types.h:322
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2815
#define XML_TICKET_ATTR_LOSS_POLICY
Definition: msg_xml.h:375