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

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