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