This source file includes following definitions.
- add_node_to_xml_by_id
- add_node_to_xml
- add_maintenance_nodes
- add_maintenance_update
- add_downed_nodes
- clone_op_key
- add_node_details
- add_resource_details
- add_action_attributes
- create_graph_action
- should_add_action_to_graph
- ordering_can_change_actions
- should_add_input_to_graph
- pcmk__graph_has_loop
- create_graph_synapse
- add_action_to_graph
- pcmk__log_transition_summary
- pcmk__add_rsc_actions_to_graph
- pcmk__create_graph
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <sys/param.h>
13 #include <crm/crm.h>
14 #include <crm/cib.h>
15 #include <crm/common/xml.h>
16
17 #include <glib.h>
18
19 #include <pacemaker-internal.h>
20
21 #include "libpacemaker_private.h"
22
23
24
25 #define action_type_str(flags) \
26 (pcmk_is_set((flags), pcmk__action_pseudo)? "pseudo-action" : "action")
27
28 #define action_optional_str(flags) \
29 (pcmk_is_set((flags), pcmk__action_optional)? "optional" : "required")
30
31 #define action_runnable_str(flags) \
32 (pcmk_is_set((flags), pcmk__action_runnable)? "runnable" : "unrunnable")
33
34 #define action_node_str(a) \
35 (((a)->node == NULL)? "no node" : (a)->node->priv->name)
36
37
38
39
40
41
42
43
44 static xmlNode*
45 add_node_to_xml_by_id(const char *id, xmlNode *xml)
46 {
47 xmlNode *node_xml;
48
49 node_xml = pcmk__xe_create(xml, PCMK_XE_NODE);
50 crm_xml_add(node_xml, PCMK_XA_ID, id);
51
52 return node_xml;
53 }
54
55
56
57
58
59
60
61
62 static void
63 add_node_to_xml(const pcmk_node_t *node, void *xml)
64 {
65 add_node_to_xml_by_id(node->priv->id, (xmlNode *) xml);
66 }
67
68
69
70
71
72
73
74
75
76
77
78 static int
79 add_maintenance_nodes(xmlNode *xml, const pcmk_scheduler_t *scheduler)
80 {
81 xmlNode *maintenance = NULL;
82 int count = 0;
83
84 if (xml != NULL) {
85 maintenance = pcmk__xe_create(xml, PCMK__XE_MAINTENANCE);
86 }
87 for (const GList *iter = scheduler->nodes;
88 iter != NULL; iter = iter->next) {
89 const pcmk_node_t *node = iter->data;
90
91 if (!pcmk__is_pacemaker_remote_node(node)) {
92 continue;
93 }
94 if ((node->details->maintenance
95 && !pcmk_is_set(node->priv->flags, pcmk__node_remote_maint))
96 || (!node->details->maintenance
97 && pcmk_is_set(node->priv->flags, pcmk__node_remote_maint))) {
98
99 if (maintenance != NULL) {
100 crm_xml_add(add_node_to_xml_by_id(node->priv->id,
101 maintenance),
102 PCMK__XA_NODE_IN_MAINTENANCE,
103 (node->details->maintenance? "1" : "0"));
104 }
105 count++;
106 }
107 }
108 crm_trace("%s %d nodes in need of maintenance mode update in state",
109 ((maintenance == NULL)? "Counted" : "Added"), count);
110 return count;
111 }
112
113
114
115
116
117
118
119 static void
120 add_maintenance_update(pcmk_scheduler_t *scheduler)
121 {
122 pcmk_action_t *action = NULL;
123
124 if (add_maintenance_nodes(NULL, scheduler) != 0) {
125 action = get_pseudo_op(PCMK_ACTION_MAINTENANCE_NODES, scheduler);
126 pcmk__set_action_flags(action, pcmk__action_always_in_graph);
127 }
128 }
129
130
131
132
133
134
135
136
137
138
139
140
141 static void
142 add_downed_nodes(xmlNode *xml, const pcmk_action_t *action)
143 {
144 CRM_CHECK((xml != NULL) && (action != NULL) && (action->node != NULL),
145 return);
146
147 if (pcmk__str_eq(action->task, PCMK_ACTION_DO_SHUTDOWN, pcmk__str_none)) {
148
149
150 xmlNode *downed = pcmk__xe_create(xml, PCMK__XE_DOWNED);
151 add_node_to_xml_by_id(action->node->priv->id, downed);
152
153 } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH,
154 pcmk__str_none)) {
155
156
157 const char *fence = g_hash_table_lookup(action->meta,
158 PCMK__META_STONITH_ACTION);
159
160 if (pcmk__is_fencing_action(fence)) {
161 xmlNode *downed = pcmk__xe_create(xml, PCMK__XE_DOWNED);
162 add_node_to_xml_by_id(action->node->priv->id, downed);
163 pe_foreach_guest_node(action->node->priv->scheduler,
164 action->node, add_node_to_xml, downed);
165 }
166
167 } else if ((action->rsc != NULL)
168 && pcmk_is_set(action->rsc->flags,
169 pcmk__rsc_is_remote_connection)
170 && pcmk__str_eq(action->task, PCMK_ACTION_STOP,
171 pcmk__str_none)) {
172
173
174
175
176 GList *iter;
177 pcmk_action_t *input;
178 bool migrating = false;
179
180 for (iter = action->actions_before; iter != NULL; iter = iter->next) {
181 input = ((pcmk__related_action_t *) iter->data)->action;
182 if ((input->rsc != NULL)
183 && pcmk__str_eq(action->rsc->id, input->rsc->id, pcmk__str_none)
184 && pcmk__str_eq(input->task, PCMK_ACTION_MIGRATE_FROM,
185 pcmk__str_none)) {
186 migrating = true;
187 break;
188 }
189 }
190 if (!migrating) {
191 xmlNode *downed = pcmk__xe_create(xml, PCMK__XE_DOWNED);
192 add_node_to_xml_by_id(action->rsc->id, downed);
193 }
194 }
195 }
196
197
198
199
200
201
202
203
204
205
206 static char *
207 clone_op_key(const pcmk_action_t *action, guint interval_ms)
208 {
209 if (pcmk__str_eq(action->task, PCMK_ACTION_NOTIFY, pcmk__str_none)) {
210 const char *n_type = g_hash_table_lookup(action->meta, "notify_type");
211 const char *n_task = g_hash_table_lookup(action->meta,
212 "notify_operation");
213
214 return pcmk__notify_key(action->rsc->priv->history_id, n_type,
215 n_task);
216 }
217 return pcmk__op_key(action->rsc->priv->history_id,
218 pcmk__s(action->cancel_task, action->task),
219 interval_ms);
220 }
221
222
223
224
225
226
227
228
229 static void
230 add_node_details(const pcmk_action_t *action, xmlNode *xml)
231 {
232 pcmk_node_t *router_node = pcmk__connection_host_for_action(action);
233
234 crm_xml_add(xml, PCMK__META_ON_NODE, action->node->priv->name);
235 crm_xml_add(xml, PCMK__META_ON_NODE_UUID, action->node->priv->id);
236 if (router_node != NULL) {
237 crm_xml_add(xml, PCMK__XA_ROUTER_NODE, router_node->priv->name);
238 }
239 }
240
241
242
243
244
245
246
247
248 static void
249 add_resource_details(const pcmk_action_t *action, xmlNode *action_xml)
250 {
251 xmlNode *rsc_xml = NULL;
252 const char *attr_list[] = {
253 PCMK_XA_CLASS,
254 PCMK_XA_PROVIDER,
255 PCMK_XA_TYPE,
256 };
257
258
259
260
261
262 if (pcmk__action_locks_rsc_to_node(action)) {
263 crm_xml_add_ll(action_xml, PCMK_OPT_SHUTDOWN_LOCK,
264 (long long) action->rsc->priv->lock_time);
265 }
266
267
268
269 rsc_xml = pcmk__xe_create(action_xml,
270 (const char *) action->rsc->priv->xml->name);
271 if (pcmk_is_set(action->rsc->flags, pcmk__rsc_removed)
272 && (action->rsc->priv->history_id != NULL)) {
273
274
275
276
277
278
279
280
281 crm_debug("Using orphan clone name %s instead of history ID %s",
282 action->rsc->id, action->rsc->priv->history_id);
283 crm_xml_add(rsc_xml, PCMK_XA_ID, action->rsc->priv->history_id);
284 crm_xml_add(rsc_xml, PCMK__XA_LONG_ID, action->rsc->id);
285
286 } else if (!pcmk_is_set(action->rsc->flags, pcmk__rsc_unique)) {
287 const char *xml_id = pcmk__xe_id(action->rsc->priv->xml);
288
289 crm_debug("Using anonymous clone name %s for %s (aka %s)",
290 xml_id, action->rsc->id, action->rsc->priv->history_id);
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305 crm_xml_add(rsc_xml, PCMK_XA_ID, xml_id);
306 if ((action->rsc->priv->history_id != NULL)
307 && !pcmk__str_eq(xml_id, action->rsc->priv->history_id,
308 pcmk__str_none)) {
309 crm_xml_add(rsc_xml, PCMK__XA_LONG_ID,
310 action->rsc->priv->history_id);
311 } else {
312 crm_xml_add(rsc_xml, PCMK__XA_LONG_ID, action->rsc->id);
313 }
314
315 } else {
316 pcmk__assert(action->rsc->priv->history_id == NULL);
317 crm_xml_add(rsc_xml, PCMK_XA_ID, action->rsc->id);
318 }
319
320 for (int lpc = 0; lpc < PCMK__NELEM(attr_list); lpc++) {
321 crm_xml_add(rsc_xml, attr_list[lpc],
322 g_hash_table_lookup(action->rsc->priv->meta,
323 attr_list[lpc]));
324 }
325 }
326
327
328
329
330
331
332
333
334 static void
335 add_action_attributes(pcmk_action_t *action, xmlNode *action_xml)
336 {
337 xmlNode *args_xml = NULL;
338 pcmk_resource_t *rsc = action->rsc;
339
340
341
342
343
344 args_xml = pcmk__xe_create(action_xml, PCMK__XE_ATTRIBUTES);
345
346 crm_xml_add(args_xml, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
347 g_hash_table_foreach(action->extra, hash2field, args_xml);
348
349 if ((rsc != NULL) && (action->node != NULL)) {
350
351 GHashTable *params = pe_rsc_params(rsc, action->node,
352 rsc->priv->scheduler);
353
354 pcmk__substitute_remote_addr(rsc, params);
355
356 g_hash_table_foreach(params, hash2smartfield, args_xml);
357
358 } else if ((rsc != NULL)
359 && (rsc->priv->variant <= pcmk__rsc_variant_primitive)) {
360 GHashTable *params = pe_rsc_params(rsc, NULL, rsc->priv->scheduler);
361
362 g_hash_table_foreach(params, hash2smartfield, args_xml);
363 }
364
365 g_hash_table_foreach(action->meta, hash2metafield, args_xml);
366 if (rsc != NULL) {
367 pcmk_resource_t *parent = rsc;
368
369 while (parent != NULL) {
370 parent->priv->cmds->add_graph_meta(parent, args_xml);
371 parent = parent->priv->parent;
372 }
373
374 pcmk__add_guest_meta_to_xml(args_xml, action);
375 }
376
377 pcmk__xe_sort_attrs(args_xml);
378 }
379
380
381
382
383
384
385
386
387
388
389 static void
390 create_graph_action(xmlNode *parent, pcmk_action_t *action, bool skip_details,
391 const pcmk_scheduler_t *scheduler)
392 {
393 bool needs_node_info = true;
394 bool needs_maintenance_info = false;
395 xmlNode *action_xml = NULL;
396
397 if ((action == NULL) || (scheduler == NULL)) {
398 return;
399 }
400
401
402
403 if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH, pcmk__str_none)) {
404
405 if (pcmk_is_set(action->flags, pcmk__action_pseudo)) {
406 action_xml = pcmk__xe_create(parent, PCMK__XE_PSEUDO_EVENT);
407 } else {
408 action_xml = pcmk__xe_create(parent, PCMK__XE_CRM_EVENT);
409 }
410
411 } else if (pcmk__str_any_of(action->task,
412 PCMK_ACTION_DO_SHUTDOWN,
413 PCMK_ACTION_CLEAR_FAILCOUNT, NULL)) {
414 action_xml = pcmk__xe_create(parent, PCMK__XE_CRM_EVENT);
415
416 } else if (pcmk__str_eq(action->task, PCMK_ACTION_LRM_DELETE,
417 pcmk__str_none)) {
418
419 action_xml = pcmk__xe_create(parent, PCMK__XE_CRM_EVENT);
420 crm_xml_add(action_xml, PCMK__XA_MODE, PCMK__VALUE_CIB);
421
422 } else if (pcmk_is_set(action->flags, pcmk__action_pseudo)) {
423 if (pcmk__str_eq(action->task, PCMK_ACTION_MAINTENANCE_NODES,
424 pcmk__str_none)) {
425 needs_maintenance_info = true;
426 }
427 action_xml = pcmk__xe_create(parent, PCMK__XE_PSEUDO_EVENT);
428 needs_node_info = false;
429
430 } else {
431 action_xml = pcmk__xe_create(parent, PCMK__XE_RSC_OP);
432 }
433
434 crm_xml_add_int(action_xml, PCMK_XA_ID, action->id);
435 crm_xml_add(action_xml, PCMK_XA_OPERATION, action->task);
436
437 if ((action->rsc != NULL) && (action->rsc->priv->history_id != NULL)) {
438 char *clone_key = NULL;
439 guint interval_ms;
440
441 if (pcmk__guint_from_hash(action->meta, PCMK_META_INTERVAL, 0,
442 &interval_ms) != pcmk_rc_ok) {
443 interval_ms = 0;
444 }
445 clone_key = clone_op_key(action, interval_ms);
446 crm_xml_add(action_xml, PCMK__XA_OPERATION_KEY, clone_key);
447 crm_xml_add(action_xml, "internal_" PCMK__XA_OPERATION_KEY,
448 action->uuid);
449 free(clone_key);
450 } else {
451 crm_xml_add(action_xml, PCMK__XA_OPERATION_KEY, action->uuid);
452 }
453
454 if (needs_node_info && (action->node != NULL)) {
455 add_node_details(action, action_xml);
456 pcmk__insert_dup(action->meta, PCMK__META_ON_NODE,
457 action->node->priv->name);
458 pcmk__insert_dup(action->meta, PCMK__META_ON_NODE_UUID,
459 action->node->priv->id);
460 }
461
462 if (skip_details) {
463 return;
464 }
465
466 if ((action->rsc != NULL)
467 && !pcmk_is_set(action->flags, pcmk__action_pseudo)) {
468
469
470 add_resource_details(action, action_xml);
471 }
472
473
474 add_action_attributes(action, action_xml);
475
476
477 if (needs_node_info && (action->node != NULL)) {
478 add_downed_nodes(action_xml, action);
479 }
480
481 if (needs_maintenance_info) {
482 add_maintenance_nodes(action_xml, scheduler);
483 }
484 }
485
486
487
488
489
490
491
492
493
494 static bool
495 should_add_action_to_graph(pcmk_action_t *action)
496 {
497 if (!pcmk_is_set(action->flags, pcmk__action_runnable)) {
498 crm_trace("Ignoring action %s (%d): unrunnable",
499 action->uuid, action->id);
500 return false;
501 }
502
503 if (pcmk_is_set(action->flags, pcmk__action_optional)
504 && !pcmk_is_set(action->flags, pcmk__action_always_in_graph)) {
505 crm_trace("Ignoring action %s (%d): optional",
506 action->uuid, action->id);
507 return false;
508 }
509
510
511
512
513 if ((action->rsc != NULL)
514 && !pcmk_is_set(action->rsc->flags, pcmk__rsc_managed)
515 && !pcmk__str_eq(action->task, PCMK_ACTION_MONITOR, pcmk__str_none)) {
516
517 const char *interval_ms_s;
518
519
520
521
522
523
524 interval_ms_s = g_hash_table_lookup(action->meta, PCMK_META_INTERVAL);
525 if (pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches)) {
526 crm_trace("Ignoring action %s (%d): for unmanaged resource (%s)",
527 action->uuid, action->id, action->rsc->id);
528 return false;
529 }
530 }
531
532
533
534
535 if (pcmk_is_set(action->flags, pcmk__action_pseudo)
536 || pcmk__strcase_any_of(action->task, PCMK_ACTION_STONITH,
537 PCMK_ACTION_DO_SHUTDOWN, NULL)) {
538 return true;
539 }
540
541 if (action->node == NULL) {
542 pcmk__sched_err(action->scheduler,
543 "Skipping action %s (%d) "
544 "because it was not assigned to a node (bug?)",
545 action->uuid, action->id);
546 pcmk__log_action("Unassigned", action, false);
547 return false;
548 }
549
550 if (pcmk_is_set(action->flags, pcmk__action_on_dc)) {
551 crm_trace("Action %s (%d) should be dumped: "
552 "can run on DC instead of %s",
553 action->uuid, action->id, pcmk__node_name(action->node));
554
555 } else if (pcmk__is_guest_or_bundle_node(action->node)
556 && !pcmk_is_set(action->node->priv->flags,
557 pcmk__node_remote_reset)) {
558 crm_trace("Action %s (%d) should be dumped: "
559 "assuming will be runnable on guest %s",
560 action->uuid, action->id, pcmk__node_name(action->node));
561
562 } else if (!action->node->details->online) {
563 pcmk__sched_err(action->scheduler,
564 "Skipping action %s (%d) "
565 "because it was scheduled for offline node (bug?)",
566 action->uuid, action->id);
567 pcmk__log_action("Offline node", action, false);
568 return false;
569
570 } else if (action->node->details->unclean) {
571 pcmk__sched_err(action->scheduler,
572 "Skipping action %s (%d) "
573 "because it was scheduled for unclean node (bug?)",
574 action->uuid, action->id);
575 pcmk__log_action("Unclean node", action, false);
576 return false;
577 }
578 return true;
579 }
580
581
582
583
584
585
586
587
588
589 static bool
590 ordering_can_change_actions(const pcmk__related_action_t *ordering)
591 {
592 return pcmk_any_flags_set(ordering->flags,
593 ~(pcmk__ar_then_implies_first_graphed
594 |pcmk__ar_first_implies_then_graphed
595 |pcmk__ar_ordered));
596 }
597
598
599
600
601
602
603
604
605
606
607
608
609 static bool
610 should_add_input_to_graph(const pcmk_action_t *action,
611 pcmk__related_action_t *input)
612 {
613 if (input->graphed) {
614 return true;
615 }
616
617 if (input->flags == pcmk__ar_none) {
618 crm_trace("Ignoring %s (%d) input %s (%d): "
619 "ordering disabled",
620 action->uuid, action->id,
621 input->action->uuid, input->action->id);
622 return false;
623
624 } else if (!pcmk_is_set(input->action->flags, pcmk__action_runnable)
625 && !ordering_can_change_actions(input)) {
626 crm_trace("Ignoring %s (%d) input %s (%d): "
627 "optional and input unrunnable",
628 action->uuid, action->id,
629 input->action->uuid, input->action->id);
630 return false;
631
632 } else if (!pcmk_is_set(input->action->flags, pcmk__action_runnable)
633 && pcmk_is_set(input->flags, pcmk__ar_min_runnable)) {
634 crm_trace("Ignoring %s (%d) input %s (%d): "
635 "minimum number of instances required but input unrunnable",
636 action->uuid, action->id,
637 input->action->uuid, input->action->id);
638 return false;
639
640 } else if (pcmk_is_set(input->flags, pcmk__ar_unmigratable_then_blocks)
641 && !pcmk_is_set(input->action->flags, pcmk__action_runnable)) {
642 crm_trace("Ignoring %s (%d) input %s (%d): "
643 "input blocked if 'then' unmigratable",
644 action->uuid, action->id,
645 input->action->uuid, input->action->id);
646 return false;
647
648 } else if (pcmk_is_set(input->flags, pcmk__ar_if_first_unmigratable)
649 && pcmk_is_set(input->action->flags, pcmk__action_migratable)) {
650 crm_trace("Ignoring %s (%d) input %s (%d): ordering applies "
651 "only if input is unmigratable, but it is migratable",
652 action->uuid, action->id,
653 input->action->uuid, input->action->id);
654 return false;
655
656 } else if ((input->flags == pcmk__ar_ordered)
657 && pcmk_is_set(input->action->flags, pcmk__action_migratable)
658 && pcmk__ends_with(input->action->uuid, "_stop_0")) {
659 crm_trace("Ignoring %s (%d) input %s (%d): "
660 "optional but stop in migration",
661 action->uuid, action->id,
662 input->action->uuid, input->action->id);
663 return false;
664
665 } else if (input->flags == pcmk__ar_if_on_same_node_or_target) {
666 pcmk_node_t *input_node = input->action->node;
667
668 if ((action->rsc != NULL)
669 && pcmk__str_eq(action->task, PCMK_ACTION_MIGRATE_TO,
670 pcmk__str_none)) {
671
672 pcmk_node_t *assigned = action->rsc->priv->assigned_node;
673
674
675
676
677
678 if (!pcmk__same_node(input_node, assigned)) {
679 crm_trace("Ignoring %s (%d) input %s (%d): "
680 "migration target %s is not same as input node %s",
681 action->uuid, action->id,
682 input->action->uuid, input->action->id,
683 (assigned? assigned->priv->name : "<none>"),
684 (input_node? input_node->priv->name : "<none>"));
685 input->flags = pcmk__ar_none;
686 return false;
687 }
688
689 } else if (!pcmk__same_node(input_node, action->node)) {
690 crm_trace("Ignoring %s (%d) input %s (%d): "
691 "not on same node (%s vs %s)",
692 action->uuid, action->id,
693 input->action->uuid, input->action->id,
694 (action->node? action->node->priv->name : "<none>"),
695 (input_node? input_node->priv->name : "<none>"));
696 input->flags = pcmk__ar_none;
697 return false;
698
699 } else if (pcmk_is_set(input->action->flags, pcmk__action_optional)) {
700 crm_trace("Ignoring %s (%d) input %s (%d): "
701 "ordering optional",
702 action->uuid, action->id,
703 input->action->uuid, input->action->id);
704 input->flags = pcmk__ar_none;
705 return false;
706 }
707
708 } else if (input->flags == pcmk__ar_if_required_on_same_node) {
709 if (input->action->node && action->node
710 && !pcmk__same_node(input->action->node, action->node)) {
711 crm_trace("Ignoring %s (%d) input %s (%d): "
712 "not on same node (%s vs %s)",
713 action->uuid, action->id,
714 input->action->uuid, input->action->id,
715 pcmk__node_name(action->node),
716 pcmk__node_name(input->action->node));
717 input->flags = pcmk__ar_none;
718 return false;
719
720 } else if (pcmk_is_set(input->action->flags, pcmk__action_optional)) {
721 crm_trace("Ignoring %s (%d) input %s (%d): optional",
722 action->uuid, action->id,
723 input->action->uuid, input->action->id);
724 input->flags = pcmk__ar_none;
725 return false;
726 }
727
728 } else if (input->action->rsc
729 && input->action->rsc != action->rsc
730 && pcmk_is_set(input->action->rsc->flags, pcmk__rsc_failed)
731 && !pcmk_is_set(input->action->rsc->flags, pcmk__rsc_managed)
732 && pcmk__ends_with(input->action->uuid, "_stop_0")
733 && pcmk__is_clone(action->rsc)) {
734 crm_warn("Ignoring requirement that %s complete before %s:"
735 " unmanaged failed resources cannot prevent clone shutdown",
736 input->action->uuid, action->uuid);
737 return false;
738
739 } else if (pcmk_is_set(input->action->flags, pcmk__action_optional)
740 && !pcmk_any_flags_set(input->action->flags,
741 pcmk__action_always_in_graph
742 |pcmk__action_added_to_graph)
743 && !should_add_action_to_graph(input->action)) {
744 crm_trace("Ignoring %s (%d) input %s (%d): "
745 "input optional",
746 action->uuid, action->id,
747 input->action->uuid, input->action->id);
748 return false;
749 }
750
751 crm_trace("%s (%d) input %s %s (%d) on %s should be dumped: %s %s %#.6x",
752 action->uuid, action->id, action_type_str(input->action->flags),
753 input->action->uuid, input->action->id,
754 action_node_str(input->action),
755 action_runnable_str(input->action->flags),
756 action_optional_str(input->action->flags), input->flags);
757 return true;
758 }
759
760
761
762
763
764
765
766
767
768
769
770
771
772 bool
773 pcmk__graph_has_loop(const pcmk_action_t *init_action,
774 const pcmk_action_t *action, pcmk__related_action_t *input)
775 {
776 bool has_loop = false;
777
778 if (pcmk_is_set(input->action->flags, pcmk__action_detect_loop)) {
779 crm_trace("Breaking tracking loop: %s@%s -> %s@%s (%#.6x)",
780 input->action->uuid,
781 input->action->node? input->action->node->priv->name : "",
782 action->uuid,
783 action->node? action->node->priv->name : "",
784 input->flags);
785 return false;
786 }
787
788
789 if (!should_add_input_to_graph(action, input)) {
790 return false;
791 }
792
793 if (input->action == init_action) {
794 crm_debug("Input loop found in %s@%s ->...-> %s@%s",
795 action->uuid,
796 action->node? action->node->priv->name : "",
797 init_action->uuid,
798 init_action->node? init_action->node->priv->name : "");
799 return true;
800 }
801
802 pcmk__set_action_flags(input->action, pcmk__action_detect_loop);
803
804 crm_trace("Checking inputs of action %s@%s input %s@%s (%#.6x)"
805 "for graph loop with %s@%s ",
806 action->uuid,
807 action->node? action->node->priv->name : "",
808 input->action->uuid,
809 input->action->node? input->action->node->priv->name : "",
810 input->flags,
811 init_action->uuid,
812 init_action->node? init_action->node->priv->name : "");
813
814
815 for (GList *iter = input->action->actions_before;
816 iter != NULL; iter = iter->next) {
817
818 if (pcmk__graph_has_loop(init_action, input->action,
819 (pcmk__related_action_t *) iter->data)) {
820
821 has_loop = true;
822 break;
823 }
824 }
825
826 pcmk__clear_action_flags(input->action, pcmk__action_detect_loop);
827
828 if (!has_loop) {
829 crm_trace("No input loop found in %s@%s -> %s@%s (%#.6x)",
830 input->action->uuid,
831 input->action->node? input->action->node->priv->name : "",
832 action->uuid,
833 action->node? action->node->priv->name : "",
834 input->flags);
835 }
836 return has_loop;
837 }
838
839
840
841
842
843
844
845
846
847
848 static xmlNode *
849 create_graph_synapse(const pcmk_action_t *action, pcmk_scheduler_t *scheduler)
850 {
851 int synapse_priority = 0;
852 xmlNode *syn = pcmk__xe_create(scheduler->priv->graph, PCMK__XE_SYNAPSE);
853
854 crm_xml_add_int(syn, PCMK_XA_ID, scheduler->priv->synapse_count++);
855
856 if (action->rsc != NULL) {
857 synapse_priority = action->rsc->priv->priority;
858 }
859 if (action->priority > synapse_priority) {
860 synapse_priority = action->priority;
861 }
862 if (synapse_priority > 0) {
863 crm_xml_add_int(syn, PCMK__XA_PRIORITY, synapse_priority);
864 }
865 return syn;
866 }
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884 static void
885 add_action_to_graph(gpointer data, gpointer user_data)
886 {
887 pcmk_action_t *action = (pcmk_action_t *) data;
888 pcmk_scheduler_t *scheduler = (pcmk_scheduler_t *) user_data;
889
890 xmlNode *syn = NULL;
891 xmlNode *set = NULL;
892 xmlNode *in = NULL;
893
894
895
896
897
898 if (!pcmk_is_set(action->flags, pcmk__action_inputs_deduplicated)) {
899 pcmk__deduplicate_action_inputs(action);
900 pcmk__set_action_flags(action, pcmk__action_inputs_deduplicated);
901 }
902
903 if (pcmk_is_set(action->flags, pcmk__action_added_to_graph)
904 || !should_add_action_to_graph(action)) {
905 return;
906 }
907 pcmk__set_action_flags(action, pcmk__action_added_to_graph);
908
909 crm_trace("Adding action %d (%s%s%s) to graph",
910 action->id, action->uuid,
911 ((action->node == NULL)? "" : " on "),
912 ((action->node == NULL)? "" : action->node->priv->name));
913
914 syn = create_graph_synapse(action, scheduler);
915 set = pcmk__xe_create(syn, PCMK__XE_ACTION_SET);
916 in = pcmk__xe_create(syn, PCMK__XE_INPUTS);
917
918 create_graph_action(set, action, false, scheduler);
919
920 for (GList *lpc = action->actions_before; lpc != NULL; lpc = lpc->next) {
921 pcmk__related_action_t *input = lpc->data;
922
923 if (should_add_input_to_graph(action, input)) {
924 xmlNode *input_xml = pcmk__xe_create(in, PCMK__XE_TRIGGER);
925
926 input->graphed = true;
927 create_graph_action(input_xml, input->action, true, scheduler);
928 }
929 }
930 }
931
932 static int transition_id = 0;
933
934
935
936
937
938
939
940
941 void
942 pcmk__log_transition_summary(const pcmk_scheduler_t *scheduler,
943 const char *filename)
944 {
945 if (pcmk_is_set(scheduler->flags, pcmk__sched_processing_error)
946 || pcmk__config_has_error) {
947 crm_err("Calculated transition %d (with errors)%s%s",
948 transition_id,
949 (filename == NULL)? "" : ", saving inputs in ",
950 (filename == NULL)? "" : filename);
951
952 } else if (pcmk_is_set(scheduler->flags, pcmk__sched_processing_warning)
953 || pcmk__config_has_warning) {
954 crm_warn("Calculated transition %d (with warnings)%s%s",
955 transition_id,
956 (filename == NULL)? "" : ", saving inputs in ",
957 (filename == NULL)? "" : filename);
958
959 } else {
960 crm_notice("Calculated transition %d%s%s",
961 transition_id,
962 (filename == NULL)? "" : ", saving inputs in ",
963 (filename == NULL)? "" : filename);
964 }
965 if (pcmk__config_has_error) {
966 crm_notice("Configuration errors found during scheduler processing,"
967 " please run \"crm_verify -L\" to identify issues");
968 }
969 }
970
971
972
973
974
975
976
977 void
978 pcmk__add_rsc_actions_to_graph(pcmk_resource_t *rsc)
979 {
980 GList *iter = NULL;
981
982 pcmk__assert(rsc != NULL);
983
984 pcmk__rsc_trace(rsc, "Adding actions for %s to graph", rsc->id);
985
986
987 g_list_foreach(rsc->priv->actions, add_action_to_graph,
988 rsc->priv->scheduler);
989
990
991 for (iter = rsc->priv->children; iter != NULL; iter = iter->next) {
992 pcmk_resource_t *child_rsc = (pcmk_resource_t *) iter->data;
993
994 child_rsc->priv->cmds->add_actions_to_graph(child_rsc);
995 }
996 }
997
998
999
1000
1001
1002
1003
1004 void
1005 pcmk__create_graph(pcmk_scheduler_t *scheduler)
1006 {
1007 GList *iter = NULL;
1008 const char *value = NULL;
1009 long long limit = 0LL;
1010 GHashTable *config_hash = scheduler->priv->options;
1011 int rc = pcmk_rc_ok;
1012
1013 transition_id++;
1014 crm_trace("Creating transition graph %d", transition_id);
1015
1016 scheduler->priv->graph = pcmk__xe_create(NULL, PCMK__XE_TRANSITION_GRAPH);
1017
1018 value = pcmk__cluster_option(config_hash, PCMK_OPT_CLUSTER_DELAY);
1019 crm_xml_add(scheduler->priv->graph, PCMK_OPT_CLUSTER_DELAY, value);
1020
1021 value = pcmk__cluster_option(config_hash, PCMK_OPT_STONITH_TIMEOUT);
1022 crm_xml_add(scheduler->priv->graph, PCMK_OPT_STONITH_TIMEOUT, value);
1023
1024 crm_xml_add(scheduler->priv->graph, PCMK__XA_FAILED_STOP_OFFSET,
1025 PCMK_VALUE_INFINITY);
1026
1027 if (pcmk_is_set(scheduler->flags, pcmk__sched_start_failure_fatal)) {
1028 crm_xml_add(scheduler->priv->graph, PCMK__XA_FAILED_START_OFFSET,
1029 PCMK_VALUE_INFINITY);
1030 } else {
1031 crm_xml_add(scheduler->priv->graph, PCMK__XA_FAILED_START_OFFSET, "1");
1032 }
1033
1034 value = pcmk__cluster_option(config_hash, PCMK_OPT_BATCH_LIMIT);
1035 crm_xml_add(scheduler->priv->graph, PCMK_OPT_BATCH_LIMIT, value);
1036
1037 crm_xml_add_int(scheduler->priv->graph, "transition_id", transition_id);
1038
1039 value = pcmk__cluster_option(config_hash, PCMK_OPT_MIGRATION_LIMIT);
1040 rc = pcmk__scan_ll(value, &limit, 0LL);
1041 if (rc != pcmk_rc_ok) {
1042 crm_warn("Ignoring invalid value '%s' for " PCMK_OPT_MIGRATION_LIMIT
1043 ": %s", value, pcmk_rc_str(rc));
1044 } else if (limit > 0) {
1045 crm_xml_add(scheduler->priv->graph, PCMK_OPT_MIGRATION_LIMIT, value);
1046 }
1047
1048 if (scheduler->priv->recheck_by > 0) {
1049 char *recheck_epoch = NULL;
1050
1051 recheck_epoch = crm_strdup_printf("%llu", (unsigned long long)
1052 scheduler->priv->recheck_by);
1053 crm_xml_add(scheduler->priv->graph, "recheck-by", recheck_epoch);
1054 free(recheck_epoch);
1055 }
1056
1057
1058
1059
1060
1061
1062
1063 for (iter = scheduler->priv->resources; iter != NULL; iter = iter->next) {
1064 pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
1065
1066 pcmk__rsc_trace(rsc, "Processing actions for %s", rsc->id);
1067 rsc->priv->cmds->add_actions_to_graph(rsc);
1068 }
1069
1070
1071 add_maintenance_update(scheduler);
1072
1073
1074 for (iter = scheduler->priv->actions; iter != NULL; iter = iter->next) {
1075 pcmk_action_t *action = (pcmk_action_t *) iter->data;
1076
1077 if ((action->rsc != NULL)
1078 && (action->node != NULL)
1079 && action->node->details->shutdown
1080 && !pcmk_is_set(action->rsc->flags, pcmk__rsc_maintenance)
1081 && !pcmk_any_flags_set(action->flags,
1082 pcmk__action_optional|pcmk__action_runnable)
1083 && pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_none)) {
1084
1085
1086
1087
1088 if (pcmk_is_set(scheduler->flags, pcmk__sched_quorate)
1089 || (scheduler->no_quorum_policy == pcmk_no_quorum_ignore)) {
1090 const bool managed = pcmk_is_set(action->rsc->flags,
1091 pcmk__rsc_managed);
1092 const bool failed = pcmk_is_set(action->rsc->flags,
1093 pcmk__rsc_failed);
1094
1095 crm_crit("Cannot %s %s because of %s:%s%s (%s)",
1096 action->node->details->unclean? "fence" : "shut down",
1097 pcmk__node_name(action->node), action->rsc->id,
1098 (managed? " blocked" : " unmanaged"),
1099 (failed? " failed" : ""), action->uuid);
1100 }
1101 }
1102
1103 add_action_to_graph((gpointer) action, (gpointer) scheduler);
1104 }
1105
1106 crm_log_xml_trace(scheduler->priv->graph, "graph");
1107 }