pacemaker 2.1.8-2.1.8
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
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
21
28
29typedef 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
46static bool
47ticket_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
64static void
65constraints_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) {
92 "__loss_of_ticket__", rsc->cluster);
93 break;
94
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
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
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
146static void
147rsc_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
236static int
237unpack_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);
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));
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
276static void
277unpack_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
355static int
356unpack_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);
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);
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;
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);
422 }
423
424 } else {
425 free_xml(*expanded_xml);
426 *expanded_xml = NULL;
427 }
428
429 return pcmk_rc_ok;
430}
431
432void
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
521void
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 pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:98
uint32_t id
Definition cpg.c:0
A dumping ground.
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)
G_GNUC_INTERNAL pcmk_resource_t * pcmk__find_constraint_resource(GList *rsc_list, const char *id)
G_GNUC_INTERNAL xmlNode * pcmk__expand_tags_in_sets(xmlNode *xml_obj, const pcmk_scheduler_t *scheduler)
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)
#define CRM_CHECK(expr, failure_action)
Definition logging.h:245
#define crm_debug(fmt, args...)
Definition logging.h:400
#define crm_log_xml_trace(xml, text)
Definition logging.h:410
#define pcmk__config_warn(fmt...)
#define pcmk__config_err(fmt...)
@ pcmk__wo_coloc_inst
#define pcmk__warn_once(wo_flag, fmt...)
pcmk_scheduler_t * scheduler
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:446
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
#define PCMK_VALUE_DEMOTE
Definition options.h:145
#define PCMK_VALUE_FREEZE
Definition options.h:155
#define PCMK_VALUE_FENCE
Definition options.h:153
#define PCMK_VALUE_STOP
Definition options.h:209
void pcmk__require_promotion_tickets(pcmk_resource_t *rsc)
loss_ticket_policy
@ loss_ticket_fence
@ loss_ticket_demote
@ loss_ticket_freeze
@ loss_ticket_stop
void pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
void destroy_ticket(gpointer data)
Definition utils.c:505
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
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
pcmk_resource_t * find_clone_instance(const pcmk_resource_t *rsc, const char *sub_id)
Definition clone.c:229
pcmk_ticket_t * ticket_new(const char *ticket_id, pcmk_scheduler_t *scheduler)
Definition utils.c:517
@ pcmk_rsc_blocked
Definition resources.h:91
@ pcmk_rsc_managed
Definition resources.h:88
@ pcmk_rc_ok
Definition results.h:162
@ pcmk_rc_unpack_error
Definition results.h:125
#define PCMK_ROLE_STARTED
Definition roles.h:26
const char * pcmk_role_text(enum rsc_role_e role)
Get readable description of a resource role.
Definition roles.c:23
enum rsc_role_e pcmk_parse_role(const char *role)
Parse a resource role from a string role specification.
Definition roles.c:59
@ pcmk_role_unknown
Resource role is unknown.
Definition roles.h:35
@ pcmk_role_promoted
Promoted.
Definition roles.h:39
#define pcmk__set_rsc_flags(resource, flags_to_set)
#define pcmk__clear_rsc_flags(resource, flags_to_clear)
#define PCMK__ROLE_UNKNOWN
@ pcmk_sched_fencing_enabled
Definition scheduler.h:89
#define pcmk__rsc_trace(rsc, fmt, args...)
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition scores.h:24
Cluster status and scheduling.
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:683
@ pcmk__str_null_matches
@ pcmk__str_casei
GList * running_on
Definition resources.h:456
GList * children
Definition resources.h:471
pcmk_scheduler_t * cluster
Definition resources.h:408
GList * rsc_tickets
Definition resources.h:445
enum rsc_role_e role
Definition resources.h:464
char * id
Definition tickets.h:31
GList * ticket_constraints
Definition scheduler.h:237
xmlNode * input
Definition scheduler.h:196
GList * resources
Definition scheduler.h:231
unsigned long long flags
Definition scheduler.h:211
GHashTable * tickets
Definition scheduler.h:222
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition xml.c:2152
void free_xml(xmlNode *child)
Definition xml.c:867
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
Definition xml.c:652
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition xml.c:883
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition xml.c:440
xmlNode * pcmk__xe_next_same(const xmlNode *node)
Definition xml.c:2108
#define PCMK_XA_RSC_ROLE
Definition xml_names.h:385
#define PCMK_XE_RESOURCE_REF
Definition xml_names.h:173
#define PCMK_XA_TICKET
Definition xml_names.h:421
#define PCMK_XA_ID
Definition xml_names.h:296
#define PCMK_XA_ROLE
Definition xml_names.h:382
#define PCMK_XE_RESOURCE_SET
Definition xml_names.h:174
#define PCMK_XA_LOSS_POLICY
Definition xml_names.h:315
#define PCMK_XA_RSC
Definition xml_names.h:383
#define PCMK__XA_RSC_INSTANCE