pacemaker 2.1.4-dc6eb4362e
Scalable High-Availability cluster resource manager
pcmk_graph_producer.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2021 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
113void
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
340 pcmk__substitute_remote_addr(action->rsc, params, action->rsc->cluster);
341
342 g_hash_table_foreach(params, hash2smartfield, args_xml);
343
344#if ENABLE_VERSIONED_ATTRS
345 {
346 xmlNode *versioned_parameters = create_xml_node(NULL, XML_TAG_RSC_VER_ATTRS);
347
348 pe_get_versioned_attributes(versioned_parameters, action->rsc,
349 action->node, action->rsc->cluster);
350 if (xml_has_children(versioned_parameters)) {
351 add_node_copy(action_xml, versioned_parameters);
352 }
353 free_xml(versioned_parameters);
354 }
355#endif
356
357 } else if ((action->rsc != NULL) && (action->rsc->variant <= pe_native)) {
358 GHashTable *params = pe_rsc_params(action->rsc, NULL,
359 action->rsc->cluster);
360
361 g_hash_table_foreach(params, hash2smartfield, args_xml);
362
363#if ENABLE_VERSIONED_ATTRS
364 if (xml_has_children(action->rsc->versioned_parameters)) {
365 add_node_copy(action_xml, action->rsc->versioned_parameters);
366 }
367#endif
368 }
369
370#if ENABLE_VERSIONED_ATTRS
371 if (rsc_details != NULL) {
372 if (xml_has_children(rsc_details->versioned_parameters)) {
373 add_node_copy(action_xml, rsc_details->versioned_parameters);
374 }
375 if (xml_has_children(rsc_details->versioned_meta)) {
376 add_node_copy(action_xml, rsc_details->versioned_meta);
377 }
378 }
379#endif
380
381 g_hash_table_foreach(action->meta, hash2metafield, args_xml);
382 if (action->rsc != NULL) {
383 const char *value = g_hash_table_lookup(action->rsc->meta,
384 "external-ip");
386
387 while (parent != NULL) {
388 parent->cmds->append_meta(parent, args_xml);
389 parent = parent->parent;
390 }
391
392 if (value != NULL) {
393 hash2smartfield((gpointer) "pcmk_external_ip", (gpointer) value,
394 (gpointer) args_xml);
395 }
396
398
399 } else if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_none)
400 && (action->node != NULL)) {
401 /* Pass the node's attributes as meta-attributes.
402 *
403 * @TODO: Determine whether it is still necessary to do this. It was
404 * added in 33d99707, probably for the libfence-based implementation in
405 * c9a90bd, which is no longer used.
406 */
407 g_hash_table_foreach(action->node->details->attrs, hash2metafield, args_xml);
408 }
409
410 sorted_xml(args_xml, action_xml, FALSE);
411 free_xml(args_xml);
412}
413
423static void
424create_graph_action(xmlNode *parent, pe_action_t *action, bool skip_details,
426{
427 bool needs_node_info = true;
428 bool needs_maintenance_info = false;
429 xmlNode *action_xml = NULL;
430#if ENABLE_VERSIONED_ATTRS
431 pe_rsc_action_details_t *rsc_details = NULL;
432#endif
433
434 if ((action == NULL) || (data_set == NULL)) {
435 return;
436 }
437
438 // Create the top-level element based on task
439
440 if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_casei)) {
441 /* All fences need node info; guest node fences are pseudo-events */
442 action_xml = create_xml_node(parent,
446
447 } else if (pcmk__str_any_of(action->task,
450 CRM_OP_LRM_REFRESH, NULL)) {
452
453 } else if (pcmk__str_eq(action->task, CRM_OP_LRM_DELETE, pcmk__str_none)) {
454 // CIB-only clean-up for shutdown locks
457
458 } else if (pcmk_is_set(action->flags, pe_action_pseudo)) {
459 if (pcmk__str_eq(action->task, CRM_OP_MAINTENANCE_NODES,
461 needs_maintenance_info = true;
462 }
464 needs_node_info = false;
465
466 } else {
468#if ENABLE_VERSIONED_ATTRS
469 rsc_details = pe_rsc_action_details(action);
470#endif
471 }
472
473 crm_xml_add_int(action_xml, XML_ATTR_ID, action->id);
474 crm_xml_add(action_xml, XML_LRM_ATTR_TASK, action->task);
475
476 if ((action->rsc != NULL) && (action->rsc->clone_name != NULL)) {
477 char *clone_key = NULL;
478 guint interval_ms;
479
481 &interval_ms) != pcmk_rc_ok) {
482 interval_ms = 0;
483 }
484 clone_key = clone_op_key(action, interval_ms);
485 crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, clone_key);
486 crm_xml_add(action_xml, "internal_" XML_LRM_ATTR_TASK_KEY, action->uuid);
487 free(clone_key);
488 } else {
489 crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, action->uuid);
490 }
491
492 if (needs_node_info && (action->node != NULL)) {
493 add_node_details(action, action_xml);
494 g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET),
495 strdup(action->node->details->uname));
496 g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET_UUID),
497 strdup(action->node->details->id));
498 }
499
500 if (skip_details) {
501 return;
502 }
503
504 if ((action->rsc != NULL)
505 && !pcmk_is_set(action->flags, pe_action_pseudo)) {
506
507 // This is a real resource action, so add resource details
508 add_resource_details(action, action_xml);
509 }
510
511 /* List any attributes in effect */
512 add_action_attributes(action, action_xml);
513
514 /* List any nodes this action is expected to make down */
515 if (needs_node_info && (action->node != NULL)) {
516 add_downed_nodes(action_xml, action, data_set);
517 }
518
519 if (needs_maintenance_info) {
520 add_maintenance_nodes(action_xml, data_set);
521 }
522}
523
532static bool
533should_add_action_to_graph(pe_action_t *action)
534{
535 if (!pcmk_is_set(action->flags, pe_action_runnable)) {
536 crm_trace("Ignoring action %s (%d): unrunnable",
537 action->uuid, action->id);
538 return false;
539 }
540
543 crm_trace("Ignoring action %s (%d): optional",
544 action->uuid, action->id);
545 return false;
546 }
547
548 /* Actions for unmanaged resources should be excluded from the graph,
549 * with the exception of monitors and cancellation of recurring monitors.
550 */
551 if ((action->rsc != NULL)
552 && !pcmk_is_set(action->rsc->flags, pe_rsc_managed)
553 && !pcmk__str_eq(action->task, RSC_STATUS, pcmk__str_none)) {
554 const char *interval_ms_s;
555
556 /* A cancellation of a recurring monitor will get here because the task
557 * is cancel rather than monitor, but the interval can still be used to
558 * recognize it. The interval has been normalized to milliseconds by
559 * this point, so a string comparison is sufficient.
560 */
561 interval_ms_s = g_hash_table_lookup(action->meta,
563 if (pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches)) {
564 crm_trace("Ignoring action %s (%d): for unmanaged resource (%s)",
565 action->uuid, action->id, action->rsc->id);
566 return false;
567 }
568 }
569
570 /* Always add pseudo-actions, fence actions, and shutdown actions (already
571 * determined to be required and runnable by this point)
572 */
575 NULL)) {
576 return true;
577 }
578
579 if (action->node == NULL) {
580 pe_err("Skipping action %s (%d) "
581 "because it was not allocated to a node (bug?)",
582 action->uuid, action->id);
583 pcmk__log_action("Unallocated", action, false);
584 return false;
585 }
586
587 if (pcmk_is_set(action->flags, pe_action_dc)) {
588 crm_trace("Action %s (%d) should be dumped: "
589 "can run on DC instead of %s",
590 action->uuid, action->id, action->node->details->uname);
591
592 } else if (pe__is_guest_node(action->node)
593 && !action->node->details->remote_requires_reset) {
594 crm_trace("Action %s (%d) should be dumped: "
595 "assuming will be runnable on guest node %s",
596 action->uuid, action->id, action->node->details->uname);
597
598 } else if (!action->node->details->online) {
599 pe_err("Skipping action %s (%d) "
600 "because it was scheduled for offline node (bug?)",
601 action->uuid, action->id);
602 pcmk__log_action("Offline node", action, false);
603 return false;
604
605 } else if (action->node->details->unclean) {
606 pe_err("Skipping action %s (%d) "
607 "because it was scheduled for unclean node (bug?)",
608 action->uuid, action->id);
609 pcmk__log_action("Unclean node", action, false);
610 return false;
611 }
612 return true;
613}
614
623static bool
624ordering_can_change_actions(pe_action_wrapper_t *ordering)
625{
626 return pcmk_any_flags_set(ordering->type, ~(pe_order_implies_first_printed
629}
630
642static bool
643should_add_input_to_graph(pe_action_t *action, pe_action_wrapper_t *input)
644{
645 if (input->state == pe_link_dumped) {
646 return true;
647 }
648
649 if (input->type == pe_order_none) {
650 crm_trace("Ignoring %s (%d) input %s (%d): "
651 "ordering disabled",
652 action->uuid, action->id,
653 input->action->uuid, input->action->id);
654 return false;
655
656 } else if (!pcmk_is_set(input->action->flags, pe_action_runnable)
657 && !ordering_can_change_actions(input)) {
658 crm_trace("Ignoring %s (%d) input %s (%d): "
659 "optional and input unrunnable",
660 action->uuid, action->id,
661 input->action->uuid, input->action->id);
662 return false;
663
664 } else if (!pcmk_is_set(input->action->flags, pe_action_runnable)
666 crm_trace("Ignoring %s (%d) input %s (%d): "
667 "one-or-more and input unrunnable",
668 action->uuid, action->id,
669 input->action->uuid, input->action->id);
670 return false;
671
673 && !pcmk_is_set(input->action->flags, pe_action_runnable)) {
674 crm_trace("Ignoring %s (%d) input %s (%d): "
675 "implies input migratable but input unrunnable",
676 action->uuid, action->id,
677 input->action->uuid, input->action->id);
678 return false;
679
681 && pcmk_is_set(input->action->flags, pe_action_migrate_runnable)) {
682 crm_trace("Ignoring %s (%d) input %s (%d): "
683 "only if input unmigratable but input unrunnable",
684 action->uuid, action->id,
685 input->action->uuid, input->action->id);
686 return false;
687
688 } else if ((input->type == pe_order_optional)
690 && pcmk__ends_with(input->action->uuid, "_stop_0")) {
691 crm_trace("Ignoring %s (%d) input %s (%d): "
692 "optional but stop in migration",
693 action->uuid, action->id,
694 input->action->uuid, input->action->id);
695 return false;
696
697 } else if (input->type == pe_order_load) {
698 pe_node_t *input_node = input->action->node;
699
700 // load orderings are relevant only if actions are for same node
701
702 if (action->rsc && pcmk__str_eq(action->task, RSC_MIGRATE, pcmk__str_casei)) {
703 pe_node_t *allocated = action->rsc->allocated_to;
704
705 /* For load_stopped -> migrate_to orderings, we care about where it
706 * has been allocated to, not where it will be executed.
707 */
708 if ((input_node == NULL) || (allocated == NULL)
709 || (input_node->details != allocated->details)) {
710 crm_trace("Ignoring %s (%d) input %s (%d): "
711 "load ordering node mismatch %s vs %s",
712 action->uuid, action->id,
713 input->action->uuid, input->action->id,
714 (allocated? allocated->details->uname : "<none>"),
715 (input_node? input_node->details->uname : "<none>"));
716 input->type = pe_order_none;
717 return false;
718 }
719
720 } else if ((input_node == NULL) || (action->node == NULL)
721 || (input_node->details != action->node->details)) {
722 crm_trace("Ignoring %s (%d) input %s (%d): "
723 "load ordering node mismatch %s vs %s",
724 action->uuid, action->id,
725 input->action->uuid, input->action->id,
726 (action->node? action->node->details->uname : "<none>"),
727 (input_node? input_node->details->uname : "<none>"));
728 input->type = pe_order_none;
729 return false;
730
731 } else if (pcmk_is_set(input->action->flags, pe_action_optional)) {
732 crm_trace("Ignoring %s (%d) input %s (%d): "
733 "load ordering input optional",
734 action->uuid, action->id,
735 input->action->uuid, input->action->id);
736 input->type = pe_order_none;
737 return false;
738 }
739
740 } else if (input->type == pe_order_anti_colocation) {
741 if (input->action->node && action->node
742 && (input->action->node->details != action->node->details)) {
743 crm_trace("Ignoring %s (%d) input %s (%d): "
744 "anti-colocation node mismatch %s vs %s",
745 action->uuid, action->id,
746 input->action->uuid, input->action->id,
747 action->node->details->uname,
748 input->action->node->details->uname);
749 input->type = pe_order_none;
750 return false;
751
752 } else if (pcmk_is_set(input->action->flags, pe_action_optional)) {
753 crm_trace("Ignoring %s (%d) input %s (%d): "
754 "anti-colocation input optional",
755 action->uuid, action->id,
756 input->action->uuid, input->action->id);
757 input->type = pe_order_none;
758 return false;
759 }
760
761 } else if (input->action->rsc
762 && input->action->rsc != action->rsc
763 && pcmk_is_set(input->action->rsc->flags, pe_rsc_failed)
764 && !pcmk_is_set(input->action->rsc->flags, pe_rsc_managed)
765 && pcmk__ends_with(input->action->uuid, "_stop_0")
766 && action->rsc && pe_rsc_is_clone(action->rsc)) {
767 crm_warn("Ignoring requirement that %s complete before %s:"
768 " unmanaged failed resources cannot prevent clone shutdown",
769 input->action->uuid, action->uuid);
770 return false;
771
772 } else if (pcmk_is_set(input->action->flags, pe_action_optional)
773 && !pcmk_any_flags_set(input->action->flags,
775 && !should_add_action_to_graph(input->action)) {
776 crm_trace("Ignoring %s (%d) input %s (%d): "
777 "input optional",
778 action->uuid, action->id,
779 input->action->uuid, input->action->id);
780 return false;
781 }
782
783 crm_trace("%s (%d) input %s %s (%d) on %s should be dumped: %s %s %#.6x",
784 action->uuid, action->id, action_type_str(input->action->flags),
785 input->action->uuid, input->action->id,
786 action_node_str(input->action),
787 action_runnable_str(input->action->flags),
788 action_optional_str(input->action->flags), input->type);
789 return true;
790}
791
804bool
807{
808 bool has_loop = false;
809
810 if (pcmk_is_set(input->action->flags, pe_action_tracking)) {
811 crm_trace("Breaking tracking loop: %s@%s -> %s@%s (%#.6x)",
812 input->action->uuid,
813 input->action->node? input->action->node->details->uname : "",
814 action->uuid,
815 action->node? action->node->details->uname : "",
816 input->type);
817 return false;
818 }
819
820 // Don't need to check inputs that won't be used
821 if (!should_add_input_to_graph(action, input)) {
822 return false;
823 }
824
825 if (input->action == init_action) {
826 crm_debug("Input loop found in %s@%s ->...-> %s@%s",
827 action->uuid,
828 action->node? action->node->details->uname : "",
829 init_action->uuid,
830 init_action->node? init_action->node->details->uname : "");
831 return true;
832 }
833
835
836 crm_trace("Checking inputs of action %s@%s input %s@%s (%#.6x)"
837 "for graph loop with %s@%s ",
838 action->uuid,
839 action->node? action->node->details->uname : "",
840 input->action->uuid,
841 input->action->node? input->action->node->details->uname : "",
842 input->type,
843 init_action->uuid,
844 init_action->node? init_action->node->details->uname : "");
845
846 // Recursively check input itself for loops
847 for (GList *iter = input->action->actions_before;
848 iter != NULL; iter = iter->next) {
849
850 if (pcmk__graph_has_loop(init_action, input->action,
851 (pe_action_wrapper_t *) iter->data)) {
852 // Recursive call already logged a debug message
853 has_loop = true;
854 break;
855 }
856 }
857
859
860 if (!has_loop) {
861 crm_trace("No input loop found in %s@%s -> %s@%s (%#.6x)",
862 input->action->uuid,
863 input->action->node? input->action->node->details->uname : "",
864 action->uuid,
865 action->node? action->node->details->uname : "",
866 input->type);
867 }
868 return has_loop;
869}
870
880static xmlNode *
881create_graph_synapse(pe_action_t *action, pe_working_set_t *data_set)
882{
883 int synapse_priority = 0;
884 xmlNode *syn = create_xml_node(data_set->graph, "synapse");
885
888
889 if (action->rsc != NULL) {
890 synapse_priority = action->rsc->priority;
891 }
892 if (action->priority > synapse_priority) {
893 synapse_priority = action->priority;
894 }
895 if (synapse_priority > 0) {
896 crm_xml_add_int(syn, XML_CIB_ATTR_PRIORITY, synapse_priority);
897 }
898 return syn;
899}
900
917void
919{
920 xmlNode *syn = NULL;
921 xmlNode *set = NULL;
922 xmlNode *in = NULL;
923
924 /* If we haven't already, de-duplicate inputs (even if we won't be adding
925 * the action to the graph, so that crm_simulate's dot graphs don't have
926 * duplicates).
927 */
928 if (!pcmk_is_set(action->flags, pe_action_dedup)) {
931 }
932
933 if (pcmk_is_set(action->flags, pe_action_dumped) // Already added, or
934 || !should_add_action_to_graph(action)) { // shouldn't be added
935 return;
936 }
938
939 syn = create_graph_synapse(action, data_set);
940 set = create_xml_node(syn, "action_set");
941 in = create_xml_node(syn, "inputs");
942
943 create_graph_action(set, action, false, data_set);
944
945 for (GList *lpc = action->actions_before; lpc != NULL; lpc = lpc->next) {
947
948 if (should_add_input_to_graph(action, input)) {
949 xmlNode *input_xml = create_xml_node(in, "trigger");
950
951 input->state = pe_link_dumped;
952 create_graph_action(input_xml, input->action, true, data_set);
953 }
954 }
955}
956
957static int transition_id = -1;
958
965void
966pcmk__log_transition_summary(const char *filename)
967{
969 crm_err("Calculated transition %d (with errors)%s%s",
970 transition_id,
971 (filename == NULL)? "" : ", saving inputs in ",
972 (filename == NULL)? "" : filename);
973
974 } else if (was_processing_warning) {
975 crm_warn("Calculated transition %d (with warnings)%s%s",
976 transition_id,
977 (filename == NULL)? "" : ", saving inputs in ",
978 (filename == NULL)? "" : filename);
979
980 } else {
981 crm_notice("Calculated transition %d%s%s",
982 transition_id,
983 (filename == NULL)? "" : ", saving inputs in ",
984 (filename == NULL)? "" : filename);
985 }
986 if (crm_config_error) {
987 crm_notice("Configuration errors found during scheduler processing,"
988 " please run \"crm_verify -L\" to identify issues");
989 }
990}
991
998void
1000{
1001 GList *iter = NULL;
1002 const char *value = NULL;
1003 long long limit = 0LL;
1004
1005 transition_id++;
1006 crm_trace("Creating transition graph %d", transition_id);
1007
1009
1010 value = pe_pref(data_set->config_hash, "cluster-delay");
1011 crm_xml_add(data_set->graph, "cluster-delay", value);
1012
1013 value = pe_pref(data_set->config_hash, "stonith-timeout");
1014 crm_xml_add(data_set->graph, "stonith-timeout", value);
1015
1016 crm_xml_add(data_set->graph, "failed-stop-offset", "INFINITY");
1017
1019 crm_xml_add(data_set->graph, "failed-start-offset", "INFINITY");
1020 } else {
1021 crm_xml_add(data_set->graph, "failed-start-offset", "1");
1022 }
1023
1024 value = pe_pref(data_set->config_hash, "batch-limit");
1025 crm_xml_add(data_set->graph, "batch-limit", value);
1026
1027 crm_xml_add_int(data_set->graph, "transition_id", transition_id);
1028
1029 value = pe_pref(data_set->config_hash, "migration-limit");
1030 if ((pcmk__scan_ll(value, &limit, 0LL) == pcmk_rc_ok) && (limit > 0)) {
1031 crm_xml_add(data_set->graph, "migration-limit", value);
1032 }
1033
1034 if (data_set->recheck_by > 0) {
1035 char *recheck_epoch = NULL;
1036
1037 recheck_epoch = crm_strdup_printf("%llu",
1038 (long long) data_set->recheck_by);
1039 crm_xml_add(data_set->graph, "recheck-by", recheck_epoch);
1040 free(recheck_epoch);
1041 }
1042
1043 /* The following code will de-duplicate action inputs, so nothing past this
1044 * should rely on the action input type flags retaining their original
1045 * values.
1046 */
1047
1048 // Add resource actions to graph
1049 for (iter = data_set->resources; iter != NULL; iter = iter->next) {
1050 pe_resource_t *rsc = (pe_resource_t *) iter->data;
1051
1052 pe_rsc_trace(rsc, "Processing actions for %s", rsc->id);
1053 rsc->cmds->expand(rsc, data_set);
1054 }
1055
1056 // Add pseudo-action for list of nodes with maintenance state update
1058
1059 // Add non-resource (node) actions
1060 for (iter = data_set->actions; iter != NULL; iter = iter->next) {
1061 pe_action_t *action = (pe_action_t *) iter->data;
1062
1063 if ((action->rsc != NULL)
1064 && (action->node != NULL)
1065 && action->node->details->shutdown
1066 && !pcmk_is_set(action->rsc->flags, pe_rsc_maintenance)
1067 && !pcmk_any_flags_set(action->flags,
1069 && pcmk__str_eq(action->task, RSC_STOP, pcmk__str_none)) {
1070 /* Eventually we should just ignore the 'fence' case, but for now
1071 * it's the best way to detect (in CTS) when CIB resource updates
1072 * are being lost.
1073 */
1076 crm_crit("Cannot %s node '%s' because of %s:%s%s (%s)",
1077 action->node->details->unclean? "fence" : "shut down",
1078 action->node->details->uname, action->rsc->id,
1079 pcmk_is_set(action->rsc->flags, pe_rsc_managed)? " blocked" : " unmanaged",
1080 pcmk_is_set(action->rsc->flags, pe_rsc_failed)? " failed" : "",
1081 action->uuid);
1082 }
1083 }
1084
1086 }
1087
1088 crm_log_xml_trace(data_set->graph, "graph");
1089}
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:536
#define PCMK__NELEM(a)
Definition: internal.h:42
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:122
gboolean was_processing_error
Definition: common.c:20
const char * pe_pref(GHashTable *options, const char *name)
Definition: common.c:305
gboolean was_processing_warning
Definition: common.c:21
GHashTable * pe_rsc_params(pe_resource_t *rsc, pe_node_t *node, pe_working_set_t *data_set)
Get a table of resource parameters.
Definition: complex.c:457
A dumping ground.
#define CRMD_ACTION_STOP
Definition: crm.h:179
#define CRM_OP_LRM_REFRESH
Definition: crm.h:149
#define CRM_OP_SHUTDOWN
Definition: crm.h:144
#define RSC_NOTIFY
Definition: crm.h:212
#define CRMD_ACTION_MIGRATED
Definition: crm.h:174
#define CRM_FEATURE_SET
Definition: crm.h:69
#define CRM_OP_CLEAR_FAILCOUNT
Definition: crm.h:155
#define RSC_STOP
Definition: crm.h:204
#define CRM_OP_MAINTENANCE_NODES
Definition: crm.h:160
#define RSC_STATUS
Definition: crm.h:215
#define CRM_OP_LRM_DELETE
Definition: crm.h:151
#define RSC_MIGRATE
Definition: crm.h:198
#define CRM_OP_FENCE
Definition: crm.h:145
#define PCMK__XA_MODE
Definition: crm_internal.h:79
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__substitute_remote_addr(pe_resource_t *rsc, GHashTable *params, pe_working_set_t *data_set)
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:359
#define crm_crit(fmt, args...)
Definition: logging.h:357
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:210
#define crm_notice(fmt, args...)
Definition: logging.h:360
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:226
#define crm_debug(fmt, args...)
Definition: logging.h:363
#define crm_err(fmt, args...)
Definition: logging.h:358
gboolean crm_config_error
Definition: utils.c:52
#define crm_log_xml_trace(xml, text)
Definition: logging.h:372
#define crm_trace(fmt, args...)
Definition: logging.h:364
#define XML_TAG_CIB
Definition: msg_xml.h:115
#define XML_LRM_ATTR_ROUTER_NODE
Definition: msg_xml.h:311
#define ID(x)
Definition: msg_xml.h:460
#define XML_ATTR_CRM_VERSION
Definition: msg_xml.h:118
#define XML_ATTR_UUID
Definition: msg_xml.h:158
#define XML_TAG_RSC_VER_ATTRS
Definition: msg_xml.h:212
#define XML_CIB_ATTR_PRIORITY
Definition: msg_xml.h:278
#define XML_LRM_ATTR_TASK_KEY
Definition: msg_xml.h:304
#define XML_CONFIG_ATTR_SHUTDOWN_LOCK
Definition: msg_xml.h:393
#define XML_LRM_ATTR_TARGET_UUID
Definition: msg_xml.h:306
#define XML_ATTR_ID
Definition: msg_xml.h:135
#define XML_AGENT_ATTR_PROVIDER
Definition: msg_xml.h:273
#define XML_AGENT_ATTR_CLASS
Definition: msg_xml.h:272
#define XML_TAG_GRAPH
Definition: msg_xml.h:331
#define XML_NODE_IS_MAINTENANCE
Definition: msg_xml.h:287
#define XML_GRAPH_TAG_CRM_EVENT
Definition: msg_xml.h:334
#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:335
#define XML_LRM_ATTR_TASK
Definition: msg_xml.h:303
#define XML_ATTR_TYPE
Definition: msg_xml.h:138
#define XML_LRM_ATTR_TARGET
Definition: msg_xml.h:305
#define XML_GRAPH_TAG_PSEUDO_EVENT
Definition: msg_xml.h:333
#define XML_GRAPH_TAG_MAINTENANCE
Definition: msg_xml.h:336
#define XML_GRAPH_TAG_RSC_OP
Definition: msg_xml.h:332
#define XML_CIB_TAG_NODE
Definition: msg_xml.h:205
#define XML_LRM_ATTR_INTERVAL_MS
Definition: msg_xml.h:301
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:810
void hash2field(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry.
Definition: nvpair.c:782
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:431
void hash2smartfield(gpointer key, gpointer value, gpointer user_data)
Add hash table entry to XML as (possibly legacy) name/value.
Definition: nvpair.c:748
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:481
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:29
void pcmk__add_action_to_graph(pe_action_t *action, pe_working_set_t *data_set)
#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__create_graph(pe_working_set_t *data_set)
void add_maintenance_update(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:526
@ pe_order_anti_colocation
Definition: pe_types.h:532
@ pe_order_one_or_more
Definition: pe_types.h:531
@ pe_order_none
Definition: pe_types.h:498
@ pe_order_implies_first_migratable
Definition: pe_types.h:507
@ pe_order_implies_then_printed
Definition: pe_types.h:527
@ pe_order_optional
Definition: pe_types.h:499
@ pe_order_load
Definition: pe_types.h:530
@ pe_order_apply_first_non_migratable
Definition: pe_types.h:500
#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:486
#define pe_flag_start_failure_fatal
Definition: pe_types.h:108
@ pe_action_optional
Definition: pe_types.h:308
@ pe_action_tracking
Definition: pe_types.h:328
@ pe_action_runnable
Definition: pe_types.h:307
@ pe_action_dumped
Definition: pe_types.h:315
@ pe_action_dedup
Definition: pe_types.h:329
@ pe_action_pseudo
Definition: pe_types.h:306
@ pe_action_print_always
Definition: pe_types.h:309
@ pe_action_dc
Internal state tracking when creating graph.
Definition: pe_types.h:331
@ pe_action_migrate_runnable
Definition: pe_types.h:313
@ 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: utils.c:1953
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:22
#define pe__clear_action_flags(action, flags_to_clear)
Definition: internal.h:70
#define pe_err(fmt...)
Definition: internal.h:24
#define pe__set_action_flags(action, flags_to_set)
Definition: internal.h:61
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:146
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:931
@ 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:955
char * uuid
Definition: pe_types.h:429
pe_node_t * node
Definition: pe_types.h:425
enum pe_ordering type
Definition: pe_types.h:546
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
char * id
Definition: pe_types.h:336
resource_alloc_functions_t * cmds
Definition: pe_types.h:348
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(* expand)(pe_resource_t *, pe_working_set_t *)
Wrappers for and extensions to libxml2.
gboolean xml_has_children(const xmlNode *root)
Definition: xml.c:2028
void free_xml(xmlNode *child)
Definition: xml.c:824
xmlNode * add_node_copy(xmlNode *new_parent, xmlNode *xml_node)
Definition: xml.c:674
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:696
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition: xml.c:2762