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