LCOV - code coverage report
Current view: top level - pacemaker - pcmk_graph_producer.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 407 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 19 0.0 %

          Line data    Source code
       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->details->uname)
      36             : 
      37             : /*!
      38             :  * \internal
      39             :  * \brief Add an XML node tag for a specified ID
      40             :  *
      41             :  * \param[in]     id      Node UUID to add
      42             :  * \param[in,out] xml     Parent XML tag to add to
      43             :  */
      44             : static xmlNode*
      45           0 : add_node_to_xml_by_id(const char *id, xmlNode *xml)
      46             : {
      47             :     xmlNode *node_xml;
      48             : 
      49           0 :     node_xml = pcmk__xe_create(xml, PCMK_XE_NODE);
      50           0 :     crm_xml_add(node_xml, PCMK_XA_ID, id);
      51             : 
      52           0 :     return node_xml;
      53             : }
      54             : 
      55             : /*!
      56             :  * \internal
      57             :  * \brief Add an XML node tag for a specified node
      58             :  *
      59             :  * \param[in]     node  Node to add
      60             :  * \param[in,out] xml   XML to add node to
      61             :  */
      62             : static void
      63           0 : add_node_to_xml(const pcmk_node_t *node, void *xml)
      64             : {
      65           0 :     add_node_to_xml_by_id(node->details->id, (xmlNode *) xml);
      66           0 : }
      67             : 
      68             : /*!
      69             :  * \internal
      70             :  * \brief Count (optionally add to XML) nodes needing maintenance state update
      71             :  *
      72             :  * \param[in,out] xml        Parent XML tag to add to, if any
      73             :  * \param[in]     scheduler  Scheduler data
      74             :  *
      75             :  * \return Count of nodes added
      76             :  * \note Only Pacemaker Remote nodes are considered currently
      77             :  */
      78             : static int
      79           0 : add_maintenance_nodes(xmlNode *xml, const pcmk_scheduler_t *scheduler)
      80             : {
      81           0 :     xmlNode *maintenance = NULL;
      82           0 :     int count = 0;
      83             : 
      84           0 :     if (xml != NULL) {
      85           0 :         maintenance = pcmk__xe_create(xml, PCMK__XE_MAINTENANCE);
      86             :     }
      87           0 :     for (const GList *iter = scheduler->nodes;
      88           0 :          iter != NULL; iter = iter->next) {
      89           0 :         const pcmk_node_t *node = iter->data;
      90             : 
      91           0 :         if (pcmk__is_pacemaker_remote_node(node) &&
      92           0 :             (node->details->maintenance != node->details->remote_maintenance)) {
      93             : 
      94           0 :             if (maintenance != NULL) {
      95           0 :                 crm_xml_add(add_node_to_xml_by_id(node->details->id,
      96             :                                                   maintenance),
      97             :                             PCMK__XA_NODE_IN_MAINTENANCE,
      98           0 :                             (node->details->maintenance? "1" : "0"));
      99             :             }
     100           0 :             count++;
     101             :         }
     102             :     }
     103           0 :     crm_trace("%s %d nodes in need of maintenance mode update in state",
     104             :               ((maintenance == NULL)? "Counted" : "Added"), count);
     105           0 :     return count;
     106             : }
     107             : 
     108             : /*!
     109             :  * \internal
     110             :  * \brief Add pseudo action with nodes needing maintenance state update
     111             :  *
     112             :  * \param[in,out] scheduler  Scheduler data
     113             :  */
     114             : static void
     115           0 : add_maintenance_update(pcmk_scheduler_t *scheduler)
     116             : {
     117           0 :     pcmk_action_t *action = NULL;
     118             : 
     119           0 :     if (add_maintenance_nodes(NULL, scheduler) != 0) {
     120           0 :         action = get_pseudo_op(PCMK_ACTION_MAINTENANCE_NODES, scheduler);
     121           0 :         pcmk__set_action_flags(action, pcmk_action_always_in_graph);
     122             :     }
     123           0 : }
     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             :  */
     136             : static void
     137           0 : add_downed_nodes(xmlNode *xml, const pcmk_action_t *action)
     138             : {
     139           0 :     CRM_CHECK((xml != NULL) && (action != NULL) && (action->node != NULL),
     140             :               return);
     141             : 
     142           0 :     if (pcmk__str_eq(action->task, PCMK_ACTION_DO_SHUTDOWN, pcmk__str_none)) {
     143             : 
     144             :         /* Shutdown makes the action's node down */
     145           0 :         xmlNode *downed = pcmk__xe_create(xml, PCMK__XE_DOWNED);
     146           0 :         add_node_to_xml_by_id(action->node->details->id, downed);
     147             : 
     148           0 :     } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH,
     149             :                             pcmk__str_none)) {
     150             : 
     151             :         /* Fencing makes the action's node and any hosted guest nodes down */
     152           0 :         const char *fence = g_hash_table_lookup(action->meta,
     153             :                                                 PCMK__META_STONITH_ACTION);
     154             : 
     155           0 :         if (pcmk__is_fencing_action(fence)) {
     156           0 :             xmlNode *downed = pcmk__xe_create(xml, PCMK__XE_DOWNED);
     157           0 :             add_node_to_xml_by_id(action->node->details->id, downed);
     158           0 :             pe_foreach_guest_node(action->node->details->data_set,
     159           0 :                                   action->node, add_node_to_xml, downed);
     160             :         }
     161             : 
     162           0 :     } else if (action->rsc && action->rsc->is_remote_node
     163           0 :                && 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;
     170             :         pcmk_action_t *input;
     171           0 :         bool migrating = false;
     172             : 
     173           0 :         for (iter = action->actions_before; iter != NULL; iter = iter->next) {
     174           0 :             input = ((pcmk__related_action_t *) iter->data)->action;
     175           0 :             if ((input->rsc != NULL)
     176           0 :                 && pcmk__str_eq(action->rsc->id, input->rsc->id, pcmk__str_none)
     177           0 :                 && pcmk__str_eq(input->task, PCMK_ACTION_MIGRATE_FROM,
     178             :                                 pcmk__str_none)) {
     179           0 :                 migrating = true;
     180           0 :                 break;
     181             :             }
     182             :         }
     183           0 :         if (!migrating) {
     184           0 :             xmlNode *downed = pcmk__xe_create(xml, PCMK__XE_DOWNED);
     185           0 :             add_node_to_xml_by_id(action->rsc->id, downed);
     186             :         }
     187             :     }
     188             : }
     189             : 
     190             : /*!
     191             :  * \internal
     192             :  * \brief Create a transition graph operation key for a clone action
     193             :  *
     194             :  * \param[in] action       Clone action
     195             :  * \param[in] interval_ms  Action interval in milliseconds
     196             :  *
     197             :  * \return Newly allocated string with transition graph operation key
     198             :  */
     199             : static char *
     200           0 : clone_op_key(const pcmk_action_t *action, guint interval_ms)
     201             : {
     202           0 :     if (pcmk__str_eq(action->task, PCMK_ACTION_NOTIFY, pcmk__str_none)) {
     203           0 :         const char *n_type = g_hash_table_lookup(action->meta, "notify_type");
     204           0 :         const char *n_task = g_hash_table_lookup(action->meta,
     205             :                                                  "notify_operation");
     206             : 
     207           0 :         CRM_LOG_ASSERT((n_type != NULL) && (n_task != NULL));
     208           0 :         return pcmk__notify_key(action->rsc->clone_name, n_type, n_task);
     209             : 
     210           0 :     } else if (action->cancel_task != NULL) {
     211           0 :         return pcmk__op_key(action->rsc->clone_name, action->cancel_task,
     212             :                             interval_ms);
     213             :     } else {
     214           0 :         return pcmk__op_key(action->rsc->clone_name, action->task, interval_ms);
     215             :     }
     216             : }
     217             : 
     218             : /*!
     219             :  * \internal
     220             :  * \brief Add node details to transition graph action XML
     221             :  *
     222             :  * \param[in]     action  Scheduled action
     223             :  * \param[in,out] xml     Transition graph action XML for \p action
     224             :  */
     225             : static void
     226           0 : add_node_details(const pcmk_action_t *action, xmlNode *xml)
     227             : {
     228           0 :     pcmk_node_t *router_node = pcmk__connection_host_for_action(action);
     229             : 
     230           0 :     crm_xml_add(xml, PCMK__META_ON_NODE, action->node->details->uname);
     231           0 :     crm_xml_add(xml, PCMK__META_ON_NODE_UUID, action->node->details->id);
     232           0 :     if (router_node != NULL) {
     233           0 :         crm_xml_add(xml, PCMK__XA_ROUTER_NODE, router_node->details->uname);
     234             :     }
     235           0 : }
     236             : 
     237             : /*!
     238             :  * \internal
     239             :  * \brief Add resource details to transition graph action XML
     240             :  *
     241             :  * \param[in]     action      Scheduled action
     242             :  * \param[in,out] action_xml  Transition graph action XML for \p action
     243             :  */
     244             : static void
     245           0 : add_resource_details(const pcmk_action_t *action, xmlNode *action_xml)
     246             : {
     247           0 :     xmlNode *rsc_xml = NULL;
     248           0 :     const char *attr_list[] = {
     249             :         PCMK_XA_CLASS,
     250             :         PCMK_XA_PROVIDER,
     251             :         PCMK_XA_TYPE,
     252             :     };
     253             : 
     254             :     /* If a resource is locked to a node via PCMK_OPT_SHUTDOWN_LOCK, mark its
     255             :      * actions so the controller can preserve the lock when the action
     256             :      * completes.
     257             :      */
     258           0 :     if (pcmk__action_locks_rsc_to_node(action)) {
     259           0 :         crm_xml_add_ll(action_xml, PCMK_OPT_SHUTDOWN_LOCK,
     260           0 :                        (long long) action->rsc->lock_time);
     261             :     }
     262             : 
     263             :     // List affected resource
     264             : 
     265           0 :     rsc_xml = pcmk__xe_create(action_xml,
     266           0 :                               (const char *) action->rsc->xml->name);
     267           0 :     if (pcmk_is_set(action->rsc->flags, pcmk_rsc_removed)
     268           0 :         && (action->rsc->clone_name != NULL)) {
     269             :         /* Use the numbered instance name here, because if there is more
     270             :          * than one instance on a node, we need to make sure the command
     271             :          * goes to the right one.
     272             :          *
     273             :          * This is important even for anonymous clones, because the clone's
     274             :          * unique meta-attribute might have just been toggled from on to
     275             :          * off.
     276             :          */
     277           0 :         crm_debug("Using orphan clone name %s instead of %s",
     278             :                   action->rsc->id, action->rsc->clone_name);
     279           0 :         crm_xml_add(rsc_xml, PCMK_XA_ID, action->rsc->clone_name);
     280           0 :         crm_xml_add(rsc_xml, PCMK__XA_LONG_ID, action->rsc->id);
     281             : 
     282           0 :     } else if (!pcmk_is_set(action->rsc->flags, pcmk_rsc_unique)) {
     283           0 :         const char *xml_id = pcmk__xe_id(action->rsc->xml);
     284             : 
     285           0 :         crm_debug("Using anonymous clone name %s for %s (aka %s)",
     286             :                   xml_id, action->rsc->id, action->rsc->clone_name);
     287             : 
     288             :         /* ID is what we'd like client to use
     289             :          * LONG_ID is what they might know it as instead
     290             :          *
     291             :          * LONG_ID is only strictly needed /here/ during the
     292             :          * transition period until all nodes in the cluster
     293             :          * are running the new software /and/ have rebooted
     294             :          * once (meaning that they've only ever spoken to a DC
     295             :          * supporting this feature).
     296             :          *
     297             :          * If anyone toggles the unique flag to 'on', the
     298             :          * 'instance free' name will correspond to an orphan
     299             :          * and fall into the clause above instead
     300             :          */
     301           0 :         crm_xml_add(rsc_xml, PCMK_XA_ID, xml_id);
     302           0 :         if ((action->rsc->clone_name != NULL)
     303           0 :             && !pcmk__str_eq(xml_id, action->rsc->clone_name,
     304             :                              pcmk__str_none)) {
     305           0 :             crm_xml_add(rsc_xml, PCMK__XA_LONG_ID, action->rsc->clone_name);
     306             :         } else {
     307           0 :             crm_xml_add(rsc_xml, PCMK__XA_LONG_ID, action->rsc->id);
     308             :         }
     309             : 
     310             :     } else {
     311           0 :         CRM_ASSERT(action->rsc->clone_name == NULL);
     312           0 :         crm_xml_add(rsc_xml, PCMK_XA_ID, action->rsc->id);
     313             :     }
     314             : 
     315           0 :     for (int lpc = 0; lpc < PCMK__NELEM(attr_list); lpc++) {
     316           0 :         crm_xml_add(rsc_xml, attr_list[lpc],
     317           0 :                     g_hash_table_lookup(action->rsc->meta, attr_list[lpc]));
     318             :     }
     319           0 : }
     320             : 
     321             : /*!
     322             :  * \internal
     323             :  * \brief Add action attributes to transition graph action XML
     324             :  *
     325             :  * \param[in,out] action      Scheduled action
     326             :  * \param[in,out] action_xml  Transition graph action XML for \p action
     327             :  */
     328             : static void
     329           0 : add_action_attributes(pcmk_action_t *action, xmlNode *action_xml)
     330             : {
     331           0 :     xmlNode *args_xml = NULL;
     332             : 
     333             :     /* We create free-standing XML to start, so we can sort the attributes
     334             :      * before adding it to action_xml, which keeps the scheduler regression
     335             :      * test graphs comparable.
     336             :      */
     337           0 :     args_xml = pcmk__xe_create(NULL, PCMK__XE_ATTRIBUTES);
     338             : 
     339           0 :     crm_xml_add(args_xml, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
     340           0 :     g_hash_table_foreach(action->extra, hash2field, args_xml);
     341             : 
     342           0 :     if ((action->rsc != NULL) && (action->node != NULL)) {
     343             :         // Get the resource instance attributes, evaluated properly for node
     344           0 :         GHashTable *params = pe_rsc_params(action->rsc, action->node,
     345           0 :                                            action->rsc->cluster);
     346             : 
     347           0 :         pcmk__substitute_remote_addr(action->rsc, params);
     348             : 
     349           0 :         g_hash_table_foreach(params, hash2smartfield, args_xml);
     350             : 
     351           0 :     } else if ((action->rsc != NULL)
     352           0 :                && (action->rsc->variant <= pcmk_rsc_variant_primitive)) {
     353           0 :         GHashTable *params = pe_rsc_params(action->rsc, NULL,
     354           0 :                                            action->rsc->cluster);
     355             : 
     356           0 :         g_hash_table_foreach(params, hash2smartfield, args_xml);
     357             :     }
     358             : 
     359           0 :     g_hash_table_foreach(action->meta, hash2metafield, args_xml);
     360           0 :     if (action->rsc != NULL) {
     361           0 :         pcmk_resource_t *parent = action->rsc;
     362             : 
     363           0 :         while (parent != NULL) {
     364           0 :             parent->cmds->add_graph_meta(parent, args_xml);
     365           0 :             parent = parent->parent;
     366             :         }
     367             : 
     368           0 :         pcmk__add_guest_meta_to_xml(args_xml, action);
     369             : 
     370           0 :     } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH, pcmk__str_none)
     371           0 :                && (action->node != NULL)) {
     372             :         /* Pass the node's attributes as meta-attributes.
     373             :          *
     374             :          * @TODO: Determine whether it is still necessary to do this. It was
     375             :          * added in 33d99707, probably for the libfence-based implementation in
     376             :          * c9a90bd, which is no longer used.
     377             :          */
     378           0 :         g_hash_table_foreach(action->node->details->attrs, hash2metafield,
     379             :                              args_xml);
     380             :     }
     381             : 
     382           0 :     sorted_xml(args_xml, action_xml, FALSE);
     383           0 :     free_xml(args_xml);
     384           0 : }
     385             : 
     386             : /*!
     387             :  * \internal
     388             :  * \brief Create the transition graph XML for a scheduled action
     389             :  *
     390             :  * \param[in,out] parent        Parent XML element to add action to
     391             :  * \param[in,out] action        Scheduled action
     392             :  * \param[in]     skip_details  If false, add action details as sub-elements
     393             :  * \param[in]     scheduler     Scheduler data
     394             :  */
     395             : static void
     396           0 : create_graph_action(xmlNode *parent, pcmk_action_t *action, bool skip_details,
     397             :                     const pcmk_scheduler_t *scheduler)
     398             : {
     399           0 :     bool needs_node_info = true;
     400           0 :     bool needs_maintenance_info = false;
     401           0 :     xmlNode *action_xml = NULL;
     402             : 
     403           0 :     if ((action == NULL) || (scheduler == NULL)) {
     404           0 :         return;
     405             :     }
     406             : 
     407             :     // Create the top-level element based on task
     408             : 
     409           0 :     if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH, pcmk__str_none)) {
     410             :         /* All fences need node info; guest node fences are pseudo-events */
     411           0 :         if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
     412           0 :             action_xml = pcmk__xe_create(parent, PCMK__XE_PSEUDO_EVENT);
     413             :         } else {
     414           0 :             action_xml = pcmk__xe_create(parent, PCMK__XE_CRM_EVENT);
     415             :         }
     416             : 
     417           0 :     } else if (pcmk__str_any_of(action->task,
     418             :                                 PCMK_ACTION_DO_SHUTDOWN,
     419             :                                 PCMK_ACTION_CLEAR_FAILCOUNT, NULL)) {
     420           0 :         action_xml = pcmk__xe_create(parent, PCMK__XE_CRM_EVENT);
     421             : 
     422           0 :     } else if (pcmk__str_eq(action->task, PCMK_ACTION_LRM_DELETE,
     423             :                             pcmk__str_none)) {
     424             :         // CIB-only clean-up for shutdown locks
     425           0 :         action_xml = pcmk__xe_create(parent, PCMK__XE_CRM_EVENT);
     426           0 :         crm_xml_add(action_xml, PCMK__XA_MODE, PCMK__VALUE_CIB);
     427             : 
     428           0 :     } else if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
     429           0 :         if (pcmk__str_eq(action->task, PCMK_ACTION_MAINTENANCE_NODES,
     430             :                          pcmk__str_none)) {
     431           0 :             needs_maintenance_info = true;
     432             :         }
     433           0 :         action_xml = pcmk__xe_create(parent, PCMK__XE_PSEUDO_EVENT);
     434           0 :         needs_node_info = false;
     435             : 
     436             :     } else {
     437           0 :         action_xml = pcmk__xe_create(parent, PCMK__XE_RSC_OP);
     438             :     }
     439             : 
     440           0 :     crm_xml_add_int(action_xml, PCMK_XA_ID, action->id);
     441           0 :     crm_xml_add(action_xml, PCMK_XA_OPERATION, action->task);
     442             : 
     443           0 :     if ((action->rsc != NULL) && (action->rsc->clone_name != NULL)) {
     444           0 :         char *clone_key = NULL;
     445             :         guint interval_ms;
     446             : 
     447           0 :         if (pcmk__guint_from_hash(action->meta, PCMK_META_INTERVAL, 0,
     448             :                                   &interval_ms) != pcmk_rc_ok) {
     449           0 :             interval_ms = 0;
     450             :         }
     451           0 :         clone_key = clone_op_key(action, interval_ms);
     452           0 :         crm_xml_add(action_xml, PCMK__XA_OPERATION_KEY, clone_key);
     453           0 :         crm_xml_add(action_xml, "internal_" PCMK__XA_OPERATION_KEY,
     454           0 :                     action->uuid);
     455           0 :         free(clone_key);
     456             :     } else {
     457           0 :         crm_xml_add(action_xml, PCMK__XA_OPERATION_KEY, action->uuid);
     458             :     }
     459             : 
     460           0 :     if (needs_node_info && (action->node != NULL)) {
     461           0 :         add_node_details(action, action_xml);
     462           0 :         pcmk__insert_dup(action->meta, PCMK__META_ON_NODE,
     463           0 :                          action->node->details->uname);
     464           0 :         pcmk__insert_dup(action->meta, PCMK__META_ON_NODE_UUID,
     465           0 :                          action->node->details->id);
     466             :     }
     467             : 
     468           0 :     if (skip_details) {
     469           0 :         return;
     470             :     }
     471             : 
     472           0 :     if ((action->rsc != NULL)
     473           0 :         && !pcmk_is_set(action->flags, pcmk_action_pseudo)) {
     474             : 
     475             :         // This is a real resource action, so add resource details
     476           0 :         add_resource_details(action, action_xml);
     477             :     }
     478             : 
     479             :     /* List any attributes in effect */
     480           0 :     add_action_attributes(action, action_xml);
     481             : 
     482             :     /* List any nodes this action is expected to make down */
     483           0 :     if (needs_node_info && (action->node != NULL)) {
     484           0 :         add_downed_nodes(action_xml, action);
     485             :     }
     486             : 
     487           0 :     if (needs_maintenance_info) {
     488           0 :         add_maintenance_nodes(action_xml, scheduler);
     489             :     }
     490             : }
     491             : 
     492             : /*!
     493             :  * \internal
     494             :  * \brief Check whether an action should be added to the transition graph
     495             :  *
     496             :  * \param[in] action  Action to check
     497             :  *
     498             :  * \return true if action should be added to graph, otherwise false
     499             :  */
     500             : static bool
     501           0 : should_add_action_to_graph(const pcmk_action_t *action)
     502             : {
     503           0 :     if (!pcmk_is_set(action->flags, pcmk_action_runnable)) {
     504           0 :         crm_trace("Ignoring action %s (%d): unrunnable",
     505             :                   action->uuid, action->id);
     506           0 :         return false;
     507             :     }
     508             : 
     509           0 :     if (pcmk_is_set(action->flags, pcmk_action_optional)
     510           0 :         && !pcmk_is_set(action->flags, pcmk_action_always_in_graph)) {
     511           0 :         crm_trace("Ignoring action %s (%d): optional",
     512             :                   action->uuid, action->id);
     513           0 :         return false;
     514             :     }
     515             : 
     516             :     /* Actions for unmanaged resources should be excluded from the graph,
     517             :      * with the exception of monitors and cancellation of recurring monitors.
     518             :      */
     519           0 :     if ((action->rsc != NULL)
     520           0 :         && !pcmk_is_set(action->rsc->flags, pcmk_rsc_managed)
     521           0 :         && !pcmk__str_eq(action->task, PCMK_ACTION_MONITOR, pcmk__str_none)) {
     522             : 
     523             :         const char *interval_ms_s;
     524             : 
     525             :         /* A cancellation of a recurring monitor will get here because the task
     526             :          * is cancel rather than monitor, but the interval can still be used to
     527             :          * recognize it. The interval has been normalized to milliseconds by
     528             :          * this point, so a string comparison is sufficient.
     529             :          */
     530           0 :         interval_ms_s = g_hash_table_lookup(action->meta, PCMK_META_INTERVAL);
     531           0 :         if (pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches)) {
     532           0 :             crm_trace("Ignoring action %s (%d): for unmanaged resource (%s)",
     533             :                       action->uuid, action->id, action->rsc->id);
     534           0 :             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             :      */
     541           0 :     if (pcmk_is_set(action->flags, pcmk_action_pseudo)
     542           0 :         || pcmk__strcase_any_of(action->task, PCMK_ACTION_STONITH,
     543             :                                 PCMK_ACTION_DO_SHUTDOWN, NULL)) {
     544           0 :         return true;
     545             :     }
     546             : 
     547           0 :     if (action->node == NULL) {
     548           0 :         pcmk__sched_err("Skipping action %s (%d) "
     549             :                         "because it was not assigned to a node (bug?)",
     550             :                         action->uuid, action->id);
     551           0 :         pcmk__log_action("Unassigned", action, false);
     552           0 :         return false;
     553             :     }
     554             : 
     555           0 :     if (pcmk_is_set(action->flags, pcmk_action_on_dc)) {
     556           0 :         crm_trace("Action %s (%d) should be dumped: "
     557             :                   "can run on DC instead of %s",
     558             :                   action->uuid, action->id, pcmk__node_name(action->node));
     559             : 
     560           0 :     } else if (pcmk__is_guest_or_bundle_node(action->node)
     561           0 :                && !action->node->details->remote_requires_reset) {
     562           0 :         crm_trace("Action %s (%d) should be dumped: "
     563             :                   "assuming will be runnable on guest %s",
     564             :                   action->uuid, action->id, pcmk__node_name(action->node));
     565             : 
     566           0 :     } else if (!action->node->details->online) {
     567           0 :         pcmk__sched_err("Skipping action %s (%d) "
     568             :                         "because it was scheduled for offline node (bug?)",
     569             :                         action->uuid, action->id);
     570           0 :         pcmk__log_action("Offline node", action, false);
     571           0 :         return false;
     572             : 
     573           0 :     } else if (action->node->details->unclean) {
     574           0 :         pcmk__sched_err("Skipping action %s (%d) "
     575             :                         "because it was scheduled for unclean node (bug?)",
     576             :                         action->uuid, action->id);
     577           0 :         pcmk__log_action("Unclean node", action, false);
     578           0 :         return false;
     579             :     }
     580           0 :     return true;
     581             : }
     582             : 
     583             : /*!
     584             :  * \internal
     585             :  * \brief Check whether an ordering's flags can change an action
     586             :  *
     587             :  * \param[in] ordering  Ordering to check
     588             :  *
     589             :  * \return true if ordering has flags that can change an action, false otherwise
     590             :  */
     591             : static bool
     592           0 : ordering_can_change_actions(const pcmk__related_action_t *ordering)
     593             : {
     594           0 :     return pcmk_any_flags_set(ordering->type,
     595             :                               ~(pcmk__ar_then_implies_first_graphed
     596             :                                 |pcmk__ar_first_implies_then_graphed
     597             :                                 |pcmk__ar_ordered));
     598             : }
     599             : 
     600             : /*!
     601             :  * \internal
     602             :  * \brief Check whether an action input should be in the transition graph
     603             :  *
     604             :  * \param[in]     action  Action to check
     605             :  * \param[in,out] input   Action input to check
     606             :  *
     607             :  * \return true if input should be in graph, false otherwise
     608             :  * \note This function may not only check an input, but disable it under certian
     609             :  *       circumstances (load or anti-colocation orderings that are not needed).
     610             :  */
     611             : static bool
     612           0 : should_add_input_to_graph(const pcmk_action_t *action,
     613             :                           pcmk__related_action_t *input)
     614             : {
     615           0 :     if (input->state == pe_link_dumped) {
     616           0 :         return true;
     617             :     }
     618             : 
     619           0 :     if ((uint32_t) input->type == pcmk__ar_none) {
     620           0 :         crm_trace("Ignoring %s (%d) input %s (%d): "
     621             :                   "ordering disabled",
     622             :                   action->uuid, action->id,
     623             :                   input->action->uuid, input->action->id);
     624           0 :         return false;
     625             : 
     626           0 :     } else if (!pcmk_is_set(input->action->flags, pcmk_action_runnable)
     627           0 :                && !ordering_can_change_actions(input)) {
     628           0 :         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           0 :         return false;
     633             : 
     634           0 :     } else if (!pcmk_is_set(input->action->flags, pcmk_action_runnable)
     635           0 :                && pcmk_is_set(input->type, pcmk__ar_min_runnable)) {
     636           0 :         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           0 :         return false;
     641             : 
     642           0 :     } else if (pcmk_is_set(input->type, pcmk__ar_unmigratable_then_blocks)
     643           0 :                && !pcmk_is_set(input->action->flags, pcmk_action_runnable)) {
     644           0 :         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           0 :         return false;
     649             : 
     650           0 :     } else if (pcmk_is_set(input->type, pcmk__ar_if_first_unmigratable)
     651           0 :                && pcmk_is_set(input->action->flags, pcmk_action_migratable)) {
     652           0 :         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           0 :         return false;
     657             : 
     658           0 :     } else if (((uint32_t) input->type == pcmk__ar_ordered)
     659           0 :                && pcmk_is_set(input->action->flags, pcmk_action_migratable)
     660           0 :                && pcmk__ends_with(input->action->uuid, "_stop_0")) {
     661           0 :         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           0 :         return false;
     666             : 
     667           0 :     } else if ((uint32_t) input->type == pcmk__ar_if_on_same_node_or_target) {
     668           0 :         pcmk_node_t *input_node = input->action->node;
     669             : 
     670           0 :         if ((action->rsc != NULL)
     671           0 :             && pcmk__str_eq(action->task, PCMK_ACTION_MIGRATE_TO,
     672           0 :                             pcmk__str_none)) {
     673             : 
     674           0 :             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           0 :             if (!pcmk__same_node(input_node, assigned)) {
     681           0 :                 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           0 :                 input->type = (enum pe_ordering) pcmk__ar_none;
     688           0 :                 return false;
     689             :             }
     690             : 
     691           0 :         } else if (!pcmk__same_node(input_node, action->node)) {
     692           0 :             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           0 :             input->type = (enum pe_ordering) pcmk__ar_none;
     699           0 :             return false;
     700             : 
     701           0 :         } else if (pcmk_is_set(input->action->flags, pcmk_action_optional)) {
     702           0 :             crm_trace("Ignoring %s (%d) input %s (%d): "
     703             :                       "ordering optional",
     704             :                       action->uuid, action->id,
     705             :                       input->action->uuid, input->action->id);
     706           0 :             input->type = (enum pe_ordering) pcmk__ar_none;
     707           0 :             return false;
     708             :         }
     709             : 
     710           0 :     } else if ((uint32_t) input->type == pcmk__ar_if_required_on_same_node) {
     711           0 :         if (input->action->node && action->node
     712           0 :             && !pcmk__same_node(input->action->node, action->node)) {
     713           0 :             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             :                       pcmk__node_name(action->node),
     718             :                       pcmk__node_name(input->action->node));
     719           0 :             input->type = (enum pe_ordering) pcmk__ar_none;
     720           0 :             return false;
     721             : 
     722           0 :         } else if (pcmk_is_set(input->action->flags, pcmk_action_optional)) {
     723           0 :             crm_trace("Ignoring %s (%d) input %s (%d): optional",
     724             :                       action->uuid, action->id,
     725             :                       input->action->uuid, input->action->id);
     726           0 :             input->type = (enum pe_ordering) pcmk__ar_none;
     727           0 :             return false;
     728             :         }
     729             : 
     730           0 :     } else if (input->action->rsc
     731           0 :                && input->action->rsc != action->rsc
     732           0 :                && pcmk_is_set(input->action->rsc->flags, pcmk_rsc_failed)
     733           0 :                && !pcmk_is_set(input->action->rsc->flags, pcmk_rsc_managed)
     734           0 :                && pcmk__ends_with(input->action->uuid, "_stop_0")
     735           0 :                && pcmk__is_clone(action->rsc)) {
     736           0 :         crm_warn("Ignoring requirement that %s complete before %s:"
     737             :                  " unmanaged failed resources cannot prevent clone shutdown",
     738             :                  input->action->uuid, action->uuid);
     739           0 :         return false;
     740             : 
     741           0 :     } else if (pcmk_is_set(input->action->flags, pcmk_action_optional)
     742           0 :                && !pcmk_any_flags_set(input->action->flags,
     743             :                                       pcmk_action_always_in_graph
     744             :                                       |pcmk_action_added_to_graph)
     745           0 :                && !should_add_action_to_graph(input->action)) {
     746           0 :         crm_trace("Ignoring %s (%d) input %s (%d): "
     747             :                   "input optional",
     748             :                   action->uuid, action->id,
     749             :                   input->action->uuid, input->action->id);
     750           0 :         return false;
     751             :     }
     752             : 
     753           0 :     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           0 :     return true;
     760             : }
     761             : 
     762             : /*!
     763             :  * \internal
     764             :  * \brief Check whether an ordering creates an ordering loop
     765             :  *
     766             :  * \param[in]     init_action  "First" action in ordering
     767             :  * \param[in]     action       Callers should always set this the same as
     768             :  *                             \p init_action (this function may use a different
     769             :  *                             value for recursive calls)
     770             :  * \param[in,out] input        Action wrapper for "then" action in ordering
     771             :  *
     772             :  * \return true if the ordering creates a loop, otherwise false
     773             :  */
     774             : bool
     775           0 : pcmk__graph_has_loop(const pcmk_action_t *init_action,
     776             :                      const pcmk_action_t *action, pcmk__related_action_t *input)
     777             : {
     778           0 :     bool has_loop = false;
     779             : 
     780           0 :     if (pcmk_is_set(input->action->flags, pcmk_action_detect_loop)) {
     781           0 :         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           0 :         return false;
     788             :     }
     789             : 
     790             :     // Don't need to check inputs that won't be used
     791           0 :     if (!should_add_input_to_graph(action, input)) {
     792           0 :         return false;
     793             :     }
     794             : 
     795           0 :     if (input->action == init_action) {
     796           0 :         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           0 :         return true;
     802             :     }
     803             : 
     804           0 :     pcmk__set_action_flags(input->action, pcmk_action_detect_loop);
     805             : 
     806           0 :     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           0 :     for (GList *iter = input->action->actions_before;
     818           0 :          iter != NULL; iter = iter->next) {
     819             : 
     820           0 :         if (pcmk__graph_has_loop(init_action, input->action,
     821           0 :                                  (pcmk__related_action_t *) iter->data)) {
     822             :             // Recursive call already logged a debug message
     823           0 :             has_loop = true;
     824           0 :             break;
     825             :         }
     826             :     }
     827             : 
     828           0 :     pcmk__clear_action_flags(input->action, pcmk_action_detect_loop);
     829             : 
     830           0 :     if (!has_loop) {
     831           0 :         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           0 :     return has_loop;
     839             : }
     840             : 
     841             : /*!
     842             :  * \internal
     843             :  * \brief Create a synapse XML element for a transition graph
     844             :  *
     845             :  * \param[in]     action     Action that synapse is for
     846             :  * \param[in,out] scheduler  Scheduler data containing graph
     847             :  *
     848             :  * \return Newly added XML element for new graph synapse
     849             :  */
     850             : static xmlNode *
     851           0 : create_graph_synapse(const pcmk_action_t *action, pcmk_scheduler_t *scheduler)
     852             : {
     853           0 :     int synapse_priority = 0;
     854           0 :     xmlNode *syn = pcmk__xe_create(scheduler->graph, "synapse");
     855             : 
     856           0 :     crm_xml_add_int(syn, PCMK_XA_ID, scheduler->num_synapse);
     857           0 :     scheduler->num_synapse++;
     858             : 
     859           0 :     if (action->rsc != NULL) {
     860           0 :         synapse_priority = action->rsc->priority;
     861             :     }
     862           0 :     if (action->priority > synapse_priority) {
     863           0 :         synapse_priority = action->priority;
     864             :     }
     865           0 :     if (synapse_priority > 0) {
     866           0 :         crm_xml_add_int(syn, PCMK__XA_PRIORITY, synapse_priority);
     867             :     }
     868           0 :     return syn;
     869             : }
     870             : 
     871             : /*!
     872             :  * \internal
     873             :  * \brief Add an action to the transition graph XML if appropriate
     874             :  *
     875             :  * \param[in,out] data       Action to possibly add
     876             :  * \param[in,out] user_data  Scheduler data
     877             :  *
     878             :  * \note This will de-duplicate the action inputs, meaning that the
     879             :  *       pcmk__related_action_t:type flags can no longer be relied on to retain
     880             :  *       their original settings. That means this MUST be called after
     881             :  *       pcmk__apply_orderings() is complete, and nothing after this should rely
     882             :  *       on those type flags. (For example, some code looks for type equal to
     883             :  *       some flag rather than whether the flag is set, and some code looks for
     884             :  *       particular combinations of flags -- such code must be done before
     885             :  *       pcmk__create_graph().)
     886             :  */
     887             : static void
     888           0 : add_action_to_graph(gpointer data, gpointer user_data)
     889             : {
     890           0 :     pcmk_action_t *action = (pcmk_action_t *) data;
     891           0 :     pcmk_scheduler_t *scheduler = (pcmk_scheduler_t *) user_data;
     892             : 
     893           0 :     xmlNode *syn = NULL;
     894           0 :     xmlNode *set = NULL;
     895           0 :     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             :      */
     901           0 :     if (!pcmk_is_set(action->flags, pcmk_action_inputs_deduplicated)) {
     902           0 :         pcmk__deduplicate_action_inputs(action);
     903           0 :         pcmk__set_action_flags(action, pcmk_action_inputs_deduplicated);
     904             :     }
     905             : 
     906           0 :     if (pcmk_is_set(action->flags, pcmk_action_added_to_graph)
     907           0 :         || !should_add_action_to_graph(action)) {
     908           0 :         return; // Already added, or shouldn't be
     909             :     }
     910           0 :     pcmk__set_action_flags(action, pcmk_action_added_to_graph);
     911             : 
     912           0 :     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           0 :     syn = create_graph_synapse(action, scheduler);
     918           0 :     set = pcmk__xe_create(syn, "action_set");
     919           0 :     in = pcmk__xe_create(syn, "inputs");
     920             : 
     921           0 :     create_graph_action(set, action, false, scheduler);
     922             : 
     923           0 :     for (GList *lpc = action->actions_before; lpc != NULL; lpc = lpc->next) {
     924           0 :         pcmk__related_action_t *input = lpc->data;
     925             : 
     926           0 :         if (should_add_input_to_graph(action, input)) {
     927           0 :             xmlNode *input_xml = pcmk__xe_create(in, "trigger");
     928             : 
     929           0 :             input->state = pe_link_dumped;
     930           0 :             create_graph_action(input_xml, input->action, true, scheduler);
     931             :         }
     932             :     }
     933             : }
     934             : 
     935             : static int transition_id = -1;
     936             : 
     937             : /*!
     938             :  * \internal
     939             :  * \brief Log a message after calculating a transition
     940             :  *
     941             :  * \param[in] filename  Where transition input is stored
     942             :  */
     943             : void
     944           0 : pcmk__log_transition_summary(const char *filename)
     945             : {
     946           0 :     if (was_processing_error || crm_config_error) {
     947           0 :         crm_err("Calculated transition %d (with errors)%s%s",
     948             :                 transition_id,
     949             :                 (filename == NULL)? "" : ", saving inputs in ",
     950             :                 (filename == NULL)? "" : filename);
     951             : 
     952           0 :     } else if (was_processing_warning || crm_config_warning) {
     953           0 :         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           0 :         crm_notice("Calculated transition %d%s%s",
     960             :                    transition_id,
     961             :                    (filename == NULL)? "" : ", saving inputs in ",
     962             :                    (filename == NULL)? "" : filename);
     963             :     }
     964           0 :     if (crm_config_error) {
     965           0 :         crm_notice("Configuration errors found during scheduler processing,"
     966             :                    "  please run \"crm_verify -L\" to identify issues");
     967             :     }
     968           0 : }
     969             : 
     970             : /*!
     971             :  * \internal
     972             :  * \brief Add a resource's actions to the transition graph
     973             :  *
     974             :  * \param[in,out] rsc  Resource whose actions should be added
     975             :  */
     976             : void
     977           0 : pcmk__add_rsc_actions_to_graph(pcmk_resource_t *rsc)
     978             : {
     979           0 :     GList *iter = NULL;
     980             : 
     981           0 :     CRM_ASSERT(rsc != NULL);
     982           0 :     pcmk__rsc_trace(rsc, "Adding actions for %s to graph", rsc->id);
     983             : 
     984             :     // First add the resource's own actions
     985           0 :     g_list_foreach(rsc->actions, add_action_to_graph, rsc->cluster);
     986             : 
     987             :     // Then recursively add its children's actions (appropriate to variant)
     988           0 :     for (iter = rsc->children; iter != NULL; iter = iter->next) {
     989           0 :         pcmk_resource_t *child_rsc = (pcmk_resource_t *) iter->data;
     990             : 
     991           0 :         child_rsc->cmds->add_actions_to_graph(child_rsc);
     992             :     }
     993           0 : }
     994             : 
     995             : /*!
     996             :  * \internal
     997             :  * \brief Create a transition graph with all cluster actions needed
     998             :  *
     999             :  * \param[in,out] scheduler  Scheduler data
    1000             :  */
    1001             : void
    1002           0 : pcmk__create_graph(pcmk_scheduler_t *scheduler)
    1003             : {
    1004           0 :     GList *iter = NULL;
    1005           0 :     const char *value = NULL;
    1006           0 :     long long limit = 0LL;
    1007           0 :     GHashTable *config_hash = scheduler->config_hash;
    1008             : 
    1009           0 :     transition_id++;
    1010           0 :     crm_trace("Creating transition graph %d", transition_id);
    1011             : 
    1012           0 :     scheduler->graph = pcmk__xe_create(NULL, PCMK__XE_TRANSITION_GRAPH);
    1013             : 
    1014           0 :     value = pcmk__cluster_option(config_hash, PCMK_OPT_CLUSTER_DELAY);
    1015           0 :     crm_xml_add(scheduler->graph, PCMK_OPT_CLUSTER_DELAY, value);
    1016             : 
    1017           0 :     value = pcmk__cluster_option(config_hash, PCMK_OPT_STONITH_TIMEOUT);
    1018           0 :     crm_xml_add(scheduler->graph, PCMK_OPT_STONITH_TIMEOUT, value);
    1019             : 
    1020           0 :     crm_xml_add(scheduler->graph, "failed-stop-offset", "INFINITY");
    1021             : 
    1022           0 :     if (pcmk_is_set(scheduler->flags, pcmk_sched_start_failure_fatal)) {
    1023           0 :         crm_xml_add(scheduler->graph, "failed-start-offset", "INFINITY");
    1024             :     } else {
    1025           0 :         crm_xml_add(scheduler->graph, "failed-start-offset", "1");
    1026             :     }
    1027             : 
    1028           0 :     value = pcmk__cluster_option(config_hash, PCMK_OPT_BATCH_LIMIT);
    1029           0 :     crm_xml_add(scheduler->graph, PCMK_OPT_BATCH_LIMIT, value);
    1030             : 
    1031           0 :     crm_xml_add_int(scheduler->graph, "transition_id", transition_id);
    1032             : 
    1033           0 :     value = pcmk__cluster_option(config_hash, PCMK_OPT_MIGRATION_LIMIT);
    1034           0 :     if ((pcmk__scan_ll(value, &limit, 0LL) == pcmk_rc_ok) && (limit > 0)) {
    1035           0 :         crm_xml_add(scheduler->graph, PCMK_OPT_MIGRATION_LIMIT, value);
    1036             :     }
    1037             : 
    1038           0 :     if (scheduler->recheck_by > 0) {
    1039           0 :         char *recheck_epoch = NULL;
    1040             : 
    1041           0 :         recheck_epoch = crm_strdup_printf("%llu",
    1042           0 :                                           (long long) scheduler->recheck_by);
    1043           0 :         crm_xml_add(scheduler->graph, "recheck-by", recheck_epoch);
    1044           0 :         free(recheck_epoch);
    1045             :     }
    1046             : 
    1047             :     /* The following code will de-duplicate action inputs, so nothing past this
    1048             :      * should rely on the action input type flags retaining their original
    1049             :      * values.
    1050             :      */
    1051             : 
    1052             :     // Add resource actions to graph
    1053           0 :     for (iter = scheduler->resources; iter != NULL; iter = iter->next) {
    1054           0 :         pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
    1055             : 
    1056           0 :         pcmk__rsc_trace(rsc, "Processing actions for %s", rsc->id);
    1057           0 :         rsc->cmds->add_actions_to_graph(rsc);
    1058             :     }
    1059             : 
    1060             :     // Add pseudo-action for list of nodes with maintenance state update
    1061           0 :     add_maintenance_update(scheduler);
    1062             : 
    1063             :     // Add non-resource (node) actions
    1064           0 :     for (iter = scheduler->actions; iter != NULL; iter = iter->next) {
    1065           0 :         pcmk_action_t *action = (pcmk_action_t *) iter->data;
    1066             : 
    1067           0 :         if ((action->rsc != NULL)
    1068           0 :             && (action->node != NULL)
    1069           0 :             && action->node->details->shutdown
    1070           0 :             && !pcmk_is_set(action->rsc->flags, pcmk_rsc_maintenance)
    1071           0 :             && !pcmk_any_flags_set(action->flags,
    1072             :                                    pcmk_action_optional|pcmk_action_runnable)
    1073           0 :             && pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_none)) {
    1074             :             /* Eventually we should just ignore the 'fence' case, but for now
    1075             :              * it's the best way to detect (in CTS) when CIB resource updates
    1076             :              * are being lost.
    1077             :              */
    1078           0 :             if (pcmk_is_set(scheduler->flags, pcmk_sched_quorate)
    1079           0 :                 || (scheduler->no_quorum_policy == pcmk_no_quorum_ignore)) {
    1080           0 :                 const bool managed = pcmk_is_set(action->rsc->flags,
    1081             :                                                  pcmk_rsc_managed);
    1082           0 :                 const bool failed = pcmk_is_set(action->rsc->flags,
    1083             :                                                 pcmk_rsc_failed);
    1084             : 
    1085           0 :                 crm_crit("Cannot %s %s because of %s:%s%s (%s)",
    1086             :                          action->node->details->unclean? "fence" : "shut down",
    1087             :                          pcmk__node_name(action->node), action->rsc->id,
    1088             :                          (managed? " blocked" : " unmanaged"),
    1089             :                          (failed? " failed" : ""), action->uuid);
    1090             :             }
    1091             :         }
    1092             : 
    1093           0 :         add_action_to_graph((gpointer) action, (gpointer) scheduler);
    1094             :     }
    1095             : 
    1096           0 :     crm_log_xml_trace(scheduler->graph, "graph");
    1097           0 : }

Generated by: LCOV version 1.14