pacemaker  2.1.7-0f7f88312f
Scalable High-Availability cluster resource manager
pcmk_graph_producer.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2023 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU General Public License version 2
7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include <crm_internal.h>
11 
12 #include <sys/param.h>
13 #include <crm/crm.h>
14 #include <crm/cib.h>
15 #include <crm/msg_xml.h>
16 #include <crm/common/xml.h>
17 
18 #include <glib.h>
19 
20 #include <pacemaker-internal.h>
21 
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), pcmk_action_pseudo)? "pseudo-action" : "action")
28 
29 #define action_optional_str(flags) \
30  (pcmk_is_set((flags), pcmk_action_optional)? "optional" : "required")
31 
32 #define action_runnable_str(flags) \
33  (pcmk_is_set((flags), pcmk_action_runnable)? "runnable" : "unrunnable")
34 
35 #define action_node_str(a) \
36  (((a)->node == NULL)? "no node" : (a)->node->details->uname)
37 
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_ID, id);
52 
53  return node_xml;
54 }
55 
63 static void
64 add_node_to_xml(const pcmk_node_t *node, void *xml)
65 {
66  add_node_to_xml_by_id(node->details->id, (xmlNode *) xml);
67 }
68 
79 static int
80 add_maintenance_nodes(xmlNode *xml, const pcmk_scheduler_t *scheduler)
81 {
82  xmlNode *maintenance = NULL;
83  int count = 0;
84 
85  if (xml != NULL) {
86  maintenance = create_xml_node(xml, XML_GRAPH_TAG_MAINTENANCE);
87  }
88  for (const GList *iter = scheduler->nodes;
89  iter != NULL; iter = iter->next) {
90  const pcmk_node_t *node = iter->data;
91 
92  if (pe__is_guest_or_remote_node(node) &&
93  (node->details->maintenance != node->details->remote_maintenance)) {
94 
95  if (maintenance != NULL) {
96  crm_xml_add(add_node_to_xml_by_id(node->details->id,
97  maintenance),
99  (node->details->maintenance? "1" : "0"));
100  }
101  count++;
102  }
103  }
104  crm_trace("%s %d nodes in need of maintenance mode update in state",
105  ((maintenance == NULL)? "Counted" : "Added"), count);
106  return count;
107 }
108 
115 static void
116 add_maintenance_update(pcmk_scheduler_t *scheduler)
117 {
118  pcmk_action_t *action = NULL;
119 
120  if (add_maintenance_nodes(NULL, scheduler) != 0) {
123  }
124 }
125 
137 static void
138 add_downed_nodes(xmlNode *xml, const pcmk_action_t *action)
139 {
140  CRM_CHECK((xml != NULL) && (action != NULL) && (action->node != NULL),
141  return);
142 
143  if (pcmk__str_eq(action->task, PCMK_ACTION_DO_SHUTDOWN, pcmk__str_none)) {
144 
145  /* Shutdown makes the action's node down */
146  xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED);
147  add_node_to_xml_by_id(action->node->details->id, downed);
148 
149  } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH,
150  pcmk__str_none)) {
151 
152  /* Fencing makes the action's node and any hosted guest nodes down */
153  const char *fence = g_hash_table_lookup(action->meta, "stonith_action");
154 
155  if (pcmk__is_fencing_action(fence)) {
156  xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED);
157  add_node_to_xml_by_id(action->node->details->id, downed);
158  pe_foreach_guest_node(action->node->details->data_set,
159  action->node, add_node_to_xml, downed);
160  }
161 
162  } else if (action->rsc && action->rsc->is_remote_node
163  && pcmk__str_eq(action->task, PCMK_ACTION_STOP,
164  pcmk__str_none)) {
165 
166  /* Stopping a remote connection resource makes connected node down,
167  * unless it's part of a migration
168  */
169  GList *iter;
171  bool migrating = false;
172 
173  for (iter = action->actions_before; iter != NULL; iter = iter->next) {
174  input = ((pcmk__related_action_t *) iter->data)->action;
175  if ((input->rsc != NULL)
176  && pcmk__str_eq(action->rsc->id, input->rsc->id, pcmk__str_none)
177  && pcmk__str_eq(input->task, PCMK_ACTION_MIGRATE_FROM,
178  pcmk__str_none)) {
179  migrating = true;
180  break;
181  }
182  }
183  if (!migrating) {
184  xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED);
185  add_node_to_xml_by_id(action->rsc->id, downed);
186  }
187  }
188 }
189 
199 static char *
200 clone_op_key(const pcmk_action_t *action, guint interval_ms)
201 {
202  if (pcmk__str_eq(action->task, PCMK_ACTION_NOTIFY, pcmk__str_none)) {
203  const char *n_type = g_hash_table_lookup(action->meta, "notify_type");
204  const char *n_task = g_hash_table_lookup(action->meta,
205  "notify_operation");
206 
207  CRM_LOG_ASSERT((n_type != NULL) && (n_task != NULL));
208  return pcmk__notify_key(action->rsc->clone_name, n_type, n_task);
209 
210  } else if (action->cancel_task != NULL) {
211  return pcmk__op_key(action->rsc->clone_name, action->cancel_task,
212  interval_ms);
213  } else {
214  return pcmk__op_key(action->rsc->clone_name, action->task, interval_ms);
215  }
216 }
217 
225 static void
226 add_node_details(const pcmk_action_t *action, xmlNode *xml)
227 {
229 
230  crm_xml_add(xml, XML_LRM_ATTR_TARGET, action->node->details->uname);
231  crm_xml_add(xml, XML_LRM_ATTR_TARGET_UUID, action->node->details->id);
232  if (router_node != NULL) {
233  crm_xml_add(xml, XML_LRM_ATTR_ROUTER_NODE, router_node->details->uname);
234  }
235 }
236 
244 static void
245 add_resource_details(const pcmk_action_t *action, xmlNode *action_xml)
246 {
247  xmlNode *rsc_xml = NULL;
248  const char *attr_list[] = {
252  };
253 
254  /* If a resource is locked to a node via shutdown-lock, mark its actions
255  * so the controller can preserve the lock when the action completes.
256  */
259  (long long) action->rsc->lock_time);
260  }
261 
262  // List affected resource
263 
264  rsc_xml = create_xml_node(action_xml,
265  (const char *) action->rsc->xml->name);
266  if (pcmk_is_set(action->rsc->flags, pcmk_rsc_removed)
267  && (action->rsc->clone_name != NULL)) {
268  /* Use the numbered instance name here, because if there is more
269  * than one instance on a node, we need to make sure the command
270  * goes to the right one.
271  *
272  * This is important even for anonymous clones, because the clone's
273  * unique meta-attribute might have just been toggled from on to
274  * off.
275  */
276  crm_debug("Using orphan clone name %s instead of %s",
277  action->rsc->id, action->rsc->clone_name);
278  crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->clone_name);
279  crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id);
280 
281  } else if (!pcmk_is_set(action->rsc->flags, pcmk_rsc_unique)) {
282  const char *xml_id = ID(action->rsc->xml);
283 
284  crm_debug("Using anonymous clone name %s for %s (aka %s)",
285  xml_id, action->rsc->id, action->rsc->clone_name);
286 
287  /* ID is what we'd like client to use
288  * ID_LONG is what they might know it as instead
289  *
290  * ID_LONG is only strictly needed /here/ during the
291  * transition period until all nodes in the cluster
292  * are running the new software /and/ have rebooted
293  * once (meaning that they've only ever spoken to a DC
294  * supporting this feature).
295  *
296  * If anyone toggles the unique flag to 'on', the
297  * 'instance free' name will correspond to an orphan
298  * and fall into the clause above instead
299  */
300  crm_xml_add(rsc_xml, XML_ATTR_ID, xml_id);
301  if ((action->rsc->clone_name != NULL)
302  && !pcmk__str_eq(xml_id, action->rsc->clone_name,
303  pcmk__str_none)) {
304  crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->clone_name);
305  } else {
306  crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id);
307  }
308 
309  } else {
310  CRM_ASSERT(action->rsc->clone_name == NULL);
311  crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->id);
312  }
313 
314  for (int lpc = 0; lpc < PCMK__NELEM(attr_list); lpc++) {
315  crm_xml_add(rsc_xml, attr_list[lpc],
316  g_hash_table_lookup(action->rsc->meta, attr_list[lpc]));
317  }
318 }
319 
327 static void
328 add_action_attributes(pcmk_action_t *action, xmlNode *action_xml)
329 {
330  xmlNode *args_xml = NULL;
331 
332  /* We create free-standing XML to start, so we can sort the attributes
333  * before adding it to action_xml, which keeps the scheduler regression
334  * test graphs comparable.
335  */
336  args_xml = create_xml_node(NULL, XML_TAG_ATTRS);
337 
339  g_hash_table_foreach(action->extra, hash2field, args_xml);
340 
341  if ((action->rsc != NULL) && (action->node != NULL)) {
342  // Get the resource instance attributes, evaluated properly for node
343  GHashTable *params = pe_rsc_params(action->rsc, action->node,
344  action->rsc->cluster);
345 
346  pcmk__substitute_remote_addr(action->rsc, params);
347 
348  g_hash_table_foreach(params, hash2smartfield, args_xml);
349 
350  } else if ((action->rsc != NULL)
351  && (action->rsc->variant <= pcmk_rsc_variant_primitive)) {
352  GHashTable *params = pe_rsc_params(action->rsc, NULL,
353  action->rsc->cluster);
354 
355  g_hash_table_foreach(params, hash2smartfield, args_xml);
356  }
357 
358  g_hash_table_foreach(action->meta, hash2metafield, args_xml);
359  if (action->rsc != NULL) {
360  pcmk_resource_t *parent = action->rsc;
361 
362  while (parent != NULL) {
363  parent->cmds->add_graph_meta(parent, args_xml);
364  parent = parent->parent;
365  }
366 
368 
369  } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH, pcmk__str_none)
370  && (action->node != NULL)) {
371  /* Pass the node's attributes as meta-attributes.
372  *
373  * @TODO: Determine whether it is still necessary to do this. It was
374  * added in 33d99707, probably for the libfence-based implementation in
375  * c9a90bd, which is no longer used.
376  */
377  g_hash_table_foreach(action->node->details->attrs, hash2metafield,
378  args_xml);
379  }
380 
381  sorted_xml(args_xml, action_xml, FALSE);
382  free_xml(args_xml);
383 }
384 
394 static void
395 create_graph_action(xmlNode *parent, pcmk_action_t *action, bool skip_details,
397 {
398  bool needs_node_info = true;
399  bool needs_maintenance_info = false;
400  xmlNode *action_xml = NULL;
401 
402  if ((action == NULL) || (scheduler == NULL)) {
403  return;
404  }
405 
406  // Create the top-level element based on task
407 
408  if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH, pcmk__str_none)) {
409  /* All fences need node info; guest node fences are pseudo-events */
410  if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
412  } else {
414  }
415 
416  } else if (pcmk__str_any_of(action->task,
420 
421  } else if (pcmk__str_eq(action->task, PCMK_ACTION_LRM_DELETE,
422  pcmk__str_none)) {
423  // CIB-only clean-up for shutdown locks
425  crm_xml_add(action_xml, PCMK__XA_MODE, XML_TAG_CIB);
426 
427  } else if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
428  if (pcmk__str_eq(action->task, PCMK_ACTION_MAINTENANCE_NODES,
429  pcmk__str_none)) {
430  needs_maintenance_info = true;
431  }
433  needs_node_info = false;
434 
435  } else {
437  }
438 
439  crm_xml_add_int(action_xml, XML_ATTR_ID, action->id);
440  crm_xml_add(action_xml, XML_LRM_ATTR_TASK, action->task);
441 
442  if ((action->rsc != NULL) && (action->rsc->clone_name != NULL)) {
443  char *clone_key = NULL;
444  guint interval_ms;
445 
447  &interval_ms) != pcmk_rc_ok) {
448  interval_ms = 0;
449  }
450  clone_key = clone_op_key(action, interval_ms);
451  crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, clone_key);
452  crm_xml_add(action_xml, "internal_" XML_LRM_ATTR_TASK_KEY,
453  action->uuid);
454  free(clone_key);
455  } else {
456  crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, action->uuid);
457  }
458 
459  if (needs_node_info && (action->node != NULL)) {
460  add_node_details(action, action_xml);
461  g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET),
462  strdup(action->node->details->uname));
463  g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET_UUID),
464  strdup(action->node->details->id));
465  }
466 
467  if (skip_details) {
468  return;
469  }
470 
471  if ((action->rsc != NULL)
472  && !pcmk_is_set(action->flags, pcmk_action_pseudo)) {
473 
474  // This is a real resource action, so add resource details
475  add_resource_details(action, action_xml);
476  }
477 
478  /* List any attributes in effect */
479  add_action_attributes(action, action_xml);
480 
481  /* List any nodes this action is expected to make down */
482  if (needs_node_info && (action->node != NULL)) {
483  add_downed_nodes(action_xml, action);
484  }
485 
486  if (needs_maintenance_info) {
487  add_maintenance_nodes(action_xml, scheduler);
488  }
489 }
490 
499 static bool
500 should_add_action_to_graph(const pcmk_action_t *action)
501 {
502  if (!pcmk_is_set(action->flags, pcmk_action_runnable)) {
503  crm_trace("Ignoring action %s (%d): unrunnable",
504  action->uuid, action->id);
505  return false;
506  }
507 
510  crm_trace("Ignoring action %s (%d): optional",
511  action->uuid, action->id);
512  return false;
513  }
514 
515  /* Actions for unmanaged resources should be excluded from the graph,
516  * with the exception of monitors and cancellation of recurring monitors.
517  */
518  if ((action->rsc != NULL)
519  && !pcmk_is_set(action->rsc->flags, pcmk_rsc_managed)
520  && !pcmk__str_eq(action->task, PCMK_ACTION_MONITOR, pcmk__str_none)) {
521 
522  const char *interval_ms_s;
523 
524  /* A cancellation of a recurring monitor will get here because the task
525  * is cancel rather than monitor, but the interval can still be used to
526  * recognize it. The interval has been normalized to milliseconds by
527  * this point, so a string comparison is sufficient.
528  */
529  interval_ms_s = g_hash_table_lookup(action->meta,
531  if (pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches)) {
532  crm_trace("Ignoring action %s (%d): for unmanaged resource (%s)",
533  action->uuid, action->id, action->rsc->id);
534  return false;
535  }
536  }
537 
538  /* Always add pseudo-actions, fence actions, and shutdown actions (already
539  * determined to be required and runnable by this point)
540  */
543  PCMK_ACTION_DO_SHUTDOWN, NULL)) {
544  return true;
545  }
546 
547  if (action->node == NULL) {
548  pe_err("Skipping action %s (%d) "
549  "because it was not assigned to a node (bug?)",
550  action->uuid, action->id);
551  pcmk__log_action("Unassigned", action, false);
552  return false;
553  }
554 
555  if (pcmk_is_set(action->flags, pcmk_action_on_dc)) {
556  crm_trace("Action %s (%d) should be dumped: "
557  "can run on DC instead of %s",
558  action->uuid, action->id, pe__node_name(action->node));
559 
560  } else if (pe__is_guest_node(action->node)
561  && !action->node->details->remote_requires_reset) {
562  crm_trace("Action %s (%d) should be dumped: "
563  "assuming will be runnable on guest %s",
564  action->uuid, action->id, pe__node_name(action->node));
565 
566  } else if (!action->node->details->online) {
567  pe_err("Skipping action %s (%d) "
568  "because it was scheduled for offline node (bug?)",
569  action->uuid, action->id);
570  pcmk__log_action("Offline node", action, false);
571  return false;
572 
573  } else if (action->node->details->unclean) {
574  pe_err("Skipping action %s (%d) "
575  "because it was scheduled for unclean node (bug?)",
576  action->uuid, action->id);
577  pcmk__log_action("Unclean node", action, false);
578  return false;
579  }
580  return true;
581 }
582 
591 static bool
592 ordering_can_change_actions(const pcmk__related_action_t *ordering)
593 {
594  return pcmk_any_flags_set(ordering->type,
597  |pcmk__ar_ordered));
598 }
599 
611 static bool
612 should_add_input_to_graph(const pcmk_action_t *action,
614 {
615  if (input->state == pe_link_dumped) {
616  return true;
617  }
618 
619  if ((uint32_t) input->type == pcmk__ar_none) {
620  crm_trace("Ignoring %s (%d) input %s (%d): "
621  "ordering disabled",
622  action->uuid, action->id,
623  input->action->uuid, input->action->id);
624  return false;
625 
626  } else if (!pcmk_is_set(input->action->flags, pcmk_action_runnable)
627  && !ordering_can_change_actions(input)) {
628  crm_trace("Ignoring %s (%d) input %s (%d): "
629  "optional and input unrunnable",
630  action->uuid, action->id,
631  input->action->uuid, input->action->id);
632  return false;
633 
634  } else if (!pcmk_is_set(input->action->flags, pcmk_action_runnable)
636  crm_trace("Ignoring %s (%d) input %s (%d): "
637  "minimum number of instances required but input unrunnable",
638  action->uuid, action->id,
639  input->action->uuid, input->action->id);
640  return false;
641 
643  && !pcmk_is_set(input->action->flags, pcmk_action_runnable)) {
644  crm_trace("Ignoring %s (%d) input %s (%d): "
645  "input blocked if 'then' unmigratable",
646  action->uuid, action->id,
647  input->action->uuid, input->action->id);
648  return false;
649 
651  && pcmk_is_set(input->action->flags, pcmk_action_migratable)) {
652  crm_trace("Ignoring %s (%d) input %s (%d): ordering applies "
653  "only if input is unmigratable, but it is migratable",
654  action->uuid, action->id,
655  input->action->uuid, input->action->id);
656  return false;
657 
658  } else if (((uint32_t) input->type == pcmk__ar_ordered)
659  && pcmk_is_set(input->action->flags, pcmk_action_migratable)
660  && pcmk__ends_with(input->action->uuid, "_stop_0")) {
661  crm_trace("Ignoring %s (%d) input %s (%d): "
662  "optional but stop in migration",
663  action->uuid, action->id,
664  input->action->uuid, input->action->id);
665  return false;
666 
667  } else if ((uint32_t) input->type == pcmk__ar_if_on_same_node_or_target) {
668  pcmk_node_t *input_node = input->action->node;
669 
670  if ((action->rsc != NULL)
671  && pcmk__str_eq(action->task, PCMK_ACTION_MIGRATE_TO,
672  pcmk__str_none)) {
673 
674  pcmk_node_t *assigned = action->rsc->allocated_to;
675 
676  /* For load_stopped -> migrate_to orderings, we care about where
677  * the resource has been assigned, not where migrate_to will be
678  * executed.
679  */
680  if (!pe__same_node(input_node, assigned)) {
681  crm_trace("Ignoring %s (%d) input %s (%d): "
682  "migration target %s is not same as input node %s",
683  action->uuid, action->id,
684  input->action->uuid, input->action->id,
685  (assigned? assigned->details->uname : "<none>"),
686  (input_node? input_node->details->uname : "<none>"));
687  input->type = (enum pe_ordering) pcmk__ar_none;
688  return false;
689  }
690 
691  } else if (!pe__same_node(input_node, action->node)) {
692  crm_trace("Ignoring %s (%d) input %s (%d): "
693  "not on same node (%s vs %s)",
694  action->uuid, action->id,
695  input->action->uuid, input->action->id,
696  (action->node? action->node->details->uname : "<none>"),
697  (input_node? input_node->details->uname : "<none>"));
698  input->type = (enum pe_ordering) pcmk__ar_none;
699  return false;
700 
701  } else if (pcmk_is_set(input->action->flags, pcmk_action_optional)) {
702  crm_trace("Ignoring %s (%d) input %s (%d): "
703  "ordering optional",
704  action->uuid, action->id,
705  input->action->uuid, input->action->id);
706  input->type = (enum pe_ordering) pcmk__ar_none;
707  return false;
708  }
709 
710  } else if ((uint32_t) input->type == pcmk__ar_if_required_on_same_node) {
711  if (input->action->node && action->node
712  && !pe__same_node(input->action->node, action->node)) {
713  crm_trace("Ignoring %s (%d) input %s (%d): "
714  "not on same node (%s vs %s)",
715  action->uuid, action->id,
716  input->action->uuid, input->action->id,
717  pe__node_name(action->node),
718  pe__node_name(input->action->node));
719  input->type = (enum pe_ordering) pcmk__ar_none;
720  return false;
721 
722  } else if (pcmk_is_set(input->action->flags, pcmk_action_optional)) {
723  crm_trace("Ignoring %s (%d) input %s (%d): optional",
724  action->uuid, action->id,
725  input->action->uuid, input->action->id);
726  input->type = (enum pe_ordering) pcmk__ar_none;
727  return false;
728  }
729 
730  } else if (input->action->rsc
731  && input->action->rsc != action->rsc
732  && pcmk_is_set(input->action->rsc->flags, pcmk_rsc_failed)
733  && !pcmk_is_set(input->action->rsc->flags, pcmk_rsc_managed)
734  && pcmk__ends_with(input->action->uuid, "_stop_0")
735  && action->rsc && pe_rsc_is_clone(action->rsc)) {
736  crm_warn("Ignoring requirement that %s complete before %s:"
737  " unmanaged failed resources cannot prevent clone shutdown",
738  input->action->uuid, action->uuid);
739  return false;
740 
741  } else if (pcmk_is_set(input->action->flags, pcmk_action_optional)
742  && !pcmk_any_flags_set(input->action->flags,
745  && !should_add_action_to_graph(input->action)) {
746  crm_trace("Ignoring %s (%d) input %s (%d): "
747  "input optional",
748  action->uuid, action->id,
749  input->action->uuid, input->action->id);
750  return false;
751  }
752 
753  crm_trace("%s (%d) input %s %s (%d) on %s should be dumped: %s %s %#.6x",
754  action->uuid, action->id, action_type_str(input->action->flags),
755  input->action->uuid, input->action->id,
756  action_node_str(input->action),
757  action_runnable_str(input->action->flags),
758  action_optional_str(input->action->flags), input->type);
759  return true;
760 }
761 
774 bool
777 {
778  bool has_loop = false;
779 
780  if (pcmk_is_set(input->action->flags, pcmk_action_detect_loop)) {
781  crm_trace("Breaking tracking loop: %s@%s -> %s@%s (%#.6x)",
782  input->action->uuid,
783  input->action->node? input->action->node->details->uname : "",
784  action->uuid,
785  action->node? action->node->details->uname : "",
786  input->type);
787  return false;
788  }
789 
790  // Don't need to check inputs that won't be used
791  if (!should_add_input_to_graph(action, input)) {
792  return false;
793  }
794 
795  if (input->action == init_action) {
796  crm_debug("Input loop found in %s@%s ->...-> %s@%s",
797  action->uuid,
798  action->node? action->node->details->uname : "",
799  init_action->uuid,
800  init_action->node? init_action->node->details->uname : "");
801  return true;
802  }
803 
805 
806  crm_trace("Checking inputs of action %s@%s input %s@%s (%#.6x)"
807  "for graph loop with %s@%s ",
808  action->uuid,
809  action->node? action->node->details->uname : "",
810  input->action->uuid,
811  input->action->node? input->action->node->details->uname : "",
812  input->type,
813  init_action->uuid,
814  init_action->node? init_action->node->details->uname : "");
815 
816  // Recursively check input itself for loops
817  for (GList *iter = input->action->actions_before;
818  iter != NULL; iter = iter->next) {
819 
820  if (pcmk__graph_has_loop(init_action, input->action,
821  (pcmk__related_action_t *) iter->data)) {
822  // Recursive call already logged a debug message
823  has_loop = true;
824  break;
825  }
826  }
827 
829 
830  if (!has_loop) {
831  crm_trace("No input loop found in %s@%s -> %s@%s (%#.6x)",
832  input->action->uuid,
833  input->action->node? input->action->node->details->uname : "",
834  action->uuid,
835  action->node? action->node->details->uname : "",
836  input->type);
837  }
838  return has_loop;
839 }
840 
850 static xmlNode *
851 create_graph_synapse(const pcmk_action_t *action, pcmk_scheduler_t *scheduler)
852 {
853  int synapse_priority = 0;
854  xmlNode *syn = create_xml_node(scheduler->graph, "synapse");
855 
858 
859  if (action->rsc != NULL) {
860  synapse_priority = action->rsc->priority;
861  }
862  if (action->priority > synapse_priority) {
863  synapse_priority = action->priority;
864  }
865  if (synapse_priority > 0) {
866  crm_xml_add_int(syn, XML_CIB_ATTR_PRIORITY, synapse_priority);
867  }
868  return syn;
869 }
870 
887 static void
888 add_action_to_graph(gpointer data, gpointer user_data)
889 {
892 
893  xmlNode *syn = NULL;
894  xmlNode *set = NULL;
895  xmlNode *in = NULL;
896 
897  /* If we haven't already, de-duplicate inputs (even if we won't be adding
898  * the action to the graph, so that crm_simulate's dot graphs don't have
899  * duplicates).
900  */
904  }
905 
907  || !should_add_action_to_graph(action)) {
908  return; // Already added, or shouldn't be
909  }
911 
912  crm_trace("Adding action %d (%s%s%s) to graph",
913  action->id, action->uuid,
914  ((action->node == NULL)? "" : " on "),
915  ((action->node == NULL)? "" : action->node->details->uname));
916 
917  syn = create_graph_synapse(action, scheduler);
918  set = create_xml_node(syn, "action_set");
919  in = create_xml_node(syn, "inputs");
920 
921  create_graph_action(set, action, false, scheduler);
922 
923  for (GList *lpc = action->actions_before; lpc != NULL; lpc = lpc->next) {
924  pcmk__related_action_t *input = lpc->data;
925 
926  if (should_add_input_to_graph(action, input)) {
927  xmlNode *input_xml = create_xml_node(in, "trigger");
928 
929  input->state = pe_link_dumped;
930  create_graph_action(input_xml, input->action, true, scheduler);
931  }
932  }
933 }
934 
935 static int transition_id = -1;
936 
943 void
944 pcmk__log_transition_summary(const char *filename)
945 {
946  if (was_processing_error) {
947  crm_err("Calculated transition %d (with errors)%s%s",
948  transition_id,
949  (filename == NULL)? "" : ", saving inputs in ",
950  (filename == NULL)? "" : filename);
951 
952  } else if (was_processing_warning) {
953  crm_warn("Calculated transition %d (with warnings)%s%s",
954  transition_id,
955  (filename == NULL)? "" : ", saving inputs in ",
956  (filename == NULL)? "" : filename);
957 
958  } else {
959  crm_notice("Calculated transition %d%s%s",
960  transition_id,
961  (filename == NULL)? "" : ", saving inputs in ",
962  (filename == NULL)? "" : filename);
963  }
964  if (crm_config_error) {
965  crm_notice("Configuration errors found during scheduler processing,"
966  " please run \"crm_verify -L\" to identify issues");
967  }
968 }
969 
976 void
978 {
979  GList *iter = NULL;
980 
981  CRM_ASSERT(rsc != NULL);
982  pe_rsc_trace(rsc, "Adding actions for %s to graph", rsc->id);
983 
984  // First add the resource's own actions
985  g_list_foreach(rsc->actions, add_action_to_graph, rsc->cluster);
986 
987  // Then recursively add its children's actions (appropriate to variant)
988  for (iter = rsc->children; iter != NULL; iter = iter->next) {
989  pcmk_resource_t *child_rsc = (pcmk_resource_t *) iter->data;
990 
991  child_rsc->cmds->add_actions_to_graph(child_rsc);
992  }
993 }
994 
1001 void
1003 {
1004  GList *iter = NULL;
1005  const char *value = NULL;
1006  long long limit = 0LL;
1007 
1008  transition_id++;
1009  crm_trace("Creating transition graph %d", transition_id);
1010 
1012 
1013  value = pe_pref(scheduler->config_hash, "cluster-delay");
1014  crm_xml_add(scheduler->graph, "cluster-delay", value);
1015 
1016  value = pe_pref(scheduler->config_hash, "stonith-timeout");
1017  crm_xml_add(scheduler->graph, "stonith-timeout", value);
1018 
1019  crm_xml_add(scheduler->graph, "failed-stop-offset", "INFINITY");
1020 
1022  crm_xml_add(scheduler->graph, "failed-start-offset", "INFINITY");
1023  } else {
1024  crm_xml_add(scheduler->graph, "failed-start-offset", "1");
1025  }
1026 
1027  value = pe_pref(scheduler->config_hash, "batch-limit");
1028  crm_xml_add(scheduler->graph, "batch-limit", value);
1029 
1030  crm_xml_add_int(scheduler->graph, "transition_id", transition_id);
1031 
1032  value = pe_pref(scheduler->config_hash, "migration-limit");
1033  if ((pcmk__scan_ll(value, &limit, 0LL) == pcmk_rc_ok) && (limit > 0)) {
1034  crm_xml_add(scheduler->graph, "migration-limit", value);
1035  }
1036 
1037  if (scheduler->recheck_by > 0) {
1038  char *recheck_epoch = NULL;
1039 
1040  recheck_epoch = crm_strdup_printf("%llu",
1041  (long long) scheduler->recheck_by);
1042  crm_xml_add(scheduler->graph, "recheck-by", recheck_epoch);
1043  free(recheck_epoch);
1044  }
1045 
1046  /* The following code will de-duplicate action inputs, so nothing past this
1047  * should rely on the action input type flags retaining their original
1048  * values.
1049  */
1050 
1051  // Add resource actions to graph
1052  for (iter = scheduler->resources; iter != NULL; iter = iter->next) {
1053  pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
1054 
1055  pe_rsc_trace(rsc, "Processing actions for %s", rsc->id);
1056  rsc->cmds->add_actions_to_graph(rsc);
1057  }
1058 
1059  // Add pseudo-action for list of nodes with maintenance state update
1060  add_maintenance_update(scheduler);
1061 
1062  // Add non-resource (node) actions
1063  for (iter = scheduler->actions; iter != NULL; iter = iter->next) {
1064  pcmk_action_t *action = (pcmk_action_t *) iter->data;
1065 
1066  if ((action->rsc != NULL)
1067  && (action->node != NULL)
1068  && action->node->details->shutdown
1069  && !pcmk_is_set(action->rsc->flags, pcmk_rsc_maintenance)
1070  && !pcmk_any_flags_set(action->flags,
1072  && pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_none)) {
1073  /* Eventually we should just ignore the 'fence' case, but for now
1074  * it's the best way to detect (in CTS) when CIB resource updates
1075  * are being lost.
1076  */
1079  const bool managed = pcmk_is_set(action->rsc->flags,
1081  const bool failed = pcmk_is_set(action->rsc->flags,
1082  pcmk_rsc_failed);
1083 
1084  crm_crit("Cannot %s %s because of %s:%s%s (%s)",
1085  action->node->details->unclean? "fence" : "shut down",
1086  pe__node_name(action->node), action->rsc->id,
1087  (managed? " blocked" : " unmanaged"),
1088  (failed? " failed" : ""), action->uuid);
1089  }
1090  }
1091 
1092  add_action_to_graph((gpointer) action, (gpointer) scheduler);
1093  }
1094 
1095  crm_log_xml_trace(scheduler->graph, "graph");
1096 }
pcmk_assignment_methods_t * cmds
Resource assignment methods.
Definition: resources.h:417
#define XML_ATTR_ID_LONG
Definition: msg_xml.h:159
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:238
enum pe_quorum_policy no_quorum_policy
Response to loss of quorum.
Definition: scheduler.h:186
A dumping ground.
#define crm_notice(fmt, args...)
Definition: logging.h:383
bool pe__is_guest_or_remote_node(const pcmk_node_t *node)
Definition: remote.c:41
pcmk_scheduler_t * cluster
Cluster that resource is part of.
Definition: resources.h:412
Actions are ordered if on same node (or migration target for migrate_to)
#define XML_CONFIG_ATTR_SHUTDOWN_LOCK
Definition: msg_xml.h:403
#define crm_crit(fmt, args...)
Definition: logging.h:380
char data[0]
Definition: cpg.c:55
pcmk_action_t * get_pseudo_op(const char *name, pcmk_scheduler_t *scheduler)
Definition: pe_actions.c:1160
Whether action should not be executed.
Definition: actions.h:244
#define pe__set_action_flags(action, flags_to_set)
Definition: internal.h:76
void hash2field(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry.
Definition: nvpair.c:700
#define XML_TAG_GRAPH
Definition: msg_xml.h:333
#define XML_GRAPH_TAG_MAINTENANCE
Definition: msg_xml.h:338
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:933
#define action_type_str(flags)
#define XML_ATTR_TYPE
Definition: msg_xml.h:160
Whether action has already been processed by a recursive procedure.
Definition: actions.h:272
Whether partition has quorum (via have-quorum property)
Definition: scheduler.h:71
GList * children
Resource&#39;s child resources, if any.
Definition: resources.h:475
pe_ordering
Definition: actions.h:346
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
Definition: nvpair.c:349
#define CRM_FEATURE_SET
Definition: crm.h:70
Implementation of pcmk_action_t.
Definition: actions.h:390
#define PCMK_ACTION_MONITOR
Definition: actions.h:59
#define XML_TAG_ATTRS
Definition: msg_xml.h:229
Whether resource, its node, or entire cluster is in maintenance mode.
Definition: resources.h:181
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: nvpair.c:302
void pcmk__log_transition_summary(const char *filename)
#define XML_GRAPH_TAG_RSC_OP
Definition: msg_xml.h:334
#define PCMK_ACTION_MIGRATE_TO
Definition: actions.h:58
#define PCMK_ACTION_DO_SHUTDOWN
Definition: actions.h:51
#define XML_NODE_IS_MAINTENANCE
Definition: msg_xml.h:290
GList * actions
Scheduled actions.
Definition: scheduler.h:204
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:222
const char * pe_pref(GHashTable *options, const char *name)
Definition: common.c:314
#define XML_GRAPH_TAG_CRM_EVENT
Definition: msg_xml.h:336
#define PCMK_ACTION_CLEAR_FAILCOUNT
Definition: actions.h:46
G_GNUC_INTERNAL pcmk_node_t * pcmk__connection_host_for_action(const pcmk_action_t *action)
bool pcmk__ends_with(const char *s, const char *match)
Definition: strings.c:533
void hash2smartfield(gpointer key, gpointer value, gpointer user_data)
Add hash table entry to XML as (possibly legacy) name/value.
Definition: nvpair.c:666
#define XML_GRAPH_TAG_DOWNED
Definition: msg_xml.h:337
Implementation of pcmk_scheduler_t.
Definition: scheduler.h:172
const char * action
Definition: pcmk_fence.c:30
void(* add_actions_to_graph)(pcmk_resource_t *rsc)
GList * resources
Resources in cluster.
Definition: scheduler.h:196
#define XML_GRAPH_TAG_PSEUDO_EVENT
Definition: msg_xml.h:335
GList * nodes
Nodes in cluster.
Definition: scheduler.h:195
#define XML_CIB_ATTR_PRIORITY
Definition: msg_xml.h:286
gboolean remote_maintenance
Definition: nodes.h:100
Whether action&#39;s inputs have been de-duplicated yet.
Definition: actions.h:275
#define PCMK_ACTION_MAINTENANCE_NODES
Definition: actions.h:55
#define XML_LRM_ATTR_TASK_KEY
Definition: msg_xml.h:307
#define XML_LRM_ATTR_TASK
Definition: msg_xml.h:306
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:382
pcmk_node_t * node
Node to execute action on, if any.
Definition: actions.h:401
int pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val, guint *result)
Definition: strings.c:311
Implementation of pcmk_resource_t.
Definition: resources.h:399
#define crm_debug(fmt, args...)
Definition: logging.h:386
Actions are ordered (optionally, if no other flags are set)
G_GNUC_INTERNAL void pcmk__log_action(const char *pre_text, const pcmk_action_t *action, bool details)
Primitive resource.
Definition: resources.h:34
#define XML_ATTR_ID
Definition: msg_xml.h:156
Whether resource is considered failed.
Definition: resources.h:151
#define pe__clear_action_flags(action, flags_to_clear)
Definition: internal.h:85
#define crm_trace(fmt, args...)
Definition: logging.h:387
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:99
struct pe_node_shared_s * details
Basic node information.
Definition: nodes.h:134
#define XML_AGENT_ATTR_PROVIDER
Definition: msg_xml.h:281
const char * uname
Node name in cluster.
Definition: nodes.h:68
Wrappers for and extensions to libxml2.
GHashTable * config_hash
Cluster properties.
Definition: scheduler.h:187
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:638
time_t recheck_by
Hint to controller when to reschedule.
Definition: scheduler.h:223
#define PCMK__NELEM(a)
Definition: internal.h:46
Ordering applies only if &#39;first&#39; is required and on same node as &#39;then&#39;.
#define PCMK_ACTION_STOP
Definition: actions.h:74
GList * actions
Definition: resources.h:447
#define PCMK_ACTION_STONITH
Definition: actions.h:73
char * uuid
Action key.
Definition: actions.h:404
#define XML_LRM_ATTR_ROUTER_NODE
Definition: msg_xml.h:314
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition: actions.c:42
void free_xml(xmlNode *child)
Definition: xml.c:783
Implementation of pcmk_node_t.
Definition: nodes.h:130
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:957
#define XML_CIB_TAG_NODE
Definition: msg_xml.h:223
const char * id
Node ID at the cluster layer.
Definition: nodes.h:67
xmlNode * sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
Definition: xml.c:2456
#define XML_LRM_ATTR_TARGET_UUID
Definition: msg_xml.h:309
const char * crm_xml_add_ll(xmlNode *node, const char *name, long long value)
Create an XML attribute with specified name and long long int value.
Definition: nvpair.c:399
void pcmk__add_rsc_actions_to_graph(pcmk_resource_t *rsc)
#define XML_TAG_CIB
Definition: msg_xml.h:137
#define PCMK_ACTION_LRM_DELETE
Definition: actions.h:53
bool pe__is_guest_node(const pcmk_node_t *node)
Definition: remote.c:33
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:183
void pcmk__create_graph(pcmk_scheduler_t *scheduler)
G_GNUC_INTERNAL bool pcmk__action_locks_rsc_to_node(const pcmk_action_t *action)
If &#39;then&#39; is required, &#39;first&#39; must be added to the transition graph.
bool pcmk__is_fencing_action(const char *action)
Definition: actions.c:489
Whether action is runnable.
Definition: actions.h:241
void pe_foreach_guest_node(const pcmk_scheduler_t *scheduler, const pcmk_node_t *host, void(*helper)(const pcmk_node_t *, void *), void *user_data)
Definition: remote.c:120
#define crm_err(fmt, args...)
Definition: logging.h:381
pcmk_scheduler_t * scheduler
Whether action does not require invoking an agent.
Definition: actions.h:238
#define CRM_ASSERT(expr)
Definition: results.h:42
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
Whether action should be added to transition graph even if optional.
Definition: actions.h:247
#define PCMK_ACTION_MIGRATE_FROM
Definition: actions.h:57
#define PCMK__XA_MODE
Definition: crm_internal.h:90
G_GNUC_INTERNAL void pcmk__deduplicate_action_inputs(pcmk_action_t *action)
#define XML_LRM_ATTR_INTERVAL_MS
Definition: msg_xml.h:304
void hash2metafield(gpointer key, gpointer value, gpointer user_data)
Set XML attribute based on hash table entry, as meta-attribute name.
Definition: nvpair.c:728
#define XML_ATTR_CRM_VERSION
Definition: msg_xml.h:140
G_GNUC_INTERNAL void pcmk__add_bundle_meta_to_xml(xmlNode *args_xml, const pcmk_action_t *action)
Add special bundle meta-attributes to XML.
gboolean maintenance
Whether in maintenance mode.
Definition: nodes.h:81
Whether action is allowed to be part of a live migration.
Definition: actions.h:253
#define crm_log_xml_trace(xml, text)
Definition: logging.h:395
Whether action has been added to transition graph.
Definition: actions.h:256
#define XML_LRM_ATTR_TARGET
Definition: msg_xml.h:308
#define action_optional_str(flags)
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:37
#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:446
unsigned long long flags
Group of enum pcmk_scheduler_flags.
Definition: scheduler.h:183
#define ID(x)
Definition: msg_xml.h:474
#define pe_err(fmt...)
Definition: internal.h:39
gboolean was_processing_error
Definition: common.c:21
Whether action can be executed on DC rather than own node.
Definition: actions.h:278
const char * parent
Definition: cib.c:27
#define action_runnable_str(flags)
Whether resource is managed.
Definition: resources.h:106
gboolean was_processing_warning
Definition: common.c:22
enum pe_ordering type
Definition: actions.h:380
&#39;then&#39; action is runnable if certain number of &#39;first&#39; instances are
Whether resource has been removed from the configuration.
Definition: resources.h:103
Whether resource is not an anonymous clone instance.
Definition: resources.h:118
#define PCMK_ACTION_NOTIFY
Definition: actions.h:61
No relation (compare with equality rather than bit set)
int num_synapse
Number of transition graph synapses.
Definition: scheduler.h:208
#define XML_AGENT_ATTR_CLASS
Definition: msg_xml.h:280
xmlNode * graph
Transition graph.
Definition: scheduler.h:212
char * id
Resource ID in configuration.
Definition: resources.h:400