pacemaker 2.1.8-2.1.8
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->details->uname)
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->details->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 (node->details->maintenance != node->details->remote_maintenance)) {
93
94 if (maintenance != NULL) {
95 crm_xml_add(add_node_to_xml_by_id(node->details->id,
96 maintenance),
98 (node->details->maintenance? "1" : "0"));
99 }
100 count++;
101 }
102 }
103 crm_trace("%s %d nodes in need of maintenance mode update in state",
104 ((maintenance == NULL)? "Counted" : "Added"), count);
105 return count;
106}
107
114static void
115add_maintenance_update(pcmk_scheduler_t *scheduler)
116{
117 pcmk_action_t *action = NULL;
118
119 if (add_maintenance_nodes(NULL, scheduler) != 0) {
122 }
123}
124
136static void
137add_downed_nodes(xmlNode *xml, const pcmk_action_t *action)
138{
139 CRM_CHECK((xml != NULL) && (action != NULL) && (action->node != NULL),
140 return);
141
142 if (pcmk__str_eq(action->task, PCMK_ACTION_DO_SHUTDOWN, pcmk__str_none)) {
143
144 /* Shutdown makes the action's node down */
145 xmlNode *downed = pcmk__xe_create(xml, PCMK__XE_DOWNED);
146 add_node_to_xml_by_id(action->node->details->id, downed);
147
148 } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH,
150
151 /* Fencing makes the action's node and any hosted guest nodes down */
152 const char *fence = g_hash_table_lookup(action->meta,
154
155 if (pcmk__is_fencing_action(fence)) {
156 xmlNode *downed = pcmk__xe_create(xml, PCMK__XE_DOWNED);
157 add_node_to_xml_by_id(action->node->details->id, downed);
158 pe_foreach_guest_node(action->node->details->data_set,
159 action->node, add_node_to_xml, downed);
160 }
161
162 } else if (action->rsc && action->rsc->is_remote_node
163 && pcmk__str_eq(action->task, PCMK_ACTION_STOP,
165
166 /* Stopping a remote connection resource makes connected node down,
167 * unless it's part of a migration
168 */
169 GList *iter;
171 bool migrating = false;
172
173 for (iter = action->actions_before; iter != NULL; iter = iter->next) {
174 input = ((pcmk__related_action_t *) iter->data)->action;
175 if ((input->rsc != NULL)
176 && pcmk__str_eq(action->rsc->id, input->rsc->id, pcmk__str_none)
177 && pcmk__str_eq(input->task, PCMK_ACTION_MIGRATE_FROM,
179 migrating = true;
180 break;
181 }
182 }
183 if (!migrating) {
184 xmlNode *downed = pcmk__xe_create(xml, PCMK__XE_DOWNED);
185 add_node_to_xml_by_id(action->rsc->id, downed);
186 }
187 }
188}
189
199static char *
200clone_op_key(const pcmk_action_t *action, guint interval_ms)
201{
202 if (pcmk__str_eq(action->task, PCMK_ACTION_NOTIFY, pcmk__str_none)) {
203 const char *n_type = g_hash_table_lookup(action->meta, "notify_type");
204 const char *n_task = g_hash_table_lookup(action->meta,
205 "notify_operation");
206
207 CRM_LOG_ASSERT((n_type != NULL) && (n_task != NULL));
208 return pcmk__notify_key(action->rsc->clone_name, n_type, n_task);
209
210 } else if (action->cancel_task != NULL) {
211 return pcmk__op_key(action->rsc->clone_name, action->cancel_task,
212 interval_ms);
213 } else {
214 return pcmk__op_key(action->rsc->clone_name, action->task, interval_ms);
215 }
216}
217
225static void
226add_node_details(const pcmk_action_t *action, xmlNode *xml)
227{
229
230 crm_xml_add(xml, PCMK__META_ON_NODE, action->node->details->uname);
231 crm_xml_add(xml, PCMK__META_ON_NODE_UUID, action->node->details->id);
232 if (router_node != NULL) {
233 crm_xml_add(xml, PCMK__XA_ROUTER_NODE, router_node->details->uname);
234 }
235}
236
244static void
245add_resource_details(const pcmk_action_t *action, xmlNode *action_xml)
246{
247 xmlNode *rsc_xml = NULL;
248 const char *attr_list[] = {
252 };
253
254 /* If a resource is locked to a node via PCMK_OPT_SHUTDOWN_LOCK, mark its
255 * actions so the controller can preserve the lock when the action
256 * completes.
257 */
260 (long long) action->rsc->lock_time);
261 }
262
263 // List affected resource
264
265 rsc_xml = pcmk__xe_create(action_xml,
266 (const char *) action->rsc->xml->name);
267 if (pcmk_is_set(action->rsc->flags, pcmk_rsc_removed)
268 && (action->rsc->clone_name != NULL)) {
269 /* Use the numbered instance name here, because if there is more
270 * than one instance on a node, we need to make sure the command
271 * goes to the right one.
272 *
273 * This is important even for anonymous clones, because the clone's
274 * unique meta-attribute might have just been toggled from on to
275 * off.
276 */
277 crm_debug("Using orphan clone name %s instead of %s",
278 action->rsc->id, action->rsc->clone_name);
279 crm_xml_add(rsc_xml, PCMK_XA_ID, action->rsc->clone_name);
280 crm_xml_add(rsc_xml, PCMK__XA_LONG_ID, action->rsc->id);
281
282 } else if (!pcmk_is_set(action->rsc->flags, pcmk_rsc_unique)) {
283 const char *xml_id = pcmk__xe_id(action->rsc->xml);
284
285 crm_debug("Using anonymous clone name %s for %s (aka %s)",
286 xml_id, action->rsc->id, action->rsc->clone_name);
287
288 /* ID is what we'd like client to use
289 * LONG_ID is what they might know it as instead
290 *
291 * LONG_ID is only strictly needed /here/ during the
292 * transition period until all nodes in the cluster
293 * are running the new software /and/ have rebooted
294 * once (meaning that they've only ever spoken to a DC
295 * supporting this feature).
296 *
297 * If anyone toggles the unique flag to 'on', the
298 * 'instance free' name will correspond to an orphan
299 * and fall into the clause above instead
300 */
301 crm_xml_add(rsc_xml, PCMK_XA_ID, xml_id);
302 if ((action->rsc->clone_name != NULL)
303 && !pcmk__str_eq(xml_id, action->rsc->clone_name,
305 crm_xml_add(rsc_xml, PCMK__XA_LONG_ID, action->rsc->clone_name);
306 } else {
307 crm_xml_add(rsc_xml, PCMK__XA_LONG_ID, action->rsc->id);
308 }
309
310 } else {
311 CRM_ASSERT(action->rsc->clone_name == NULL);
312 crm_xml_add(rsc_xml, PCMK_XA_ID, action->rsc->id);
313 }
314
315 for (int lpc = 0; lpc < PCMK__NELEM(attr_list); lpc++) {
316 crm_xml_add(rsc_xml, attr_list[lpc],
317 g_hash_table_lookup(action->rsc->meta, attr_list[lpc]));
318 }
319}
320
328static void
329add_action_attributes(pcmk_action_t *action, xmlNode *action_xml)
330{
331 xmlNode *args_xml = NULL;
332
333 /* We create free-standing XML to start, so we can sort the attributes
334 * before adding it to action_xml, which keeps the scheduler regression
335 * test graphs comparable.
336 */
337 args_xml = pcmk__xe_create(NULL, PCMK__XE_ATTRIBUTES);
338
340 g_hash_table_foreach(action->extra, hash2field, args_xml);
341
342 if ((action->rsc != NULL) && (action->node != NULL)) {
343 // Get the resource instance attributes, evaluated properly for node
344 GHashTable *params = pe_rsc_params(action->rsc, action->node,
345 action->rsc->cluster);
346
348
349 g_hash_table_foreach(params, hash2smartfield, args_xml);
350
351 } else if ((action->rsc != NULL)
352 && (action->rsc->variant <= pcmk_rsc_variant_primitive)) {
353 GHashTable *params = pe_rsc_params(action->rsc, NULL,
354 action->rsc->cluster);
355
356 g_hash_table_foreach(params, hash2smartfield, args_xml);
357 }
358
359 g_hash_table_foreach(action->meta, hash2metafield, args_xml);
360 if (action->rsc != NULL) {
362
363 while (parent != NULL) {
364 parent->cmds->add_graph_meta(parent, args_xml);
365 parent = parent->parent;
366 }
367
369
370 } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH, pcmk__str_none)
371 && (action->node != NULL)) {
372 /* Pass the node's attributes as meta-attributes.
373 *
374 * @TODO: Determine whether it is still necessary to do this. It was
375 * added in 33d99707, probably for the libfence-based implementation in
376 * c9a90bd, which is no longer used.
377 */
378 g_hash_table_foreach(action->node->details->attrs, hash2metafield,
379 args_xml);
380 }
381
382 sorted_xml(args_xml, action_xml, FALSE);
383 free_xml(args_xml);
384}
385
395static void
396create_graph_action(xmlNode *parent, pcmk_action_t *action, bool skip_details,
398{
399 bool needs_node_info = true;
400 bool needs_maintenance_info = false;
401 xmlNode *action_xml = NULL;
402
403 if ((action == NULL) || (scheduler == NULL)) {
404 return;
405 }
406
407 // Create the top-level element based on task
408
409 if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH, pcmk__str_none)) {
410 /* All fences need node info; guest node fences are pseudo-events */
413 } else {
415 }
416
417 } else if (pcmk__str_any_of(action->task,
421
422 } else if (pcmk__str_eq(action->task, PCMK_ACTION_LRM_DELETE,
424 // CIB-only clean-up for shutdown locks
427
428 } else if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
429 if (pcmk__str_eq(action->task, PCMK_ACTION_MAINTENANCE_NODES,
431 needs_maintenance_info = true;
432 }
434 needs_node_info = false;
435
436 } else {
438 }
439
440 crm_xml_add_int(action_xml, PCMK_XA_ID, action->id);
441 crm_xml_add(action_xml, PCMK_XA_OPERATION, action->task);
442
443 if ((action->rsc != NULL) && (action->rsc->clone_name != NULL)) {
444 char *clone_key = NULL;
445 guint interval_ms;
446
448 &interval_ms) != pcmk_rc_ok) {
449 interval_ms = 0;
450 }
451 clone_key = clone_op_key(action, interval_ms);
452 crm_xml_add(action_xml, PCMK__XA_OPERATION_KEY, clone_key);
453 crm_xml_add(action_xml, "internal_" PCMK__XA_OPERATION_KEY,
454 action->uuid);
455 free(clone_key);
456 } else {
457 crm_xml_add(action_xml, PCMK__XA_OPERATION_KEY, action->uuid);
458 }
459
460 if (needs_node_info && (action->node != NULL)) {
461 add_node_details(action, action_xml);
463 action->node->details->uname);
465 action->node->details->id);
466 }
467
468 if (skip_details) {
469 return;
470 }
471
472 if ((action->rsc != NULL)
474
475 // This is a real resource action, so add resource details
476 add_resource_details(action, action_xml);
477 }
478
479 /* List any attributes in effect */
480 add_action_attributes(action, action_xml);
481
482 /* List any nodes this action is expected to make down */
483 if (needs_node_info && (action->node != NULL)) {
484 add_downed_nodes(action_xml, action);
485 }
486
487 if (needs_maintenance_info) {
488 add_maintenance_nodes(action_xml, scheduler);
489 }
490}
491
500static bool
501should_add_action_to_graph(const pcmk_action_t *action)
502{
504 crm_trace("Ignoring action %s (%d): unrunnable",
505 action->uuid, action->id);
506 return false;
507 }
508
511 crm_trace("Ignoring action %s (%d): optional",
512 action->uuid, action->id);
513 return false;
514 }
515
516 /* Actions for unmanaged resources should be excluded from the graph,
517 * with the exception of monitors and cancellation of recurring monitors.
518 */
519 if ((action->rsc != NULL)
520 && !pcmk_is_set(action->rsc->flags, pcmk_rsc_managed)
521 && !pcmk__str_eq(action->task, PCMK_ACTION_MONITOR, pcmk__str_none)) {
522
523 const char *interval_ms_s;
524
525 /* A cancellation of a recurring monitor will get here because the task
526 * is cancel rather than monitor, but the interval can still be used to
527 * recognize it. The interval has been normalized to milliseconds by
528 * this point, so a string comparison is sufficient.
529 */
530 interval_ms_s = g_hash_table_lookup(action->meta, PCMK_META_INTERVAL);
531 if (pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches)) {
532 crm_trace("Ignoring action %s (%d): for unmanaged resource (%s)",
533 action->uuid, action->id, action->rsc->id);
534 return false;
535 }
536 }
537
538 /* Always add pseudo-actions, fence actions, and shutdown actions (already
539 * determined to be required and runnable by this point)
540 */
544 return true;
545 }
546
547 if (action->node == NULL) {
548 pcmk__sched_err("Skipping action %s (%d) "
549 "because it was not assigned to a node (bug?)",
550 action->uuid, action->id);
551 pcmk__log_action("Unassigned", action, false);
552 return false;
553 }
554
555 if (pcmk_is_set(action->flags, pcmk_action_on_dc)) {
556 crm_trace("Action %s (%d) should be dumped: "
557 "can run on DC instead of %s",
558 action->uuid, action->id, pcmk__node_name(action->node));
559
560 } else if (pcmk__is_guest_or_bundle_node(action->node)
561 && !action->node->details->remote_requires_reset) {
562 crm_trace("Action %s (%d) should be dumped: "
563 "assuming will be runnable on guest %s",
564 action->uuid, action->id, pcmk__node_name(action->node));
565
566 } else if (!action->node->details->online) {
567 pcmk__sched_err("Skipping action %s (%d) "
568 "because it was scheduled for offline node (bug?)",
569 action->uuid, action->id);
570 pcmk__log_action("Offline node", action, false);
571 return false;
572
573 } else if (action->node->details->unclean) {
574 pcmk__sched_err("Skipping action %s (%d) "
575 "because it was scheduled for unclean node (bug?)",
576 action->uuid, action->id);
577 pcmk__log_action("Unclean node", action, false);
578 return false;
579 }
580 return true;
581}
582
591static bool
592ordering_can_change_actions(const pcmk__related_action_t *ordering)
593{
594 return pcmk_any_flags_set(ordering->type,
598}
599
611static bool
612should_add_input_to_graph(const pcmk_action_t *action,
614{
615 if (input->state == pe_link_dumped) {
616 return true;
617 }
618
619 if ((uint32_t) input->type == pcmk__ar_none) {
620 crm_trace("Ignoring %s (%d) input %s (%d): "
621 "ordering disabled",
622 action->uuid, action->id,
623 input->action->uuid, input->action->id);
624 return false;
625
626 } else if (!pcmk_is_set(input->action->flags, pcmk_action_runnable)
627 && !ordering_can_change_actions(input)) {
628 crm_trace("Ignoring %s (%d) input %s (%d): "
629 "optional and input unrunnable",
630 action->uuid, action->id,
631 input->action->uuid, input->action->id);
632 return false;
633
634 } else if (!pcmk_is_set(input->action->flags, pcmk_action_runnable)
636 crm_trace("Ignoring %s (%d) input %s (%d): "
637 "minimum number of instances required but input unrunnable",
638 action->uuid, action->id,
639 input->action->uuid, input->action->id);
640 return false;
641
643 && !pcmk_is_set(input->action->flags, pcmk_action_runnable)) {
644 crm_trace("Ignoring %s (%d) input %s (%d): "
645 "input blocked if 'then' unmigratable",
646 action->uuid, action->id,
647 input->action->uuid, input->action->id);
648 return false;
649
651 && pcmk_is_set(input->action->flags, pcmk_action_migratable)) {
652 crm_trace("Ignoring %s (%d) input %s (%d): ordering applies "
653 "only if input is unmigratable, but it is migratable",
654 action->uuid, action->id,
655 input->action->uuid, input->action->id);
656 return false;
657
658 } else if (((uint32_t) input->type == pcmk__ar_ordered)
659 && pcmk_is_set(input->action->flags, pcmk_action_migratable)
660 && pcmk__ends_with(input->action->uuid, "_stop_0")) {
661 crm_trace("Ignoring %s (%d) input %s (%d): "
662 "optional but stop in migration",
663 action->uuid, action->id,
664 input->action->uuid, input->action->id);
665 return false;
666
667 } else if ((uint32_t) input->type == pcmk__ar_if_on_same_node_or_target) {
668 pcmk_node_t *input_node = input->action->node;
669
670 if ((action->rsc != NULL)
671 && pcmk__str_eq(action->task, PCMK_ACTION_MIGRATE_TO,
673
674 pcmk_node_t *assigned = action->rsc->allocated_to;
675
676 /* For load_stopped -> migrate_to orderings, we care about where
677 * the resource has been assigned, not where migrate_to will be
678 * executed.
679 */
680 if (!pcmk__same_node(input_node, assigned)) {
681 crm_trace("Ignoring %s (%d) input %s (%d): "
682 "migration target %s is not same as input node %s",
683 action->uuid, action->id,
684 input->action->uuid, input->action->id,
685 (assigned? assigned->details->uname : "<none>"),
686 (input_node? input_node->details->uname : "<none>"));
687 input->type = (enum pe_ordering) pcmk__ar_none;
688 return false;
689 }
690
691 } else if (!pcmk__same_node(input_node, action->node)) {
692 crm_trace("Ignoring %s (%d) input %s (%d): "
693 "not on same node (%s vs %s)",
694 action->uuid, action->id,
695 input->action->uuid, input->action->id,
696 (action->node? action->node->details->uname : "<none>"),
697 (input_node? input_node->details->uname : "<none>"));
698 input->type = (enum pe_ordering) pcmk__ar_none;
699 return false;
700
701 } else if (pcmk_is_set(input->action->flags, pcmk_action_optional)) {
702 crm_trace("Ignoring %s (%d) input %s (%d): "
703 "ordering optional",
704 action->uuid, action->id,
705 input->action->uuid, input->action->id);
706 input->type = (enum pe_ordering) pcmk__ar_none;
707 return false;
708 }
709
710 } else if ((uint32_t) input->type == pcmk__ar_if_required_on_same_node) {
711 if (input->action->node && action->node
712 && !pcmk__same_node(input->action->node, action->node)) {
713 crm_trace("Ignoring %s (%d) input %s (%d): "
714 "not on same node (%s vs %s)",
715 action->uuid, action->id,
716 input->action->uuid, input->action->id,
717 pcmk__node_name(action->node),
718 pcmk__node_name(input->action->node));
719 input->type = (enum pe_ordering) pcmk__ar_none;
720 return false;
721
722 } else if (pcmk_is_set(input->action->flags, pcmk_action_optional)) {
723 crm_trace("Ignoring %s (%d) input %s (%d): optional",
724 action->uuid, action->id,
725 input->action->uuid, input->action->id);
726 input->type = (enum pe_ordering) pcmk__ar_none;
727 return false;
728 }
729
730 } else if (input->action->rsc
731 && input->action->rsc != action->rsc
732 && pcmk_is_set(input->action->rsc->flags, pcmk_rsc_failed)
733 && !pcmk_is_set(input->action->rsc->flags, pcmk_rsc_managed)
734 && pcmk__ends_with(input->action->uuid, "_stop_0")
735 && pcmk__is_clone(action->rsc)) {
736 crm_warn("Ignoring requirement that %s complete before %s:"
737 " unmanaged failed resources cannot prevent clone shutdown",
738 input->action->uuid, action->uuid);
739 return false;
740
741 } else if (pcmk_is_set(input->action->flags, pcmk_action_optional)
742 && !pcmk_any_flags_set(input->action->flags,
745 && !should_add_action_to_graph(input->action)) {
746 crm_trace("Ignoring %s (%d) input %s (%d): "
747 "input optional",
748 action->uuid, action->id,
749 input->action->uuid, input->action->id);
750 return false;
751 }
752
753 crm_trace("%s (%d) input %s %s (%d) on %s should be dumped: %s %s %#.6x",
754 action->uuid, action->id, action_type_str(input->action->flags),
755 input->action->uuid, input->action->id,
756 action_node_str(input->action),
757 action_runnable_str(input->action->flags),
758 action_optional_str(input->action->flags), input->type);
759 return true;
760}
761
774bool
777{
778 bool has_loop = false;
779
780 if (pcmk_is_set(input->action->flags, pcmk_action_detect_loop)) {
781 crm_trace("Breaking tracking loop: %s@%s -> %s@%s (%#.6x)",
782 input->action->uuid,
783 input->action->node? input->action->node->details->uname : "",
784 action->uuid,
785 action->node? action->node->details->uname : "",
786 input->type);
787 return false;
788 }
789
790 // Don't need to check inputs that won't be used
791 if (!should_add_input_to_graph(action, input)) {
792 return false;
793 }
794
795 if (input->action == init_action) {
796 crm_debug("Input loop found in %s@%s ->...-> %s@%s",
797 action->uuid,
798 action->node? action->node->details->uname : "",
799 init_action->uuid,
800 init_action->node? init_action->node->details->uname : "");
801 return true;
802 }
803
805
806 crm_trace("Checking inputs of action %s@%s input %s@%s (%#.6x)"
807 "for graph loop with %s@%s ",
808 action->uuid,
809 action->node? action->node->details->uname : "",
810 input->action->uuid,
811 input->action->node? input->action->node->details->uname : "",
812 input->type,
813 init_action->uuid,
814 init_action->node? init_action->node->details->uname : "");
815
816 // Recursively check input itself for loops
817 for (GList *iter = input->action->actions_before;
818 iter != NULL; iter = iter->next) {
819
820 if (pcmk__graph_has_loop(init_action, input->action,
821 (pcmk__related_action_t *) iter->data)) {
822 // Recursive call already logged a debug message
823 has_loop = true;
824 break;
825 }
826 }
827
829
830 if (!has_loop) {
831 crm_trace("No input loop found in %s@%s -> %s@%s (%#.6x)",
832 input->action->uuid,
833 input->action->node? input->action->node->details->uname : "",
834 action->uuid,
835 action->node? action->node->details->uname : "",
836 input->type);
837 }
838 return has_loop;
839}
840
850static xmlNode *
851create_graph_synapse(const pcmk_action_t *action, pcmk_scheduler_t *scheduler)
852{
853 int synapse_priority = 0;
854 xmlNode *syn = pcmk__xe_create(scheduler->graph, "synapse");
855
858
859 if (action->rsc != NULL) {
860 synapse_priority = action->rsc->priority;
861 }
862 if (action->priority > synapse_priority) {
863 synapse_priority = action->priority;
864 }
865 if (synapse_priority > 0) {
866 crm_xml_add_int(syn, PCMK__XA_PRIORITY, synapse_priority);
867 }
868 return syn;
869}
870
887static void
888add_action_to_graph(gpointer data, gpointer user_data)
889{
892
893 xmlNode *syn = NULL;
894 xmlNode *set = NULL;
895 xmlNode *in = NULL;
896
897 /* If we haven't already, de-duplicate inputs (even if we won't be adding
898 * the action to the graph, so that crm_simulate's dot graphs don't have
899 * duplicates).
900 */
904 }
905
907 || !should_add_action_to_graph(action)) {
908 return; // Already added, or shouldn't be
909 }
911
912 crm_trace("Adding action %d (%s%s%s) to graph",
913 action->id, action->uuid,
914 ((action->node == NULL)? "" : " on "),
915 ((action->node == NULL)? "" : action->node->details->uname));
916
917 syn = create_graph_synapse(action, scheduler);
918 set = pcmk__xe_create(syn, "action_set");
919 in = pcmk__xe_create(syn, "inputs");
920
921 create_graph_action(set, action, false, scheduler);
922
923 for (GList *lpc = action->actions_before; lpc != NULL; lpc = lpc->next) {
924 pcmk__related_action_t *input = lpc->data;
925
926 if (should_add_input_to_graph(action, input)) {
927 xmlNode *input_xml = pcmk__xe_create(in, "trigger");
928
929 input->state = pe_link_dumped;
930 create_graph_action(input_xml, input->action, true, scheduler);
931 }
932 }
933}
934
935static int transition_id = -1;
936
943void
944pcmk__log_transition_summary(const char *filename)
945{
947 crm_err("Calculated transition %d (with errors)%s%s",
948 transition_id,
949 (filename == NULL)? "" : ", saving inputs in ",
950 (filename == NULL)? "" : filename);
951
953 crm_warn("Calculated transition %d (with warnings)%s%s",
954 transition_id,
955 (filename == NULL)? "" : ", saving inputs in ",
956 (filename == NULL)? "" : filename);
957
958 } else {
959 crm_notice("Calculated transition %d%s%s",
960 transition_id,
961 (filename == NULL)? "" : ", saving inputs in ",
962 (filename == NULL)? "" : filename);
963 }
964 if (crm_config_error) {
965 crm_notice("Configuration errors found during scheduler processing,"
966 " please run \"crm_verify -L\" to identify issues");
967 }
968}
969
976void
978{
979 GList *iter = NULL;
980
981 CRM_ASSERT(rsc != NULL);
982 pcmk__rsc_trace(rsc, "Adding actions for %s to graph", rsc->id);
983
984 // First add the resource's own actions
985 g_list_foreach(rsc->actions, add_action_to_graph, rsc->cluster);
986
987 // Then recursively add its children's actions (appropriate to variant)
988 for (iter = rsc->children; iter != NULL; iter = iter->next) {
989 pcmk_resource_t *child_rsc = (pcmk_resource_t *) iter->data;
990
991 child_rsc->cmds->add_actions_to_graph(child_rsc);
992 }
993}
994
1001void
1003{
1004 GList *iter = NULL;
1005 const char *value = NULL;
1006 long long limit = 0LL;
1007 GHashTable *config_hash = scheduler->config_hash;
1008
1009 transition_id++;
1010 crm_trace("Creating transition graph %d", transition_id);
1011
1013
1014 value = pcmk__cluster_option(config_hash, PCMK_OPT_CLUSTER_DELAY);
1016
1017 value = pcmk__cluster_option(config_hash, PCMK_OPT_STONITH_TIMEOUT);
1019
1020 crm_xml_add(scheduler->graph, "failed-stop-offset", "INFINITY");
1021
1023 crm_xml_add(scheduler->graph, "failed-start-offset", "INFINITY");
1024 } else {
1025 crm_xml_add(scheduler->graph, "failed-start-offset", "1");
1026 }
1027
1028 value = pcmk__cluster_option(config_hash, PCMK_OPT_BATCH_LIMIT);
1030
1031 crm_xml_add_int(scheduler->graph, "transition_id", transition_id);
1032
1033 value = pcmk__cluster_option(config_hash, PCMK_OPT_MIGRATION_LIMIT);
1034 if ((pcmk__scan_ll(value, &limit, 0LL) == pcmk_rc_ok) && (limit > 0)) {
1036 }
1037
1038 if (scheduler->recheck_by > 0) {
1039 char *recheck_epoch = NULL;
1040
1041 recheck_epoch = crm_strdup_printf("%llu",
1042 (long long) scheduler->recheck_by);
1043 crm_xml_add(scheduler->graph, "recheck-by", recheck_epoch);
1044 free(recheck_epoch);
1045 }
1046
1047 /* The following code will de-duplicate action inputs, so nothing past this
1048 * should rely on the action input type flags retaining their original
1049 * values.
1050 */
1051
1052 // Add resource actions to graph
1053 for (iter = scheduler->resources; iter != NULL; iter = iter->next) {
1054 pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
1055
1056 pcmk__rsc_trace(rsc, "Processing actions for %s", rsc->id);
1057 rsc->cmds->add_actions_to_graph(rsc);
1058 }
1059
1060 // Add pseudo-action for list of nodes with maintenance state update
1061 add_maintenance_update(scheduler);
1062
1063 // Add non-resource (node) actions
1064 for (iter = scheduler->actions; iter != NULL; iter = iter->next) {
1065 pcmk_action_t *action = (pcmk_action_t *) iter->data;
1066
1067 if ((action->rsc != NULL)
1068 && (action->node != NULL)
1069 && action->node->details->shutdown
1070 && !pcmk_is_set(action->rsc->flags, pcmk_rsc_maintenance)
1071 && !pcmk_any_flags_set(action->flags,
1073 && pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_none)) {
1074 /* Eventually we should just ignore the 'fence' case, but for now
1075 * it's the best way to detect (in CTS) when CIB resource updates
1076 * are being lost.
1077 */
1080 const bool managed = pcmk_is_set(action->rsc->flags,
1082 const bool failed = pcmk_is_set(action->rsc->flags,
1084
1085 crm_crit("Cannot %s %s because of %s:%s%s (%s)",
1086 action->node->details->unclean? "fence" : "shut down",
1087 pcmk__node_name(action->node), action->rsc->id,
1088 (managed? " blocked" : " unmanaged"),
1089 (failed? " failed" : ""), action->uuid);
1090 }
1091 }
1092
1093 add_action_to_graph((gpointer) action, (gpointer) scheduler);
1094 }
1095
1097}
@ 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:75
#define PCMK_ACTION_MAINTENANCE_NODES
Definition actions.h:55
pe_ordering
Definition actions.h:283
@ pe_link_dumped
Definition actions.h:277
#define PCMK_ACTION_LRM_DELETE
Definition actions.h:53
#define PCMK_ACTION_CLEAR_FAILCOUNT
Definition actions.h:46
#define PCMK_ACTION_MIGRATE_FROM
Definition actions.h:58
@ pcmk_action_detect_loop
Definition actions.h:239
@ pcmk_action_runnable
Definition actions.h:207
@ pcmk_action_migratable
Definition actions.h:219
@ pcmk_action_added_to_graph
Definition actions.h:222
@ pcmk_action_pseudo
Definition actions.h:204
@ pcmk_action_optional
Definition actions.h:210
@ pcmk_action_on_dc
Definition actions.h:245
@ pcmk_action_always_in_graph
Definition actions.h:213
@ pcmk_action_inputs_deduplicated
Definition actions.h:242
#define PCMK_ACTION_MIGRATE_TO
Definition actions.h:59
#define PCMK_ACTION_MONITOR
Definition actions.h:60
#define PCMK_ACTION_STONITH
Definition actions.h:74
#define PCMK_ACTION_DO_SHUTDOWN
Definition actions.h:51
#define PCMK_ACTION_NOTIFY
Definition actions.h:62
char * pcmk__notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition actions.c:337
#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:196
bool pcmk__is_fencing_action(const char *action)
Definition actions.c:583
#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:48
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:98
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:484
char uname[MAX_NAME]
Definition cpg.c:5
char data[0]
Definition cpg.c:10
A dumping ground.
#define CRM_FEATURE_SET
Definition crm.h:72
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:392
gboolean crm_config_warning
Definition utils.c:49
#define crm_crit(fmt, args...)
Definition logging.h:386
#define CRM_LOG_ASSERT(expr)
Definition logging.h:228
#define crm_notice(fmt, args...)
Definition logging.h:395
#define CRM_CHECK(expr, failure_action)
Definition logging.h:245
#define crm_debug(fmt, args...)
Definition logging.h:400
#define crm_err(fmt, args...)
Definition logging.h:389
gboolean crm_config_error
Definition utils.c:48
#define crm_log_xml_trace(xml, text)
Definition logging.h:410
#define crm_trace(fmt, args...)
Definition logging.h:402
pcmk_scheduler_t * scheduler
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:764
void hash2field(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry.
Definition nvpair.c:736
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:348
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:694
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:398
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:301
#define PCMK_META_INTERVAL
Definition options.h:91
#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:45
#define PCMK_OPT_STONITH_TIMEOUT
Definition options.h:67
#define PCMK_OPT_SHUTDOWN_LOCK
Definition options.h:60
const char * pcmk__cluster_option(GHashTable *options, const char *name)
Definition options.c:1412
#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:30
#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__add_rsc_actions_to_graph(pcmk_resource_t *rsc)
void pcmk__log_transition_summary(const char *filename)
#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:83
@ pcmk_rsc_variant_primitive
Definition resources.h:37
@ pcmk_rsc_unique
Definition resources.h:100
@ pcmk_rsc_maintenance
Definition resources.h:166
@ pcmk_rsc_removed
Definition resources.h:85
@ pcmk_rsc_managed
Definition resources.h:88
@ pcmk_rsc_failed
Definition resources.h:133
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk_rc_ok
Definition results.h:162
@ pcmk_no_quorum_ignore
Definition scheduler.h:43
gboolean was_processing_error
Definition scheduler.c:21
@ pcmk_sched_quorate
Definition scheduler.h:80
@ pcmk_sched_start_failure_fatal
Definition scheduler.h:124
gboolean was_processing_warning
Definition scheduler.c:22
#define pcmk__rsc_trace(rsc, fmt, args...)
#define pcmk__sched_err(fmt...)
void pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
Definition strings.c:701
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:1026
@ pcmk__str_none
@ pcmk__str_null_matches
bool pcmk__ends_with(const char *s, const char *match)
Definition strings.c:608
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:1050
pcmk_node_t * node
Definition actions.h:341
char * uuid
Definition actions.h:344
enum pe_ordering type
Definition actions.h:317
struct pe_node_shared_s * details
Definition nodes.h:167
const char * id
Definition nodes.h:72
const char * uname
Definition nodes.h:73
gboolean remote_maintenance
Definition nodes.h:124
gboolean maintenance
Definition nodes.h:104
pcmk_assignment_methods_t * cmds
Definition resources.h:413
GList * actions
Definition resources.h:444
GList * children
Definition resources.h:471
pcmk_scheduler_t * cluster
Definition resources.h:408
GHashTable * config_hash
Definition scheduler.h:219
GList * resources
Definition scheduler.h:231
xmlNode * graph
Definition scheduler.h:247
unsigned long long flags
Definition scheduler.h:211
enum pe_quorum_policy no_quorum_policy
Definition scheduler.h:217
void(* add_actions_to_graph)(pcmk_resource_t *rsc)
void(* add_graph_meta)(const pcmk_resource_t *rsc, xmlNode *xml)
Wrappers for and extensions to libxml2.
void free_xml(xmlNode *child)
Definition xml.c:867
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition xml.c:2072
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition xml.c:720
#define PCMK_XA_CLASS
Definition xml_names.h:241
#define PCMK_XE_NODE
Definition xml_names.h:133
#define PCMK_XA_OPERATION
Definition xml_names.h:344
#define PCMK_XA_ID
Definition xml_names.h:296
#define PCMK_XA_PROVIDER
Definition xml_names.h:359
#define PCMK_XA_CRM_FEATURE_SET
Definition xml_names.h:249
#define PCMK_XA_TYPE
Definition xml_names.h:425
#define PCMK__XA_PRIORITY
#define PCMK__XE_TRANSITION_GRAPH
#define PCMK__XE_RSC_OP
#define PCMK__XE_CRM_EVENT
#define PCMK__XE_PSEUDO_EVENT
#define PCMK__XA_ROUTER_NODE
#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