pacemaker 3.0.1-16e74fc4da
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>
18#include <crm/pengine/status.h>
19#include <pacemaker-internal.h>
20
22
29
30typedef struct {
31 const char *id;
32 pcmk_resource_t *rsc;
33 pcmk__ticket_t *ticket;
34 enum loss_ticket_policy loss_policy;
35 int role;
36} rsc_ticket_t;
37
47static bool
48ticket_role_matches(const pcmk_resource_t *rsc, const rsc_ticket_t *rsc_ticket)
49{
50 if ((rsc_ticket->role == pcmk_role_unknown)
51 || (rsc_ticket->role == rsc->priv->orig_role)) {
52 return true;
53 }
54 pcmk__rsc_trace(rsc, "Skipping constraint: \"%s\" state filter",
55 pcmk_role_text(rsc_ticket->role));
56 return false;
57}
58
65static void
66constraints_for_ticket(pcmk_resource_t *rsc, const rsc_ticket_t *rsc_ticket)
67{
68 GList *iter = NULL;
69
70 CRM_CHECK((rsc != NULL) && (rsc_ticket != NULL), return);
71
72 if (pcmk_is_set(rsc_ticket->ticket->flags, pcmk__ticket_granted)
73 && !pcmk_is_set(rsc_ticket->ticket->flags, pcmk__ticket_standby)) {
74 return;
75 }
76
77 if (rsc->priv->children != NULL) {
78 pcmk__rsc_trace(rsc, "Processing ticket dependencies from %s", rsc->id);
79 for (iter = rsc->priv->children; iter != NULL; iter = iter->next) {
80 constraints_for_ticket((pcmk_resource_t *) iter->data, rsc_ticket);
81 }
82 return;
83 }
84
85 pcmk__rsc_trace(rsc, "%s: Processing ticket dependency on %s (%s, %s)",
86 rsc->id, rsc_ticket->ticket->id, rsc_ticket->id,
87 pcmk_role_text(rsc_ticket->role));
88
89 if (!pcmk_is_set(rsc_ticket->ticket->flags, pcmk__ticket_granted)
90 && (rsc->priv->active_nodes != NULL)) {
91
92 switch (rsc_ticket->loss_policy) {
95 "__loss_of_ticket__",
96 rsc->priv->scheduler);
97 break;
98
100 // Promotion score will be set to -INFINITY in promotion_order()
101 if (rsc_ticket->role != pcmk_role_promoted) {
103 "__loss_of_ticket__",
104 rsc->priv->scheduler);
105 }
106 break;
107
109 if (!ticket_role_matches(rsc, rsc_ticket)) {
110 return;
111 }
112
114 "__loss_of_ticket__",
115 rsc->priv->scheduler);
116
117 for (iter = rsc->priv->active_nodes;
118 iter != NULL; iter = iter->next) {
119
121 (pcmk_node_t *) iter->data,
122 "deadman ticket was lost", FALSE);
123 }
124 break;
125
127 if (!ticket_role_matches(rsc, rsc_ticket)) {
128 return;
129 }
130 if (rsc->priv->active_nodes != NULL) {
133 }
134 break;
135 }
136
137 } else if (!pcmk_is_set(rsc_ticket->ticket->flags, pcmk__ticket_granted)) {
138
139 if ((rsc_ticket->role != pcmk_role_promoted)
140 || (rsc_ticket->loss_policy == loss_ticket_stop)) {
142 "__no_ticket__", rsc->priv->scheduler);
143 }
144
145 } else if (pcmk_is_set(rsc_ticket->ticket->flags, pcmk__ticket_standby)) {
146
147 if ((rsc_ticket->role != pcmk_role_promoted)
148 || (rsc_ticket->loss_policy == loss_ticket_stop)) {
150 "__ticket_standby__", rsc->priv->scheduler);
151 }
152 }
153}
154
155static void
156rsc_ticket_new(const char *id, pcmk_resource_t *rsc, pcmk__ticket_t *ticket,
157 const char *role_spec, const char *loss_policy)
158{
159 rsc_ticket_t *new_rsc_ticket = NULL;
160 enum rsc_role_e role = pcmk_role_unknown;
161
162 if (rsc == NULL) {
163 pcmk__config_err("Ignoring ticket '%s' because resource "
164 "does not exist", id);
165 return;
166 }
167 if (pcmk__parse_constraint_role(id, role_spec, &role) != pcmk_rc_ok) {
168 // Not possible with schema validation enabled (error already logged)
169 return;
170 }
171
172 new_rsc_ticket = pcmk__assert_alloc(1, sizeof(rsc_ticket_t));
173 new_rsc_ticket->id = id;
174 new_rsc_ticket->ticket = ticket;
175 new_rsc_ticket->rsc = rsc;
176 new_rsc_ticket->role = role;
177
178 if (pcmk__str_eq(loss_policy, PCMK_VALUE_FENCE, pcmk__str_casei)) {
179 if (pcmk_is_set(rsc->priv->scheduler->flags,
181 new_rsc_ticket->loss_policy = loss_ticket_fence;
182 } else {
183 pcmk__config_err("Resetting '" PCMK_XA_LOSS_POLICY "' "
184 "for ticket '%s' to '" PCMK_VALUE_STOP "' "
185 "because fencing is not configured", ticket->id);
186 loss_policy = PCMK_VALUE_STOP;
187 }
188 }
189
190 if (new_rsc_ticket->loss_policy == loss_ticket_fence) {
191 crm_debug("On loss of ticket '%s': Fence the nodes running %s (%s)",
192 new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id,
193 pcmk_role_text(new_rsc_ticket->role));
194
195 } else if (pcmk__str_eq(loss_policy, PCMK_VALUE_FREEZE, pcmk__str_casei)) {
196 crm_debug("On loss of ticket '%s': Freeze %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_freeze;
200
201 } else if (pcmk__str_eq(loss_policy, PCMK_VALUE_DEMOTE, pcmk__str_casei)) {
202 crm_debug("On loss of ticket '%s': Demote %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_demote;
206
207 } else if (pcmk__str_eq(loss_policy, PCMK_VALUE_STOP, pcmk__str_casei)) {
208 crm_debug("On loss of ticket '%s': Stop %s (%s)",
209 new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id,
210 pcmk_role_text(new_rsc_ticket->role));
211 new_rsc_ticket->loss_policy = loss_ticket_stop;
212
213 } else {
214 if (new_rsc_ticket->role == pcmk_role_promoted) {
215 crm_debug("On loss of ticket '%s': Default to demote %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_demote;
219
220 } else {
221 crm_debug("On loss of ticket '%s': Default to stop %s (%s)",
222 new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id,
223 pcmk_role_text(new_rsc_ticket->role));
224 new_rsc_ticket->loss_policy = loss_ticket_stop;
225 }
226 }
227
228 pcmk__rsc_trace(rsc, "%s (%s) ==> %s",
229 rsc->id, pcmk_role_text(new_rsc_ticket->role), ticket->id);
230
232 g_list_append(rsc->priv->ticket_constraints, new_rsc_ticket);
233
234 if (!pcmk_is_set(new_rsc_ticket->ticket->flags, pcmk__ticket_granted)
235 || pcmk_is_set(new_rsc_ticket->ticket->flags, pcmk__ticket_standby)) {
236 constraints_for_ticket(rsc, new_rsc_ticket);
237 }
238}
239
240// \return Standard Pacemaker return code
241static int
242unpack_rsc_ticket_set(xmlNode *set, pcmk__ticket_t *ticket,
243 const char *loss_policy, pcmk_scheduler_t *scheduler)
244{
245 const char *set_id = NULL;
246 const char *role = NULL;
247
248 CRM_CHECK(set != NULL, return EINVAL);
249 CRM_CHECK(ticket != NULL, return EINVAL);
250
251 set_id = pcmk__xe_id(set);
252 if (set_id == NULL) {
253 pcmk__config_err("Ignoring <" PCMK_XE_RESOURCE_SET "> without "
254 PCMK_XA_ID);
256 }
257
258 role = crm_element_value(set, PCMK_XA_ROLE);
259
260 for (xmlNode *xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF,
261 NULL, NULL);
262 xml_rsc != NULL;
263 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
264
265 pcmk_resource_t *resource = NULL;
266
268 pcmk__xe_id(xml_rsc));
269 if (resource == NULL) {
270 pcmk__config_err("%s: No resource found for %s",
271 set_id, pcmk__xe_id(xml_rsc));
273 }
274 pcmk__rsc_trace(resource, "Resource '%s' depends on ticket '%s'",
275 resource->id, ticket->id);
276 rsc_ticket_new(set_id, resource, ticket, role, loss_policy);
277 }
278
279 return pcmk_rc_ok;
280}
281
282static void
283unpack_simple_rsc_ticket(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
284{
285 const char *id = NULL;
286 const char *ticket_str = crm_element_value(xml_obj, PCMK_XA_TICKET);
287 const char *loss_policy = crm_element_value(xml_obj, PCMK_XA_LOSS_POLICY);
288
289 pcmk__ticket_t *ticket = NULL;
290
291 const char *rsc_id = crm_element_value(xml_obj, PCMK_XA_RSC);
292 const char *state = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE);
293
294 pcmk_resource_t *rsc = NULL;
295
296 CRM_CHECK(xml_obj != NULL, return);
297
298 id = pcmk__xe_id(xml_obj);
299 if (id == NULL) {
300 pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
301 xml_obj->name);
302 return;
303 }
304
305 if (ticket_str == NULL) {
306 pcmk__config_err("Ignoring constraint '%s' without ticket specified",
307 id);
308 return;
309 } else {
310 ticket = g_hash_table_lookup(scheduler->priv->ticket_constraints,
311 ticket_str);
312 }
313
314 if (ticket == NULL) {
315 pcmk__config_err("Ignoring constraint '%s' because ticket '%s' "
316 "does not exist", id, ticket_str);
317 return;
318 }
319
320 if (rsc_id == NULL) {
321 pcmk__config_err("Ignoring constraint '%s' without resource", id);
322 return;
323 } else {
325 rsc_id);
326 }
327
328 if (rsc == NULL) {
329 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
330 "does not exist", id, rsc_id);
331 return;
332 }
333
334 rsc_ticket_new(id, rsc, ticket, state, loss_policy);
335}
336
337// \return Standard Pacemaker return code
338static int
339unpack_rsc_ticket_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
341{
342 const char *id = NULL;
343 const char *rsc_id = NULL;
344 const char *state = NULL;
345
346 pcmk_resource_t *rsc = NULL;
347 pcmk__idref_t *tag = NULL;
348
349 xmlNode *rsc_set = NULL;
350
351 *expanded_xml = NULL;
352
353 CRM_CHECK(xml_obj != NULL, return EINVAL);
354
355 id = pcmk__xe_id(xml_obj);
356 if (id == NULL) {
357 pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
358 xml_obj->name);
360 }
361
362 // Check whether there are any resource sets with template or tag references
363 *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
364 if (*expanded_xml != NULL) {
365 crm_log_xml_trace(*expanded_xml, "Expanded rsc_ticket");
366 return pcmk_rc_ok;
367 }
368
369 rsc_id = crm_element_value(xml_obj, PCMK_XA_RSC);
370 if (rsc_id == NULL) {
371 return pcmk_rc_ok;
372 }
373
374 if (!pcmk__valid_resource_or_tag(scheduler, rsc_id, &rsc, &tag)) {
375 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
376 "valid resource or tag", id, rsc_id);
378
379 } else if (rsc != NULL) {
380 // No template or tag is referenced
381 return pcmk_rc_ok;
382 }
383
384 state = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE);
385
386 *expanded_xml = pcmk__xml_copy(NULL, xml_obj);
387
388 /* Convert any template or tag reference in "rsc" into ticket
389 * PCMK_XE_RESOURCE_SET
390 */
391 if (!pcmk__tag_to_set(*expanded_xml, &rsc_set, PCMK_XA_RSC, false,
392 scheduler)) {
393 pcmk__xml_free(*expanded_xml);
394 *expanded_xml = NULL;
396 }
397
398 if (rsc_set != NULL) {
399 if (state != NULL) {
400 /* Move PCMK_XA_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as a
401 * PCMK_XA_ROLE attribute
402 */
403 crm_xml_add(rsc_set, PCMK_XA_ROLE, state);
405 }
406
407 } else {
408 pcmk__xml_free(*expanded_xml);
409 *expanded_xml = NULL;
410 }
411
412 return pcmk_rc_ok;
413}
414
415void
417{
418 xmlNode *set = NULL;
419 bool any_sets = false;
420
421 const char *id = NULL;
422 const char *ticket_str = NULL;
423
424 pcmk__ticket_t *ticket = NULL;
425
426 xmlNode *orig_xml = NULL;
427 xmlNode *expanded_xml = NULL;
428
429 CRM_CHECK(xml_obj != NULL, return);
430
431 id = pcmk__xe_id(xml_obj);
432 if (id == NULL) {
433 pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
434 xml_obj->name);
435 return;
436 }
437
438 if (scheduler->priv->ticket_constraints == NULL) {
441 }
442
443 ticket_str = crm_element_value(xml_obj, PCMK_XA_TICKET);
444 if (ticket_str == NULL) {
445 pcmk__config_err("Ignoring constraint '%s' without ticket", id);
446 return;
447 } else {
448 ticket = g_hash_table_lookup(scheduler->priv->ticket_constraints,
449 ticket_str);
450 }
451
452 if (ticket == NULL) {
453 ticket = ticket_new(ticket_str, scheduler);
454 if (ticket == NULL) {
455 return;
456 }
457 }
458
459 if (unpack_rsc_ticket_tags(xml_obj, &expanded_xml,
460 scheduler) != pcmk_rc_ok) {
461 return;
462 }
463 if (expanded_xml != NULL) {
464 orig_xml = xml_obj;
465 xml_obj = expanded_xml;
466 }
467
468 for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL);
469 set != NULL; set = pcmk__xe_next(set, PCMK_XE_RESOURCE_SET)) {
470
471 const char *loss_policy = NULL;
472
473 any_sets = true;
475 loss_policy = crm_element_value(xml_obj, PCMK_XA_LOSS_POLICY);
476
477 if ((set == NULL) // Configuration error, message already logged
478 || (unpack_rsc_ticket_set(set, ticket, loss_policy,
479 scheduler) != pcmk_rc_ok)) {
480 if (expanded_xml != NULL) {
481 pcmk__xml_free(expanded_xml);
482 }
483 return;
484 }
485 }
486
487 if (expanded_xml) {
488 pcmk__xml_free(expanded_xml);
489 xml_obj = orig_xml;
490 }
491
492 if (!any_sets) {
493 unpack_simple_rsc_ticket(xml_obj, scheduler);
494 }
495}
496
506void
508{
509 for (GList *item = rsc->priv->ticket_constraints;
510 item != NULL; item = item->next) {
511
512 rsc_ticket_t *rsc_ticket = (rsc_ticket_t *) item->data;
513
514 if ((rsc_ticket->role == pcmk_role_promoted)
515 && (!pcmk_is_set(rsc_ticket->ticket->flags, pcmk__ticket_granted)
516 || pcmk_is_set(rsc_ticket->ticket->flags,
519 "__stateful_without_ticket__",
520 rsc->priv->scheduler);
521 }
522 }
523}
#define pcmk__assert_alloc(nmemb, size)
Definition internal.h:246
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:80
uint32_t id
Definition cpg.c:0
A dumping ground.
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 int pcmk__parse_constraint_role(const char *id, const char *role_spec, enum rsc_role_e *role)
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)
G_GNUC_INTERNAL bool pcmk__valid_resource_or_tag(const pcmk_scheduler_t *scheduler, const char *id, pcmk_resource_t **rsc, pcmk__idref_t **tag)
#define CRM_CHECK(expr, failure_action)
Definition logging.h:213
#define crm_debug(fmt, args...)
Definition logging.h:368
#define crm_log_xml_trace(xml, text)
Definition logging.h:378
#define pcmk__config_err(fmt...)
pcmk_scheduler_t * scheduler
#define PCMK_VALUE_DEMOTE
Definition options.h:147
#define PCMK_VALUE_FREEZE
Definition options.h:157
#define PCMK_VALUE_FENCE
Definition options.h:155
#define PCMK_VALUE_STOP
Definition options.h:213
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:531
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:116
void resource_location(pcmk_resource_t *rsc, const pcmk_node_t *node, int score, const char *tag, pcmk_scheduler_t *scheduler)
Definition utils.c:398
pcmk__ticket_t * ticket_new(const char *ticket_id, pcmk_scheduler_t *scheduler)
Definition utils.c:543
@ pcmk__rsc_managed
@ pcmk__rsc_blocked
#define pcmk__set_rsc_flags(resource, flags_to_set)
#define pcmk__clear_rsc_flags(resource, flags_to_clear)
@ pcmk_rc_ok
Definition results.h:159
@ pcmk_rc_unpack_error
Definition results.h:122
const char * pcmk_role_text(enum rsc_role_e role)
Get readable description of a resource role.
Definition roles.c:23
rsc_role_e
Definition roles.h:34
@ pcmk_role_unknown
Resource role is unknown.
Definition roles.h:35
@ pcmk_role_promoted
Promoted.
Definition roles.h:39
Scheduler API.
#define pcmk__rsc_trace(rsc, fmt, args...)
@ pcmk__sched_fencing_enabled
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition scores.h:26
Cluster status and scheduling.
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:685
@ pcmk__str_casei
pcmk_scheduler_t * scheduler
pcmk__resource_private_t * priv
Definition resources.h:61
pcmk__scheduler_private_t * priv
Definition scheduler.h:99
xmlNode * input
Definition scheduler.h:81
uint64_t flags
Definition scheduler.h:89
@ pcmk__ticket_granted
@ pcmk__ticket_standby
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition xml_element.c:43
xmlNode * pcmk__xe_next(const xmlNode *node, const char *element_name)
xmlNode * pcmk__xe_resolve_idref(xmlNode *xml, xmlNode *search)
Definition xml_idref.c:85
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition xml.c:832
void pcmk__xml_free(xmlNode *xml)
Definition xml.c:816
#define PCMK_XA_RSC_ROLE
Definition xml_names.h:390
#define PCMK_XE_RESOURCE_REF
Definition xml_names.h:177
#define PCMK_XA_TICKET
Definition xml_names.h:426
#define PCMK_XA_ID
Definition xml_names.h:301
#define PCMK_XA_ROLE
Definition xml_names.h:387
#define PCMK_XE_RESOURCE_SET
Definition xml_names.h:178
#define PCMK_XA_LOSS_POLICY
Definition xml_names.h:320
#define PCMK_XA_RSC
Definition xml_names.h:388