This source file includes following definitions.
- ticket_role_matches
- constraints_for_ticket
- rsc_ticket_new
- unpack_rsc_ticket_set
- unpack_simple_rsc_ticket
- unpack_rsc_ticket_tags
- pcmk__unpack_rsc_ticket
- pcmk__require_promotion_tickets
1
2
3
4
5
6
7
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/common/scheduler_internal.h>
17 #include <crm/pengine/status.h>
18 #include <pacemaker-internal.h>
19
20 #include "libpacemaker_private.h"
21
22 enum loss_ticket_policy {
23 loss_ticket_stop,
24 loss_ticket_demote,
25 loss_ticket_fence,
26 loss_ticket_freeze
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
37
38
39
40
41
42
43
44
45
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 pcmk__rsc_trace(rsc, "Skipping constraint: \"%s\" state filter",
54 pcmk_role_text(rsc_ticket->role));
55 return false;
56 }
57
58
59
60
61
62
63
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 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) {
90 case loss_ticket_stop:
91 resource_location(rsc, NULL, -PCMK_SCORE_INFINITY,
92 "__loss_of_ticket__", rsc->cluster);
93 break;
94
95 case loss_ticket_demote:
96
97 if (rsc_ticket->role != pcmk_role_promoted) {
98 resource_location(rsc, NULL, -PCMK_SCORE_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, -PCMK_SCORE_INFINITY,
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
117 case loss_ticket_freeze:
118 if (!ticket_role_matches(rsc, rsc_ticket)) {
119 return;
120 }
121 if (rsc->running_on != NULL) {
122 pcmk__clear_rsc_flags(rsc, pcmk_rsc_managed);
123 pcmk__set_rsc_flags(rsc, pcmk_rsc_blocked);
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, -PCMK_SCORE_INFINITY,
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)) {
140 resource_location(rsc, NULL, -PCMK_SCORE_INFINITY,
141 "__ticket_standby__", 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,
164 pcmk__str_null_matches|pcmk__str_casei)) {
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)) {
174 if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_fencing_enabled)) {
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
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 = pcmk__xe_id(set);
247 if (set_id == NULL) {
248 pcmk__config_err("Ignoring <" PCMK_XE_RESOURCE_SET "> without "
249 PCMK_XA_ID);
250 return pcmk_rc_unpack_error;
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
261 resource = pcmk__find_constraint_resource(scheduler->resources,
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));
266 return pcmk_rc_unpack_error;
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
276 static void
277 unpack_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
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) {
294 pcmk__warn_once(pcmk__wo_coloc_inst,
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 {
326 rsc = pcmk__find_constraint_resource(scheduler->resources, rsc_id);
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
355 static int
356 unpack_rsc_ticket_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
357 pcmk_scheduler_t *scheduler)
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);
376 return pcmk_rc_unpack_error;
377 }
378
379
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);
394 return pcmk_rc_unpack_error;
395
396 } else if (rsc != NULL) {
397
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
406
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;
412 return pcmk_rc_unpack_error;
413 }
414
415 if (rsc_set != NULL) {
416 if (state != NULL) {
417
418
419
420 crm_xml_add(rsc_set, PCMK_XA_ROLE, state);
421 pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_RSC_ROLE);
422 }
423
424 } else {
425 free_xml(*expanded_xml);
426 *expanded_xml = NULL;
427 }
428
429 return pcmk_rc_ok;
430 }
431
432 void
433 pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
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) {
456 scheduler->tickets = pcmk__strkey_table(free, destroy_ticket);
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)
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
512
513
514
515
516
517
518
519
520
521 void
522 pcmk__require_promotion_tickets(pcmk_resource_t *rsc)
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)) {
529 resource_location(rsc, NULL, -PCMK_SCORE_INFINITY,
530 "__stateful_without_ticket__", rsc->cluster);
531 }
532 }
533 }