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, const xmlNode *xml_obj,
21 const 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, const pe_resource_t *rsc,
55 const pe_node_t *node, const 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, const 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, const xmlNode *xml_obj,
521 guint interval_ms, const crm_time_t *now,
522 long long *start_delay)
523 {
524 long long result = 0;
525 guint interval_sec = interval_ms / 1000;
526 crm_time_t *origin = NULL;
527
528
529 if ((value == NULL) || (interval_ms == 0) || (now == NULL)) {
530 return false;
531 }
532
533
534 origin = crm_time_new(value);
535 if (origin == NULL) {
536 pcmk__config_err("Ignoring '" XML_OP_ATTR_ORIGIN "' for operation "
537 "'%s' because '%s' is not valid",
538 (ID(xml_obj)? ID(xml_obj) : "(missing ID)"), value);
539 return false;
540 }
541
542
543 result = crm_time_get_seconds(now) - crm_time_get_seconds(origin);
544 crm_time_free(origin);
545
546
547 result = result % interval_sec;
548
549
550 result = ((result <= 0)? 0 : interval_sec) - result;
551 crm_info("Calculated a start delay of %llds for operation '%s'",
552 result,
553 (ID(xml_obj)? ID(xml_obj) : "(unspecified)"));
554
555 if (start_delay != NULL) {
556 *start_delay = result * 1000;
557 }
558 return true;
559 }
560
561 static int
562 unpack_start_delay(const char *value, GHashTable *meta)
563 {
564 int start_delay = 0;
565
566 if (value != NULL) {
567 start_delay = crm_get_msec(value);
568
569 if (start_delay < 0) {
570 start_delay = 0;
571 }
572
573 if (meta) {
574 g_hash_table_replace(meta, strdup(XML_OP_ATTR_START_DELAY),
575 pcmk__itoa(start_delay));
576 }
577 }
578
579 return start_delay;
580 }
581
582 static xmlNode *
583 find_min_interval_mon(pe_resource_t * rsc, gboolean include_disabled)
584 {
585 guint interval_ms = 0;
586 guint min_interval_ms = G_MAXUINT;
587 const char *name = NULL;
588 const char *interval_spec = NULL;
589 xmlNode *op = NULL;
590 xmlNode *operation = NULL;
591
592 for (operation = pcmk__xe_first_child(rsc->ops_xml);
593 operation != NULL;
594 operation = pcmk__xe_next(operation)) {
595
596 if (pcmk__str_eq((const char *)operation->name, "op", pcmk__str_none)) {
597 bool enabled = false;
598
599 name = crm_element_value(operation, "name");
600 interval_spec = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
601 if (!include_disabled && pcmk__xe_get_bool_attr(operation, "enabled", &enabled) == pcmk_rc_ok &&
602 !enabled) {
603 continue;
604 }
605
606 if (!pcmk__str_eq(name, RSC_STATUS, pcmk__str_casei)) {
607 continue;
608 }
609
610 interval_ms = crm_parse_interval_spec(interval_spec);
611
612 if (interval_ms && (interval_ms < min_interval_ms)) {
613 min_interval_ms = interval_ms;
614 op = operation;
615 }
616 }
617 }
618
619 return op;
620 }
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635 static void
636 unpack_operation(pe_action_t *action, const xmlNode *xml_obj,
637 const pe_resource_t *container,
638 pe_working_set_t *data_set, guint interval_ms)
639 {
640 int timeout_ms = 0;
641 const char *value = NULL;
642 bool is_probe = false;
643
644 pe_rsc_eval_data_t rsc_rule_data = {
645 .standard = crm_element_value(action->rsc->xml, XML_AGENT_ATTR_CLASS),
646 .provider = crm_element_value(action->rsc->xml, XML_AGENT_ATTR_PROVIDER),
647 .agent = crm_element_value(action->rsc->xml, XML_EXPR_ATTR_TYPE)
648 };
649
650 pe_op_eval_data_t op_rule_data = {
651 .op_name = action->task,
652 .interval = interval_ms
653 };
654
655 pe_rule_eval_data_t rule_data = {
656 .node_hash = NULL,
657 .role = RSC_ROLE_UNKNOWN,
658 .now = data_set->now,
659 .match_data = NULL,
660 .rsc_data = &rsc_rule_data,
661 .op_data = &op_rule_data
662 };
663
664 CRM_CHECK(action && action->rsc, return);
665
666 is_probe = pcmk_is_probe(action->task, interval_ms);
667
668
669 pe__unpack_dataset_nvpairs(data_set->op_defaults, XML_TAG_META_SETS, &rule_data,
670 action->meta, NULL, FALSE, data_set);
671
672
673 if (is_probe) {
674 xmlNode *min_interval_mon = find_min_interval_mon(action->rsc, FALSE);
675
676 if (min_interval_mon) {
677 value = crm_element_value(min_interval_mon, XML_ATTR_TIMEOUT);
678 if (value) {
679 crm_trace("\t%s: Setting default timeout to minimum-interval "
680 "monitor's timeout '%s'", action->uuid, value);
681 g_hash_table_replace(action->meta, strdup(XML_ATTR_TIMEOUT),
682 strdup(value));
683 }
684 }
685 }
686
687 if (xml_obj) {
688 xmlAttrPtr xIter = NULL;
689
690
691 pe__unpack_dataset_nvpairs(xml_obj, XML_TAG_META_SETS, &rule_data,
692 action->meta, NULL, TRUE, data_set);
693
694
695
696
697 for (xIter = xml_obj->properties; xIter; xIter = xIter->next) {
698 const char *prop_name = (const char *)xIter->name;
699 const char *prop_value = crm_element_value(xml_obj, prop_name);
700
701 g_hash_table_replace(action->meta, strdup(prop_name), strdup(prop_value));
702 }
703 }
704
705 g_hash_table_remove(action->meta, "id");
706
707
708 if (interval_ms > 0) {
709 g_hash_table_replace(action->meta, strdup(XML_LRM_ATTR_INTERVAL),
710 crm_strdup_printf("%u", interval_ms));
711 } else {
712 g_hash_table_remove(action->meta, XML_LRM_ATTR_INTERVAL);
713 }
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729 if (pcmk_is_set(pcmk_get_ra_caps(rsc_rule_data.standard),
730 pcmk_ra_cap_fence_params)
731 && (pcmk__str_eq(action->task, RSC_START, pcmk__str_casei)
732 || is_probe)) {
733
734 GHashTable *params = pe_rsc_params(action->rsc, action->node, data_set);
735
736 value = g_hash_table_lookup(params, "pcmk_monitor_timeout");
737
738 if (value) {
739 crm_trace("\t%s: Setting timeout to pcmk_monitor_timeout '%s', "
740 "overriding default", action->uuid, value);
741 g_hash_table_replace(action->meta, strdup(XML_ATTR_TIMEOUT),
742 strdup(value));
743 }
744 }
745
746
747 value = g_hash_table_lookup(action->meta, XML_ATTR_TIMEOUT);
748 timeout_ms = unpack_timeout(value);
749 g_hash_table_replace(action->meta, strdup(XML_ATTR_TIMEOUT),
750 pcmk__itoa(timeout_ms));
751
752 if (!pcmk__strcase_any_of(action->task, RSC_START, RSC_PROMOTE, NULL)) {
753 action->needs = rsc_req_nothing;
754 value = "nothing (not start or promote)";
755
756 } else if (pcmk_is_set(action->rsc->flags, pe_rsc_needs_fencing)) {
757 action->needs = rsc_req_stonith;
758 value = "fencing";
759
760 } else if (pcmk_is_set(action->rsc->flags, pe_rsc_needs_quorum)) {
761 action->needs = rsc_req_quorum;
762 value = "quorum";
763
764 } else {
765 action->needs = rsc_req_nothing;
766 value = "nothing";
767 }
768 pe_rsc_trace(action->rsc, "%s requires %s", action->uuid, value);
769
770 value = unpack_operation_on_fail(action);
771
772 if (value == NULL) {
773
774 } else if (pcmk__str_eq(value, "block", pcmk__str_casei)) {
775 action->on_fail = action_fail_block;
776 g_hash_table_insert(action->meta, strdup(XML_OP_ATTR_ON_FAIL), strdup("block"));
777 value = "block";
778
779 } else if (pcmk__str_eq(value, "fence", pcmk__str_casei)) {
780 action->on_fail = action_fail_fence;
781 value = "node fencing";
782
783 if (!pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
784 pcmk__config_err("Resetting '" XML_OP_ATTR_ON_FAIL "' for "
785 "operation '%s' to 'stop' because 'fence' is not "
786 "valid when fencing is disabled", action->uuid);
787 action->on_fail = action_fail_stop;
788 action->fail_role = RSC_ROLE_STOPPED;
789 value = "stop resource";
790 }
791
792 } else if (pcmk__str_eq(value, "standby", pcmk__str_casei)) {
793 action->on_fail = action_fail_standby;
794 value = "node standby";
795
796 } else if (pcmk__strcase_any_of(value, "ignore", PCMK__VALUE_NOTHING,
797 NULL)) {
798 action->on_fail = action_fail_ignore;
799 value = "ignore";
800
801 } else if (pcmk__str_eq(value, "migrate", pcmk__str_casei)) {
802 action->on_fail = action_fail_migrate;
803 value = "force migration";
804
805 } else if (pcmk__str_eq(value, "stop", pcmk__str_casei)) {
806 action->on_fail = action_fail_stop;
807 action->fail_role = RSC_ROLE_STOPPED;
808 value = "stop resource";
809
810 } else if (pcmk__str_eq(value, "restart", pcmk__str_casei)) {
811 action->on_fail = action_fail_recover;
812 value = "restart (and possibly migrate)";
813
814 } else if (pcmk__str_eq(value, "restart-container", pcmk__str_casei)) {
815 if (container) {
816 action->on_fail = action_fail_restart_container;
817 value = "restart container (and possibly migrate)";
818
819 } else {
820 value = NULL;
821 }
822
823 } else if (pcmk__str_eq(value, "demote", pcmk__str_casei)) {
824 action->on_fail = action_fail_demote;
825 value = "demote instance";
826
827 } else {
828 pe_err("Resource %s: Unknown failure type (%s)", action->rsc->id, value);
829 value = NULL;
830 }
831
832
833 if (value == NULL && container) {
834 action->on_fail = action_fail_restart_container;
835 value = "restart container (and possibly migrate) (default)";
836
837
838
839
840
841
842
843
844
845 } else if (((value == NULL) || !pcmk_is_set(action->rsc->flags, pe_rsc_managed))
846 && pe__resource_is_remote_conn(action->rsc, data_set)
847 && !(pcmk__str_eq(action->task, CRMD_ACTION_STATUS, pcmk__str_casei)
848 && (interval_ms == 0))
849 && !pcmk__str_eq(action->task, CRMD_ACTION_START, pcmk__str_casei)) {
850
851 if (!pcmk_is_set(action->rsc->flags, pe_rsc_managed)) {
852 action->on_fail = action_fail_stop;
853 action->fail_role = RSC_ROLE_STOPPED;
854 value = "stop unmanaged remote node (enforcing default)";
855
856 } else {
857 if (pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
858 value = "fence remote node (default)";
859 } else {
860 value = "recover remote node connection (default)";
861 }
862
863 if (action->rsc->remote_reconnect_ms) {
864 action->fail_role = RSC_ROLE_STOPPED;
865 }
866 action->on_fail = action_fail_reset_remote;
867 }
868
869 } else if (value == NULL && pcmk__str_eq(action->task, CRMD_ACTION_STOP, pcmk__str_casei)) {
870 if (pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)) {
871 action->on_fail = action_fail_fence;
872 value = "resource fence (default)";
873
874 } else {
875 action->on_fail = action_fail_block;
876 value = "resource block (default)";
877 }
878
879 } else if (value == NULL) {
880 action->on_fail = action_fail_recover;
881 value = "restart (and possibly migrate) (default)";
882 }
883
884 pe_rsc_trace(action->rsc, "%s failure handling: %s",
885 action->uuid, value);
886
887 value = NULL;
888 if (xml_obj != NULL) {
889 value = g_hash_table_lookup(action->meta, "role_after_failure");
890 if (value) {
891 pe_warn_once(pe_wo_role_after,
892 "Support for role_after_failure is deprecated and will be removed in a future release");
893 }
894 }
895 if (value != NULL && action->fail_role == RSC_ROLE_UNKNOWN) {
896 action->fail_role = text2role(value);
897 }
898
899 if (action->fail_role == RSC_ROLE_UNKNOWN) {
900 if (pcmk__str_eq(action->task, CRMD_ACTION_PROMOTE, pcmk__str_casei)) {
901 action->fail_role = RSC_ROLE_UNPROMOTED;
902 } else {
903 action->fail_role = RSC_ROLE_STARTED;
904 }
905 }
906 pe_rsc_trace(action->rsc, "%s failure results in: %s",
907 action->uuid, role2text(action->fail_role));
908
909 value = g_hash_table_lookup(action->meta, XML_OP_ATTR_START_DELAY);
910 if (value) {
911 unpack_start_delay(value, action->meta);
912 } else {
913 long long start_delay = 0;
914
915 value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN);
916 if (unpack_interval_origin(value, xml_obj, interval_ms, data_set->now,
917 &start_delay)) {
918 g_hash_table_replace(action->meta, strdup(XML_OP_ATTR_START_DELAY),
919 crm_strdup_printf("%lld", start_delay));
920 }
921 }
922 }
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941 pe_action_t *
942 custom_action(pe_resource_t *rsc, char *key, const char *task,
943 const pe_node_t *on_node, gboolean optional, gboolean save_action,
944 pe_working_set_t *data_set)
945 {
946 pe_action_t *action = NULL;
947
948 CRM_ASSERT((key != NULL) && (task != NULL) && (data_set != NULL));
949
950 if (save_action) {
951 action = find_existing_action(key, rsc, on_node, data_set);
952 }
953
954 if (action == NULL) {
955 action = new_action(key, task, rsc, on_node, optional, save_action,
956 data_set);
957 } else {
958 free(key);
959 }
960
961 update_action_optional(action, optional);
962
963 if (rsc != NULL) {
964 if (action->node != NULL) {
965 unpack_action_node_attributes(action, data_set);
966 }
967
968 update_resource_action_runnable(action, save_action, data_set);
969
970 if (save_action) {
971 update_resource_flags_for_action(rsc, action);
972 }
973 }
974
975 return action;
976 }
977
978 pe_action_t *
979 get_pseudo_op(const char *name, pe_working_set_t * data_set)
980 {
981 pe_action_t *op = lookup_singleton(data_set, name);
982
983 if (op == NULL) {
984 op = custom_action(NULL, strdup(name), name, NULL, TRUE, TRUE, data_set);
985 pe__set_action_flags(op, pe_action_pseudo|pe_action_runnable);
986 }
987 return op;
988 }
989
990 static GList *
991 find_unfencing_devices(GList *candidates, GList *matches)
992 {
993 for (GList *gIter = candidates; gIter != NULL; gIter = gIter->next) {
994 pe_resource_t *candidate = gIter->data;
995
996 if (candidate->children != NULL) {
997 matches = find_unfencing_devices(candidate->children, matches);
998
999 } else if (!pcmk_is_set(candidate->flags, pe_rsc_fence_device)) {
1000 continue;
1001
1002 } else if (pcmk_is_set(candidate->flags, pe_rsc_needs_unfencing)) {
1003 matches = g_list_prepend(matches, candidate);
1004
1005 } else if (pcmk__str_eq(g_hash_table_lookup(candidate->meta,
1006 PCMK_STONITH_PROVIDES),
1007 PCMK__VALUE_UNFENCING,
1008 pcmk__str_casei)) {
1009 matches = g_list_prepend(matches, candidate);
1010 }
1011 }
1012 return matches;
1013 }
1014
1015 static int
1016 node_priority_fencing_delay(const pe_node_t *node,
1017 const pe_working_set_t *data_set)
1018 {
1019 int member_count = 0;
1020 int online_count = 0;
1021 int top_priority = 0;
1022 int lowest_priority = 0;
1023 GList *gIter = NULL;
1024
1025
1026 if (data_set->priority_fencing_delay <= 0) {
1027 return 0;
1028 }
1029
1030
1031
1032 if (node->details->type != node_member) {
1033 return 0;
1034 }
1035
1036
1037 if (node->details->online) {
1038 return 0;
1039 }
1040
1041 for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
1042 pe_node_t *n = gIter->data;
1043
1044 if (n->details->type != node_member) {
1045 continue;
1046 }
1047
1048 member_count ++;
1049
1050 if (n->details->online) {
1051 online_count++;
1052 }
1053
1054 if (member_count == 1
1055 || n->details->priority > top_priority) {
1056 top_priority = n->details->priority;
1057 }
1058
1059 if (member_count == 1
1060 || n->details->priority < lowest_priority) {
1061 lowest_priority = n->details->priority;
1062 }
1063 }
1064
1065
1066 if (online_count > member_count / 2) {
1067 return 0;
1068 }
1069
1070
1071
1072 if (lowest_priority == top_priority) {
1073 return 0;
1074 }
1075
1076 if (node->details->priority < top_priority) {
1077 return 0;
1078 }
1079
1080 return data_set->priority_fencing_delay;
1081 }
1082
1083 pe_action_t *
1084 pe_fence_op(pe_node_t *node, const char *op, bool optional,
1085 const char *reason, bool priority_delay, pe_working_set_t *data_set)
1086 {
1087 char *op_key = NULL;
1088 pe_action_t *stonith_op = NULL;
1089
1090 if(op == NULL) {
1091 op = data_set->stonith_action;
1092 }
1093
1094 op_key = crm_strdup_printf("%s-%s-%s", CRM_OP_FENCE, node->details->uname, op);
1095
1096 stonith_op = lookup_singleton(data_set, op_key);
1097 if(stonith_op == NULL) {
1098 stonith_op = custom_action(NULL, op_key, CRM_OP_FENCE, node, TRUE, TRUE, data_set);
1099
1100 add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET, node->details->uname);
1101 add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET_UUID, node->details->id);
1102 add_hash_param(stonith_op->meta, "stonith_action", op);
1103
1104 if (pcmk_is_set(data_set->flags, pe_flag_enable_unfencing)) {
1105
1106
1107 GString *digests_all = g_string_sized_new(1024);
1108 GString *digests_secure = g_string_sized_new(1024);
1109
1110 GList *matches = find_unfencing_devices(data_set->resources, NULL);
1111
1112 char *key = NULL;
1113 char *value = NULL;
1114
1115 for (GList *gIter = matches; gIter != NULL; gIter = gIter->next) {
1116 pe_resource_t *match = gIter->data;
1117 const char *agent = g_hash_table_lookup(match->meta,
1118 XML_ATTR_TYPE);
1119 op_digest_cache_t *data = NULL;
1120
1121 data = pe__compare_fencing_digest(match, agent, node, data_set);
1122 if(data->rc == RSC_DIGEST_ALL) {
1123 optional = FALSE;
1124 crm_notice("Unfencing node %s because the definition of "
1125 "%s changed", pe__node_name(node), match->id);
1126 if (!pcmk__is_daemon && data_set->priv != NULL) {
1127 pcmk__output_t *out = data_set->priv;
1128
1129 out->info(out,
1130 "notice: Unfencing node %s because the "
1131 "definition of %s changed",
1132 pe__node_name(node), match->id);
1133 }
1134 }
1135
1136 pcmk__g_strcat(digests_all,
1137 match->id, ":", agent, ":",
1138 data->digest_all_calc, ",", NULL);
1139 pcmk__g_strcat(digests_secure,
1140 match->id, ":", agent, ":",
1141 data->digest_secure_calc, ",", NULL);
1142 }
1143 key = strdup(XML_OP_ATTR_DIGESTS_ALL);
1144 value = strdup((const char *) digests_all->str);
1145 CRM_ASSERT((key != NULL) && (value != NULL));
1146 g_hash_table_insert(stonith_op->meta, key, value);
1147 g_string_free(digests_all, TRUE);
1148
1149 key = strdup(XML_OP_ATTR_DIGESTS_SECURE);
1150 value = strdup((const char *) digests_secure->str);
1151 CRM_ASSERT((key != NULL) && (value != NULL));
1152 g_hash_table_insert(stonith_op->meta, key, value);
1153 g_string_free(digests_secure, TRUE);
1154 }
1155
1156 } else {
1157 free(op_key);
1158 }
1159
1160 if (data_set->priority_fencing_delay > 0
1161
1162
1163
1164 && (priority_delay
1165
1166
1167
1168
1169
1170 || g_hash_table_lookup(stonith_op->meta,
1171 XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY) != NULL)) {
1172
1173
1174
1175
1176
1177 char *delay_s = pcmk__itoa(node_priority_fencing_delay(node, data_set));
1178
1179 g_hash_table_insert(stonith_op->meta,
1180 strdup(XML_CONFIG_ATTR_PRIORITY_FENCING_DELAY),
1181 delay_s);
1182 }
1183
1184 if(optional == FALSE && pe_can_fence(data_set, node)) {
1185 pe__clear_action_flags(stonith_op, pe_action_optional);
1186 pe_action_set_reason(stonith_op, reason, false);
1187
1188 } else if(reason && stonith_op->reason == NULL) {
1189 stonith_op->reason = strdup(reason);
1190 }
1191
1192 return stonith_op;
1193 }
1194
1195 void
1196 pe_free_action(pe_action_t * action)
1197 {
1198 if (action == NULL) {
1199 return;
1200 }
1201 g_list_free_full(action->actions_before, free);
1202 g_list_free_full(action->actions_after, free);
1203 if (action->extra) {
1204 g_hash_table_destroy(action->extra);
1205 }
1206 if (action->meta) {
1207 g_hash_table_destroy(action->meta);
1208 }
1209 free(action->cancel_task);
1210 free(action->reason);
1211 free(action->task);
1212 free(action->uuid);
1213 free(action->node);
1214 free(action);
1215 }
1216
1217 int
1218 pe_get_configured_timeout(pe_resource_t *rsc, const char *action, pe_working_set_t *data_set)
1219 {
1220 xmlNode *child = NULL;
1221 GHashTable *action_meta = NULL;
1222 const char *timeout_spec = NULL;
1223 int timeout_ms = 0;
1224
1225 pe_rule_eval_data_t rule_data = {
1226 .node_hash = NULL,
1227 .role = RSC_ROLE_UNKNOWN,
1228 .now = data_set->now,
1229 .match_data = NULL,
1230 .rsc_data = NULL,
1231 .op_data = NULL
1232 };
1233
1234 for (child = first_named_child(rsc->ops_xml, XML_ATTR_OP);
1235 child != NULL; child = crm_next_same_xml(child)) {
1236 if (pcmk__str_eq(action, crm_element_value(child, XML_NVPAIR_ATTR_NAME),
1237 pcmk__str_casei)) {
1238 timeout_spec = crm_element_value(child, XML_ATTR_TIMEOUT);
1239 break;
1240 }
1241 }
1242
1243 if (timeout_spec == NULL && data_set->op_defaults) {
1244 action_meta = pcmk__strkey_table(free, free);
1245 pe__unpack_dataset_nvpairs(data_set->op_defaults, XML_TAG_META_SETS,
1246 &rule_data, action_meta, NULL, FALSE, data_set);
1247 timeout_spec = g_hash_table_lookup(action_meta, XML_ATTR_TIMEOUT);
1248 }
1249
1250
1251
1252
1253 timeout_ms = crm_get_msec(timeout_spec);
1254 if (timeout_ms < 0) {
1255 timeout_ms = crm_get_msec(CRM_DEFAULT_OP_TIMEOUT_S);
1256 }
1257
1258 if (action_meta != NULL) {
1259 g_hash_table_destroy(action_meta);
1260 }
1261 return timeout_ms;
1262 }
1263
1264 enum action_tasks
1265 get_complex_task(const pe_resource_t *rsc, const char *name)
1266 {
1267 enum action_tasks task = text2task(name);
1268
1269 if ((rsc != NULL) && (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",
1276 name, rsc->id);
1277 --task;
1278 break;
1279 default:
1280 break;
1281 }
1282 }
1283 return task;
1284 }
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297 pe_action_t *
1298 find_first_action(const GList *input, const char *uuid, const char *task,
1299 const pe_node_t *on_node)
1300 {
1301 CRM_CHECK(uuid || task, return NULL);
1302
1303 for (const GList *gIter = input; gIter != NULL; gIter = gIter->next) {
1304 pe_action_t *action = (pe_action_t *) gIter->data;
1305
1306 if (uuid != NULL && !pcmk__str_eq(uuid, action->uuid, pcmk__str_casei)) {
1307 continue;
1308
1309 } else if (task != NULL && !pcmk__str_eq(task, action->task, pcmk__str_casei)) {
1310 continue;
1311
1312 } else if (on_node == NULL) {
1313 return action;
1314
1315 } else if (action->node == NULL) {
1316 continue;
1317
1318 } else if (on_node->details == action->node->details) {
1319 return action;
1320 }
1321 }
1322
1323 return NULL;
1324 }
1325
1326 GList *
1327 find_actions(GList *input, const char *key, const pe_node_t *on_node)
1328 {
1329 GList *gIter = input;
1330 GList *result = NULL;
1331
1332 CRM_CHECK(key != NULL, return NULL);
1333
1334 for (; gIter != NULL; gIter = gIter->next) {
1335 pe_action_t *action = (pe_action_t *) gIter->data;
1336
1337 if (!pcmk__str_eq(key, action->uuid, pcmk__str_casei)) {
1338 continue;
1339
1340 } else if (on_node == NULL) {
1341 crm_trace("Action %s matches (ignoring node)", key);
1342 result = g_list_prepend(result, action);
1343
1344 } else if (action->node == NULL) {
1345 crm_trace("Action %s matches (unallocated, assigning to %s)",
1346 key, pe__node_name(on_node));
1347
1348 action->node = pe__copy_node(on_node);
1349 result = g_list_prepend(result, action);
1350
1351 } else if (on_node->details == action->node->details) {
1352 crm_trace("Action %s on %s matches", key, pe__node_name(on_node));
1353 result = g_list_prepend(result, action);
1354 }
1355 }
1356
1357 return result;
1358 }
1359
1360 GList *
1361 find_actions_exact(GList *input, const char *key, const pe_node_t *on_node)
1362 {
1363 GList *result = NULL;
1364
1365 CRM_CHECK(key != NULL, return NULL);
1366
1367 if (on_node == NULL) {
1368 return NULL;
1369 }
1370
1371 for (GList *gIter = input; gIter != NULL; gIter = gIter->next) {
1372 pe_action_t *action = (pe_action_t *) gIter->data;
1373
1374 if ((action->node != NULL)
1375 && pcmk__str_eq(key, action->uuid, pcmk__str_casei)
1376 && pcmk__str_eq(on_node->details->id, action->node->details->id,
1377 pcmk__str_casei)) {
1378
1379 crm_trace("Action %s on %s matches", key, pe__node_name(on_node));
1380 result = g_list_prepend(result, action);
1381 }
1382 }
1383
1384 return result;
1385 }
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399 GList *
1400 pe__resource_actions(const pe_resource_t *rsc, const pe_node_t *node,
1401 const char *task, bool require_node)
1402 {
1403 GList *result = NULL;
1404 char *key = pcmk__op_key(rsc->id, task, 0);
1405
1406 if (require_node) {
1407 result = find_actions_exact(rsc->actions, key, node);
1408 } else {
1409 result = find_actions(rsc->actions, key, node);
1410 }
1411 free(key);
1412 return result;
1413 }
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425 char *
1426 pe__action2reason(const pe_action_t *action, enum pe_action_flags flag)
1427 {
1428 const char *change = NULL;
1429
1430 switch (flag) {
1431 case pe_action_runnable:
1432 case pe_action_migrate_runnable:
1433 change = "unrunnable";
1434 break;
1435 case pe_action_optional:
1436 change = "required";
1437 break;
1438 default:
1439
1440 CRM_CHECK(change != NULL, change = "");
1441 break;
1442 }
1443 return crm_strdup_printf("%s%s%s %s", change,
1444 (action->rsc == NULL)? "" : " ",
1445 (action->rsc == NULL)? "" : action->rsc->id,
1446 action->task);
1447 }
1448
1449 void pe_action_set_reason(pe_action_t *action, const char *reason, bool overwrite)
1450 {
1451 if (action->reason != NULL && overwrite) {
1452 pe_rsc_trace(action->rsc, "Changing %s reason from '%s' to '%s'",
1453 action->uuid, action->reason, pcmk__s(reason, "(none)"));
1454 } else if (action->reason == NULL) {
1455 pe_rsc_trace(action->rsc, "Set %s reason to '%s'",
1456 action->uuid, pcmk__s(reason, "(none)"));
1457 } else {
1458
1459 return;
1460 }
1461
1462 pcmk__str_update(&action->reason, reason);
1463 }
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475 pe_action_t *
1476 pe__clear_resource_history(pe_resource_t *rsc, const pe_node_t *node,
1477 pe_working_set_t *data_set)
1478 {
1479 char *key = NULL;
1480
1481 CRM_ASSERT(rsc && node);
1482 key = pcmk__op_key(rsc->id, CRM_OP_LRM_DELETE, 0);
1483 return custom_action(rsc, key, CRM_OP_LRM_DELETE, node, FALSE, TRUE,
1484 data_set);
1485 }
1486
1487 #define sort_return(an_int, why) do { \
1488 free(a_uuid); \
1489 free(b_uuid); \
1490 crm_trace("%s (%d) %c %s (%d) : %s", \
1491 a_xml_id, a_call_id, an_int>0?'>':an_int<0?'<':'=', \
1492 b_xml_id, b_call_id, why); \
1493 return an_int; \
1494 } while(0)
1495
1496 int
1497 pe__is_newer_op(const xmlNode *xml_a, const xmlNode *xml_b,
1498 bool same_node_default)
1499 {
1500 int a_call_id = -1;
1501 int b_call_id = -1;
1502
1503 char *a_uuid = NULL;
1504 char *b_uuid = NULL;
1505
1506 const char *a_xml_id = crm_element_value(xml_a, XML_ATTR_ID);
1507 const char *b_xml_id = crm_element_value(xml_b, XML_ATTR_ID);
1508
1509 const char *a_node = crm_element_value(xml_a, XML_LRM_ATTR_TARGET);
1510 const char *b_node = crm_element_value(xml_b, XML_LRM_ATTR_TARGET);
1511 bool same_node = true;
1512
1513
1514
1515
1516
1517
1518
1519
1520 if (a_node == NULL || b_node == NULL) {
1521 same_node = same_node_default;
1522
1523 } else {
1524 same_node = pcmk__str_eq(a_node, b_node, pcmk__str_casei);
1525 }
1526
1527 if (same_node && pcmk__str_eq(a_xml_id, b_xml_id, pcmk__str_none)) {
1528
1529
1530
1531
1532
1533 pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id);
1534 sort_return(0, "duplicate");
1535 }
1536
1537 crm_element_value_int(xml_a, XML_LRM_ATTR_CALLID, &a_call_id);
1538 crm_element_value_int(xml_b, XML_LRM_ATTR_CALLID, &b_call_id);
1539
1540 if (a_call_id == -1 && b_call_id == -1) {
1541
1542
1543
1544 sort_return(0, "pending");
1545
1546 } else if (same_node && a_call_id >= 0 && a_call_id < b_call_id) {
1547 sort_return(-1, "call id");
1548
1549 } else if (same_node && b_call_id >= 0 && a_call_id > b_call_id) {
1550 sort_return(1, "call id");
1551
1552 } else if (a_call_id >= 0 && b_call_id >= 0
1553 && (!same_node || a_call_id == b_call_id)) {
1554
1555
1556
1557
1558 time_t last_a = -1;
1559 time_t last_b = -1;
1560
1561 crm_element_value_epoch(xml_a, XML_RSC_OP_LAST_CHANGE, &last_a);
1562 crm_element_value_epoch(xml_b, XML_RSC_OP_LAST_CHANGE, &last_b);
1563
1564 crm_trace("rc-change: %lld vs %lld",
1565 (long long) last_a, (long long) last_b);
1566 if (last_a >= 0 && last_a < last_b) {
1567 sort_return(-1, "rc-change");
1568
1569 } else if (last_b >= 0 && last_a > last_b) {
1570 sort_return(1, "rc-change");
1571 }
1572 sort_return(0, "rc-change");
1573
1574 } else {
1575
1576
1577
1578
1579 int a_id = -1;
1580 int b_id = -1;
1581
1582 const char *a_magic = crm_element_value(xml_a, XML_ATTR_TRANSITION_MAGIC);
1583 const char *b_magic = crm_element_value(xml_b, XML_ATTR_TRANSITION_MAGIC);
1584
1585 CRM_CHECK(a_magic != NULL && b_magic != NULL, sort_return(0, "No magic"));
1586 if (!decode_transition_magic(a_magic, &a_uuid, &a_id, NULL, NULL, NULL,
1587 NULL)) {
1588 sort_return(0, "bad magic a");
1589 }
1590 if (!decode_transition_magic(b_magic, &b_uuid, &b_id, NULL, NULL, NULL,
1591 NULL)) {
1592 sort_return(0, "bad magic b");
1593 }
1594
1595
1596
1597
1598
1599
1600 if (!pcmk__str_eq(a_uuid, b_uuid, pcmk__str_casei) || a_id == b_id) {
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610 if (b_call_id == -1) {
1611 sort_return(-1, "transition + call");
1612
1613 } else if (a_call_id == -1) {
1614 sort_return(1, "transition + call");
1615 }
1616
1617 } else if ((a_id >= 0 && a_id < b_id) || b_id == -1) {
1618 sort_return(-1, "transition");
1619
1620 } else if ((b_id >= 0 && a_id > b_id) || a_id == -1) {
1621 sort_return(1, "transition");
1622 }
1623 }
1624
1625
1626 CRM_CHECK(FALSE, sort_return(0, "default"));
1627 }
1628
1629 gint
1630 sort_op_by_callid(gconstpointer a, gconstpointer b)
1631 {
1632 const xmlNode *xml_a = a;
1633 const xmlNode *xml_b = b;
1634
1635 return pe__is_newer_op(xml_a, xml_b, true);
1636 }
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649 pe_action_t *
1650 pe__new_rsc_pseudo_action(pe_resource_t *rsc, const char *task, bool optional,
1651 bool runnable)
1652 {
1653 pe_action_t *action = NULL;
1654
1655 CRM_ASSERT((rsc != NULL) && (task != NULL));
1656
1657 action = custom_action(rsc, pcmk__op_key(rsc->id, task, 0), task, NULL,
1658 optional, TRUE, rsc->cluster);
1659 pe__set_action_flags(action, pe_action_pseudo);
1660 if (runnable) {
1661 pe__set_action_flags(action, pe_action_runnable);
1662 }
1663 return action;
1664 }
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675 void
1676 pe__add_action_expected_result(pe_action_t *action, int expected_result)
1677 {
1678 char *name = NULL;
1679
1680 CRM_ASSERT((action != NULL) && (action->meta != NULL));
1681
1682 name = strdup(XML_ATTR_TE_TARGET_RC);
1683 CRM_ASSERT (name != NULL);
1684
1685 g_hash_table_insert(action->meta, name, pcmk__itoa(expected_result));
1686 }