pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
pcmk_graph_producer.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2024 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/common/xml.h>
16
17#include <glib.h>
18
19#include <pacemaker-internal.h>
20
22
23// Convenience macros for logging action properties
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
44static xmlNode*
45add_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
62static void
63add_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
78static int
79add_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
96 || (!node->details->maintenance
98
99 if (maintenance != NULL) {
100 crm_xml_add(add_node_to_xml_by_id(node->priv->id,
101 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
119static void
120add_maintenance_update(pcmk_scheduler_t *scheduler)
121{
122 pcmk_action_t *action = NULL;
123
124 if (add_maintenance_nodes(NULL, scheduler) != 0) {
127 }
128}
129
141static void
142add_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 /* Shutdown makes the action's node down */
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,
155
156 /* Fencing makes the action's node and any hosted guest nodes down */
157 const char *fence = g_hash_table_lookup(action->meta,
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,
170 && pcmk__str_eq(action->task, PCMK_ACTION_STOP,
172
173 /* Stopping a remote connection resource makes connected node down,
174 * unless it's part of a migration
175 */
176 GList *iter;
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,
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
206static char *
207clone_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
229static void
230add_node_details(const pcmk_action_t *action, xmlNode *xml)
231{
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
248static void
249add_resource_details(const pcmk_action_t *action, xmlNode *action_xml)
250{
251 xmlNode *rsc_xml = NULL;
252 const char *attr_list[] = {
256 };
257
258 /* If a resource is locked to a node via PCMK_OPT_SHUTDOWN_LOCK, mark its
259 * actions so the controller can preserve the lock when the action
260 * completes.
261 */
264 (long long) action->rsc->priv->lock_time);
265 }
266
267 // List affected resource
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 /* Use the numbered instance name here, because if there is more
274 * than one instance on a node, we need to make sure the command
275 * goes to the right one.
276 *
277 * This is important even for anonymous clones, because the clone's
278 * unique meta-attribute might have just been toggled from on to
279 * off.
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 /* ID is what we'd like client to use
293 * LONG_ID is what they might know it as instead
294 *
295 * LONG_ID is only strictly needed /here/ during the
296 * transition period until all nodes in the cluster
297 * are running the new software /and/ have rebooted
298 * once (meaning that they've only ever spoken to a DC
299 * supporting this feature). (@TODO The effect of removing this on
300 * regression tests suggests that it is still needed for unique clones)
301 *
302 * If anyone toggles the unique flag to 'on', the
303 * 'instance free' name will correspond to an orphan
304 * and fall into the clause above instead
305 */
306 crm_xml_add(rsc_xml, PCMK_XA_ID, xml_id);
307 if ((action->rsc->priv->history_id != NULL)
308 && !pcmk__str_eq(xml_id, action->rsc->priv->history_id,
311 action->rsc->priv->history_id);
312 } else {
313 crm_xml_add(rsc_xml, PCMK__XA_LONG_ID, action->rsc->id);
314 }
315
316 } else {
317 pcmk__assert(action->rsc->priv->history_id == NULL);
318 crm_xml_add(rsc_xml, PCMK_XA_ID, action->rsc->id);
319 }
320
321 for (int lpc = 0; lpc < PCMK__NELEM(attr_list); lpc++) {
322 crm_xml_add(rsc_xml, attr_list[lpc],
323 g_hash_table_lookup(action->rsc->priv->meta,
324 attr_list[lpc]));
325 }
326}
327
335static void
336add_action_attributes(pcmk_action_t *action, xmlNode *action_xml)
337{
338 xmlNode *args_xml = NULL;
339 pcmk_resource_t *rsc = action->rsc;
340
341 /* We create free-standing XML to start, so we can sort the attributes
342 * before adding it to action_xml, which keeps the scheduler regression
343 * test graphs comparable.
344 */
345 args_xml = pcmk__xe_create(action_xml, PCMK__XE_ATTRIBUTES);
346
348 g_hash_table_foreach(action->extra, hash2field, args_xml);
349
350 if ((rsc != NULL) && (action->node != NULL)) {
351 // Get the resource instance attributes, evaluated properly for node
352 GHashTable *params = pe_rsc_params(rsc, action->node,
353 rsc->priv->scheduler);
354
355 pcmk__substitute_remote_addr(rsc, params);
356
357 g_hash_table_foreach(params, hash2smartfield, args_xml);
358
359 } else if ((rsc != NULL)
361 GHashTable *params = pe_rsc_params(rsc, NULL, rsc->priv->scheduler);
362
363 g_hash_table_foreach(params, hash2smartfield, args_xml);
364 }
365
366 g_hash_table_foreach(action->meta, hash2metafield, args_xml);
367 if (rsc != NULL) {
368 pcmk_resource_t *parent = rsc;
369
370 while (parent != NULL) {
371 parent->priv->cmds->add_graph_meta(parent, args_xml);
372 parent = parent->priv->parent;
373 }
374
376 }
377
378 pcmk__xe_sort_attrs(args_xml);
379}
380
390static void
391create_graph_action(xmlNode *parent, pcmk_action_t *action, bool skip_details,
393{
394 bool needs_node_info = true;
395 bool needs_maintenance_info = false;
396 xmlNode *action_xml = NULL;
397
398 if ((action == NULL) || (scheduler == NULL)) {
399 return;
400 }
401
402 // Create the top-level element based on task
403
404 if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH, pcmk__str_none)) {
405 /* All fences need node info; guest node fences are pseudo-events */
408 } else {
410 }
411
412 } else if (pcmk__str_any_of(action->task,
416
417 } else if (pcmk__str_eq(action->task, PCMK_ACTION_LRM_DELETE,
419 // CIB-only clean-up for shutdown locks
422
423 } else if (pcmk_is_set(action->flags, pcmk__action_pseudo)) {
424 if (pcmk__str_eq(action->task, PCMK_ACTION_MAINTENANCE_NODES,
426 needs_maintenance_info = true;
427 }
429 needs_node_info = false;
430
431 } else {
433 }
434
435 crm_xml_add_int(action_xml, PCMK_XA_ID, action->id);
436 crm_xml_add(action_xml, PCMK_XA_OPERATION, action->task);
437
438 if ((action->rsc != NULL) && (action->rsc->priv->history_id != NULL)) {
439 char *clone_key = NULL;
440 guint interval_ms;
441
443 &interval_ms) != pcmk_rc_ok) {
444 interval_ms = 0;
445 }
446 clone_key = clone_op_key(action, interval_ms);
447 crm_xml_add(action_xml, PCMK__XA_OPERATION_KEY, clone_key);
448 crm_xml_add(action_xml, "internal_" PCMK__XA_OPERATION_KEY,
449 action->uuid);
450 free(clone_key);
451 } else {
452 crm_xml_add(action_xml, PCMK__XA_OPERATION_KEY, action->uuid);
453 }
454
455 if (needs_node_info && (action->node != NULL)) {
456 add_node_details(action, action_xml);
458 action->node->priv->name);
460 action->node->priv->id);
461 }
462
463 if (skip_details) {
464 return;
465 }
466
467 if ((action->rsc != NULL)
469
470 // This is a real resource action, so add resource details
471 add_resource_details(action, action_xml);
472 }
473
474 /* List any attributes in effect */
475 add_action_attributes(action, action_xml);
476
477 /* List any nodes this action is expected to make down */
478 if (needs_node_info && (action->node != NULL)) {
479 add_downed_nodes(action_xml, action);
480 }
481
482 if (needs_maintenance_info) {
483 add_maintenance_nodes(action_xml, scheduler);
484 }
485}
486
495static bool
496should_add_action_to_graph(pcmk_action_t *action)
497{
499 crm_trace("Ignoring action %s (%d): unrunnable",
500 action->uuid, action->id);
501 return false;
502 }
503
506 crm_trace("Ignoring action %s (%d): optional",
507 action->uuid, action->id);
508 return false;
509 }
510
511 /* Actions for unmanaged resources should be excluded from the graph,
512 * with the exception of monitors and cancellation of recurring monitors.
513 */
514 if ((action->rsc != NULL)
515 && !pcmk_is_set(action->rsc->flags, pcmk__rsc_managed)
516 && !pcmk__str_eq(action->task, PCMK_ACTION_MONITOR, pcmk__str_none)) {
517
518 const char *interval_ms_s;
519
520 /* A cancellation of a recurring monitor will get here because the task
521 * is cancel rather than monitor, but the interval can still be used to
522 * recognize it. The interval has been normalized to milliseconds by
523 * this point, so a string comparison is sufficient.
524 */
525 interval_ms_s = g_hash_table_lookup(action->meta, PCMK_META_INTERVAL);
526 if (pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches)) {
527 crm_trace("Ignoring action %s (%d): for unmanaged resource (%s)",
528 action->uuid, action->id, action->rsc->id);
529 return false;
530 }
531 }
532
533 /* Always add pseudo-actions, fence actions, and shutdown actions (already
534 * determined to be required and runnable by this point)
535 */
539 return true;
540 }
541
542 if (action->node == NULL) {
543 pcmk__sched_err(action->scheduler,
544 "Skipping action %s (%d) "
545 "because it was not assigned to a node (bug?)",
546 action->uuid, action->id);
547 pcmk__log_action("Unassigned", action, false);
548 return false;
549 }
550
552 crm_trace("Action %s (%d) should be dumped: "
553 "can run on DC instead of %s",
554 action->uuid, action->id, pcmk__node_name(action->node));
555
556 } else if (pcmk__is_guest_or_bundle_node(action->node)
557 && !pcmk_is_set(action->node->priv->flags,
559 crm_trace("Action %s (%d) should be dumped: "
560 "assuming will be runnable on guest %s",
561 action->uuid, action->id, pcmk__node_name(action->node));
562
563 } else if (!action->node->details->online) {
564 pcmk__sched_err(action->scheduler,
565 "Skipping action %s (%d) "
566 "because it was scheduled for offline node (bug?)",
567 action->uuid, action->id);
568 pcmk__log_action("Offline node", action, false);
569 return false;
570
571 } else if (action->node->details->unclean) {
572 pcmk__sched_err(action->scheduler,
573 "Skipping action %s (%d) "
574 "because it was scheduled for unclean node (bug?)",
575 action->uuid, action->id);
576 pcmk__log_action("Unclean node", action, false);
577 return false;
578 }
579 return true;
580}
581
590static bool
591ordering_can_change_actions(const pcmk__related_action_t *ordering)
592{
593 return pcmk_any_flags_set(ordering->flags,
597}
598
610static bool
611should_add_input_to_graph(const pcmk_action_t *action,
613{
614 if (input->graphed) {
615 return true;
616 }
617
618 if (input->flags == pcmk__ar_none) {
619 crm_trace("Ignoring %s (%d) input %s (%d): "
620 "ordering disabled",
621 action->uuid, action->id,
622 input->action->uuid, input->action->id);
623 return false;
624
625 } else if (!pcmk_is_set(input->action->flags, pcmk__action_runnable)
626 && !ordering_can_change_actions(input)) {
627 crm_trace("Ignoring %s (%d) input %s (%d): "
628 "optional and input unrunnable",
629 action->uuid, action->id,
630 input->action->uuid, input->action->id);
631 return false;
632
633 } else if (!pcmk_is_set(input->action->flags, pcmk__action_runnable)
635 crm_trace("Ignoring %s (%d) input %s (%d): "
636 "minimum number of instances required but input unrunnable",
637 action->uuid, action->id,
638 input->action->uuid, input->action->id);
639 return false;
640
642 && !pcmk_is_set(input->action->flags, pcmk__action_runnable)) {
643 crm_trace("Ignoring %s (%d) input %s (%d): "
644 "input blocked if 'then' unmigratable",
645 action->uuid, action->id,
646 input->action->uuid, input->action->id);
647 return false;
648
650 && pcmk_is_set(input->action->flags, pcmk__action_migratable)) {
651 crm_trace("Ignoring %s (%d) input %s (%d): ordering applies "
652 "only if input is unmigratable, but it is migratable",
653 action->uuid, action->id,
654 input->action->uuid, input->action->id);
655 return false;
656
657 } else if ((input->flags == pcmk__ar_ordered)
658 && pcmk_is_set(input->action->flags, pcmk__action_migratable)
659 && pcmk__ends_with(input->action->uuid, "_stop_0")) {
660 crm_trace("Ignoring %s (%d) input %s (%d): "
661 "optional but stop in migration",
662 action->uuid, action->id,
663 input->action->uuid, input->action->id);
664 return false;
665
666 } else if (input->flags == pcmk__ar_if_on_same_node_or_target) {
667 pcmk_node_t *input_node = input->action->node;
668
669 if ((action->rsc != NULL)
670 && pcmk__str_eq(action->task, PCMK_ACTION_MIGRATE_TO,
672
673 pcmk_node_t *assigned = action->rsc->priv->assigned_node;
674
675 /* For load_stopped -> migrate_to orderings, we care about where
676 * the resource has been assigned, not where migrate_to will be
677 * executed.
678 */
679 if (!pcmk__same_node(input_node, assigned)) {
680 crm_trace("Ignoring %s (%d) input %s (%d): "
681 "migration target %s is not same as input node %s",
682 action->uuid, action->id,
683 input->action->uuid, input->action->id,
684 (assigned? assigned->priv->name : "<none>"),
685 (input_node? input_node->priv->name : "<none>"));
686 input->flags = pcmk__ar_none;
687 return false;
688 }
689
690 } else if (!pcmk__same_node(input_node, action->node)) {
691 crm_trace("Ignoring %s (%d) input %s (%d): "
692 "not on same node (%s vs %s)",
693 action->uuid, action->id,
694 input->action->uuid, input->action->id,
695 (action->node? action->node->priv->name : "<none>"),
696 (input_node? input_node->priv->name : "<none>"));
697 input->flags = pcmk__ar_none;
698 return false;
699
700 } else if (pcmk_is_set(input->action->flags, pcmk__action_optional)) {
701 crm_trace("Ignoring %s (%d) input %s (%d): "
702 "ordering optional",
703 action->uuid, action->id,
704 input->action->uuid, input->action->id);
705 input->flags = pcmk__ar_none;
706 return false;
707 }
708
709 } else if (input->flags == pcmk__ar_if_required_on_same_node) {
710 if (input->action->node && action->node
711 && !pcmk__same_node(input->action->node, action->node)) {
712 crm_trace("Ignoring %s (%d) input %s (%d): "
713 "not on same node (%s vs %s)",
714 action->uuid, action->id,
715 input->action->uuid, input->action->id,
716 pcmk__node_name(action->node),
717 pcmk__node_name(input->action->node));
718 input->flags = pcmk__ar_none;
719 return false;
720
721 } else if (pcmk_is_set(input->action->flags, pcmk__action_optional)) {
722 crm_trace("Ignoring %s (%d) input %s (%d): optional",
723 action->uuid, action->id,
724 input->action->uuid, input->action->id);
725 input->flags = pcmk__ar_none;
726 return false;
727 }
728
729 } else if (input->action->rsc
730 && input->action->rsc != action->rsc
731 && pcmk_is_set(input->action->rsc->flags, pcmk__rsc_failed)
732 && !pcmk_is_set(input->action->rsc->flags, pcmk__rsc_managed)
733 && pcmk__ends_with(input->action->uuid, "_stop_0")
734 && pcmk__is_clone(action->rsc)) {
735 crm_warn("Ignoring requirement that %s complete before %s:"
736 " unmanaged failed resources cannot prevent clone shutdown",
737 input->action->uuid, action->uuid);
738 return false;
739
740 } else if (pcmk_is_set(input->action->flags, pcmk__action_optional)
741 && !pcmk_any_flags_set(input->action->flags,
744 && !should_add_action_to_graph(input->action)) {
745 crm_trace("Ignoring %s (%d) input %s (%d): "
746 "input optional",
747 action->uuid, action->id,
748 input->action->uuid, input->action->id);
749 return false;
750 }
751
752 crm_trace("%s (%d) input %s %s (%d) on %s should be dumped: %s %s %#.6x",
753 action->uuid, action->id, action_type_str(input->action->flags),
754 input->action->uuid, input->action->id,
755 action_node_str(input->action),
756 action_runnable_str(input->action->flags),
757 action_optional_str(input->action->flags), input->flags);
758 return true;
759}
760
773bool
776{
777 bool has_loop = false;
778
779 if (pcmk_is_set(input->action->flags, pcmk__action_detect_loop)) {
780 crm_trace("Breaking tracking loop: %s@%s -> %s@%s (%#.6x)",
781 input->action->uuid,
782 input->action->node? input->action->node->priv->name : "",
783 action->uuid,
784 action->node? action->node->priv->name : "",
785 input->flags);
786 return false;
787 }
788
789 // Don't need to check inputs that won't be used
790 if (!should_add_input_to_graph(action, input)) {
791 return false;
792 }
793
794 if (input->action == init_action) {
795 crm_debug("Input loop found in %s@%s ->...-> %s@%s",
796 action->uuid,
797 action->node? action->node->priv->name : "",
798 init_action->uuid,
799 init_action->node? init_action->node->priv->name : "");
800 return true;
801 }
802
804
805 crm_trace("Checking inputs of action %s@%s input %s@%s (%#.6x)"
806 "for graph loop with %s@%s ",
807 action->uuid,
808 action->node? action->node->priv->name : "",
809 input->action->uuid,
810 input->action->node? input->action->node->priv->name : "",
811 input->flags,
812 init_action->uuid,
813 init_action->node? init_action->node->priv->name : "");
814
815 // Recursively check input itself for loops
816 for (GList *iter = input->action->actions_before;
817 iter != NULL; iter = iter->next) {
818
819 if (pcmk__graph_has_loop(init_action, input->action,
820 (pcmk__related_action_t *) iter->data)) {
821 // Recursive call already logged a debug message
822 has_loop = true;
823 break;
824 }
825 }
826
828
829 if (!has_loop) {
830 crm_trace("No input loop found in %s@%s -> %s@%s (%#.6x)",
831 input->action->uuid,
832 input->action->node? input->action->node->priv->name : "",
833 action->uuid,
834 action->node? action->node->priv->name : "",
835 input->flags);
836 }
837 return has_loop;
838}
839
849static xmlNode *
850create_graph_synapse(const pcmk_action_t *action, pcmk_scheduler_t *scheduler)
851{
852 int synapse_priority = 0;
854
856
857 if (action->rsc != NULL) {
858 synapse_priority = action->rsc->priv->priority;
859 }
860 if (action->priority > synapse_priority) {
861 synapse_priority = action->priority;
862 }
863 if (synapse_priority > 0) {
864 crm_xml_add_int(syn, PCMK__XA_PRIORITY, synapse_priority);
865 }
866 return syn;
867}
868
885static void
886add_action_to_graph(gpointer data, gpointer user_data)
887{
890
891 xmlNode *syn = NULL;
892 xmlNode *set = NULL;
893 xmlNode *in = NULL;
894
895 /* If we haven't already, de-duplicate inputs (even if we won't be adding
896 * the action to the graph, so that crm_simulate's dot graphs don't have
897 * duplicates).
898 */
902 }
903
905 || !should_add_action_to_graph(action)) {
906 return; // Already added, or shouldn't be
907 }
909
910 crm_trace("Adding action %d (%s%s%s) to graph",
911 action->id, action->uuid,
912 ((action->node == NULL)? "" : " on "),
913 ((action->node == NULL)? "" : action->node->priv->name));
914
915 syn = create_graph_synapse(action, scheduler);
918
919 create_graph_action(set, action, false, scheduler);
920
921 for (GList *lpc = action->actions_before; lpc != NULL; lpc = lpc->next) {
922 pcmk__related_action_t *input = lpc->data;
923
924 if (should_add_input_to_graph(action, input)) {
925 xmlNode *input_xml = pcmk__xe_create(in, PCMK__XE_TRIGGER);
926
927 input->graphed = true;
928 create_graph_action(input_xml, input->action, true, scheduler);
929 }
930 }
931}
932
933static int transition_id = 0;
934
942void
944 const char *filename)
945{
948 crm_err("Calculated transition %d (with errors)%s%s",
949 transition_id,
950 (filename == NULL)? "" : ", saving inputs in ",
951 (filename == NULL)? "" : filename);
952
955 crm_warn("Calculated transition %d (with warnings)%s%s",
956 transition_id,
957 (filename == NULL)? "" : ", saving inputs in ",
958 (filename == NULL)? "" : filename);
959
960 } else {
961 crm_notice("Calculated transition %d%s%s",
962 transition_id,
963 (filename == NULL)? "" : ", saving inputs in ",
964 (filename == NULL)? "" : filename);
965 }
967 crm_notice("Configuration errors found during scheduler processing,"
968 " please run \"crm_verify -L\" to identify issues");
969 }
970}
971
978void
980{
981 GList *iter = NULL;
982
983 pcmk__assert(rsc != NULL);
984
985 pcmk__rsc_trace(rsc, "Adding actions for %s to graph", rsc->id);
986
987 // First add the resource's own actions
988 g_list_foreach(rsc->priv->actions, add_action_to_graph,
989 rsc->priv->scheduler);
990
991 // Then recursively add its children's actions (appropriate to variant)
992 for (iter = rsc->priv->children; iter != NULL; iter = iter->next) {
993 pcmk_resource_t *child_rsc = (pcmk_resource_t *) iter->data;
994
995 child_rsc->priv->cmds->add_actions_to_graph(child_rsc);
996 }
997}
998
1005void
1007{
1008 GList *iter = NULL;
1009 const char *value = NULL;
1010 long long limit = 0LL;
1011 GHashTable *config_hash = scheduler->priv->options;
1012 int rc = pcmk_rc_ok;
1013
1014 transition_id++;
1015 crm_trace("Creating transition graph %d", transition_id);
1016
1018
1019 value = pcmk__cluster_option(config_hash, PCMK_OPT_CLUSTER_DELAY);
1021
1022 value = pcmk__cluster_option(config_hash, PCMK_OPT_STONITH_TIMEOUT);
1024
1027
1031 } else {
1033 }
1034
1035 value = pcmk__cluster_option(config_hash, PCMK_OPT_BATCH_LIMIT);
1037
1038 crm_xml_add_int(scheduler->priv->graph, "transition_id", transition_id);
1039
1040 value = pcmk__cluster_option(config_hash, PCMK_OPT_MIGRATION_LIMIT);
1041 rc = pcmk__scan_ll(value, &limit, 0LL);
1042 if (rc != pcmk_rc_ok) {
1043 crm_warn("Ignoring invalid value '%s' for " PCMK_OPT_MIGRATION_LIMIT
1044 ": %s", value, pcmk_rc_str(rc));
1045 } else if (limit > 0) {
1047 }
1048
1049 if (scheduler->priv->recheck_by > 0) {
1050 char *recheck_epoch = NULL;
1051
1052 recheck_epoch = crm_strdup_printf("%llu", (unsigned long long)
1054 crm_xml_add(scheduler->priv->graph, "recheck-by", recheck_epoch);
1055 free(recheck_epoch);
1056 }
1057
1058 /* The following code will de-duplicate action inputs, so nothing past this
1059 * should rely on the action input type flags retaining their original
1060 * values.
1061 */
1062
1063 // Add resource actions to graph
1064 for (iter = scheduler->priv->resources; iter != NULL; iter = iter->next) {
1065 pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
1066
1067 pcmk__rsc_trace(rsc, "Processing actions for %s", rsc->id);
1068 rsc->priv->cmds->add_actions_to_graph(rsc);
1069 }
1070
1071 // Add pseudo-action for list of nodes with maintenance state update
1072 add_maintenance_update(scheduler);
1073
1074 // Add non-resource (node) actions
1075 for (iter = scheduler->priv->actions; iter != NULL; iter = iter->next) {
1076 pcmk_action_t *action = (pcmk_action_t *) iter->data;
1077
1078 if ((action->rsc != NULL)
1079 && (action->node != NULL)
1080 && action->node->details->shutdown
1081 && !pcmk_is_set(action->rsc->flags, pcmk__rsc_maintenance)
1082 && !pcmk_any_flags_set(action->flags,
1084 && pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_none)) {
1085 /* Eventually we should just ignore the 'fence' case, but for now
1086 * it's the best way to detect (in CTS) when CIB resource updates
1087 * are being lost.
1088 */
1091 const bool managed = pcmk_is_set(action->rsc->flags,
1093 const bool failed = pcmk_is_set(action->rsc->flags,
1095
1096 crm_crit("Cannot %s %s because of %s:%s%s (%s)",
1097 action->node->details->unclean? "fence" : "shut down",
1098 pcmk__node_name(action->node), action->rsc->id,
1099 (managed? " blocked" : " unmanaged"),
1100 (failed? " failed" : ""), action->uuid);
1101 }
1102 }
1103
1104 add_action_to_graph((gpointer) action, (gpointer) scheduler);
1105 }
1106
1108}
@ pcmk__ar_if_first_unmigratable
Relation applies only if 'first' cannot be part of a live migration.
@ pcmk__ar_then_implies_first_graphed
If 'then' is required, 'first' must be added to the transition graph.
@ pcmk__ar_min_runnable
'then' action is runnable if certain number of 'first' instances are
@ pcmk__ar_none
No relation (compare with equality rather than bit set)
@ pcmk__ar_first_implies_then_graphed
If 'first' is required and runnable, 'then' must be in graph.
@ pcmk__ar_if_required_on_same_node
Ordering applies only if 'first' is required and on same node as 'then'.
@ pcmk__ar_unmigratable_then_blocks
@ pcmk__ar_ordered
Actions are ordered (optionally, if no other flags are set)
@ pcmk__ar_if_on_same_node_or_target
Actions are ordered if on same node (or migration target for migrate_to)
#define PCMK_ACTION_STOP
Definition actions.h:66
#define PCMK_ACTION_MAINTENANCE_NODES
Definition actions.h:46
#define PCMK_ACTION_LRM_DELETE
Definition actions.h:44
#define PCMK_ACTION_CLEAR_FAILCOUNT
Definition actions.h:37
#define PCMK_ACTION_MIGRATE_FROM
Definition actions.h:49
#define PCMK_ACTION_MIGRATE_TO
Definition actions.h:50
#define PCMK_ACTION_MONITOR
Definition actions.h:51
#define PCMK_ACTION_STONITH
Definition actions.h:65
#define PCMK_ACTION_DO_SHUTDOWN
Definition actions.h:42
#define PCMK_ACTION_NOTIFY
Definition actions.h:53
@ pcmk__action_inputs_deduplicated
@ pcmk__action_always_in_graph
@ pcmk__action_runnable
@ pcmk__action_added_to_graph
@ pcmk__action_migratable
@ pcmk__action_detect_loop
@ pcmk__action_optional
@ pcmk__action_on_dc
@ pcmk__action_pseudo
char * pcmk__notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition actions.c:365
#define pcmk__set_action_flags(action, flags_to_set)
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition actions.c:225
bool pcmk__is_fencing_action(const char *action)
Definition actions.c:611
#define pcmk__clear_action_flags(action, flags_to_clear)
const char * parent
Definition cib.c:27
Cluster Configuration.
#define PCMK__NELEM(a)
Definition internal.h:50
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:80
GHashTable * pe_rsc_params(pcmk_resource_t *rsc, const pcmk_node_t *node, pcmk_scheduler_t *scheduler)
Get a table of resource parameters.
Definition complex.c:462
char data[0]
Definition cpg.c:10
A dumping ground.
#define CRM_FEATURE_SET
Definition crm.h:66
G_GNUC_INTERNAL void pcmk__substitute_remote_addr(pcmk_resource_t *rsc, GHashTable *params)
G_GNUC_INTERNAL void pcmk__log_action(const char *pre_text, const pcmk_action_t *action, bool details)
G_GNUC_INTERNAL pcmk_node_t * pcmk__connection_host_for_action(const pcmk_action_t *action)
G_GNUC_INTERNAL bool pcmk__action_locks_rsc_to_node(const pcmk_action_t *action)
G_GNUC_INTERNAL void pcmk__deduplicate_action_inputs(pcmk_action_t *action)
G_GNUC_INTERNAL void pcmk__add_guest_meta_to_xml(xmlNode *args_xml, const pcmk_action_t *action)
Add special guest node meta-attributes to XML.
#define crm_warn(fmt, args...)
Definition logging.h:360
#define crm_crit(fmt, args...)
Definition logging.h:354
#define crm_notice(fmt, args...)
Definition logging.h:363
#define CRM_CHECK(expr, failure_action)
Definition logging.h:213
#define crm_debug(fmt, args...)
Definition logging.h:368
#define crm_err(fmt, args...)
Definition logging.h:357
#define crm_log_xml_trace(xml, text)
Definition logging.h:378
#define crm_trace(fmt, args...)
Definition logging.h:370
bool pcmk__config_has_error
Definition utils.c:43
bool pcmk__config_has_warning
Definition utils.c:44
pcmk_scheduler_t * scheduler
xmlNode * input
@ pcmk__node_remote_maint
@ pcmk__node_remote_reset
void hash2metafield(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry, as meta-attribute name.
Definition nvpair.c:266
void hash2field(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry.
Definition nvpair.c:238
void hash2smartfield(gpointer key, gpointer value, gpointer user_data)
Safely add hash table entry to XML as attribute or name-value pair.
Definition nvpair.c:196
#define PCMK_META_INTERVAL
Definition options.h:92
#define PCMK_OPT_BATCH_LIMIT
Definition options.h:27
#define PCMK_OPT_CLUSTER_DELAY
Definition options.h:28
#define PCMK_OPT_MIGRATION_LIMIT
Definition options.h:46
#define PCMK_OPT_STONITH_TIMEOUT
Definition options.h:68
#define PCMK_VALUE_INFINITY
Definition options.h:165
#define PCMK_OPT_SHUTDOWN_LOCK
Definition options.h:61
const char * pcmk__cluster_option(GHashTable *options, const char *name)
Definition options.c:1410
#define PCMK__VALUE_CIB
#define PCMK__META_ON_NODE_UUID
#define PCMK__META_ON_NODE
#define PCMK__META_STONITH_ACTION
const char * action
Definition pcmk_fence.c:32
#define action_type_str(flags)
bool pcmk__graph_has_loop(const pcmk_action_t *init_action, const pcmk_action_t *action, pcmk__related_action_t *input)
void pcmk__create_graph(pcmk_scheduler_t *scheduler)
#define action_node_str(a)
#define action_runnable_str(flags)
void pcmk__log_transition_summary(const pcmk_scheduler_t *scheduler, const char *filename)
void pcmk__add_rsc_actions_to_graph(pcmk_resource_t *rsc)
#define action_optional_str(flags)
pcmk_action_t * get_pseudo_op(const char *name, pcmk_scheduler_t *scheduler)
void pe_foreach_guest_node(const pcmk_scheduler_t *scheduler, const pcmk_node_t *host, void(*helper)(const pcmk_node_t *, void *), void *user_data)
Definition remote.c:85
@ pcmk__rsc_managed
@ pcmk__rsc_maintenance
@ pcmk__rsc_unique
@ pcmk__rsc_removed
@ pcmk__rsc_is_remote_connection
@ pcmk__rsc_failed
@ pcmk__rsc_variant_primitive
Primitive resource.
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:617
@ pcmk_rc_ok
Definition results.h:159
#define pcmk__assert(expr)
@ pcmk_no_quorum_ignore
Act as if partition still holds quorum.
Definition scheduler.h:41
#define pcmk__sched_err(scheduler, fmt...)
#define pcmk__rsc_trace(rsc, fmt, args...)
@ pcmk__sched_quorate
@ pcmk__sched_start_failure_fatal
@ pcmk__sched_processing_warning
@ pcmk__sched_processing_error
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
void pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
Definition strings.c:703
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition strings.c:92
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1029
@ pcmk__str_none
@ pcmk__str_null_matches
bool pcmk__ends_with(const char *s, const char *match)
Definition strings.c:610
int pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val, guint *result)
Definition strings.c:303
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1053
pcmk_node_t * node
void(* add_actions_to_graph)(pcmk_resource_t *rsc)
gboolean maintenance
Definition nodes.h:66
pcmk_scheduler_t * scheduler
enum pcmk__rsc_variant variant
const pcmk__assignment_methods_t * cmds
pcmk__resource_private_t * priv
Definition resources.h:61
pcmk__scheduler_private_t * priv
Definition scheduler.h:99
enum pe_quorum_policy no_quorum_policy
Definition scheduler.h:93
GList * nodes
Definition scheduler.h:97
uint64_t flags
Definition scheduler.h:89
pcmk__node_private_t * priv
Definition nodes.h:85
struct pcmk__node_details * details
Definition nodes.h:82
Wrappers for and extensions to libxml2.
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
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.
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
void pcmk__xe_sort_attrs(xmlNode *xml)
#define PCMK_XA_CLASS
Definition xml_names.h:246
#define PCMK_XE_NODE
Definition xml_names.h:136
#define PCMK_XA_OPERATION
Definition xml_names.h:349
#define PCMK_XA_ID
Definition xml_names.h:301
#define PCMK_XA_PROVIDER
Definition xml_names.h:364
#define PCMK_XA_CRM_FEATURE_SET
Definition xml_names.h:254
#define PCMK_XA_TYPE
Definition xml_names.h:430
#define PCMK__XA_PRIORITY
#define PCMK__XE_TRANSITION_GRAPH
#define PCMK__XE_TRIGGER
#define PCMK__XE_RSC_OP
#define PCMK__XE_CRM_EVENT
#define PCMK__XE_INPUTS
#define PCMK__XA_FAILED_STOP_OFFSET
#define PCMK__XE_PSEUDO_EVENT
#define PCMK__XE_ACTION_SET
#define PCMK__XE_SYNAPSE
#define PCMK__XA_ROUTER_NODE
#define PCMK__XA_FAILED_START_OFFSET
#define PCMK__XA_MODE
#define PCMK__XA_OPERATION_KEY
#define PCMK__XE_DOWNED
#define PCMK__XE_ATTRIBUTES
#define PCMK__XE_MAINTENANCE
#define PCMK__XA_NODE_IN_MAINTENANCE
#define PCMK__XA_LONG_ID