root/lib/pacemaker/pcmk_graph_consumer.c

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

DEFINITIONS

This source file includes following definitions.
  1. update_synapse_ready
  2. update_synapse_confirmed
  3. pcmk__update_graph
  4. pcmk__set_graph_functions
  5. should_fire_synapse
  6. initiate_action
  7. fire_synapse
  8. pseudo_action_dummy
  9. pcmk__execute_graph
  10. unpack_action
  11. unpack_synapse
  12. pcmk__unpack_graph
  13. free_graph_action
  14. free_graph_synapse
  15. pcmk__free_graph
  16. pcmk__event_from_graph_action

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

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