pacemaker 2.1.7-2.1.7
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-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
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 pe_rsc_trace(rsc, "Skipping constraint: \"%s\" state filter",
54 role2text(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 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) {
91 resource_location(rsc, NULL, -INFINITY, "__loss_of_ticket__",
92 rsc->cluster);
93 break;
94
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
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
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
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 = 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
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 = ID(set);
247 if (set_id == NULL) {
248 pcmk__config_err("Ignoring <" XML_CONS_TAG_RSC_SET "> without "
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));
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
275static void
276unpack_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
357static int
358unpack_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);
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);
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;
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
430void
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
519void
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 PCMK_ACTION_DEMOTE
Definition actions.h:49
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:99
const char * role2text(enum rsc_role_e role)
Definition common.c:458
enum rsc_role_e text2role(const char *role)
Definition common.c:487
uint32_t id
Definition cpg.c:0
A dumping ground.
#define INFINITY
Definition crm.h:98
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:238
#define crm_debug(fmt, args...)
Definition logging.h:384
#define crm_log_xml_trace(xml, text)
Definition logging.h:393
#define pcmk__config_warn(fmt...)
#define pcmk__config_err(fmt...)
#define XML_COLOC_ATTR_SOURCE_ROLE
Definition msg_xml.h:363
#define XML_TAG_RESOURCE_REF
Definition msg_xml.h:234
#define ID(x)
Definition msg_xml.h:474
#define XML_CONS_TAG_RSC_SET
Definition msg_xml.h:357
#define XML_ATTR_ID
Definition msg_xml.h:156
#define XML_TICKET_ATTR_LOSS_POLICY
Definition msg_xml.h:391
#define XML_COLOC_ATTR_SOURCE_INSTANCE
Definition msg_xml.h:370
#define XML_TICKET_ATTR_TICKET
Definition msg_xml.h:390
#define XML_COLOC_ATTR_SOURCE
Definition msg_xml.h:362
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:447
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
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)
#define pe_warn_once(pe_wo_bit, fmt...)
Definition internal.h:142
void destroy_ticket(gpointer data)
Definition utils.c:498
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
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
pcmk_resource_t * find_clone_instance(const pcmk_resource_t *rsc, const char *sub_id)
Definition clone.c:227
#define pe__clear_resource_flags(resource, flags_to_clear)
Definition internal.h:70
#define pe_rsc_trace(rsc, fmt, args...)
Definition internal.h:37
#define pe__set_resource_flags(resource, flags_to_set)
Definition internal.h:64
pcmk_ticket_t * ticket_new(const char *ticket_id, pcmk_scheduler_t *scheduler)
Definition utils.c:510
@ pcmk_rsc_blocked
Whether resource is blocked from further action.
Definition resources.h:109
@ pcmk_rsc_managed
Whether resource is managed.
Definition resources.h:106
@ pcmk_rc_ok
Definition results.h:154
@ pcmk_rc_unpack_error
Definition results.h:118
@ pcmk_role_unknown
Resource role is unknown.
Definition roles.h:28
@ pcmk_role_promoted
Promoted.
Definition roles.h:32
#define PCMK__ROLE_STARTED
#define PCMK__ROLE_UNKNOWN
@ pcmk_sched_fencing_enabled
Whether fencing is enabled (via stonith-enabled property)
Definition scheduler.h:80
@ pcmk__wo_coloc_inst
Cluster status and scheduling.
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:608
@ pcmk__str_null_matches
@ pcmk__str_casei
Implementation of pcmk_node_t.
Definition nodes.h:130
Implementation of pcmk_resource_t.
Definition resources.h:399
GList * running_on
Nodes where resource may be active.
Definition resources.h:460
GList * children
Resource's child resources, if any.
Definition resources.h:475
pcmk_scheduler_t * cluster
Cluster that resource is part of.
Definition resources.h:412
char * id
Resource ID in configuration.
Definition resources.h:400
GList * rsc_tickets
Definition resources.h:448
enum rsc_role_e role
Resource's current role.
Definition resources.h:468
Configuration tag object.
Definition tags.h:26
Ticket constraint object.
Definition tickets.h:27
char * id
XML ID of ticket constraint or state.
Definition tickets.h:28
Implementation of pcmk_scheduler_t.
Definition scheduler.h:172
GList * ticket_constraints
Definition scheduler.h:202
xmlNode * input
CIB XML.
Definition scheduler.h:175
GList * resources
Resources in cluster.
Definition scheduler.h:196
unsigned long long flags
Group of enum pcmk_scheduler_flags.
Definition scheduler.h:183
GHashTable * tickets
Definition scheduler.h:190
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition xml.c:2555
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition xml.c:2484
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition xml.c:2510
void free_xml(xmlNode *child)
Definition xml.c:783
xmlNode * copy_xml(xmlNode *src_node)
Definition xml.c:789
void xml_remove_prop(xmlNode *obj, const char *name)
Definition xml.c:1696