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.h>
17 #include <crm/common/scheduler_internal.h>
18 #include <crm/pengine/status.h>
19 #include <pacemaker-internal.h>
20
21 #include "libpacemaker_private.h"
22
23 enum loss_ticket_policy {
24 loss_ticket_stop,
25 loss_ticket_demote,
26 loss_ticket_fence,
27 loss_ticket_freeze
28 };
29
30 typedef 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
38
39
40
41
42
43
44
45
46
47 static bool
48 ticket_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
59
60
61
62
63
64
65 static void
66 constraints_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) {
93 case loss_ticket_stop:
94 resource_location(rsc, NULL, -PCMK_SCORE_INFINITY,
95 "__loss_of_ticket__",
96 rsc->priv->scheduler);
97 break;
98
99 case loss_ticket_demote:
100
101 if (rsc_ticket->role != pcmk_role_promoted) {
102 resource_location(rsc, NULL, -PCMK_SCORE_INFINITY,
103 "__loss_of_ticket__",
104 rsc->priv->scheduler);
105 }
106 break;
107
108 case loss_ticket_fence:
109 if (!ticket_role_matches(rsc, rsc_ticket)) {
110 return;
111 }
112
113 resource_location(rsc, NULL, -PCMK_SCORE_INFINITY,
114 "__loss_of_ticket__",
115 rsc->priv->scheduler);
116
117 for (iter = rsc->priv->active_nodes;
118 iter != NULL; iter = iter->next) {
119
120 pe_fence_node(rsc->priv->scheduler,
121 (pcmk_node_t *) iter->data,
122 "deadman ticket was lost", FALSE);
123 }
124 break;
125
126 case loss_ticket_freeze:
127 if (!ticket_role_matches(rsc, rsc_ticket)) {
128 return;
129 }
130 if (rsc->priv->active_nodes != NULL) {
131 pcmk__clear_rsc_flags(rsc, pcmk__rsc_managed);
132 pcmk__set_rsc_flags(rsc, pcmk__rsc_blocked);
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)) {
141 resource_location(rsc, NULL, -PCMK_SCORE_INFINITY,
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)) {
149 resource_location(rsc, NULL, -PCMK_SCORE_INFINITY,
150 "__ticket_standby__", rsc->priv->scheduler);
151 }
152 }
153 }
154
155 static void
156 rsc_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
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,
180 pcmk__sched_fencing_enabled)) {
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
231 rsc->priv->ticket_constraints =
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
241 static int
242 unpack_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);
255 return pcmk_rc_unpack_error;
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
267 resource = pcmk__find_constraint_resource(scheduler->priv->resources,
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));
272 return pcmk_rc_unpack_error;
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
282 static void
283 unpack_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 {
324 rsc = pcmk__find_constraint_resource(scheduler->priv->resources,
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
338 static int
339 unpack_rsc_ticket_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
340 pcmk_scheduler_t *scheduler)
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);
359 return pcmk_rc_unpack_error;
360 }
361
362
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);
377 return pcmk_rc_unpack_error;
378
379 } else if (rsc != NULL) {
380
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
389
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;
395 return pcmk_rc_unpack_error;
396 }
397
398 if (rsc_set != NULL) {
399 if (state != NULL) {
400
401
402
403 crm_xml_add(rsc_set, PCMK_XA_ROLE, state);
404 pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_RSC_ROLE);
405 }
406
407 } else {
408 pcmk__xml_free(*expanded_xml);
409 *expanded_xml = NULL;
410 }
411
412 return pcmk_rc_ok;
413 }
414
415 void
416 pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
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) {
439 scheduler->priv->ticket_constraints =
440 pcmk__strkey_table(free, destroy_ticket);
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;
474 set = pcmk__xe_resolve_idref(set, scheduler->input);
475 loss_policy = crm_element_value(xml_obj, PCMK_XA_LOSS_POLICY);
476
477 if ((set == NULL)
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
497
498
499
500
501
502
503
504
505
506 void
507 pcmk__require_promotion_tickets(pcmk_resource_t *rsc)
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,
517 pcmk__ticket_standby))) {
518 resource_location(rsc, NULL, -PCMK_SCORE_INFINITY,
519 "__stateful_without_ticket__",
520 rsc->priv->scheduler);
521 }
522 }
523 }