pacemaker  2.1.8-3980678f03
Scalable High-Availability cluster resource manager
pcmk_sched_tickets.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 <stdbool.h>
13 #include <glib.h>
14 
15 #include <crm/crm.h>
17 #include <crm/pengine/status.h>
18 #include <pacemaker-internal.h>
19 
20 #include "libpacemaker_private.h"
21 
27 };
28 
29 typedef struct {
30  const char *id;
31  pcmk_resource_t *rsc;
32  pcmk_ticket_t *ticket;
33  enum loss_ticket_policy loss_policy;
34  int role;
35 } rsc_ticket_t;
36 
46 static bool
47 ticket_role_matches(const pcmk_resource_t *rsc, const rsc_ticket_t *rsc_ticket)
48 {
49  if ((rsc_ticket->role == pcmk_role_unknown)
50  || (rsc_ticket->role == rsc->role)) {
51  return true;
52  }
53  pcmk__rsc_trace(rsc, "Skipping constraint: \"%s\" state filter",
54  pcmk_role_text(rsc_ticket->role));
55  return false;
56 }
57 
64 static void
65 constraints_for_ticket(pcmk_resource_t *rsc, const rsc_ticket_t *rsc_ticket)
66 {
67  GList *iter = NULL;
68 
69  CRM_CHECK((rsc != NULL) && (rsc_ticket != NULL), return);
70 
71  if (rsc_ticket->ticket->granted && !rsc_ticket->ticket->standby) {
72  return;
73  }
74 
75  if (rsc->children) {
76  pcmk__rsc_trace(rsc, "Processing ticket dependencies from %s", rsc->id);
77  for (iter = rsc->children; iter != NULL; iter = iter->next) {
78  constraints_for_ticket((pcmk_resource_t *) iter->data, rsc_ticket);
79  }
80  return;
81  }
82 
83  pcmk__rsc_trace(rsc, "%s: Processing ticket dependency on %s (%s, %s)",
84  rsc->id, rsc_ticket->ticket->id, rsc_ticket->id,
85  pcmk_role_text(rsc_ticket->role));
86 
87  if (!rsc_ticket->ticket->granted && (rsc->running_on != NULL)) {
88 
89  switch (rsc_ticket->loss_policy) {
90  case loss_ticket_stop:
92  "__loss_of_ticket__", rsc->cluster);
93  break;
94 
95  case loss_ticket_demote:
96  // Promotion score will be set to -INFINITY in promotion_order()
97  if (rsc_ticket->role != pcmk_role_promoted) {
99  "__loss_of_ticket__", rsc->cluster);
100  }
101  break;
102 
103  case loss_ticket_fence:
104  if (!ticket_role_matches(rsc, rsc_ticket)) {
105  return;
106  }
107 
109  "__loss_of_ticket__", rsc->cluster);
110 
111  for (iter = rsc->running_on; iter != NULL; iter = iter->next) {
112  pe_fence_node(rsc->cluster, (pcmk_node_t *) iter->data,
113  "deadman ticket was lost", FALSE);
114  }
115  break;
116 
117  case loss_ticket_freeze:
118  if (!ticket_role_matches(rsc, rsc_ticket)) {
119  return;
120  }
121  if (rsc->running_on != NULL) {
124  }
125  break;
126  }
127 
128  } else if (!rsc_ticket->ticket->granted) {
129 
130  if ((rsc_ticket->role != pcmk_role_promoted)
131  || (rsc_ticket->loss_policy == loss_ticket_stop)) {
133  "__no_ticket__", rsc->cluster);
134  }
135 
136  } else if (rsc_ticket->ticket->standby) {
137 
138  if ((rsc_ticket->role != pcmk_role_promoted)
139  || (rsc_ticket->loss_policy == loss_ticket_stop)) {
141  "__ticket_standby__", rsc->cluster);
142  }
143  }
144 }
145 
146 static void
147 rsc_ticket_new(const char *id, pcmk_resource_t *rsc, pcmk_ticket_t *ticket,
148  const char *state, const char *loss_policy)
149 {
150  rsc_ticket_t *new_rsc_ticket = NULL;
151 
152  if (rsc == NULL) {
153  pcmk__config_err("Ignoring ticket '%s' because resource "
154  "does not exist", id);
155  return;
156  }
157 
158  new_rsc_ticket = calloc(1, sizeof(rsc_ticket_t));
159  if (new_rsc_ticket == NULL) {
160  return;
161  }
162 
163  if (pcmk__str_eq(state, PCMK_ROLE_STARTED,
165  state = PCMK__ROLE_UNKNOWN;
166  }
167 
168  new_rsc_ticket->id = id;
169  new_rsc_ticket->ticket = ticket;
170  new_rsc_ticket->rsc = rsc;
171  new_rsc_ticket->role = pcmk_parse_role(state);
172 
173  if (pcmk__str_eq(loss_policy, PCMK_VALUE_FENCE, pcmk__str_casei)) {
175  new_rsc_ticket->loss_policy = loss_ticket_fence;
176  } else {
177  pcmk__config_err("Resetting '" PCMK_XA_LOSS_POLICY "' "
178  "for ticket '%s' to '" PCMK_VALUE_STOP "' "
179  "because fencing is not configured", ticket->id);
180  loss_policy = PCMK_VALUE_STOP;
181  }
182  }
183 
184  if (new_rsc_ticket->loss_policy == loss_ticket_fence) {
185  crm_debug("On loss of ticket '%s': Fence the nodes running %s (%s)",
186  new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id,
187  pcmk_role_text(new_rsc_ticket->role));
188 
189  } else if (pcmk__str_eq(loss_policy, PCMK_VALUE_FREEZE, pcmk__str_casei)) {
190  crm_debug("On loss of ticket '%s': Freeze %s (%s)",
191  new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id,
192  pcmk_role_text(new_rsc_ticket->role));
193  new_rsc_ticket->loss_policy = loss_ticket_freeze;
194 
195  } else if (pcmk__str_eq(loss_policy, PCMK_VALUE_DEMOTE, pcmk__str_casei)) {
196  crm_debug("On loss of ticket '%s': Demote %s (%s)",
197  new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id,
198  pcmk_role_text(new_rsc_ticket->role));
199  new_rsc_ticket->loss_policy = loss_ticket_demote;
200 
201  } else if (pcmk__str_eq(loss_policy, PCMK_VALUE_STOP, pcmk__str_casei)) {
202  crm_debug("On loss of ticket '%s': Stop %s (%s)",
203  new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id,
204  pcmk_role_text(new_rsc_ticket->role));
205  new_rsc_ticket->loss_policy = loss_ticket_stop;
206 
207  } else {
208  if (new_rsc_ticket->role == pcmk_role_promoted) {
209  crm_debug("On loss of ticket '%s': Default to demote %s (%s)",
210  new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id,
211  pcmk_role_text(new_rsc_ticket->role));
212  new_rsc_ticket->loss_policy = loss_ticket_demote;
213 
214  } else {
215  crm_debug("On loss of ticket '%s': Default to stop %s (%s)",
216  new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id,
217  pcmk_role_text(new_rsc_ticket->role));
218  new_rsc_ticket->loss_policy = loss_ticket_stop;
219  }
220  }
221 
222  pcmk__rsc_trace(rsc, "%s (%s) ==> %s",
223  rsc->id, pcmk_role_text(new_rsc_ticket->role), ticket->id);
224 
225  rsc->rsc_tickets = g_list_append(rsc->rsc_tickets, new_rsc_ticket);
226 
227  rsc->cluster->ticket_constraints = g_list_append(
228  rsc->cluster->ticket_constraints, new_rsc_ticket);
229 
230  if (!(new_rsc_ticket->ticket->granted) || new_rsc_ticket->ticket->standby) {
231  constraints_for_ticket(rsc, new_rsc_ticket);
232  }
233 }
234 
235 // \return Standard Pacemaker return code
236 static int
237 unpack_rsc_ticket_set(xmlNode *set, pcmk_ticket_t *ticket,
238  const char *loss_policy, pcmk_scheduler_t *scheduler)
239 {
240  const char *set_id = NULL;
241  const char *role = NULL;
242 
243  CRM_CHECK(set != NULL, return EINVAL);
244  CRM_CHECK(ticket != NULL, return EINVAL);
245 
246  set_id = pcmk__xe_id(set);
247  if (set_id == NULL) {
248  pcmk__config_err("Ignoring <" PCMK_XE_RESOURCE_SET "> without "
249  PCMK_XA_ID);
250  return pcmk_rc_unpack_error;
251  }
252 
253  role = crm_element_value(set, PCMK_XA_ROLE);
254 
255  for (xmlNode *xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF,
256  NULL, NULL);
257  xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
258 
259  pcmk_resource_t *resource = NULL;
260 
262  pcmk__xe_id(xml_rsc));
263  if (resource == NULL) {
264  pcmk__config_err("%s: No resource found for %s",
265  set_id, pcmk__xe_id(xml_rsc));
266  return pcmk_rc_unpack_error;
267  }
268  pcmk__rsc_trace(resource, "Resource '%s' depends on ticket '%s'",
269  resource->id, ticket->id);
270  rsc_ticket_new(set_id, resource, ticket, role, loss_policy);
271  }
272 
273  return pcmk_rc_ok;
274 }
275 
276 static void
277 unpack_simple_rsc_ticket(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
278 {
279  const char *id = NULL;
280  const char *ticket_str = crm_element_value(xml_obj, PCMK_XA_TICKET);
281  const char *loss_policy = crm_element_value(xml_obj, PCMK_XA_LOSS_POLICY);
282 
283  pcmk_ticket_t *ticket = NULL;
284 
285  const char *rsc_id = crm_element_value(xml_obj, PCMK_XA_RSC);
286  const char *state = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE);
287 
288  // @COMPAT: Deprecated since 2.1.5
289  const char *instance = crm_element_value(xml_obj, PCMK__XA_RSC_INSTANCE);
290 
291  pcmk_resource_t *rsc = NULL;
292 
293  if (instance != NULL) {
295  "Support for " PCMK__XA_RSC_INSTANCE " is deprecated "
296  "and will be removed in a future release");
297  }
298 
299  CRM_CHECK(xml_obj != NULL, return);
300 
301  id = pcmk__xe_id(xml_obj);
302  if (id == NULL) {
303  pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
304  xml_obj->name);
305  return;
306  }
307 
308  if (ticket_str == NULL) {
309  pcmk__config_err("Ignoring constraint '%s' without ticket specified",
310  id);
311  return;
312  } else {
313  ticket = g_hash_table_lookup(scheduler->tickets, ticket_str);
314  }
315 
316  if (ticket == NULL) {
317  pcmk__config_err("Ignoring constraint '%s' because ticket '%s' "
318  "does not exist", id, ticket_str);
319  return;
320  }
321 
322  if (rsc_id == NULL) {
323  pcmk__config_err("Ignoring constraint '%s' without resource", id);
324  return;
325  } else {
327  }
328 
329  if (rsc == NULL) {
330  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
331  "does not exist", id, rsc_id);
332  return;
333 
334  } else if ((instance != NULL) && !pcmk__is_clone(rsc)) {
335  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
336  "is not a clone but instance '%s' was requested",
337  id, rsc_id, instance);
338  return;
339  }
340 
341  if (instance != NULL) {
342  rsc = find_clone_instance(rsc, instance);
343  if (rsc == NULL) {
344  pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
345  "does not have an instance '%s'",
346  id, rsc_id, instance);
347  return;
348  }
349  }
350 
351  rsc_ticket_new(id, rsc, ticket, state, loss_policy);
352 }
353 
354 // \return Standard Pacemaker return code
355 static int
356 unpack_rsc_ticket_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
358 {
359  const char *id = NULL;
360  const char *rsc_id = NULL;
361  const char *state = NULL;
362 
363  pcmk_resource_t *rsc = NULL;
364  pcmk_tag_t *tag = NULL;
365 
366  xmlNode *rsc_set = NULL;
367 
368  *expanded_xml = NULL;
369 
370  CRM_CHECK(xml_obj != NULL, return EINVAL);
371 
372  id = pcmk__xe_id(xml_obj);
373  if (id == NULL) {
374  pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
375  xml_obj->name);
376  return pcmk_rc_unpack_error;
377  }
378 
379  // Check whether there are any resource sets with template or tag references
380  *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
381  if (*expanded_xml != NULL) {
382  crm_log_xml_trace(*expanded_xml, "Expanded rsc_ticket");
383  return pcmk_rc_ok;
384  }
385 
386  rsc_id = crm_element_value(xml_obj, PCMK_XA_RSC);
387  if (rsc_id == NULL) {
388  return pcmk_rc_ok;
389  }
390 
391  if (!pcmk__valid_resource_or_tag(scheduler, rsc_id, &rsc, &tag)) {
392  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
393  "valid resource or tag", id, rsc_id);
394  return pcmk_rc_unpack_error;
395 
396  } else if (rsc != NULL) {
397  // No template or tag is referenced
398  return pcmk_rc_ok;
399  }
400 
401  state = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE);
402 
403  *expanded_xml = pcmk__xml_copy(NULL, xml_obj);
404 
405  /* Convert any template or tag reference in "rsc" into ticket
406  * PCMK_XE_RESOURCE_SET
407  */
408  if (!pcmk__tag_to_set(*expanded_xml, &rsc_set, PCMK_XA_RSC, false,
409  scheduler)) {
410  free_xml(*expanded_xml);
411  *expanded_xml = NULL;
412  return pcmk_rc_unpack_error;
413  }
414 
415  if (rsc_set != NULL) {
416  if (state != NULL) {
417  /* Move PCMK_XA_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as a
418  * PCMK_XA_ROLE attribute
419  */
420  crm_xml_add(rsc_set, PCMK_XA_ROLE, state);
421  pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_RSC_ROLE);
422  }
423 
424  } else {
425  free_xml(*expanded_xml);
426  *expanded_xml = NULL;
427  }
428 
429  return pcmk_rc_ok;
430 }
431 
432 void
434 {
435  xmlNode *set = NULL;
436  bool any_sets = false;
437 
438  const char *id = NULL;
439  const char *ticket_str = NULL;
440 
441  pcmk_ticket_t *ticket = NULL;
442 
443  xmlNode *orig_xml = NULL;
444  xmlNode *expanded_xml = NULL;
445 
446  CRM_CHECK(xml_obj != NULL, return);
447 
448  id = pcmk__xe_id(xml_obj);
449  if (id == NULL) {
450  pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
451  xml_obj->name);
452  return;
453  }
454 
455  if (scheduler->tickets == NULL) {
457  }
458 
459  ticket_str = crm_element_value(xml_obj, PCMK_XA_TICKET);
460  if (ticket_str == NULL) {
461  pcmk__config_err("Ignoring constraint '%s' without ticket", id);
462  return;
463  } else {
464  ticket = g_hash_table_lookup(scheduler->tickets, ticket_str);
465  }
466 
467  if (ticket == NULL) {
468  ticket = ticket_new(ticket_str, scheduler);
469  if (ticket == NULL) {
470  return;
471  }
472  }
473 
474  if (unpack_rsc_ticket_tags(xml_obj, &expanded_xml,
475  scheduler) != pcmk_rc_ok) {
476  return;
477  }
478  if (expanded_xml != NULL) {
479  orig_xml = xml_obj;
480  xml_obj = expanded_xml;
481  }
482 
483  for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL);
484  set != NULL; set = pcmk__xe_next_same(set)) {
485 
486  const char *loss_policy = NULL;
487 
488  any_sets = true;
489  set = expand_idref(set, scheduler->input);
490  loss_policy = crm_element_value(xml_obj, PCMK_XA_LOSS_POLICY);
491 
492  if ((set == NULL) // Configuration error, message already logged
493  || (unpack_rsc_ticket_set(set, ticket, loss_policy,
494  scheduler) != pcmk_rc_ok)) {
495  if (expanded_xml != NULL) {
496  free_xml(expanded_xml);
497  }
498  return;
499  }
500  }
501 
502  if (expanded_xml) {
503  free_xml(expanded_xml);
504  xml_obj = orig_xml;
505  }
506 
507  if (!any_sets) {
508  unpack_simple_rsc_ticket(xml_obj, scheduler);
509  }
510 }
511 
521 void
523 {
524  for (GList *item = rsc->rsc_tickets; item != NULL; item = item->next) {
525  rsc_ticket_t *rsc_ticket = (rsc_ticket_t *) item->data;
526 
527  if ((rsc_ticket->role == pcmk_role_promoted)
528  && (!rsc_ticket->ticket->granted || rsc_ticket->ticket->standby)) {
530  "__stateful_without_ticket__", rsc->cluster);
531  }
532  }
533 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:245
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition: xml.c:883
A dumping ground.
pcmk_ticket_t * ticket_new(const char *ticket_id, pcmk_scheduler_t *scheduler)
Definition: utils.c:517
pcmk_scheduler_t * cluster
Definition: resources.h:408
const char * pcmk_role_text(enum rsc_role_e role)
Get readable description of a resource role.
Definition: roles.c:23
GList * rsc_tickets
Definition: resources.h:445
G_GNUC_INTERNAL bool pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr, bool convert_rsc, const pcmk_scheduler_t *scheduler)
enum rsc_role_e role
Definition: resources.h:464
#define pcmk__config_warn(fmt...)
GList * children
Definition: resources.h:471
#define pcmk__rsc_trace(rsc, fmt, args...)
#define PCMK_XE_RESOURCE_REF
Definition: xml_names.h:173
#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:301
Promoted.
Definition: roles.h:39
#define PCMK_XA_RSC
Definition: xml_names.h:383
void pe_fence_node(pcmk_scheduler_t *scheduler, pcmk_node_t *node, const char *reason, bool priority_delay)
Schedule a fence action for a node.
Definition: unpack.c:112
#define PCMK__XA_RSC_INSTANCE
GHashTable * tickets
Definition: scheduler.h:222
enum rsc_role_e pcmk_parse_role(const char *role)
Parse a resource role from a string role specification.
Definition: roles.c:59
GList * resources
Definition: scheduler.h:231
GList * ticket_constraints
Definition: scheduler.h:237
#define PCMK_VALUE_FENCE
Definition: options.h:153
#define crm_debug(fmt, args...)
Definition: logging.h:402
G_GNUC_INTERNAL pcmk_resource_t * pcmk__find_constraint_resource(GList *rsc_list, const char *id)
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:446
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:440
void resource_location(pcmk_resource_t *rsc, const pcmk_node_t *node, int score, const char *tag, pcmk_scheduler_t *scheduler)
Definition: utils.c:359
loss_ticket_policy
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:98
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:2152
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
Definition: xml.c:652
#define PCMK_XA_ID
Definition: xml_names.h:296
#define pcmk__set_rsc_flags(resource, flags_to_set)
G_GNUC_INTERNAL bool pcmk__valid_resource_or_tag(const pcmk_scheduler_t *scheduler, const char *id, pcmk_resource_t **rsc, pcmk_tag_t **tag)
void free_xml(xmlNode *child)
Definition: xml.c:867
xmlNode * input
Definition: scheduler.h:196
#define pcmk__warn_once(wo_flag, fmt...)
uint32_t id
Definition: cpg.c:48
char * id
Definition: tickets.h:31
void pcmk__require_promotion_tickets(pcmk_resource_t *rsc)
Cluster status and scheduling.
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:683
#define PCMK__ROLE_UNKNOWN
#define PCMK_ROLE_STARTED
Definition: roles.h:26
pcmk_scheduler_t * scheduler
#define PCMK_XA_TICKET
Definition: xml_names.h:421
#define PCMK_XE_RESOURCE_SET
Definition: xml_names.h:174
Definition: tags.h:29
#define PCMK_XA_RSC_ROLE
Definition: xml_names.h:385
GList * running_on
Definition: resources.h:456
#define PCMK_VALUE_STOP
Definition: options.h:209
void destroy_ticket(gpointer data)
Definition: utils.c:505
#define crm_log_xml_trace(xml, text)
Definition: logging.h:412
Resource role is unknown.
Definition: roles.h:35
#define PCMK_VALUE_FREEZE
Definition: options.h:155
unsigned long long flags
Definition: scheduler.h:211
pcmk_resource_t * find_clone_instance(const pcmk_resource_t *rsc, const char *sub_id)
Definition: clone.c:229
xmlNode * pcmk__xe_next_same(const xmlNode *node)
Definition: xml.c:2108
#define PCMK_XA_LOSS_POLICY
Definition: xml_names.h:315
G_GNUC_INTERNAL xmlNode * pcmk__expand_tags_in_sets(xmlNode *xml_obj, const pcmk_scheduler_t *scheduler)
#define PCMK_XA_ROLE
Definition: xml_names.h:382
#define pcmk__clear_rsc_flags(resource, flags_to_clear)
#define PCMK_VALUE_DEMOTE
Definition: options.h:145
void pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition: scores.h:24