pacemaker 2.1.5-a3f44794f94
Scalable High-Availability cluster resource manager
pcmk_graph_producer.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2022 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU General Public License version 2
7 * or later (GPLv2+) WITHOUT ANY WARRANTY.
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/msg_xml.h>
16#include <crm/common/xml.h>
17
18#include <glib.h>
19
20#include <pacemaker-internal.h>
21
23
24// Convenience macros for logging action properties
25
26#define action_type_str(flags) \
27 (pcmk_is_set((flags), pe_action_pseudo)? "pseudo-action" : "action")
28
29#define action_optional_str(flags) \
30 (pcmk_is_set((flags), pe_action_optional)? "optional" : "required")
31
32#define action_runnable_str(flags) \
33 (pcmk_is_set((flags), pe_action_runnable)? "runnable" : "unrunnable")
34
35#define action_node_str(a) \
36 (((a)->node == NULL)? "no node" : (a)->node->details->uname)
37
45static xmlNode*
46add_node_to_xml_by_id(const char *id, xmlNode *xml)
47{
48 xmlNode *node_xml;
49
50 node_xml = create_xml_node(xml, XML_CIB_TAG_NODE);
51 crm_xml_add(node_xml, XML_ATTR_UUID, id);
52
53 return node_xml;
54}
55
63static void
64add_node_to_xml(const pe_node_t *node, void *xml)
65{
66 add_node_to_xml_by_id(node->details->id, (xmlNode *) xml);
67}
68
76static int
77add_maintenance_nodes(xmlNode *xml, const pe_working_set_t *data_set)
78{
79 GList *gIter = NULL;
80 xmlNode *maintenance =
82 int count = 0;
83
84 for (gIter = data_set->nodes; gIter != NULL;
85 gIter = gIter->next) {
86 pe_node_t *node = (pe_node_t *) gIter->data;
87 struct pe_node_shared_s *details = node->details;
88
89 if (!pe__is_guest_or_remote_node(node)) {
90 continue; /* just remote nodes need to know atm */
91 }
92
93 if (details->maintenance != details->remote_maintenance) {
94 if (maintenance) {
96 add_node_to_xml_by_id(node->details->id, maintenance),
97 XML_NODE_IS_MAINTENANCE, details->maintenance?"1":"0");
98 }
99 count++;
100 }
101 }
102 crm_trace("%s %d nodes to adjust maintenance-mode "
103 "to transition", maintenance?"Added":"Counted", count);
104 return count;
105}
106
113static void
114add_maintenance_update(pe_working_set_t *data_set)
115{
116 pe_action_t *action = NULL;
117
118 if (add_maintenance_nodes(NULL, data_set)) {
119 crm_trace("adding maintenance state update pseudo action");
122 }
123}
124
137static void
138add_downed_nodes(xmlNode *xml, const pe_action_t *action,
140{
141 CRM_CHECK(xml && action && action->node && data_set, return);
142
143 if (pcmk__str_eq(action->task, CRM_OP_SHUTDOWN, pcmk__str_casei)) {
144
145 /* Shutdown makes the action's node down */
146 xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED);
147 add_node_to_xml_by_id(action->node->details->id, downed);
148
149 } else if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_casei)) {
150
151 /* Fencing makes the action's node and any hosted guest nodes down */
152 const char *fence = g_hash_table_lookup(action->meta, "stonith_action");
153
154 if (pcmk__is_fencing_action(fence)) {
155 xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED);
156 add_node_to_xml_by_id(action->node->details->id, downed);
157 pe_foreach_guest_node(data_set, action->node, add_node_to_xml, downed);
158 }
159
160 } else if (action->rsc && action->rsc->is_remote_node
161 && pcmk__str_eq(action->task, CRMD_ACTION_STOP, pcmk__str_casei)) {
162
163 /* Stopping a remote connection resource makes connected node down,
164 * unless it's part of a migration
165 */
166 GList *iter;
168 gboolean migrating = FALSE;
169
170 for (iter = action->actions_before; iter != NULL; iter = iter->next) {
171 input = ((pe_action_wrapper_t *) iter->data)->action;
172 if (input->rsc && pcmk__str_eq(action->rsc->id, input->rsc->id, pcmk__str_casei)
173 && pcmk__str_eq(input->task, CRMD_ACTION_MIGRATED, pcmk__str_casei)) {
174 migrating = TRUE;
175 break;
176 }
177 }
178 if (!migrating) {
179 xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED);
180 add_node_to_xml_by_id(action->rsc->id, downed);
181 }
182 }
183}
184
194static char *
195clone_op_key(pe_action_t *action, guint interval_ms)
196{
197 if (pcmk__str_eq(action->task, RSC_NOTIFY, pcmk__str_none)) {
198 const char *n_type = g_hash_table_lookup(action->meta, "notify_type");
199 const char *n_task = g_hash_table_lookup(action->meta,
200 "notify_operation");
201
202 CRM_LOG_ASSERT((n_type != NULL) && (n_task != NULL));
203 return pcmk__notify_key(action->rsc->clone_name, n_type, n_task);
204
205 } else if (action->cancel_task != NULL) {
206 return pcmk__op_key(action->rsc->clone_name, action->cancel_task,
207 interval_ms);
208 } else {
209 return pcmk__op_key(action->rsc->clone_name, action->task, interval_ms);
210 }
211}
212
220static void
221add_node_details(pe_action_t *action, xmlNode *xml)
222{
224
225 crm_xml_add(xml, XML_LRM_ATTR_TARGET, action->node->details->uname);
226 crm_xml_add(xml, XML_LRM_ATTR_TARGET_UUID, action->node->details->id);
227 if (router_node != NULL) {
229 }
230}
231
239static void
240add_resource_details(pe_action_t *action, xmlNode *action_xml)
241{
242 xmlNode *rsc_xml = NULL;
243 const char *attr_list[] = {
247 };
248
249 /* If a resource is locked to a node via shutdown-lock, mark its actions
250 * so the controller can preserve the lock when the action completes.
251 */
254 (long long) action->rsc->lock_time);
255 }
256
257 // List affected resource
258
259 rsc_xml = create_xml_node(action_xml, crm_element_name(action->rsc->xml));
260 if (pcmk_is_set(action->rsc->flags, pe_rsc_orphan)
261 && (action->rsc->clone_name != NULL)) {
262 /* Use the numbered instance name here, because if there is more
263 * than one instance on a node, we need to make sure the command
264 * goes to the right one.
265 *
266 * This is important even for anonymous clones, because the clone's
267 * unique meta-attribute might have just been toggled from on to
268 * off.
269 */
270 crm_debug("Using orphan clone name %s instead of %s",
271 action->rsc->id, action->rsc->clone_name);
272 crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->clone_name);
273 crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id);
274
275 } else if (!pcmk_is_set(action->rsc->flags, pe_rsc_unique)) {
276 const char *xml_id = ID(action->rsc->xml);
277
278 crm_debug("Using anonymous clone name %s for %s (aka %s)",
279 xml_id, action->rsc->id, action->rsc->clone_name);
280
281 /* ID is what we'd like client to use
282 * ID_LONG is what they might know it as instead
283 *
284 * ID_LONG is only strictly needed /here/ during the
285 * transition period until all nodes in the cluster
286 * are running the new software /and/ have rebooted
287 * once (meaning that they've only ever spoken to a DC
288 * supporting this feature).
289 *
290 * If anyone toggles the unique flag to 'on', the
291 * 'instance free' name will correspond to an orphan
292 * and fall into the clause above instead
293 */
294 crm_xml_add(rsc_xml, XML_ATTR_ID, xml_id);
295 if ((action->rsc->clone_name != NULL)
296 && !pcmk__str_eq(xml_id, action->rsc->clone_name,
298 crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->clone_name);
299 } else {
300 crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id);
301 }
302
303 } else {
304 CRM_ASSERT(action->rsc->clone_name == NULL);
305 crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->id);
306 }
307
308 for (int lpc = 0; lpc < PCMK__NELEM(attr_list); lpc++) {
309 crm_xml_add(rsc_xml, attr_list[lpc],
310 g_hash_table_lookup(action->rsc->meta, attr_list[lpc]));
311 }
312}
313
321static void
322add_action_attributes(pe_action_t *action, xmlNode *action_xml)
323{
324 xmlNode *args_xml = NULL;
325
326 /* We create free-standing XML to start, so we can sort the attributes
327 * before adding it to action_xml, which keeps the scheduler regression
328 * test graphs comparable.
329 */
330 args_xml = create_xml_node(NULL, XML_TAG_ATTRS);
331
333 g_hash_table_foreach(action->extra, hash2field, args_xml);
334
335 if ((action->rsc != NULL) && (action->node != NULL)) {
336 // Get the resource instance attributes, evaluated properly for node
337 GHashTable *params = pe_rsc_params(action->rsc, action->node,
338 action->rsc->cluster);
339
341
342 g_hash_table_foreach(params, hash2smartfield, args_xml);
343
344 } else if ((action->rsc != NULL) && (action->rsc->variant <= pe_native)) {
345 GHashTable *params = pe_rsc_params(action->rsc, NULL,
346 action->rsc->cluster);
347
348 g_hash_table_foreach(params, hash2smartfield, args_xml);
349 }
350
351 g_hash_table_foreach(action->meta, hash2metafield, args_xml);
352 if (action->rsc != NULL) {
354
355 while (parent != NULL) {
356 parent->cmds->add_graph_meta(parent, args_xml);
357 parent = parent->parent;
358 }
359
361
362 } else if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_none)
363 && (action->node != NULL)) {
364 /* Pass the node's attributes as meta-attributes.
365 *
366 * @TODO: Determine whether it is still necessary to do this. It was
367 * added in 33d99707, probably for the libfence-based implementation in
368 * c9a90bd, which is no longer used.
369 */
370 g_hash_table_foreach(action->node->details->attrs, hash2metafield, args_xml);
371 }
372
373 sorted_xml(args_xml, action_xml, FALSE);
374 free_xml(args_xml);
375}
376
386static void
387create_graph_action(xmlNode *parent, pe_action_t *action, bool skip_details,
389{
390 bool needs_node_info = true;
391 bool needs_maintenance_info = false;
392 xmlNode *action_xml = NULL;
393
394 if ((action == NULL) || (data_set == NULL)) {
395 return;
396 }
397
398 // Create the top-level element based on task
399
400 if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_casei)) {
401 /* All fences need node info; guest node fences are pseudo-events */
402 action_xml = create_xml_node(parent,
406
407 } else if (pcmk__str_any_of(action->task,
409 CRM_OP_CLEAR_FAILCOUNT, NULL)) {
411
412 } else if (pcmk__str_eq(action->task, CRM_OP_LRM_DELETE, pcmk__str_none)) {
413 // CIB-only clean-up for shutdown locks
416
417 } else if (pcmk_is_set(action->flags, pe_action_pseudo)) {
418 if (pcmk__str_eq(action->task, CRM_OP_MAINTENANCE_NODES,
420 needs_maintenance_info = true;
421 }
423 needs_node_info = false;
424
425 } else {
427 }
428
429 crm_xml_add_int(action_xml, XML_ATTR_ID, action->id);
430 crm_xml_add(action_xml, XML_LRM_ATTR_TASK, action->task);
431
432 if ((action->rsc != NULL) && (action->rsc->clone_name != NULL)) {
433 char *clone_key = NULL;
434 guint interval_ms;
435
437 &interval_ms) != pcmk_rc_ok) {
438 interval_ms = 0;
439 }
440 clone_key = clone_op_key(action, interval_ms);
441 crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, clone_key);
442 crm_xml_add(action_xml, "internal_" XML_LRM_ATTR_TASK_KEY, action->uuid);
443 free(clone_key);
444 } else {
445 crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, action->uuid);
446 }
447
448 if (needs_node_info && (action->node != NULL)) {
449 add_node_details(action, action_xml);
450 g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET),
451 strdup(action->node->details->uname));
452 g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET_UUID),
453 strdup(action->node->details->id));
454 }
455
456 if (skip_details) {
457 return;
458 }
459
460 if ((action->rsc != NULL)
461 && !pcmk_is_set(action->flags, pe_action_pseudo)) {
462
463 // This is a real resource action, so add resource details
464 add_resource_details(action, action_xml);
465 }
466
467 /* List any attributes in effect */
468 add_action_attributes(action, action_xml);
469
470 /* List any nodes this action is expected to make down */
471 if (needs_node_info && (action->node != NULL)) {
472 add_downed_nodes(action_xml, action, data_set);
473 }
474
475 if (needs_maintenance_info) {
476 add_maintenance_nodes(action_xml, data_set);
477 }
478}
479
488static bool
489should_add_action_to_graph(pe_action_t *action)
490{
491 if (!pcmk_is_set(action->flags, pe_action_runnable)) {
492 crm_trace("Ignoring action %s (%d): unrunnable",
493 action->uuid, action->id);
494 return false;
495 }
496
499 crm_trace("Ignoring action %s (%d): optional",
500 action->uuid, action->id);
501 return false;
502 }
503
504 /* Actions for unmanaged resources should be excluded from the graph,
505 * with the exception of monitors and cancellation of recurring monitors.
506 */
507 if ((action->rsc != NULL)
508 && !pcmk_is_set(action->rsc->flags, pe_rsc_managed)
509 && !pcmk__str_eq(action->task, RSC_STATUS, pcmk__str_none)) {
510 const char *interval_ms_s;
511
512 /* A cancellation of a recurring monitor will get here because the task
513 * is cancel rather than monitor, but the interval can still be used to
514 * recognize it. The interval has been normalized to milliseconds by
515 * this point, so a string comparison is sufficient.
516 */
517 interval_ms_s = g_hash_table_lookup(action->meta,
519 if (pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches)) {
520 crm_trace("Ignoring action %s (%d): for unmanaged resource (%s)",
521 action->uuid, action->id, action->rsc->id);
522 return false;
523 }
524 }
525
526 /* Always add pseudo-actions, fence actions, and shutdown actions (already
527 * determined to be required and runnable by this point)
528 */
531 NULL)) {
532 return true;
533 }
534
535 if (action->node == NULL) {
536 pe_err("Skipping action %s (%d) "
537 "because it was not allocated to a node (bug?)",
538 action->uuid, action->id);
539 pcmk__log_action("Unallocated", action, false);
540 return false;
541 }
542
543 if (pcmk_is_set(action->flags, pe_action_dc)) {
544 crm_trace("Action %s (%d) should be dumped: "
545 "can run on DC instead of %s",
546 action->uuid, action->id, pe__node_name(action->node));
547
548 } else if (pe__is_guest_node(action->node)
549 && !action->node->details->remote_requires_reset) {
550 crm_trace("Action %s (%d) should be dumped: "
551 "assuming will be runnable on guest %s",
552 action->uuid, action->id, pe__node_name(action->node));
553
554 } else if (!action->node->details->online) {
555 pe_err("Skipping action %s (%d) "
556 "because it was scheduled for offline node (bug?)",
557 action->uuid, action->id);
558 pcmk__log_action("Offline node", action, false);
559 return false;
560
561 } else if (action->node->details->unclean) {
562 pe_err("Skipping action %s (%d) "
563 "because it was scheduled for unclean node (bug?)",
564 action->uuid, action->id);
565 pcmk__log_action("Unclean node", action, false);
566 return false;
567 }
568 return true;
569}
570
579static bool
580ordering_can_change_actions(pe_action_wrapper_t *ordering)
581{
582 return pcmk_any_flags_set(ordering->type, ~(pe_order_implies_first_printed
585}
586
598static bool
599should_add_input_to_graph(pe_action_t *action, pe_action_wrapper_t *input)
600{
601 if (input->state == pe_link_dumped) {
602 return true;
603 }
604
605 if (input->type == pe_order_none) {
606 crm_trace("Ignoring %s (%d) input %s (%d): "
607 "ordering disabled",
608 action->uuid, action->id,
609 input->action->uuid, input->action->id);
610 return false;
611
612 } else if (!pcmk_is_set(input->action->flags, pe_action_runnable)
613 && !ordering_can_change_actions(input)) {
614 crm_trace("Ignoring %s (%d) input %s (%d): "
615 "optional and input unrunnable",
616 action->uuid, action->id,
617 input->action->uuid, input->action->id);
618 return false;
619
620 } else if (!pcmk_is_set(input->action->flags, pe_action_runnable)
622 crm_trace("Ignoring %s (%d) input %s (%d): "
623 "one-or-more and input unrunnable",
624 action->uuid, action->id,
625 input->action->uuid, input->action->id);
626 return false;
627
629 && !pcmk_is_set(input->action->flags, pe_action_runnable)) {
630 crm_trace("Ignoring %s (%d) input %s (%d): "
631 "implies input migratable but input unrunnable",
632 action->uuid, action->id,
633 input->action->uuid, input->action->id);
634 return false;
635
637 && pcmk_is_set(input->action->flags, pe_action_migrate_runnable)) {
638 crm_trace("Ignoring %s (%d) input %s (%d): "
639 "only if input unmigratable but input unrunnable",
640 action->uuid, action->id,
641 input->action->uuid, input->action->id);
642 return false;
643
644 } else if ((input->type == pe_order_optional)
646 && pcmk__ends_with(input->action->uuid, "_stop_0")) {
647 crm_trace("Ignoring %s (%d) input %s (%d): "
648 "optional but stop in migration",
649 action->uuid, action->id,
650 input->action->uuid, input->action->id);
651 return false;
652
653 } else if (input->type == pe_order_load) {
654 pe_node_t *input_node = input->action->node;
655
656 // load orderings are relevant only if actions are for same node
657
658 if (action->rsc && pcmk__str_eq(action->task, RSC_MIGRATE, pcmk__str_casei)) {
659 pe_node_t *allocated = action->rsc->allocated_to;
660
661 /* For load_stopped -> migrate_to orderings, we care about where it
662 * has been allocated to, not where it will be executed.
663 */
664 if ((input_node == NULL) || (allocated == NULL)
665 || (input_node->details != allocated->details)) {
666 crm_trace("Ignoring %s (%d) input %s (%d): "
667 "load ordering node mismatch %s vs %s",
668 action->uuid, action->id,
669 input->action->uuid, input->action->id,
670 (allocated? allocated->details->uname : "<none>"),
671 (input_node? input_node->details->uname : "<none>"));
672 input->type = pe_order_none;
673 return false;
674 }
675
676 } else if ((input_node == NULL) || (action->node == NULL)
677 || (input_node->details != action->node->details)) {
678 crm_trace("Ignoring %s (%d) input %s (%d): "
679 "load ordering node mismatch %s vs %s",
680 action->uuid, action->id,
681 input->action->uuid, input->action->id,
682 (action->node? action->node->details->uname : "<none>"),
683 (input_node? input_node->details->uname : "<none>"));
684 input->type = pe_order_none;
685 return false;
686
687 } else if (pcmk_is_set(input->action->flags, pe_action_optional)) {
688 crm_trace("Ignoring %s (%d) input %s (%d): "
689 "load ordering input optional",
690 action->uuid, action->id,
691 input->action->uuid, input->action->id);
692 input->type = pe_order_none;
693 return false;
694 }
695
696 } else if (input->type == pe_order_anti_colocation) {
697 if (input->action->node && action->node
698 && (input->action->node->details != action->node->details)) {
699 crm_trace("Ignoring %s (%d) input %s (%d): "
700 "anti-colocation node mismatch %s vs %s",
701 action->uuid, action->id,
702 input->action->uuid, input->action->id,
703 pe__node_name(action->node),
704 pe__node_name(input->action->node));
705 input->type = pe_order_none;
706 return false;
707
708 } else if (pcmk_is_set(input->action->flags, pe_action_optional)) {
709 crm_trace("Ignoring %s (%d) input %s (%d): "
710 "anti-colocation input optional",
711 action->uuid, action->id,
712 input->action->uuid, input->action->id);
713 input->type = pe_order_none;
714 return false;
715 }
716
717 } else if (input->action->rsc
718 && input->action->rsc != action->rsc
719 && pcmk_is_set(input->action->rsc->flags, pe_rsc_failed)
720 && !pcmk_is_set(input->action->rsc->flags, pe_rsc_managed)
721 && pcmk__ends_with(input->action->uuid, "_stop_0")
722 && action->rsc && pe_rsc_is_clone(action->rsc)) {
723 crm_warn("Ignoring requirement that %s complete before %s:"
724 " unmanaged failed resources cannot prevent clone shutdown",
725 input->action->uuid, action->uuid);
726 return false;
727
728 } else if (pcmk_is_set(input->action->flags, pe_action_optional)
729 && !pcmk_any_flags_set(input->action->flags,
731 && !should_add_action_to_graph(input->action)) {
732 crm_trace("Ignoring %s (%d) input %s (%d): "
733 "input optional",
734 action->uuid, action->id,
735 input->action->uuid, input->action->id);
736 return false;
737 }
738
739 crm_trace("%s (%d) input %s %s (%d) on %s should be dumped: %s %s %#.6x",
740 action->uuid, action->id, action_type_str(input->action->flags),
741 input->action->uuid, input->action->id,
742 action_node_str(input->action),
743 action_runnable_str(input->action->flags),
744 action_optional_str(input->action->flags), input->type);
745 return true;
746}
747
760bool
763{
764 bool has_loop = false;
765
766 if (pcmk_is_set(input->action->flags, pe_action_tracking)) {
767 crm_trace("Breaking tracking loop: %s@%s -> %s@%s (%#.6x)",
768 input->action->uuid,
769 input->action->node? input->action->node->details->uname : "",
770 action->uuid,
771 action->node? action->node->details->uname : "",
772 input->type);
773 return false;
774 }
775
776 // Don't need to check inputs that won't be used
777 if (!should_add_input_to_graph(action, input)) {
778 return false;
779 }
780
781 if (input->action == init_action) {
782 crm_debug("Input loop found in %s@%s ->...-> %s@%s",
783 action->uuid,
784 action->node? action->node->details->uname : "",
785 init_action->uuid,
786 init_action->node? init_action->node->details->uname : "");
787 return true;
788 }
789
791
792 crm_trace("Checking inputs of action %s@%s input %s@%s (%#.6x)"
793 "for graph loop with %s@%s ",
794 action->uuid,
795 action->node? action->node->details->uname : "",
796 input->action->uuid,
797 input->action->node? input->action->node->details->uname : "",
798 input->type,
799 init_action->uuid,
800 init_action->node? init_action->node->details->uname : "");
801
802 // Recursively check input itself for loops
803 for (GList *iter = input->action->actions_before;
804 iter != NULL; iter = iter->next) {
805
806 if (pcmk__graph_has_loop(init_action, input->action,
807 (pe_action_wrapper_t *) iter->data)) {
808 // Recursive call already logged a debug message
809 has_loop = true;
810 break;
811 }
812 }
813
815
816 if (!has_loop) {
817 crm_trace("No input loop found in %s@%s -> %s@%s (%#.6x)",
818 input->action->uuid,
819 input->action->node? input->action->node->details->uname : "",
820 action->uuid,
821 action->node? action->node->details->uname : "",
822 input->type);
823 }
824 return has_loop;
825}
826
836static xmlNode *
837create_graph_synapse(pe_action_t *action, pe_working_set_t *data_set)
838{
839 int synapse_priority = 0;
840 xmlNode *syn = create_xml_node(data_set->graph, "synapse");
841
844
845 if (action->rsc != NULL) {
846 synapse_priority = action->rsc->priority;
847 }
848 if (action->priority > synapse_priority) {
849 synapse_priority = action->priority;
850 }
851 if (synapse_priority > 0) {
852 crm_xml_add_int(syn, XML_CIB_ATTR_PRIORITY, synapse_priority);
853 }
854 return syn;
855}
856
873static void
874add_action_to_graph(gpointer data, gpointer user_data)
875{
878
879 xmlNode *syn = NULL;
880 xmlNode *set = NULL;
881 xmlNode *in = NULL;
882
883 /* If we haven't already, de-duplicate inputs (even if we won't be adding
884 * the action to the graph, so that crm_simulate's dot graphs don't have
885 * duplicates).
886 */
887 if (!pcmk_is_set(action->flags, pe_action_dedup)) {
890 }
891
892 if (pcmk_is_set(action->flags, pe_action_dumped) // Already added, or
893 || !should_add_action_to_graph(action)) { // shouldn't be added
894 return;
895 }
897
898 crm_trace("Adding action %d (%s%s%s) to graph",
899 action->id, action->uuid,
900 ((action->node == NULL)? "" : " on "),
901 ((action->node == NULL)? "" : action->node->details->uname));
902
903 syn = create_graph_synapse(action, data_set);
904 set = create_xml_node(syn, "action_set");
905 in = create_xml_node(syn, "inputs");
906
907 create_graph_action(set, action, false, data_set);
908
909 for (GList *lpc = action->actions_before; lpc != NULL; lpc = lpc->next) {
911
912 if (should_add_input_to_graph(action, input)) {
913 xmlNode *input_xml = create_xml_node(in, "trigger");
914
915 input->state = pe_link_dumped;
916 create_graph_action(input_xml, input->action, true, data_set);
917 }
918 }
919}
920
921static int transition_id = -1;
922
929void
930pcmk__log_transition_summary(const char *filename)
931{
933 crm_err("Calculated transition %d (with errors)%s%s",
934 transition_id,
935 (filename == NULL)? "" : ", saving inputs in ",
936 (filename == NULL)? "" : filename);
937
938 } else if (was_processing_warning) {
939 crm_warn("Calculated transition %d (with warnings)%s%s",
940 transition_id,
941 (filename == NULL)? "" : ", saving inputs in ",
942 (filename == NULL)? "" : filename);
943
944 } else {
945 crm_notice("Calculated transition %d%s%s",
946 transition_id,
947 (filename == NULL)? "" : ", saving inputs in ",
948 (filename == NULL)? "" : filename);
949 }
950 if (crm_config_error) {
951 crm_notice("Configuration errors found during scheduler processing,"
952 " please run \"crm_verify -L\" to identify issues");
953 }
954}
955
962void
964{
965 GList *iter = NULL;
966
967 CRM_ASSERT(rsc != NULL);
968 pe_rsc_trace(rsc, "Adding actions for %s to graph", rsc->id);
969
970 // First add the resource's own actions
971 g_list_foreach(rsc->actions, add_action_to_graph, rsc->cluster);
972
973 // Then recursively add its children's actions (appropriate to variant)
974 for (iter = rsc->children; iter != NULL; iter = iter->next) {
975 pe_resource_t *child_rsc = (pe_resource_t *) iter->data;
976
977 child_rsc->cmds->add_actions_to_graph(child_rsc);
978 }
979}
980
987void
989{
990 GList *iter = NULL;
991 const char *value = NULL;
992 long long limit = 0LL;
993
994 transition_id++;
995 crm_trace("Creating transition graph %d", transition_id);
996
998
999 value = pe_pref(data_set->config_hash, "cluster-delay");
1000 crm_xml_add(data_set->graph, "cluster-delay", value);
1001
1002 value = pe_pref(data_set->config_hash, "stonith-timeout");
1003 crm_xml_add(data_set->graph, "stonith-timeout", value);
1004
1005 crm_xml_add(data_set->graph, "failed-stop-offset", "INFINITY");
1006
1008 crm_xml_add(data_set->graph, "failed-start-offset", "INFINITY");
1009 } else {
1010 crm_xml_add(data_set->graph, "failed-start-offset", "1");
1011 }
1012
1013 value = pe_pref(data_set->config_hash, "batch-limit");
1014 crm_xml_add(data_set->graph, "batch-limit", value);
1015
1016 crm_xml_add_int(data_set->graph, "transition_id", transition_id);
1017
1018 value = pe_pref(data_set->config_hash, "migration-limit");
1019 if ((pcmk__scan_ll(value, &limit, 0LL) == pcmk_rc_ok) && (limit > 0)) {
1020 crm_xml_add(data_set->graph, "migration-limit", value);
1021 }
1022
1023 if (data_set->recheck_by > 0) {
1024 char *recheck_epoch = NULL;
1025
1026 recheck_epoch = crm_strdup_printf("%llu",
1027 (long long) data_set->recheck_by);
1028 crm_xml_add(data_set->graph, "recheck-by", recheck_epoch);
1029 free(recheck_epoch);
1030 }
1031
1032 /* The following code will de-duplicate action inputs, so nothing past this
1033 * should rely on the action input type flags retaining their original
1034 * values.
1035 */
1036
1037 // Add resource actions to graph
1038 for (iter = data_set->resources; iter != NULL; iter = iter->next) {
1039 pe_resource_t *rsc = (pe_resource_t *) iter->data;
1040
1041 pe_rsc_trace(rsc, "Processing actions for %s", rsc->id);
1042 rsc->cmds->add_actions_to_graph(rsc);
1043 }
1044
1045 // Add pseudo-action for list of nodes with maintenance state update
1046 add_maintenance_update(data_set);
1047
1048 // Add non-resource (node) actions
1049 for (iter = data_set->actions; iter != NULL; iter = iter->next) {
1050 pe_action_t *action = (pe_action_t *) iter->data;
1051
1052 if ((action->rsc != NULL)
1053 && (action->node != NULL)
1054 && action->node->details->shutdown
1055 && !pcmk_is_set(action->rsc->flags, pe_rsc_maintenance)
1056 && !pcmk_any_flags_set(action->flags,
1058 && pcmk__str_eq(action->task, RSC_STOP, pcmk__str_none)) {
1059 /* Eventually we should just ignore the 'fence' case, but for now
1060 * it's the best way to detect (in CTS) when CIB resource updates
1061 * are being lost.
1062 */
1065 crm_crit("Cannot %s %s because of %s:%s%s (%s)",
1066 action->node->details->unclean? "fence" : "shut down",
1067 pe__node_name(action->node), action->rsc->id,
1068 pcmk_is_set(action->rsc->flags, pe_rsc_managed)? " blocked" : " unmanaged",
1069 pcmk_is_set(action->rsc->flags, pe_rsc_failed)? " failed" : "",
1070 action->uuid);
1071 }
1072 }
1073
1074 add_action_to_graph((gpointer) action, (gpointer) data_set);
1075 }
1076
1077 crm_log_xml_trace(data_set->graph, "graph");
1078}
const char * parent
Definition: cib.c:25
Cluster Configuration.
char * pcmk__notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition: operations.c:229
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition: operations.c:45
bool pcmk__is_fencing_action(const char *action)
Definition: operations.c:535
#define PCMK__NELEM(a)
Definition: internal.h:41
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:121
gboolean was_processing_error
Definition: common.c:20
const char * pe_pref(GHashTable *options, const char *name)
Definition: common.c:307
gboolean was_processing_warning
Definition: common.c:21
GHashTable * pe_rsc_params(pe_resource_t *rsc, const pe_node_t *node, pe_working_set_t *data_set)
Get a table of resource parameters.
Definition: complex.c:429
char data[0]
Definition: cpg.c:10
A dumping ground.
#define CRMD_ACTION_STOP
Definition: crm.h:177
#define CRM_OP_SHUTDOWN
Definition: crm.h:143
#define RSC_NOTIFY
Definition: crm.h:210
#define CRMD_ACTION_MIGRATED
Definition: crm.h:172
#define CRM_FEATURE_SET
Definition: crm.h:69
#define CRM_OP_CLEAR_FAILCOUNT
Definition: crm.h:153
#define RSC_STOP
Definition: crm.h:202
#define CRM_OP_MAINTENANCE_NODES
Definition: crm.h:158
#define RSC_STATUS
Definition: crm.h:213
#define CRM_OP_LRM_DELETE
Definition: crm.h:149
#define RSC_MIGRATE
Definition: crm.h:196
#define CRM_OP_FENCE
Definition: crm.h:144
#define PCMK__XA_MODE
Definition: crm_internal.h:83
G_GNUC_INTERNAL void pcmk__substitute_remote_addr(pe_resource_t *rsc, GHashTable *params)
G_GNUC_INTERNAL pe_node_t * pcmk__connection_host_for_action(pe_action_t *action)
G_GNUC_INTERNAL void pcmk__log_action(const char *pre_text, pe_action_t *action, bool details)
G_GNUC_INTERNAL bool pcmk__action_locks_rsc_to_node(const pe_action_t *action)
G_GNUC_INTERNAL void pcmk__add_bundle_meta_to_xml(xmlNode *args_xml, pe_action_t *action)
Add special bundle meta-attributes to XML.
G_GNUC_INTERNAL void pcmk__deduplicate_action_inputs(pe_action_t *action)
#define crm_warn(fmt, args...)
Definition: logging.h:360
#define crm_crit(fmt, args...)
Definition: logging.h:358
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:211
#define crm_notice(fmt, args...)
Definition: logging.h:361
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:227
#define crm_debug(fmt, args...)
Definition: logging.h:364
#define crm_err(fmt, args...)
Definition: logging.h:359
gboolean crm_config_error
Definition: utils.c:49
#define crm_log_xml_trace(xml, text)
Definition: logging.h:373
#define crm_trace(fmt, args...)
Definition: logging.h:365
#define XML_TAG_CIB
Definition: msg_xml.h:115
#define XML_LRM_ATTR_ROUTER_NODE
Definition: msg_xml.h:308
#define ID(x)
Definition: msg_xml.h:468
#define XML_ATTR_CRM_VERSION
Definition: msg_xml.h:118
#define XML_ATTR_UUID
Definition: msg_xml.h:158
#define XML_CIB_ATTR_PRIORITY
Definition: msg_xml.h:275
#define XML_LRM_ATTR_TASK_KEY
Definition: msg_xml.h:301
#define XML_CONFIG_ATTR_SHUTDOWN_LOCK
Definition: msg_xml.h:401
#define XML_LRM_ATTR_TARGET_UUID
Definition: msg_xml.h:303
#define XML_ATTR_ID
Definition: msg_xml.h:134
#define XML_AGENT_ATTR_PROVIDER
Definition: msg_xml.h:270
#define XML_AGENT_ATTR_CLASS
Definition: msg_xml.h:269
#define XML_TAG_GRAPH
Definition: msg_xml.h:328
#define XML_NODE_IS_MAINTENANCE
Definition: msg_xml.h:284
#define XML_GRAPH_TAG_CRM_EVENT
Definition: msg_xml.h:331
#define XML_TAG_ATTRS
Definition: msg_xml.h:211
#define XML_ATTR_ID_LONG
Definition: msg_xml.h:137
#define XML_GRAPH_TAG_DOWNED
Definition: msg_xml.h:332
#define XML_LRM_ATTR_TASK
Definition: msg_xml.h:300
#define XML_ATTR_TYPE
Definition: msg_xml.h:138
#define XML_LRM_ATTR_TARGET
Definition: msg_xml.h:302
#define XML_GRAPH_TAG_PSEUDO_EVENT
Definition: msg_xml.h:330
#define XML_GRAPH_TAG_MAINTENANCE
Definition: msg_xml.h:333
#define XML_GRAPH_TAG_RSC_OP
Definition: msg_xml.h:329
#define XML_CIB_TAG_NODE
Definition: msg_xml.h:205
#define XML_LRM_ATTR_INTERVAL_MS
Definition: msg_xml.h:298
pe_working_set_t * data_set
xmlNode * input
void hash2metafield(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry, as meta-attribute name.
Definition: nvpair.c:798
void hash2field(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry.
Definition: nvpair.c:770
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
Definition: nvpair.c:419
void hash2smartfield(gpointer key, gpointer value, gpointer user_data)
Add hash table entry to XML as (possibly legacy) name/value.
Definition: nvpair.c:736
const char * crm_xml_add_ll(xmlNode *node, const char *name, long long value)
Create an XML attribute with specified name and long long int value.
Definition: nvpair.c:469
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: nvpair.c:323
const char * action
Definition: pcmk_fence.c:30
#define action_type_str(flags)
#define action_node_str(a)
#define action_runnable_str(flags)
bool pcmk__graph_has_loop(pe_action_t *init_action, pe_action_t *action, pe_action_wrapper_t *input)
void pcmk__add_rsc_actions_to_graph(pe_resource_t *rsc)
void pcmk__create_graph(pe_working_set_t *data_set)
void pcmk__log_transition_summary(const char *filename)
#define action_optional_str(flags)
@ no_quorum_ignore
Definition: pe_types.h:66
#define pe_flag_have_quorum
Definition: pe_types.h:95
#define pe_rsc_managed
Definition: pe_types.h:257
@ pe_order_implies_first_printed
Definition: pe_types.h:508
@ pe_order_anti_colocation
Definition: pe_types.h:514
@ pe_order_one_or_more
Definition: pe_types.h:513
@ pe_order_none
Definition: pe_types.h:480
@ pe_order_implies_first_migratable
Definition: pe_types.h:489
@ pe_order_implies_then_printed
Definition: pe_types.h:509
@ pe_order_optional
Definition: pe_types.h:481
@ pe_order_load
Definition: pe_types.h:512
@ pe_order_apply_first_non_migratable
Definition: pe_types.h:482
#define pe_rsc_unique
Definition: pe_types.h:262
#define pe_rsc_orphan
Definition: pe_types.h:256
@ pe_link_dumped
Internal tracking for transition graph creation.
Definition: pe_types.h:468
#define pe_flag_start_failure_fatal
Definition: pe_types.h:108
@ pe_action_optional
Definition: pe_types.h:301
@ pe_action_tracking
Definition: pe_types.h:321
@ pe_action_runnable
Definition: pe_types.h:300
@ pe_action_dumped
Definition: pe_types.h:308
@ pe_action_dedup
Definition: pe_types.h:322
@ pe_action_pseudo
Definition: pe_types.h:299
@ pe_action_print_always
Definition: pe_types.h:302
@ pe_action_dc
Internal state tracking when creating graph.
Definition: pe_types.h:324
@ pe_action_migrate_runnable
Definition: pe_types.h:306
@ pe_native
Definition: pe_types.h:38
#define pe_rsc_maintenance
Definition: pe_types.h:290
#define pe_rsc_failed
Definition: pe_types.h:276
pe_action_t * get_pseudo_op(const char *name, pe_working_set_t *data_set)
Definition: pe_actions.c:977
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:47
#define pe__clear_action_flags(action, flags_to_clear)
Definition: internal.h:95
#define pe_err(fmt...)
Definition: internal.h:49
#define pe__set_action_flags(action, flags_to_set)
Definition: internal.h:86
bool pe__is_guest_node(const pe_node_t *node)
Definition: remote.c:33
bool pe__is_guest_or_remote_node(const pe_node_t *node)
Definition: remote.c:41
void pe_foreach_guest_node(const pe_working_set_t *data_set, const pe_node_t *host, void(*helper)(const pe_node_t *, void *), void *user_data)
Definition: remote.c:120
#define CRM_ASSERT(expr)
Definition: results.h:42
@ pcmk_rc_ok
Definition: results.h:148
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition: strings.c:97
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:928
@ pcmk__str_none
@ pcmk__str_null_matches
@ pcmk__str_casei
bool pcmk__ends_with(const char *s, const char *match)
Definition: strings.c:536
int pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val, guint *result)
Definition: strings.c:311
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:952
char * uuid
Definition: pe_types.h:411
pe_node_t * node
Definition: pe_types.h:407
enum pe_ordering type
Definition: pe_types.h:528
struct pe_node_shared_s * details
Definition: pe_types.h:252
const char * id
Definition: pe_types.h:215
const char * uname
Definition: pe_types.h:216
GList * actions
Definition: pe_types.h:366
GList * children
Definition: pe_types.h:384
pe_working_set_t * cluster
Definition: pe_types.h:335
char * id
Definition: pe_types.h:329
resource_alloc_functions_t * cmds
Definition: pe_types.h:341
GHashTable * config_hash
Definition: pe_types.h:158
GList * actions
Definition: pe_types.h:171
GList * resources
Definition: pe_types.h:165
xmlNode * graph
Definition: pe_types.h:183
unsigned long long flags
Definition: pe_types.h:153
enum pe_quorum_policy no_quorum_policy
Definition: pe_types.h:156
GList * nodes
Definition: pe_types.h:164
time_t recheck_by
Definition: pe_types.h:194
void(* add_actions_to_graph)(pe_resource_t *rsc)
Wrappers for and extensions to libxml2.
void free_xml(xmlNode *child)
Definition: xml.c:885
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:749
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition: xml.c:2898