LCOV - code coverage report
Current view: top level - pacemaker - pcmk_graph_consumer.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 363 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 16 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 Lesser General Public License
       7             :  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
       8             :  */
       9             : 
      10             : #include <crm_internal.h>
      11             : 
      12             : #include <sys/param.h>
      13             : #include <sys/stat.h>
      14             : 
      15             : #include <crm/crm.h>
      16             : #include <crm/common/xml.h>
      17             : #include <crm/common/xml_internal.h>
      18             : #include <crm/lrmd_internal.h>
      19             : #include <pacemaker-internal.h>
      20             : 
      21             : 
      22             : /*
      23             :  * Functions for freeing transition graph objects
      24             :  */
      25             : 
      26             : /*!
      27             :  * \internal
      28             :  * \brief Free a transition graph action object
      29             :  *
      30             :  * \param[in,out] user_data  Action to free
      31             :  */
      32             : static void
      33           0 : free_graph_action(gpointer user_data)
      34             : {
      35           0 :     pcmk__graph_action_t *action = user_data;
      36             : 
      37           0 :     if (action->timer != 0) {
      38           0 :         crm_warn("Cancelling timer for graph action %d", action->id);
      39           0 :         g_source_remove(action->timer);
      40             :     }
      41           0 :     if (action->params != NULL) {
      42           0 :         g_hash_table_destroy(action->params);
      43             :     }
      44           0 :     free_xml(action->xml);
      45           0 :     free(action);
      46           0 : }
      47             : 
      48             : /*!
      49             :  * \internal
      50             :  * \brief Free a transition graph synapse object
      51             :  *
      52             :  * \param[in,out] user_data  Synapse to free
      53             :  */
      54             : static void
      55           0 : free_graph_synapse(gpointer user_data)
      56             : {
      57           0 :     pcmk__graph_synapse_t *synapse = user_data;
      58             : 
      59           0 :     g_list_free_full(synapse->actions, free_graph_action);
      60           0 :     g_list_free_full(synapse->inputs, free_graph_action);
      61           0 :     free(synapse);
      62           0 : }
      63             : 
      64             : /*!
      65             :  * \internal
      66             :  * \brief Free a transition graph object
      67             :  *
      68             :  * \param[in,out] graph  Transition graph to free
      69             :  */
      70             : void
      71           0 : pcmk__free_graph(pcmk__graph_t *graph)
      72             : {
      73           0 :     if (graph != NULL) {
      74           0 :         g_list_free_full(graph->synapses, free_graph_synapse);
      75           0 :         free(graph->source);
      76           0 :         free(graph->failed_stop_offset);
      77           0 :         free(graph->failed_start_offset);
      78           0 :         free(graph);
      79             :     }
      80           0 : }
      81             : 
      82             : 
      83             : /*
      84             :  * Functions for updating graph
      85             :  */
      86             : 
      87             : /*!
      88             :  * \internal
      89             :  * \brief Update synapse after completed prerequisite
      90             :  *
      91             :  * A synapse is ready to be executed once all its prerequisite actions (inputs)
      92             :  * complete. Given a completed action, check whether it is an input for a given
      93             :  * synapse, and if so, mark the input as confirmed, and mark the synapse as
      94             :  * ready if appropriate.
      95             :  *
      96             :  * \param[in,out] synapse    Transition graph synapse to update
      97             :  * \param[in]     action_id  ID of an action that completed
      98             :  *
      99             :  * \note The only substantial effect here is confirming synapse inputs.
     100             :  *       should_fire_synapse() will recalculate pcmk__synapse_ready, so the only
     101             :  *       thing that uses the pcmk__synapse_ready from here is
     102             :  *       synapse_state_str().
     103             :  */
     104             : static void
     105           0 : update_synapse_ready(pcmk__graph_synapse_t *synapse, int action_id)
     106             : {
     107           0 :     if (pcmk_is_set(synapse->flags, pcmk__synapse_ready)) {
     108           0 :         return; // All inputs have already been confirmed
     109             :     }
     110             : 
     111             :     // Presume ready until proven otherwise
     112           0 :     pcmk__set_synapse_flags(synapse, pcmk__synapse_ready);
     113             : 
     114           0 :     for (GList *lpc = synapse->inputs; lpc != NULL; lpc = lpc->next) {
     115           0 :         pcmk__graph_action_t *prereq = (pcmk__graph_action_t *) lpc->data;
     116             : 
     117           0 :         if (prereq->id == action_id) {
     118           0 :             crm_trace("Confirming input %d of synapse %d",
     119             :                       action_id, synapse->id);
     120           0 :             pcmk__set_graph_action_flags(prereq, pcmk__graph_action_confirmed);
     121             : 
     122           0 :         } else if (!pcmk_is_set(prereq->flags, pcmk__graph_action_confirmed)) {
     123           0 :             pcmk__clear_synapse_flags(synapse, pcmk__synapse_ready);
     124           0 :             crm_trace("Synapse %d still not ready after action %d",
     125             :                       synapse->id, action_id);
     126             :         }
     127             :     }
     128           0 :     if (pcmk_is_set(synapse->flags, pcmk__synapse_ready)) {
     129           0 :         crm_trace("Synapse %d is now ready to execute", synapse->id);
     130             :     }
     131             : }
     132             : 
     133             : /*!
     134             :  * \internal
     135             :  * \brief Update action and synapse confirmation after action completion
     136             :  *
     137             :  * \param[in,out] synapse    Transition graph synapse that action belongs to
     138             :  * \param[in]     action_id  ID of action that completed
     139             :  */
     140             : static void
     141           0 : update_synapse_confirmed(pcmk__graph_synapse_t *synapse, int action_id)
     142             : {
     143           0 :     bool all_confirmed = true;
     144             : 
     145           0 :     for (GList *lpc = synapse->actions; lpc != NULL; lpc = lpc->next) {
     146           0 :         pcmk__graph_action_t *action = (pcmk__graph_action_t *) lpc->data;
     147             : 
     148           0 :         if (action->id == action_id) {
     149           0 :             crm_trace("Confirmed action %d of synapse %d",
     150             :                       action_id, synapse->id);
     151           0 :             pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
     152             : 
     153           0 :         } else if (all_confirmed &&
     154           0 :                    !pcmk_is_set(action->flags, pcmk__graph_action_confirmed)) {
     155           0 :             all_confirmed = false;
     156           0 :             crm_trace("Synapse %d still not confirmed after action %d",
     157             :                       synapse->id, action_id);
     158             :         }
     159             :     }
     160             : 
     161           0 :     if (all_confirmed
     162           0 :         && !pcmk_is_set(synapse->flags, pcmk__synapse_confirmed)) {
     163           0 :         crm_trace("Confirmed synapse %d", synapse->id);
     164           0 :         pcmk__set_synapse_flags(synapse, pcmk__synapse_confirmed);
     165             :     }
     166           0 : }
     167             : 
     168             : /*!
     169             :  * \internal
     170             :  * \brief Update the transition graph with a completed action result
     171             :  *
     172             :  * \param[in,out] graph   Transition graph to update
     173             :  * \param[in]     action  Action that completed
     174             :  */
     175             : void
     176           0 : pcmk__update_graph(pcmk__graph_t *graph, const pcmk__graph_action_t *action)
     177             : {
     178           0 :     for (GList *lpc = graph->synapses; lpc != NULL; lpc = lpc->next) {
     179           0 :         pcmk__graph_synapse_t *synapse = (pcmk__graph_synapse_t *) lpc->data;
     180             : 
     181           0 :         if (pcmk_any_flags_set(synapse->flags,
     182             :                                pcmk__synapse_confirmed|pcmk__synapse_failed)) {
     183           0 :             continue; // This synapse already completed
     184             : 
     185           0 :         } else if (pcmk_is_set(synapse->flags, pcmk__synapse_executed)) {
     186           0 :             update_synapse_confirmed(synapse, action->id);
     187             : 
     188           0 :         } else if (!pcmk_is_set(action->flags, pcmk__graph_action_failed)
     189           0 :                    || (synapse->priority == PCMK_SCORE_INFINITY)) {
     190           0 :             update_synapse_ready(synapse, action->id);
     191             :         }
     192             :     }
     193           0 : }
     194             : 
     195             : 
     196             : /*
     197             :  * Functions for executing graph
     198             :  */
     199             : 
     200             : /* A transition graph consists of various types of actions. The library caller
     201             :  * registers execution functions for each action type, which will be stored
     202             :  * here.
     203             :  */
     204             : static pcmk__graph_functions_t *graph_fns = NULL;
     205             : 
     206             : /*!
     207             :  * \internal
     208             :  * \brief Set transition graph execution functions
     209             :  *
     210             :  * \param[in]  Execution functions to use
     211             :  */
     212             : void
     213           0 : pcmk__set_graph_functions(pcmk__graph_functions_t *fns)
     214             : {
     215           0 :     crm_debug("Setting custom functions for executing transition graphs");
     216           0 :     graph_fns = fns;
     217             : 
     218           0 :     CRM_ASSERT(graph_fns != NULL);
     219           0 :     CRM_ASSERT(graph_fns->rsc != NULL);
     220           0 :     CRM_ASSERT(graph_fns->cluster != NULL);
     221           0 :     CRM_ASSERT(graph_fns->pseudo != NULL);
     222           0 :     CRM_ASSERT(graph_fns->fence != NULL);
     223           0 : }
     224             : 
     225             : /*!
     226             :  * \internal
     227             :  * \brief Check whether a graph synapse is ready to be executed
     228             :  *
     229             :  * \param[in,out] graph    Transition graph that synapse is part of
     230             :  * \param[in,out] synapse  Synapse to check
     231             :  *
     232             :  * \return true if synapse is ready, false otherwise
     233             :  */
     234             : static bool
     235           0 : should_fire_synapse(pcmk__graph_t *graph, pcmk__graph_synapse_t *synapse)
     236             : {
     237           0 :     GList *lpc = NULL;
     238             : 
     239           0 :     pcmk__set_synapse_flags(synapse, pcmk__synapse_ready);
     240           0 :     for (lpc = synapse->inputs; lpc != NULL; lpc = lpc->next) {
     241           0 :         pcmk__graph_action_t *prereq = (pcmk__graph_action_t *) lpc->data;
     242             : 
     243           0 :         if (!(pcmk_is_set(prereq->flags, pcmk__graph_action_confirmed))) {
     244           0 :             crm_trace("Input %d for synapse %d not yet confirmed",
     245             :                       prereq->id, synapse->id);
     246           0 :             pcmk__clear_synapse_flags(synapse, pcmk__synapse_ready);
     247           0 :             break;
     248             : 
     249           0 :         } else if (pcmk_is_set(prereq->flags, pcmk__graph_action_failed)
     250           0 :                    && !pcmk_is_set(prereq->flags,
     251             :                                    pcmk__graph_action_can_fail)) {
     252           0 :             crm_trace("Input %d for synapse %d confirmed but failed",
     253             :                       prereq->id, synapse->id);
     254           0 :             pcmk__clear_synapse_flags(synapse, pcmk__synapse_ready);
     255           0 :             break;
     256             :         }
     257             :     }
     258           0 :     if (pcmk_is_set(synapse->flags, pcmk__synapse_ready)) {
     259           0 :         crm_trace("Synapse %d is ready to execute", synapse->id);
     260             :     } else {
     261           0 :         return false;
     262             :     }
     263             : 
     264           0 :     for (lpc = synapse->actions; lpc != NULL; lpc = lpc->next) {
     265           0 :         pcmk__graph_action_t *a = (pcmk__graph_action_t *) lpc->data;
     266             : 
     267           0 :         if (a->type == pcmk__pseudo_graph_action) {
     268             :             /* None of the below applies to pseudo ops */
     269             : 
     270           0 :         } else if (synapse->priority < graph->abort_priority) {
     271           0 :             crm_trace("Skipping synapse %d: priority %d is less than "
     272             :                       "abort priority %d",
     273             :                       synapse->id, synapse->priority, graph->abort_priority);
     274           0 :             graph->skipped++;
     275           0 :             return false;
     276             : 
     277           0 :         } else if (graph_fns->allowed && !(graph_fns->allowed(graph, a))) {
     278           0 :             crm_trace("Deferring synapse %d: not allowed", synapse->id);
     279           0 :             return false;
     280             :         }
     281             :     }
     282             : 
     283           0 :     return true;
     284             : }
     285             : 
     286             : /*!
     287             :  * \internal
     288             :  * \brief Initiate an action from a transition graph
     289             :  *
     290             :  * \param[in,out] graph   Transition graph containing action
     291             :  * \param[in,out] action  Action to execute
     292             :  *
     293             :  * \return Standard Pacemaker return code
     294             :  */
     295             : static int
     296           0 : initiate_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
     297             : {
     298           0 :     const char *id = pcmk__xe_id(action->xml);
     299             : 
     300           0 :     CRM_CHECK(id != NULL, return EINVAL);
     301           0 :     CRM_CHECK(!pcmk_is_set(action->flags, pcmk__graph_action_executed),
     302             :               return pcmk_rc_already);
     303             : 
     304           0 :     pcmk__set_graph_action_flags(action, pcmk__graph_action_executed);
     305           0 :     switch (action->type) {
     306           0 :         case pcmk__pseudo_graph_action:
     307           0 :             crm_trace("Executing pseudo-action %d (%s)", action->id, id);
     308           0 :             return graph_fns->pseudo(graph, action);
     309             : 
     310           0 :         case pcmk__rsc_graph_action:
     311           0 :             crm_trace("Executing resource action %d (%s)", action->id, id);
     312           0 :             return graph_fns->rsc(graph, action);
     313             : 
     314           0 :         case pcmk__cluster_graph_action:
     315           0 :             if (pcmk__str_eq(crm_element_value(action->xml, PCMK_XA_OPERATION),
     316             :                              PCMK_ACTION_STONITH, pcmk__str_none)) {
     317           0 :                 crm_trace("Executing fencing action %d (%s)",
     318             :                           action->id, id);
     319           0 :                 return graph_fns->fence(graph, action);
     320             :             }
     321           0 :             crm_trace("Executing cluster action %d (%s)", action->id, id);
     322           0 :             return graph_fns->cluster(graph, action);
     323             : 
     324           0 :         default:
     325           0 :             crm_err("Unsupported graph action type <%s " PCMK_XA_ID "='%s'> "
     326             :                     "(bug?)",
     327             :                     action->xml->name, id);
     328           0 :             return EINVAL;
     329             :     }
     330             : }
     331             : 
     332             : /*!
     333             :  * \internal
     334             :  * \brief Execute a graph synapse
     335             :  *
     336             :  * \param[in,out] graph    Transition graph with synapse to execute
     337             :  * \param[in,out] synapse  Synapse to execute
     338             :  *
     339             :  * \return Standard Pacemaker return value
     340             :  */
     341             : static int
     342           0 : fire_synapse(pcmk__graph_t *graph, pcmk__graph_synapse_t *synapse)
     343             : {
     344           0 :     pcmk__set_synapse_flags(synapse, pcmk__synapse_executed);
     345           0 :     for (GList *lpc = synapse->actions; lpc != NULL; lpc = lpc->next) {
     346           0 :         pcmk__graph_action_t *action = (pcmk__graph_action_t *) lpc->data;
     347           0 :         int rc = initiate_action(graph, action);
     348             : 
     349           0 :         if (rc != pcmk_rc_ok) {
     350           0 :             crm_err("Failed initiating <%s " PCMK_XA_ID "=%d> in synapse %d: "
     351             :                     "%s",
     352             :                     action->xml->name, action->id, synapse->id,
     353             :                     pcmk_rc_str(rc));
     354           0 :             pcmk__set_synapse_flags(synapse, pcmk__synapse_confirmed);
     355           0 :             pcmk__set_graph_action_flags(action,
     356             :                                          pcmk__graph_action_confirmed
     357             :                                          |pcmk__graph_action_failed);
     358           0 :             return pcmk_rc_error;
     359             :         }
     360             :     }
     361           0 :     return pcmk_rc_ok;
     362             : }
     363             : 
     364             : /*!
     365             :  * \internal
     366             :  * \brief Dummy graph method that can be used with simulations
     367             :  *
     368             :  * \param[in,out] graph   Transition graph containing action
     369             :  * \param[in,out] action  Graph action to be initiated
     370             :  *
     371             :  * \return Standard Pacemaker return code
     372             :  * \note If the PE_fail environment variable is set to the action ID,
     373             :  *       then the graph action will be marked as failed.
     374             :  */
     375             : static int
     376           0 : pseudo_action_dummy(pcmk__graph_t *graph, pcmk__graph_action_t *action)
     377             : {
     378             :     static int fail = -1;
     379             : 
     380           0 :     if (fail < 0) {
     381             :         long long fail_ll;
     382             : 
     383           0 :         if ((pcmk__scan_ll(getenv("PE_fail"), &fail_ll, 0LL) == pcmk_rc_ok)
     384           0 :             && (fail_ll > 0LL) && (fail_ll <= INT_MAX)) {
     385           0 :             fail = (int) fail_ll;
     386             :         } else {
     387           0 :             fail = 0;
     388             :         }
     389             :     }
     390             : 
     391           0 :     if (action->id == fail) {
     392           0 :         crm_err("Dummy event handler: pretending action %d failed", action->id);
     393           0 :         pcmk__set_graph_action_flags(action, pcmk__graph_action_failed);
     394           0 :         graph->abort_priority = PCMK_SCORE_INFINITY;
     395             :     } else {
     396           0 :         crm_trace("Dummy event handler: action %d initiated", action->id);
     397             :     }
     398           0 :     pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
     399           0 :     pcmk__update_graph(graph, action);
     400           0 :     return pcmk_rc_ok;
     401             : }
     402             : 
     403             : static pcmk__graph_functions_t default_fns = {
     404             :     pseudo_action_dummy,
     405             :     pseudo_action_dummy,
     406             :     pseudo_action_dummy,
     407             :     pseudo_action_dummy
     408             : };
     409             : 
     410             : /*!
     411             :  * \internal
     412             :  * \brief Execute all actions in a transition graph
     413             :  *
     414             :  * \param[in,out] graph  Transition graph to execute
     415             :  *
     416             :  * \return Status of transition after execution
     417             :  */
     418             : enum pcmk__graph_status
     419           0 : pcmk__execute_graph(pcmk__graph_t *graph)
     420             : {
     421           0 :     GList *lpc = NULL;
     422           0 :     int log_level = LOG_DEBUG;
     423           0 :     enum pcmk__graph_status pass_result = pcmk__graph_active;
     424           0 :     const char *status = "In progress";
     425             : 
     426           0 :     if (graph_fns == NULL) {
     427           0 :         graph_fns = &default_fns;
     428             :     }
     429           0 :     if (graph == NULL) {
     430           0 :         return pcmk__graph_complete;
     431             :     }
     432             : 
     433           0 :     graph->fired = 0;
     434           0 :     graph->pending = 0;
     435           0 :     graph->skipped = 0;
     436           0 :     graph->completed = 0;
     437           0 :     graph->incomplete = 0;
     438             : 
     439             :     // Count completed and in-flight synapses
     440           0 :     for (lpc = graph->synapses; lpc != NULL; lpc = lpc->next) {
     441           0 :         pcmk__graph_synapse_t *synapse = (pcmk__graph_synapse_t *) lpc->data;
     442             : 
     443           0 :         if (pcmk_is_set(synapse->flags, pcmk__synapse_confirmed)) {
     444           0 :             graph->completed++;
     445             : 
     446           0 :         } else if (!pcmk_is_set(synapse->flags, pcmk__synapse_failed)
     447           0 :                    && pcmk_is_set(synapse->flags, pcmk__synapse_executed)) {
     448           0 :             graph->pending++;
     449             :         }
     450             :     }
     451           0 :     crm_trace("Executing graph %d (%d synapses already completed, %d pending)",
     452             :               graph->id, graph->completed, graph->pending);
     453             : 
     454             :     // Execute any synapses that are ready
     455           0 :     for (lpc = graph->synapses; lpc != NULL; lpc = lpc->next) {
     456           0 :         pcmk__graph_synapse_t *synapse = (pcmk__graph_synapse_t *) lpc->data;
     457             : 
     458           0 :         if ((graph->batch_limit > 0)
     459           0 :             && (graph->pending >= graph->batch_limit)) {
     460             : 
     461           0 :             crm_debug("Throttling graph execution: batch limit (%d) reached",
     462             :                       graph->batch_limit);
     463           0 :             break;
     464             : 
     465           0 :         } else if (pcmk_is_set(synapse->flags, pcmk__synapse_failed)) {
     466           0 :             graph->skipped++;
     467           0 :             continue;
     468             : 
     469           0 :         } else if (pcmk_any_flags_set(synapse->flags,
     470             :                                       pcmk__synapse_confirmed
     471             :                                       |pcmk__synapse_executed)) {
     472           0 :             continue; // Already handled
     473             : 
     474           0 :         } else if (should_fire_synapse(graph, synapse)) {
     475           0 :             graph->fired++;
     476           0 :             if (fire_synapse(graph, synapse) != pcmk_rc_ok) {
     477           0 :                 crm_err("Synapse %d failed to fire", synapse->id);
     478           0 :                 log_level = LOG_ERR;
     479           0 :                 graph->abort_priority = PCMK_SCORE_INFINITY;
     480           0 :                 graph->incomplete++;
     481           0 :                 graph->fired--;
     482             :             }
     483             : 
     484           0 :             if (!(pcmk_is_set(synapse->flags, pcmk__synapse_confirmed))) {
     485           0 :                 graph->pending++;
     486             :             }
     487             : 
     488             :         } else {
     489           0 :             crm_trace("Synapse %d cannot fire", synapse->id);
     490           0 :             graph->incomplete++;
     491             :         }
     492             :     }
     493             : 
     494           0 :     if ((graph->pending == 0) && (graph->fired == 0)) {
     495           0 :         graph->complete = true;
     496             : 
     497           0 :         if ((graph->incomplete != 0) && (graph->abort_priority <= 0)) {
     498           0 :             log_level = LOG_WARNING;
     499           0 :             pass_result = pcmk__graph_terminated;
     500           0 :             status = "Terminated";
     501             : 
     502           0 :         } else if (graph->skipped != 0) {
     503           0 :             log_level = LOG_NOTICE;
     504           0 :             pass_result = pcmk__graph_complete;
     505           0 :             status = "Stopped";
     506             : 
     507             :         } else {
     508           0 :             log_level = LOG_NOTICE;
     509           0 :             pass_result = pcmk__graph_complete;
     510           0 :             status = "Complete";
     511             :         }
     512             : 
     513           0 :     } else if (graph->fired == 0) {
     514           0 :         pass_result = pcmk__graph_pending;
     515             :     }
     516             : 
     517           0 :     do_crm_log(log_level,
     518             :                "Transition %d (Complete=%d, Pending=%d,"
     519             :                " Fired=%d, Skipped=%d, Incomplete=%d, Source=%s): %s",
     520             :                graph->id, graph->completed, graph->pending, graph->fired,
     521             :                graph->skipped, graph->incomplete, graph->source, status);
     522             : 
     523           0 :     return pass_result;
     524             : }
     525             : 
     526             : 
     527             : /*
     528             :  * Functions for unpacking transition graph XML into structs
     529             :  */
     530             : 
     531             : /*!
     532             :  * \internal
     533             :  * \brief Unpack a transition graph action from XML
     534             :  *
     535             :  * \param[in] parent      Synapse that action is part of
     536             :  * \param[in] xml_action  Action XML to unparse
     537             :  *
     538             :  * \return Newly allocated action on success, or NULL otherwise
     539             :  */
     540             : static pcmk__graph_action_t *
     541           0 : unpack_action(pcmk__graph_synapse_t *parent, xmlNode *xml_action)
     542             : {
     543             :     enum pcmk__graph_action_type action_type;
     544           0 :     pcmk__graph_action_t *action = NULL;
     545           0 :     const char *value = pcmk__xe_id(xml_action);
     546             : 
     547           0 :     if (value == NULL) {
     548           0 :         crm_err("Ignoring transition graph action without " PCMK_XA_ID
     549             :                 " (bug?)");
     550           0 :         crm_log_xml_trace(xml_action, "invalid");
     551           0 :         return NULL;
     552             :     }
     553             : 
     554           0 :     if (pcmk__xe_is(xml_action, PCMK__XE_RSC_OP)) {
     555           0 :         action_type = pcmk__rsc_graph_action;
     556             : 
     557           0 :     } else if (pcmk__xe_is(xml_action, PCMK__XE_PSEUDO_EVENT)) {
     558           0 :         action_type = pcmk__pseudo_graph_action;
     559             : 
     560           0 :     } else if (pcmk__xe_is(xml_action, PCMK__XE_CRM_EVENT)) {
     561           0 :         action_type = pcmk__cluster_graph_action;
     562             : 
     563             :     } else {
     564           0 :         crm_err("Ignoring transition graph action of unknown type '%s' (bug?)",
     565             :                 xml_action->name);
     566           0 :         crm_log_xml_trace(xml_action, "invalid");
     567           0 :         return NULL;
     568             :     }
     569             : 
     570           0 :     action = calloc(1, sizeof(pcmk__graph_action_t));
     571           0 :     if (action == NULL) {
     572           0 :         crm_perror(LOG_CRIT, "Cannot unpack transition graph action");
     573           0 :         crm_log_xml_trace(xml_action, "lost");
     574           0 :         return NULL;
     575             :     }
     576             : 
     577           0 :     pcmk__scan_min_int(value, &(action->id), -1);
     578           0 :     action->type = pcmk__rsc_graph_action;
     579           0 :     action->xml = pcmk__xml_copy(NULL, xml_action);
     580           0 :     action->synapse = parent;
     581           0 :     action->type = action_type;
     582           0 :     action->params = xml2list(action->xml);
     583             : 
     584           0 :     value = crm_meta_value(action->params, PCMK_META_TIMEOUT);
     585           0 :     pcmk__scan_min_int(value, &(action->timeout), 0);
     586             : 
     587             :     /* Take PCMK_META_START_DELAY into account for the timeout of the action
     588             :      * timer
     589             :      */
     590           0 :     value = crm_meta_value(action->params, PCMK_META_START_DELAY);
     591             :     {
     592             :         int start_delay;
     593             : 
     594           0 :         pcmk__scan_min_int(value, &start_delay, 0);
     595           0 :         action->timeout += start_delay;
     596             :     }
     597             : 
     598           0 :     if (pcmk__guint_from_hash(action->params, CRM_META "_" PCMK_META_INTERVAL,
     599             :                               0, &(action->interval_ms)) != pcmk_rc_ok) {
     600           0 :         action->interval_ms = 0;
     601             :     }
     602             : 
     603           0 :     value = crm_meta_value(action->params, PCMK__META_CAN_FAIL);
     604           0 :     if (value != NULL) {
     605           0 :         int can_fail = 0;
     606             : 
     607           0 :         if ((crm_str_to_boolean(value, &can_fail) > 0) && (can_fail > 0)) {
     608           0 :             pcmk__set_graph_action_flags(action, pcmk__graph_action_can_fail);
     609             :         } else {
     610           0 :             pcmk__clear_graph_action_flags(action, pcmk__graph_action_can_fail);
     611             :         }
     612             : 
     613           0 :         if (pcmk_is_set(action->flags, pcmk__graph_action_can_fail)) {
     614           0 :             crm_warn("Support for the " PCMK__META_CAN_FAIL " meta-attribute "
     615             :                      "is deprecated and will be removed in a future release");
     616             :         }
     617             :     }
     618             : 
     619           0 :     crm_trace("Action %d has timer set to %dms", action->id, action->timeout);
     620             : 
     621           0 :     return action;
     622             : }
     623             : 
     624             : /*!
     625             :  * \internal
     626             :  * \brief Unpack transition graph synapse from XML
     627             :  *
     628             :  * \param[in,out] new_graph    Transition graph that synapse is part of
     629             :  * \param[in]     xml_synapse  Synapse XML
     630             :  *
     631             :  * \return Newly allocated synapse on success, or NULL otherwise
     632             :  */
     633             : static pcmk__graph_synapse_t *
     634           0 : unpack_synapse(pcmk__graph_t *new_graph, const xmlNode *xml_synapse)
     635             : {
     636           0 :     const char *value = NULL;
     637           0 :     xmlNode *action_set = NULL;
     638           0 :     pcmk__graph_synapse_t *new_synapse = NULL;
     639             : 
     640           0 :     crm_trace("Unpacking synapse %s", pcmk__xe_id(xml_synapse));
     641             : 
     642           0 :     new_synapse = calloc(1, sizeof(pcmk__graph_synapse_t));
     643           0 :     if (new_synapse == NULL) {
     644           0 :         return NULL;
     645             :     }
     646             : 
     647           0 :     pcmk__scan_min_int(pcmk__xe_id(xml_synapse), &(new_synapse->id), 0);
     648             : 
     649           0 :     value = crm_element_value(xml_synapse, PCMK__XA_PRIORITY);
     650           0 :     pcmk__scan_min_int(value, &(new_synapse->priority), 0);
     651             : 
     652           0 :     CRM_CHECK(new_synapse->id >= 0,
     653             :               free_graph_synapse((gpointer) new_synapse); return NULL);
     654             : 
     655           0 :     new_graph->num_synapses++;
     656             : 
     657           0 :     crm_trace("Unpacking synapse %s action sets",
     658             :               crm_element_value(xml_synapse, PCMK_XA_ID));
     659             : 
     660           0 :     for (action_set = pcmk__xe_first_child(xml_synapse, "action_set", NULL,
     661             :                                            NULL);
     662           0 :          action_set != NULL; action_set = pcmk__xe_next_same(action_set)) {
     663             : 
     664           0 :         for (xmlNode *action = pcmk__xe_first_child(action_set, NULL, NULL,
     665             :                                                     NULL);
     666           0 :              action != NULL; action = pcmk__xe_next(action)) {
     667             : 
     668           0 :             pcmk__graph_action_t *new_action = unpack_action(new_synapse,
     669             :                                                              action);
     670             : 
     671           0 :             if (new_action == NULL) {
     672           0 :                 continue;
     673             :             }
     674             : 
     675           0 :             crm_trace("Adding action %d to synapse %d",
     676             :                       new_action->id, new_synapse->id);
     677           0 :             new_graph->num_actions++;
     678           0 :             new_synapse->actions = g_list_append(new_synapse->actions,
     679             :                                                  new_action);
     680             :         }
     681             :     }
     682             : 
     683           0 :     crm_trace("Unpacking synapse %s inputs", pcmk__xe_id(xml_synapse));
     684             : 
     685           0 :     for (xmlNode *inputs = pcmk__xe_first_child(xml_synapse, "inputs", NULL,
     686             :                                                 NULL);
     687           0 :          inputs != NULL; inputs = pcmk__xe_next_same(inputs)) {
     688             : 
     689           0 :         for (xmlNode *trigger = pcmk__xe_first_child(inputs, "trigger", NULL,
     690             :                                                      NULL);
     691           0 :              trigger != NULL; trigger = pcmk__xe_next_same(trigger)) {
     692             : 
     693           0 :             for (xmlNode *input = pcmk__xe_first_child(trigger, NULL, NULL,
     694             :                                                        NULL);
     695           0 :                  input != NULL; input = pcmk__xe_next(input)) {
     696             : 
     697           0 :                 pcmk__graph_action_t *new_input = unpack_action(new_synapse,
     698             :                                                                 input);
     699             : 
     700           0 :                 if (new_input == NULL) {
     701           0 :                     continue;
     702             :                 }
     703             : 
     704           0 :                 crm_trace("Adding input %d to synapse %d",
     705             :                            new_input->id, new_synapse->id);
     706             : 
     707           0 :                 new_synapse->inputs = g_list_append(new_synapse->inputs,
     708             :                                                     new_input);
     709             :             }
     710             :         }
     711             :     }
     712             : 
     713           0 :     return new_synapse;
     714             : }
     715             : 
     716             : /*!
     717             :  * \internal
     718             :  * \brief Unpack transition graph XML
     719             :  *
     720             :  * \param[in] xml_graph  Transition graph XML to unpack
     721             :  * \param[in] reference  Where the XML came from (for logging)
     722             :  *
     723             :  * \return Newly allocated transition graph on success, NULL otherwise
     724             :  * \note The caller is responsible for freeing the return value using
     725             :  *       pcmk__free_graph().
     726             :  * \note The XML is expected to be structured like:
     727             :          <transition_graph ...>
     728             :            <synapse id="0">
     729             :              <action_set>
     730             :                <rsc_op id="2" ...>
     731             :                ...
     732             :              </action_set>
     733             :              <inputs>
     734             :                  <rsc_op id="1" ...
     735             :                  ...
     736             :              </inputs>
     737             :            </synapse>
     738             :            ...
     739             :          </transition_graph>
     740             :  */
     741             : pcmk__graph_t *
     742           0 : pcmk__unpack_graph(const xmlNode *xml_graph, const char *reference)
     743             : {
     744           0 :     pcmk__graph_t *new_graph = NULL;
     745             : 
     746           0 :     new_graph = calloc(1, sizeof(pcmk__graph_t));
     747           0 :     if (new_graph == NULL) {
     748           0 :         return NULL;
     749             :     }
     750             : 
     751           0 :     new_graph->source = strdup(pcmk__s(reference, "unknown"));
     752           0 :     if (new_graph->source == NULL) {
     753           0 :         pcmk__free_graph(new_graph);
     754           0 :         return NULL;
     755             :     }
     756             : 
     757           0 :     new_graph->id = -1;
     758           0 :     new_graph->abort_priority = 0;
     759           0 :     new_graph->network_delay = 0;
     760           0 :     new_graph->stonith_timeout = 0;
     761           0 :     new_graph->completion_action = pcmk__graph_done;
     762             : 
     763             :     // Parse top-level attributes from PCMK__XE_TRANSITION_GRAPH
     764           0 :     if (xml_graph != NULL) {
     765           0 :         const char *buf = crm_element_value(xml_graph, "transition_id");
     766             : 
     767           0 :         CRM_CHECK(buf != NULL,
     768             :                   pcmk__free_graph(new_graph); return NULL);
     769           0 :         pcmk__scan_min_int(buf, &(new_graph->id), -1);
     770             : 
     771           0 :         buf = crm_element_value(xml_graph, PCMK_OPT_CLUSTER_DELAY);
     772           0 :         CRM_CHECK(buf != NULL,
     773             :                   pcmk__free_graph(new_graph); return NULL);
     774           0 :         pcmk_parse_interval_spec(buf, &(new_graph->network_delay));
     775             : 
     776           0 :         buf = crm_element_value(xml_graph, PCMK_OPT_STONITH_TIMEOUT);
     777           0 :         if (buf == NULL) {
     778           0 :             new_graph->stonith_timeout = new_graph->network_delay;
     779             :         } else {
     780           0 :             pcmk_parse_interval_spec(buf, &(new_graph->stonith_timeout));
     781             :         }
     782             : 
     783             :         // Use 0 (dynamic limit) as default/invalid, -1 (no limit) as minimum
     784           0 :         buf = crm_element_value(xml_graph, PCMK_OPT_BATCH_LIMIT);
     785           0 :         if ((buf == NULL)
     786           0 :             || (pcmk__scan_min_int(buf, &(new_graph->batch_limit),
     787             :                                    -1) != pcmk_rc_ok)) {
     788           0 :             new_graph->batch_limit = 0;
     789             :         }
     790             : 
     791           0 :         buf = crm_element_value(xml_graph, PCMK_OPT_MIGRATION_LIMIT);
     792           0 :         pcmk__scan_min_int(buf, &(new_graph->migration_limit), -1);
     793             : 
     794           0 :         new_graph->failed_stop_offset =
     795           0 :             crm_element_value_copy(xml_graph, "failed-stop-offset");
     796           0 :         new_graph->failed_start_offset =
     797           0 :             crm_element_value_copy(xml_graph, "failed-start-offset");
     798             : 
     799           0 :         if (crm_element_value_epoch(xml_graph, "recheck-by",
     800             :                                     &(new_graph->recheck_by)) != pcmk_ok) {
     801           0 :             new_graph->recheck_by = 0;
     802             :         }
     803             :     }
     804             : 
     805             :     // Unpack each child <synapse> element
     806           0 :     for (const xmlNode *synapse_xml = pcmk__xe_first_child(xml_graph,
     807             :                                                            "synapse", NULL,
     808             :                                                            NULL);
     809           0 :          synapse_xml != NULL; synapse_xml = pcmk__xe_next_same(synapse_xml)) {
     810             : 
     811           0 :         pcmk__graph_synapse_t *new_synapse = unpack_synapse(new_graph,
     812             :                                                             synapse_xml);
     813             : 
     814           0 :         if (new_synapse != NULL) {
     815           0 :             new_graph->synapses = g_list_append(new_graph->synapses,
     816             :                                                 new_synapse);
     817             :         }
     818             :     }
     819             : 
     820           0 :     crm_debug("Unpacked transition %d from %s: %d actions in %d synapses",
     821             :               new_graph->id, new_graph->source, new_graph->num_actions,
     822             :               new_graph->num_synapses);
     823             : 
     824           0 :     return new_graph;
     825             : }
     826             : 
     827             : 
     828             : /*
     829             :  * Other transition graph utilities
     830             :  */
     831             : 
     832             : /*!
     833             :  * \internal
     834             :  * \brief Synthesize an executor event from a graph action
     835             :  *
     836             :  * \param[in] resource     If not NULL, use greater call ID than in this XML
     837             :  * \param[in] action       Graph action
     838             :  * \param[in] status       What to use as event execution status
     839             :  * \param[in] rc           What to use as event exit status
     840             :  * \param[in] exit_reason  What to use as event exit reason
     841             :  *
     842             :  * \return Newly allocated executor event on success, or NULL otherwise
     843             :  */
     844             : lrmd_event_data_t *
     845           0 : pcmk__event_from_graph_action(const xmlNode *resource,
     846             :                               const pcmk__graph_action_t *action,
     847             :                               int status, int rc, const char *exit_reason)
     848             : {
     849           0 :     lrmd_event_data_t *op = NULL;
     850             :     GHashTableIter iter;
     851           0 :     const char *name = NULL;
     852           0 :     const char *value = NULL;
     853           0 :     xmlNode *action_resource = NULL;
     854             : 
     855           0 :     CRM_CHECK(action != NULL, return NULL);
     856           0 :     CRM_CHECK(action->type == pcmk__rsc_graph_action, return NULL);
     857             : 
     858           0 :     action_resource = pcmk__xe_first_child(action->xml, PCMK_XE_PRIMITIVE, NULL,
     859             :                                            NULL);
     860           0 :     CRM_CHECK(action_resource != NULL, crm_log_xml_warn(action->xml, "invalid");
     861             :                                        return NULL);
     862             : 
     863           0 :     op = lrmd_new_event(pcmk__xe_id(action_resource),
     864           0 :                         crm_element_value(action->xml, PCMK_XA_OPERATION),
     865           0 :                         action->interval_ms);
     866           0 :     lrmd__set_result(op, rc, status, exit_reason);
     867           0 :     op->t_run = time(NULL);
     868           0 :     op->t_rcchange = op->t_run;
     869           0 :     op->params = pcmk__strkey_table(free, free);
     870             : 
     871           0 :     g_hash_table_iter_init(&iter, action->params);
     872           0 :     while (g_hash_table_iter_next(&iter, (void **)&name, (void **)&value)) {
     873           0 :         pcmk__insert_dup(op->params, name, value);
     874             :     }
     875             : 
     876           0 :     for (xmlNode *xop = pcmk__xe_first_child(resource, NULL, NULL, NULL);
     877           0 :          xop != NULL; xop = pcmk__xe_next(xop)) {
     878             : 
     879           0 :         int tmp = 0;
     880             : 
     881           0 :         crm_element_value_int(xop, PCMK__XA_CALL_ID, &tmp);
     882           0 :         crm_debug("Got call_id=%d for %s", tmp, pcmk__xe_id(resource));
     883           0 :         if (tmp > op->call_id) {
     884           0 :             op->call_id = tmp;
     885             :         }
     886             :     }
     887             : 
     888           0 :     op->call_id++;
     889           0 :     return op;
     890             : }

Generated by: LCOV version 1.14