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

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