pacemaker 2.1.7-2.1.7
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-2023 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), pcmk_action_pseudo)? "pseudo-action" : "action")
28
29#define action_optional_str(flags) \
30 (pcmk_is_set((flags), pcmk_action_optional)? "optional" : "required")
31
32#define action_runnable_str(flags) \
33 (pcmk_is_set((flags), pcmk_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_ID, id);
52
53 return node_xml;
54}
55
63static void
64add_node_to_xml(const pcmk_node_t *node, void *xml)
65{
66 add_node_to_xml_by_id(node->details->id, (xmlNode *) xml);
67}
68
79static int
80add_maintenance_nodes(xmlNode *xml, const pcmk_scheduler_t *scheduler)
81{
82 xmlNode *maintenance = NULL;
83 int count = 0;
84
85 if (xml != NULL) {
87 }
88 for (const GList *iter = scheduler->nodes;
89 iter != NULL; iter = iter->next) {
90 const pcmk_node_t *node = iter->data;
91
93 (node->details->maintenance != node->details->remote_maintenance)) {
94
95 if (maintenance != NULL) {
96 crm_xml_add(add_node_to_xml_by_id(node->details->id,
97 maintenance),
99 (node->details->maintenance? "1" : "0"));
100 }
101 count++;
102 }
103 }
104 crm_trace("%s %d nodes in need of maintenance mode update in state",
105 ((maintenance == NULL)? "Counted" : "Added"), count);
106 return count;
107}
108
115static void
116add_maintenance_update(pcmk_scheduler_t *scheduler)
117{
118 pcmk_action_t *action = NULL;
119
120 if (add_maintenance_nodes(NULL, scheduler) != 0) {
123 }
124}
125
137static void
138add_downed_nodes(xmlNode *xml, const pcmk_action_t *action)
139{
140 CRM_CHECK((xml != NULL) && (action != NULL) && (action->node != NULL),
141 return);
142
143 if (pcmk__str_eq(action->task, PCMK_ACTION_DO_SHUTDOWN, pcmk__str_none)) {
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, PCMK_ACTION_STONITH,
151
152 /* Fencing makes the action's node and any hosted guest nodes down */
153 const char *fence = g_hash_table_lookup(action->meta, "stonith_action");
154
155 if (pcmk__is_fencing_action(fence)) {
156 xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_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 = create_xml_node(xml, XML_GRAPH_TAG_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, XML_LRM_ATTR_TARGET, action->node->details->uname);
231 crm_xml_add(xml, XML_LRM_ATTR_TARGET_UUID, action->node->details->id);
232 if (router_node != NULL) {
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 shutdown-lock, mark its actions
255 * so the controller can preserve the lock when the action completes.
256 */
259 (long long) action->rsc->lock_time);
260 }
261
262 // List affected resource
263
264 rsc_xml = create_xml_node(action_xml,
265 (const char *) action->rsc->xml->name);
266 if (pcmk_is_set(action->rsc->flags, pcmk_rsc_removed)
267 && (action->rsc->clone_name != NULL)) {
268 /* Use the numbered instance name here, because if there is more
269 * than one instance on a node, we need to make sure the command
270 * goes to the right one.
271 *
272 * This is important even for anonymous clones, because the clone's
273 * unique meta-attribute might have just been toggled from on to
274 * off.
275 */
276 crm_debug("Using orphan clone name %s instead of %s",
277 action->rsc->id, action->rsc->clone_name);
278 crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->clone_name);
279 crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id);
280
281 } else if (!pcmk_is_set(action->rsc->flags, pcmk_rsc_unique)) {
282 const char *xml_id = ID(action->rsc->xml);
283
284 crm_debug("Using anonymous clone name %s for %s (aka %s)",
285 xml_id, action->rsc->id, action->rsc->clone_name);
286
287 /* ID is what we'd like client to use
288 * ID_LONG is what they might know it as instead
289 *
290 * ID_LONG is only strictly needed /here/ during the
291 * transition period until all nodes in the cluster
292 * are running the new software /and/ have rebooted
293 * once (meaning that they've only ever spoken to a DC
294 * supporting this feature).
295 *
296 * If anyone toggles the unique flag to 'on', the
297 * 'instance free' name will correspond to an orphan
298 * and fall into the clause above instead
299 */
300 crm_xml_add(rsc_xml, XML_ATTR_ID, xml_id);
301 if ((action->rsc->clone_name != NULL)
302 && !pcmk__str_eq(xml_id, action->rsc->clone_name,
304 crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->clone_name);
305 } else {
306 crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id);
307 }
308
309 } else {
310 CRM_ASSERT(action->rsc->clone_name == NULL);
311 crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->id);
312 }
313
314 for (int lpc = 0; lpc < PCMK__NELEM(attr_list); lpc++) {
315 crm_xml_add(rsc_xml, attr_list[lpc],
316 g_hash_table_lookup(action->rsc->meta, attr_list[lpc]));
317 }
318}
319
327static void
328add_action_attributes(pcmk_action_t *action, xmlNode *action_xml)
329{
330 xmlNode *args_xml = NULL;
331
332 /* We create free-standing XML to start, so we can sort the attributes
333 * before adding it to action_xml, which keeps the scheduler regression
334 * test graphs comparable.
335 */
336 args_xml = create_xml_node(NULL, XML_TAG_ATTRS);
337
339 g_hash_table_foreach(action->extra, hash2field, args_xml);
340
341 if ((action->rsc != NULL) && (action->node != NULL)) {
342 // Get the resource instance attributes, evaluated properly for node
343 GHashTable *params = pe_rsc_params(action->rsc, action->node,
344 action->rsc->cluster);
345
347
348 g_hash_table_foreach(params, hash2smartfield, args_xml);
349
350 } else if ((action->rsc != NULL)
351 && (action->rsc->variant <= pcmk_rsc_variant_primitive)) {
352 GHashTable *params = pe_rsc_params(action->rsc, NULL,
353 action->rsc->cluster);
354
355 g_hash_table_foreach(params, hash2smartfield, args_xml);
356 }
357
358 g_hash_table_foreach(action->meta, hash2metafield, args_xml);
359 if (action->rsc != NULL) {
361
362 while (parent != NULL) {
363 parent->cmds->add_graph_meta(parent, args_xml);
364 parent = parent->parent;
365 }
366
368
369 } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH, pcmk__str_none)
370 && (action->node != NULL)) {
371 /* Pass the node's attributes as meta-attributes.
372 *
373 * @TODO: Determine whether it is still necessary to do this. It was
374 * added in 33d99707, probably for the libfence-based implementation in
375 * c9a90bd, which is no longer used.
376 */
377 g_hash_table_foreach(action->node->details->attrs, hash2metafield,
378 args_xml);
379 }
380
381 sorted_xml(args_xml, action_xml, FALSE);
382 free_xml(args_xml);
383}
384
394static void
395create_graph_action(xmlNode *parent, pcmk_action_t *action, bool skip_details,
397{
398 bool needs_node_info = true;
399 bool needs_maintenance_info = false;
400 xmlNode *action_xml = NULL;
401
402 if ((action == NULL) || (scheduler == NULL)) {
403 return;
404 }
405
406 // Create the top-level element based on task
407
408 if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH, pcmk__str_none)) {
409 /* All fences need node info; guest node fences are pseudo-events */
412 } else {
414 }
415
416 } else if (pcmk__str_any_of(action->task,
420
421 } else if (pcmk__str_eq(action->task, PCMK_ACTION_LRM_DELETE,
423 // CIB-only clean-up for shutdown locks
426
427 } else if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
428 if (pcmk__str_eq(action->task, PCMK_ACTION_MAINTENANCE_NODES,
430 needs_maintenance_info = true;
431 }
433 needs_node_info = false;
434
435 } else {
437 }
438
439 crm_xml_add_int(action_xml, XML_ATTR_ID, action->id);
440 crm_xml_add(action_xml, XML_LRM_ATTR_TASK, action->task);
441
442 if ((action->rsc != NULL) && (action->rsc->clone_name != NULL)) {
443 char *clone_key = NULL;
444 guint interval_ms;
445
447 &interval_ms) != pcmk_rc_ok) {
448 interval_ms = 0;
449 }
450 clone_key = clone_op_key(action, interval_ms);
451 crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, clone_key);
452 crm_xml_add(action_xml, "internal_" XML_LRM_ATTR_TASK_KEY,
453 action->uuid);
454 free(clone_key);
455 } else {
456 crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, action->uuid);
457 }
458
459 if (needs_node_info && (action->node != NULL)) {
460 add_node_details(action, action_xml);
461 g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET),
462 strdup(action->node->details->uname));
463 g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET_UUID),
464 strdup(action->node->details->id));
465 }
466
467 if (skip_details) {
468 return;
469 }
470
471 if ((action->rsc != NULL)
473
474 // This is a real resource action, so add resource details
475 add_resource_details(action, action_xml);
476 }
477
478 /* List any attributes in effect */
479 add_action_attributes(action, action_xml);
480
481 /* List any nodes this action is expected to make down */
482 if (needs_node_info && (action->node != NULL)) {
483 add_downed_nodes(action_xml, action);
484 }
485
486 if (needs_maintenance_info) {
487 add_maintenance_nodes(action_xml, scheduler);
488 }
489}
490
499static bool
500should_add_action_to_graph(const pcmk_action_t *action)
501{
503 crm_trace("Ignoring action %s (%d): unrunnable",
504 action->uuid, action->id);
505 return false;
506 }
507
510 crm_trace("Ignoring action %s (%d): optional",
511 action->uuid, action->id);
512 return false;
513 }
514
515 /* Actions for unmanaged resources should be excluded from the graph,
516 * with the exception of monitors and cancellation of recurring monitors.
517 */
518 if ((action->rsc != NULL)
519 && !pcmk_is_set(action->rsc->flags, pcmk_rsc_managed)
520 && !pcmk__str_eq(action->task, PCMK_ACTION_MONITOR, pcmk__str_none)) {
521
522 const char *interval_ms_s;
523
524 /* A cancellation of a recurring monitor will get here because the task
525 * is cancel rather than monitor, but the interval can still be used to
526 * recognize it. The interval has been normalized to milliseconds by
527 * this point, so a string comparison is sufficient.
528 */
529 interval_ms_s = g_hash_table_lookup(action->meta,
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 pe_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, pe__node_name(action->node));
559
560 } else if (pe__is_guest_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, pe__node_name(action->node));
565
566 } else if (!action->node->details->online) {
567 pe_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 pe_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 (!pe__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 (!pe__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 && !pe__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 pe__node_name(action->node),
718 pe__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 && action->rsc && pe_rsc_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 = create_xml_node(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, XML_CIB_ATTR_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 = create_xml_node(syn, "action_set");
919 in = create_xml_node(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 = create_xml_node(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
952 } else if (was_processing_warning) {
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 pe_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
1008 transition_id++;
1009 crm_trace("Creating transition graph %d", transition_id);
1010
1012
1013 value = pe_pref(scheduler->config_hash, "cluster-delay");
1014 crm_xml_add(scheduler->graph, "cluster-delay", value);
1015
1016 value = pe_pref(scheduler->config_hash, "stonith-timeout");
1017 crm_xml_add(scheduler->graph, "stonith-timeout", value);
1018
1019 crm_xml_add(scheduler->graph, "failed-stop-offset", "INFINITY");
1020
1022 crm_xml_add(scheduler->graph, "failed-start-offset", "INFINITY");
1023 } else {
1024 crm_xml_add(scheduler->graph, "failed-start-offset", "1");
1025 }
1026
1027 value = pe_pref(scheduler->config_hash, "batch-limit");
1028 crm_xml_add(scheduler->graph, "batch-limit", value);
1029
1030 crm_xml_add_int(scheduler->graph, "transition_id", transition_id);
1031
1032 value = pe_pref(scheduler->config_hash, "migration-limit");
1033 if ((pcmk__scan_ll(value, &limit, 0LL) == pcmk_rc_ok) && (limit > 0)) {
1034 crm_xml_add(scheduler->graph, "migration-limit", value);
1035 }
1036
1037 if (scheduler->recheck_by > 0) {
1038 char *recheck_epoch = NULL;
1039
1040 recheck_epoch = crm_strdup_printf("%llu",
1041 (long long) scheduler->recheck_by);
1042 crm_xml_add(scheduler->graph, "recheck-by", recheck_epoch);
1043 free(recheck_epoch);
1044 }
1045
1046 /* The following code will de-duplicate action inputs, so nothing past this
1047 * should rely on the action input type flags retaining their original
1048 * values.
1049 */
1050
1051 // Add resource actions to graph
1052 for (iter = scheduler->resources; iter != NULL; iter = iter->next) {
1053 pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
1054
1055 pe_rsc_trace(rsc, "Processing actions for %s", rsc->id);
1056 rsc->cmds->add_actions_to_graph(rsc);
1057 }
1058
1059 // Add pseudo-action for list of nodes with maintenance state update
1060 add_maintenance_update(scheduler);
1061
1062 // Add non-resource (node) actions
1063 for (iter = scheduler->actions; iter != NULL; iter = iter->next) {
1064 pcmk_action_t *action = (pcmk_action_t *) iter->data;
1065
1066 if ((action->rsc != NULL)
1067 && (action->node != NULL)
1068 && action->node->details->shutdown
1069 && !pcmk_is_set(action->rsc->flags, pcmk_rsc_maintenance)
1070 && !pcmk_any_flags_set(action->flags,
1072 && pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_none)) {
1073 /* Eventually we should just ignore the 'fence' case, but for now
1074 * it's the best way to detect (in CTS) when CIB resource updates
1075 * are being lost.
1076 */
1079 const bool managed = pcmk_is_set(action->rsc->flags,
1081 const bool failed = pcmk_is_set(action->rsc->flags,
1083
1084 crm_crit("Cannot %s %s because of %s:%s%s (%s)",
1085 action->node->details->unclean? "fence" : "shut down",
1086 pe__node_name(action->node), action->rsc->id,
1087 (managed? " blocked" : " unmanaged"),
1088 (failed? " failed" : ""), action->uuid);
1089 }
1090 }
1091
1092 add_action_to_graph((gpointer) action, (gpointer) scheduler);
1093 }
1094
1096}
@ 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:74
#define PCMK_ACTION_MAINTENANCE_NODES
Definition actions.h:55
pe_ordering
Definition actions.h:346
@ pe_link_dumped
Definition actions.h:340
#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:57
@ pcmk_action_detect_loop
Whether action has already been processed by a recursive procedure.
Definition actions.h:272
@ pcmk_action_runnable
Whether action is runnable.
Definition actions.h:241
@ pcmk_action_migratable
Whether action is allowed to be part of a live migration.
Definition actions.h:253
@ pcmk_action_added_to_graph
Whether action has been added to transition graph.
Definition actions.h:256
@ pcmk_action_pseudo
Whether action does not require invoking an agent.
Definition actions.h:238
@ pcmk_action_optional
Whether action should not be executed.
Definition actions.h:244
@ pcmk_action_on_dc
Whether action can be executed on DC rather than own node.
Definition actions.h:278
@ pcmk_action_always_in_graph
Whether action should be added to transition graph even if optional.
Definition actions.h:247
@ pcmk_action_inputs_deduplicated
Whether action's inputs have been de-duplicated yet.
Definition actions.h:275
#define PCMK_ACTION_MIGRATE_TO
Definition actions.h:58
#define PCMK_ACTION_MONITOR
Definition actions.h:59
#define PCMK_ACTION_STONITH
Definition actions.h:73
#define PCMK_ACTION_DO_SHUTDOWN
Definition actions.h:51
#define PCMK_ACTION_NOTIFY
Definition actions.h:61
char * pcmk__notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition actions.c:183
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:42
bool pcmk__is_fencing_action(const char *action)
Definition actions.c:489
const char * parent
Definition cib.c:27
Cluster Configuration.
#define PCMK__NELEM(a)
Definition internal.h:46
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:99
gboolean was_processing_error
Definition common.c:21
const char * pe_pref(GHashTable *options, const char *name)
Definition common.c:314
gboolean was_processing_warning
Definition common.c:22
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:446
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:70
#define PCMK__XA_MODE
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__add_bundle_meta_to_xml(xmlNode *args_xml, const pcmk_action_t *action)
Add special bundle meta-attributes to XML.
G_GNUC_INTERNAL void pcmk__deduplicate_action_inputs(pcmk_action_t *action)
#define crm_warn(fmt, args...)
Definition logging.h:380
#define crm_crit(fmt, args...)
Definition logging.h:378
#define CRM_LOG_ASSERT(expr)
Definition logging.h:222
#define crm_notice(fmt, args...)
Definition logging.h:381
#define CRM_CHECK(expr, failure_action)
Definition logging.h:238
#define crm_debug(fmt, args...)
Definition logging.h:384
#define crm_err(fmt, args...)
Definition logging.h:379
gboolean crm_config_error
Definition utils.c:49
#define crm_log_xml_trace(xml, text)
Definition logging.h:393
#define crm_trace(fmt, args...)
Definition logging.h:385
#define XML_TAG_CIB
Definition msg_xml.h:137
#define XML_LRM_ATTR_ROUTER_NODE
Definition msg_xml.h:314
#define ID(x)
Definition msg_xml.h:474
#define XML_ATTR_CRM_VERSION
Definition msg_xml.h:140
#define XML_CIB_ATTR_PRIORITY
Definition msg_xml.h:286
#define XML_LRM_ATTR_TASK_KEY
Definition msg_xml.h:307
#define XML_CONFIG_ATTR_SHUTDOWN_LOCK
Definition msg_xml.h:403
#define XML_LRM_ATTR_TARGET_UUID
Definition msg_xml.h:309
#define XML_ATTR_ID
Definition msg_xml.h:156
#define XML_AGENT_ATTR_PROVIDER
Definition msg_xml.h:281
#define XML_AGENT_ATTR_CLASS
Definition msg_xml.h:280
#define XML_TAG_GRAPH
Definition msg_xml.h:333
#define XML_NODE_IS_MAINTENANCE
Definition msg_xml.h:290
#define XML_GRAPH_TAG_CRM_EVENT
Definition msg_xml.h:336
#define XML_TAG_ATTRS
Definition msg_xml.h:229
#define XML_ATTR_ID_LONG
Definition msg_xml.h:159
#define XML_GRAPH_TAG_DOWNED
Definition msg_xml.h:337
#define XML_LRM_ATTR_TASK
Definition msg_xml.h:306
#define XML_ATTR_TYPE
Definition msg_xml.h:160
#define XML_LRM_ATTR_TARGET
Definition msg_xml.h:308
#define XML_GRAPH_TAG_PSEUDO_EVENT
Definition msg_xml.h:335
#define XML_GRAPH_TAG_MAINTENANCE
Definition msg_xml.h:338
#define XML_GRAPH_TAG_RSC_OP
Definition msg_xml.h:334
#define XML_CIB_TAG_NODE
Definition msg_xml.h:223
#define XML_LRM_ATTR_INTERVAL_MS
Definition msg_xml.h:304
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:728
void hash2field(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry.
Definition nvpair.c:700
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:349
void hash2smartfield(gpointer key, gpointer value, gpointer user_data)
Add hash table entry to XML as (possibly legacy) name/value.
Definition nvpair.c:666
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:399
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:302
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)
#define pe_rsc_trace(rsc, fmt, args...)
Definition internal.h:37
#define pe__clear_action_flags(action, flags_to_clear)
Definition internal.h:85
#define pe_err(fmt...)
Definition internal.h:39
#define pe__set_action_flags(action, flags_to_set)
Definition internal.h:76
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:120
bool pe__is_guest_or_remote_node(const pcmk_node_t *node)
Definition remote.c:41
bool pe__is_guest_node(const pcmk_node_t *node)
Definition remote.c:33
@ pcmk_rsc_variant_primitive
Primitive resource.
Definition resources.h:34
@ pcmk_rsc_unique
Whether resource is not an anonymous clone instance.
Definition resources.h:118
@ pcmk_rsc_maintenance
Whether resource, its node, or entire cluster is in maintenance mode.
Definition resources.h:181
@ pcmk_rsc_removed
Whether resource has been removed from the configuration.
Definition resources.h:103
@ pcmk_rsc_managed
Whether resource is managed.
Definition resources.h:106
@ pcmk_rsc_failed
Whether resource is considered failed.
Definition resources.h:151
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk_rc_ok
Definition results.h:154
@ pcmk_no_quorum_ignore
Definition scheduler.h:41
@ pcmk_sched_quorate
Whether partition has quorum (via have-quorum property)
Definition scheduler.h:71
@ pcmk_sched_start_failure_fatal
Definition scheduler.h:110
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:933
@ pcmk__str_none
@ pcmk__str_null_matches
bool pcmk__ends_with(const char *s, const char *match)
Definition strings.c:533
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:957
Implementation of pcmk_action_t.
Definition actions.h:390
pcmk_node_t * node
Node to execute action on, if any.
Definition actions.h:401
char * uuid
Action key.
Definition actions.h:404
enum pe_ordering type
Definition actions.h:380
Implementation of pcmk_node_t.
Definition nodes.h:130
struct pe_node_shared_s * details
Basic node information.
Definition nodes.h:134
const char * id
Node ID at the cluster layer.
Definition nodes.h:67
const char * uname
Node name in cluster.
Definition nodes.h:68
gboolean remote_maintenance
Definition nodes.h:100
gboolean maintenance
Whether in maintenance mode.
Definition nodes.h:81
Implementation of pcmk_resource_t.
Definition resources.h:399
pcmk_assignment_methods_t * cmds
Resource assignment methods.
Definition resources.h:417
GList * actions
Definition resources.h:447
GList * children
Resource's child resources, if any.
Definition resources.h:475
pcmk_scheduler_t * cluster
Cluster that resource is part of.
Definition resources.h:412
char * id
Resource ID in configuration.
Definition resources.h:400
Implementation of pcmk_scheduler_t.
Definition scheduler.h:172
GHashTable * config_hash
Cluster properties.
Definition scheduler.h:187
GList * actions
Scheduled actions.
Definition scheduler.h:204
GList * resources
Resources in cluster.
Definition scheduler.h:196
xmlNode * graph
Transition graph.
Definition scheduler.h:212
unsigned long long flags
Group of enum pcmk_scheduler_flags.
Definition scheduler.h:183
enum pe_quorum_policy no_quorum_policy
Response to loss of quorum.
Definition scheduler.h:186
GList * nodes
Nodes in cluster.
Definition scheduler.h:195
time_t recheck_by
Hint to controller when to reschedule.
Definition scheduler.h:223
int num_synapse
Number of transition graph synapses.
Definition scheduler.h:208
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:783
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition xml.c:638
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition xml.c:2456