root/lib/pacemaker/pcmk_graph_producer.c

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

DEFINITIONS

This source file includes following definitions.
  1. add_node_to_xml_by_id
  2. add_node_to_xml
  3. add_maintenance_nodes
  4. add_maintenance_update
  5. add_downed_nodes
  6. clone_op_key
  7. add_node_details
  8. add_resource_details
  9. add_action_attributes
  10. create_graph_action
  11. should_add_action_to_graph
  12. ordering_can_change_actions
  13. should_add_input_to_graph
  14. pcmk__graph_has_loop
  15. create_graph_synapse
  16. add_action_to_graph
  17. pcmk__log_transition_summary
  18. pcmk__add_rsc_actions_to_graph
  19. pcmk__create_graph

   1 /*
   2  * Copyright 2004-2023 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <sys/param.h>
  13 #include <crm/crm.h>
  14 #include <crm/cib.h>
  15 #include <crm/msg_xml.h>
  16 #include <crm/common/xml.h>
  17 
  18 #include <glib.h>
  19 
  20 #include <pacemaker-internal.h>
  21 
  22 #include "libpacemaker_private.h"
  23 
  24 // Convenience macros for logging action properties
  25 
  26 #define action_type_str(flags) \
  27     (pcmk_is_set((flags), pcmk_action_pseudo)? "pseudo-action" : "action")
  28 
  29 #define action_optional_str(flags) \
  30     (pcmk_is_set((flags), pcmk_action_optional)? "optional" : "required")
  31 
  32 #define action_runnable_str(flags) \
  33     (pcmk_is_set((flags), pcmk_action_runnable)? "runnable" : "unrunnable")
  34 
  35 #define action_node_str(a) \
  36     (((a)->node == NULL)? "no node" : (a)->node->details->uname)
  37 
  38 /*!
  39  * \internal
  40  * \brief Add an XML node tag for a specified ID
  41  *
  42  * \param[in]     id      Node UUID to add
  43  * \param[in,out] xml     Parent XML tag to add to
  44  */
  45 static xmlNode*
  46 add_node_to_xml_by_id(const char *id, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
  47 {
  48     xmlNode *node_xml;
  49 
  50     node_xml = create_xml_node(xml, XML_CIB_TAG_NODE);
  51     crm_xml_add(node_xml, XML_ATTR_ID, id);
  52 
  53     return node_xml;
  54 }
  55 
  56 /*!
  57  * \internal
  58  * \brief Add an XML node tag for a specified node
  59  *
  60  * \param[in]     node  Node to add
  61  * \param[in,out] xml   XML to add node to
  62  */
  63 static void
  64 add_node_to_xml(const pcmk_node_t *node, void *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
  65 {
  66     add_node_to_xml_by_id(node->details->id, (xmlNode *) xml);
  67 }
  68 
  69 /*!
  70  * \internal
  71  * \brief Count (optionally add to XML) nodes needing maintenance state update
  72  *
  73  * \param[in,out] xml        Parent XML tag to add to, if any
  74  * \param[in]     scheduler  Scheduler data
  75  *
  76  * \return Count of nodes added
  77  * \note Only Pacemaker Remote nodes are considered currently
  78  */
  79 static int
  80 add_maintenance_nodes(xmlNode *xml, const pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
  81 {
  82     xmlNode *maintenance = NULL;
  83     int count = 0;
  84 
  85     if (xml != NULL) {
  86         maintenance = create_xml_node(xml, XML_GRAPH_TAG_MAINTENANCE);
  87     }
  88     for (const GList *iter = scheduler->nodes;
  89          iter != NULL; iter = iter->next) {
  90         const pcmk_node_t *node = iter->data;
  91 
  92         if (pe__is_guest_or_remote_node(node) &&
  93             (node->details->maintenance != node->details->remote_maintenance)) {
  94 
  95             if (maintenance != NULL) {
  96                 crm_xml_add(add_node_to_xml_by_id(node->details->id,
  97                                                   maintenance),
  98                             XML_NODE_IS_MAINTENANCE,
  99                             (node->details->maintenance? "1" : "0"));
 100             }
 101             count++;
 102         }
 103     }
 104     crm_trace("%s %d nodes in need of maintenance mode update in state",
 105               ((maintenance == NULL)? "Counted" : "Added"), count);
 106     return count;
 107 }
 108 
 109 /*!
 110  * \internal
 111  * \brief Add pseudo action with nodes needing maintenance state update
 112  *
 113  * \param[in,out] scheduler  Scheduler data
 114  */
 115 static void
 116 add_maintenance_update(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 117 {
 118     pcmk_action_t *action = NULL;
 119 
 120     if (add_maintenance_nodes(NULL, scheduler) != 0) {
 121         action = get_pseudo_op(PCMK_ACTION_MAINTENANCE_NODES, scheduler);
 122         pe__set_action_flags(action, pcmk_action_always_in_graph);
 123     }
 124 }
 125 
 126 /*!
 127  * \internal
 128  * \brief Add XML with nodes that an action is expected to bring down
 129  *
 130  * If a specified action is expected to bring any nodes down, add an XML block
 131  * with their UUIDs. When a node is lost, this allows the controller to
 132  * determine whether it was expected.
 133  *
 134  * \param[in,out] xml       Parent XML tag to add to
 135  * \param[in]     action    Action to check for downed nodes
 136  */
 137 static void
 138 add_downed_nodes(xmlNode *xml, const pcmk_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 139 {
 140     CRM_CHECK((xml != NULL) && (action != NULL) && (action->node != NULL),
 141               return);
 142 
 143     if (pcmk__str_eq(action->task, PCMK_ACTION_DO_SHUTDOWN, pcmk__str_none)) {
 144 
 145         /* Shutdown makes the action's node down */
 146         xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED);
 147         add_node_to_xml_by_id(action->node->details->id, downed);
 148 
 149     } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH,
 150                             pcmk__str_none)) {
 151 
 152         /* Fencing makes the action's node and any hosted guest nodes down */
 153         const char *fence = g_hash_table_lookup(action->meta, "stonith_action");
 154 
 155         if (pcmk__is_fencing_action(fence)) {
 156             xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED);
 157             add_node_to_xml_by_id(action->node->details->id, downed);
 158             pe_foreach_guest_node(action->node->details->data_set,
 159                                   action->node, add_node_to_xml, downed);
 160         }
 161 
 162     } else if (action->rsc && action->rsc->is_remote_node
 163                && pcmk__str_eq(action->task, PCMK_ACTION_STOP,
 164                                pcmk__str_none)) {
 165 
 166         /* Stopping a remote connection resource makes connected node down,
 167          * unless it's part of a migration
 168          */
 169         GList *iter;
 170         pcmk_action_t *input;
 171         bool migrating = false;
 172 
 173         for (iter = action->actions_before; iter != NULL; iter = iter->next) {
 174             input = ((pcmk__related_action_t *) iter->data)->action;
 175             if ((input->rsc != NULL)
 176                 && pcmk__str_eq(action->rsc->id, input->rsc->id, pcmk__str_none)
 177                 && pcmk__str_eq(input->task, PCMK_ACTION_MIGRATE_FROM,
 178                                 pcmk__str_none)) {
 179                 migrating = true;
 180                 break;
 181             }
 182         }
 183         if (!migrating) {
 184             xmlNode *downed = create_xml_node(xml, XML_GRAPH_TAG_DOWNED);
 185             add_node_to_xml_by_id(action->rsc->id, downed);
 186         }
 187     }
 188 }
 189 
 190 /*!
 191  * \internal
 192  * \brief Create a transition graph operation key for a clone action
 193  *
 194  * \param[in] action       Clone action
 195  * \param[in] interval_ms  Action interval in milliseconds
 196  *
 197  * \return Newly allocated string with transition graph operation key
 198  */
 199 static char *
 200 clone_op_key(const pcmk_action_t *action, guint interval_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 201 {
 202     if (pcmk__str_eq(action->task, PCMK_ACTION_NOTIFY, pcmk__str_none)) {
 203         const char *n_type = g_hash_table_lookup(action->meta, "notify_type");
 204         const char *n_task = g_hash_table_lookup(action->meta,
 205                                                  "notify_operation");
 206 
 207         CRM_LOG_ASSERT((n_type != NULL) && (n_task != NULL));
 208         return pcmk__notify_key(action->rsc->clone_name, n_type, n_task);
 209 
 210     } else if (action->cancel_task != NULL) {
 211         return pcmk__op_key(action->rsc->clone_name, action->cancel_task,
 212                             interval_ms);
 213     } else {
 214         return pcmk__op_key(action->rsc->clone_name, action->task, interval_ms);
 215     }
 216 }
 217 
 218 /*!
 219  * \internal
 220  * \brief Add node details to transition graph action XML
 221  *
 222  * \param[in]     action  Scheduled action
 223  * \param[in,out] xml     Transition graph action XML for \p action
 224  */
 225 static void
 226 add_node_details(const pcmk_action_t *action, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 227 {
 228     pcmk_node_t *router_node = pcmk__connection_host_for_action(action);
 229 
 230     crm_xml_add(xml, XML_LRM_ATTR_TARGET, action->node->details->uname);
 231     crm_xml_add(xml, XML_LRM_ATTR_TARGET_UUID, action->node->details->id);
 232     if (router_node != NULL) {
 233         crm_xml_add(xml, XML_LRM_ATTR_ROUTER_NODE, router_node->details->uname);
 234     }
 235 }
 236 
 237 /*!
 238  * \internal
 239  * \brief Add resource details to transition graph action XML
 240  *
 241  * \param[in]     action      Scheduled action
 242  * \param[in,out] action_xml  Transition graph action XML for \p action
 243  */
 244 static void
 245 add_resource_details(const pcmk_action_t *action, xmlNode *action_xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 246 {
 247     xmlNode *rsc_xml = NULL;
 248     const char *attr_list[] = {
 249         XML_AGENT_ATTR_CLASS,
 250         XML_AGENT_ATTR_PROVIDER,
 251         XML_ATTR_TYPE
 252     };
 253 
 254     /* If a resource is locked to a node via shutdown-lock, mark its actions
 255      * so the controller can preserve the lock when the action completes.
 256      */
 257     if (pcmk__action_locks_rsc_to_node(action)) {
 258         crm_xml_add_ll(action_xml, XML_CONFIG_ATTR_SHUTDOWN_LOCK,
 259                        (long long) action->rsc->lock_time);
 260     }
 261 
 262     // List affected resource
 263 
 264     rsc_xml = create_xml_node(action_xml,
 265                               (const char *) action->rsc->xml->name);
 266     if (pcmk_is_set(action->rsc->flags, pcmk_rsc_removed)
 267         && (action->rsc->clone_name != NULL)) {
 268         /* Use the numbered instance name here, because if there is more
 269          * than one instance on a node, we need to make sure the command
 270          * goes to the right one.
 271          *
 272          * This is important even for anonymous clones, because the clone's
 273          * unique meta-attribute might have just been toggled from on to
 274          * off.
 275          */
 276         crm_debug("Using orphan clone name %s instead of %s",
 277                   action->rsc->id, action->rsc->clone_name);
 278         crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->clone_name);
 279         crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id);
 280 
 281     } else if (!pcmk_is_set(action->rsc->flags, pcmk_rsc_unique)) {
 282         const char *xml_id = ID(action->rsc->xml);
 283 
 284         crm_debug("Using anonymous clone name %s for %s (aka %s)",
 285                   xml_id, action->rsc->id, action->rsc->clone_name);
 286 
 287         /* ID is what we'd like client to use
 288          * ID_LONG is what they might know it as instead
 289          *
 290          * ID_LONG is only strictly needed /here/ during the
 291          * transition period until all nodes in the cluster
 292          * are running the new software /and/ have rebooted
 293          * once (meaning that they've only ever spoken to a DC
 294          * supporting this feature).
 295          *
 296          * If anyone toggles the unique flag to 'on', the
 297          * 'instance free' name will correspond to an orphan
 298          * and fall into the clause above instead
 299          */
 300         crm_xml_add(rsc_xml, XML_ATTR_ID, xml_id);
 301         if ((action->rsc->clone_name != NULL)
 302             && !pcmk__str_eq(xml_id, action->rsc->clone_name,
 303                              pcmk__str_none)) {
 304             crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->clone_name);
 305         } else {
 306             crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id);
 307         }
 308 
 309     } else {
 310         CRM_ASSERT(action->rsc->clone_name == NULL);
 311         crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->id);
 312     }
 313 
 314     for (int lpc = 0; lpc < PCMK__NELEM(attr_list); lpc++) {
 315         crm_xml_add(rsc_xml, attr_list[lpc],
 316                     g_hash_table_lookup(action->rsc->meta, attr_list[lpc]));
 317     }
 318 }
 319 
 320 /*!
 321  * \internal
 322  * \brief Add action attributes to transition graph action XML
 323  *
 324  * \param[in,out] action      Scheduled action
 325  * \param[in,out] action_xml  Transition graph action XML for \p action
 326  */
 327 static void
 328 add_action_attributes(pcmk_action_t *action, xmlNode *action_xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 329 {
 330     xmlNode *args_xml = NULL;
 331 
 332     /* We create free-standing XML to start, so we can sort the attributes
 333      * before adding it to action_xml, which keeps the scheduler regression
 334      * test graphs comparable.
 335      */
 336     args_xml = create_xml_node(NULL, XML_TAG_ATTRS);
 337 
 338     crm_xml_add(args_xml, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
 339     g_hash_table_foreach(action->extra, hash2field, args_xml);
 340 
 341     if ((action->rsc != NULL) && (action->node != NULL)) {
 342         // Get the resource instance attributes, evaluated properly for node
 343         GHashTable *params = pe_rsc_params(action->rsc, action->node,
 344                                            action->rsc->cluster);
 345 
 346         pcmk__substitute_remote_addr(action->rsc, params);
 347 
 348         g_hash_table_foreach(params, hash2smartfield, args_xml);
 349 
 350     } else if ((action->rsc != NULL)
 351                && (action->rsc->variant <= pcmk_rsc_variant_primitive)) {
 352         GHashTable *params = pe_rsc_params(action->rsc, NULL,
 353                                            action->rsc->cluster);
 354 
 355         g_hash_table_foreach(params, hash2smartfield, args_xml);
 356     }
 357 
 358     g_hash_table_foreach(action->meta, hash2metafield, args_xml);
 359     if (action->rsc != NULL) {
 360         pcmk_resource_t *parent = action->rsc;
 361 
 362         while (parent != NULL) {
 363             parent->cmds->add_graph_meta(parent, args_xml);
 364             parent = parent->parent;
 365         }
 366 
 367         pcmk__add_bundle_meta_to_xml(args_xml, action);
 368 
 369     } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH, pcmk__str_none)
 370                && (action->node != NULL)) {
 371         /* Pass the node's attributes as meta-attributes.
 372          *
 373          * @TODO: Determine whether it is still necessary to do this. It was
 374          * added in 33d99707, probably for the libfence-based implementation in
 375          * c9a90bd, which is no longer used.
 376          */
 377         g_hash_table_foreach(action->node->details->attrs, hash2metafield,
 378                              args_xml);
 379     }
 380 
 381     sorted_xml(args_xml, action_xml, FALSE);
 382     free_xml(args_xml);
 383 }
 384 
 385 /*!
 386  * \internal
 387  * \brief Create the transition graph XML for a scheduled action
 388  *
 389  * \param[in,out] parent        Parent XML element to add action to
 390  * \param[in,out] action        Scheduled action
 391  * \param[in]     skip_details  If false, add action details as sub-elements
 392  * \param[in]     scheduler     Scheduler data
 393  */
 394 static void
 395 create_graph_action(xmlNode *parent, pcmk_action_t *action, bool skip_details,
     /* [previous][next][first][last][top][bottom][index][help] */
 396                     const pcmk_scheduler_t *scheduler)
 397 {
 398     bool needs_node_info = true;
 399     bool needs_maintenance_info = false;
 400     xmlNode *action_xml = NULL;
 401 
 402     if ((action == NULL) || (scheduler == NULL)) {
 403         return;
 404     }
 405 
 406     // Create the top-level element based on task
 407 
 408     if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH, pcmk__str_none)) {
 409         /* All fences need node info; guest node fences are pseudo-events */
 410         if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
 411             action_xml = create_xml_node(parent, XML_GRAPH_TAG_PSEUDO_EVENT);
 412         } else {
 413             action_xml = create_xml_node(parent, XML_GRAPH_TAG_CRM_EVENT);
 414         }
 415 
 416     } else if (pcmk__str_any_of(action->task,
 417                                 PCMK_ACTION_DO_SHUTDOWN,
 418                                 PCMK_ACTION_CLEAR_FAILCOUNT, NULL)) {
 419         action_xml = create_xml_node(parent, XML_GRAPH_TAG_CRM_EVENT);
 420 
 421     } else if (pcmk__str_eq(action->task, PCMK_ACTION_LRM_DELETE,
 422                             pcmk__str_none)) {
 423         // CIB-only clean-up for shutdown locks
 424         action_xml = create_xml_node(parent, XML_GRAPH_TAG_CRM_EVENT);
 425         crm_xml_add(action_xml, PCMK__XA_MODE, XML_TAG_CIB);
 426 
 427     } else if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
 428         if (pcmk__str_eq(action->task, PCMK_ACTION_MAINTENANCE_NODES,
 429                          pcmk__str_none)) {
 430             needs_maintenance_info = true;
 431         }
 432         action_xml = create_xml_node(parent, XML_GRAPH_TAG_PSEUDO_EVENT);
 433         needs_node_info = false;
 434 
 435     } else {
 436         action_xml = create_xml_node(parent, XML_GRAPH_TAG_RSC_OP);
 437     }
 438 
 439     crm_xml_add_int(action_xml, XML_ATTR_ID, action->id);
 440     crm_xml_add(action_xml, XML_LRM_ATTR_TASK, action->task);
 441 
 442     if ((action->rsc != NULL) && (action->rsc->clone_name != NULL)) {
 443         char *clone_key = NULL;
 444         guint interval_ms;
 445 
 446         if (pcmk__guint_from_hash(action->meta, XML_LRM_ATTR_INTERVAL_MS, 0,
 447                                   &interval_ms) != pcmk_rc_ok) {
 448             interval_ms = 0;
 449         }
 450         clone_key = clone_op_key(action, interval_ms);
 451         crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, clone_key);
 452         crm_xml_add(action_xml, "internal_" XML_LRM_ATTR_TASK_KEY,
 453                     action->uuid);
 454         free(clone_key);
 455     } else {
 456         crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, action->uuid);
 457     }
 458 
 459     if (needs_node_info && (action->node != NULL)) {
 460         add_node_details(action, action_xml);
 461         g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET),
 462                             strdup(action->node->details->uname));
 463         g_hash_table_insert(action->meta, strdup(XML_LRM_ATTR_TARGET_UUID),
 464                             strdup(action->node->details->id));
 465     }
 466 
 467     if (skip_details) {
 468         return;
 469     }
 470 
 471     if ((action->rsc != NULL)
 472         && !pcmk_is_set(action->flags, pcmk_action_pseudo)) {
 473 
 474         // This is a real resource action, so add resource details
 475         add_resource_details(action, action_xml);
 476     }
 477 
 478     /* List any attributes in effect */
 479     add_action_attributes(action, action_xml);
 480 
 481     /* List any nodes this action is expected to make down */
 482     if (needs_node_info && (action->node != NULL)) {
 483         add_downed_nodes(action_xml, action);
 484     }
 485 
 486     if (needs_maintenance_info) {
 487         add_maintenance_nodes(action_xml, scheduler);
 488     }
 489 }
 490 
 491 /*!
 492  * \internal
 493  * \brief Check whether an action should be added to the transition graph
 494  *
 495  * \param[in] action  Action to check
 496  *
 497  * \return true if action should be added to graph, otherwise false
 498  */
 499 static bool
 500 should_add_action_to_graph(const pcmk_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 501 {
 502     if (!pcmk_is_set(action->flags, pcmk_action_runnable)) {
 503         crm_trace("Ignoring action %s (%d): unrunnable",
 504                   action->uuid, action->id);
 505         return false;
 506     }
 507 
 508     if (pcmk_is_set(action->flags, pcmk_action_optional)
 509         && !pcmk_is_set(action->flags, pcmk_action_always_in_graph)) {
 510         crm_trace("Ignoring action %s (%d): optional",
 511                   action->uuid, action->id);
 512         return false;
 513     }
 514 
 515     /* Actions for unmanaged resources should be excluded from the graph,
 516      * with the exception of monitors and cancellation of recurring monitors.
 517      */
 518     if ((action->rsc != NULL)
 519         && !pcmk_is_set(action->rsc->flags, pcmk_rsc_managed)
 520         && !pcmk__str_eq(action->task, PCMK_ACTION_MONITOR, pcmk__str_none)) {
 521 
 522         const char *interval_ms_s;
 523 
 524         /* A cancellation of a recurring monitor will get here because the task
 525          * is cancel rather than monitor, but the interval can still be used to
 526          * recognize it. The interval has been normalized to milliseconds by
 527          * this point, so a string comparison is sufficient.
 528          */
 529         interval_ms_s = g_hash_table_lookup(action->meta,
 530                                             XML_LRM_ATTR_INTERVAL_MS);
 531         if (pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches)) {
 532             crm_trace("Ignoring action %s (%d): for unmanaged resource (%s)",
 533                       action->uuid, action->id, action->rsc->id);
 534             return false;
 535         }
 536     }
 537 
 538     /* Always add pseudo-actions, fence actions, and shutdown actions (already
 539      * determined to be required and runnable by this point)
 540      */
 541     if (pcmk_is_set(action->flags, pcmk_action_pseudo)
 542         || pcmk__strcase_any_of(action->task, PCMK_ACTION_STONITH,
 543                                 PCMK_ACTION_DO_SHUTDOWN, NULL)) {
 544         return true;
 545     }
 546 
 547     if (action->node == NULL) {
 548         pe_err("Skipping action %s (%d) "
 549                "because it was not assigned to a node (bug?)",
 550                action->uuid, action->id);
 551         pcmk__log_action("Unassigned", action, false);
 552         return false;
 553     }
 554 
 555     if (pcmk_is_set(action->flags, pcmk_action_on_dc)) {
 556         crm_trace("Action %s (%d) should be dumped: "
 557                   "can run on DC instead of %s",
 558                   action->uuid, action->id, pe__node_name(action->node));
 559 
 560     } else if (pe__is_guest_node(action->node)
 561                && !action->node->details->remote_requires_reset) {
 562         crm_trace("Action %s (%d) should be dumped: "
 563                   "assuming will be runnable on guest %s",
 564                   action->uuid, action->id, pe__node_name(action->node));
 565 
 566     } else if (!action->node->details->online) {
 567         pe_err("Skipping action %s (%d) "
 568                "because it was scheduled for offline node (bug?)",
 569                action->uuid, action->id);
 570         pcmk__log_action("Offline node", action, false);
 571         return false;
 572 
 573     } else if (action->node->details->unclean) {
 574         pe_err("Skipping action %s (%d) "
 575                "because it was scheduled for unclean node (bug?)",
 576                action->uuid, action->id);
 577         pcmk__log_action("Unclean node", action, false);
 578         return false;
 579     }
 580     return true;
 581 }
 582 
 583 /*!
 584  * \internal
 585  * \brief Check whether an ordering's flags can change an action
 586  *
 587  * \param[in] ordering  Ordering to check
 588  *
 589  * \return true if ordering has flags that can change an action, false otherwise
 590  */
 591 static bool
 592 ordering_can_change_actions(const pcmk__related_action_t *ordering)
     /* [previous][next][first][last][top][bottom][index][help] */
 593 {
 594     return pcmk_any_flags_set(ordering->type,
 595                               ~(pcmk__ar_then_implies_first_graphed
 596                                 |pcmk__ar_first_implies_then_graphed
 597                                 |pcmk__ar_ordered));
 598 }
 599 
 600 /*!
 601  * \internal
 602  * \brief Check whether an action input should be in the transition graph
 603  *
 604  * \param[in]     action  Action to check
 605  * \param[in,out] input   Action input to check
 606  *
 607  * \return true if input should be in graph, false otherwise
 608  * \note This function may not only check an input, but disable it under certian
 609  *       circumstances (load or anti-colocation orderings that are not needed).
 610  */
 611 static bool
 612 should_add_input_to_graph(const pcmk_action_t *action,
     /* [previous][next][first][last][top][bottom][index][help] */
 613                           pcmk__related_action_t *input)
 614 {
 615     if (input->state == pe_link_dumped) {
 616         return true;
 617     }
 618 
 619     if ((uint32_t) input->type == pcmk__ar_none) {
 620         crm_trace("Ignoring %s (%d) input %s (%d): "
 621                   "ordering disabled",
 622                   action->uuid, action->id,
 623                   input->action->uuid, input->action->id);
 624         return false;
 625 
 626     } else if (!pcmk_is_set(input->action->flags, pcmk_action_runnable)
 627                && !ordering_can_change_actions(input)) {
 628         crm_trace("Ignoring %s (%d) input %s (%d): "
 629                   "optional and input unrunnable",
 630                   action->uuid, action->id,
 631                   input->action->uuid, input->action->id);
 632         return false;
 633 
 634     } else if (!pcmk_is_set(input->action->flags, pcmk_action_runnable)
 635                && pcmk_is_set(input->type, pcmk__ar_min_runnable)) {
 636         crm_trace("Ignoring %s (%d) input %s (%d): "
 637                   "minimum number of instances required but input unrunnable",
 638                   action->uuid, action->id,
 639                   input->action->uuid, input->action->id);
 640         return false;
 641 
 642     } else if (pcmk_is_set(input->type, pcmk__ar_unmigratable_then_blocks)
 643                && !pcmk_is_set(input->action->flags, pcmk_action_runnable)) {
 644         crm_trace("Ignoring %s (%d) input %s (%d): "
 645                   "input blocked if 'then' unmigratable",
 646                   action->uuid, action->id,
 647                   input->action->uuid, input->action->id);
 648         return false;
 649 
 650     } else if (pcmk_is_set(input->type, pcmk__ar_if_first_unmigratable)
 651                && pcmk_is_set(input->action->flags, pcmk_action_migratable)) {
 652         crm_trace("Ignoring %s (%d) input %s (%d): ordering applies "
 653                   "only if input is unmigratable, but it is migratable",
 654                   action->uuid, action->id,
 655                   input->action->uuid, input->action->id);
 656         return false;
 657 
 658     } else if (((uint32_t) input->type == pcmk__ar_ordered)
 659                && pcmk_is_set(input->action->flags, pcmk_action_migratable)
 660                && pcmk__ends_with(input->action->uuid, "_stop_0")) {
 661         crm_trace("Ignoring %s (%d) input %s (%d): "
 662                   "optional but stop in migration",
 663                   action->uuid, action->id,
 664                   input->action->uuid, input->action->id);
 665         return false;
 666 
 667     } else if ((uint32_t) input->type == pcmk__ar_if_on_same_node_or_target) {
 668         pcmk_node_t *input_node = input->action->node;
 669 
 670         if ((action->rsc != NULL)
 671             && pcmk__str_eq(action->task, PCMK_ACTION_MIGRATE_TO,
 672                             pcmk__str_none)) {
 673 
 674             pcmk_node_t *assigned = action->rsc->allocated_to;
 675 
 676             /* For load_stopped -> migrate_to orderings, we care about where
 677              * the resource has been assigned, not where migrate_to will be
 678              * executed.
 679              */
 680             if (!pe__same_node(input_node, assigned)) {
 681                 crm_trace("Ignoring %s (%d) input %s (%d): "
 682                           "migration target %s is not same as input node %s",
 683                           action->uuid, action->id,
 684                           input->action->uuid, input->action->id,
 685                           (assigned? assigned->details->uname : "<none>"),
 686                           (input_node? input_node->details->uname : "<none>"));
 687                 input->type = (enum pe_ordering) pcmk__ar_none;
 688                 return false;
 689             }
 690 
 691         } else if (!pe__same_node(input_node, action->node)) {
 692             crm_trace("Ignoring %s (%d) input %s (%d): "
 693                       "not on same node (%s vs %s)",
 694                       action->uuid, action->id,
 695                       input->action->uuid, input->action->id,
 696                       (action->node? action->node->details->uname : "<none>"),
 697                       (input_node? input_node->details->uname : "<none>"));
 698             input->type = (enum pe_ordering) pcmk__ar_none;
 699             return false;
 700 
 701         } else if (pcmk_is_set(input->action->flags, pcmk_action_optional)) {
 702             crm_trace("Ignoring %s (%d) input %s (%d): "
 703                       "ordering optional",
 704                       action->uuid, action->id,
 705                       input->action->uuid, input->action->id);
 706             input->type = (enum pe_ordering) pcmk__ar_none;
 707             return false;
 708         }
 709 
 710     } else if ((uint32_t) input->type == pcmk__ar_if_required_on_same_node) {
 711         if (input->action->node && action->node
 712             && !pe__same_node(input->action->node, action->node)) {
 713             crm_trace("Ignoring %s (%d) input %s (%d): "
 714                       "not on same node (%s vs %s)",
 715                       action->uuid, action->id,
 716                       input->action->uuid, input->action->id,
 717                       pe__node_name(action->node),
 718                       pe__node_name(input->action->node));
 719             input->type = (enum pe_ordering) pcmk__ar_none;
 720             return false;
 721 
 722         } else if (pcmk_is_set(input->action->flags, pcmk_action_optional)) {
 723             crm_trace("Ignoring %s (%d) input %s (%d): optional",
 724                       action->uuid, action->id,
 725                       input->action->uuid, input->action->id);
 726             input->type = (enum pe_ordering) pcmk__ar_none;
 727             return false;
 728         }
 729 
 730     } else if (input->action->rsc
 731                && input->action->rsc != action->rsc
 732                && pcmk_is_set(input->action->rsc->flags, pcmk_rsc_failed)
 733                && !pcmk_is_set(input->action->rsc->flags, pcmk_rsc_managed)
 734                && pcmk__ends_with(input->action->uuid, "_stop_0")
 735                && action->rsc && pe_rsc_is_clone(action->rsc)) {
 736         crm_warn("Ignoring requirement that %s complete before %s:"
 737                  " unmanaged failed resources cannot prevent clone shutdown",
 738                  input->action->uuid, action->uuid);
 739         return false;
 740 
 741     } else if (pcmk_is_set(input->action->flags, pcmk_action_optional)
 742                && !pcmk_any_flags_set(input->action->flags,
 743                                       pcmk_action_always_in_graph
 744                                       |pcmk_action_added_to_graph)
 745                && !should_add_action_to_graph(input->action)) {
 746         crm_trace("Ignoring %s (%d) input %s (%d): "
 747                   "input optional",
 748                   action->uuid, action->id,
 749                   input->action->uuid, input->action->id);
 750         return false;
 751     }
 752 
 753     crm_trace("%s (%d) input %s %s (%d) on %s should be dumped: %s %s %#.6x",
 754               action->uuid, action->id, action_type_str(input->action->flags),
 755               input->action->uuid, input->action->id,
 756               action_node_str(input->action),
 757               action_runnable_str(input->action->flags),
 758               action_optional_str(input->action->flags), input->type);
 759     return true;
 760 }
 761 
 762 /*!
 763  * \internal
 764  * \brief Check whether an ordering creates an ordering loop
 765  *
 766  * \param[in]     init_action  "First" action in ordering
 767  * \param[in]     action       Callers should always set this the same as
 768  *                             \p init_action (this function may use a different
 769  *                             value for recursive calls)
 770  * \param[in,out] input        Action wrapper for "then" action in ordering
 771  *
 772  * \return true if the ordering creates a loop, otherwise false
 773  */
 774 bool
 775 pcmk__graph_has_loop(const pcmk_action_t *init_action,
     /* [previous][next][first][last][top][bottom][index][help] */
 776                      const pcmk_action_t *action, pcmk__related_action_t *input)
 777 {
 778     bool has_loop = false;
 779 
 780     if (pcmk_is_set(input->action->flags, pcmk_action_detect_loop)) {
 781         crm_trace("Breaking tracking loop: %s@%s -> %s@%s (%#.6x)",
 782                   input->action->uuid,
 783                   input->action->node? input->action->node->details->uname : "",
 784                   action->uuid,
 785                   action->node? action->node->details->uname : "",
 786                   input->type);
 787         return false;
 788     }
 789 
 790     // Don't need to check inputs that won't be used
 791     if (!should_add_input_to_graph(action, input)) {
 792         return false;
 793     }
 794 
 795     if (input->action == init_action) {
 796         crm_debug("Input loop found in %s@%s ->...-> %s@%s",
 797                   action->uuid,
 798                   action->node? action->node->details->uname : "",
 799                   init_action->uuid,
 800                   init_action->node? init_action->node->details->uname : "");
 801         return true;
 802     }
 803 
 804     pe__set_action_flags(input->action, pcmk_action_detect_loop);
 805 
 806     crm_trace("Checking inputs of action %s@%s input %s@%s (%#.6x)"
 807               "for graph loop with %s@%s ",
 808               action->uuid,
 809               action->node? action->node->details->uname : "",
 810               input->action->uuid,
 811               input->action->node? input->action->node->details->uname : "",
 812               input->type,
 813               init_action->uuid,
 814               init_action->node? init_action->node->details->uname : "");
 815 
 816     // Recursively check input itself for loops
 817     for (GList *iter = input->action->actions_before;
 818          iter != NULL; iter = iter->next) {
 819 
 820         if (pcmk__graph_has_loop(init_action, input->action,
 821                                  (pcmk__related_action_t *) iter->data)) {
 822             // Recursive call already logged a debug message
 823             has_loop = true;
 824             break;
 825         }
 826     }
 827 
 828     pe__clear_action_flags(input->action, pcmk_action_detect_loop);
 829 
 830     if (!has_loop) {
 831         crm_trace("No input loop found in %s@%s -> %s@%s (%#.6x)",
 832                   input->action->uuid,
 833                   input->action->node? input->action->node->details->uname : "",
 834                   action->uuid,
 835                   action->node? action->node->details->uname : "",
 836                   input->type);
 837     }
 838     return has_loop;
 839 }
 840 
 841 /*!
 842  * \internal
 843  * \brief Create a synapse XML element for a transition graph
 844  *
 845  * \param[in]     action     Action that synapse is for
 846  * \param[in,out] scheduler  Scheduler data containing graph
 847  *
 848  * \return Newly added XML element for new graph synapse
 849  */
 850 static xmlNode *
 851 create_graph_synapse(const pcmk_action_t *action, pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 852 {
 853     int synapse_priority = 0;
 854     xmlNode *syn = create_xml_node(scheduler->graph, "synapse");
 855 
 856     crm_xml_add_int(syn, XML_ATTR_ID, scheduler->num_synapse);
 857     scheduler->num_synapse++;
 858 
 859     if (action->rsc != NULL) {
 860         synapse_priority = action->rsc->priority;
 861     }
 862     if (action->priority > synapse_priority) {
 863         synapse_priority = action->priority;
 864     }
 865     if (synapse_priority > 0) {
 866         crm_xml_add_int(syn, XML_CIB_ATTR_PRIORITY, synapse_priority);
 867     }
 868     return syn;
 869 }
 870 
 871 /*!
 872  * \internal
 873  * \brief Add an action to the transition graph XML if appropriate
 874  *
 875  * \param[in,out] data       Action to possibly add
 876  * \param[in,out] user_data  Scheduler data
 877  *
 878  * \note This will de-duplicate the action inputs, meaning that the
 879  *       pcmk__related_action_t:type flags can no longer be relied on to retain
 880  *       their original settings. That means this MUST be called after
 881  *       pcmk__apply_orderings() is complete, and nothing after this should rely
 882  *       on those type flags. (For example, some code looks for type equal to
 883  *       some flag rather than whether the flag is set, and some code looks for
 884  *       particular combinations of flags -- such code must be done before
 885  *       pcmk__create_graph().)
 886  */
 887 static void
 888 add_action_to_graph(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 889 {
 890     pcmk_action_t *action = (pcmk_action_t *) data;
 891     pcmk_scheduler_t *scheduler = (pcmk_scheduler_t *) user_data;
 892 
 893     xmlNode *syn = NULL;
 894     xmlNode *set = NULL;
 895     xmlNode *in = NULL;
 896 
 897     /* If we haven't already, de-duplicate inputs (even if we won't be adding
 898      * the action to the graph, so that crm_simulate's dot graphs don't have
 899      * duplicates).
 900      */
 901     if (!pcmk_is_set(action->flags, pcmk_action_inputs_deduplicated)) {
 902         pcmk__deduplicate_action_inputs(action);
 903         pe__set_action_flags(action, pcmk_action_inputs_deduplicated);
 904     }
 905 
 906     if (pcmk_is_set(action->flags, pcmk_action_added_to_graph)
 907         || !should_add_action_to_graph(action)) {
 908         return; // Already added, or shouldn't be
 909     }
 910     pe__set_action_flags(action, pcmk_action_added_to_graph);
 911 
 912     crm_trace("Adding action %d (%s%s%s) to graph",
 913               action->id, action->uuid,
 914               ((action->node == NULL)? "" : " on "),
 915               ((action->node == NULL)? "" : action->node->details->uname));
 916 
 917     syn = create_graph_synapse(action, scheduler);
 918     set = create_xml_node(syn, "action_set");
 919     in = create_xml_node(syn, "inputs");
 920 
 921     create_graph_action(set, action, false, scheduler);
 922 
 923     for (GList *lpc = action->actions_before; lpc != NULL; lpc = lpc->next) {
 924         pcmk__related_action_t *input = lpc->data;
 925 
 926         if (should_add_input_to_graph(action, input)) {
 927             xmlNode *input_xml = create_xml_node(in, "trigger");
 928 
 929             input->state = pe_link_dumped;
 930             create_graph_action(input_xml, input->action, true, scheduler);
 931         }
 932     }
 933 }
 934 
 935 static int transition_id = -1;
 936 
 937 /*!
 938  * \internal
 939  * \brief Log a message after calculating a transition
 940  *
 941  * \param[in] filename  Where transition input is stored
 942  */
 943 void
 944 pcmk__log_transition_summary(const char *filename)
     /* [previous][next][first][last][top][bottom][index][help] */
 945 {
 946     if (was_processing_error) {
 947         crm_err("Calculated transition %d (with errors)%s%s",
 948                 transition_id,
 949                 (filename == NULL)? "" : ", saving inputs in ",
 950                 (filename == NULL)? "" : filename);
 951 
 952     } else if (was_processing_warning) {
 953         crm_warn("Calculated transition %d (with warnings)%s%s",
 954                  transition_id,
 955                  (filename == NULL)? "" : ", saving inputs in ",
 956                  (filename == NULL)? "" : filename);
 957 
 958     } else {
 959         crm_notice("Calculated transition %d%s%s",
 960                    transition_id,
 961                    (filename == NULL)? "" : ", saving inputs in ",
 962                    (filename == NULL)? "" : filename);
 963     }
 964     if (crm_config_error) {
 965         crm_notice("Configuration errors found during scheduler processing,"
 966                    "  please run \"crm_verify -L\" to identify issues");
 967     }
 968 }
 969 
 970 /*!
 971  * \internal
 972  * \brief Add a resource's actions to the transition graph
 973  *
 974  * \param[in,out] rsc  Resource whose actions should be added
 975  */
 976 void
 977 pcmk__add_rsc_actions_to_graph(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 978 {
 979     GList *iter = NULL;
 980 
 981     CRM_ASSERT(rsc != NULL);
 982     pe_rsc_trace(rsc, "Adding actions for %s to graph", rsc->id);
 983 
 984     // First add the resource's own actions
 985     g_list_foreach(rsc->actions, add_action_to_graph, rsc->cluster);
 986 
 987     // Then recursively add its children's actions (appropriate to variant)
 988     for (iter = rsc->children; iter != NULL; iter = iter->next) {
 989         pcmk_resource_t *child_rsc = (pcmk_resource_t *) iter->data;
 990 
 991         child_rsc->cmds->add_actions_to_graph(child_rsc);
 992     }
 993 }
 994 
 995 /*!
 996  * \internal
 997  * \brief Create a transition graph with all cluster actions needed
 998  *
 999  * \param[in,out] scheduler  Scheduler data
1000  */
1001 void
1002 pcmk__create_graph(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
1003 {
1004     GList *iter = NULL;
1005     const char *value = NULL;
1006     long long limit = 0LL;
1007 
1008     transition_id++;
1009     crm_trace("Creating transition graph %d", transition_id);
1010 
1011     scheduler->graph = create_xml_node(NULL, XML_TAG_GRAPH);
1012 
1013     value = pe_pref(scheduler->config_hash, "cluster-delay");
1014     crm_xml_add(scheduler->graph, "cluster-delay", value);
1015 
1016     value = pe_pref(scheduler->config_hash, "stonith-timeout");
1017     crm_xml_add(scheduler->graph, "stonith-timeout", value);
1018 
1019     crm_xml_add(scheduler->graph, "failed-stop-offset", "INFINITY");
1020 
1021     if (pcmk_is_set(scheduler->flags, pcmk_sched_start_failure_fatal)) {
1022         crm_xml_add(scheduler->graph, "failed-start-offset", "INFINITY");
1023     } else {
1024         crm_xml_add(scheduler->graph, "failed-start-offset", "1");
1025     }
1026 
1027     value = pe_pref(scheduler->config_hash, "batch-limit");
1028     crm_xml_add(scheduler->graph, "batch-limit", value);
1029 
1030     crm_xml_add_int(scheduler->graph, "transition_id", transition_id);
1031 
1032     value = pe_pref(scheduler->config_hash, "migration-limit");
1033     if ((pcmk__scan_ll(value, &limit, 0LL) == pcmk_rc_ok) && (limit > 0)) {
1034         crm_xml_add(scheduler->graph, "migration-limit", value);
1035     }
1036 
1037     if (scheduler->recheck_by > 0) {
1038         char *recheck_epoch = NULL;
1039 
1040         recheck_epoch = crm_strdup_printf("%llu",
1041                                           (long long) scheduler->recheck_by);
1042         crm_xml_add(scheduler->graph, "recheck-by", recheck_epoch);
1043         free(recheck_epoch);
1044     }
1045 
1046     /* The following code will de-duplicate action inputs, so nothing past this
1047      * should rely on the action input type flags retaining their original
1048      * values.
1049      */
1050 
1051     // Add resource actions to graph
1052     for (iter = scheduler->resources; iter != NULL; iter = iter->next) {
1053         pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
1054 
1055         pe_rsc_trace(rsc, "Processing actions for %s", rsc->id);
1056         rsc->cmds->add_actions_to_graph(rsc);
1057     }
1058 
1059     // Add pseudo-action for list of nodes with maintenance state update
1060     add_maintenance_update(scheduler);
1061 
1062     // Add non-resource (node) actions
1063     for (iter = scheduler->actions; iter != NULL; iter = iter->next) {
1064         pcmk_action_t *action = (pcmk_action_t *) iter->data;
1065 
1066         if ((action->rsc != NULL)
1067             && (action->node != NULL)
1068             && action->node->details->shutdown
1069             && !pcmk_is_set(action->rsc->flags, pcmk_rsc_maintenance)
1070             && !pcmk_any_flags_set(action->flags,
1071                                    pcmk_action_optional|pcmk_action_runnable)
1072             && pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_none)) {
1073             /* Eventually we should just ignore the 'fence' case, but for now
1074              * it's the best way to detect (in CTS) when CIB resource updates
1075              * are being lost.
1076              */
1077             if (pcmk_is_set(scheduler->flags, pcmk_sched_quorate)
1078                 || (scheduler->no_quorum_policy == pcmk_no_quorum_ignore)) {
1079                 const bool managed = pcmk_is_set(action->rsc->flags,
1080                                                  pcmk_rsc_managed);
1081                 const bool failed = pcmk_is_set(action->rsc->flags,
1082                                                 pcmk_rsc_failed);
1083 
1084                 crm_crit("Cannot %s %s because of %s:%s%s (%s)",
1085                          action->node->details->unclean? "fence" : "shut down",
1086                          pe__node_name(action->node), action->rsc->id,
1087                          (managed? " blocked" : " unmanaged"),
1088                          (failed? " failed" : ""), action->uuid);
1089             }
1090         }
1091 
1092         add_action_to_graph((gpointer) action, (gpointer) scheduler);
1093     }
1094 
1095     crm_log_xml_trace(scheduler->graph, "graph");
1096 }

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