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