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/pengine/status.h>
17 #include <pacemaker-internal.h>
18
19 #include "libpacemaker_private.h"
20
21 enum loss_ticket_policy {
22 loss_ticket_stop,
23 loss_ticket_demote,
24 loss_ticket_fence,
25 loss_ticket_freeze
26 };
27
28 typedef struct {
29 const char *id;
30 pe_resource_t *rsc;
31 pe_ticket_t *ticket;
32 enum loss_ticket_policy loss_policy;
33 int role;
34 } rsc_ticket_t;
35
36
37
38
39
40
41
42
43
44
45 static bool
46 ticket_role_matches(const pe_resource_t *rsc, const rsc_ticket_t *rsc_ticket)
47 {
48 if ((rsc_ticket->role == RSC_ROLE_UNKNOWN)
49 || (rsc_ticket->role == rsc->role)) {
50 return true;
51 }
52 pe_rsc_trace(rsc, "Skipping constraint: \"%s\" state filter",
53 role2text(rsc_ticket->role));
54 return false;
55 }
56
57
58
59
60
61
62
63
64 static void
65 constraints_for_ticket(pe_resource_t *rsc, const rsc_ticket_t *rsc_ticket,
66 pe_working_set_t *data_set)
67 {
68 GList *gIter = NULL;
69
70 CRM_CHECK((rsc != NULL) && (rsc_ticket != NULL), return);
71
72 if (rsc_ticket->ticket->granted && !rsc_ticket->ticket->standby) {
73 return;
74 }
75
76 if (rsc->children) {
77 pe_rsc_trace(rsc, "Processing ticket dependencies from %s", rsc->id);
78 for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
79 constraints_for_ticket((pe_resource_t *) gIter->data, rsc_ticket,
80 data_set);
81 }
82 return;
83 }
84
85 pe_rsc_trace(rsc, "%s: Processing ticket dependency on %s (%s, %s)",
86 rsc->id, rsc_ticket->ticket->id, rsc_ticket->id,
87 role2text(rsc_ticket->role));
88
89 if (!rsc_ticket->ticket->granted && (rsc->running_on != NULL)) {
90
91 switch (rsc_ticket->loss_policy) {
92 case loss_ticket_stop:
93 resource_location(rsc, NULL, -INFINITY, "__loss_of_ticket__",
94 data_set);
95 break;
96
97 case loss_ticket_demote:
98
99 if (rsc_ticket->role != RSC_ROLE_PROMOTED) {
100 resource_location(rsc, NULL, -INFINITY,
101 "__loss_of_ticket__", data_set);
102 }
103 break;
104
105 case loss_ticket_fence:
106 if (!ticket_role_matches(rsc, rsc_ticket)) {
107 return;
108 }
109
110 resource_location(rsc, NULL, -INFINITY, "__loss_of_ticket__",
111 data_set);
112
113 for (gIter = rsc->running_on; gIter != NULL;
114 gIter = gIter->next) {
115 pe_fence_node(data_set, (pe_node_t *) gIter->data,
116 "deadman ticket was lost", FALSE);
117 }
118 break;
119
120 case loss_ticket_freeze:
121 if (!ticket_role_matches(rsc, rsc_ticket)) {
122 return;
123 }
124 if (rsc->running_on != NULL) {
125 pe__clear_resource_flags(rsc, pe_rsc_managed);
126 pe__set_resource_flags(rsc, pe_rsc_block);
127 }
128 break;
129 }
130
131 } else if (!rsc_ticket->ticket->granted) {
132
133 if ((rsc_ticket->role != RSC_ROLE_PROMOTED)
134 || (rsc_ticket->loss_policy == loss_ticket_stop)) {
135 resource_location(rsc, NULL, -INFINITY, "__no_ticket__",
136 data_set);
137 }
138
139 } else if (rsc_ticket->ticket->standby) {
140
141 if ((rsc_ticket->role != RSC_ROLE_PROMOTED)
142 || (rsc_ticket->loss_policy == loss_ticket_stop)) {
143 resource_location(rsc, NULL, -INFINITY, "__ticket_standby__",
144 data_set);
145 }
146 }
147 }
148
149 static void
150 rsc_ticket_new(const char *id, pe_resource_t *rsc, pe_ticket_t *ticket,
151 const char *state, const char *loss_policy,
152 pe_working_set_t *data_set)
153 {
154 rsc_ticket_t *new_rsc_ticket = NULL;
155
156 if (rsc == NULL) {
157 pcmk__config_err("Ignoring ticket '%s' because resource "
158 "does not exist", id);
159 return;
160 }
161
162 new_rsc_ticket = calloc(1, sizeof(rsc_ticket_t));
163 if (new_rsc_ticket == NULL) {
164 return;
165 }
166
167 if (pcmk__str_eq(state, RSC_ROLE_STARTED_S,
168 pcmk__str_null_matches|pcmk__str_casei)) {
169 state = RSC_ROLE_UNKNOWN_S;
170 }
171
172 new_rsc_ticket->id = id;
173 new_rsc_ticket->ticket = ticket;
174 new_rsc_ticket->rsc = rsc;
175 new_rsc_ticket->role = text2role(state);
176
177 if (pcmk__str_eq(loss_policy, "fence", pcmk__str_casei)) {
178 if (pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
179 new_rsc_ticket->loss_policy = loss_ticket_fence;
180 } else {
181 pcmk__config_err("Resetting '" XML_TICKET_ATTR_LOSS_POLICY
182 "' for ticket '%s' to 'stop' "
183 "because fencing is not configured", ticket->id);
184 loss_policy = "stop";
185 }
186 }
187
188 if (new_rsc_ticket->loss_policy == loss_ticket_fence) {
189 crm_debug("On loss of ticket '%s': Fence the nodes running %s (%s)",
190 new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id,
191 role2text(new_rsc_ticket->role));
192
193 } else if (pcmk__str_eq(loss_policy, "freeze", pcmk__str_casei)) {
194 crm_debug("On loss of ticket '%s': Freeze %s (%s)",
195 new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id,
196 role2text(new_rsc_ticket->role));
197 new_rsc_ticket->loss_policy = loss_ticket_freeze;
198
199 } else if (pcmk__str_eq(loss_policy, "demote", pcmk__str_casei)) {
200 crm_debug("On loss of ticket '%s': Demote %s (%s)",
201 new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id,
202 role2text(new_rsc_ticket->role));
203 new_rsc_ticket->loss_policy = loss_ticket_demote;
204
205 } else if (pcmk__str_eq(loss_policy, "stop", pcmk__str_casei)) {
206 crm_debug("On loss of ticket '%s': Stop %s (%s)",
207 new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id,
208 role2text(new_rsc_ticket->role));
209 new_rsc_ticket->loss_policy = loss_ticket_stop;
210
211 } else {
212 if (new_rsc_ticket->role == RSC_ROLE_PROMOTED) {
213 crm_debug("On loss of ticket '%s': Default to demote %s (%s)",
214 new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id,
215 role2text(new_rsc_ticket->role));
216 new_rsc_ticket->loss_policy = loss_ticket_demote;
217
218 } else {
219 crm_debug("On loss of ticket '%s': Default to stop %s (%s)",
220 new_rsc_ticket->ticket->id, new_rsc_ticket->rsc->id,
221 role2text(new_rsc_ticket->role));
222 new_rsc_ticket->loss_policy = loss_ticket_stop;
223 }
224 }
225
226 pe_rsc_trace(rsc, "%s (%s) ==> %s",
227 rsc->id, role2text(new_rsc_ticket->role), ticket->id);
228
229 rsc->rsc_tickets = g_list_append(rsc->rsc_tickets, new_rsc_ticket);
230
231 data_set->ticket_constraints = g_list_append(data_set->ticket_constraints,
232 new_rsc_ticket);
233
234 if (!(new_rsc_ticket->ticket->granted) || new_rsc_ticket->ticket->standby) {
235 constraints_for_ticket(rsc, new_rsc_ticket, data_set);
236 }
237 }
238
239
240 static int
241 unpack_rsc_ticket_set(xmlNode *set, pe_ticket_t *ticket,
242 const char *loss_policy, pe_working_set_t *data_set)
243 {
244 const char *set_id = NULL;
245 const char *role = NULL;
246
247 CRM_CHECK(set != NULL, return EINVAL);
248 CRM_CHECK(ticket != NULL, return EINVAL);
249
250 set_id = ID(set);
251 if (set_id == NULL) {
252 pcmk__config_err("Ignoring <" XML_CONS_TAG_RSC_SET "> without "
253 XML_ATTR_ID);
254 return pcmk_rc_unpack_error;
255 }
256
257 role = crm_element_value(set, "role");
258
259 for (xmlNode *xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
260 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
261
262 pe_resource_t *resource = NULL;
263
264 resource = pcmk__find_constraint_resource(data_set->resources,
265 ID(xml_rsc));
266 if (resource == NULL) {
267 pcmk__config_err("%s: No resource found for %s",
268 set_id, ID(xml_rsc));
269 return pcmk_rc_unpack_error;
270 }
271 pe_rsc_trace(resource, "Resource '%s' depends on ticket '%s'",
272 resource->id, ticket->id);
273 rsc_ticket_new(set_id, resource, ticket, role, loss_policy, data_set);
274 }
275
276 return pcmk_rc_ok;
277 }
278
279 static void
280 unpack_simple_rsc_ticket(xmlNode *xml_obj, pe_working_set_t *data_set)
281 {
282 const char *id = NULL;
283 const char *ticket_str = crm_element_value(xml_obj, XML_TICKET_ATTR_TICKET);
284 const char *loss_policy = crm_element_value(xml_obj,
285 XML_TICKET_ATTR_LOSS_POLICY);
286
287 pe_ticket_t *ticket = NULL;
288
289 const char *rsc_id = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
290 const char *state = crm_element_value(xml_obj,
291 XML_COLOC_ATTR_SOURCE_ROLE);
292
293
294 const char *instance = crm_element_value(xml_obj,
295 XML_COLOC_ATTR_SOURCE_INSTANCE);
296
297 pe_resource_t *rsc = NULL;
298
299 if (instance != NULL) {
300 pe_warn_once(pe_wo_coloc_inst,
301 "Support for " XML_COLOC_ATTR_SOURCE_INSTANCE " is "
302 "deprecated and will be removed in a future release.");
303 }
304
305 CRM_CHECK(xml_obj != NULL, return);
306
307 id = ID(xml_obj);
308 if (id == NULL) {
309 pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
310 crm_element_name(xml_obj));
311 return;
312 }
313
314 if (ticket_str == NULL) {
315 pcmk__config_err("Ignoring constraint '%s' without ticket specified",
316 id);
317 return;
318 } else {
319 ticket = g_hash_table_lookup(data_set->tickets, ticket_str);
320 }
321
322 if (ticket == NULL) {
323 pcmk__config_err("Ignoring constraint '%s' because ticket '%s' "
324 "does not exist", id, ticket_str);
325 return;
326 }
327
328 if (rsc_id == NULL) {
329 pcmk__config_err("Ignoring constraint '%s' without resource", id);
330 return;
331 } else {
332 rsc = pcmk__find_constraint_resource(data_set->resources, rsc_id);
333 }
334
335 if (rsc == NULL) {
336 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
337 "does not exist", id, rsc_id);
338 return;
339
340 } else if ((instance != NULL) && !pe_rsc_is_clone(rsc)) {
341 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
342 "is not a clone but instance '%s' was requested",
343 id, rsc_id, instance);
344 return;
345 }
346
347 if (instance != NULL) {
348 rsc = find_clone_instance(rsc, instance);
349 if (rsc == NULL) {
350 pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
351 "does not have an instance '%s'",
352 "'%s'", id, rsc_id, instance);
353 return;
354 }
355 }
356
357 rsc_ticket_new(id, rsc, ticket, state, loss_policy, data_set);
358 }
359
360
361 static int
362 unpack_rsc_ticket_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
363 pe_working_set_t *data_set)
364 {
365 const char *id = NULL;
366 const char *rsc_id = NULL;
367 const char *state = NULL;
368
369 pe_resource_t *rsc = NULL;
370 pe_tag_t *tag = NULL;
371
372 xmlNode *rsc_set = NULL;
373
374 *expanded_xml = NULL;
375
376 CRM_CHECK(xml_obj != NULL, return EINVAL);
377
378 id = ID(xml_obj);
379 if (id == NULL) {
380 pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
381 crm_element_name(xml_obj));
382 return pcmk_rc_unpack_error;
383 }
384
385
386 *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, data_set);
387 if (*expanded_xml != NULL) {
388 crm_log_xml_trace(*expanded_xml, "Expanded rsc_ticket");
389 return pcmk_rc_ok;
390 }
391
392 rsc_id = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
393 if (rsc_id == NULL) {
394 return pcmk_rc_ok;
395 }
396
397 if (!pcmk__valid_resource_or_tag(data_set, rsc_id, &rsc, &tag)) {
398 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
399 "valid resource or tag", id, rsc_id);
400 return pcmk_rc_unpack_error;
401
402 } else if (rsc != NULL) {
403
404 return pcmk_rc_ok;
405 }
406
407 state = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
408
409 *expanded_xml = copy_xml(xml_obj);
410
411
412 if (!pcmk__tag_to_set(*expanded_xml, &rsc_set, XML_COLOC_ATTR_SOURCE,
413 false, data_set)) {
414 free_xml(*expanded_xml);
415 *expanded_xml = NULL;
416 return pcmk_rc_unpack_error;
417 }
418
419 if (rsc_set != NULL) {
420 if (state != NULL) {
421
422 crm_xml_add(rsc_set, "role", state);
423 xml_remove_prop(*expanded_xml, XML_COLOC_ATTR_SOURCE_ROLE);
424 }
425
426 } else {
427 free_xml(*expanded_xml);
428 *expanded_xml = NULL;
429 }
430
431 return pcmk_rc_ok;
432 }
433
434 void
435 pcmk__unpack_rsc_ticket(xmlNode *xml_obj, pe_working_set_t *data_set)
436 {
437 xmlNode *set = NULL;
438 bool any_sets = false;
439
440 const char *id = NULL;
441 const char *ticket_str = crm_element_value(xml_obj, XML_TICKET_ATTR_TICKET);
442 const char *loss_policy = crm_element_value(xml_obj, XML_TICKET_ATTR_LOSS_POLICY);
443
444 pe_ticket_t *ticket = NULL;
445
446 xmlNode *orig_xml = NULL;
447 xmlNode *expanded_xml = NULL;
448
449 CRM_CHECK(xml_obj != NULL, return);
450
451 id = ID(xml_obj);
452 if (id == NULL) {
453 pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
454 crm_element_name(xml_obj));
455 return;
456 }
457
458 if (data_set->tickets == NULL) {
459 data_set->tickets = pcmk__strkey_table(free, destroy_ticket);
460 }
461
462 if (ticket_str == NULL) {
463 pcmk__config_err("Ignoring constraint '%s' without ticket", id);
464 return;
465 } else {
466 ticket = g_hash_table_lookup(data_set->tickets, ticket_str);
467 }
468
469 if (ticket == NULL) {
470 ticket = ticket_new(ticket_str, data_set);
471 if (ticket == NULL) {
472 return;
473 }
474 }
475
476 if (unpack_rsc_ticket_tags(xml_obj, &expanded_xml,
477 data_set) != pcmk_rc_ok) {
478 return;
479 }
480 if (expanded_xml != NULL) {
481 orig_xml = xml_obj;
482 xml_obj = expanded_xml;
483 }
484
485 for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET); set != NULL;
486 set = crm_next_same_xml(set)) {
487
488 any_sets = true;
489 set = expand_idref(set, data_set->input);
490 if ((set == NULL)
491 || (unpack_rsc_ticket_set(set, ticket, loss_policy,
492 data_set) != 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, data_set);
507 }
508 }
509
510
511
512
513
514
515
516
517
518
519 void
520 pcmk__require_promotion_tickets(pe_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 == RSC_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 }