This source file includes following definitions.
- add_singleton
- lookup_singleton
- find_existing_action
- find_rsc_op_entry_helper
- find_rsc_op_entry
- new_action
- unpack_action_node_attributes
- update_action_optional
- effective_quorum_policy
- update_resource_action_runnable
- update_resource_flags_for_action
- valid_stop_on_fail
- unpack_operation_on_fail
- unpack_timeout
- unpack_interval_origin
- unpack_start_delay
- find_min_interval_mon
- unpack_operation
- custom_action
- get_pseudo_op
- find_unfencing_devices
- node_priority_fencing_delay
- pe_fence_op
- pe_free_action
- pe_get_configured_timeout
- get_complex_task
- find_first_action
- find_actions
- find_actions_exact
- pe__resource_actions
- pe__action2reason
- pe_action_set_reason
- pe__clear_resource_history
- pe__is_newer_op
- sort_op_by_callid
- pe__new_rsc_pseudo_action
- pe__add_action_expected_result
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <glib.h>
13 #include <stdbool.h>
14
15 #include <crm/crm.h>
16 #include <crm/msg_xml.h>
17 #include <crm/pengine/internal.h>
18 #include "pe_status_private.h"
19
20 static void unpack_operation(pe_action_t *action, xmlNode *xml_obj,
21 pe_resource_t *container,
22 pe_working_set_t *data_set, guint interval_ms);
23
24 static void
25 add_singleton(pe_working_set_t *data_set, pe_action_t *action)
26 {
27 if (data_set->singletons == NULL) {
28 data_set->singletons = pcmk__strkey_table(NULL, NULL);
29 }
30 g_hash_table_insert(data_set->singletons, action->uuid, action);
31 }
32
33 static pe_action_t *
34 lookup_singleton(pe_working_set_t *data_set, const char *action_uuid)
35 {
36 if (data_set->singletons == NULL) {
37 return NULL;
38 }
39 return g_hash_table_lookup(data_set->singletons, action_uuid);
40 }
41
42
43
44
45
46
47
48
49
50
51
52
53 static pe_action_t *
54 find_existing_action(const char *key, pe_resource_t *rsc, const pe_node_t *node,
55 pe_working_set_t *data_set)
56 {
57 GList *matches = NULL;
58 pe_action_t *action = NULL;
59
60
61
62
63 matches = find_actions(((rsc == NULL)? data_set->actions : rsc->actions),
64 key, node);
65 if (matches == NULL) {
66 return NULL;
67 }
68 CRM_LOG_ASSERT(!pcmk__list_of_multiple(matches));
69
70 action = matches->data;
71 g_list_free(matches);
72 return action;
73 }
74
75 static xmlNode *
76 find_rsc_op_entry_helper(const pe_resource_t *rsc, const char *key,
77 gboolean include_disabled)
78 {
79 guint interval_ms = 0;
80 gboolean do_retry = TRUE;
81 char *local_key = NULL;
82 const char *name = NULL;
83 const char *interval_spec = NULL;
84 char *match_key = NULL;
85 xmlNode *op = NULL;
86 xmlNode *operation = NULL;
87
88 retry:
89 for (operation = pcmk__xe_first_child(rsc->ops_xml); operation != NULL;
90 operation = pcmk__xe_next(operation)) {
91
92 if (pcmk__str_eq((const char *)operation->name, "op", pcmk__str_none)) {
93 bool enabled = false;
94
95 name = crm_element_value(operation, "name");
96 interval_spec = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
97 if (!include_disabled && pcmk__xe_get_bool_attr(operation, "enabled", &enabled) == pcmk_rc_ok &&
98 !enabled) {
99 continue;
100 }
101
102 interval_ms = crm_parse_interval_spec(interval_spec);
103 match_key = pcmk__op_key(rsc->id, name, interval_ms);
104 if (pcmk__str_eq(key, match_key, pcmk__str_casei)) {
105 op = operation;
106 }
107 free(match_key);
108
109 if (rsc->clone_name) {
110 match_key = pcmk__op_key(rsc->clone_name, name, interval_ms);
111 if (pcmk__str_eq(key, match_key, pcmk__str_casei)) {
112 op = operation;
113 }
114 free(match_key);
115 }
116
117 if (op != NULL) {
118 free(local_key);
119 return op;
120 }
121 }
122 }
123
124 free(local_key);
125 if (do_retry == FALSE) {
126 return NULL;
127 }
128
129 do_retry = FALSE;
130 if (strstr(key, CRMD_ACTION_MIGRATE) || strstr(key, CRMD_ACTION_MIGRATED)) {
131 local_key = pcmk__op_key(rsc->id, "migrate", 0);
132 key = local_key;
133 goto retry;
134
135 } else if (strstr(key, "_notify_")) {
136 local_key = pcmk__op_key(rsc->id, "notify", 0);
137 key = local_key;
138 goto retry;
139 }
140
141 return NULL;
142 }
143
144 xmlNode *
145 find_rsc_op_entry(const pe_resource_t *rsc, const char *key)
146 {
147 return find_rsc_op_entry_helper(rsc, key, FALSE);
148 }
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166 static pe_action_t *
167 new_action(char *key, const char *task, pe_resource_t *rsc,
168 const pe_node_t *node, bool optional, bool for_graph,
169 pe_working_set_t *data_set)
170 {
171 pe_action_t *action = calloc(1, sizeof(pe_action_t));
172
173 CRM_ASSERT(action != NULL);
174
175 action->rsc = rsc;
176 action->task = strdup(task); CRM_ASSERT(action->task != NULL);
177 action->uuid = key;
178 action->extra = pcmk__strkey_table(free, free);
179 action->meta = pcmk__strkey_table(free, free);
180
181 if (node) {
182 action->node = pe__copy_node(node);
183 }
184
185 if (pcmk__str_eq(task, CRM_OP_LRM_DELETE, pcmk__str_casei)) {
186
187 pe__set_action_flags(action, pe_action_dc);
188 }
189
190 pe__set_action_flags(action, pe_action_runnable);
191 if (optional) {
192 pe__set_action_flags(action, pe_action_optional);
193 } else {
194 pe__clear_action_flags(action, pe_action_optional);
195 }
196
197 if (rsc != NULL) {
198 guint interval_ms = 0;
199
200 action->op_entry = find_rsc_op_entry_helper(rsc, key, TRUE);
201 parse_op_key(key, NULL, NULL, &interval_ms);
202 unpack_operation(action, action->op_entry, rsc->container, data_set,
203 interval_ms);
204 }
205
206 if (for_graph) {
207 pe_rsc_trace(rsc, "Created %s action %d (%s): %s for %s on %s",
208 (optional? "optional" : "required"),
209 data_set->action_id, key, task,
210 ((rsc == NULL)? "no resource" : rsc->id),
211 pe__node_name(node));
212 action->id = data_set->action_id++;
213
214 data_set->actions = g_list_prepend(data_set->actions, action);
215 if (rsc == NULL) {
216 add_singleton(data_set, action);
217 } else {
218 rsc->actions = g_list_prepend(rsc->actions, action);
219 }
220 }
221 return action;
222 }
223
224
225
226
227
228
229
230
231 static void
232 unpack_action_node_attributes(pe_action_t *action, pe_working_set_t *data_set)
233 {
234 if (!pcmk_is_set(action->flags, pe_action_have_node_attrs)
235 && (action->op_entry != NULL)) {
236
237 pe_rule_eval_data_t rule_data = {
238 .node_hash = action->node->details->attrs,
239 .role = RSC_ROLE_UNKNOWN,
240 .now = data_set->now,
241 .match_data = NULL,
242 .rsc_data = NULL,
243 .op_data = NULL
244 };
245
246 pe__set_action_flags(action, pe_action_have_node_attrs);
247 pe__unpack_dataset_nvpairs(action->op_entry, XML_TAG_ATTR_SETS,
248 &rule_data, action->extra, NULL,
249 FALSE, data_set);
250 }
251 }
252
253
254
255
256
257
258
259
260 static void
261 update_action_optional(pe_action_t *action, gboolean optional)
262 {
263
264 if ((action->rsc != NULL) && (action->node != NULL)
265 && !pcmk_is_set(action->flags, pe_action_pseudo)
266 && !pcmk_is_set(action->rsc->flags, pe_rsc_managed)
267 && (g_hash_table_lookup(action->meta,
268 XML_LRM_ATTR_INTERVAL_MS) == NULL)) {
269 pe_rsc_debug(action->rsc, "%s on %s is optional (%s is unmanaged)",
270 action->uuid, pe__node_name(action->node),
271 action->rsc->id);
272 pe__set_action_flags(action, pe_action_optional);
273
274
275
276 } else if (!optional) {
277 pe__clear_action_flags(action, pe_action_optional);
278 }
279 }
280
281 static enum pe_quorum_policy
282 effective_quorum_policy(pe_resource_t *rsc, pe_working_set_t *data_set)
283 {
284 enum pe_quorum_policy policy = data_set->no_quorum_policy;
285
286 if (pcmk_is_set(data_set->flags, pe_flag_have_quorum)) {
287 policy = no_quorum_ignore;
288
289 } else if (data_set->no_quorum_policy == no_quorum_demote) {
290 switch (rsc->role) {
291 case RSC_ROLE_PROMOTED:
292 case RSC_ROLE_UNPROMOTED:
293 if (rsc->next_role > RSC_ROLE_UNPROMOTED) {
294 pe__set_next_role(rsc, RSC_ROLE_UNPROMOTED,
295 "no-quorum-policy=demote");
296 }
297 policy = no_quorum_ignore;
298 break;
299 default:
300 policy = no_quorum_stop;
301 break;
302 }
303 }
304 return policy;
305 }
306
307
308
309
310
311
312
313
314
315
316
317 static void
318 update_resource_action_runnable(pe_action_t *action, bool for_graph,
319 pe_working_set_t *data_set)
320 {
321 if (pcmk_is_set(action->flags, pe_action_pseudo)) {
322 return;
323 }
324
325 if (action->node == NULL) {
326 pe_rsc_trace(action->rsc, "%s is unrunnable (unallocated)",
327 action->uuid);
328 pe__clear_action_flags(action, pe_action_runnable);
329
330 } else if (!pcmk_is_set(action->flags, pe_action_dc)
331 && !(action->node->details->online)
332 && (!pe__is_guest_node(action->node)
333 || action->node->details->remote_requires_reset)) {
334 pe__clear_action_flags(action, pe_action_runnable);
335 do_crm_log((for_graph? LOG_WARNING: LOG_TRACE),
336 "%s on %s is unrunnable (node is offline)",
337 action->uuid, pe__node_name(action->node));
338 if (pcmk_is_set(action->rsc->flags, pe_rsc_managed)
339 && for_graph
340 && pcmk__str_eq(action->task, CRMD_ACTION_STOP, pcmk__str_casei)
341 && !(action->node->details->unclean)) {
342 pe_fence_node(data_set, action->node, "stop is unrunnable", false);
343 }
344
345 } else if (!pcmk_is_set(action->flags, pe_action_dc)
346 && action->node->details->pending) {
347 pe__clear_action_flags(action, pe_action_runnable);
348 do_crm_log((for_graph? LOG_WARNING: LOG_TRACE),
349 "Action %s on %s is unrunnable (node is pending)",
350 action->uuid, pe__node_name(action->node));
351
352 } else if (action->needs == rsc_req_nothing) {
353 pe_action_set_reason(action, NULL, TRUE);
354 if (pe__is_guest_node(action->node)
355 && !pe_can_fence(data_set, action->node)) {
356
357
358
359
360
361 pe_rsc_debug(action->rsc, "%s on %s is unrunnable "
362 "(node's host cannot be fenced)",
363 action->uuid, pe__node_name(action->node));
364 pe__clear_action_flags(action, pe_action_runnable);
365 } else {
366 pe_rsc_trace(action->rsc,
367 "%s on %s does not require fencing or quorum",
368 action->uuid, pe__node_name(action->node));
369 pe__set_action_flags(action, pe_action_runnable);
370 }
371
372 } else {
373 switch (effective_quorum_policy(action->rsc, data_set)) {
374 case no_quorum_stop:
375 pe_rsc_debug(action->rsc, "%s on %s is unrunnable (no quorum)",
376 action->uuid, pe__node_name(action->node));
377 pe__clear_action_flags(action, pe_action_runnable);
378 pe_action_set_reason(action, "no quorum", true);
379 break;
380
381 case no_quorum_freeze:
382 if (!action->rsc->fns->active(action->rsc, TRUE)
383 || (action->rsc->next_role > action->rsc->role)) {
384 pe_rsc_debug(action->rsc,
385 "%s on %s is unrunnable (no quorum)",
386 action->uuid, pe__node_name(action->node));
387 pe__clear_action_flags(action, pe_action_runnable);
388 pe_action_set_reason(action, "quorum freeze", true);
389 }
390 break;
391
392 default:
393
394 pe__set_action_flags(action, pe_action_runnable);
395 break;
396 }
397 }
398 }
399
400
401
402
403
404
405
406
407 static void
408 update_resource_flags_for_action(pe_resource_t *rsc, pe_action_t *action)
409 {
410
411
412
413 if (pcmk__str_eq(action->task, CRMD_ACTION_STOP, pcmk__str_casei)) {
414 pe__set_resource_flags(rsc, pe_rsc_stopping);
415
416 } else if (pcmk__str_eq(action->task, CRMD_ACTION_START, pcmk__str_casei)) {
417 if (pcmk_is_set(action->flags, pe_action_runnable)) {
418 pe__set_resource_flags(rsc, pe_rsc_starting);
419 } else {
420 pe__clear_resource_flags(rsc, pe_rsc_starting);
421 }
422 }
423 }
424
425 static bool
426 valid_stop_on_fail(const char *value)
427 {
428 return !pcmk__strcase_any_of(value, "standby", "demote", "stop", NULL);
429 }
430
431 static const char *
432 unpack_operation_on_fail(pe_action_t * action)
433 {
434 const char *name = NULL;
435 const char *role = NULL;
436 const char *on_fail = NULL;
437 const char *interval_spec = NULL;
438 const char *value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ON_FAIL);
439
440 if (pcmk__str_eq(action->task, CRMD_ACTION_STOP, pcmk__str_casei)
441 && !valid_stop_on_fail(value)) {
442
443 pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for %s stop "
444 "action to default value because '%s' is not "
445 "allowed for stop", action->rsc->id, value);
446 return NULL;
447
448 } else if (pcmk__str_eq(action->task, CRMD_ACTION_DEMOTE, pcmk__str_casei) && !value) {
449
450 xmlNode *operation = NULL;
451
452 CRM_CHECK(action->rsc != NULL, return NULL);
453
454 for (operation = pcmk__xe_first_child(action->rsc->ops_xml);
455 (operation != NULL) && (value == NULL);
456 operation = pcmk__xe_next(operation)) {
457 bool enabled = false;
458
459 if (!pcmk__str_eq((const char *)operation->name, "op", pcmk__str_none)) {
460 continue;
461 }
462 name = crm_element_value(operation, "name");
463 role = crm_element_value(operation, "role");
464 on_fail = crm_element_value(operation, XML_OP_ATTR_ON_FAIL);
465 interval_spec = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
466 if (!on_fail) {
467 continue;
468 } else if (pcmk__xe_get_bool_attr(operation, "enabled", &enabled) == pcmk_rc_ok && !enabled) {
469 continue;
470 } else if (!pcmk__str_eq(name, "monitor", pcmk__str_casei)
471 || !pcmk__strcase_any_of(role, RSC_ROLE_PROMOTED_S,
472 RSC_ROLE_PROMOTED_LEGACY_S,
473 NULL)) {
474 continue;
475 } else if (crm_parse_interval_spec(interval_spec) == 0) {
476 continue;
477 } else if (pcmk__str_eq(on_fail, "demote", pcmk__str_casei)) {
478 continue;
479 }
480
481 value = on_fail;
482 }
483 } else if (pcmk__str_eq(action->task, CRM_OP_LRM_DELETE, pcmk__str_casei)) {
484 value = "ignore";
485
486 } else if (pcmk__str_eq(value, "demote", pcmk__str_casei)) {
487 name = crm_element_value(action->op_entry, "name");
488 role = crm_element_value(action->op_entry, "role");
489 interval_spec = crm_element_value(action->op_entry,
490 XML_LRM_ATTR_INTERVAL);
491
492 if (!pcmk__str_eq(name, CRMD_ACTION_PROMOTE, pcmk__str_casei)
493 && (!pcmk__str_eq(name, CRMD_ACTION_STATUS, pcmk__str_casei)
494 || !pcmk__strcase_any_of(role, RSC_ROLE_PROMOTED_S,
495 RSC_ROLE_PROMOTED_LEGACY_S, NULL)
496 || (crm_parse_interval_spec(interval_spec) == 0))) {
497 pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for %s %s "
498 "action to default value because 'demote' is not "
499 "allowed for it", action->rsc->id, name);
500 return NULL;
501 }
502 }
503
504 return value;
505 }
506
507 static int
508 unpack_timeout(const char *value)
509 {
510 int timeout_ms = crm_get_msec(value);
511
512 if (timeout_ms < 0) {
513 timeout_ms = crm_get_msec(CRM_DEFAULT_OP_TIMEOUT_S);
514 }
515 return timeout_ms;
516 }
517
518
519 static bool
520 unpack_interval_origin(const char *value, xmlNode *xml_obj, guint interval_ms,
521 crm_time_t *now, long long *start_delay)
522 {
523 long long result = 0;
524 guint interval_sec = interval_ms / 1000;
525 crm_time_t *origin = NULL;
526
527
528 if ((value == NULL) || (interval_ms == 0) || (now == NULL)) {
529 return false;
530 }
531
532
533 origin = crm_time_new(value);
534 if (origin == NULL) {
535 pcmk__config_err("Ignoring '" XML_OP_ATTR_ORIGIN "' for operation "
536 "'%s' because '%s' is not valid",
537 (ID(xml_obj)? ID(xml_obj) : "(missing ID)"), value);
538 return false;
539 }
540
541
542 result = crm_time_get_seconds(now) - crm_time_get_seconds(origin);
543 crm_time_free(origin);
544
545
546 result = result % interval_sec;
547
548
549 result = ((result <= 0)? 0 : interval_sec) - result;
550 crm_info("Calculated a start delay of %llds for operation '%s'",
551 result,
552 (ID(xml_obj)? ID(xml_obj) : "(unspecified)"));
553
554 if (start_delay != NULL) {
555 *start_delay = result * 1000;
556 }
557 return true;
558 }
559
560 static int
561 unpack_start_delay(const char *value, GHashTable *meta)
562 {
563 int start_delay = 0;
564
565 if (value != NULL) {
566 start_delay = crm_get_msec(value);
567
568 if (start_delay < 0) {
569 start_delay = 0;
570 }
571
572 if (meta) {
573 g_hash_table_replace(meta, strdup(XML_OP_ATTR_START_DELAY),
574 pcmk__itoa(start_delay));
575 }
576 }
577
578 return start_delay;
579 }
580
581 static xmlNode *
582 find_min_interval_mon(pe_resource_t * rsc, gboolean include_disabled)
583 {
584 guint interval_ms = 0;
585 guint min_interval_ms = G_MAXUINT;
586 const char *name = NULL;
587 const char *interval_spec = NULL;
588 xmlNode *op = NULL;
589 xmlNode *operation = NULL;
590
591 for (operation = pcmk__xe_first_child(rsc->ops_xml);
592 operation != NULL;
593 operation = pcmk__xe_next(operation)) {
594
595 if (pcmk__str_eq((const char *)operation->name, "op", pcmk__str_none)) {
596 bool enabled = false;
597
598 name = crm_element_value(operation, "name");
599 interval_spec = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
600 if (!include_disabled && pcmk__xe_get_bool_attr(operation, "enabled", &enabled) == pcmk_rc_ok &&
601 !enabled) {
602 continue;
603 }
604
605 if (!pcmk__str_eq(name, RSC_STATUS, pcmk__str_casei)) {
606 continue;
607 }
608
609 interval_ms = crm_parse_interval_spec(interval_spec);
610
611 if (interval_ms && (interval_ms < min_interval_ms)) {
612 min_interval_ms = interval_ms;
613 op = operation;
614 }
615 }
616 }
617
618 return op;
619 }
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634 static void
635 unpack_operation(pe_action_t * action, xmlNode * xml_obj, pe_resource_t * container,
636 pe_working_set_t * data_set, guint interval_ms)
637 {
638 int timeout_ms = 0;
639 const char *value = NULL;
640 bool is_probe = false;
641
642 pe_rsc_eval_data_t rsc_rule_data = {
643 .standard = crm_element_value(action->rsc->xml, XML_AGENT_ATTR_CLASS),
644 .provider = crm_element_value(action->rsc->xml, XML_AGENT_ATTR_PROVIDER),
645 .agent = crm_element_value(action->rsc->xml, XML_EXPR_ATTR_TYPE)
646 };
647
648 pe_op_eval_data_t op_rule_data = {
649 .op_name = action->task,
650 .interval = interval_ms
651 };
652
653 pe_rule_eval_data_t rule_data = {
654 .node_hash = NULL,
655 .role = RSC_ROLE_UNKNOWN,
656 .now = data_set->now,
657 .match_data = NULL,
658 .rsc_data = &rsc_rule_data,
659 .op_data = &op_rule_data
660 };
661
662 CRM_CHECK(action && action->rsc, return);
663
664 is_probe = pcmk_is_probe(action->task, interval_ms);
665
666
667 pe__unpack_dataset_nvpairs(data_set->op_defaults, XML_TAG_META_SETS, &rule_data,
668 action->meta, NULL, FALSE, data_set);
669
670
671 if (is_probe) {
672 xmlNode *min_interval_mon = find_min_interval_mon(action->rsc, FALSE);
673
674 if (min_interval_mon) {
675 value = crm_element_value(min_interval_mon, XML_ATTR_TIMEOUT);
676 if (value) {
677 crm_trace("\t%s: Setting default timeout to minimum-interval "
678 "monitor's timeout '%s'", action->uuid, value);
679 g_hash_table_replace(action->meta, strdup(XML_ATTR_TIMEOUT),
680 strdup(value));
681 }
682 }
683 }
684
685 if (xml_obj) {
686 xmlAttrPtr xIter = NULL;
687
688
689 pe__unpack_dataset_nvpairs(xml_obj, XML_TAG_META_SETS, &rule_data,
690 action->meta, NULL, TRUE, data_set);
691
692
693
694
695 for (xIter = xml_obj->properties; xIter; xIter = xIter->next) {
696 const char *prop_name = (const char *)xIter->name;
697 const char *prop_value = crm_element_value(xml_obj, prop_name);
698
699 g_hash_table_replace(action->meta, strdup(prop_name), strdup(prop_value));
700 }
701 }
702
703 g_hash_table_remove(action->meta, "id");
704
705
706 if (interval_ms > 0) {
707 g_hash_table_replace(action->meta, strdup(XML_LRM_ATTR_INTERVAL),
708 crm_strdup_printf("%u", interval_ms));
709 } else {
710 g_hash_table_remove(action->meta, XML_LRM_ATTR_INTERVAL);
711 }
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727 if (pcmk_is_set(pcmk_get_ra_caps(rsc_rule_data.standard),
728 pcmk_ra_cap_fence_params)
729 && (pcmk__str_eq(action->task, RSC_START, pcmk__str_casei)
730 || is_probe)) {
731
732 GHashTable *params = pe_rsc_params(action->rsc, action->node, data_set);
733
734 value = g_hash_table_lookup(params, "pcmk_monitor_timeout");
735
736 if (value) {
737 crm_trace("\t%s: Setting timeout to pcmk_monitor_timeout '%s', "
738 "overriding default", action->uuid, value);
739 g_hash_table_replace(action->meta, strdup(XML_ATTR_TIMEOUT),
740 strdup(value));
741 }
742 }
743
744
745 value = g_hash_table_lookup(action->meta, XML_ATTR_TIMEOUT);
746 timeout_ms = unpack_timeout(value);
747 g_hash_table_replace(action->meta, strdup(XML_ATTR_TIMEOUT),
748 pcmk__itoa(timeout_ms));
749
750 if (!pcmk__strcase_any_of(action->task, RSC_START, RSC_PROMOTE, NULL)) {
751 action->needs = rsc_req_nothing;
752 value = "nothing (not start or promote)";
753
754 } else if (pcmk_is_set(action->rsc->flags, pe_rsc_needs_fencing)) {
755 action->needs = rsc_req_stonith;
756 value = "fencing";
757
758 } else if (pcmk_is_set(action->rsc->flags, pe_rsc_needs_quorum)) {
759 action->needs = rsc_req_quorum;
760 value = "quorum";
761
762 } else {
763 action->needs = rsc_req_nothing;
764 value = "nothing";
765 }
766 pe_rsc_trace(action->rsc, "%s requires %s", action->uuid, value);
767
768 value = unpack_operation_on_fail(action);
769
770 if (value == NULL) {
771
772 } else if (pcmk__str_eq(value, "block", pcmk__str_casei)) {
773 action->on_fail = action_fail_block;
774 g_hash_table_insert(action->meta, strdup(XML_OP_ATTR_ON_FAIL), strdup("block"));
775 value = "block";
776
777 } else if (pcmk__str_eq(value, "fence", pcmk__str_casei)) {
778 action->on_fail = action_fail_fence;
779 value = "node fencing";
780
781 if (!pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
782 pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for "
783 "operation '%s' to 'stop' because 'fence' is not "
784 "valid when fencing is disabled", action->uuid);
785 action->on_fail = action_fail_stop;
786 action->fail_role = RSC_ROLE_STOPPED;
787 value = "stop resource";
788 }
789
790 } else if (pcmk__str_eq(value, "standby", pcmk__str_casei)) {
791 action->on_fail = action_fail_standby;
792 value = "node standby";
793
794 } else if (pcmk__strcase_any_of(value, "ignore", PCMK__VALUE_NOTHING,
795 NULL)) {
796 action->on_fail = action_fail_ignore;
797 value = "ignore";
798
799 } else if (pcmk__str_eq(value, "migrate", pcmk__str_casei)) {
800 action->on_fail = action_fail_migrate;
801 value = "force migration";
802
803 } else if (pcmk__str_eq(value, "stop", pcmk__str_casei)) {
804 action->on_fail = action_fail_stop;
805 action->fail_role = RSC_ROLE_STOPPED;
806 value = "stop resource";
807
808 } else if (pcmk__str_eq(value, "restart", pcmk__str_casei)) {
809 action->on_fail = action_fail_recover;
810 value = "restart (and possibly migrate)";
811
812 } else if (pcmk__str_eq(value, "restart-container", pcmk__str_casei)) {
813 if (container) {
814 action->on_fail = action_fail_restart_container;
815 value = "restart container (and possibly migrate)";
816
817 } else {
818 value = NULL;
819 }
820
821 } else if (pcmk__str_eq(value, "demote", pcmk__str_casei)) {
822 action->on_fail = action_fail_demote;
823 value = "demote instance";
824
825 } else {
826 pe_err("Resource %s: Unknown failure type (%s)", action->rsc->id, value);
827 value = NULL;
828 }
829
830
831 if (value == NULL && container) {
832 action->on_fail = action_fail_restart_container;
833 value = "restart container (and possibly migrate) (default)";
834
835
836
837
838
839
840
841
842
843 } else if (((value == NULL) || !pcmk_is_set(action->rsc->flags, pe_rsc_managed))
844 && pe__resource_is_remote_conn(action->rsc, data_set)
845 && !(pcmk__str_eq(action->task, CRMD_ACTION_STATUS, pcmk__str_casei)
846 && (interval_ms == 0))
847 && !pcmk__str_eq(action->task, CRMD_ACTION_START, pcmk__str_casei)) {
848
849 if (!pcmk_is_set(action->rsc->flags, pe_rsc_managed)) {
850 action->on_fail = action_fail_stop;
851 action->fail_role = RSC_ROLE_STOPPED;
852 value = "stop unmanaged remote node (enforcing default)";
853
854 } else {
855 if (pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
856 value = "fence remote node (default)";
857 } else {
858 value = "recover remote node connection (default)";
859 }
860
861 if (action->rsc->remote_reconnect_ms) {
862 action->fail_role = RSC_ROLE_STOPPED;
863 }
864 action->on_fail = action_fail_reset_remote;
865 }
866
867 } else if (value == NULL && pcmk__str_eq(action->task, CRMD_ACTION_STOP, pcmk__str_casei)) {
868 if (pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
869 action->on_fail = action_fail_fence;
870 value = "resource fence (default)";
871
872 } else {
873 action->on_fail = action_fail_block;
874 value = "resource block (default)";
875 }
876
877 } else if (value == NULL) {
878 action->on_fail = action_fail_recover;
879 value = "restart (and possibly migrate) (default)";
880 }
881
882 pe_rsc_trace(action->rsc, "%s failure handling: %s",
883 action->uuid, value);
884
885 value = NULL;
886 if (xml_obj != NULL) {
887 value = g_hash_table_lookup(action->meta, "role_after_failure");
888 if (value) {
889 pe_warn_once(pe_wo_role_after,
890 "Support for role_after_failure is deprecated and will be removed in a future release");
891 }
892 }
893 if (value != NULL && action->fail_role == RSC_ROLE_UNKNOWN) {
894 action->fail_role = text2role(value);
895 }
896
897 if (action->fail_role == RSC_ROLE_UNKNOWN) {
898 if (pcmk__str_eq(action->task, CRMD_ACTION_PROMOTE, pcmk__str_casei)) {
899 action->fail_role = RSC_ROLE_UNPROMOTED;
900 } else {
901 action->fail_role = RSC_ROLE_STARTED;
902 }
903 }
904 pe_rsc_trace(action->rsc, "%s failure results in: %s",
905 action->uuid, role2text(action->fail_role));
906
907 value = g_hash_table_lookup(action->meta, XML_OP_ATTR_START_DELAY);
908 if (value) {
909 unpack_start_delay(value, action->meta);
910 } else {
911 long long start_delay = 0;
912
913 value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN);
914 if (unpack_interval_origin(value, xml_obj, interval_ms, data_set->now,
915 &start_delay)) {
916 g_hash_table_replace(action->meta, strdup(XML_OP_ATTR_START_DELAY),
917 crm_strdup_printf("%lld", start_delay));
918 }
919 }
920 }
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939 pe_action_t *
940 custom_action(pe_resource_t *rsc, char *key, const char *task,
941 const pe_node_t *on_node, gboolean optional, gboolean save_action,
942 pe_working_set_t *data_set)
943 {
944 pe_action_t *action = NULL;
945
946 CRM_ASSERT((key != NULL) && (task != NULL) && (data_set != NULL));
947
948 if (save_action) {
949 action = find_existing_action(key, rsc, on_node, data_set);
950 }
951
952 if (action == NULL) {
953 action = new_action(key, task, rsc, on_node, optional, save_action,
954 data_set);
955 } else {
956 free(key);
957 }
958
959 update_action_optional(action, optional);
960
961 if (rsc != NULL) {
962 if (action->node != NULL) {
963 unpack_action_node_attributes(action, data_set);
964 }
965
966 update_resource_action_runnable(action, save_action, data_set);
967
968 if (save_action) {
969 update_resource_flags_for_action(rsc, action);
970 }
971 }
972
973 return action;
974 }
975
976 pe_action_t *
977 get_pseudo_op(const char *name, pe_working_set_t * data_set)
978 {
979 pe_action_t *op = lookup_singleton(data_set, name);
980
981 if (op == NULL) {
982 op = custom_action(NULL, strdup(name), name, NULL, TRUE, TRUE, data_set);
983 pe__set_action_flags(op, pe_action_pseudo|pe_action_runnable);
984 }
985 return op;
986 }
987
988 static GList *
989 find_unfencing_devices(GList *candidates, GList *matches)
990 {
991 for (GList *gIter = candidates; gIter != NULL; gIter = gIter->next) {
992 pe_resource_t *candidate = gIter->data;
993
994 if (candidate->children != NULL) {
995 matches = find_unfencing_devices(candidate->children, matches);
996
997 } else if (!pcmk_is_set(candidate->flags, pe_rsc_fence_device)) {
998 continue;
999
1000 } else if (pcmk_is_set(candidate->flags, pe_rsc_needs_unfencing)) {
1001 matches = g_list_prepend(matches, candidate);
1002
1003 } else if (pcmk__str_eq(g_hash_table_lookup(candidate->meta,
1004 PCMK_STONITH_PROVIDES),
1005 PCMK__VALUE_UNFENCING,
1006 pcmk__str_casei)) {
1007 matches = g_list_prepend(matches, candidate);
1008 }
1009 }
1010 return matches;
1011 }
1012
1013 static int
1014 node_priority_fencing_delay(pe_node_t * node, pe_working_set_t * data_set)
1015 {
1016 int member_count = 0;
1017 int online_count = 0;
1018 int top_priority = 0;
1019 int lowest_priority = 0;
1020 GList *gIter = NULL;
1021
1022
1023 if (data_set->priority_fencing_delay <= 0) {
1024 return 0;
1025 }
1026
1027
1028
1029 if (node->details->type != node_member) {
1030 return 0;
1031 }
1032
1033
1034 if (node->details->online) {
1035 return 0;
1036 }
1037
1038 for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
1039 pe_node_t *n = gIter->data;
1040
1041 if (n->details->type != node_member) {
1042 continue;
1043 }
1044
1045 member_count ++;
1046
1047 if (n->details->online) {
1048 online_count++;
1049 }
1050
1051 if (member_count == 1
1052 || n->details->priority > top_priority) {
1053 top_priority = n->details->priority;
1054 }
1055
1056 if (member_count == 1
1057 || n->details->priority < lowest_priority) {
1058 lowest_priority = n->details->priority;
1059 }
1060 }
1061
1062
1063 if (online_count > member_count / 2) {
1064 return 0;
1065 }
1066
1067
1068
1069 if (lowest_priority == top_priority) {
1070 return 0;
1071 }
1072
1073 if (node->details->priority < top_priority) {
1074 return 0;
1075 }
1076
1077 return data_set->priority_fencing_delay;
1078 }
1079
1080 pe_action_t *
1081 pe_fence_op(pe_node_t * node, const char *op, bool optional, const char *reason,
1082 bool priority_delay, pe_working_set_t * data_set)
1083 {
1084 char *op_key = NULL;
1085 pe_action_t *stonith_op = NULL;
1086
1087 if(op == NULL) {
1088 op = data_set->stonith_action;
1089 }
1090
1091 op_key = crm_strdup_printf("%s-%s-%s", CRM_OP_FENCE, node->details->uname, op);
1092
1093 stonith_op = lookup_singleton(data_set, op_key);
1094 if(stonith_op == NULL) {
1095 stonith_op = custom_action(NULL, op_key, CRM_OP_FENCE, node, TRUE, TRUE, data_set);
1096
1097 add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET, node->details->uname);
1098 add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET_UUID, node->details->id);
1099 add_hash_param(stonith_op->meta, "stonith_action", op);
1100
1101 if (pcmk_is_set(data_set->flags, pe_flag_enable_unfencing)) {
1102
1103
1104 GString *digests_all = g_string_sized_new(1024);
1105 GString *digests_secure = g_string_sized_new(1024);
1106
1107 GList *matches = find_unfencing_devices(data_set->resources, NULL);
1108
1109 char *key = NULL;
1110 char *value = NULL;
1111
1112 for (GList *gIter = matches; gIter != NULL; gIter = gIter->next) {
1113 pe_resource_t *match = gIter->data;
1114 const char *agent = g_hash_table_lookup(match->meta,
1115 XML_ATTR_TYPE);
1116 op_digest_cache_t *data = NULL;
1117
1118 data = pe__compare_fencing_digest(match, agent, node, data_set);
1119 if(data->rc == RSC_DIGEST_ALL) {
1120 optional = FALSE;
1121 crm_notice("Unfencing Pacemaker Remote node %s "
1122 "because the definition of %s changed",
1123 pe__node_name(node), match->id);
1124 if (!pcmk__is_daemon && data_set->priv != NULL) {
1125 pcmk__output_t *out = data_set->priv;
1126 out->info(out,
1127 "notice: Unfencing Pacemaker Remote node %s "
1128 "because the definition of %s changed",
1129 pe__node_name(node), match->id);
1130 }
1131 }
1132
1133 pcmk__g_strcat(digests_all,
1134 match->id, ":", agent, ":",
1135 data->digest_all_calc, ",", NULL);
1136 pcmk__g_strcat(digests_secure,
1137 match->id, ":", agent, ":",
1138 data->digest_secure_calc, ",", NULL);
1139 }
1140 key = strdup(XML_OP_ATTR_DIGESTS_ALL);
1141 value = strdup((const char *) digests_all->str);
1142 CRM_ASSERT((key != NULL) && (value != NULL));
1143 g_hash_table_insert(stonith_op->meta, key, value);
1144 g_string_free(digests_all, TRUE);
1145
1146 key = strdup(XML_OP_ATTR_DIGESTS_SECURE);
1147 value = strdup((const char *) digests_secure->str);
1148 CRM_ASSERT((key != NULL) && (value != NULL));
1149 g_hash_table_insert(stonith_op->meta, key, value);
1150 g_string_free(digests_secure, TRUE);
1151 }
1152
1153 } else {
1154 free(op_key);
1155 }
1156
1157 if (data_set->priority_fencing_delay > 0
1158
1159
1160
1161 && (priority_delay
1162
1163
1164
1165
1166
1167 || g_hash_table_lookup(stonith_op->meta,
1168 XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY) != NULL)) {
1169
1170
1171
1172
1173
1174 char *delay_s = pcmk__itoa(node_priority_fencing_delay(node, data_set));
1175
1176 g_hash_table_insert(stonith_op->meta,
1177 strdup(XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY),
1178 delay_s);
1179 }
1180
1181 if(optional == FALSE && pe_can_fence(data_set, node)) {
1182 pe__clear_action_flags(stonith_op, pe_action_optional);
1183 pe_action_set_reason(stonith_op, reason, false);
1184
1185 } else if(reason && stonith_op->reason == NULL) {
1186 stonith_op->reason = strdup(reason);
1187 }
1188
1189 return stonith_op;
1190 }
1191
1192 void
1193 pe_free_action(pe_action_t * action)
1194 {
1195 if (action == NULL) {
1196 return;
1197 }
1198 g_list_free_full(action->actions_before, free);
1199 g_list_free_full(action->actions_after, free);
1200 if (action->extra) {
1201 g_hash_table_destroy(action->extra);
1202 }
1203 if (action->meta) {
1204 g_hash_table_destroy(action->meta);
1205 }
1206 free(action->cancel_task);
1207 free(action->reason);
1208 free(action->task);
1209 free(action->uuid);
1210 free(action->node);
1211 free(action);
1212 }
1213
1214 int
1215 pe_get_configured_timeout(pe_resource_t *rsc, const char *action, pe_working_set_t *data_set)
1216 {
1217 xmlNode *child = NULL;
1218 GHashTable *action_meta = NULL;
1219 const char *timeout_spec = NULL;
1220 int timeout_ms = 0;
1221
1222 pe_rule_eval_data_t rule_data = {
1223 .node_hash = NULL,
1224 .role = RSC_ROLE_UNKNOWN,
1225 .now = data_set->now,
1226 .match_data = NULL,
1227 .rsc_data = NULL,
1228 .op_data = NULL
1229 };
1230
1231 for (child = first_named_child(rsc->ops_xml, XML_ATTR_OP);
1232 child != NULL; child = crm_next_same_xml(child)) {
1233 if (pcmk__str_eq(action, crm_element_value(child, XML_NVPAIR_ATTR_NAME),
1234 pcmk__str_casei)) {
1235 timeout_spec = crm_element_value(child, XML_ATTR_TIMEOUT);
1236 break;
1237 }
1238 }
1239
1240 if (timeout_spec == NULL && data_set->op_defaults) {
1241 action_meta = pcmk__strkey_table(free, free);
1242 pe__unpack_dataset_nvpairs(data_set->op_defaults, XML_TAG_META_SETS,
1243 &rule_data, action_meta, NULL, FALSE, data_set);
1244 timeout_spec = g_hash_table_lookup(action_meta, XML_ATTR_TIMEOUT);
1245 }
1246
1247
1248
1249
1250 timeout_ms = crm_get_msec(timeout_spec);
1251 if (timeout_ms < 0) {
1252 timeout_ms = crm_get_msec(CRM_DEFAULT_OP_TIMEOUT_S);
1253 }
1254
1255 if (action_meta != NULL) {
1256 g_hash_table_destroy(action_meta);
1257 }
1258 return timeout_ms;
1259 }
1260
1261 enum action_tasks
1262 get_complex_task(pe_resource_t * rsc, const char *name, gboolean allow_non_atomic)
1263 {
1264 enum action_tasks task = text2task(name);
1265
1266 if (rsc == NULL) {
1267 return task;
1268
1269 } else if (allow_non_atomic == FALSE || rsc->variant == pe_native) {
1270 switch (task) {
1271 case stopped_rsc:
1272 case started_rsc:
1273 case action_demoted:
1274 case action_promoted:
1275 crm_trace("Folding %s back into its atomic counterpart for %s", name, rsc->id);
1276 return task - 1;
1277 default:
1278 break;
1279 }
1280 }
1281 return task;
1282 }
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295 pe_action_t *
1296 find_first_action(const GList *input, const char *uuid, const char *task,
1297 const pe_node_t *on_node)
1298 {
1299 CRM_CHECK(uuid || task, return NULL);
1300
1301 for (const GList *gIter = input; gIter != NULL; gIter = gIter->next) {
1302 pe_action_t *action = (pe_action_t *) gIter->data;
1303
1304 if (uuid != NULL && !pcmk__str_eq(uuid, action->uuid, pcmk__str_casei)) {
1305 continue;
1306
1307 } else if (task != NULL && !pcmk__str_eq(task, action->task, pcmk__str_casei)) {
1308 continue;
1309
1310 } else if (on_node == NULL) {
1311 return action;
1312
1313 } else if (action->node == NULL) {
1314 continue;
1315
1316 } else if (on_node->details == action->node->details) {
1317 return action;
1318 }
1319 }
1320
1321 return NULL;
1322 }
1323
1324 GList *
1325 find_actions(GList *input, const char *key, const pe_node_t *on_node)
1326 {
1327 GList *gIter = input;
1328 GList *result = NULL;
1329
1330 CRM_CHECK(key != NULL, return NULL);
1331
1332 for (; gIter != NULL; gIter = gIter->next) {
1333 pe_action_t *action = (pe_action_t *) gIter->data;
1334
1335 if (!pcmk__str_eq(key, action->uuid, pcmk__str_casei)) {
1336 continue;
1337
1338 } else if (on_node == NULL) {
1339 crm_trace("Action %s matches (ignoring node)", key);
1340 result = g_list_prepend(result, action);
1341
1342 } else if (action->node == NULL) {
1343 crm_trace("Action %s matches (unallocated, assigning to %s)",
1344 key, pe__node_name(on_node));
1345
1346 action->node = pe__copy_node(on_node);
1347 result = g_list_prepend(result, action);
1348
1349 } else if (on_node->details == action->node->details) {
1350 crm_trace("Action %s on %s matches", key, pe__node_name(on_node));
1351 result = g_list_prepend(result, action);
1352 }
1353 }
1354
1355 return result;
1356 }
1357
1358 GList *
1359 find_actions_exact(GList *input, const char *key, const pe_node_t *on_node)
1360 {
1361 GList *result = NULL;
1362
1363 CRM_CHECK(key != NULL, return NULL);
1364
1365 if (on_node == NULL) {
1366 return NULL;
1367 }
1368
1369 for (GList *gIter = input; gIter != NULL; gIter = gIter->next) {
1370 pe_action_t *action = (pe_action_t *) gIter->data;
1371
1372 if ((action->node != NULL)
1373 && pcmk__str_eq(key, action->uuid, pcmk__str_casei)
1374 && pcmk__str_eq(on_node->details->id, action->node->details->id,
1375 pcmk__str_casei)) {
1376
1377 crm_trace("Action %s on %s matches", key, pe__node_name(on_node));
1378 result = g_list_prepend(result, action);
1379 }
1380 }
1381
1382 return result;
1383 }
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397 GList *
1398 pe__resource_actions(const pe_resource_t *rsc, const pe_node_t *node,
1399 const char *task, bool require_node)
1400 {
1401 GList *result = NULL;
1402 char *key = pcmk__op_key(rsc->id, task, 0);
1403
1404 if (require_node) {
1405 result = find_actions_exact(rsc->actions, key, node);
1406 } else {
1407 result = find_actions(rsc->actions, key, node);
1408 }
1409 free(key);
1410 return result;
1411 }
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423 char *
1424 pe__action2reason(pe_action_t *action, enum pe_action_flags flag)
1425 {
1426 const char *change = NULL;
1427
1428 switch (flag) {
1429 case pe_action_runnable:
1430 case pe_action_migrate_runnable:
1431 change = "unrunnable";
1432 break;
1433 case pe_action_optional:
1434 change = "required";
1435 break;
1436 default:
1437
1438 CRM_CHECK(change != NULL, change = "");
1439 break;
1440 }
1441 return crm_strdup_printf("%s%s%s %s", change,
1442 (action->rsc == NULL)? "" : " ",
1443 (action->rsc == NULL)? "" : action->rsc->id,
1444 action->task);
1445 }
1446
1447 void pe_action_set_reason(pe_action_t *action, const char *reason, bool overwrite)
1448 {
1449 if (action->reason != NULL && overwrite) {
1450 pe_rsc_trace(action->rsc, "Changing %s reason from '%s' to '%s'",
1451 action->uuid, action->reason, pcmk__s(reason, "(none)"));
1452 } else if (action->reason == NULL) {
1453 pe_rsc_trace(action->rsc, "Set %s reason to '%s'",
1454 action->uuid, pcmk__s(reason, "(none)"));
1455 } else {
1456
1457 return;
1458 }
1459
1460 pcmk__str_update(&action->reason, reason);
1461 }
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472 pe_action_t *
1473 pe__clear_resource_history(pe_resource_t *rsc, pe_node_t *node,
1474 pe_working_set_t *data_set)
1475 {
1476 char *key = NULL;
1477
1478 CRM_ASSERT(rsc && node);
1479 key = pcmk__op_key(rsc->id, CRM_OP_LRM_DELETE, 0);
1480 return custom_action(rsc, key, CRM_OP_LRM_DELETE, node, FALSE, TRUE,
1481 data_set);
1482 }
1483
1484 #define sort_return(an_int, why) do { \
1485 free(a_uuid); \
1486 free(b_uuid); \
1487 crm_trace("%s (%d) %c %s (%d) : %s", \
1488 a_xml_id, a_call_id, an_int>0?'>':an_int<0?'<':'=', \
1489 b_xml_id, b_call_id, why); \
1490 return an_int; \
1491 } while(0)
1492
1493 int
1494 pe__is_newer_op(const xmlNode *xml_a, const xmlNode *xml_b,
1495 bool same_node_default)
1496 {
1497 int a_call_id = -1;
1498 int b_call_id = -1;
1499
1500 char *a_uuid = NULL;
1501 char *b_uuid = NULL;
1502
1503 const char *a_xml_id = crm_element_value(xml_a, XML_ATTR_ID);
1504 const char *b_xml_id = crm_element_value(xml_b, XML_ATTR_ID);
1505
1506 const char *a_node = crm_element_value(xml_a, XML_LRM_ATTR_TARGET);
1507 const char *b_node = crm_element_value(xml_b, XML_LRM_ATTR_TARGET);
1508 bool same_node = true;
1509
1510
1511
1512
1513
1514
1515
1516
1517 if (a_node == NULL || b_node == NULL) {
1518 same_node = same_node_default;
1519
1520 } else {
1521 same_node = pcmk__str_eq(a_node, b_node, pcmk__str_casei);
1522 }
1523
1524 if (same_node && pcmk__str_eq(a_xml_id, b_xml_id, pcmk__str_none)) {
1525
1526
1527
1528
1529
1530 pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id);
1531 sort_return(0, "duplicate");
1532 }
1533
1534 crm_element_value_int(xml_a, XML_LRM_ATTR_CALLID, &a_call_id);
1535 crm_element_value_int(xml_b, XML_LRM_ATTR_CALLID, &b_call_id);
1536
1537 if (a_call_id == -1 && b_call_id == -1) {
1538
1539
1540
1541 sort_return(0, "pending");
1542
1543 } else if (same_node && a_call_id >= 0 && a_call_id < b_call_id) {
1544 sort_return(-1, "call id");
1545
1546 } else if (same_node && b_call_id >= 0 && a_call_id > b_call_id) {
1547 sort_return(1, "call id");
1548
1549 } else if (a_call_id >= 0 && b_call_id >= 0
1550 && (!same_node || a_call_id == b_call_id)) {
1551
1552
1553
1554
1555 time_t last_a = -1;
1556 time_t last_b = -1;
1557
1558 crm_element_value_epoch(xml_a, XML_RSC_OP_LAST_CHANGE, &last_a);
1559 crm_element_value_epoch(xml_b, XML_RSC_OP_LAST_CHANGE, &last_b);
1560
1561 crm_trace("rc-change: %lld vs %lld",
1562 (long long) last_a, (long long) last_b);
1563 if (last_a >= 0 && last_a < last_b) {
1564 sort_return(-1, "rc-change");
1565
1566 } else if (last_b >= 0 && last_a > last_b) {
1567 sort_return(1, "rc-change");
1568 }
1569 sort_return(0, "rc-change");
1570
1571 } else {
1572
1573
1574
1575
1576 int a_id = -1;
1577 int b_id = -1;
1578
1579 const char *a_magic = crm_element_value(xml_a, XML_ATTR_TRANSITION_MAGIC);
1580 const char *b_magic = crm_element_value(xml_b, XML_ATTR_TRANSITION_MAGIC);
1581
1582 CRM_CHECK(a_magic != NULL && b_magic != NULL, sort_return(0, "No magic"));
1583 if (!decode_transition_magic(a_magic, &a_uuid, &a_id, NULL, NULL, NULL,
1584 NULL)) {
1585 sort_return(0, "bad magic a");
1586 }
1587 if (!decode_transition_magic(b_magic, &b_uuid, &b_id, NULL, NULL, NULL,
1588 NULL)) {
1589 sort_return(0, "bad magic b");
1590 }
1591
1592
1593
1594
1595
1596
1597 if (!pcmk__str_eq(a_uuid, b_uuid, pcmk__str_casei) || a_id == b_id) {
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607 if (b_call_id == -1) {
1608 sort_return(-1, "transition + call");
1609
1610 } else if (a_call_id == -1) {
1611 sort_return(1, "transition + call");
1612 }
1613
1614 } else if ((a_id >= 0 && a_id < b_id) || b_id == -1) {
1615 sort_return(-1, "transition");
1616
1617 } else if ((b_id >= 0 && a_id > b_id) || a_id == -1) {
1618 sort_return(1, "transition");
1619 }
1620 }
1621
1622
1623 CRM_CHECK(FALSE, sort_return(0, "default"));
1624 }
1625
1626 gint
1627 sort_op_by_callid(gconstpointer a, gconstpointer b)
1628 {
1629 const xmlNode *xml_a = a;
1630 const xmlNode *xml_b = b;
1631
1632 return pe__is_newer_op(xml_a, xml_b, true);
1633 }
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646 pe_action_t *
1647 pe__new_rsc_pseudo_action(pe_resource_t *rsc, const char *task, bool optional,
1648 bool runnable)
1649 {
1650 pe_action_t *action = NULL;
1651
1652 CRM_ASSERT((rsc != NULL) && (task != NULL));
1653
1654 action = custom_action(rsc, pcmk__op_key(rsc->id, task, 0), task, NULL,
1655 optional, TRUE, rsc->cluster);
1656 pe__set_action_flags(action, pe_action_pseudo);
1657 if (runnable) {
1658 pe__set_action_flags(action, pe_action_runnable);
1659 }
1660 return action;
1661 }
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672 void
1673 pe__add_action_expected_result(pe_action_t *action, int expected_result)
1674 {
1675 char *name = NULL;
1676
1677 CRM_ASSERT((action != NULL) && (action->meta != NULL));
1678
1679 name = strdup(XML_ATTR_TE_TARGET_RC);
1680 CRM_ASSERT (name != NULL);
1681
1682 g_hash_table_insert(action->meta, name, pcmk__itoa(expected_result));
1683 }