pacemaker 2.1.4-dc6eb4362e
Scalable High-Availability cluster resource manager
pcmk_sched_tickets.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2021 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>
16#include <crm/pengine/status.h>
17#include <pacemaker-internal.h>
18
20
30static bool
31ticket_role_matches(pe_resource_t *rsc_lh, rsc_ticket_t *rsc_ticket)
32{
33 if ((rsc_ticket->role_lh == RSC_ROLE_UNKNOWN)
34 || (rsc_ticket->role_lh == rsc_lh->role)) {
35 return true;
36 }
37 pe_rsc_trace(rsc_lh, "LH: Skipping constraint: \"%s\" state filter",
38 role2text(rsc_ticket->role_lh));
39 return false;
40}
41
49static void
50constraints_for_ticket(pe_resource_t *rsc_lh, rsc_ticket_t *rsc_ticket,
52{
53 GList *gIter = NULL;
54
55 CRM_CHECK((rsc_lh != NULL) && (rsc_ticket != NULL), return);
56
57 if (rsc_ticket->ticket->granted && !rsc_ticket->ticket->standby) {
58 return;
59 }
60
61 if (rsc_lh->children) {
62 pe_rsc_trace(rsc_lh, "Processing ticket dependencies from %s", rsc_lh->id);
63 for (gIter = rsc_lh->children; gIter != NULL; gIter = gIter->next) {
64 constraints_for_ticket((pe_resource_t *) gIter->data, rsc_ticket,
65 data_set);
66 }
67 return;
68 }
69
70 pe_rsc_trace(rsc_lh, "%s: Processing ticket dependency on %s (%s, %s)",
71 rsc_lh->id, rsc_ticket->ticket->id, rsc_ticket->id,
72 role2text(rsc_ticket->role_lh));
73
74 if (!rsc_ticket->ticket->granted && (rsc_lh->running_on != NULL)) {
75
76 switch (rsc_ticket->loss_policy) {
78 resource_location(rsc_lh, NULL, -INFINITY, "__loss_of_ticket__",
79 data_set);
80 break;
81
83 // Promotion score will be set to -INFINITY in promotion_order()
84 if (rsc_ticket->role_lh != RSC_ROLE_PROMOTED) {
85 resource_location(rsc_lh, NULL, -INFINITY,
86 "__loss_of_ticket__", data_set);
87 }
88 break;
89
91 if (!ticket_role_matches(rsc_lh, rsc_ticket)) {
92 return;
93 }
94
95 resource_location(rsc_lh, NULL, -INFINITY, "__loss_of_ticket__",
96 data_set);
97
98 for (gIter = rsc_lh->running_on; gIter != NULL;
99 gIter = gIter->next) {
100 pe_fence_node(data_set, (pe_node_t *) gIter->data,
101 "deadman ticket was lost", FALSE);
102 }
103 break;
104
106 if (!ticket_role_matches(rsc_lh, rsc_ticket)) {
107 return;
108 }
109 if (rsc_lh->running_on != NULL) {
112 }
113 break;
114 }
115
116 } else if (!rsc_ticket->ticket->granted) {
117
118 if ((rsc_ticket->role_lh != RSC_ROLE_PROMOTED)
119 || (rsc_ticket->loss_policy == loss_ticket_stop)) {
120 resource_location(rsc_lh, NULL, -INFINITY, "__no_ticket__",
121 data_set);
122 }
123
124 } else if (rsc_ticket->ticket->standby) {
125
126 if ((rsc_ticket->role_lh != RSC_ROLE_PROMOTED)
127 || (rsc_ticket->loss_policy == loss_ticket_stop)) {
128 resource_location(rsc_lh, NULL, -INFINITY, "__ticket_standby__",
129 data_set);
130 }
131 }
132}
133
134static void
135rsc_ticket_new(const char *id, pe_resource_t *rsc_lh, pe_ticket_t *ticket,
136 const char *state_lh, const char *loss_policy,
138{
139 rsc_ticket_t *new_rsc_ticket = NULL;
140
141 if (rsc_lh == NULL) {
142 pcmk__config_err("Ignoring ticket '%s' because resource "
143 "does not exist", id);
144 return;
145 }
146
147 new_rsc_ticket = calloc(1, sizeof(rsc_ticket_t));
148 if (new_rsc_ticket == NULL) {
149 return;
150 }
151
152 if (pcmk__str_eq(state_lh, RSC_ROLE_STARTED_S,
154 state_lh = RSC_ROLE_UNKNOWN_S;
155 }
156
157 new_rsc_ticket->id = id;
158 new_rsc_ticket->ticket = ticket;
159 new_rsc_ticket->rsc_lh = rsc_lh;
160 new_rsc_ticket->role_lh = text2role(state_lh);
161
162 if (pcmk__str_eq(loss_policy, "fence", pcmk__str_casei)) {
164 new_rsc_ticket->loss_policy = loss_ticket_fence;
165 } else {
167 "' for ticket '%s' to 'stop' "
168 "because fencing is not configured", ticket->id);
169 loss_policy = "stop";
170 }
171 }
172
173 if (new_rsc_ticket->loss_policy == loss_ticket_fence) {
174 crm_debug("On loss of ticket '%s': Fence the nodes running %s (%s)",
175 new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
176 role2text(new_rsc_ticket->role_lh));
177
178 } else if (pcmk__str_eq(loss_policy, "freeze", pcmk__str_casei)) {
179 crm_debug("On loss of ticket '%s': Freeze %s (%s)",
180 new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
181 role2text(new_rsc_ticket->role_lh));
182 new_rsc_ticket->loss_policy = loss_ticket_freeze;
183
184 } else if (pcmk__str_eq(loss_policy, "demote", pcmk__str_casei)) {
185 crm_debug("On loss of ticket '%s': Demote %s (%s)",
186 new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
187 role2text(new_rsc_ticket->role_lh));
188 new_rsc_ticket->loss_policy = loss_ticket_demote;
189
190 } else if (pcmk__str_eq(loss_policy, "stop", pcmk__str_casei)) {
191 crm_debug("On loss of ticket '%s': Stop %s (%s)",
192 new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
193 role2text(new_rsc_ticket->role_lh));
194 new_rsc_ticket->loss_policy = loss_ticket_stop;
195
196 } else {
197 if (new_rsc_ticket->role_lh == RSC_ROLE_PROMOTED) {
198 crm_debug("On loss of ticket '%s': Default to demote %s (%s)",
199 new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
200 role2text(new_rsc_ticket->role_lh));
201 new_rsc_ticket->loss_policy = loss_ticket_demote;
202
203 } else {
204 crm_debug("On loss of ticket '%s': Default to stop %s (%s)",
205 new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
206 role2text(new_rsc_ticket->role_lh));
207 new_rsc_ticket->loss_policy = loss_ticket_stop;
208 }
209 }
210
211 pe_rsc_trace(rsc_lh, "%s (%s) ==> %s",
212 rsc_lh->id, role2text(new_rsc_ticket->role_lh), ticket->id);
213
214 rsc_lh->rsc_tickets = g_list_append(rsc_lh->rsc_tickets, new_rsc_ticket);
215
217 new_rsc_ticket);
218
219 if (!(new_rsc_ticket->ticket->granted) || new_rsc_ticket->ticket->standby) {
220 constraints_for_ticket(rsc_lh, new_rsc_ticket, data_set);
221 }
222}
223
224// \return Standard Pacemaker return code
225static int
226unpack_rsc_ticket_set(xmlNode *set, pe_ticket_t *ticket,
227 const char *loss_policy, pe_working_set_t *data_set)
228{
229 const char *set_id = NULL;
230 const char *role = NULL;
231
232 CRM_CHECK(set != NULL, return EINVAL);
233 CRM_CHECK(ticket != NULL, return EINVAL);
234
235 set_id = ID(set);
236 if (set_id == NULL) {
237 pcmk__config_err("Ignoring <" XML_CONS_TAG_RSC_SET "> without "
240 }
241
242 role = crm_element_value(set, "role");
243
244 for (xmlNode *xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
245 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
246
247 pe_resource_t *resource = NULL;
248
250 ID(xml_rsc));
251 if (resource == NULL) {
252 pcmk__config_err("%s: No resource found for %s",
253 set_id, ID(xml_rsc));
255 }
256 pe_rsc_trace(resource, "Resource '%s' depends on ticket '%s'",
257 resource->id, ticket->id);
258 rsc_ticket_new(set_id, resource, ticket, role, loss_policy, data_set);
259 }
260
261 return pcmk_rc_ok;
262}
263
264static void
265unpack_simple_rsc_ticket(xmlNode *xml_obj, pe_working_set_t *data_set)
266{
267 const char *id = NULL;
268 const char *ticket_str = crm_element_value(xml_obj, XML_TICKET_ATTR_TICKET);
269 const char *loss_policy = crm_element_value(xml_obj,
271
272 pe_ticket_t *ticket = NULL;
273
274 const char *id_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
275 const char *state_lh = crm_element_value(xml_obj,
277
278 // experimental syntax from pacemaker-next (unlikely to be adopted as-is)
279 const char *instance_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_INSTANCE);
280
281 pe_resource_t *rsc_lh = NULL;
282
283 CRM_CHECK(xml_obj != NULL, return);
284
285 id = ID(xml_obj);
286 if (id == NULL) {
287 pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
288 crm_element_name(xml_obj));
289 return;
290 }
291
292 if (ticket_str == NULL) {
293 pcmk__config_err("Ignoring constraint '%s' without ticket specified",
294 id);
295 return;
296 } else {
297 ticket = g_hash_table_lookup(data_set->tickets, ticket_str);
298 }
299
300 if (ticket == NULL) {
301 pcmk__config_err("Ignoring constraint '%s' because ticket '%s' "
302 "does not exist", id, ticket_str);
303 return;
304 }
305
306 if (id_lh == NULL) {
307 pcmk__config_err("Ignoring constraint '%s' without resource", id);
308 return;
309 } else {
311 }
312
313 if (rsc_lh == NULL) {
314 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
315 "does not exist", id, id_lh);
316 return;
317
318 } else if ((instance_lh != NULL) && !pe_rsc_is_clone(rsc_lh)) {
319 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
320 "is not a clone but instance '%s' was requested",
321 id, id_lh, instance_lh);
322 return;
323 }
324
325 if (instance_lh != NULL) {
326 rsc_lh = find_clone_instance(rsc_lh, instance_lh, data_set);
327 if (rsc_lh == NULL) {
328 pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
329 "does not have an instance '%s'",
330 "'%s'", id, id_lh, instance_lh);
331 return;
332 }
333 }
334
335 rsc_ticket_new(id, rsc_lh, ticket, state_lh, loss_policy, data_set);
336}
337
338// \return Standard Pacemaker return code
339static int
340unpack_rsc_ticket_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
342{
343 const char *id = NULL;
344 const char *id_lh = NULL;
345 const char *state_lh = NULL;
346
347 pe_resource_t *rsc_lh = NULL;
348 pe_tag_t *tag_lh = NULL;
349
350 xmlNode *rsc_set_lh = NULL;
351
352 *expanded_xml = NULL;
353
354 CRM_CHECK(xml_obj != NULL, return EINVAL);
355
356 id = ID(xml_obj);
357 if (id == NULL) {
358 pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
359 crm_element_name(xml_obj));
361 }
362
363 // Check whether there are any resource sets with template or tag references
364 *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, data_set);
365 if (*expanded_xml != NULL) {
366 crm_log_xml_trace(*expanded_xml, "Expanded rsc_ticket");
367 return pcmk_rc_ok;
368 }
369
371 if (id_lh == NULL) {
372 return pcmk_rc_ok;
373 }
374
375 if (!pcmk__valid_resource_or_tag(data_set, id_lh, &rsc_lh, &tag_lh)) {
376 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
377 "valid resource or tag", id, id_lh);
379
380 } else if (rsc_lh) {
381 // No template or tag is referenced
382 return pcmk_rc_ok;
383 }
384
386
387 *expanded_xml = copy_xml(xml_obj);
388
389 // Convert template/tag reference in "rsc" into resource_set under rsc_ticket
390 if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_lh, XML_COLOC_ATTR_SOURCE,
391 false, data_set)) {
392 free_xml(*expanded_xml);
393 *expanded_xml = NULL;
395 }
396
397 if (rsc_set_lh != NULL) {
398 if (state_lh != NULL) {
399 // Move "rsc-role" into converted resource_set as a "role" attribute
400 crm_xml_add(rsc_set_lh, "role", state_lh);
402 }
403
404 } else {
405 free_xml(*expanded_xml);
406 *expanded_xml = NULL;
407 }
408
409 return pcmk_rc_ok;
410}
411
412void
414{
415 xmlNode *set = NULL;
416 bool any_sets = false;
417
418 const char *id = NULL;
419 const char *ticket_str = crm_element_value(xml_obj, XML_TICKET_ATTR_TICKET);
420 const char *loss_policy = crm_element_value(xml_obj, XML_TICKET_ATTR_LOSS_POLICY);
421
422 pe_ticket_t *ticket = NULL;
423
424 xmlNode *orig_xml = NULL;
425 xmlNode *expanded_xml = NULL;
426
427 CRM_CHECK(xml_obj != NULL, return);
428
429 id = ID(xml_obj);
430 if (id == NULL) {
431 pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
432 crm_element_name(xml_obj));
433 return;
434 }
435
436 if (data_set->tickets == NULL) {
438 }
439
440 if (ticket_str == NULL) {
441 pcmk__config_err("Ignoring constraint '%s' without ticket", id);
442 return;
443 } else {
444 ticket = g_hash_table_lookup(data_set->tickets, ticket_str);
445 }
446
447 if (ticket == NULL) {
448 ticket = ticket_new(ticket_str, data_set);
449 if (ticket == NULL) {
450 return;
451 }
452 }
453
454 if (unpack_rsc_ticket_tags(xml_obj, &expanded_xml,
455 data_set) != pcmk_rc_ok) {
456 return;
457 }
458 if (expanded_xml != NULL) {
459 orig_xml = xml_obj;
460 xml_obj = expanded_xml;
461 }
462
463 for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET); set != NULL;
464 set = crm_next_same_xml(set)) {
465
466 any_sets = true;
467 set = expand_idref(set, data_set->input);
468 if ((set == NULL) // Configuration error, message already logged
469 || (unpack_rsc_ticket_set(set, ticket, loss_policy,
470 data_set) != pcmk_rc_ok)) {
471 if (expanded_xml != NULL) {
472 free_xml(expanded_xml);
473 }
474 return;
475 }
476 }
477
478 if (expanded_xml) {
479 free_xml(expanded_xml);
480 xml_obj = orig_xml;
481 }
482
483 if (!any_sets) {
484 unpack_simple_rsc_ticket(xml_obj, data_set);
485 }
486}
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:122
#define RSC_ROLE_STARTED_S
Definition: common.h:112
const char * role2text(enum rsc_role_e role)
Definition: common.c:454
@ RSC_ROLE_PROMOTED
Definition: common.h:97
@ RSC_ROLE_UNKNOWN
Definition: common.h:93
enum rsc_role_e text2role(const char *role)
Definition: common.c:483
#define RSC_ROLE_UNKNOWN_S
Definition: common.h:110
uint32_t id
Definition: cpg.c:0
A dumping ground.
#define INFINITY
Definition: crm.h:99
G_GNUC_INTERNAL xmlNode * pcmk__expand_tags_in_sets(xmlNode *xml_obj, pe_working_set_t *data_set)
G_GNUC_INTERNAL bool pcmk__valid_resource_or_tag(pe_working_set_t *data_set, const char *id, pe_resource_t **rsc, pe_tag_t **tag)
G_GNUC_INTERNAL pe_resource_t * pcmk__find_constraint_resource(GList *rsc_list, const char *id)
G_GNUC_INTERNAL bool pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr, bool convert_rsc, pe_working_set_t *data_set)
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:226
#define crm_debug(fmt, args...)
Definition: logging.h:363
#define crm_log_xml_trace(xml, text)
Definition: logging.h:372
#define pcmk__config_warn(fmt...)
#define pcmk__config_err(fmt...)
#define XML_COLOC_ATTR_SOURCE_ROLE
Definition: msg_xml.h:361
#define XML_TAG_RESOURCE_REF
Definition: msg_xml.h:219
#define ID(x)
Definition: msg_xml.h:460
#define XML_CONS_TAG_RSC_SET
Definition: msg_xml.h:355
#define XML_ATTR_ID
Definition: msg_xml.h:135
#define XML_TICKET_ATTR_LOSS_POLICY
Definition: msg_xml.h:381
#define XML_COLOC_ATTR_SOURCE_INSTANCE
Definition: msg_xml.h:365
#define XML_TICKET_ATTR_TICKET
Definition: msg_xml.h:380
#define XML_COLOC_ATTR_SOURCE
Definition: msg_xml.h:360
pe_working_set_t * data_set
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:529
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:323
void pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pe_working_set_t *data_set)
@ loss_ticket_fence
@ loss_ticket_demote
@ loss_ticket_freeze
@ loss_ticket_stop
#define pe_rsc_block
Definition: pe_types.h:258
#define pe_rsc_managed
Definition: pe_types.h:257
#define pe_flag_stonith_enabled
Definition: pe_types.h:99
void pe_fence_node(pe_working_set_t *data_set, pe_node_t *node, const char *reason, bool priority_delay)
Schedule a fence action for a node.
Definition: unpack.c:95
void destroy_ticket(gpointer data)
Definition: utils.c:1965
#define pe__clear_resource_flags(resource, flags_to_clear)
Definition: internal.h:55
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:22
void resource_location(pe_resource_t *rsc, pe_node_t *node, int score, const char *tag, pe_working_set_t *data_set)
Definition: utils.c:1692
#define pe__set_resource_flags(resource, flags_to_set)
Definition: internal.h:49
pe_ticket_t * ticket_new(const char *ticket_id, pe_working_set_t *data_set)
Definition: utils.c:1977
pe_resource_t * find_clone_instance(pe_resource_t *rsc, const char *sub_id, pe_working_set_t *data_set)
Definition: clone.c:135
@ pcmk_rc_ok
Definition: results.h:146
@ pcmk_rc_schema_validation
Definition: results.h:126
Cluster status and scheduling.
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:611
@ pcmk__str_null_matches
@ pcmk__str_casei
GList * running_on
Definition: pe_types.h:380
GList * children
Definition: pe_types.h:391
char * id
Definition: pe_types.h:336
GList * rsc_tickets
Definition: pe_types.h:374
enum rsc_role_e role
Definition: pe_types.h:384
char * id
Definition: pe_types.h:471
gboolean standby
Definition: pe_types.h:474
gboolean granted
Definition: pe_types.h:472
GList * ticket_constraints
Definition: pe_types.h:169
xmlNode * input
Definition: pe_types.h:144
GList * resources
Definition: pe_types.h:165
unsigned long long flags
Definition: pe_types.h:153
GHashTable * tickets
Definition: pe_types.h:159
enum loss_ticket_policy_e loss_policy
const char * id
pe_resource_t * rsc_lh
pe_ticket_t * ticket
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:2867
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2794
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2820
void free_xml(xmlNode *child)
Definition: xml.c:824
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:830
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:2037