root/lib/pacemaker/pcmk_graph_consumer.c

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

DEFINITIONS

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

   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 free_graph_action(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  34 {
  35     pcmk__graph_action_t *action = user_data;
  36 
  37     if (action->timer != 0) {
  38         crm_warn("Cancelling timer for graph action %d", action->id);
  39         g_source_remove(action->timer);
  40     }
  41     if (action->params != NULL) {
  42         g_hash_table_destroy(action->params);
  43     }
  44     free_xml(action->xml);
  45     free(action);
  46 }
  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 free_graph_synapse(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  56 {
  57     pcmk__graph_synapse_t *synapse = user_data;
  58 
  59     g_list_free_full(synapse->actions, free_graph_action);
  60     g_list_free_full(synapse->inputs, free_graph_action);
  61     free(synapse);
  62 }
  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 pcmk__free_graph(pcmk__graph_t *graph)
     /* [previous][next][first][last][top][bottom][index][help] */
  72 {
  73     if (graph != NULL) {
  74         g_list_free_full(graph->synapses, free_graph_synapse);
  75         free(graph->source);
  76         free(graph->failed_stop_offset);
  77         free(graph->failed_start_offset);
  78         free(graph);
  79     }
  80 }
  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 update_synapse_ready(pcmk__graph_synapse_t *synapse, int action_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 106 {
 107     if (pcmk_is_set(synapse->flags, pcmk__synapse_ready)) {
 108         return; // All inputs have already been confirmed
 109     }
 110 
 111     // Presume ready until proven otherwise
 112     pcmk__set_synapse_flags(synapse, pcmk__synapse_ready);
 113 
 114     for (GList *lpc = synapse->inputs; lpc != NULL; lpc = lpc->next) {
 115         pcmk__graph_action_t *prereq = (pcmk__graph_action_t *) lpc->data;
 116 
 117         if (prereq->id == action_id) {
 118             crm_trace("Confirming input %d of synapse %d",
 119                       action_id, synapse->id);
 120             pcmk__set_graph_action_flags(prereq, pcmk__graph_action_confirmed);
 121 
 122         } else if (!pcmk_is_set(prereq->flags, pcmk__graph_action_confirmed)) {
 123             pcmk__clear_synapse_flags(synapse, pcmk__synapse_ready);
 124             crm_trace("Synapse %d still not ready after action %d",
 125                       synapse->id, action_id);
 126         }
 127     }
 128     if (pcmk_is_set(synapse->flags, pcmk__synapse_ready)) {
 129         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 update_synapse_confirmed(pcmk__graph_synapse_t *synapse, int action_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 142 {
 143     bool all_confirmed = true;
 144 
 145     for (GList *lpc = synapse->actions; lpc != NULL; lpc = lpc->next) {
 146         pcmk__graph_action_t *action = (pcmk__graph_action_t *) lpc->data;
 147 
 148         if (action->id == action_id) {
 149             crm_trace("Confirmed action %d of synapse %d",
 150                       action_id, synapse->id);
 151             pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
 152 
 153         } else if (all_confirmed &&
 154                    !pcmk_is_set(action->flags, pcmk__graph_action_confirmed)) {
 155             all_confirmed = false;
 156             crm_trace("Synapse %d still not confirmed after action %d",
 157                       synapse->id, action_id);
 158         }
 159     }
 160 
 161     if (all_confirmed
 162         && !pcmk_is_set(synapse->flags, pcmk__synapse_confirmed)) {
 163         crm_trace("Confirmed synapse %d", synapse->id);
 164         pcmk__set_synapse_flags(synapse, pcmk__synapse_confirmed);
 165     }
 166 }
 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 pcmk__update_graph(pcmk__graph_t *graph, const pcmk__graph_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 177 {
 178     for (GList *lpc = graph->synapses; lpc != NULL; lpc = lpc->next) {
 179         pcmk__graph_synapse_t *synapse = (pcmk__graph_synapse_t *) lpc->data;
 180 
 181         if (pcmk_any_flags_set(synapse->flags,
 182                                pcmk__synapse_confirmed|pcmk__synapse_failed)) {
 183             continue; // This synapse already completed
 184 
 185         } else if (pcmk_is_set(synapse->flags, pcmk__synapse_executed)) {
 186             update_synapse_confirmed(synapse, action->id);
 187 
 188         } else if (!pcmk_is_set(action->flags, pcmk__graph_action_failed)
 189                    || (synapse->priority == PCMK_SCORE_INFINITY)) {
 190             update_synapse_ready(synapse, action->id);
 191         }
 192     }
 193 }
 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 pcmk__set_graph_functions(pcmk__graph_functions_t *fns)
     /* [previous][next][first][last][top][bottom][index][help] */
 214 {
 215     crm_debug("Setting custom functions for executing transition graphs");
 216     graph_fns = fns;
 217 
 218     CRM_ASSERT(graph_fns != NULL);
 219     CRM_ASSERT(graph_fns->rsc != NULL);
 220     CRM_ASSERT(graph_fns->cluster != NULL);
 221     CRM_ASSERT(graph_fns->pseudo != NULL);
 222     CRM_ASSERT(graph_fns->fence != NULL);
 223 }
 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 should_fire_synapse(pcmk__graph_t *graph, pcmk__graph_synapse_t *synapse)
     /* [previous][next][first][last][top][bottom][index][help] */
 236 {
 237     GList *lpc = NULL;
 238 
 239     pcmk__set_synapse_flags(synapse, pcmk__synapse_ready);
 240     for (lpc = synapse->inputs; lpc != NULL; lpc = lpc->next) {
 241         pcmk__graph_action_t *prereq = (pcmk__graph_action_t *) lpc->data;
 242 
 243         if (!(pcmk_is_set(prereq->flags, pcmk__graph_action_confirmed))) {
 244             crm_trace("Input %d for synapse %d not yet confirmed",
 245                       prereq->id, synapse->id);
 246             pcmk__clear_synapse_flags(synapse, pcmk__synapse_ready);
 247             break;
 248 
 249         } else if (pcmk_is_set(prereq->flags, pcmk__graph_action_failed)
 250                    && !pcmk_is_set(prereq->flags,
 251                                    pcmk__graph_action_can_fail)) {
 252             crm_trace("Input %d for synapse %d confirmed but failed",
 253                       prereq->id, synapse->id);
 254             pcmk__clear_synapse_flags(synapse, pcmk__synapse_ready);
 255             break;
 256         }
 257     }
 258     if (pcmk_is_set(synapse->flags, pcmk__synapse_ready)) {
 259         crm_trace("Synapse %d is ready to execute", synapse->id);
 260     } else {
 261         return false;
 262     }
 263 
 264     for (lpc = synapse->actions; lpc != NULL; lpc = lpc->next) {
 265         pcmk__graph_action_t *a = (pcmk__graph_action_t *) lpc->data;
 266 
 267         if (a->type == pcmk__pseudo_graph_action) {
 268             /* None of the below applies to pseudo ops */
 269 
 270         } else if (synapse->priority < graph->abort_priority) {
 271             crm_trace("Skipping synapse %d: priority %d is less than "
 272                       "abort priority %d",
 273                       synapse->id, synapse->priority, graph->abort_priority);
 274             graph->skipped++;
 275             return false;
 276 
 277         } else if (graph_fns->allowed && !(graph_fns->allowed(graph, a))) {
 278             crm_trace("Deferring synapse %d: not allowed", synapse->id);
 279             return false;
 280         }
 281     }
 282 
 283     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 initiate_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 297 {
 298     const char *id = pcmk__xe_id(action->xml);
 299 
 300     CRM_CHECK(id != NULL, return EINVAL);
 301     CRM_CHECK(!pcmk_is_set(action->flags, pcmk__graph_action_executed),
 302               return pcmk_rc_already);
 303 
 304     pcmk__set_graph_action_flags(action, pcmk__graph_action_executed);
 305     switch (action->type) {
 306         case pcmk__pseudo_graph_action:
 307             crm_trace("Executing pseudo-action %d (%s)", action->id, id);
 308             return graph_fns->pseudo(graph, action);
 309 
 310         case pcmk__rsc_graph_action:
 311             crm_trace("Executing resource action %d (%s)", action->id, id);
 312             return graph_fns->rsc(graph, action);
 313 
 314         case pcmk__cluster_graph_action:
 315             if (pcmk__str_eq(crm_element_value(action->xml, PCMK_XA_OPERATION),
 316                              PCMK_ACTION_STONITH, pcmk__str_none)) {
 317                 crm_trace("Executing fencing action %d (%s)",
 318                           action->id, id);
 319                 return graph_fns->fence(graph, action);
 320             }
 321             crm_trace("Executing cluster action %d (%s)", action->id, id);
 322             return graph_fns->cluster(graph, action);
 323 
 324         default:
 325             crm_err("Unsupported graph action type <%s " PCMK_XA_ID "='%s'> "
 326                     "(bug?)",
 327                     action->xml->name, id);
 328             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 fire_synapse(pcmk__graph_t *graph, pcmk__graph_synapse_t *synapse)
     /* [previous][next][first][last][top][bottom][index][help] */
 343 {
 344     pcmk__set_synapse_flags(synapse, pcmk__synapse_executed);
 345     for (GList *lpc = synapse->actions; lpc != NULL; lpc = lpc->next) {
 346         pcmk__graph_action_t *action = (pcmk__graph_action_t *) lpc->data;
 347         int rc = initiate_action(graph, action);
 348 
 349         if (rc != pcmk_rc_ok) {
 350             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             pcmk__set_synapse_flags(synapse, pcmk__synapse_confirmed);
 355             pcmk__set_graph_action_flags(action,
 356                                          pcmk__graph_action_confirmed
 357                                          |pcmk__graph_action_failed);
 358             return pcmk_rc_error;
 359         }
 360     }
 361     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 pseudo_action_dummy(pcmk__graph_t *graph, pcmk__graph_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 377 {
 378     static int fail = -1;
 379 
 380     if (fail < 0) {
 381         long long fail_ll;
 382 
 383         if ((pcmk__scan_ll(getenv("PE_fail"), &fail_ll, 0LL) == pcmk_rc_ok)
 384             && (fail_ll > 0LL) && (fail_ll <= INT_MAX)) {
 385             fail = (int) fail_ll;
 386         } else {
 387             fail = 0;
 388         }
 389     }
 390 
 391     if (action->id == fail) {
 392         crm_err("Dummy event handler: pretending action %d failed", action->id);
 393         pcmk__set_graph_action_flags(action, pcmk__graph_action_failed);
 394         graph->abort_priority = PCMK_SCORE_INFINITY;
 395     } else {
 396         crm_trace("Dummy event handler: action %d initiated", action->id);
 397     }
 398     pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
 399     pcmk__update_graph(graph, action);
 400     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 pcmk__execute_graph(pcmk__graph_t *graph)
     /* [previous][next][first][last][top][bottom][index][help] */
 420 {
 421     GList *lpc = NULL;
 422     int log_level = LOG_DEBUG;
 423     enum pcmk__graph_status pass_result = pcmk__graph_active;
 424     const char *status = "In progress";
 425 
 426     if (graph_fns == NULL) {
 427         graph_fns = &default_fns;
 428     }
 429     if (graph == NULL) {
 430         return pcmk__graph_complete;
 431     }
 432 
 433     graph->fired = 0;
 434     graph->pending = 0;
 435     graph->skipped = 0;
 436     graph->completed = 0;
 437     graph->incomplete = 0;
 438 
 439     // Count completed and in-flight synapses
 440     for (lpc = graph->synapses; lpc != NULL; lpc = lpc->next) {
 441         pcmk__graph_synapse_t *synapse = (pcmk__graph_synapse_t *) lpc->data;
 442 
 443         if (pcmk_is_set(synapse->flags, pcmk__synapse_confirmed)) {
 444             graph->completed++;
 445 
 446         } else if (!pcmk_is_set(synapse->flags, pcmk__synapse_failed)
 447                    && pcmk_is_set(synapse->flags, pcmk__synapse_executed)) {
 448             graph->pending++;
 449         }
 450     }
 451     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     for (lpc = graph->synapses; lpc != NULL; lpc = lpc->next) {
 456         pcmk__graph_synapse_t *synapse = (pcmk__graph_synapse_t *) lpc->data;
 457 
 458         if ((graph->batch_limit > 0)
 459             && (graph->pending >= graph->batch_limit)) {
 460 
 461             crm_debug("Throttling graph execution: batch limit (%d) reached",
 462                       graph->batch_limit);
 463             break;
 464 
 465         } else if (pcmk_is_set(synapse->flags, pcmk__synapse_failed)) {
 466             graph->skipped++;
 467             continue;
 468 
 469         } else if (pcmk_any_flags_set(synapse->flags,
 470                                       pcmk__synapse_confirmed
 471                                       |pcmk__synapse_executed)) {
 472             continue; // Already handled
 473 
 474         } else if (should_fire_synapse(graph, synapse)) {
 475             graph->fired++;
 476             if (fire_synapse(graph, synapse) != pcmk_rc_ok) {
 477                 crm_err("Synapse %d failed to fire", synapse->id);
 478                 log_level = LOG_ERR;
 479                 graph->abort_priority = PCMK_SCORE_INFINITY;
 480                 graph->incomplete++;
 481                 graph->fired--;
 482             }
 483 
 484             if (!(pcmk_is_set(synapse->flags, pcmk__synapse_confirmed))) {
 485                 graph->pending++;
 486             }
 487 
 488         } else {
 489             crm_trace("Synapse %d cannot fire", synapse->id);
 490             graph->incomplete++;
 491         }
 492     }
 493 
 494     if ((graph->pending == 0) && (graph->fired == 0)) {
 495         graph->complete = true;
 496 
 497         if ((graph->incomplete != 0) && (graph->abort_priority <= 0)) {
 498             log_level = LOG_WARNING;
 499             pass_result = pcmk__graph_terminated;
 500             status = "Terminated";
 501 
 502         } else if (graph->skipped != 0) {
 503             log_level = LOG_NOTICE;
 504             pass_result = pcmk__graph_complete;
 505             status = "Stopped";
 506 
 507         } else {
 508             log_level = LOG_NOTICE;
 509             pass_result = pcmk__graph_complete;
 510             status = "Complete";
 511         }
 512 
 513     } else if (graph->fired == 0) {
 514         pass_result = pcmk__graph_pending;
 515     }
 516 
 517     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     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 unpack_action(pcmk__graph_synapse_t *parent, xmlNode *xml_action)
     /* [previous][next][first][last][top][bottom][index][help] */
 542 {
 543     enum pcmk__graph_action_type action_type;
 544     pcmk__graph_action_t *action = NULL;
 545     const char *value = pcmk__xe_id(xml_action);
 546 
 547     if (value == NULL) {
 548         crm_err("Ignoring transition graph action without " PCMK_XA_ID
 549                 " (bug?)");
 550         crm_log_xml_trace(xml_action, "invalid");
 551         return NULL;
 552     }
 553 
 554     if (pcmk__xe_is(xml_action, PCMK__XE_RSC_OP)) {
 555         action_type = pcmk__rsc_graph_action;
 556 
 557     } else if (pcmk__xe_is(xml_action, PCMK__XE_PSEUDO_EVENT)) {
 558         action_type = pcmk__pseudo_graph_action;
 559 
 560     } else if (pcmk__xe_is(xml_action, PCMK__XE_CRM_EVENT)) {
 561         action_type = pcmk__cluster_graph_action;
 562 
 563     } else {
 564         crm_err("Ignoring transition graph action of unknown type '%s' (bug?)",
 565                 xml_action->name);
 566         crm_log_xml_trace(xml_action, "invalid");
 567         return NULL;
 568     }
 569 
 570     action = calloc(1, sizeof(pcmk__graph_action_t));
 571     if (action == NULL) {
 572         crm_perror(LOG_CRIT, "Cannot unpack transition graph action");
 573         crm_log_xml_trace(xml_action, "lost");
 574         return NULL;
 575     }
 576 
 577     pcmk__scan_min_int(value, &(action->id), -1);
 578     action->type = pcmk__rsc_graph_action;
 579     action->xml = pcmk__xml_copy(NULL, xml_action);
 580     action->synapse = parent;
 581     action->type = action_type;
 582     action->params = xml2list(action->xml);
 583 
 584     value = crm_meta_value(action->params, PCMK_META_TIMEOUT);
 585     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     value = crm_meta_value(action->params, PCMK_META_START_DELAY);
 591     {
 592         int start_delay;
 593 
 594         pcmk__scan_min_int(value, &start_delay, 0);
 595         action->timeout += start_delay;
 596     }
 597 
 598     if (pcmk__guint_from_hash(action->params, CRM_META "_" PCMK_META_INTERVAL,
 599                               0, &(action->interval_ms)) != pcmk_rc_ok) {
 600         action->interval_ms = 0;
 601     }
 602 
 603     value = crm_meta_value(action->params, PCMK__META_CAN_FAIL);
 604     if (value != NULL) {
 605         int can_fail = 0;
 606 
 607         if ((crm_str_to_boolean(value, &can_fail) > 0) && (can_fail > 0)) {
 608             pcmk__set_graph_action_flags(action, pcmk__graph_action_can_fail);
 609         } else {
 610             pcmk__clear_graph_action_flags(action, pcmk__graph_action_can_fail);
 611         }
 612 
 613         if (pcmk_is_set(action->flags, pcmk__graph_action_can_fail)) {
 614             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     crm_trace("Action %d has timer set to %dms", action->id, action->timeout);
 620 
 621     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 unpack_synapse(pcmk__graph_t *new_graph, const xmlNode *xml_synapse)
     /* [previous][next][first][last][top][bottom][index][help] */
 635 {
 636     const char *value = NULL;
 637     xmlNode *action_set = NULL;
 638     pcmk__graph_synapse_t *new_synapse = NULL;
 639 
 640     crm_trace("Unpacking synapse %s", pcmk__xe_id(xml_synapse));
 641 
 642     new_synapse = calloc(1, sizeof(pcmk__graph_synapse_t));
 643     if (new_synapse == NULL) {
 644         return NULL;
 645     }
 646 
 647     pcmk__scan_min_int(pcmk__xe_id(xml_synapse), &(new_synapse->id), 0);
 648 
 649     value = crm_element_value(xml_synapse, PCMK__XA_PRIORITY);
 650     pcmk__scan_min_int(value, &(new_synapse->priority), 0);
 651 
 652     CRM_CHECK(new_synapse->id >= 0,
 653               free_graph_synapse((gpointer) new_synapse); return NULL);
 654 
 655     new_graph->num_synapses++;
 656 
 657     crm_trace("Unpacking synapse %s action sets",
 658               crm_element_value(xml_synapse, PCMK_XA_ID));
 659 
 660     for (action_set = pcmk__xe_first_child(xml_synapse, "action_set", NULL,
 661                                            NULL);
 662          action_set != NULL; action_set = pcmk__xe_next_same(action_set)) {
 663 
 664         for (xmlNode *action = pcmk__xe_first_child(action_set, NULL, NULL,
 665                                                     NULL);
 666              action != NULL; action = pcmk__xe_next(action)) {
 667 
 668             pcmk__graph_action_t *new_action = unpack_action(new_synapse,
 669                                                              action);
 670 
 671             if (new_action == NULL) {
 672                 continue;
 673             }
 674 
 675             crm_trace("Adding action %d to synapse %d",
 676                       new_action->id, new_synapse->id);
 677             new_graph->num_actions++;
 678             new_synapse->actions = g_list_append(new_synapse->actions,
 679                                                  new_action);
 680         }
 681     }
 682 
 683     crm_trace("Unpacking synapse %s inputs", pcmk__xe_id(xml_synapse));
 684 
 685     for (xmlNode *inputs = pcmk__xe_first_child(xml_synapse, "inputs", NULL,
 686                                                 NULL);
 687          inputs != NULL; inputs = pcmk__xe_next_same(inputs)) {
 688 
 689         for (xmlNode *trigger = pcmk__xe_first_child(inputs, "trigger", NULL,
 690                                                      NULL);
 691              trigger != NULL; trigger = pcmk__xe_next_same(trigger)) {
 692 
 693             for (xmlNode *input = pcmk__xe_first_child(trigger, NULL, NULL,
 694                                                        NULL);
 695                  input != NULL; input = pcmk__xe_next(input)) {
 696 
 697                 pcmk__graph_action_t *new_input = unpack_action(new_synapse,
 698                                                                 input);
 699 
 700                 if (new_input == NULL) {
 701                     continue;
 702                 }
 703 
 704                 crm_trace("Adding input %d to synapse %d",
 705                            new_input->id, new_synapse->id);
 706 
 707                 new_synapse->inputs = g_list_append(new_synapse->inputs,
 708                                                     new_input);
 709             }
 710         }
 711     }
 712 
 713     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 pcmk__unpack_graph(const xmlNode *xml_graph, const char *reference)
     /* [previous][next][first][last][top][bottom][index][help] */
 743 {
 744     pcmk__graph_t *new_graph = NULL;
 745 
 746     new_graph = calloc(1, sizeof(pcmk__graph_t));
 747     if (new_graph == NULL) {
 748         return NULL;
 749     }
 750 
 751     new_graph->source = strdup(pcmk__s(reference, "unknown"));
 752     if (new_graph->source == NULL) {
 753         pcmk__free_graph(new_graph);
 754         return NULL;
 755     }
 756 
 757     new_graph->id = -1;
 758     new_graph->abort_priority = 0;
 759     new_graph->network_delay = 0;
 760     new_graph->stonith_timeout = 0;
 761     new_graph->completion_action = pcmk__graph_done;
 762 
 763     // Parse top-level attributes from PCMK__XE_TRANSITION_GRAPH
 764     if (xml_graph != NULL) {
 765         const char *buf = crm_element_value(xml_graph, "transition_id");
 766 
 767         CRM_CHECK(buf != NULL,
 768                   pcmk__free_graph(new_graph); return NULL);
 769         pcmk__scan_min_int(buf, &(new_graph->id), -1);
 770 
 771         buf = crm_element_value(xml_graph, PCMK_OPT_CLUSTER_DELAY);
 772         CRM_CHECK(buf != NULL,
 773                   pcmk__free_graph(new_graph); return NULL);
 774         pcmk_parse_interval_spec(buf, &(new_graph->network_delay));
 775 
 776         buf = crm_element_value(xml_graph, PCMK_OPT_STONITH_TIMEOUT);
 777         if (buf == NULL) {
 778             new_graph->stonith_timeout = new_graph->network_delay;
 779         } else {
 780             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         buf = crm_element_value(xml_graph, PCMK_OPT_BATCH_LIMIT);
 785         if ((buf == NULL)
 786             || (pcmk__scan_min_int(buf, &(new_graph->batch_limit),
 787                                    -1) != pcmk_rc_ok)) {
 788             new_graph->batch_limit = 0;
 789         }
 790 
 791         buf = crm_element_value(xml_graph, PCMK_OPT_MIGRATION_LIMIT);
 792         pcmk__scan_min_int(buf, &(new_graph->migration_limit), -1);
 793 
 794         new_graph->failed_stop_offset =
 795             crm_element_value_copy(xml_graph, "failed-stop-offset");
 796         new_graph->failed_start_offset =
 797             crm_element_value_copy(xml_graph, "failed-start-offset");
 798 
 799         if (crm_element_value_epoch(xml_graph, "recheck-by",
 800                                     &(new_graph->recheck_by)) != pcmk_ok) {
 801             new_graph->recheck_by = 0;
 802         }
 803     }
 804 
 805     // Unpack each child <synapse> element
 806     for (const xmlNode *synapse_xml = pcmk__xe_first_child(xml_graph,
 807                                                            "synapse", NULL,
 808                                                            NULL);
 809          synapse_xml != NULL; synapse_xml = pcmk__xe_next_same(synapse_xml)) {
 810 
 811         pcmk__graph_synapse_t *new_synapse = unpack_synapse(new_graph,
 812                                                             synapse_xml);
 813 
 814         if (new_synapse != NULL) {
 815             new_graph->synapses = g_list_append(new_graph->synapses,
 816                                                 new_synapse);
 817         }
 818     }
 819 
 820     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     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 pcmk__event_from_graph_action(const xmlNode *resource,
     /* [previous][next][first][last][top][bottom][index][help] */
 846                               const pcmk__graph_action_t *action,
 847                               int status, int rc, const char *exit_reason)
 848 {
 849     lrmd_event_data_t *op = NULL;
 850     GHashTableIter iter;
 851     const char *name = NULL;
 852     const char *value = NULL;
 853     xmlNode *action_resource = NULL;
 854 
 855     CRM_CHECK(action != NULL, return NULL);
 856     CRM_CHECK(action->type == pcmk__rsc_graph_action, return NULL);
 857 
 858     action_resource = pcmk__xe_first_child(action->xml, PCMK_XE_PRIMITIVE, NULL,
 859                                            NULL);
 860     CRM_CHECK(action_resource != NULL, crm_log_xml_warn(action->xml, "invalid");
 861                                        return NULL);
 862 
 863     op = lrmd_new_event(pcmk__xe_id(action_resource),
 864                         crm_element_value(action->xml, PCMK_XA_OPERATION),
 865                         action->interval_ms);
 866     lrmd__set_result(op, rc, status, exit_reason);
 867     op->t_run = time(NULL);
 868     op->t_rcchange = op->t_run;
 869     op->params = pcmk__strkey_table(free, free);
 870 
 871     g_hash_table_iter_init(&iter, action->params);
 872     while (g_hash_table_iter_next(&iter, (void **)&name, (void **)&value)) {
 873         pcmk__insert_dup(op->params, name, value);
 874     }
 875 
 876     for (xmlNode *xop = pcmk__xe_first_child(resource, NULL, NULL, NULL);
 877          xop != NULL; xop = pcmk__xe_next(xop)) {
 878 
 879         int tmp = 0;
 880 
 881         crm_element_value_int(xop, PCMK__XA_CALL_ID, &tmp);
 882         crm_debug("Got call_id=%d for %s", tmp, pcmk__xe_id(resource));
 883         if (tmp > op->call_id) {
 884             op->call_id = tmp;
 885         }
 886     }
 887 
 888     op->call_id++;
 889     return op;
 890 }

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