root/lib/pacemaker/pcmk_graph_producer.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. add_node_to_xml_by_id
  2. add_node_to_xml
  3. add_maintenance_nodes
  4. add_maintenance_update
  5. add_downed_nodes
  6. clone_op_key
  7. add_node_details
  8. add_resource_details
  9. add_action_attributes
  10. create_graph_action
  11. should_add_action_to_graph
  12. ordering_can_change_actions
  13. should_add_input_to_graph
  14. pcmk__graph_has_loop
  15. create_graph_synapse
  16. pcmk__add_action_to_graph
  17. pcmk__log_transition_summary
  18. pcmk__create_graph

   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 
  38 /*!
  39  * \internal
  40  * \brief Add an XML node tag for a specified ID
  41  *
  42  * \param[in]     id      Node UUID to add
  43  * \param[in,out] xml     Parent XML tag to add to
  44  */
  45 static xmlNode*
  46 add_node_to_xml_by_id(const char *id, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
  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 
  56 /*!
  57  * \internal
  58  * \brief Add an XML node tag for a specified node
  59  *
  60  * \param[in]     node  Node to add
  61  * \param[in,out] xml   XML to add node to
  62  */
  63 static void
  64 add_node_to_xml(const pe_node_t *node, void *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
  65 {
  66     add_node_to_xml_by_id(node->details->id, (xmlNode *) xml);
  67 }
  68 
  69 /*!
  70  * \internal
  71  * \brief Add XML with nodes that need an update of their maintenance state
  72  *
  73  * \param[in,out] xml       Parent XML tag to add to
  74  * \param[in]     data_set  Working set for cluster
  75  */
  76 static int
  77 add_maintenance_nodes(xmlNode *xml, const pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
  78 {
  79     GList *gIter = NULL;
  80     xmlNode *maintenance =
  81         xml?create_xml_node(xml, XML_GRAPH_TAG_MAINTENANCE):NULL;
  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) {
  95                 crm_xml_add(
  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 
 107 /*!
 108  * \internal
 109  * \brief Add pseudo action with nodes needing maintenance state update
 110  *
 111  * \param[in,out] data_set  Working set for cluster
 112  */
 113 void
 114 add_maintenance_update(pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 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");
 120         action = get_pseudo_op(CRM_OP_MAINTENANCE_NODES, data_set);
 121         pe__set_action_flags(action, pe_action_print_always);
 122     }
 123 }
 124 
 125 /*!
 126  * \internal
 127  * \brief Add XML with nodes that an action is expected to bring down
 128  *
 129  * If a specified action is expected to bring any nodes down, add an XML block
 130  * with their UUIDs. When a node is lost, this allows the controller to
 131  * determine whether it was expected.
 132  *
 133  * \param[in,out] xml       Parent XML tag to add to
 134  * \param[in]     action    Action to check for downed nodes
 135  * \param[in]     data_set  Working set for cluster
 136  */
 137 static void
 138 add_downed_nodes(xmlNode *xml, const pe_action_t *action,
     /* [previous][next][first][last][top][bottom][index][help] */
 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;
 167         pe_action_t *input;
 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 
 185 /*!
 186  * \internal
 187  * \brief Create a transition graph operation key for a clone action
 188  *
 189  * \param[in] action       Clone action
 190  * \param[in] interval_ms  Action interval in milliseconds
 191  *
 192  * \return Newly allocated string with transition graph operation key
 193  */
 194 static char *
 195 clone_op_key(pe_action_t *action, guint interval_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 213 /*!
 214  * \internal
 215  * \brief Add node details to transition graph action XML
 216  *
 217  * \param[in] action  Scheduled action
 218  * \param[in] xml     Transition graph action XML for \p action
 219  */
 220 static void
 221 add_node_details(pe_action_t *action, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 222 {
 223     pe_node_t *router_node = pcmk__connection_host_for_action(action);
 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 
 232 /*!
 233  * \internal
 234  * \brief Add resource details to transition graph action XML
 235  *
 236  * \param[in] action      Scheduled action
 237  * \param[in] action_xml  Transition graph action XML for \p action
 238  */
 239 static void
 240 add_resource_details(pe_action_t *action, xmlNode *action_xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 241 {
 242     xmlNode *rsc_xml = NULL;
 243     const char *attr_list[] = {
 244         XML_AGENT_ATTR_CLASS,
 245         XML_AGENT_ATTR_PROVIDER,
 246         XML_ATTR_TYPE
 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      */
 252     if (pcmk__action_locks_rsc_to_node(action)) {
 253         crm_xml_add_ll(action_xml, XML_CONFIG_ATTR_SHUTDOWN_LOCK,
 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 
 314 /*!
 315  * \internal
 316  * \brief Add action attributes to transition graph action XML
 317  *
 318  * \param[in] action  Scheduled action
 319  * \param[in] action_xml  Transition graph action XML for \p action
 320  */
 321 static void
 322 add_action_attributes(pe_action_t *action, xmlNode *action_xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 332     crm_xml_add(args_xml, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
 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 
 397         pcmk__add_bundle_meta_to_xml(args_xml, action);
 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 
 414 /*!
 415  * \internal
 416  * \brief Create the transition graph XML for a scheduled action
 417  *
 418  * \param[in] parent        Parent XML element to add action to
 419  * \param[in] action        Scheduled action
 420  * \param[in] skip_details  If false, add action details as sub-elements
 421  * \param[in] data_set      Cluster working set
 422  */
 423 static void
 424 create_graph_action(xmlNode *parent, pe_action_t *action, bool skip_details,
     /* [previous][next][first][last][top][bottom][index][help] */
 425                     pe_working_set_t *data_set)
 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,
 443                                      pcmk_is_set(action->flags, pe_action_pseudo)?
 444                                      XML_GRAPH_TAG_PSEUDO_EVENT :
 445                                      XML_GRAPH_TAG_CRM_EVENT);
 446 
 447     } else if (pcmk__str_any_of(action->task,
 448                                 CRM_OP_SHUTDOWN,
 449                                 CRM_OP_CLEAR_FAILCOUNT,
 450                                 CRM_OP_LRM_REFRESH, NULL)) {
 451         action_xml = create_xml_node(parent, XML_GRAPH_TAG_CRM_EVENT);
 452 
 453     } else if (pcmk__str_eq(action->task, CRM_OP_LRM_DELETE, pcmk__str_none)) {
 454         // CIB-only clean-up for shutdown locks
 455         action_xml = create_xml_node(parent, XML_GRAPH_TAG_CRM_EVENT);
 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         }
 463         action_xml = create_xml_node(parent, XML_GRAPH_TAG_PSEUDO_EVENT);
 464         needs_node_info = false;
 465 
 466     } else {
 467         action_xml = create_xml_node(parent, XML_GRAPH_TAG_RSC_OP);
 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 
 480         if (pcmk__guint_from_hash(action->meta, XML_LRM_ATTR_INTERVAL_MS, 0,
 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 
 524 /*!
 525  * \internal
 526  * \brief Check whether an action should be added to the transition graph
 527  *
 528  * \param[in] action  Action to check
 529  *
 530  * \return true if action should be added to graph, otherwise false
 531  */
 532 static bool
 533 should_add_action_to_graph(pe_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 541     if (pcmk_is_set(action->flags, pe_action_optional)
 542         && !pcmk_is_set(action->flags, pe_action_print_always)) {
 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,
 562                                             XML_LRM_ATTR_INTERVAL_MS);
 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)
 574         || pcmk__strcase_any_of(action->task, CRM_OP_FENCE, CRM_OP_SHUTDOWN,
 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 
 615 /*!
 616  * \internal
 617  * \brief Check whether an ordering's flags can change an action
 618  *
 619  * \param[in] ordering  Ordering to check
 620  *
 621  * \return true if ordering has flags that can change an action, false otherwise
 622  */
 623 static bool
 624 ordering_can_change_actions(pe_action_wrapper_t *ordering)
     /* [previous][next][first][last][top][bottom][index][help] */
 625 {
 626     return pcmk_any_flags_set(ordering->type, ~(pe_order_implies_first_printed
 627                                                 |pe_order_implies_then_printed
 628                                                 |pe_order_optional));
 629 }
 630 
 631 /*!
 632  * \internal
 633  * \brief Check whether an action input should be in the transition graph
 634  *
 635  * \param[in]     action  Action to check
 636  * \param[in,out] input   Action input to check
 637  *
 638  * \return true if input should be in graph, false otherwise
 639  * \note This function may not only check an input, but disable it under certian
 640  *       circumstances (load or anti-colocation orderings that are not needed).
 641  */
 642 static bool
 643 should_add_input_to_graph(pe_action_t *action, pe_action_wrapper_t *input)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
 665                && pcmk_is_set(input->type, pe_order_one_or_more)) {
 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 
 672     } else if (pcmk_is_set(input->type, pe_order_implies_first_migratable)
 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 
 680     } else if (pcmk_is_set(input->type, pe_order_apply_first_non_migratable)
 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,
 774                                       pe_action_print_always|pe_action_dumped)
 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 
 792 /*!
 793  * \internal
 794  * \brief Check whether an ordering creates an ordering loop
 795  *
 796  * \param[in] init_action  "First" action in ordering
 797  * \param[in] action       Callers should always set this the same as
 798  *                         \p init_action (this function may use a different
 799  *                         value for recursive calls)
 800  * \param[in] input        Action wrapper for "then" action in ordering
 801  *
 802  * \return true if the ordering creates a loop, otherwise false
 803  */
 804 bool
 805 pcmk__graph_has_loop(pe_action_t *init_action, pe_action_t *action,
     /* [previous][next][first][last][top][bottom][index][help] */
 806                      pe_action_wrapper_t *input)
 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 
 834     pe__set_action_flags(input->action, pe_action_tracking);
 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 
 858     pe__clear_action_flags(input->action, pe_action_tracking);
 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 
 871 /*!
 872  * \internal
 873  * \brief Create a synapse XML element for a transition graph
 874  *
 875  * \param[in] action    Action that synapse is for
 876  * \param[in] data_set  Cluster working set containing graph
 877  *
 878  * \return Newly added XML element for new graph synapse
 879  */
 880 static xmlNode *
 881 create_graph_synapse(pe_action_t *action, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 882 {
 883     int synapse_priority = 0;
 884     xmlNode *syn = create_xml_node(data_set->graph, "synapse");
 885 
 886     crm_xml_add_int(syn, XML_ATTR_ID, data_set->num_synapse);
 887     data_set->num_synapse++;
 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 
 901 /*!
 902  * \internal
 903  * \brief Add an action to the transition graph XML if appropriate
 904  *
 905  * \param[in] action    Action to possibly add
 906  * \param[in] data_set  Cluster working set
 907  *
 908  * \note This will de-duplicate the action inputs, meaning that the
 909  *       pe_action_wrapper_t:type flags can no longer be relied on to retain
 910  *       their original settings. That means this MUST be called after
 911  *       pcmk__apply_orderings() is complete, and nothing after this should rely
 912  *       on those type flags. (For example, some code looks for type equal to
 913  *       some flag rather than whether the flag is set, and some code looks for
 914  *       particular combinations of flags -- such code must be done before
 915  *       pcmk__create_graph().)
 916  */
 917 void
 918 pcmk__add_action_to_graph(pe_action_t *action, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)) {
 929         pcmk__deduplicate_action_inputs(action);
 930         pe__set_action_flags(action, 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     }
 937     pe__set_action_flags(action, pe_action_dumped);
 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) {
 946         pe_action_wrapper_t *input = (pe_action_wrapper_t *) lpc->data;
 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 
 959 /*!
 960  * \internal
 961  * \brief Log a message after calculating a transition
 962  *
 963  * \param[in] filename  Where transition input is stored
 964  */
 965 void
 966 pcmk__log_transition_summary(const char *filename)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 992 /*!
 993  * \internal
 994  * \brief Create a transition graph with all cluster actions needed
 995  *
 996  * \param[in] data_set  Cluster working set
 997  */
 998 void
 999 pcmk__create_graph(pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
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 
1008     data_set->graph = create_xml_node(NULL, XML_TAG_GRAPH);
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 
1018     if (pcmk_is_set(data_set->flags, pe_flag_start_failure_fatal)) {
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
1057     add_maintenance_update(data_set);
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,
1068                                    pe_action_optional|pe_action_runnable)
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              */
1074             if (pcmk_is_set(data_set->flags, pe_flag_have_quorum)
1075                 || (data_set->no_quorum_policy == no_quorum_ignore)) {
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 
1085         pcmk__add_action_to_graph(action, data_set);
1086     }
1087 
1088     crm_log_xml_trace(data_set->graph, "graph");
1089 }

/* [previous][next][first][last][top][bottom][index][help] */