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 pe_rsc_trace(rsc, "Skipping constraint: \"%s\" state filter",
54 role2text(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 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) {
90 case loss_ticket_stop:
91 resource_location(rsc, NULL, -INFINITY, "__loss_of_ticket__",
92 rsc->cluster);
93 break;
94
95 case loss_ticket_demote:
96
97 if (rsc_ticket->role != pcmk_role_promoted) {
98 resource_location(rsc, NULL, -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, -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
117 case loss_ticket_freeze:
118 if (!ticket_role_matches(rsc, rsc_ticket)) {
119 return;
120 }
121 if (rsc->running_on != NULL) {
122 pe__clear_resource_flags(rsc, pcmk_rsc_managed);
123 pe__set_resource_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, -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
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 = text2role(state);
172
173 if (pcmk__str_eq(loss_policy, "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 '" XML_TICKET_ATTR_LOSS_POLICY
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
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 = ID(set);
247 if (set_id == NULL) {
248 pcmk__config_err("Ignoring <" XML_CONS_TAG_RSC_SET "> without "
249 XML_ATTR_ID);
250 return pcmk_rc_unpack_error;
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
260 resource = pcmk__find_constraint_resource(scheduler->resources,
261 ID(xml_rsc));
262 if (resource == NULL) {
263 pcmk__config_err("%s: No resource found for %s",
264 set_id, ID(xml_rsc));
265 return pcmk_rc_unpack_error;
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
275 static void
276 unpack_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,
281 XML_TICKET_ATTR_LOSS_POLICY);
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,
287 XML_COLOC_ATTR_SOURCE_ROLE);
288
289
290 const char *instance = crm_element_value(xml_obj,
291 XML_COLOC_ATTR_SOURCE_INSTANCE);
292
293 pcmk_resource_t *rsc = NULL;
294
295 if (instance != NULL) {
296 pe_warn_once(pcmk__wo_coloc_inst,
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 {
328 rsc = pcmk__find_constraint_resource(scheduler->resources, rsc_id);
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
357 static int
358 unpack_rsc_ticket_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
359 pcmk_scheduler_t *scheduler)
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);
378 return pcmk_rc_unpack_error;
379 }
380
381
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);
396 return pcmk_rc_unpack_error;
397
398 } else if (rsc != NULL) {
399
400 return pcmk_rc_ok;
401 }
402
403 state = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
404
405 *expanded_xml = copy_xml(xml_obj);
406
407
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;
412 return pcmk_rc_unpack_error;
413 }
414
415 if (rsc_set != NULL) {
416 if (state != NULL) {
417
418 crm_xml_add(rsc_set, "role", state);
419 xml_remove_prop(*expanded_xml, XML_COLOC_ATTR_SOURCE_ROLE);
420 }
421
422 } else {
423 free_xml(*expanded_xml);
424 *expanded_xml = NULL;
425 }
426
427 return pcmk_rc_ok;
428 }
429
430 void
431 pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
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) {
454 scheduler->tickets = pcmk__strkey_table(free, destroy_ticket);
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)
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
510
511
512
513
514
515
516
517
518
519 void
520 pcmk__require_promotion_tickets(pcmk_resource_t *rsc)
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 }