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

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