pacemaker  2.1.7-0f7f88312f
Scalable High-Availability cluster resource manager
pcmk_sched_tickets.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2023 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  pe_rsc_trace(rsc, "Skipping constraint: \"%s\" state filter",
54  role2text(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  pe_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  pe_rsc_trace(rsc, "%s: Processing ticket dependency on %s (%s, %s)",
84  rsc->id, rsc_ticket->ticket->id, rsc_ticket->id,
85  role2text(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:
91  resource_location(rsc, NULL, -INFINITY, "__loss_of_ticket__",
92  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) {
98  resource_location(rsc, NULL, -INFINITY,
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 
108  resource_location(rsc, NULL, -INFINITY, "__loss_of_ticket__",
109  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)) {
132  resource_location(rsc, NULL, -INFINITY, "__no_ticket__",
133  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)) {
140  resource_location(rsc, NULL, -INFINITY, "__ticket_standby__",
141  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 = text2role(state);
172 
173  if (pcmk__str_eq(loss_policy, "fence", pcmk__str_casei)) {
175  new_rsc_ticket->loss_policy = loss_ticket_fence;
176  } else {
178  "' for ticket '%s' to 'stop' "
179  "because fencing is not configured", ticket->id);
180  loss_policy = "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  role2text(new_rsc_ticket->role));
188 
189  } else if (pcmk__str_eq(loss_policy, "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  role2text(new_rsc_ticket->role));
193  new_rsc_ticket->loss_policy = loss_ticket_freeze;
194 
195  } else if (pcmk__str_eq(loss_policy, PCMK_ACTION_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  role2text(new_rsc_ticket->role));
199  new_rsc_ticket->loss_policy = loss_ticket_demote;
200 
201  } else if (pcmk__str_eq(loss_policy, "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  role2text(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  role2text(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  role2text(new_rsc_ticket->role));
218  new_rsc_ticket->loss_policy = loss_ticket_stop;
219  }
220  }
221 
222  pe_rsc_trace(rsc, "%s (%s) ==> %s",
223  rsc->id, role2text(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 = ID(set);
247  if (set_id == NULL) {
248  pcmk__config_err("Ignoring <" XML_CONS_TAG_RSC_SET "> without "
249  XML_ATTR_ID);
250  return pcmk_rc_unpack_error;
251  }
252 
253  role = crm_element_value(set, "role");
254 
255  for (xmlNode *xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
256  xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
257 
258  pcmk_resource_t *resource = NULL;
259 
261  ID(xml_rsc));
262  if (resource == NULL) {
263  pcmk__config_err("%s: No resource found for %s",
264  set_id, ID(xml_rsc));
265  return pcmk_rc_unpack_error;
266  }
267  pe_rsc_trace(resource, "Resource '%s' depends on ticket '%s'",
268  resource->id, ticket->id);
269  rsc_ticket_new(set_id, resource, ticket, role, loss_policy);
270  }
271 
272  return pcmk_rc_ok;
273 }
274 
275 static void
276 unpack_simple_rsc_ticket(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
277 {
278  const char *id = NULL;
279  const char *ticket_str = crm_element_value(xml_obj, XML_TICKET_ATTR_TICKET);
280  const char *loss_policy = crm_element_value(xml_obj,
282 
283  pcmk_ticket_t *ticket = NULL;
284 
285  const char *rsc_id = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
286  const char *state = crm_element_value(xml_obj,
288 
289  // @COMPAT: Deprecated since 2.1.5
290  const char *instance = crm_element_value(xml_obj,
292 
293  pcmk_resource_t *rsc = NULL;
294 
295  if (instance != NULL) {
297  "Support for " XML_COLOC_ATTR_SOURCE_INSTANCE " is "
298  "deprecated and will be removed in a future release.");
299  }
300 
301  CRM_CHECK(xml_obj != NULL, return);
302 
303  id = ID(xml_obj);
304  if (id == NULL) {
305  pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
306  xml_obj->name);
307  return;
308  }
309 
310  if (ticket_str == NULL) {
311  pcmk__config_err("Ignoring constraint '%s' without ticket specified",
312  id);
313  return;
314  } else {
315  ticket = g_hash_table_lookup(scheduler->tickets, ticket_str);
316  }
317 
318  if (ticket == NULL) {
319  pcmk__config_err("Ignoring constraint '%s' because ticket '%s' "
320  "does not exist", id, ticket_str);
321  return;
322  }
323 
324  if (rsc_id == NULL) {
325  pcmk__config_err("Ignoring constraint '%s' without resource", id);
326  return;
327  } else {
329  }
330 
331  if (rsc == NULL) {
332  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
333  "does not exist", id, rsc_id);
334  return;
335 
336  } else if ((instance != NULL) && !pe_rsc_is_clone(rsc)) {
337  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
338  "is not a clone but instance '%s' was requested",
339  id, rsc_id, instance);
340  return;
341  }
342 
343  if (instance != NULL) {
344  rsc = find_clone_instance(rsc, instance);
345  if (rsc == NULL) {
346  pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
347  "does not have an instance '%s'",
348  "'%s'", id, rsc_id, instance);
349  return;
350  }
351  }
352 
353  rsc_ticket_new(id, rsc, ticket, state, loss_policy);
354 }
355 
356 // \return Standard Pacemaker return code
357 static int
358 unpack_rsc_ticket_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
360 {
361  const char *id = NULL;
362  const char *rsc_id = NULL;
363  const char *state = NULL;
364 
365  pcmk_resource_t *rsc = NULL;
366  pcmk_tag_t *tag = NULL;
367 
368  xmlNode *rsc_set = NULL;
369 
370  *expanded_xml = NULL;
371 
372  CRM_CHECK(xml_obj != NULL, return EINVAL);
373 
374  id = ID(xml_obj);
375  if (id == NULL) {
376  pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
377  xml_obj->name);
378  return pcmk_rc_unpack_error;
379  }
380 
381  // Check whether there are any resource sets with template or tag references
382  *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
383  if (*expanded_xml != NULL) {
384  crm_log_xml_trace(*expanded_xml, "Expanded rsc_ticket");
385  return pcmk_rc_ok;
386  }
387 
388  rsc_id = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
389  if (rsc_id == NULL) {
390  return pcmk_rc_ok;
391  }
392 
393  if (!pcmk__valid_resource_or_tag(scheduler, rsc_id, &rsc, &tag)) {
394  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
395  "valid resource or tag", id, rsc_id);
396  return pcmk_rc_unpack_error;
397 
398  } else if (rsc != NULL) {
399  // No template or tag is referenced
400  return pcmk_rc_ok;
401  }
402 
404 
405  *expanded_xml = copy_xml(xml_obj);
406 
407  // Convert any template or tag reference in "rsc" into ticket resource_set
408  if (!pcmk__tag_to_set(*expanded_xml, &rsc_set, XML_COLOC_ATTR_SOURCE,
409  false, 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 "rsc-role" into converted resource_set as a "role" attribute
418  crm_xml_add(rsc_set, "role", state);
420  }
421 
422  } else {
423  free_xml(*expanded_xml);
424  *expanded_xml = NULL;
425  }
426 
427  return pcmk_rc_ok;
428 }
429 
430 void
432 {
433  xmlNode *set = NULL;
434  bool any_sets = false;
435 
436  const char *id = NULL;
437  const char *ticket_str = NULL;
438 
439  pcmk_ticket_t *ticket = NULL;
440 
441  xmlNode *orig_xml = NULL;
442  xmlNode *expanded_xml = NULL;
443 
444  CRM_CHECK(xml_obj != NULL, return);
445 
446  id = ID(xml_obj);
447  if (id == NULL) {
448  pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
449  xml_obj->name);
450  return;
451  }
452 
453  if (scheduler->tickets == NULL) {
455  }
456 
457  ticket_str = crm_element_value(xml_obj, XML_TICKET_ATTR_TICKET);
458  if (ticket_str == NULL) {
459  pcmk__config_err("Ignoring constraint '%s' without ticket", id);
460  return;
461  } else {
462  ticket = g_hash_table_lookup(scheduler->tickets, ticket_str);
463  }
464 
465  if (ticket == NULL) {
466  ticket = ticket_new(ticket_str, scheduler);
467  if (ticket == NULL) {
468  return;
469  }
470  }
471 
472  if (unpack_rsc_ticket_tags(xml_obj, &expanded_xml,
473  scheduler) != pcmk_rc_ok) {
474  return;
475  }
476  if (expanded_xml != NULL) {
477  orig_xml = xml_obj;
478  xml_obj = expanded_xml;
479  }
480 
481  for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET); set != NULL;
482  set = crm_next_same_xml(set)) {
483 
484  const char *loss_policy = NULL;
485 
486  any_sets = true;
487  set = expand_idref(set, scheduler->input);
488  loss_policy = crm_element_value(xml_obj, XML_TICKET_ATTR_LOSS_POLICY);
489 
490  if ((set == NULL) // Configuration error, message already logged
491  || (unpack_rsc_ticket_set(set, ticket, loss_policy,
492  scheduler) != pcmk_rc_ok)) {
493  if (expanded_xml != NULL) {
494  free_xml(expanded_xml);
495  }
496  return;
497  }
498  }
499 
500  if (expanded_xml) {
501  free_xml(expanded_xml);
502  xml_obj = orig_xml;
503  }
504 
505  if (!any_sets) {
506  unpack_simple_rsc_ticket(xml_obj, scheduler);
507  }
508 }
509 
519 void
521 {
522  for (GList *item = rsc->rsc_tickets; item != NULL; item = item->next) {
523  rsc_ticket_t *rsc_ticket = (rsc_ticket_t *) item->data;
524 
525  if ((rsc_ticket->role == pcmk_role_promoted)
526  && (!rsc_ticket->ticket->granted || rsc_ticket->ticket->standby)) {
527  resource_location(rsc, NULL, -INFINITY,
528  "__stateful_without_ticket__", rsc->cluster);
529  }
530  }
531 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:238
A dumping ground.
pcmk_ticket_t * ticket_new(const char *ticket_id, pcmk_scheduler_t *scheduler)
Definition: utils.c:510
pcmk_scheduler_t * cluster
Cluster that resource is part of.
Definition: resources.h:412
GList * rsc_tickets
Definition: resources.h:448
#define INFINITY
Definition: crm.h:98
#define PCMK__ROLE_STARTED
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
Resource&#39;s current role.
Definition: resources.h:468
#define pcmk__config_warn(fmt...)
GList * children
Resource&#39;s child resources, if any.
Definition: resources.h:475
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2484
#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:302
Promoted.
Definition: roles.h:32
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:110
GHashTable * tickets
Definition: scheduler.h:190
Implementation of pcmk_scheduler_t.
Definition: scheduler.h:172
#define XML_CONS_TAG_RSC_SET
Definition: msg_xml.h:357
#define pe__set_resource_flags(resource, flags_to_set)
Definition: internal.h:64
GList * resources
Resources in cluster.
Definition: scheduler.h:196
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:789
const char * role2text(enum rsc_role_e role)
Definition: common.c:458
GList * ticket_constraints
Definition: scheduler.h:202
#define PCMK_ACTION_DEMOTE
Definition: actions.h:49
Implementation of pcmk_resource_t.
Definition: resources.h:399
#define crm_debug(fmt, args...)
Definition: logging.h:386
G_GNUC_INTERNAL pcmk_resource_t * pcmk__find_constraint_resource(GList *rsc_list, const char *id)
#define XML_ATTR_ID
Definition: msg_xml.h:156
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:447
void resource_location(pcmk_resource_t *rsc, const pcmk_node_t *node, int score, const char *tag, pcmk_scheduler_t *scheduler)
Definition: utils.c:360
loss_ticket_policy
#define pe_warn_once(pe_wo_bit, fmt...)
Definition: internal.h:142
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:99
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:2555
Ticket constraint object.
Definition: tickets.h:27
#define XML_COLOC_ATTR_SOURCE_INSTANCE
Definition: msg_xml.h:370
#define XML_TAG_RESOURCE_REF
Definition: msg_xml.h:234
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:783
Whether resource is blocked from further action.
Definition: resources.h:109
Implementation of pcmk_node_t.
Definition: nodes.h:130
enum rsc_role_e text2role(const char *role)
Definition: common.c:487
xmlNode * input
CIB XML.
Definition: scheduler.h:175
uint32_t id
Definition: cpg.c:45
#define XML_COLOC_ATTR_SOURCE_ROLE
Definition: msg_xml.h:363
char * id
XML ID of ticket constraint or state.
Definition: tickets.h:28
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:608
#define PCMK__ROLE_UNKNOWN
Whether fencing is enabled (via stonith-enabled property)
Definition: scheduler.h:80
pcmk_scheduler_t * scheduler
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:1696
Configuration tag object.
Definition: tags.h:26
#define pe__clear_resource_flags(resource, flags_to_clear)
Definition: internal.h:70
GList * running_on
Nodes where resource may be active.
Definition: resources.h:460
void destroy_ticket(gpointer data)
Definition: utils.c:498
#define crm_log_xml_trace(xml, text)
Definition: logging.h:395
#define XML_TICKET_ATTR_TICKET
Definition: msg_xml.h:390
Resource role is unknown.
Definition: roles.h:28
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:37
#define ID(x)
Definition: msg_xml.h:474
unsigned long long flags
Group of enum pcmk_scheduler_flags.
Definition: scheduler.h:183
pcmk_resource_t * find_clone_instance(const pcmk_resource_t *rsc, const char *sub_id)
Definition: clone.c:227
Whether resource is managed.
Definition: resources.h:106
G_GNUC_INTERNAL xmlNode * pcmk__expand_tags_in_sets(xmlNode *xml_obj, const pcmk_scheduler_t *scheduler)
#define XML_COLOC_ATTR_SOURCE
Definition: msg_xml.h:362
void pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
char * id
Resource ID in configuration.
Definition: resources.h:400
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2510
#define XML_TICKET_ATTR_LOSS_POLICY
Definition: msg_xml.h:391